From f9f5283a65ac22ef5099db264231701ba8216754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 13 Sep 2022 17:08:46 +0000 Subject: [PATCH] style: React to font-size changes on query containers Much like we react to font-size changes on the root. Differential Revision: https://phabricator.services.mozilla.com/D157173 --- components/style/matching.rs | 101 +++++++++++++++++++--------------- components/style/traversal.rs | 37 +++++++------ 2 files changed, 78 insertions(+), 60 deletions(-) diff --git a/components/style/matching.rs b/components/style/matching.rs index 1c1da3e05ad..b01c756c5d9 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -50,10 +50,11 @@ pub enum StyleChange { }, } -/// Whether or not newly computed values for an element need to be cascade -/// to children. +/// Whether or not newly computed values for an element need to be cascaded to +/// children (or children might need to be re-matched, e.g., for container +/// queries). #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum ChildCascadeRequirement { +pub enum ChildRestyleRequirement { /// Old and new computed values were the same, or we otherwise know that /// we won't bother recomputing style for children, so we can skip cascading /// the new values into child elements. @@ -68,12 +69,16 @@ pub enum ChildCascadeRequirement { /// used to handle root font-size updates needing to recascade the whole /// document. MustCascadeDescendants = 3, + /// We need to re-match the whole subttree. This is used to handle container + /// query relative unit changes for example. Container query size changes + /// also trigger re-match, but after layout. + MustMatchDescendants = 4, } -impl ChildCascadeRequirement { +impl ChildRestyleRequirement { /// Whether we can unconditionally skip the cascade. pub fn can_skip_cascade(&self) -> bool { - matches!(*self, ChildCascadeRequirement::CanSkipCascade) + matches!(*self, ChildRestyleRequirement::CanSkipCascade) } } @@ -731,7 +736,7 @@ trait PrivateMatchMethods: TElement { old_values: &ComputedValues, new_values: &ComputedValues, pseudo: Option<&PseudoElement>, - ) -> ChildCascadeRequirement { + ) -> ChildRestyleRequirement { debug!("accumulate_damage_for: {:?}", self); debug_assert!(!shared_context .traversal_flags @@ -750,16 +755,16 @@ trait PrivateMatchMethods: TElement { " > flags changed: {:?} != {:?}", old_values.flags, new_values.flags ); - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } match difference.change { - StyleChange::Unchanged => return ChildCascadeRequirement::CanSkipCascade, + StyleChange::Unchanged => return ChildRestyleRequirement::CanSkipCascade, StyleChange::Changed { reset_only } => { // If inherited properties changed, the best we can do is // cascade the children. if !reset_only { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } }, } @@ -771,26 +776,26 @@ trait PrivateMatchMethods: TElement { // If we used to be a display: none element, and no longer are, our // children need to be restyled because they're unstyled. if old_display == Display::None { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } // Blockification of children may depend on our display value, // so we need to actually do the recascade. We could potentially // do better, but it doesn't seem worth it. if old_display.is_item_container() != new_display.is_item_container() { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } // We may also need to blockify and un-blockify descendants if our // display goes from / to display: contents, since the "layout // parent style" changes. if old_display.is_contents() || new_display.is_contents() { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } // Line break suppression may also be affected if the display // type changes from ruby to non-ruby. #[cfg(feature = "gecko")] { if old_display.is_ruby_type() != new_display.is_ruby_type() { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } } } @@ -813,12 +818,12 @@ trait PrivateMatchMethods: TElement { let is_legacy_justify_items = new_justify_items.computed.0.contains(AlignFlags::LEGACY); if is_legacy_justify_items != was_legacy_justify_items { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } } @@ -827,13 +832,13 @@ trait PrivateMatchMethods: TElement { // We may need to set or propagate the CAN_BE_FRAGMENTED bit // on our children. if old_values.is_multicol() != new_values.is_multicol() { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } } // We could prove that, if our children don't inherit reset // properties, we can stop the cascade. - ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle + ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle } } @@ -880,7 +885,7 @@ pub trait MatchMethods: TElement { data: &mut ElementData, mut new_styles: ResolvedElementStyles, important_rules_changed: bool, - ) -> ChildCascadeRequirement { + ) -> ChildRestyleRequirement { use std::cmp; self.process_animations( @@ -896,26 +901,37 @@ pub trait MatchMethods: TElement { let new_primary_style = data.styles.primary.as_ref().unwrap(); - let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade; - if new_primary_style - .flags - .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE) - { - let device = context.shared.stylist.device(); + let mut restyle_requirement = ChildRestyleRequirement::CanSkipCascade; + let is_root = new_primary_style.flags.contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE); + let is_container = !new_primary_style.get_box().clone_container_type().is_empty(); + if is_root || is_container { let new_font_size = new_primary_style.get_font().clone_font_size(); - - if old_styles + let old_font_size = old_styles .primary .as_ref() - .map_or(true, |s| s.get_font().clone_font_size() != new_font_size) - { - debug_assert!(self.owner_doc_matches_for_testing(device)); - device.set_root_font_size(new_font_size.size().into()); - // If the root font-size changed since last time, and something - // in the document did use rem units, ensure we recascade the - // entire tree. - if device.used_root_font_size() { - cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants; + .map(|s| s.get_font().clone_font_size()); + + if old_font_size != Some(new_font_size) { + if is_root { + let device = context.shared.stylist.device(); + debug_assert!(self.owner_doc_matches_for_testing(device)); + device.set_root_font_size(new_font_size.size().into()); + if device.used_root_font_size() { + // If the root font-size changed since last time, and something + // in the document did use rem units, ensure we recascade the + // entire tree. + restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants; + } + } + + if is_container && old_font_size.is_some() { + // TODO(emilio): Maybe only do this if we were matched + // against relative font sizes? + // Also, maybe we should do this as well for font-family / + // etc changes (for ex/ch/ic units to work correctly)? We + // should probably do the optimization mentioned above if + // so. + restyle_requirement = ChildRestyleRequirement::MustMatchDescendants; } } } @@ -926,7 +942,6 @@ pub trait MatchMethods: TElement { // changes and before we reach our children the cascade stops, // but we don't track right now whether we use the document body // color, and nobody else handles that properly anyway. - let device = context.shared.stylist.device(); // Needed for the "inherit from body" quirk. @@ -941,17 +956,17 @@ pub trait MatchMethods: TElement { .traversal_flags .contains(TraversalFlags::FinalAnimationTraversal) { - return ChildCascadeRequirement::MustCascadeChildren; + return ChildRestyleRequirement::MustCascadeChildren; } // Also, don't do anything if there was no style. let old_primary_style = match old_styles.primary { Some(s) => s, - None => return ChildCascadeRequirement::MustCascadeChildren, + None => return ChildRestyleRequirement::MustCascadeChildren, }; - cascade_requirement = cmp::max( - cascade_requirement, + restyle_requirement = cmp::max( + restyle_requirement, self.accumulate_damage_for( context.shared, &mut data.damage, @@ -963,7 +978,7 @@ pub trait MatchMethods: TElement { if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() { // This is the common case; no need to examine pseudos here. - return cascade_requirement; + return restyle_requirement; } let pseudo_styles = old_styles @@ -996,13 +1011,13 @@ pub trait MatchMethods: TElement { old.as_ref().map_or(false, |s| pseudo.should_exist(s)); if new_pseudo_should_exist != old_pseudo_should_exist { data.damage |= RestyleDamage::reconstruct(); - return cascade_requirement; + return restyle_requirement; } }, } } - cascade_requirement + restyle_requirement } /// Updates the rule nodes without re-running selector matching, using just diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 6637c97b57d..9ccd67ce6bf 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -8,7 +8,7 @@ use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext}; use crate::data::{ElementData, ElementStyles}; use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode}; use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::matching::{ChildCascadeRequirement, MatchMethods}; +use crate::matching::{ChildRestyleRequirement, MatchMethods}; use crate::selector_parser::PseudoElement; use crate::sharing::StyleSharingTarget; use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement}; @@ -423,19 +423,19 @@ pub fn recalc_style_at( data ); - let mut child_cascade_requirement = ChildCascadeRequirement::CanSkipCascade; + let mut child_restyle_requirement = ChildRestyleRequirement::CanSkipCascade; // Compute style for this element if necessary. if compute_self { - child_cascade_requirement = compute_style(traversal_data, context, element, data); + child_restyle_requirement = compute_style(traversal_data, context, element, data); if element.is_in_native_anonymous_subtree() { // We must always cascade native anonymous subtrees, since they // may have pseudo-elements underneath that would inherit from the // closest non-NAC ancestor instead of us. - child_cascade_requirement = cmp::max( - child_cascade_requirement, - ChildCascadeRequirement::MustCascadeChildren, + child_restyle_requirement = cmp::max( + child_restyle_requirement, + ChildRestyleRequirement::MustCascadeChildren, ); } @@ -472,10 +472,10 @@ pub fn recalc_style_at( let propagated_hint = data.hint.propagate(&flags); trace!( - "propagated_hint={:?}, cascade_requirement={:?}, \ + "propagated_hint={:?}, restyle_requirement={:?}, \ is_display_none={:?}, implementing_pseudo={:?}", propagated_hint, - child_cascade_requirement, + child_restyle_requirement, data.styles.is_display_none(), element.implemented_pseudo_element() ); @@ -504,7 +504,7 @@ pub fn recalc_style_at( // it's useless to style children. let mut traverse_children = has_dirty_descendants_for_this_restyle || !propagated_hint.is_empty() || - !child_cascade_requirement.can_skip_cascade() || + !child_restyle_requirement.can_skip_cascade() || is_servo_nonincremental_layout(); traverse_children = traverse_children && !data.styles.is_display_none(); @@ -516,7 +516,7 @@ pub fn recalc_style_at( element, data, propagated_hint, - child_cascade_requirement, + child_restyle_requirement, is_initial_style, note_child, ); @@ -549,7 +549,7 @@ fn compute_style( context: &mut StyleContext, element: E, data: &mut ElementData, -) -> ChildCascadeRequirement +) -> ChildRestyleRequirement where E: TElement, { @@ -734,7 +734,7 @@ fn note_children( element: E, data: &ElementData, propagated_hint: RestyleHint, - cascade_requirement: ChildCascadeRequirement, + restyle_requirement: ChildRestyleRequirement, is_initial_style: bool, mut note_child: F, ) where @@ -771,12 +771,12 @@ fn note_children( if let Some(ref mut child_data) = child_data { let mut child_hint = propagated_hint; - match cascade_requirement { - ChildCascadeRequirement::CanSkipCascade => {}, - ChildCascadeRequirement::MustCascadeDescendants => { + match restyle_requirement { + ChildRestyleRequirement::CanSkipCascade => {}, + ChildRestyleRequirement::MustCascadeDescendants => { child_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS; }, - ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle => { + ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle => { use crate::computed_value_flags::ComputedValueFlags; if child_data .styles @@ -787,9 +787,12 @@ fn note_children( child_hint |= RestyleHint::RECASCADE_SELF; } }, - ChildCascadeRequirement::MustCascadeChildren => { + ChildRestyleRequirement::MustCascadeChildren => { child_hint |= RestyleHint::RECASCADE_SELF; }, + ChildRestyleRequirement::MustMatchDescendants => { + child_hint |= RestyleHint::restyle_subtree(); + } } child_data.hint.insert(child_hint);