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::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::element::document_state::InvalidationMatchingData;
use crate::properties::ComputedValues;
use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
use crate::str::starts_with_ignore_ascii_case;
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
@ -17,6 +18,7 @@ use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
use dom::{DocumentState, ElementState};
use selectors::parser::SelectorParseErrorKind;
use selectors::SelectorList;
use servo_arc::Arc;
use std::fmt;
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
/// selector-matching or invalidation.
#[derive(Debug, Default)]
#[derive(Default)]
pub struct ExtraMatchingData {
/// The invalidation data to invalidate doc-state pseudo-classes correctly.
pub invalidation_data: InvalidationMatchingData,
@ -241,6 +243,10 @@ pub struct ExtraMatchingData {
/// The invalidation bits from matching container queries. These are here
/// just for convenience mostly.
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 {

View file

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

View file

@ -135,15 +135,26 @@ enum TraversalResult<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
E: TElement,
F: Fn(E) -> TraversalResult<R>,
F: Fn(E, Option<&Arc<ComputedValues>>) -> TraversalResult<R>,
{
while let Some(element) = e.traversal_parent() {
match evaluator(element) {
if originating_element_style.is_some() {
match evaluator(e, originating_element_style) {
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)),
}
e = element;
@ -174,15 +185,22 @@ impl ContainerCondition {
fn valid_container_info<E>(
&self,
potential_container: E,
originating_element_style: Option<&Arc<ComputedValues>>,
) -> TraversalResult<ContainerLookupResult<E>>
where
E: TElement,
{
let data = match potential_container.borrow_data() {
Some(data) => data,
let data;
let style = match originating_element_style {
Some(s) => s,
None => {
data = match potential_container.borrow_data() {
Some(d) => d,
None => return TraversalResult::InProgress,
};
let style = data.styles.primary();
data.styles.primary()
},
};
let wm = style.writing_mode;
let box_style = style.get_box();
@ -211,11 +229,21 @@ impl ContainerCondition {
}
/// 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
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),
None => None,
}
@ -226,18 +254,19 @@ impl ContainerCondition {
&self,
device: &Device,
element: E,
originating_element_style: Option<&Arc<ComputedValues>>,
invalidation_flags: &mut ComputedValueFlags,
) -> KleeneValue
where
E: TElement,
{
let result = self.find_container(element);
let result = self.find_container(element, originating_element_style);
let (container, info) = match result {
Some(r) => (Some(r.element), Some((r.info, r.style))),
None => (None, None),
};
// 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(
device,
info,
@ -448,16 +477,24 @@ pub enum 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
E: TElement,
{
let data = match e.borrow_data() {
Some(data) => data,
let data;
let style = match originating_element_style {
Some(s) => s,
None => {
data = match e.borrow_data() {
Some(d) => d,
None => return TraversalResult::InProgress,
};
let style = data.styles.primary();
data.styles.primary()
},
};
if !style
.flags
.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().
fn lookup<E>(element: E) -> ContainerSizeQueryResult
fn lookup<E>(
element: E,
originating_element_style: Option<&Arc<ComputedValues>>,
) -> ContainerSizeQueryResult
where
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)) => {
if result.is_complete() {
result
} else {
// 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(),
@ -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.
pub fn for_element<E>(element: E) -> Self
pub fn for_element<E>(
element: E,
originating_element_style: Option<&'a Arc<ComputedValues>>,
) -> Self
where
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.
if let Some(parent) = element.traversal_parent() {
let should_traverse = match parent.borrow_data() {
Some(data) => {
let style = data.styles.primary();
style
.flags
.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)
parent = match element.traversal_parent() {
Some(parent) => parent,
None => return Self::none(),
};
data = parent.borrow_data();
data.as_ref().map(|data| data.styles.primary())
},
};
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.
};
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()
}
/// 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
E: TElement + 'a,
{
if let Some(e) = element {
Self::for_element(e)
Self::for_element(e, originating_element_style)
} else {
Self::none()
}

View file

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