diff options
Diffstat (limited to 'components/selectors/matching.rs')
-rw-r--r-- | components/selectors/matching.rs | 152 |
1 files changed, 115 insertions, 37 deletions
diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index c4e19f47fd3..9c60e6b78d7 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -69,28 +69,67 @@ impl ElementSelectorFlags { } } +/// What kind of selector matching mode we should use. +/// +/// There are two modes of selector matching. The difference is only noticeable +/// in presence of pseudo-elements. +#[derive(Debug, PartialEq)] +pub enum MatchingMode { + /// Don't ignore any pseudo-element selectors. + Normal, + + /// Ignores any stateless pseudo-element selectors in the rightmost sequence + /// of simple selectors. + /// + /// This is useful, for example, to match against ::before when you aren't a + /// pseudo-element yourself. + /// + /// For example, in presence of `::before:hover`, it would never match, but + /// `::before` would be ignored as in "matching". + /// + /// It's required for all the selectors you match using this mode to have a + /// pseudo-element. + ForStatelessPseudoElement, +} + + /// Data associated with the matching process for a element. This context is /// used across many selectors for an element, so it's not appropriate for /// transient data that applies to only a single selector. -#[derive(Default)] -pub struct MatchingContext { +pub struct MatchingContext<'a> { /// Output that records certains relations between elements noticed during /// matching (and also extended after matching). pub relations: StyleRelations, + /// The matching mode we should use when matching selectors. + pub matching_mode: MatchingMode, + /// The bloom filter used to fast-reject selectors. + pub bloom_filter: Option<&'a BloomFilter>, +} + +impl<'a> MatchingContext<'a> { + /// Constructs a new `MatchingContext`. + pub fn new(matching_mode: MatchingMode, + bloom_filter: Option<&'a BloomFilter>) + -> Self + { + Self { + relations: StyleRelations::empty(), + matching_mode: matching_mode, + bloom_filter: bloom_filter, + } + } } pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>], element: &E, - parent_bf: Option<&BloomFilter>) + context: &mut MatchingContext) -> bool where E: Element { selector_list.iter().any(|selector| { - selector.pseudo_element.is_none() && matches_selector(&selector.inner, element, - parent_bf, - &mut MatchingContext::default(), + context, &mut |_, _| {}) }) } @@ -115,27 +154,6 @@ fn may_match<E>(sel: &SelectorInner<E::Impl>, true } -/// Determines whether the given element matches the given complex selector. -pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>, - element: &E, - parent_bf: Option<&BloomFilter>, - context: &mut MatchingContext, - flags_setter: &mut F) - -> bool - where E: Element, - F: FnMut(&E, ElementSelectorFlags), -{ - // Use the bloom filter to fast-reject. - if let Some(filter) = parent_bf { - if !may_match::<E>(selector, filter) { - return false; - } - } - - // Match the selector. - matches_complex_selector(&selector.complex, element, context, flags_setter) -} - /// A result of selector matching, includes 3 failure types, /// /// NotMatchedAndRestartFromClosestLaterSibling @@ -186,16 +204,65 @@ enum SelectorMatchingResult { NotMatchedGlobally, } +/// Matches an inner selector. +pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>, + element: &E, + context: &mut MatchingContext, + flags_setter: &mut F) + -> bool + where E: Element, + F: FnMut(&E, ElementSelectorFlags), +{ + // Use the bloom filter to fast-reject. + if let Some(filter) = context.bloom_filter { + if !may_match::<E>(&selector, filter) { + return false; + } + } + + matches_complex_selector(&selector.complex, element, context, flags_setter) +} + /// Matches a complex selector. -pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>, +/// +/// Use `matches_selector` if you need to skip pseudos. +pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl>, element: &E, context: &mut MatchingContext, flags_setter: &mut F) -> bool - where E: Element, - F: FnMut(&E, ElementSelectorFlags), + where E: Element, + F: FnMut(&E, ElementSelectorFlags), { - match matches_complex_selector_internal(selector.iter(), + let mut iter = complex_selector.iter(); + + if cfg!(debug_assertions) { + if context.matching_mode == MatchingMode::ForStatelessPseudoElement { + assert!(complex_selector.iter().any(|c| { + matches!(*c, Component::PseudoElement(..)) + })); + } + } + + if context.matching_mode == MatchingMode::ForStatelessPseudoElement { + match *iter.next().unwrap() { + // Stateful pseudo, just don't match. + Component::NonTSPseudoClass(..) => return false, + Component::PseudoElement(..) => { + // Pseudo, just eat the whole sequence. + let next = iter.next(); + debug_assert!(next.is_none(), + "Someone messed up pseudo-element parsing?"); + + if iter.next_sequence().is_none() { + return true; + } + } + _ => panic!("Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"), + } + } + + match matches_complex_selector_internal(iter, element, context, flags_setter) { @@ -229,12 +296,19 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im match combinator { None => SelectorMatchingResult::Matched, Some(c) => { - let (mut next_element, candidate_not_found) = if siblings { - (element.prev_sibling_element(), - SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) - } else { - (element.parent_element(), - SelectorMatchingResult::NotMatchedGlobally) + let (mut next_element, candidate_not_found) = match c { + Combinator::NextSibling | Combinator::LaterSibling => { + (element.prev_sibling_element(), + SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) + } + Combinator::Child | Combinator::Descendant => { + (element.parent_element(), + SelectorMatchingResult::NotMatchedGlobally) + } + Combinator::PseudoElement => { + (element.pseudo_element_originating_element(), + SelectorMatchingResult::NotMatchedGlobally) + } }; loop { @@ -253,6 +327,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im // Upgrade the failure status to // NotMatchedAndRestartFromClosestDescendant. + (_, Combinator::PseudoElement) | (_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, // Return the status directly. @@ -306,6 +381,9 @@ fn matches_simple_selector<E, F>( match *selector { Component::Combinator(_) => unreachable!(), + Component::PseudoElement(ref pseudo) => { + element.match_pseudo_element(pseudo, context) + } Component::LocalName(LocalName { ref name, ref lower_name }) => { let name = if element.is_html_element_in_html_document() { lower_name } else { name }; element.get_local_name() == name.borrow() |