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
This commit is contained in:
Emilio Cobos Álvarez 2022-09-13 17:08:46 +00:00 committed by Martin Robinson
parent 00c9d9d033
commit f9f5283a65
2 changed files with 78 additions and 60 deletions

View file

@ -50,10 +50,11 @@ pub enum StyleChange {
}, },
} }
/// Whether or not newly computed values for an element need to be cascade /// Whether or not newly computed values for an element need to be cascaded to
/// to children. /// children (or children might need to be re-matched, e.g., for container
/// queries).
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[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 /// 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 /// we won't bother recomputing style for children, so we can skip cascading
/// the new values into child elements. /// 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 /// used to handle root font-size updates needing to recascade the whole
/// document. /// document.
MustCascadeDescendants = 3, 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. /// Whether we can unconditionally skip the cascade.
pub fn can_skip_cascade(&self) -> bool { 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, old_values: &ComputedValues,
new_values: &ComputedValues, new_values: &ComputedValues,
pseudo: Option<&PseudoElement>, pseudo: Option<&PseudoElement>,
) -> ChildCascadeRequirement { ) -> ChildRestyleRequirement {
debug!("accumulate_damage_for: {:?}", self); debug!("accumulate_damage_for: {:?}", self);
debug_assert!(!shared_context debug_assert!(!shared_context
.traversal_flags .traversal_flags
@ -750,16 +755,16 @@ trait PrivateMatchMethods: TElement {
" > flags changed: {:?} != {:?}", " > flags changed: {:?} != {:?}",
old_values.flags, new_values.flags old_values.flags, new_values.flags
); );
return ChildCascadeRequirement::MustCascadeChildren; return ChildRestyleRequirement::MustCascadeChildren;
} }
match difference.change { match difference.change {
StyleChange::Unchanged => return ChildCascadeRequirement::CanSkipCascade, StyleChange::Unchanged => return ChildRestyleRequirement::CanSkipCascade,
StyleChange::Changed { reset_only } => { StyleChange::Changed { reset_only } => {
// If inherited properties changed, the best we can do is // If inherited properties changed, the best we can do is
// cascade the children. // cascade the children.
if !reset_only { 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 // If we used to be a display: none element, and no longer are, our
// children need to be restyled because they're unstyled. // children need to be restyled because they're unstyled.
if old_display == Display::None { if old_display == Display::None {
return ChildCascadeRequirement::MustCascadeChildren; return ChildRestyleRequirement::MustCascadeChildren;
} }
// Blockification of children may depend on our display value, // Blockification of children may depend on our display value,
// so we need to actually do the recascade. We could potentially // so we need to actually do the recascade. We could potentially
// do better, but it doesn't seem worth it. // do better, but it doesn't seem worth it.
if old_display.is_item_container() != new_display.is_item_container() { 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 // We may also need to blockify and un-blockify descendants if our
// display goes from / to display: contents, since the "layout // display goes from / to display: contents, since the "layout
// parent style" changes. // parent style" changes.
if old_display.is_contents() || new_display.is_contents() { 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 // Line break suppression may also be affected if the display
// type changes from ruby to non-ruby. // type changes from ruby to non-ruby.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
{ {
if old_display.is_ruby_type() != new_display.is_ruby_type() { 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); let is_legacy_justify_items = new_justify_items.computed.0.contains(AlignFlags::LEGACY);
if is_legacy_justify_items != was_legacy_justify_items { 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 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 // We may need to set or propagate the CAN_BE_FRAGMENTED bit
// on our children. // on our children.
if old_values.is_multicol() != new_values.is_multicol() { 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 // We could prove that, if our children don't inherit reset
// properties, we can stop the cascade. // properties, we can stop the cascade.
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle
} }
} }
@ -880,7 +885,7 @@ pub trait MatchMethods: TElement {
data: &mut ElementData, data: &mut ElementData,
mut new_styles: ResolvedElementStyles, mut new_styles: ResolvedElementStyles,
important_rules_changed: bool, important_rules_changed: bool,
) -> ChildCascadeRequirement { ) -> ChildRestyleRequirement {
use std::cmp; use std::cmp;
self.process_animations( self.process_animations(
@ -896,26 +901,37 @@ pub trait MatchMethods: TElement {
let new_primary_style = data.styles.primary.as_ref().unwrap(); let new_primary_style = data.styles.primary.as_ref().unwrap();
let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade; let mut restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
if new_primary_style let is_root = new_primary_style.flags.contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);
.flags let is_container = !new_primary_style.get_box().clone_container_type().is_empty();
.contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE) if is_root || is_container {
{
let device = context.shared.stylist.device();
let new_font_size = new_primary_style.get_font().clone_font_size(); let new_font_size = new_primary_style.get_font().clone_font_size();
let old_font_size = old_styles
if old_styles
.primary .primary
.as_ref() .as_ref()
.map_or(true, |s| s.get_font().clone_font_size() != new_font_size) .map(|s| s.get_font().clone_font_size());
{
debug_assert!(self.owner_doc_matches_for_testing(device)); if old_font_size != Some(new_font_size) {
device.set_root_font_size(new_font_size.size().into()); if is_root {
// If the root font-size changed since last time, and something let device = context.shared.stylist.device();
// in the document did use rem units, ensure we recascade the debug_assert!(self.owner_doc_matches_for_testing(device));
// entire tree. device.set_root_font_size(new_font_size.size().into());
if device.used_root_font_size() { if device.used_root_font_size() {
cascade_requirement = ChildCascadeRequirement::MustCascadeDescendants; // 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, // changes and before we reach our children the cascade stops,
// but we don't track right now whether we use the document body // but we don't track right now whether we use the document body
// color, and nobody else handles that properly anyway. // color, and nobody else handles that properly anyway.
let device = context.shared.stylist.device(); let device = context.shared.stylist.device();
// Needed for the "inherit from body" quirk. // Needed for the "inherit from body" quirk.
@ -941,17 +956,17 @@ pub trait MatchMethods: TElement {
.traversal_flags .traversal_flags
.contains(TraversalFlags::FinalAnimationTraversal) .contains(TraversalFlags::FinalAnimationTraversal)
{ {
return ChildCascadeRequirement::MustCascadeChildren; return ChildRestyleRequirement::MustCascadeChildren;
} }
// Also, don't do anything if there was no style. // Also, don't do anything if there was no style.
let old_primary_style = match old_styles.primary { let old_primary_style = match old_styles.primary {
Some(s) => s, Some(s) => s,
None => return ChildCascadeRequirement::MustCascadeChildren, None => return ChildRestyleRequirement::MustCascadeChildren,
}; };
cascade_requirement = cmp::max( restyle_requirement = cmp::max(
cascade_requirement, restyle_requirement,
self.accumulate_damage_for( self.accumulate_damage_for(
context.shared, context.shared,
&mut data.damage, &mut data.damage,
@ -963,7 +978,7 @@ pub trait MatchMethods: TElement {
if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() { if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {
// This is the common case; no need to examine pseudos here. // This is the common case; no need to examine pseudos here.
return cascade_requirement; return restyle_requirement;
} }
let pseudo_styles = old_styles let pseudo_styles = old_styles
@ -996,13 +1011,13 @@ pub trait MatchMethods: TElement {
old.as_ref().map_or(false, |s| pseudo.should_exist(s)); old.as_ref().map_or(false, |s| pseudo.should_exist(s));
if new_pseudo_should_exist != old_pseudo_should_exist { if new_pseudo_should_exist != old_pseudo_should_exist {
data.damage |= RestyleDamage::reconstruct(); 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 /// Updates the rule nodes without re-running selector matching, using just

View file

@ -8,7 +8,7 @@ use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext};
use crate::data::{ElementData, ElementStyles}; use crate::data::{ElementData, ElementStyles};
use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode}; use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode};
use crate::invalidation::element::restyle_hints::RestyleHint; use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::matching::{ChildCascadeRequirement, MatchMethods}; use crate::matching::{ChildRestyleRequirement, MatchMethods};
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use crate::sharing::StyleSharingTarget; use crate::sharing::StyleSharingTarget;
use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement}; use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
@ -423,19 +423,19 @@ pub fn recalc_style_at<E, D, F>(
data data
); );
let mut child_cascade_requirement = ChildCascadeRequirement::CanSkipCascade; let mut child_restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
// Compute style for this element if necessary. // Compute style for this element if necessary.
if compute_self { 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() { if element.is_in_native_anonymous_subtree() {
// We must always cascade native anonymous subtrees, since they // We must always cascade native anonymous subtrees, since they
// may have pseudo-elements underneath that would inherit from the // may have pseudo-elements underneath that would inherit from the
// closest non-NAC ancestor instead of us. // closest non-NAC ancestor instead of us.
child_cascade_requirement = cmp::max( child_restyle_requirement = cmp::max(
child_cascade_requirement, child_restyle_requirement,
ChildCascadeRequirement::MustCascadeChildren, ChildRestyleRequirement::MustCascadeChildren,
); );
} }
@ -472,10 +472,10 @@ pub fn recalc_style_at<E, D, F>(
let propagated_hint = data.hint.propagate(&flags); let propagated_hint = data.hint.propagate(&flags);
trace!( trace!(
"propagated_hint={:?}, cascade_requirement={:?}, \ "propagated_hint={:?}, restyle_requirement={:?}, \
is_display_none={:?}, implementing_pseudo={:?}", is_display_none={:?}, implementing_pseudo={:?}",
propagated_hint, propagated_hint,
child_cascade_requirement, child_restyle_requirement,
data.styles.is_display_none(), data.styles.is_display_none(),
element.implemented_pseudo_element() element.implemented_pseudo_element()
); );
@ -504,7 +504,7 @@ pub fn recalc_style_at<E, D, F>(
// it's useless to style children. // it's useless to style children.
let mut traverse_children = has_dirty_descendants_for_this_restyle || let mut traverse_children = has_dirty_descendants_for_this_restyle ||
!propagated_hint.is_empty() || !propagated_hint.is_empty() ||
!child_cascade_requirement.can_skip_cascade() || !child_restyle_requirement.can_skip_cascade() ||
is_servo_nonincremental_layout(); is_servo_nonincremental_layout();
traverse_children = traverse_children && !data.styles.is_display_none(); traverse_children = traverse_children && !data.styles.is_display_none();
@ -516,7 +516,7 @@ pub fn recalc_style_at<E, D, F>(
element, element,
data, data,
propagated_hint, propagated_hint,
child_cascade_requirement, child_restyle_requirement,
is_initial_style, is_initial_style,
note_child, note_child,
); );
@ -549,7 +549,7 @@ fn compute_style<E>(
context: &mut StyleContext<E>, context: &mut StyleContext<E>,
element: E, element: E,
data: &mut ElementData, data: &mut ElementData,
) -> ChildCascadeRequirement ) -> ChildRestyleRequirement
where where
E: TElement, E: TElement,
{ {
@ -734,7 +734,7 @@ fn note_children<E, D, F>(
element: E, element: E,
data: &ElementData, data: &ElementData,
propagated_hint: RestyleHint, propagated_hint: RestyleHint,
cascade_requirement: ChildCascadeRequirement, restyle_requirement: ChildRestyleRequirement,
is_initial_style: bool, is_initial_style: bool,
mut note_child: F, mut note_child: F,
) where ) where
@ -771,12 +771,12 @@ fn note_children<E, D, F>(
if let Some(ref mut child_data) = child_data { if let Some(ref mut child_data) = child_data {
let mut child_hint = propagated_hint; let mut child_hint = propagated_hint;
match cascade_requirement { match restyle_requirement {
ChildCascadeRequirement::CanSkipCascade => {}, ChildRestyleRequirement::CanSkipCascade => {},
ChildCascadeRequirement::MustCascadeDescendants => { ChildRestyleRequirement::MustCascadeDescendants => {
child_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS; child_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
}, },
ChildCascadeRequirement::MustCascadeChildrenIfInheritResetStyle => { ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle => {
use crate::computed_value_flags::ComputedValueFlags; use crate::computed_value_flags::ComputedValueFlags;
if child_data if child_data
.styles .styles
@ -787,9 +787,12 @@ fn note_children<E, D, F>(
child_hint |= RestyleHint::RECASCADE_SELF; child_hint |= RestyleHint::RECASCADE_SELF;
} }
}, },
ChildCascadeRequirement::MustCascadeChildren => { ChildRestyleRequirement::MustCascadeChildren => {
child_hint |= RestyleHint::RECASCADE_SELF; child_hint |= RestyleHint::RECASCADE_SELF;
}, },
ChildRestyleRequirement::MustMatchDescendants => {
child_hint |= RestyleHint::restyle_subtree();
}
} }
child_data.hint.insert(child_hint); child_data.hint.insert(child_hint);