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
This commit is contained in:
Emilio Cobos Álvarez 2022-10-20 14:24:36 +00:00 committed by Martin Robinson
parent bfa293c5c5
commit 5ded58a2b1
10 changed files with 92 additions and 14 deletions

View file

@ -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)

View file

@ -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<StrongRuleNode>,
/// 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(),
}
}
}

View file

@ -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 {

View file

@ -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.

View file

@ -59,6 +59,7 @@ pub fn cascade<E>(
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<E>(
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

View file

@ -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()
}
}

View file

@ -359,6 +359,7 @@ impl SelectorMap<Rule> {
rule.container_condition_id,
stylist,
element,
matching_context,
) {
continue;
}

View file

@ -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<ResolvedStyle> {
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<StrongRuleNode> {
) -> Option<MatchingResults> {
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,
})
}
}

View file

@ -225,7 +225,12 @@ impl ContainerCondition {
}
/// Tries to match a container query condition for a given element.
pub(crate) fn matches<E>(&self, device: &Device, element: E) -> bool
pub(crate) fn matches<E>(
&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
},
)
}
}

View file

@ -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<E::Impl>,
) -> 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;