mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
style: Add a way to match a single compound selector.
Also improve the ergonomics of matches_complex_selector. Bug: 1368240 MozReview-Commit-ID: 9DWDvyZmetM
This commit is contained in:
parent
262f6adc30
commit
151b636562
3 changed files with 91 additions and 17 deletions
|
@ -335,9 +335,9 @@ enum SelectorMatchingResult {
|
||||||
|
|
||||||
/// Matches a selector, fast-rejecting against a bloom filter.
|
/// Matches a selector, fast-rejecting against a bloom filter.
|
||||||
///
|
///
|
||||||
/// We accept an offset to allow consumers to represent and match against partial
|
/// We accept an offset to allow consumers to represent and match against
|
||||||
/// selectors (indexed from the right). We use this API design, rather than
|
/// partial selectors (indexed from the right). We use this API design, rather
|
||||||
/// having the callers pass a SelectorIter, because creating a SelectorIter
|
/// than having the callers pass a SelectorIter, because creating a SelectorIter
|
||||||
/// requires dereferencing the selector to get the length, which adds an
|
/// requires dereferencing the selector to get the length, which adds an
|
||||||
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
|
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
|
||||||
/// (which the caller can store inline with the selector pointer).
|
/// (which the caller can store inline with the selector pointer).
|
||||||
|
@ -360,12 +360,74 @@ pub fn matches_selector<E, F>(selector: &Selector<E::Impl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut local_context = LocalMatchingContext::new(context, selector);
|
let mut local_context = LocalMatchingContext::new(context, selector);
|
||||||
matches_complex_selector(&selector, offset, element, &mut local_context, flags_setter)
|
let iter = if offset == 0 {
|
||||||
|
selector.iter()
|
||||||
|
} else {
|
||||||
|
selector.iter_from(offset)
|
||||||
|
};
|
||||||
|
matches_complex_selector(iter, element, &mut local_context, flags_setter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a compound selector matched, and whether it was the rightmost
|
||||||
|
/// selector inside the complex selector.
|
||||||
|
pub enum CompoundSelectorMatchingResult {
|
||||||
|
/// The compound selector matched, and the next combinator offset is
|
||||||
|
/// `next_combinator_offset`.
|
||||||
|
///
|
||||||
|
/// If the next combinator offset is zero, it means that it's the rightmost
|
||||||
|
/// selector.
|
||||||
|
Matched { next_combinator_offset: usize, },
|
||||||
|
/// The selector didn't match.
|
||||||
|
NotMatched,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches a compound selector belonging to `selector`, starting at offset
|
||||||
|
/// `from_offset`, matching left to right.
|
||||||
|
///
|
||||||
|
/// Requires that `from_offset` points to a `Combinator`.
|
||||||
|
///
|
||||||
|
/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
|
||||||
|
/// complex selector, but it happens to be the case we don't need it.
|
||||||
|
pub fn matches_compound_selector<E>(
|
||||||
|
selector: &Selector<E::Impl>,
|
||||||
|
mut from_offset: usize,
|
||||||
|
context: &mut MatchingContext,
|
||||||
|
element: &E,
|
||||||
|
) -> CompoundSelectorMatchingResult
|
||||||
|
where
|
||||||
|
E: Element
|
||||||
|
{
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
selector.combinator_at(from_offset); // This asserts.
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut local_context = LocalMatchingContext::new(context, selector);
|
||||||
|
for component in selector.iter_raw_rev_from(from_offset - 1) {
|
||||||
|
if matches!(*component, Component::Combinator(..)) {
|
||||||
|
return CompoundSelectorMatchingResult::Matched {
|
||||||
|
next_combinator_offset: from_offset - 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matches_simple_selector(
|
||||||
|
component,
|
||||||
|
element,
|
||||||
|
&mut local_context,
|
||||||
|
&RelevantLinkStatus::NotLooking,
|
||||||
|
&mut |_, _| {}) {
|
||||||
|
return CompoundSelectorMatchingResult::NotMatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
from_offset -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompoundSelectorMatchingResult::Matched {
|
||||||
|
next_combinator_offset: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Matches a complex selector.
|
/// Matches a complex selector.
|
||||||
pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>,
|
pub fn matches_complex_selector<E, F>(mut iter: SelectorIter<E::Impl>,
|
||||||
offset: usize,
|
|
||||||
element: &E,
|
element: &E,
|
||||||
mut context: &mut LocalMatchingContext<E::Impl>,
|
mut context: &mut LocalMatchingContext<E::Impl>,
|
||||||
flags_setter: &mut F)
|
flags_setter: &mut F)
|
||||||
|
@ -373,12 +435,6 @@ pub fn matches_complex_selector<E, F>(complex_selector: &Selector<E::Impl>,
|
||||||
where E: Element,
|
where E: Element,
|
||||||
F: FnMut(&E, ElementSelectorFlags),
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
{
|
{
|
||||||
let mut iter = if offset == 0 {
|
|
||||||
complex_selector.iter()
|
|
||||||
} else {
|
|
||||||
complex_selector.iter_from(offset)
|
|
||||||
};
|
|
||||||
|
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
|
if context.shared.matching_mode == MatchingMode::ForStatelessPseudoElement {
|
||||||
assert!(iter.clone().any(|c| {
|
assert!(iter.clone().any(|c| {
|
||||||
|
|
|
@ -449,18 +449,36 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the entire sequence of simple selectors and combinators,
|
/// Returns the combinator at index `index`, or panics if the component is
|
||||||
/// from right to left.
|
/// not a combinator.
|
||||||
|
pub fn combinator_at(&self, index: usize) -> Combinator {
|
||||||
|
match self.0.slice[self.0.slice.len() - index] {
|
||||||
|
Component::Combinator(c) => c,
|
||||||
|
ref other => {
|
||||||
|
panic!("Not a combinator: {:?}, {:?}, index: {}",
|
||||||
|
other, self, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the entire sequence of simple selectors and
|
||||||
|
/// combinators, from right to left.
|
||||||
pub fn iter_raw(&self) -> Rev<slice::Iter<Component<Impl>>> {
|
pub fn iter_raw(&self) -> Rev<slice::Iter<Component<Impl>>> {
|
||||||
self.iter_raw_rev().rev()
|
self.iter_raw_rev().rev()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the entire sequence of simple selectors and combinators,
|
/// Returns an iterator over the entire sequence of simple selectors and
|
||||||
/// from left to right.
|
/// combinators, from left to right.
|
||||||
pub fn iter_raw_rev(&self) -> slice::Iter<Component<Impl>> {
|
pub fn iter_raw_rev(&self) -> slice::Iter<Component<Impl>> {
|
||||||
self.0.slice.iter()
|
self.0.slice.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the sequence of simple selectors and
|
||||||
|
/// combinators after `offset`, from left to right.
|
||||||
|
pub fn iter_raw_rev_from(&self, offset: usize) -> slice::Iter<Component<Impl>> {
|
||||||
|
self.0.slice[(self.0.slice.len() - offset)..].iter()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a Selector from a vec of Components. Used in tests.
|
/// Creates a Selector from a vec of Components. Used in tests.
|
||||||
pub fn from_vec(vec: Vec<Component<Impl>>, specificity_and_flags: u32) -> Self {
|
pub fn from_vec(vec: Vec<Component<Impl>>, specificity_and_flags: u32) -> Self {
|
||||||
let header = HeaderWithLength::new(SpecificityAndFlags(specificity_and_flags), vec.len());
|
let header = HeaderWithLength::new(SpecificityAndFlags(specificity_and_flags), vec.len());
|
||||||
|
|
|
@ -1547,7 +1547,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
let old_value = context.within_functional_pseudo_class_argument;
|
let old_value = context.within_functional_pseudo_class_argument;
|
||||||
context.within_functional_pseudo_class_argument = true;
|
context.within_functional_pseudo_class_argument = true;
|
||||||
let result = sels.iter().any(|s| {
|
let result = sels.iter().any(|s| {
|
||||||
matches_complex_selector(s, 0, self, context, flags_setter)
|
matches_complex_selector(s.iter(), self, context, flags_setter)
|
||||||
});
|
});
|
||||||
context.within_functional_pseudo_class_argument = old_value;
|
context.within_functional_pseudo_class_argument = old_value;
|
||||||
result
|
result
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue