diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 84c2165f759..b71fe48f33e 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -8,6 +8,7 @@ use crate::computed_value_flags::ComputedValueFlags; use crate::gecko_bindings::structs::RawServoSelectorList; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::invalidation::element::document_state::InvalidationMatchingData; +use crate::properties::ComputedValues; use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser}; use crate::str::starts_with_ignore_ascii_case; use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; @@ -17,6 +18,7 @@ use cssparser::{CowRcStr, SourceLocation, ToCss, Token}; use dom::{DocumentState, ElementState}; use selectors::parser::SelectorParseErrorKind; use selectors::SelectorList; +use servo_arc::Arc; use std::fmt; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; @@ -233,7 +235,7 @@ pub struct SelectorImpl; /// A set of extra data to carry along with the matching context, either for /// selector-matching or invalidation. -#[derive(Debug, Default)] +#[derive(Default)] pub struct ExtraMatchingData { /// The invalidation data to invalidate doc-state pseudo-classes correctly. pub invalidation_data: InvalidationMatchingData, @@ -241,6 +243,10 @@ pub struct ExtraMatchingData { /// The invalidation bits from matching container queries. These are here /// just for convenience mostly. pub cascade_input_flags: ComputedValueFlags, + + /// The style of the originating element in order to evaluate @container + /// size queries affecting pseudo-elements. + pub originating_element_style: Option>, } impl ::selectors::SelectorImpl for SelectorImpl { diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 18eff8200cc..0035a5a53fc 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -281,7 +281,7 @@ where }; let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root()); - let container_size_query = ContainerSizeQuery::for_option_element(element); + let container_size_query = ContainerSizeQuery::for_option_element(element, None); let mut context = computed::Context::new( // We'd really like to own the rules here to avoid refcount traffic, but diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index b768e5ff5db..eba94c78629 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -420,7 +420,7 @@ where layout_parent_style: Option<&ComputedValues>, ) -> Option { let MatchingResults { rule_node, mut flags } = self.match_pseudo( - originating_element_style.style(), + &originating_element_style.style.0, pseudo, VisitedHandlingMode::AllLinksUnvisited, )?; @@ -428,7 +428,7 @@ where let mut visited_rules = None; if originating_element_style.style().visited_style().is_some() { visited_rules = self.match_pseudo( - originating_element_style.style(), + &originating_element_style.style.0, pseudo, VisitedHandlingMode::RelevantLinkVisited, ).map(|results| { @@ -506,7 +506,7 @@ where fn match_pseudo( &mut self, - originating_element_style: &ComputedValues, + originating_element_style: &Arc, pseudo_element: &PseudoElement, visited_handling: VisitedHandlingMode, ) -> Option { @@ -534,7 +534,7 @@ where let bloom_filter = self.context.thread_local.bloom_filter.filter(); let nth_index_cache = &mut self.context.thread_local.nth_index_cache; - let mut matching_context = MatchingContext::new_for_visited( + let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited( MatchingMode::ForStatelessPseudoElement, Some(bloom_filter), Some(nth_index_cache), @@ -542,6 +542,8 @@ where self.context.shared.quirks_mode(), NeedsSelectorFlags::Yes, ); + matching_context.extra_data.originating_element_style = + Some(originating_element_style.clone()); // NB: We handle animation rules for ::before and ::after when // traversing them. diff --git a/components/style/stylesheets/container_rule.rs b/components/style/stylesheets/container_rule.rs index ac1ddd3de14..0720cde1366 100644 --- a/components/style/stylesheets/container_rule.rs +++ b/components/style/stylesheets/container_rule.rs @@ -135,15 +135,26 @@ enum TraversalResult { Done(T), } -fn traverse_container(mut e: E, evaluator: F) -> Option<(E, R)> +fn traverse_container( + mut e: E, + originating_element_style: Option<&Arc>, + evaluator: F, +) -> Option<(E, R)> where E: TElement, - F: Fn(E) -> TraversalResult, + F: Fn(E, Option<&Arc>) -> TraversalResult, { - while let Some(element) = e.traversal_parent() { - match evaluator(element) { + if originating_element_style.is_some() { + match evaluator(e, originating_element_style) { TraversalResult::InProgress => {}, - TraversalResult::StopTraversal => break, + TraversalResult::StopTraversal => return None, + TraversalResult::Done(result) => return Some((e, result)), + } + } + while let Some(element) = e.traversal_parent() { + match evaluator(element, None) { + TraversalResult::InProgress => {}, + TraversalResult::StopTraversal => return None, TraversalResult::Done(result) => return Some((element, result)), } e = element; @@ -174,15 +185,22 @@ impl ContainerCondition { fn valid_container_info( &self, potential_container: E, + originating_element_style: Option<&Arc>, ) -> TraversalResult> where E: TElement, { - let data = match potential_container.borrow_data() { - Some(data) => data, - None => return TraversalResult::InProgress, + let data; + let style = match originating_element_style { + Some(s) => s, + None => { + data = match potential_container.borrow_data() { + Some(d) => d, + None => return TraversalResult::InProgress, + }; + data.styles.primary() + }, }; - let style = data.styles.primary(); let wm = style.writing_mode; let box_style = style.get_box(); @@ -211,11 +229,21 @@ impl ContainerCondition { } /// Performs container lookup for a given element. - pub fn find_container(&self, e: E) -> Option> + pub fn find_container( + &self, + e: E, + originating_element_style: Option<&Arc>, + ) -> Option> where E: TElement, { - match traverse_container(e, |element| self.valid_container_info(element)) { + match traverse_container( + e, + originating_element_style, + |element, originating_element_style| { + self.valid_container_info(element, originating_element_style) + }, + ) { Some((_, result)) => Some(result), None => None, } @@ -226,18 +254,19 @@ impl ContainerCondition { &self, device: &Device, element: E, + originating_element_style: Option<&Arc>, invalidation_flags: &mut ComputedValueFlags, ) -> KleeneValue where E: TElement, { - let result = self.find_container(element); + let result = self.find_container(element, originating_element_style); let (container, info) = match result { Some(r) => (Some(r.element), Some((r.info, r.style))), None => (None, None), }; // Set up the lookup for the container in question, as the condition may be using container query lengths. - let size_query_container_lookup = ContainerSizeQuery::for_option_element(container); + let size_query_container_lookup = ContainerSizeQuery::for_option_element(container, None); Context::for_container_query_evaluation( device, info, @@ -448,16 +477,24 @@ pub enum ContainerSizeQuery<'a> { } impl<'a> ContainerSizeQuery<'a> { - fn evaluate_potential_size_container(e: E) -> TraversalResult + fn evaluate_potential_size_container( + e: E, + originating_element_style: Option<&Arc>, + ) -> TraversalResult where E: TElement, { - let data = match e.borrow_data() { - Some(data) => data, - None => return TraversalResult::InProgress, + let data; + let style = match originating_element_style { + Some(s) => s, + None => { + data = match e.borrow_data() { + Some(d) => d, + None => return TraversalResult::InProgress, + }; + data.styles.primary() + }, }; - - let style = data.styles.primary(); if !style .flags .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE) @@ -494,17 +531,26 @@ impl<'a> ContainerSizeQuery<'a> { } /// Find the query container size for a given element. Meant to be used as a callback for new(). - fn lookup(element: E) -> ContainerSizeQueryResult + fn lookup( + element: E, + originating_element_style: Option<&Arc>, + ) -> ContainerSizeQueryResult where E: TElement + 'a, { - match traverse_container(element, |e| Self::evaluate_potential_size_container(e)) { + match traverse_container( + element, + originating_element_style, + |e, originating_element_style| { + Self::evaluate_potential_size_container(e, originating_element_style) + }, + ) { Some((container, result)) => { if result.is_complete() { result } else { // Traverse up from the found size container to see if we can get a complete containment. - result.merge(Self::lookup(container)) + result.merge(Self::lookup(container, None)) } }, None => ContainerSizeQueryResult::default(), @@ -512,35 +558,51 @@ impl<'a> ContainerSizeQuery<'a> { } /// Create a new instance of the container size query for given element, with a deferred lookup callback. - pub fn for_element(element: E) -> Self + pub fn for_element( + element: E, + originating_element_style: Option<&'a Arc>, + ) -> Self where E: TElement + 'a, { - // No need to bother if we're the top element. - if let Some(parent) = element.traversal_parent() { - let should_traverse = match parent.borrow_data() { - Some(data) => { - let style = data.styles.primary(); - style - .flags - .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE) - }, - None => true, // `display: none`, still want to show a correct computed value, so give it a try. - }; - if should_traverse { - return Self::NotEvaluated(Box::new(move || Self::lookup(element))); - } + let parent; + let data; + let style = match originating_element_style { + Some(s) => Some(s), + None => { + // No need to bother if we're the top element. + parent = match element.traversal_parent() { + Some(parent) => parent, + None => return Self::none(), + }; + data = parent.borrow_data(); + data.as_ref().map(|data| data.styles.primary()) + }, + }; + let should_traverse = match style { + Some(style) => style + .flags + .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE), + None => true, // `display: none`, still want to show a correct computed value, so give it a try. + }; + if should_traverse { + return Self::NotEvaluated(Box::new(move || { + Self::lookup(element, originating_element_style) + })); } Self::none() } /// Create a new instance, but with optional element. - pub fn for_option_element(element: Option) -> Self + pub fn for_option_element( + element: Option, + originating_element_style: Option<&'a Arc>, + ) -> Self where E: TElement + 'a, { if let Some(e) = element { - Self::for_element(e) + Self::for_element(e, originating_element_style) } else { Self::none() } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 88ace3df421..7a17c5a0c59 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -966,7 +966,7 @@ impl Stylist { element: E, pseudo: &PseudoElement, rule_inclusion: RuleInclusion, - parent_style: &ComputedValues, + parent_style: &Arc, is_probe: bool, matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>, ) -> Option> @@ -1105,7 +1105,7 @@ impl Stylist { &self, guards: &StylesheetGuards, element: E, - parent_style: &ComputedValues, + parent_style: &Arc, pseudo: &PseudoElement, is_probe: bool, rule_inclusion: RuleInclusion, @@ -1125,7 +1125,7 @@ impl Stylist { }; let mut declarations = ApplicableDeclarationList::new(); - let mut matching_context = MatchingContext::new( + let mut matching_context = MatchingContext::<'_, E::Impl>::new( MatchingMode::ForStatelessPseudoElement, None, None, @@ -1134,6 +1134,7 @@ impl Stylist { ); matching_context.pseudo_element_matching_fn = matching_fn; + matching_context.extra_data.originating_element_style = Some(parent_style.clone()); self.push_applicable_declarations( element, @@ -1155,7 +1156,7 @@ impl Stylist { let mut visited_rules = None; if parent_style.visited_style().is_some() { let mut declarations = ApplicableDeclarationList::new(); - let mut matching_context = MatchingContext::new_for_visited( + let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited( MatchingMode::ForStatelessPseudoElement, None, None, @@ -1164,6 +1165,7 @@ impl Stylist { needs_selector_flags, ); matching_context.pseudo_element_matching_fn = matching_fn; + matching_context.extra_data.originating_element_style = Some(parent_style.clone()); self.push_applicable_declarations( element, @@ -2380,7 +2382,14 @@ impl CascadeData { None => return true, Some(ref c) => c, }; - let matches = condition.matches(stylist.device(), element, &mut context.extra_data.cascade_input_flags).to_bool(/* unknown = */ false); + let matches = condition + .matches( + stylist.device(), + element, + context.extra_data.originating_element_style.as_ref(), + &mut context.extra_data.cascade_input_flags, + ) + .to_bool(/* unknown = */ false); if !matches { return false; }