mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
style: Implement parsing / selector-matching for :is() and :where().
This implements the easy / straight-forward parts of the :where / :is selectors. The biggest missing piece is to handle properly invalidation when there are combinators present inside the :where. That's the hard part of this, actually. But this is probably worth landing in the interim. This fixes some of the visitors that were easy to fix. Differential Revision: https://phabricator.services.mozilla.com/D70788
This commit is contained in:
parent
66f14773c6
commit
83ea321096
10 changed files with 338 additions and 223 deletions
|
@ -15,7 +15,7 @@ use crate::values::serialize_atom_identifier;
|
|||
use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
|
||||
use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
|
||||
use selectors::parser::{self as selector_parser, Selector};
|
||||
use selectors::parser::{SelectorParseErrorKind, Visit};
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use selectors::SelectorList;
|
||||
use std::fmt;
|
||||
|
@ -109,25 +109,6 @@ impl ToCss for NonTSPseudoClass {
|
|||
}
|
||||
}
|
||||
|
||||
impl Visit for NonTSPseudoClass {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where
|
||||
V: SelectorVisitor<Impl = Self::Impl>,
|
||||
{
|
||||
if let NonTSPseudoClass::MozAny(ref selectors) = *self {
|
||||
for selector in selectors.iter() {
|
||||
if !selector.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTSPseudoClass {
|
||||
/// Parses the name and returns a non-ts-pseudo-class if succeeds.
|
||||
/// None otherwise. It doesn't check whether the pseudo-class is enabled
|
||||
|
@ -280,6 +261,21 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
|
|||
fn has_zero_specificity(&self) -> bool {
|
||||
matches!(*self, NonTSPseudoClass::MozNativeAnonymousNoSpecificity)
|
||||
}
|
||||
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where
|
||||
V: SelectorVisitor<Impl = Self::Impl>,
|
||||
{
|
||||
if let NonTSPseudoClass::MozAny(ref selectors) = *self {
|
||||
for selector in selectors.iter() {
|
||||
if !selector.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// The dummy struct we use to implement our selector parsing.
|
||||
|
@ -354,6 +350,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_is_and_where(&self) -> bool {
|
||||
static_prefs::pref!("layout.css.is-where-selectors.enabled")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_part(&self) -> bool {
|
||||
self.chrome_rules_enabled() || static_prefs::pref!("layout.css.shadow-parts.enabled")
|
||||
|
|
|
@ -13,7 +13,7 @@ use fallible::FallibleVec;
|
|||
use hashglobe::FailedAllocationError;
|
||||
use selectors::attr::NamespaceConstraint;
|
||||
use selectors::parser::{Combinator, Component};
|
||||
use selectors::parser::{Selector, SelectorIter, Visit};
|
||||
use selectors::parser::{Selector, SelectorIter};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -322,6 +322,17 @@ impl InvalidationMap {
|
|||
}
|
||||
|
||||
/// A struct that collects invalidations for a given compound selector.
|
||||
///
|
||||
/// FIXME(emilio, bug 1509418): :where is mishandled, figure out the right way
|
||||
/// to do invalidation for :where when combinators are inside.
|
||||
///
|
||||
/// Simplest feasible idea seems to be: For each :where branch, if there are
|
||||
/// combinators in the branch, treat as its own selector (basically, call
|
||||
/// `.note_selector` with the nested selector). That may over-invalidate, but
|
||||
/// not too much. If there are no combinators, then behave like we do today and
|
||||
/// use the outer selector as a whole. If we care a lot about that potential
|
||||
/// over-invalidation where combinators are present then we need more complex
|
||||
/// data-structures in `Dependency`.
|
||||
struct CompoundSelectorDependencyCollector<'a> {
|
||||
/// The state this compound selector is affected by.
|
||||
state: ElementState,
|
||||
|
|
|
@ -500,6 +500,15 @@ fn specific_bucket_for<'a>(component: &'a Component<SelectorImpl>) -> Bucket<'a>
|
|||
// match the slotted <span>.
|
||||
Component::Slotted(ref selector) => find_bucket(selector.iter()),
|
||||
Component::Host(Some(ref selector)) => find_bucket(selector.iter()),
|
||||
Component::Is(ref list) | Component::Where(ref list) => {
|
||||
if list.len() == 1 {
|
||||
find_bucket(list[0].iter())
|
||||
} else {
|
||||
// TODO(emilio): We should handle the multiple element case by
|
||||
// inserting multiple entries in the map.
|
||||
Bucket::Universal
|
||||
}
|
||||
},
|
||||
_ => Bucket::Universal,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
|
|||
use cssparser::{serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation, ToCss};
|
||||
use fxhash::FxHashMap;
|
||||
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
|
||||
use selectors::parser::{SelectorParseErrorKind, Visit};
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
@ -315,8 +315,16 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
|
|||
fn has_zero_specificity(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn visit<V>(&self, _: &mut V) -> bool
|
||||
where
|
||||
V: SelectorVisitor<Impl = Self::Impl>,
|
||||
{
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ToCss for NonTSPseudoClass {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||
where
|
||||
|
@ -352,17 +360,6 @@ impl ToCss for NonTSPseudoClass {
|
|||
}
|
||||
}
|
||||
|
||||
impl Visit for NonTSPseudoClass {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn visit<V>(&self, _: &mut V) -> bool
|
||||
where
|
||||
V: SelectorVisitor<Impl = Self::Impl>,
|
||||
{
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl NonTSPseudoClass {
|
||||
/// Gets a given state flag for this pseudo-class. This is used to do
|
||||
/// selector matching, and it's set from the DOM.
|
||||
|
|
|
@ -42,8 +42,7 @@ use selectors::attr::{CaseSensitivity, NamespaceConstraint};
|
|||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::VisitedHandlingMode;
|
||||
use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||
use selectors::parser::{AncestorHashes, Combinator, Component, Selector};
|
||||
use selectors::parser::{SelectorIter, Visit};
|
||||
use selectors::parser::{AncestorHashes, Combinator, Component, Selector, SelectorIter};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use selectors::NthIndexCache;
|
||||
use servo_arc::{Arc, ArcBorrow};
|
||||
|
@ -1530,11 +1529,11 @@ impl SelectorMapEntry for RevalidationSelectorAndHashes {
|
|||
/// A selector visitor implementation that collects all the state the Stylist
|
||||
/// cares about a selector.
|
||||
struct StylistSelectorVisitor<'a> {
|
||||
/// Whether the selector needs revalidation for the style sharing cache.
|
||||
needs_revalidation: bool,
|
||||
/// Whether we've past the rightmost compound selector, not counting
|
||||
/// pseudo-elements.
|
||||
passed_rightmost_selector: bool,
|
||||
/// Whether the selector needs revalidation for the style sharing cache.
|
||||
needs_revalidation: &'a mut bool,
|
||||
/// The filter with all the id's getting referenced from rightmost
|
||||
/// selectors.
|
||||
mapped_ids: &'a mut PrecomputedHashSet<Atom>,
|
||||
|
@ -1582,14 +1581,10 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
|
|||
type Impl = SelectorImpl;
|
||||
|
||||
fn visit_complex_selector(&mut self, combinator: Option<Combinator>) -> bool {
|
||||
self.needs_revalidation =
|
||||
self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());
|
||||
*self.needs_revalidation =
|
||||
*self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());
|
||||
|
||||
// NOTE(emilio): This works properly right now because we can't store
|
||||
// complex selectors in nested selectors, otherwise we may need to
|
||||
// rethink this.
|
||||
//
|
||||
// Also, note that this call happens before we visit any of the simple
|
||||
// NOTE(emilio): this call happens before we visit any of the simple
|
||||
// selectors in the next ComplexSelector, so we can use this to skip
|
||||
// looking at them.
|
||||
self.passed_rightmost_selector = self.passed_rightmost_selector ||
|
||||
|
@ -1598,6 +1593,22 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
|
|||
true
|
||||
}
|
||||
|
||||
fn visit_selector_list(&mut self, list: &[Selector<Self::Impl>]) -> bool {
|
||||
for selector in list {
|
||||
let mut nested = StylistSelectorVisitor {
|
||||
passed_rightmost_selector: false,
|
||||
needs_revalidation: &mut *self.needs_revalidation,
|
||||
attribute_dependencies: &mut *self.attribute_dependencies,
|
||||
state_dependencies: &mut *self.state_dependencies,
|
||||
document_state_dependencies: &mut *self.document_state_dependencies,
|
||||
mapped_ids: &mut *self.mapped_ids,
|
||||
};
|
||||
let _ret = selector.visit(&mut nested);
|
||||
debug_assert!(_ret, "We never return false");
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn visit_attribute_selector(
|
||||
&mut self,
|
||||
_ns: &NamespaceConstraint<&Namespace>,
|
||||
|
@ -1610,7 +1621,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
|
|||
}
|
||||
|
||||
fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {
|
||||
self.needs_revalidation = self.needs_revalidation ||
|
||||
*self.needs_revalidation = *self.needs_revalidation ||
|
||||
component_needs_revalidation(s, self.passed_rightmost_selector);
|
||||
|
||||
match *s {
|
||||
|
@ -2022,8 +2033,9 @@ impl CascadeData {
|
|||
|
||||
if rebuild_kind.should_rebuild_invalidation() {
|
||||
self.invalidation_map.note_selector(selector, quirks_mode)?;
|
||||
let mut needs_revalidation = false;
|
||||
let mut visitor = StylistSelectorVisitor {
|
||||
needs_revalidation: false,
|
||||
needs_revalidation: &mut needs_revalidation,
|
||||
passed_rightmost_selector: false,
|
||||
attribute_dependencies: &mut self.attribute_dependencies,
|
||||
state_dependencies: &mut self.state_dependencies,
|
||||
|
@ -2033,7 +2045,7 @@ impl CascadeData {
|
|||
|
||||
rule.selector.visit(&mut visitor);
|
||||
|
||||
if visitor.needs_revalidation {
|
||||
if needs_revalidation {
|
||||
self.selectors_for_cache_revalidation.insert(
|
||||
RevalidationSelectorAndHashes::new(
|
||||
rule.selector.clone(),
|
||||
|
@ -2365,14 +2377,15 @@ pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
|
|||
let mut mapped_ids = Default::default();
|
||||
let mut state_dependencies = ElementState::empty();
|
||||
let mut document_state_dependencies = DocumentState::empty();
|
||||
let mut needs_revalidation = false;
|
||||
let mut visitor = StylistSelectorVisitor {
|
||||
needs_revalidation: false,
|
||||
passed_rightmost_selector: false,
|
||||
needs_revalidation: &mut needs_revalidation,
|
||||
attribute_dependencies: &mut attribute_dependencies,
|
||||
state_dependencies: &mut state_dependencies,
|
||||
document_state_dependencies: &mut document_state_dependencies,
|
||||
mapped_ids: &mut mapped_ids,
|
||||
};
|
||||
s.visit(&mut visitor);
|
||||
visitor.needs_revalidation
|
||||
needs_revalidation
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue