From 5ded58a2b190f1295affc84ad1b8963be82c2efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 20 Oct 2022 14:24:36 +0000 Subject: [PATCH] style: Allow propagating computed style bits from the selector-matching process This allows us to propagate flags from the container query styles all the way to the computed style of the element. The flag for viewport units in container queries has to be different because it requires rematching, see comments. Depends on D159851 Differential Revision: https://phabricator.services.mozilla.com/D159852 --- components/style/animation.rs | 1 + components/style/context.rs | 7 ++++- components/style/gecko/selector_parser.rs | 5 ++++ components/style/matching.rs | 1 + components/style/properties/cascade.rs | 10 +++++++ .../style/properties/computed_value_flags.rs | 30 +++++++++++++++++-- components/style/selector_map.rs | 1 + components/style/style_resolver.rs | 27 ++++++++++++----- .../style/stylesheets/container_rule.rs | 17 +++++++++-- components/style/stylist.rs | 7 ++++- 10 files changed, 92 insertions(+), 14 deletions(-) diff --git a/components/style/animation.rs b/components/style/animation.rs index 2d2748d29e5..b8b98dcb4e4 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -259,6 +259,7 @@ impl IntermediateComputedKeyframe { let inputs = CascadeInputs { rules: new_node, visited_rules: base_style.visited_rules().cloned(), + flags: base_style.flags.for_cascade_inputs(), }; resolver .cascade_style_and_visited_with_default_parents(inputs) diff --git a/components/style/context.rs b/components/style/context.rs index 633479c5a7a..d6b4d2cc102 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -7,6 +7,7 @@ #[cfg(feature = "servo")] use crate::animation::DocumentAnimationSet; use crate::bloom::StyleBloom; +use crate::computed_value_flags::ComputedValueFlags; use crate::data::{EagerPseudoStyles, ElementData}; use crate::dom::{SendElement, TElement}; #[cfg(feature = "gecko")] @@ -189,14 +190,18 @@ pub struct CascadeInputs { /// element. A element's "relevant link" is the element being matched if it /// is a link or the nearest ancestor link. pub visited_rules: Option, + + /// The set of flags from container queries that we need for invalidation. + pub flags: ComputedValueFlags, } impl CascadeInputs { /// Construct inputs from previous cascade results, if any. pub fn new_from_style(style: &ComputedValues) -> Self { - CascadeInputs { + Self { rules: style.rules.clone(), visited_rules: style.visited_style().and_then(|v| v.rules.clone()), + flags: style.flags.for_cascade_inputs(), } } } diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index bf1cad4311d..84c2165f759 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -4,6 +4,7 @@ //! Gecko-specific bits for selector-parsing. +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; @@ -236,6 +237,10 @@ pub struct SelectorImpl; pub struct ExtraMatchingData { /// The invalidation data to invalidate doc-state pseudo-classes correctly. pub invalidation_data: InvalidationMatchingData, + + /// The invalidation bits from matching container queries. These are here + /// just for convenience mostly. + pub cascade_input_flags: ComputedValueFlags, } impl ::selectors::SelectorImpl for SelectorImpl { diff --git a/components/style/matching.rs b/components/style/matching.rs index f62c8cbbdfb..5e433efc79e 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -238,6 +238,7 @@ trait PrivateMatchMethods: TElement { let inputs = CascadeInputs { rules: Some(without_transition_rules), visited_rules: primary_style.visited_rules().cloned(), + flags: primary_style.flags.for_cascade_inputs(), }; // Actually `PseudoElementResolution` doesn't really matter. diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index ce928c1c692..18eff8200cc 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -59,6 +59,7 @@ pub fn cascade( parent_style_ignoring_first_line: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, visited_rules: Option<&StrongRuleNode>, + cascade_input_flags: ComputedValueFlags, quirks_mode: QuirksMode, rule_cache: Option<&RuleCache>, rule_cache_conditions: &mut RuleCacheConditions, @@ -76,6 +77,7 @@ where parent_style_ignoring_first_line, layout_parent_style, CascadeMode::Unvisited { visited_rules }, + cascade_input_flags, quirks_mode, rule_cache, rule_cache_conditions, @@ -175,6 +177,7 @@ fn cascade_rules( parent_style_ignoring_first_line: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, cascade_mode: CascadeMode, + cascade_input_flags: ComputedValueFlags, quirks_mode: QuirksMode, rule_cache: Option<&RuleCache>, rule_cache_conditions: &mut RuleCacheConditions, @@ -197,6 +200,7 @@ where parent_style_ignoring_first_line, layout_parent_style, cascade_mode, + cascade_input_flags, quirks_mode, rule_cache, rule_cache_conditions, @@ -232,6 +236,7 @@ pub fn apply_declarations<'a, E, I>( parent_style_ignoring_first_line: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>, cascade_mode: CascadeMode, + cascade_input_flags: ComputedValueFlags, quirks_mode: QuirksMode, rule_cache: Option<&RuleCache>, rule_cache_conditions: &mut RuleCacheConditions, @@ -296,6 +301,8 @@ where container_size_query, ); + context.style().add_flags(cascade_input_flags); + let using_cached_reset_properties; let mut cascade = Cascade::new(&mut context, cascade_mode, &referenced_properties); let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default(); @@ -751,6 +758,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { visited_parent!(parent_style_ignoring_first_line), visited_parent!(layout_parent_style), CascadeMode::Visited { writing_mode }, + // Cascade input flags don't matter for the visited style, they are + // in the main (unvisited) style. + Default::default(), self.context.quirks_mode, // The rule cache doesn't care about caching :visited // styles, we cache the unvisited style instead. We still do diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index 1cfaee084df..056245f35a1 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -102,9 +102,23 @@ bitflags! { /// Whether the style depends on viewport units. const USES_VIEWPORT_UNITS = 1 << 20; + /// Whether the style depends on viewport units on container queries. + /// + /// This needs to be a separate flag from `USES_VIEWPORT_UNITS` because + /// it causes us to re-match the style (rather than re-cascascading it, + /// which is enough for other uses of viewport units). + const USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES = 1 << 21; + /// A flag used to mark styles which have `container-type` of `size` or /// `inline-size`, or under one. - const SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE = 1 << 21; + const SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE = 1 << 22; + } +} + +impl Default for ComputedValueFlags { + #[inline] + fn default() -> Self { + Self::empty() } } @@ -124,7 +138,13 @@ impl ComputedValueFlags { /// Flags that may be propagated to descendants. #[inline] fn maybe_inherited_flags() -> Self { - Self::inherited_flags() | ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK + Self::inherited_flags() | Self::SHOULD_SUPPRESS_LINEBREAK + } + + /// Flags that are an input to the cascade. + #[inline] + fn cascade_input_flags() -> Self { + Self::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES } /// Returns the flags that are always propagated to descendants. @@ -141,4 +161,10 @@ impl ComputedValueFlags { pub fn maybe_inherited(self) -> Self { self & Self::maybe_inherited_flags() } + + /// Flags that are an input to the cascade. + #[inline] + pub fn for_cascade_inputs(self) -> Self { + self & Self::cascade_input_flags() + } } diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index 6331f60cb53..3b7515292bf 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -359,6 +359,7 @@ impl SelectorMap { rule.container_condition_id, stylist, element, + matching_context, ) { continue; } diff --git a/components/style/style_resolver.rs b/components/style/style_resolver.rs index e787e19d176..b768e5ff5db 100644 --- a/components/style/style_resolver.rs +++ b/components/style/style_resolver.rs @@ -5,6 +5,7 @@ //! Style resolution for a given element or pseudo-element. use crate::applicable_declarations::ApplicableDeclarationList; +use crate::computed_value_flags::ComputedValueFlags; use crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext}; use crate::data::{EagerPseudoStyles, ElementStyles}; use crate::dom::TElement; @@ -44,6 +45,7 @@ where struct MatchingResults { rule_node: StrongRuleNode, + flags: ComputedValueFlags, } /// A style returned from the resolver machinery. @@ -133,8 +135,6 @@ fn eager_pseudo_is_definitely_not_generated( pseudo: &PseudoElement, style: &ComputedValues, ) -> bool { - use crate::computed_value_flags::ComputedValueFlags; - if !pseudo.is_before_or_after() { return false; } @@ -204,6 +204,7 @@ where CascadeInputs { rules: Some(primary_results.rule_node), visited_rules, + flags: primary_results.flags, }, parent_style, layout_parent_style, @@ -418,7 +419,7 @@ where originating_element_style: &PrimaryStyle, layout_parent_style: Option<&ComputedValues>, ) -> Option { - let rules = self.match_pseudo( + let MatchingResults { rule_node, mut flags } = self.match_pseudo( originating_element_style.style(), pseudo, VisitedHandlingMode::AllLinksUnvisited, @@ -430,13 +431,17 @@ where originating_element_style.style(), pseudo, VisitedHandlingMode::RelevantLinkVisited, - ); + ).map(|results| { + flags |= results.flags; + results.rule_node + }); } Some(self.cascade_style_and_visited( CascadeInputs { - rules: Some(rules), + rules: Some(rule_node), visited_rules, + flags, }, Some(originating_element_style.style()), layout_parent_style, @@ -493,7 +498,10 @@ where } } - MatchingResults { rule_node } + MatchingResults { + rule_node, + flags: matching_context.extra_data.cascade_input_flags, + } } fn match_pseudo( @@ -501,7 +509,7 @@ where originating_element_style: &ComputedValues, pseudo_element: &PseudoElement, visited_handling: VisitedHandlingMode, - ) -> Option { + ) -> Option { debug!( "Match pseudo {:?} for {:?}, visited: {:?}", self.element, pseudo_element, visited_handling @@ -556,6 +564,9 @@ where .rule_tree() .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards); - Some(rule_node) + Some(MatchingResults { + rule_node, + flags: matching_context.extra_data.cascade_input_flags, + }) } } diff --git a/components/style/stylesheets/container_rule.rs b/components/style/stylesheets/container_rule.rs index 6850fa2f853..f4d652e6e45 100644 --- a/components/style/stylesheets/container_rule.rs +++ b/components/style/stylesheets/container_rule.rs @@ -225,7 +225,12 @@ impl ContainerCondition { } /// Tries to match a container query condition for a given element. - pub(crate) fn matches(&self, device: &Device, element: E) -> bool + pub(crate) fn matches( + &self, + device: &Device, + element: E, + invalidation_flags: &mut ComputedValueFlags, + ) -> bool where E: TElement, { @@ -240,7 +245,15 @@ impl ContainerCondition { device, info, size_query_container_lookup, - |context| self.condition.matches(context), + |context| { + let matches = self.condition.matches(context); + if context.style().flags().contains(ComputedValueFlags::USES_VIEWPORT_UNITS) { + // TODO(emilio): Might need something similar to improve + // invalidation of font relative container-query lengths. + invalidation_flags.insert(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES); + } + matches + }, ) } } diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 8dfdc91e58b..321f879c83a 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -891,6 +891,7 @@ impl Stylist { CascadeInputs { rules: Some(rules), visited_rules: None, + flags: Default::default(), }, pseudo, guards, @@ -1088,6 +1089,7 @@ impl Stylist { parent_style_ignoring_first_line, layout_parent_style, visited_rules, + inputs.flags, self.quirks_mode, rule_cache, rule_cache_conditions, @@ -1187,6 +1189,7 @@ impl Stylist { Some(CascadeInputs { rules: Some(rules), visited_rules, + flags: matching_context.extra_data.cascade_input_flags, }) } @@ -1501,6 +1504,7 @@ impl Stylist { CascadeMode::Unvisited { visited_rules: None, }, + Default::default(), self.quirks_mode, /* rule_cache = */ None, &mut Default::default(), @@ -2365,6 +2369,7 @@ impl CascadeData { mut id: ContainerConditionId, stylist: &Stylist, element: E, + context: &mut MatchingContext, ) -> bool where E: TElement, @@ -2375,7 +2380,7 @@ impl CascadeData { None => return true, Some(ref c) => c, }; - if !condition.matches(stylist.device(), element) { + if !condition.matches(stylist.device(), element, &mut context.extra_data.cascade_input_flags) { return false; } id = condition_ref.parent;