selectors/
matching.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use crate::attr::{
6    AttrSelectorOperation, AttrSelectorWithOptionalNamespace, CaseSensitivity, NamespaceConstraint,
7    ParsedAttrSelectorOperation, ParsedCaseSensitivity,
8};
9use crate::bloom::{BloomFilter, BLOOM_HASH_MASK};
10use crate::kleene_value::KleeneValue;
11use crate::parser::{
12    AncestorHashes, Combinator, Component, LocalName, MatchesFeaturelessHost, NthSelectorData,
13    RelativeSelectorMatchHint,
14};
15use crate::parser::{
16    NonTSPseudoClass, RelativeSelector, Selector, SelectorImpl, SelectorIter, SelectorList,
17};
18use crate::relative_selector::cache::RelativeSelectorCachedMatch;
19use crate::tree::Element;
20use bitflags::bitflags;
21use debug_unreachable::debug_unreachable;
22use log::debug;
23use smallvec::SmallVec;
24use std::borrow::Borrow;
25
26pub use crate::context::*;
27
28// The bloom filter for descendant CSS selectors will have a <1% false
29// positive rate until it has this many selectors in it, then it will
30// rapidly increase.
31pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
32
33bitflags! {
34    /// Set of flags that are set on either the element or its parent (depending
35    /// on the flag) if the element could potentially match a selector.
36    #[derive(Clone, Copy)]
37    pub struct ElementSelectorFlags: usize {
38        /// When a child is added or removed from the parent, all the children
39        /// must be restyled, because they may match :nth-last-child,
40        /// :last-of-type, :nth-last-of-type, or :only-of-type.
41        const HAS_SLOW_SELECTOR = 1 << 0;
42
43        /// When a child is added or removed from the parent, any later
44        /// children must be restyled, because they may match :nth-child,
45        /// :first-of-type, or :nth-of-type.
46        const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1;
47
48        /// HAS_SLOW_SELECTOR* was set by the presence of :nth (But not of).
49        const HAS_SLOW_SELECTOR_NTH = 1 << 2;
50
51        /// When a DOM mutation occurs on a child that might be matched by
52        /// :nth-last-child(.. of <selector list>), earlier children must be
53        /// restyled, and HAS_SLOW_SELECTOR will be set (which normally
54        /// indicates that all children will be restyled).
55        ///
56        /// Similarly, when a DOM mutation occurs on a child that might be
57        /// matched by :nth-child(.. of <selector list>), later children must be
58        /// restyled, and HAS_SLOW_SELECTOR_LATER_SIBLINGS will be set.
59        const HAS_SLOW_SELECTOR_NTH_OF = 1 << 3;
60
61        /// When a child is added or removed from the parent, the first and
62        /// last children must be restyled, because they may match :first-child,
63        /// :last-child, or :only-child.
64        const HAS_EDGE_CHILD_SELECTOR = 1 << 4;
65
66        /// The element has an empty selector, so when a child is appended we
67        /// might need to restyle the parent completely.
68        const HAS_EMPTY_SELECTOR = 1 << 5;
69
70        /// The element may anchor a relative selector.
71        const ANCHORS_RELATIVE_SELECTOR = 1 << 6;
72
73        /// The element may anchor a relative selector that is not the subject
74        /// of the whole selector.
75        const ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT = 1 << 7;
76
77        /// The element is reached by a relative selector search in the sibling direction.
78        const RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING = 1 << 8;
79
80        /// The element is reached by a relative selector search in the ancestor direction.
81        const RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR = 1 << 9;
82
83        // The element is reached by a relative selector search in both sibling and ancestor directions.
84        const RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING =
85            Self::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING.bits() |
86            Self::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR.bits();
87    }
88}
89
90impl ElementSelectorFlags {
91    /// Returns the subset of flags that apply to the element.
92    pub fn for_self(self) -> ElementSelectorFlags {
93        self & (ElementSelectorFlags::HAS_EMPTY_SELECTOR
94            | ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR
95            | ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT
96            | ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
97            | ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR)
98    }
99
100    /// Returns the subset of flags that apply to the parent.
101    pub fn for_parent(self) -> ElementSelectorFlags {
102        self & (ElementSelectorFlags::HAS_SLOW_SELECTOR
103            | ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS
104            | ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH
105            | ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF
106            | ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR)
107    }
108}
109
110/// Holds per-compound-selector data.
111struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
112    shared: &'a mut MatchingContext<'b, Impl>,
113    rightmost: SubjectOrPseudoElement,
114    quirks_data: Option<SelectorIter<'a, Impl>>,
115}
116
117#[inline(always)]
118pub fn matches_selector_list<E>(
119    selector_list: &SelectorList<E::Impl>,
120    element: &E,
121    context: &mut MatchingContext<E::Impl>,
122) -> bool
123where
124    E: Element,
125{
126    // This is pretty much any(..) but manually inlined because the compiler
127    // refuses to do so from querySelector / querySelectorAll.
128    for selector in selector_list.slice() {
129        let matches = matches_selector(selector, 0, None, element, context);
130        if matches {
131            return true;
132        }
133    }
134
135    false
136}
137
138/// Given the ancestor hashes from a selector, see if the current element,
139/// represented by the bloom filter, has a chance of matching at all.
140#[inline(always)]
141pub fn selector_may_match(hashes: &AncestorHashes, bf: &BloomFilter) -> bool {
142    // Check the first three hashes. Note that we can check for zero before
143    // masking off the high bits, since if any of the first three hashes is
144    // zero the fourth will be as well. We also take care to avoid the
145    // special-case complexity of the fourth hash until we actually reach it,
146    // because we usually don't.
147    //
148    // To be clear: this is all extremely hot.
149    for i in 0..3 {
150        let packed = hashes.packed_hashes[i];
151        if packed == 0 {
152            // No more hashes left - unable to fast-reject.
153            return true;
154        }
155
156        if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) {
157            // Hooray! We fast-rejected on this hash.
158            return false;
159        }
160    }
161
162    // Now do the slighty-more-complex work of synthesizing the fourth hash,
163    // and check it against the filter if it exists.
164    let fourth = hashes.fourth_hash();
165    fourth == 0 || bf.might_contain_hash(fourth)
166}
167
168/// A result of selector matching, includes 3 failure types,
169///
170///   NotMatchedAndRestartFromClosestLaterSibling
171///   NotMatchedAndRestartFromClosestDescendant
172///   NotMatchedGlobally
173///
174/// When NotMatchedGlobally appears, stop selector matching completely since
175/// the succeeding selectors never matches.
176/// It is raised when
177///   Child combinator cannot find the candidate element.
178///   Descendant combinator cannot find the candidate element.
179///
180/// When NotMatchedAndRestartFromClosestDescendant appears, the selector
181/// matching does backtracking and restarts from the closest Descendant
182/// combinator.
183/// It is raised when
184///   NextSibling combinator cannot find the candidate element.
185///   LaterSibling combinator cannot find the candidate element.
186///   Child combinator doesn't match on the found element.
187///
188/// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector
189/// matching does backtracking and restarts from the closest LaterSibling
190/// combinator.
191/// It is raised when
192///   NextSibling combinator doesn't match on the found element.
193///
194/// For example, when the selector "d1 d2 a" is provided and we cannot *find*
195/// an appropriate ancestor element for "d1", this selector matching raises
196/// NotMatchedGlobally since even if "d2" is moved to more upper element, the
197/// candidates for "d1" becomes less than before and d1 .
198///
199/// The next example is siblings. When the selector "b1 + b2 ~ d1 a" is
200/// provided and we cannot *find* an appropriate brother element for b1,
201/// the selector matching raises NotMatchedAndRestartFromClosestDescendant.
202/// The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1".
203///
204/// The additional example is child and sibling. When the selector
205/// "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on
206/// the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling.
207/// However since the selector "c1" raises
208/// NotMatchedAndRestartFromClosestDescendant. So the selector
209/// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
210///
211/// There is also the unknown result, which is used during invalidation when
212/// specific selector is being tested for before/after comparison. More specifically,
213/// selectors that are too expensive to correctly compute during invalidation may
214/// return unknown, as the computation will be thrown away and only to be recomputed
215/// during styling. For most cases, the unknown result can be treated as matching.
216/// This is because a compound of selectors acts like &&, and unknown && matched
217/// == matched and unknown && not-matched == not-matched. However, some selectors,
218/// like `:is()`, behave like || i.e. `:is(.a, .b)` == a || b. Treating unknown
219/// == matching then causes these selectors to always return matching, which undesired
220/// for before/after comparison. Coercing to not-matched doesn't work since each
221/// inner selector may have compounds: e.g. Toggling `.foo` in `:is(.foo:has(..))`
222/// with coersion to not-matched would result in an invalid before/after comparison
223/// of not-matched/not-matched.
224#[derive(Clone, Copy, Eq, PartialEq)]
225enum SelectorMatchingResult {
226    Matched,
227    NotMatchedAndRestartFromClosestLaterSibling,
228    NotMatchedAndRestartFromClosestDescendant,
229    NotMatchedGlobally,
230    Unknown,
231}
232
233impl From<SelectorMatchingResult> for KleeneValue {
234    #[inline]
235    fn from(value: SelectorMatchingResult) -> Self {
236        match value {
237            SelectorMatchingResult::Matched => KleeneValue::True,
238            SelectorMatchingResult::Unknown => KleeneValue::Unknown,
239            SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling
240            | SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
241            | SelectorMatchingResult::NotMatchedGlobally => KleeneValue::False,
242        }
243    }
244}
245
246/// Matches a selector, fast-rejecting against a bloom filter.
247///
248/// We accept an offset to allow consumers to represent and match against
249/// partial selectors (indexed from the right). We use this API design, rather
250/// than having the callers pass a SelectorIter, because creating a SelectorIter
251/// requires dereferencing the selector to get the length, which adds an
252/// unnecessary cache miss for cases when we can fast-reject with AncestorHashes
253/// (which the caller can store inline with the selector pointer).
254#[inline(always)]
255pub fn matches_selector<E>(
256    selector: &Selector<E::Impl>,
257    offset: usize,
258    hashes: Option<&AncestorHashes>,
259    element: &E,
260    context: &mut MatchingContext<E::Impl>,
261) -> bool
262where
263    E: Element,
264{
265    let result = matches_selector_kleene(selector, offset, hashes, element, context);
266    if cfg!(debug_assertions) && result == KleeneValue::Unknown {
267        debug_assert!(
268            context
269                .matching_for_invalidation_comparison()
270                .unwrap_or(false),
271            "How did we return unknown?"
272        );
273    }
274    result.to_bool(true)
275}
276
277/// Same as matches_selector, but returns the Kleene value as-is.
278#[inline(always)]
279pub fn matches_selector_kleene<E>(
280    selector: &Selector<E::Impl>,
281    offset: usize,
282    hashes: Option<&AncestorHashes>,
283    element: &E,
284    context: &mut MatchingContext<E::Impl>,
285) -> KleeneValue
286where
287    E: Element,
288{
289    // Use the bloom filter to fast-reject.
290    if let Some(hashes) = hashes {
291        if let Some(filter) = context.bloom_filter {
292            if !selector_may_match(hashes, filter) {
293                return KleeneValue::False;
294            }
295        }
296    }
297    matches_complex_selector(
298        selector.iter_from(offset),
299        element,
300        context,
301        if selector.is_rightmost(offset) {
302            SubjectOrPseudoElement::Yes
303        } else {
304            SubjectOrPseudoElement::No
305        },
306    )
307}
308
309/// Whether a compound selector matched, and whether it was the rightmost
310/// selector inside the complex selector.
311pub enum CompoundSelectorMatchingResult {
312    /// The selector was fully matched.
313    FullyMatched,
314    /// The compound selector matched, and the next combinator offset is
315    /// `next_combinator_offset`.
316    Matched { next_combinator_offset: usize },
317    /// The selector didn't match.
318    NotMatched,
319}
320
321/// Matches a compound selector belonging to `selector`, starting at offset
322/// `from_offset`, matching left to right.
323///
324/// Requires that `from_offset` points to a `Combinator`.
325///
326/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
327/// complex selector, but it happens to be the case we don't need it.
328pub fn matches_compound_selector_from<E>(
329    selector: &Selector<E::Impl>,
330    mut from_offset: usize,
331    context: &mut MatchingContext<E::Impl>,
332    element: &E,
333) -> CompoundSelectorMatchingResult
334where
335    E: Element,
336{
337    debug_assert!(
338        !context
339            .matching_for_invalidation_comparison()
340            .unwrap_or(false),
341        "CompoundSelectorMatchingResult doesn't support unknown"
342    );
343    if cfg!(debug_assertions) && from_offset != 0 {
344        selector.combinator_at_parse_order(from_offset - 1); // This asserts.
345    }
346
347    let mut local_context = LocalMatchingContext {
348        shared: context,
349        // We have no info if this is an outer selector. This function is called in
350        // an invalidation context, which only calls this for non-subject (i.e.
351        // Non-rightmost) positions.
352        rightmost: SubjectOrPseudoElement::No,
353        quirks_data: None,
354    };
355
356    // Find the end of the selector or the next combinator, then match
357    // backwards, so that we match in the same order as
358    // matches_complex_selector, which is usually faster.
359    let start_offset = from_offset;
360    for component in selector.iter_raw_parse_order_from(from_offset) {
361        if matches!(*component, Component::Combinator(..)) {
362            debug_assert_ne!(from_offset, 0, "Selector started with a combinator?");
363            break;
364        }
365
366        from_offset += 1;
367    }
368
369    debug_assert!(from_offset >= 1);
370    debug_assert!(from_offset <= selector.len());
371
372    let iter = selector.iter_from(selector.len() - from_offset);
373    debug_assert!(
374        iter.clone().next().is_some() || from_offset != selector.len(),
375        "Got the math wrong: {:?} | {:?} | {} {}",
376        selector,
377        selector.iter_raw_match_order().as_slice(),
378        from_offset,
379        start_offset
380    );
381
382    debug_assert!(
383        !local_context.shared.featureless(),
384        "Invalidating featureless element somehow?"
385    );
386
387    for component in iter {
388        let result = matches_simple_selector(component, element, &mut local_context);
389        debug_assert!(
390            result != KleeneValue::Unknown,
391            "Returned unknown in non invalidation context?"
392        );
393        if !result.to_bool(true) {
394            return CompoundSelectorMatchingResult::NotMatched;
395        }
396    }
397
398    if from_offset != selector.len() {
399        return CompoundSelectorMatchingResult::Matched {
400            next_combinator_offset: from_offset,
401        };
402    }
403
404    CompoundSelectorMatchingResult::FullyMatched
405}
406
407/// Matches a complex selector.
408#[inline(always)]
409fn matches_complex_selector<E>(
410    mut iter: SelectorIter<E::Impl>,
411    element: &E,
412    context: &mut MatchingContext<E::Impl>,
413    rightmost: SubjectOrPseudoElement,
414) -> KleeneValue
415where
416    E: Element,
417{
418    // If this is the special pseudo-element mode, consume the ::pseudo-element
419    // before proceeding, since the caller has already handled that part.
420    if context.matching_mode() == MatchingMode::ForStatelessPseudoElement && !context.is_nested() {
421        // Consume the pseudo.
422        match *iter.next().unwrap() {
423            Component::PseudoElement(ref pseudo) => {
424                if let Some(ref f) = context.pseudo_element_matching_fn {
425                    if !f(pseudo) {
426                        return KleeneValue::False;
427                    }
428                }
429            },
430            ref other => {
431                debug_assert!(
432                    false,
433                    "Used MatchingMode::ForStatelessPseudoElement \
434                     in a non-pseudo selector {:?}",
435                    other
436                );
437                return KleeneValue::False;
438            },
439        }
440
441        if !iter.matches_for_stateless_pseudo_element() {
442            return KleeneValue::False;
443        }
444
445        // Advance to the non-pseudo-element part of the selector.
446        let next_sequence = iter.next_sequence().unwrap();
447        debug_assert_eq!(next_sequence, Combinator::PseudoElement);
448    }
449
450    matches_complex_selector_internal(
451        iter,
452        element,
453        context,
454        rightmost,
455        SubjectOrPseudoElement::Yes,
456    )
457    .into()
458}
459
460/// Matches each selector of a list as a complex selector
461fn matches_complex_selector_list<E: Element>(
462    list: &[Selector<E::Impl>],
463    element: &E,
464    context: &mut MatchingContext<E::Impl>,
465    rightmost: SubjectOrPseudoElement,
466) -> KleeneValue {
467    KleeneValue::any(list.iter(), |selector| {
468        matches_complex_selector(selector.iter(), element, context, rightmost)
469    })
470}
471
472fn matches_relative_selector<E: Element>(
473    relative_selector: &RelativeSelector<E::Impl>,
474    element: &E,
475    context: &mut MatchingContext<E::Impl>,
476    rightmost: SubjectOrPseudoElement,
477) -> bool {
478    // Overall, we want to mark the path that we've traversed so that when an element
479    // is invalidated, we early-reject unnecessary relative selector invalidations.
480    if relative_selector.match_hint.is_descendant_direction() {
481        if context.needs_selector_flags() {
482            element.apply_selector_flags(
483                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
484            );
485        }
486        let mut next_element = element.first_element_child();
487        while let Some(el) = next_element {
488            if context.needs_selector_flags() {
489                el.apply_selector_flags(
490                    ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
491                );
492            }
493            let mut matched = matches_complex_selector(
494                relative_selector.selector.iter(),
495                &el,
496                context,
497                rightmost,
498            )
499            .to_bool(true);
500            if !matched && relative_selector.match_hint.is_subtree() {
501                matched = matches_relative_selector_subtree(
502                    &relative_selector.selector,
503                    &el,
504                    context,
505                    rightmost,
506                );
507            }
508            if matched {
509                return true;
510            }
511            next_element = el.next_sibling_element();
512        }
513    } else {
514        debug_assert!(
515            matches!(
516                relative_selector.match_hint,
517                RelativeSelectorMatchHint::InNextSibling
518                    | RelativeSelectorMatchHint::InNextSiblingSubtree
519                    | RelativeSelectorMatchHint::InSibling
520                    | RelativeSelectorMatchHint::InSiblingSubtree
521            ),
522            "Not descendant direction, but also not sibling direction?"
523        );
524        if context.needs_selector_flags() {
525            element.apply_selector_flags(
526                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
527            );
528        }
529        let sibling_flag = if relative_selector.match_hint.is_subtree() {
530            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
531        } else {
532            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
533        };
534        let mut next_element = element.next_sibling_element();
535        while let Some(el) = next_element {
536            if context.needs_selector_flags() {
537                el.apply_selector_flags(sibling_flag);
538            }
539            let matched = if relative_selector.match_hint.is_subtree() {
540                matches_relative_selector_subtree(
541                    &relative_selector.selector,
542                    &el,
543                    context,
544                    rightmost,
545                )
546            } else {
547                matches_complex_selector(relative_selector.selector.iter(), &el, context, rightmost)
548                    .to_bool(true)
549            };
550            if matched {
551                return true;
552            }
553            if relative_selector.match_hint.is_next_sibling() {
554                break;
555            }
556            next_element = el.next_sibling_element();
557        }
558    }
559    return false;
560}
561
562fn relative_selector_match_early<E: Element>(
563    selector: &RelativeSelector<E::Impl>,
564    element: &E,
565    context: &mut MatchingContext<E::Impl>,
566) -> Option<bool> {
567    // See if we can return a cached result.
568    if let Some(cached) = context
569        .selector_caches
570        .relative_selector
571        .lookup(element.opaque(), selector)
572    {
573        return Some(cached.matched());
574    }
575    // See if we can fast-reject.
576    if context
577        .selector_caches
578        .relative_selector_filter_map
579        .fast_reject(element, selector, context.quirks_mode())
580    {
581        // Alright, add as unmatched to cache.
582        context.selector_caches.relative_selector.add(
583            element.opaque(),
584            selector,
585            RelativeSelectorCachedMatch::NotMatched,
586        );
587        return Some(false);
588    }
589    None
590}
591
592fn match_relative_selectors<E: Element>(
593    selectors: &[RelativeSelector<E::Impl>],
594    element: &E,
595    context: &mut MatchingContext<E::Impl>,
596    rightmost: SubjectOrPseudoElement,
597) -> KleeneValue {
598    if context.relative_selector_anchor().is_some() {
599        // FIXME(emilio): This currently can happen with nesting, and it's not fully
600        // correct, arguably. But the ideal solution isn't super-clear either. For now,
601        // cope with it and explicitly reject it at match time. See [1] for discussion.
602        //
603        // [1]: https://github.com/w3c/csswg-drafts/issues/9600
604        return KleeneValue::False;
605    }
606    if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {
607        // In the context of invalidation, :has is expensive, especially because we
608        // can't use caching/filtering due to now/then matches. DOM structure also
609        // may have changed.
610        return if may_return_unknown {
611            KleeneValue::Unknown
612        } else {
613            KleeneValue::from(!context.in_negation())
614        };
615    }
616    context
617        .nest_for_relative_selector(element.opaque(), |context| {
618            do_match_relative_selectors(selectors, element, context, rightmost)
619        })
620        .into()
621}
622
623/// Matches a relative selector in a list of relative selectors.
624fn do_match_relative_selectors<E: Element>(
625    selectors: &[RelativeSelector<E::Impl>],
626    element: &E,
627    context: &mut MatchingContext<E::Impl>,
628    rightmost: SubjectOrPseudoElement,
629) -> bool {
630    // Due to style sharing implications (See style sharing code), we mark the current styling context
631    // to mark elements considered for :has matching. Additionally, we want to mark the elements themselves,
632    // since we don't want to indiscriminately invalidate every element as a potential anchor.
633    if rightmost == SubjectOrPseudoElement::Yes {
634        if context.needs_selector_flags() {
635            element.apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR);
636        }
637    } else {
638        if context.needs_selector_flags() {
639            element
640                .apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT);
641        }
642    }
643
644    for relative_selector in selectors.iter() {
645        if let Some(result) = relative_selector_match_early(relative_selector, element, context) {
646            if result {
647                return true;
648            }
649            // Early return indicates no match, continue to next selector.
650            continue;
651        }
652
653        let matched = matches_relative_selector(relative_selector, element, context, rightmost);
654        context.selector_caches.relative_selector.add(
655            element.opaque(),
656            relative_selector,
657            if matched {
658                RelativeSelectorCachedMatch::Matched
659            } else {
660                RelativeSelectorCachedMatch::NotMatched
661            },
662        );
663        if matched {
664            return true;
665        }
666    }
667
668    false
669}
670
671fn matches_relative_selector_subtree<E: Element>(
672    selector: &Selector<E::Impl>,
673    element: &E,
674    context: &mut MatchingContext<E::Impl>,
675    rightmost: SubjectOrPseudoElement,
676) -> bool {
677    let mut current = element.first_element_child();
678
679    while let Some(el) = current {
680        if context.needs_selector_flags() {
681            el.apply_selector_flags(
682                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
683            );
684        }
685        if matches_complex_selector(selector.iter(), &el, context, rightmost).to_bool(true) {
686            return true;
687        }
688
689        if matches_relative_selector_subtree(selector, &el, context, rightmost) {
690            return true;
691        }
692
693        current = el.next_sibling_element();
694    }
695
696    false
697}
698
699/// Whether the :hover and :active quirk applies.
700///
701/// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk
702fn hover_and_active_quirk_applies<Impl: SelectorImpl>(
703    selector_iter: &SelectorIter<Impl>,
704    context: &MatchingContext<Impl>,
705    rightmost: SubjectOrPseudoElement,
706) -> bool {
707    debug_assert_eq!(context.quirks_mode(), QuirksMode::Quirks);
708
709    if context.is_nested() {
710        return false;
711    }
712
713    // This compound selector had a pseudo-element to the right that we
714    // intentionally skipped.
715    if rightmost == SubjectOrPseudoElement::Yes
716        && context.matching_mode() == MatchingMode::ForStatelessPseudoElement
717    {
718        return false;
719    }
720
721    selector_iter.clone().all(|simple| match *simple {
722        Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),
723        _ => false,
724    })
725}
726
727#[derive(Clone, Copy, PartialEq)]
728enum SubjectOrPseudoElement {
729    Yes,
730    No,
731}
732
733fn host_for_part<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>
734where
735    E: Element,
736{
737    let scope = context.current_host;
738    let mut curr = element.containing_shadow_host()?;
739    if scope == Some(curr.opaque()) {
740        return Some(curr);
741    }
742    loop {
743        let parent = curr.containing_shadow_host();
744        if parent.as_ref().map(|h| h.opaque()) == scope {
745            return Some(curr);
746        }
747        curr = parent?;
748    }
749}
750
751fn assigned_slot<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>
752where
753    E: Element,
754{
755    debug_assert!(element
756        .assigned_slot()
757        .map_or(true, |s| s.is_html_slot_element()));
758    let scope = context.current_host?;
759    let mut current_slot = element.assigned_slot()?;
760    while current_slot.containing_shadow_host().unwrap().opaque() != scope {
761        current_slot = current_slot.assigned_slot()?;
762    }
763    Some(current_slot)
764}
765
766struct NextElement<E> {
767    next_element: Option<E>,
768    featureless: bool,
769}
770
771impl<E> NextElement<E> {
772    #[inline(always)]
773    fn new(next_element: Option<E>, featureless: bool) -> Self {
774        Self {
775            next_element,
776            featureless,
777        }
778    }
779}
780
781#[inline(always)]
782fn next_element_for_combinator<E>(
783    element: &E,
784    combinator: Combinator,
785    context: &MatchingContext<E::Impl>,
786) -> NextElement<E>
787where
788    E: Element,
789{
790    match combinator {
791        Combinator::NextSibling | Combinator::LaterSibling => {
792            NextElement::new(element.prev_sibling_element(), false)
793        },
794        Combinator::Child | Combinator::Descendant => {
795            if let Some(parent) = element.parent_element() {
796                return NextElement::new(Some(parent), false);
797            }
798
799            let element = if element.parent_node_is_shadow_root() {
800                element.containing_shadow_host()
801            } else {
802                None
803            };
804            NextElement::new(element, true)
805        },
806        Combinator::Part => NextElement::new(host_for_part(element, context), false),
807        Combinator::SlotAssignment => NextElement::new(assigned_slot(element, context), false),
808        Combinator::PseudoElement => {
809            NextElement::new(element.pseudo_element_originating_element(), false)
810        },
811    }
812}
813
814fn matches_complex_selector_internal<E>(
815    mut selector_iter: SelectorIter<E::Impl>,
816    element: &E,
817    context: &mut MatchingContext<E::Impl>,
818    mut rightmost: SubjectOrPseudoElement,
819    mut first_subject_compound: SubjectOrPseudoElement,
820) -> SelectorMatchingResult
821where
822    E: Element,
823{
824    debug!(
825        "Matching complex selector {:?} for {:?}",
826        selector_iter, element
827    );
828
829    let matches_compound_selector =
830        matches_compound_selector(&mut selector_iter, element, context, rightmost);
831
832    let Some(combinator) = selector_iter.next_sequence() else {
833        return match matches_compound_selector {
834            KleeneValue::True => SelectorMatchingResult::Matched,
835            KleeneValue::Unknown => SelectorMatchingResult::Unknown,
836            KleeneValue::False => {
837                SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling
838            },
839        };
840    };
841
842    let is_pseudo_combinator = combinator.is_pseudo_element();
843    if context.featureless() && !is_pseudo_combinator {
844        // A featureless element shouldn't match any further combinator.
845        // TODO(emilio): Maybe we could avoid the compound matching more eagerly.
846        return SelectorMatchingResult::NotMatchedGlobally;
847    }
848
849    let is_sibling_combinator = combinator.is_sibling();
850    if is_sibling_combinator && context.needs_selector_flags() {
851        // We need the flags even if we don't match.
852        element.apply_selector_flags(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);
853    }
854
855    if matches_compound_selector == KleeneValue::False {
856        // We don't short circuit unknown here, since the rest of the selector
857        // to the left of this compound may still return false.
858        return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
859    }
860
861    if !is_pseudo_combinator {
862        rightmost = SubjectOrPseudoElement::No;
863        first_subject_compound = SubjectOrPseudoElement::No;
864    }
865
866    // Stop matching :visited as soon as we find a link, or a combinator for
867    // something that isn't an ancestor.
868    let mut visited_handling = if is_sibling_combinator {
869        VisitedHandlingMode::AllLinksUnvisited
870    } else {
871        context.visited_handling()
872    };
873
874    let candidate_not_found = if is_sibling_combinator {
875        SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
876    } else {
877        SelectorMatchingResult::NotMatchedGlobally
878    };
879
880    let mut element = element.clone();
881    loop {
882        if element.is_link() {
883            visited_handling = VisitedHandlingMode::AllLinksUnvisited;
884        }
885
886        let NextElement {
887            next_element,
888            featureless,
889        } = next_element_for_combinator(&element, combinator, &context);
890        element = match next_element {
891            None => return candidate_not_found,
892            Some(e) => e,
893        };
894
895        let result = context.with_visited_handling_mode(visited_handling, |context| {
896            context.with_featureless(featureless, |context| {
897                matches_complex_selector_internal(
898                    selector_iter.clone(),
899                    &element,
900                    context,
901                    rightmost,
902                    first_subject_compound,
903                )
904            })
905        });
906
907        // Return the status immediately if it is one of the global states.
908        match result {
909            SelectorMatchingResult::Matched => {
910                debug_assert!(
911                    matches_compound_selector.to_bool(true),
912                    "Compound didn't match?"
913                );
914                if !matches_compound_selector.to_bool(false) {
915                    return SelectorMatchingResult::Unknown;
916                }
917                return result;
918            },
919            SelectorMatchingResult::Unknown | SelectorMatchingResult::NotMatchedGlobally => {
920                return result
921            },
922            _ => {},
923        }
924
925        match combinator {
926            Combinator::Descendant => {
927                // The Descendant combinator and the status is
928                // NotMatchedAndRestartFromClosestLaterSibling or
929                // NotMatchedAndRestartFromClosestDescendant, or the LaterSibling combinator and
930                // the status is NotMatchedAndRestartFromClosestDescendant, we can continue to
931                // matching on the next candidate element.
932            },
933            Combinator::Child => {
934                // Upgrade the failure status to NotMatchedAndRestartFromClosestDescendant.
935                return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant;
936            },
937            Combinator::LaterSibling => {
938                // If the failure status is NotMatchedAndRestartFromClosestDescendant and combinator is
939                // LaterSibling, give up this LaterSibling matching and restart from the closest
940                // descendant combinator.
941                if matches!(
942                    result,
943                    SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
944                ) {
945                    return result;
946                }
947            },
948            Combinator::NextSibling
949            | Combinator::PseudoElement
950            | Combinator::Part
951            | Combinator::SlotAssignment => {
952                // NOTE(emilio): Conceptually, PseudoElement / Part / SlotAssignment should return
953                // `candidate_not_found`, but it doesn't matter in practice since they don't have
954                // sibling / descendant combinators to the right of them. This hopefully saves one
955                // branch.
956                return result;
957            },
958        }
959
960        if featureless {
961            // A featureless element didn't match the selector, we can stop matching now rather
962            // than looking at following elements for our combinator.
963            return candidate_not_found;
964        }
965    }
966}
967
968#[inline]
969fn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool
970where
971    E: Element,
972{
973    let name = select_name(element, &local_name.name, &local_name.lower_name).borrow();
974    element.has_local_name(name)
975}
976
977fn matches_part<E>(
978    element: &E,
979    parts: &[<E::Impl as SelectorImpl>::Identifier],
980    context: &mut MatchingContext<E::Impl>,
981) -> bool
982where
983    E: Element,
984{
985    let mut hosts = SmallVec::<[E; 4]>::new();
986
987    let mut host = match element.containing_shadow_host() {
988        Some(h) => h,
989        None => return false,
990    };
991
992    let current_host = context.current_host;
993    if current_host != Some(host.opaque()) {
994        loop {
995            let outer_host = host.containing_shadow_host();
996            if outer_host.as_ref().map(|h| h.opaque()) == current_host {
997                break;
998            }
999            let outer_host = match outer_host {
1000                Some(h) => h,
1001                None => return false,
1002            };
1003            // TODO(emilio): if worth it, we could early return if
1004            // host doesn't have the exportparts attribute.
1005            hosts.push(host);
1006            host = outer_host;
1007        }
1008    }
1009
1010    // Translate the part into the right scope.
1011    parts.iter().all(|part| {
1012        let mut part = part.clone();
1013        for host in hosts.iter().rev() {
1014            part = match host.imported_part(&part) {
1015                Some(p) => p,
1016                None => return false,
1017            };
1018        }
1019        element.is_part(&part)
1020    })
1021}
1022
1023fn matches_host<E>(
1024    element: &E,
1025    selector: Option<&Selector<E::Impl>>,
1026    context: &mut MatchingContext<E::Impl>,
1027    rightmost: SubjectOrPseudoElement,
1028) -> KleeneValue
1029where
1030    E: Element,
1031{
1032    let host = match context.shadow_host() {
1033        Some(h) => h,
1034        None => return KleeneValue::False,
1035    };
1036    if host != element.opaque() {
1037        return KleeneValue::False;
1038    }
1039    let Some(selector) = selector else {
1040        return KleeneValue::True;
1041    };
1042    context.nest(|context| {
1043        context.with_featureless(false, |context| {
1044            matches_complex_selector(selector.iter(), element, context, rightmost)
1045        })
1046    })
1047}
1048
1049fn matches_slotted<E>(
1050    element: &E,
1051    selector: &Selector<E::Impl>,
1052    context: &mut MatchingContext<E::Impl>,
1053    rightmost: SubjectOrPseudoElement,
1054) -> KleeneValue
1055where
1056    E: Element,
1057{
1058    // <slots> are never flattened tree slottables.
1059    if element.is_html_slot_element() {
1060        return KleeneValue::False;
1061    }
1062    context.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))
1063}
1064
1065fn matches_rare_attribute_selector<E>(
1066    element: &E,
1067    attr_sel: &AttrSelectorWithOptionalNamespace<E::Impl>,
1068) -> bool
1069where
1070    E: Element,
1071{
1072    let empty_string;
1073    let namespace = match attr_sel.namespace() {
1074        Some(ns) => ns,
1075        None => {
1076            empty_string = crate::parser::namespace_empty_string::<E::Impl>();
1077            NamespaceConstraint::Specific(&empty_string)
1078        },
1079    };
1080    element.attr_matches(
1081        &namespace,
1082        select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower),
1083        &match attr_sel.operation {
1084            ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists,
1085            ParsedAttrSelectorOperation::WithValue {
1086                operator,
1087                case_sensitivity,
1088                ref value,
1089            } => AttrSelectorOperation::WithValue {
1090                operator,
1091                case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),
1092                value,
1093            },
1094        },
1095    )
1096}
1097
1098/// There are relatively few selectors in a given compound that may match a featureless element.
1099/// Instead of adding a check to every selector that may not match, we handle it here in an out of
1100/// line path.
1101pub(crate) fn compound_matches_featureless_host<Impl: SelectorImpl>(
1102    iter: &mut SelectorIter<Impl>,
1103    scope_matches_featureless_host: bool,
1104) -> MatchesFeaturelessHost {
1105    let mut matches = MatchesFeaturelessHost::Only;
1106    for component in iter {
1107        match component {
1108            Component::Scope | Component::ImplicitScope if scope_matches_featureless_host => {},
1109            // :host only matches featureless elements.
1110            Component::Host(..) => {},
1111            // Pseudo-elements are allowed to match as well.
1112            Component::PseudoElement(..) => {},
1113            // We allow logical pseudo-classes, but we'll fail matching of the inner selectors if
1114            // necessary.
1115            Component::Is(ref l) | Component::Where(ref l) => {
1116                let mut any_yes = false;
1117                let mut any_no = false;
1118                for selector in l.slice() {
1119                    match selector.matches_featureless_host(scope_matches_featureless_host) {
1120                        MatchesFeaturelessHost::Never => {
1121                            any_no = true;
1122                        },
1123                        MatchesFeaturelessHost::Yes => {
1124                            any_yes = true;
1125                            any_no = true;
1126                        },
1127                        MatchesFeaturelessHost::Only => {
1128                            any_yes = true;
1129                        },
1130                    }
1131                }
1132                if !any_yes {
1133                    return MatchesFeaturelessHost::Never;
1134                }
1135                if any_no {
1136                    // Potentially downgrade since we might match non-featureless elements too.
1137                    matches = MatchesFeaturelessHost::Yes;
1138                }
1139            },
1140            Component::Negation(ref l) => {
1141                // For now preserving behavior, see
1142                // https://github.com/w3c/csswg-drafts/issues/10179 for existing resolutions that
1143                // tweak this behavior.
1144                for selector in l.slice() {
1145                    if selector.matches_featureless_host(scope_matches_featureless_host)
1146                        != MatchesFeaturelessHost::Only
1147                    {
1148                        return MatchesFeaturelessHost::Never;
1149                    }
1150                }
1151            },
1152            // Other components don't match the host scope.
1153            _ => return MatchesFeaturelessHost::Never,
1154        }
1155    }
1156    matches
1157}
1158
1159/// Determines whether the given element matches the given compound selector.
1160#[inline]
1161fn matches_compound_selector<E>(
1162    selector_iter: &mut SelectorIter<E::Impl>,
1163    element: &E,
1164    context: &mut MatchingContext<E::Impl>,
1165    rightmost: SubjectOrPseudoElement,
1166) -> KleeneValue
1167where
1168    E: Element,
1169{
1170    if context.featureless()
1171        && compound_matches_featureless_host(
1172            &mut selector_iter.clone(),
1173            /* scope_matches_featureless_host = */ true,
1174        ) == MatchesFeaturelessHost::Never
1175    {
1176        return KleeneValue::False;
1177    }
1178    let quirks_data = if context.quirks_mode() == QuirksMode::Quirks {
1179        Some(selector_iter.clone())
1180    } else {
1181        None
1182    };
1183    let mut local_context = LocalMatchingContext {
1184        shared: context,
1185        rightmost,
1186        quirks_data,
1187    };
1188    KleeneValue::any_false(selector_iter, |simple| {
1189        matches_simple_selector(simple, element, &mut local_context)
1190    })
1191}
1192
1193/// Determines whether the given element matches the given single selector.
1194fn matches_simple_selector<E>(
1195    selector: &Component<E::Impl>,
1196    element: &E,
1197    context: &mut LocalMatchingContext<E::Impl>,
1198) -> KleeneValue
1199where
1200    E: Element,
1201{
1202    debug_assert!(context.shared.is_nested() || !context.shared.in_negation());
1203    let rightmost = context.rightmost;
1204    KleeneValue::from(match *selector {
1205        Component::ID(ref id) => {
1206            element.has_id(id, context.shared.classes_and_ids_case_sensitivity())
1207        },
1208        Component::Class(ref class) => {
1209            element.has_class(class, context.shared.classes_and_ids_case_sensitivity())
1210        },
1211        Component::LocalName(ref local_name) => matches_local_name(element, local_name),
1212        Component::AttributeInNoNamespaceExists {
1213            ref local_name,
1214            ref local_name_lower,
1215        } => element.has_attr_in_no_namespace(select_name(element, local_name, local_name_lower)),
1216        Component::AttributeInNoNamespace {
1217            ref local_name,
1218            ref value,
1219            operator,
1220            case_sensitivity,
1221        } => element.attr_matches(
1222            &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
1223            local_name,
1224            &AttrSelectorOperation::WithValue {
1225                operator,
1226                case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),
1227                value,
1228            },
1229        ),
1230        Component::AttributeOther(ref attr_sel) => {
1231            matches_rare_attribute_selector(element, attr_sel)
1232        },
1233        Component::Part(ref parts) => matches_part(element, parts, &mut context.shared),
1234        Component::Slotted(ref selector) => {
1235            return matches_slotted(element, selector, &mut context.shared, rightmost);
1236        },
1237        Component::PseudoElement(ref pseudo) => {
1238            element.match_pseudo_element(pseudo, context.shared)
1239        },
1240        Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true,
1241        Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
1242            element.has_namespace(&url.borrow())
1243        },
1244        Component::ExplicitNoNamespace => {
1245            let ns = crate::parser::namespace_empty_string::<E::Impl>();
1246            element.has_namespace(&ns.borrow())
1247        },
1248        Component::NonTSPseudoClass(ref pc) => {
1249            if let Some(ref iter) = context.quirks_data {
1250                if pc.is_active_or_hover()
1251                    && !element.is_link()
1252                    && hover_and_active_quirk_applies(iter, context.shared, context.rightmost)
1253                {
1254                    return KleeneValue::False;
1255                }
1256            }
1257            element.match_non_ts_pseudo_class(pc, &mut context.shared)
1258        },
1259        Component::Root => element.is_root(),
1260        Component::Empty => {
1261            if context.shared.needs_selector_flags() {
1262                element.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);
1263            }
1264            element.is_empty()
1265        },
1266        Component::Host(ref selector) => {
1267            return matches_host(element, selector.as_ref(), &mut context.shared, rightmost);
1268        },
1269        Component::ParentSelector => match context.shared.scope_element {
1270            Some(ref scope_element) => element.opaque() == *scope_element,
1271            None => element.is_root(),
1272        },
1273        Component::Scope | Component::ImplicitScope => {
1274            if let Some(may_return_unknown) = context.shared.matching_for_invalidation_comparison()
1275            {
1276                return if may_return_unknown {
1277                    KleeneValue::Unknown
1278                } else {
1279                    KleeneValue::from(!context.shared.in_negation())
1280                };
1281            } else {
1282                match context.shared.scope_element {
1283                    Some(ref scope_element) => element.opaque() == *scope_element,
1284                    None => element.is_root(),
1285                }
1286            }
1287        },
1288        Component::Nth(ref nth_data) => {
1289            return matches_generic_nth_child(element, context.shared, nth_data, &[], rightmost);
1290        },
1291        Component::NthOf(ref nth_of_data) => {
1292            return context.shared.nest(|context| {
1293                matches_generic_nth_child(
1294                    element,
1295                    context,
1296                    nth_of_data.nth_data(),
1297                    nth_of_data.selectors(),
1298                    rightmost,
1299                )
1300            })
1301        },
1302        Component::Is(ref list) | Component::Where(ref list) => {
1303            return context.shared.nest(|context| {
1304                matches_complex_selector_list(list.slice(), element, context, rightmost)
1305            })
1306        },
1307        Component::Negation(ref list) => {
1308            return context.shared.nest_for_negation(|context| {
1309                !matches_complex_selector_list(list.slice(), element, context, rightmost)
1310            })
1311        },
1312        Component::Has(ref relative_selectors) => {
1313            return match_relative_selectors(
1314                relative_selectors,
1315                element,
1316                context.shared,
1317                rightmost,
1318            );
1319        },
1320        Component::Combinator(_) => unsafe {
1321            debug_unreachable!("Shouldn't try to selector-match combinators")
1322        },
1323        Component::RelativeSelectorAnchor => {
1324            let anchor = context.shared.relative_selector_anchor();
1325            // We may match inner relative selectors, in which case we want to always match.
1326            anchor.map_or(true, |a| a == element.opaque())
1327        },
1328        Component::Invalid(..) => false,
1329    })
1330}
1331
1332#[inline(always)]
1333pub fn select_name<'a, E: Element, T: PartialEq>(
1334    element: &E,
1335    local_name: &'a T,
1336    local_name_lower: &'a T,
1337) -> &'a T {
1338    if local_name == local_name_lower || element.is_html_element_in_html_document() {
1339        local_name_lower
1340    } else {
1341        local_name
1342    }
1343}
1344
1345#[inline(always)]
1346pub fn to_unconditional_case_sensitivity<'a, E: Element>(
1347    parsed: ParsedCaseSensitivity,
1348    element: &E,
1349) -> CaseSensitivity {
1350    match parsed {
1351        ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => {
1352            CaseSensitivity::CaseSensitive
1353        },
1354        ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
1355        ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
1356            if element.is_html_element_in_html_document() {
1357                CaseSensitivity::AsciiCaseInsensitive
1358            } else {
1359                CaseSensitivity::CaseSensitive
1360            }
1361        },
1362    }
1363}
1364
1365fn matches_generic_nth_child<E>(
1366    element: &E,
1367    context: &mut MatchingContext<E::Impl>,
1368    nth_data: &NthSelectorData,
1369    selectors: &[Selector<E::Impl>],
1370    rightmost: SubjectOrPseudoElement,
1371) -> KleeneValue
1372where
1373    E: Element,
1374{
1375    if element.ignores_nth_child_selectors() {
1376        return KleeneValue::False;
1377    }
1378    let has_selectors = !selectors.is_empty();
1379    let selectors_match = !has_selectors
1380        || matches_complex_selector_list(selectors, element, context, rightmost).to_bool(true);
1381    if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {
1382        // Skip expensive indexing math in invalidation.
1383        return if selectors_match && may_return_unknown {
1384            KleeneValue::Unknown
1385        } else {
1386            KleeneValue::from(selectors_match && !context.in_negation())
1387        };
1388    }
1389
1390    let NthSelectorData { ty, an_plus_b, .. } = *nth_data;
1391    let is_of_type = ty.is_of_type();
1392    if ty.is_only() {
1393        debug_assert!(
1394            !has_selectors,
1395            ":only-child and :only-of-type cannot have a selector list!"
1396        );
1397        return KleeneValue::from(
1398            matches_generic_nth_child(
1399                element,
1400                context,
1401                &NthSelectorData::first(is_of_type),
1402                selectors,
1403                rightmost,
1404            )
1405            .to_bool(true)
1406                && matches_generic_nth_child(
1407                    element,
1408                    context,
1409                    &NthSelectorData::last(is_of_type),
1410                    selectors,
1411                    rightmost,
1412                )
1413                .to_bool(true),
1414        );
1415    }
1416
1417    let is_from_end = ty.is_from_end();
1418
1419    // It's useful to know whether this can only select the first/last element
1420    // child for optimization purposes, see the `HAS_EDGE_CHILD_SELECTOR` flag.
1421    let is_edge_child_selector = nth_data.is_simple_edge() && !has_selectors;
1422
1423    if context.needs_selector_flags() {
1424        let mut flags = if is_edge_child_selector {
1425            ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR
1426        } else if is_from_end {
1427            ElementSelectorFlags::HAS_SLOW_SELECTOR
1428        } else {
1429            ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS
1430        };
1431        flags |= if has_selectors {
1432            ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF
1433        } else {
1434            ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH
1435        };
1436        element.apply_selector_flags(flags);
1437    }
1438
1439    if !selectors_match {
1440        return KleeneValue::False;
1441    }
1442
1443    // :first/last-child are rather trivial to match, don't bother with the
1444    // cache.
1445    if is_edge_child_selector {
1446        return if is_from_end {
1447            element.next_sibling_element()
1448        } else {
1449            element.prev_sibling_element()
1450        }
1451        .is_none()
1452        .into();
1453    }
1454
1455    // Lookup or compute the index.
1456    let index = if let Some(i) = context
1457        .nth_index_cache(is_of_type, is_from_end, selectors)
1458        .lookup(element.opaque())
1459    {
1460        i
1461    } else {
1462        let i = nth_child_index(
1463            element,
1464            context,
1465            selectors,
1466            is_of_type,
1467            is_from_end,
1468            /* check_cache = */ true,
1469            rightmost,
1470        );
1471        context
1472            .nth_index_cache(is_of_type, is_from_end, selectors)
1473            .insert(element.opaque(), i);
1474        i
1475    };
1476    debug_assert_eq!(
1477        index,
1478        nth_child_index(
1479            element,
1480            context,
1481            selectors,
1482            is_of_type,
1483            is_from_end,
1484            /* check_cache = */ false,
1485            rightmost,
1486        ),
1487        "invalid cache"
1488    );
1489
1490    an_plus_b.matches_index(index).into()
1491}
1492
1493#[inline]
1494fn nth_child_index<E>(
1495    element: &E,
1496    context: &mut MatchingContext<E::Impl>,
1497    selectors: &[Selector<E::Impl>],
1498    is_of_type: bool,
1499    is_from_end: bool,
1500    check_cache: bool,
1501    rightmost: SubjectOrPseudoElement,
1502) -> i32
1503where
1504    E: Element,
1505{
1506    // The traversal mostly processes siblings left to right. So when we walk
1507    // siblings to the right when computing NthLast/NthLastOfType we're unlikely
1508    // to get cache hits along the way. As such, we take the hit of walking the
1509    // siblings to the left checking the cache in the is_from_end case (this
1510    // matches what Gecko does). The indices-from-the-left is handled during the
1511    // regular look further below.
1512    if check_cache
1513        && is_from_end
1514        && !context
1515            .nth_index_cache(is_of_type, is_from_end, selectors)
1516            .is_empty()
1517    {
1518        let mut index: i32 = 1;
1519        let mut curr = element.clone();
1520        while let Some(e) = curr.prev_sibling_element() {
1521            curr = e;
1522            let matches = if is_of_type {
1523                element.is_same_type(&curr)
1524            } else if !selectors.is_empty() {
1525                matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)
1526            } else {
1527                true
1528            };
1529            if !matches {
1530                continue;
1531            }
1532            if let Some(i) = context
1533                .nth_index_cache(is_of_type, is_from_end, selectors)
1534                .lookup(curr.opaque())
1535            {
1536                return i - index;
1537            }
1538            index += 1;
1539        }
1540    }
1541
1542    let mut index: i32 = 1;
1543    let mut curr = element.clone();
1544    let next = |e: E| {
1545        if is_from_end {
1546            e.next_sibling_element()
1547        } else {
1548            e.prev_sibling_element()
1549        }
1550    };
1551    while let Some(e) = next(curr) {
1552        curr = e;
1553        let matches = if is_of_type {
1554            element.is_same_type(&curr)
1555        } else if !selectors.is_empty() {
1556            matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)
1557        } else {
1558            true
1559        };
1560        if !matches {
1561            continue;
1562        }
1563        // If we're computing indices from the left, check each element in the
1564        // cache. We handle the indices-from-the-right case at the top of this
1565        // function.
1566        if !is_from_end && check_cache {
1567            if let Some(i) = context
1568                .nth_index_cache(is_of_type, is_from_end, selectors)
1569                .lookup(curr.opaque())
1570            {
1571                return i + index;
1572            }
1573        }
1574        index += 1;
1575    }
1576
1577    index
1578}