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
/// 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