style: Fix elements not being query containers for the pseudo-element they originate. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D164807
This commit is contained in:
Oriol Brufau 2022-12-17 02:04:02 +00:00 committed by Martin Robinson
parent c511b9c3f9
commit 661e7d5c48
5 changed files with 129 additions and 50 deletions

View file

@ -8,6 +8,7 @@ use crate::computed_value_flags::ComputedValueFlags;
use crate::gecko_bindings::structs::RawServoSelectorList; use crate::gecko_bindings::structs::RawServoSelectorList;
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::element::document_state::InvalidationMatchingData; use crate::invalidation::element::document_state::InvalidationMatchingData;
use crate::properties::ComputedValues;
use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser}; use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
use crate::str::starts_with_ignore_ascii_case; use crate::str::starts_with_ignore_ascii_case;
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
@ -17,6 +18,7 @@ use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
use dom::{DocumentState, ElementState}; use dom::{DocumentState, ElementState};
use selectors::parser::SelectorParseErrorKind; use selectors::parser::SelectorParseErrorKind;
use selectors::SelectorList; use selectors::SelectorList;
use servo_arc::Arc;
use std::fmt; use std::fmt;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
@ -233,7 +235,7 @@ pub struct SelectorImpl;
/// A set of extra data to carry along with the matching context, either for /// A set of extra data to carry along with the matching context, either for
/// selector-matching or invalidation. /// selector-matching or invalidation.
#[derive(Debug, Default)] #[derive(Default)]
pub struct ExtraMatchingData { pub struct ExtraMatchingData {
/// The invalidation data to invalidate doc-state pseudo-classes correctly. /// The invalidation data to invalidate doc-state pseudo-classes correctly.
pub invalidation_data: InvalidationMatchingData, pub invalidation_data: InvalidationMatchingData,
@ -241,6 +243,10 @@ pub struct ExtraMatchingData {
/// The invalidation bits from matching container queries. These are here /// The invalidation bits from matching container queries. These are here
/// just for convenience mostly. /// just for convenience mostly.
pub cascade_input_flags: ComputedValueFlags, pub cascade_input_flags: ComputedValueFlags,
/// The style of the originating element in order to evaluate @container
/// size queries affecting pseudo-elements.
pub originating_element_style: Option<Arc<ComputedValues>>,
} }
impl ::selectors::SelectorImpl for SelectorImpl { impl ::selectors::SelectorImpl for SelectorImpl {

View file

@ -281,7 +281,7 @@ where
}; };
let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root()); let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
let container_size_query = ContainerSizeQuery::for_option_element(element); let container_size_query = ContainerSizeQuery::for_option_element(element, None);
let mut context = computed::Context::new( let mut context = computed::Context::new(
// We'd really like to own the rules here to avoid refcount traffic, but // We'd really like to own the rules here to avoid refcount traffic, but

View file

@ -420,7 +420,7 @@ where
layout_parent_style: Option<&ComputedValues>, layout_parent_style: Option<&ComputedValues>,
) -> Option<ResolvedStyle> { ) -> Option<ResolvedStyle> {
let MatchingResults { rule_node, mut flags } = self.match_pseudo( let MatchingResults { rule_node, mut flags } = self.match_pseudo(
originating_element_style.style(), &originating_element_style.style.0,
pseudo, pseudo,
VisitedHandlingMode::AllLinksUnvisited, VisitedHandlingMode::AllLinksUnvisited,
)?; )?;
@ -428,7 +428,7 @@ where
let mut visited_rules = None; let mut visited_rules = None;
if originating_element_style.style().visited_style().is_some() { if originating_element_style.style().visited_style().is_some() {
visited_rules = self.match_pseudo( visited_rules = self.match_pseudo(
originating_element_style.style(), &originating_element_style.style.0,
pseudo, pseudo,
VisitedHandlingMode::RelevantLinkVisited, VisitedHandlingMode::RelevantLinkVisited,
).map(|results| { ).map(|results| {
@ -506,7 +506,7 @@ where
fn match_pseudo( fn match_pseudo(
&mut self, &mut self,
originating_element_style: &ComputedValues, originating_element_style: &Arc<ComputedValues>,
pseudo_element: &PseudoElement, pseudo_element: &PseudoElement,
visited_handling: VisitedHandlingMode, visited_handling: VisitedHandlingMode,
) -> Option<MatchingResults> { ) -> Option<MatchingResults> {
@ -534,7 +534,7 @@ where
let bloom_filter = self.context.thread_local.bloom_filter.filter(); let bloom_filter = self.context.thread_local.bloom_filter.filter();
let nth_index_cache = &mut self.context.thread_local.nth_index_cache; let nth_index_cache = &mut self.context.thread_local.nth_index_cache;
let mut matching_context = MatchingContext::new_for_visited( let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
MatchingMode::ForStatelessPseudoElement, MatchingMode::ForStatelessPseudoElement,
Some(bloom_filter), Some(bloom_filter),
Some(nth_index_cache), Some(nth_index_cache),
@ -542,6 +542,8 @@ where
self.context.shared.quirks_mode(), self.context.shared.quirks_mode(),
NeedsSelectorFlags::Yes, NeedsSelectorFlags::Yes,
); );
matching_context.extra_data.originating_element_style =
Some(originating_element_style.clone());
// NB: We handle animation rules for ::before and ::after when // NB: We handle animation rules for ::before and ::after when
// traversing them. // traversing them.

View file

@ -135,15 +135,26 @@ enum TraversalResult<T> {
Done(T), Done(T),
} }
fn traverse_container<E, F, R>(mut e: E, evaluator: F) -> Option<(E, R)> fn traverse_container<E, F, R>(
mut e: E,
originating_element_style: Option<&Arc<ComputedValues>>,
evaluator: F,
) -> Option<(E, R)>
where where
E: TElement, E: TElement,
F: Fn(E) -> TraversalResult<R>, F: Fn(E, Option<&Arc<ComputedValues>>) -> TraversalResult<R>,
{ {
while let Some(element) = e.traversal_parent() { if originating_element_style.is_some() {
match evaluator(element) { match evaluator(e, originating_element_style) {
TraversalResult::InProgress => {}, TraversalResult::InProgress => {},
TraversalResult::StopTraversal => break, TraversalResult::StopTraversal => return None,
TraversalResult::Done(result) => return Some((e, result)),
}
}
while let Some(element) = e.traversal_parent() {
match evaluator(element, None) {
TraversalResult::InProgress => {},
TraversalResult::StopTraversal => return None,
TraversalResult::Done(result) => return Some((element, result)), TraversalResult::Done(result) => return Some((element, result)),
} }
e = element; e = element;
@ -174,15 +185,22 @@ impl ContainerCondition {
fn valid_container_info<E>( fn valid_container_info<E>(
&self, &self,
potential_container: E, potential_container: E,
originating_element_style: Option<&Arc<ComputedValues>>,
) -> TraversalResult<ContainerLookupResult<E>> ) -> TraversalResult<ContainerLookupResult<E>>
where where
E: TElement, E: TElement,
{ {
let data = match potential_container.borrow_data() { let data;
Some(data) => data, let style = match originating_element_style {
Some(s) => s,
None => {
data = match potential_container.borrow_data() {
Some(d) => d,
None => return TraversalResult::InProgress, None => return TraversalResult::InProgress,
}; };
let style = data.styles.primary(); data.styles.primary()
},
};
let wm = style.writing_mode; let wm = style.writing_mode;
let box_style = style.get_box(); let box_style = style.get_box();
@ -211,11 +229,21 @@ impl ContainerCondition {
} }
/// Performs container lookup for a given element. /// Performs container lookup for a given element.
pub fn find_container<E>(&self, e: E) -> Option<ContainerLookupResult<E>> pub fn find_container<E>(
&self,
e: E,
originating_element_style: Option<&Arc<ComputedValues>>,
) -> Option<ContainerLookupResult<E>>
where where
E: TElement, E: TElement,
{ {
match traverse_container(e, |element| self.valid_container_info(element)) { match traverse_container(
e,
originating_element_style,
|element, originating_element_style| {
self.valid_container_info(element, originating_element_style)
},
) {
Some((_, result)) => Some(result), Some((_, result)) => Some(result),
None => None, None => None,
} }
@ -226,18 +254,19 @@ impl ContainerCondition {
&self, &self,
device: &Device, device: &Device,
element: E, element: E,
originating_element_style: Option<&Arc<ComputedValues>>,
invalidation_flags: &mut ComputedValueFlags, invalidation_flags: &mut ComputedValueFlags,
) -> KleeneValue ) -> KleeneValue
where where
E: TElement, E: TElement,
{ {
let result = self.find_container(element); let result = self.find_container(element, originating_element_style);
let (container, info) = match result { let (container, info) = match result {
Some(r) => (Some(r.element), Some((r.info, r.style))), Some(r) => (Some(r.element), Some((r.info, r.style))),
None => (None, None), None => (None, None),
}; };
// Set up the lookup for the container in question, as the condition may be using container query lengths. // Set up the lookup for the container in question, as the condition may be using container query lengths.
let size_query_container_lookup = ContainerSizeQuery::for_option_element(container); let size_query_container_lookup = ContainerSizeQuery::for_option_element(container, None);
Context::for_container_query_evaluation( Context::for_container_query_evaluation(
device, device,
info, info,
@ -448,16 +477,24 @@ pub enum ContainerSizeQuery<'a> {
} }
impl<'a> ContainerSizeQuery<'a> { impl<'a> ContainerSizeQuery<'a> {
fn evaluate_potential_size_container<E>(e: E) -> TraversalResult<ContainerSizeQueryResult> fn evaluate_potential_size_container<E>(
e: E,
originating_element_style: Option<&Arc<ComputedValues>>,
) -> TraversalResult<ContainerSizeQueryResult>
where where
E: TElement, E: TElement,
{ {
let data = match e.borrow_data() { let data;
Some(data) => data, let style = match originating_element_style {
Some(s) => s,
None => {
data = match e.borrow_data() {
Some(d) => d,
None => return TraversalResult::InProgress, None => return TraversalResult::InProgress,
}; };
data.styles.primary()
let style = data.styles.primary(); },
};
if !style if !style
.flags .flags
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE) .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
@ -494,17 +531,26 @@ impl<'a> ContainerSizeQuery<'a> {
} }
/// Find the query container size for a given element. Meant to be used as a callback for new(). /// Find the query container size for a given element. Meant to be used as a callback for new().
fn lookup<E>(element: E) -> ContainerSizeQueryResult fn lookup<E>(
element: E,
originating_element_style: Option<&Arc<ComputedValues>>,
) -> ContainerSizeQueryResult
where where
E: TElement + 'a, E: TElement + 'a,
{ {
match traverse_container(element, |e| Self::evaluate_potential_size_container(e)) { match traverse_container(
element,
originating_element_style,
|e, originating_element_style| {
Self::evaluate_potential_size_container(e, originating_element_style)
},
) {
Some((container, result)) => { Some((container, result)) => {
if result.is_complete() { if result.is_complete() {
result result
} else { } else {
// Traverse up from the found size container to see if we can get a complete containment. // Traverse up from the found size container to see if we can get a complete containment.
result.merge(Self::lookup(container)) result.merge(Self::lookup(container, None))
} }
}, },
None => ContainerSizeQueryResult::default(), None => ContainerSizeQueryResult::default(),
@ -512,35 +558,51 @@ impl<'a> ContainerSizeQuery<'a> {
} }
/// Create a new instance of the container size query for given element, with a deferred lookup callback. /// Create a new instance of the container size query for given element, with a deferred lookup callback.
pub fn for_element<E>(element: E) -> Self pub fn for_element<E>(
element: E,
originating_element_style: Option<&'a Arc<ComputedValues>>,
) -> Self
where where
E: TElement + 'a, E: TElement + 'a,
{ {
let parent;
let data;
let style = match originating_element_style {
Some(s) => Some(s),
None => {
// No need to bother if we're the top element. // No need to bother if we're the top element.
if let Some(parent) = element.traversal_parent() { parent = match element.traversal_parent() {
let should_traverse = match parent.borrow_data() { Some(parent) => parent,
Some(data) => { None => return Self::none(),
let style = data.styles.primary(); };
style data = parent.borrow_data();
.flags data.as_ref().map(|data| data.styles.primary())
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
}, },
};
let should_traverse = match style {
Some(style) => style
.flags
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE),
None => true, // `display: none`, still want to show a correct computed value, so give it a try. None => true, // `display: none`, still want to show a correct computed value, so give it a try.
}; };
if should_traverse { if should_traverse {
return Self::NotEvaluated(Box::new(move || Self::lookup(element))); return Self::NotEvaluated(Box::new(move || {
} Self::lookup(element, originating_element_style)
}));
} }
Self::none() Self::none()
} }
/// Create a new instance, but with optional element. /// Create a new instance, but with optional element.
pub fn for_option_element<E>(element: Option<E>) -> Self pub fn for_option_element<E>(
element: Option<E>,
originating_element_style: Option<&'a Arc<ComputedValues>>,
) -> Self
where where
E: TElement + 'a, E: TElement + 'a,
{ {
if let Some(e) = element { if let Some(e) = element {
Self::for_element(e) Self::for_element(e, originating_element_style)
} else { } else {
Self::none() Self::none()
} }

View file

@ -966,7 +966,7 @@ impl Stylist {
element: E, element: E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
parent_style: &ComputedValues, parent_style: &Arc<ComputedValues>,
is_probe: bool, is_probe: bool,
matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>, matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
) -> Option<Arc<ComputedValues>> ) -> Option<Arc<ComputedValues>>
@ -1105,7 +1105,7 @@ impl Stylist {
&self, &self,
guards: &StylesheetGuards, guards: &StylesheetGuards,
element: E, element: E,
parent_style: &ComputedValues, parent_style: &Arc<ComputedValues>,
pseudo: &PseudoElement, pseudo: &PseudoElement,
is_probe: bool, is_probe: bool,
rule_inclusion: RuleInclusion, rule_inclusion: RuleInclusion,
@ -1125,7 +1125,7 @@ impl Stylist {
}; };
let mut declarations = ApplicableDeclarationList::new(); let mut declarations = ApplicableDeclarationList::new();
let mut matching_context = MatchingContext::new( let mut matching_context = MatchingContext::<'_, E::Impl>::new(
MatchingMode::ForStatelessPseudoElement, MatchingMode::ForStatelessPseudoElement,
None, None,
None, None,
@ -1134,6 +1134,7 @@ impl Stylist {
); );
matching_context.pseudo_element_matching_fn = matching_fn; matching_context.pseudo_element_matching_fn = matching_fn;
matching_context.extra_data.originating_element_style = Some(parent_style.clone());
self.push_applicable_declarations( self.push_applicable_declarations(
element, element,
@ -1155,7 +1156,7 @@ impl Stylist {
let mut visited_rules = None; let mut visited_rules = None;
if parent_style.visited_style().is_some() { if parent_style.visited_style().is_some() {
let mut declarations = ApplicableDeclarationList::new(); let mut declarations = ApplicableDeclarationList::new();
let mut matching_context = MatchingContext::new_for_visited( let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(
MatchingMode::ForStatelessPseudoElement, MatchingMode::ForStatelessPseudoElement,
None, None,
None, None,
@ -1164,6 +1165,7 @@ impl Stylist {
needs_selector_flags, needs_selector_flags,
); );
matching_context.pseudo_element_matching_fn = matching_fn; matching_context.pseudo_element_matching_fn = matching_fn;
matching_context.extra_data.originating_element_style = Some(parent_style.clone());
self.push_applicable_declarations( self.push_applicable_declarations(
element, element,
@ -2380,7 +2382,14 @@ impl CascadeData {
None => return true, None => return true,
Some(ref c) => c, Some(ref c) => c,
}; };
let matches = condition.matches(stylist.device(), element, &mut context.extra_data.cascade_input_flags).to_bool(/* unknown = */ false); let matches = condition
.matches(
stylist.device(),
element,
context.extra_data.originating_element_style.as_ref(),
&mut context.extra_data.cascade_input_flags,
)
.to_bool(/* unknown = */ false);
if !matches { if !matches {
return false; return false;
} }