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:
Emilio Cobos Álvarez 2017-06-13 10:58:47 +02:00
parent 262f6adc30
commit 151b636562
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
3 changed files with 91 additions and 17 deletions

View file

@ -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| {

View file

@ -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());

View file

@ -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