mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
parent
c511b9c3f9
commit
661e7d5c48
5 changed files with 129 additions and 50 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
None => return TraversalResult::InProgress,
|
||||
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,
|
||||
};
|
||||
data.styles.primary()
|
||||
},
|
||||
};
|
||||
let style = 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,
|
||||
None => return TraversalResult::InProgress,
|
||||
let data;
|
||||
let style = match originating_element_style {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
data = match e.borrow_data() {
|
||||
Some(d) => d,
|
||||
None => return TraversalResult::InProgress,
|
||||
};
|
||||
data.styles.primary()
|
||||
},
|
||||
};
|
||||
|
||||
let style = 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,
|
||||
{
|
||||
// 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)
|
||||
},
|
||||
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)));
|
||||
}
|
||||
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.
|
||||
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, 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()
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue