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
321fn complex_selector_early_reject_by_local_name<E: Element>(
322    list: &SelectorList<E::Impl>,
323    element: &E,
324) -> bool {
325    list.slice()
326        .iter()
327        .all(|s| early_reject_by_local_name(s, 0, element))
328}
329
330/// Returns true if this compound would not match the given element by due
331/// to a local name selector (If one exists).
332pub fn early_reject_by_local_name<E: Element>(
333    selector: &Selector<E::Impl>,
334    from_offset: usize,
335    element: &E,
336) -> bool {
337    let iter = selector.iter_from(from_offset);
338    for component in iter {
339        if match component {
340            Component::LocalName(name) => !matches_local_name(element, name),
341            Component::Is(list) | Component::Where(list) => {
342                complex_selector_early_reject_by_local_name(list, element)
343            },
344            _ => continue,
345        } {
346            return true;
347        }
348    }
349    false
350}
351
352/// Matches a compound selector belonging to `selector`, starting at offset
353/// `from_offset`, matching left to right.
354///
355/// Requires that `from_offset` points to a `Combinator`.
356///
357/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
358/// complex selector, but it happens to be the case we don't need it.
359pub fn matches_compound_selector_from<E>(
360    selector: &Selector<E::Impl>,
361    mut from_offset: usize,
362    context: &mut MatchingContext<E::Impl>,
363    element: &E,
364) -> CompoundSelectorMatchingResult
365where
366    E: Element,
367{
368    debug_assert!(
369        !context
370            .matching_for_invalidation_comparison()
371            .unwrap_or(false),
372        "CompoundSelectorMatchingResult doesn't support unknown"
373    );
374    if cfg!(debug_assertions) && from_offset != 0 {
375        selector.combinator_at_parse_order(from_offset - 1); // This asserts.
376    }
377
378    let mut local_context = LocalMatchingContext {
379        shared: context,
380        // We have no info if this is an outer selector. This function is called in
381        // an invalidation context, which only calls this for non-subject (i.e.
382        // Non-rightmost) positions.
383        rightmost: SubjectOrPseudoElement::No,
384        quirks_data: None,
385    };
386
387    // Find the end of the selector or the next combinator, then match
388    // backwards, so that we match in the same order as
389    // matches_complex_selector, which is usually faster.
390    let start_offset = from_offset;
391    for component in selector.iter_raw_parse_order_from(from_offset) {
392        if matches!(*component, Component::Combinator(..)) {
393            debug_assert_ne!(from_offset, 0, "Selector started with a combinator?");
394            break;
395        }
396
397        from_offset += 1;
398    }
399
400    debug_assert!(from_offset >= 1);
401    debug_assert!(from_offset <= selector.len());
402
403    let iter = selector.iter_from(selector.len() - from_offset);
404    debug_assert!(
405        iter.clone().next().is_some() || from_offset != selector.len(),
406        "Got the math wrong: {:?} | {:?} | {} {}",
407        selector,
408        selector.iter_raw_match_order().as_slice(),
409        from_offset,
410        start_offset
411    );
412
413    debug_assert!(
414        !local_context.shared.featureless(),
415        "Invalidating featureless element somehow?"
416    );
417
418    for component in iter {
419        let result = matches_simple_selector(component, element, &mut local_context);
420        debug_assert!(
421            result != KleeneValue::Unknown,
422            "Returned unknown in non invalidation context?"
423        );
424        if !result.to_bool(true) {
425            return CompoundSelectorMatchingResult::NotMatched;
426        }
427    }
428
429    if from_offset != selector.len() {
430        return CompoundSelectorMatchingResult::Matched {
431            next_combinator_offset: from_offset,
432        };
433    }
434
435    CompoundSelectorMatchingResult::FullyMatched
436}
437
438/// Matches a complex selector.
439#[inline(always)]
440fn matches_complex_selector<E>(
441    mut iter: SelectorIter<E::Impl>,
442    element: &E,
443    context: &mut MatchingContext<E::Impl>,
444    rightmost: SubjectOrPseudoElement,
445) -> KleeneValue
446where
447    E: Element,
448{
449    // If this is the special pseudo-element mode, consume the ::pseudo-element
450    // before proceeding, since the caller has already handled that part.
451    if context.matching_mode() == MatchingMode::ForStatelessPseudoElement && !context.is_nested() {
452        // Consume the pseudo.
453        match *iter.next().unwrap() {
454            Component::PseudoElement(ref pseudo) => {
455                if let Some(ref f) = context.pseudo_element_matching_fn {
456                    if !f(pseudo) {
457                        return KleeneValue::False;
458                    }
459                }
460            },
461            ref other => {
462                debug_assert!(
463                    false,
464                    "Used MatchingMode::ForStatelessPseudoElement \
465                     in a non-pseudo selector {:?}",
466                    other
467                );
468                return KleeneValue::False;
469            },
470        }
471
472        if !iter.matches_for_stateless_pseudo_element() {
473            return KleeneValue::False;
474        }
475
476        // Advance to the non-pseudo-element part of the selector.
477        let next_sequence = iter.next_sequence().unwrap();
478        debug_assert_eq!(next_sequence, Combinator::PseudoElement);
479    }
480
481    matches_complex_selector_internal(
482        iter,
483        element,
484        context,
485        rightmost,
486        SubjectOrPseudoElement::Yes,
487    )
488    .into()
489}
490
491/// Matches each selector of a list as a complex selector
492fn matches_complex_selector_list<E: Element>(
493    list: &[Selector<E::Impl>],
494    element: &E,
495    context: &mut MatchingContext<E::Impl>,
496    rightmost: SubjectOrPseudoElement,
497) -> KleeneValue {
498    KleeneValue::any(list.iter(), |selector| {
499        matches_complex_selector(selector.iter(), element, context, rightmost)
500    })
501}
502
503fn matches_relative_selector<E: Element>(
504    relative_selector: &RelativeSelector<E::Impl>,
505    element: &E,
506    context: &mut MatchingContext<E::Impl>,
507    rightmost: SubjectOrPseudoElement,
508) -> bool {
509    // Overall, we want to mark the path that we've traversed so that when an element
510    // is invalidated, we early-reject unnecessary relative selector invalidations.
511    if relative_selector.match_hint.is_descendant_direction() {
512        if context.needs_selector_flags() {
513            element.apply_selector_flags(
514                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
515            );
516        }
517        let mut next_element = element.first_element_child();
518        while let Some(el) = next_element {
519            if context.needs_selector_flags() {
520                el.apply_selector_flags(
521                    ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
522                );
523            }
524            let mut matched = matches_complex_selector(
525                relative_selector.selector.iter(),
526                &el,
527                context,
528                rightmost,
529            )
530            .to_bool(true);
531            if !matched && relative_selector.match_hint.is_subtree() {
532                matched = matches_relative_selector_subtree(
533                    &relative_selector.selector,
534                    &el,
535                    context,
536                    rightmost,
537                );
538            }
539            if matched {
540                return true;
541            }
542            next_element = el.next_sibling_element();
543        }
544    } else {
545        debug_assert!(
546            matches!(
547                relative_selector.match_hint,
548                RelativeSelectorMatchHint::InNextSibling
549                    | RelativeSelectorMatchHint::InNextSiblingSubtree
550                    | RelativeSelectorMatchHint::InSibling
551                    | RelativeSelectorMatchHint::InSiblingSubtree
552            ),
553            "Not descendant direction, but also not sibling direction?"
554        );
555        if context.needs_selector_flags() {
556            element.apply_selector_flags(
557                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,
558            );
559        }
560        let sibling_flag = if relative_selector.match_hint.is_subtree() {
561            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
562        } else {
563            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
564        };
565        let mut next_element = element.next_sibling_element();
566        while let Some(el) = next_element {
567            if context.needs_selector_flags() {
568                el.apply_selector_flags(sibling_flag);
569            }
570            let matched = if relative_selector.match_hint.is_subtree() {
571                matches_relative_selector_subtree(
572                    &relative_selector.selector,
573                    &el,
574                    context,
575                    rightmost,
576                )
577            } else {
578                matches_complex_selector(relative_selector.selector.iter(), &el, context, rightmost)
579                    .to_bool(true)
580            };
581            if matched {
582                return true;
583            }
584            if relative_selector.match_hint.is_next_sibling() {
585                break;
586            }
587            next_element = el.next_sibling_element();
588        }
589    }
590    return false;
591}
592
593fn relative_selector_match_early<E: Element>(
594    selector: &RelativeSelector<E::Impl>,
595    element: &E,
596    context: &mut MatchingContext<E::Impl>,
597) -> Option<bool> {
598    // See if we can return a cached result.
599    if let Some(cached) = context
600        .selector_caches
601        .relative_selector
602        .lookup(element.opaque(), selector)
603    {
604        return Some(cached.matched());
605    }
606    // See if we can fast-reject.
607    if context
608        .selector_caches
609        .relative_selector_filter_map
610        .fast_reject(element, selector, context.quirks_mode())
611    {
612        // Alright, add as unmatched to cache.
613        context.selector_caches.relative_selector.add(
614            element.opaque(),
615            selector,
616            RelativeSelectorCachedMatch::NotMatched,
617        );
618        return Some(false);
619    }
620    None
621}
622
623fn match_relative_selectors<E: Element>(
624    selectors: &[RelativeSelector<E::Impl>],
625    element: &E,
626    context: &mut MatchingContext<E::Impl>,
627    rightmost: SubjectOrPseudoElement,
628) -> KleeneValue {
629    if context.relative_selector_anchor().is_some() {
630        // FIXME(emilio): This currently can happen with nesting, and it's not fully
631        // correct, arguably. But the ideal solution isn't super-clear either. For now,
632        // cope with it and explicitly reject it at match time. See [1] for discussion.
633        //
634        // [1]: https://github.com/w3c/csswg-drafts/issues/9600
635        return KleeneValue::False;
636    }
637    if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {
638        // In the context of invalidation, :has is expensive, especially because we
639        // can't use caching/filtering due to now/then matches. DOM structure also
640        // may have changed.
641        return if may_return_unknown {
642            KleeneValue::Unknown
643        } else {
644            KleeneValue::from(!context.in_negation())
645        };
646    }
647    context
648        .nest_for_relative_selector(element.opaque(), |context| {
649            do_match_relative_selectors(selectors, element, context, rightmost)
650        })
651        .into()
652}
653
654/// Matches a relative selector in a list of relative selectors.
655fn do_match_relative_selectors<E: Element>(
656    selectors: &[RelativeSelector<E::Impl>],
657    element: &E,
658    context: &mut MatchingContext<E::Impl>,
659    rightmost: SubjectOrPseudoElement,
660) -> bool {
661    // Due to style sharing implications (See style sharing code), we mark the current styling context
662    // to mark elements considered for :has matching. Additionally, we want to mark the elements themselves,
663    // since we don't want to indiscriminately invalidate every element as a potential anchor.
664    if rightmost == SubjectOrPseudoElement::Yes {
665        if context.needs_selector_flags() {
666            element.apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR);
667        }
668    } else {
669        if context.needs_selector_flags() {
670            element
671                .apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT);
672        }
673    }
674
675    for relative_selector in selectors.iter() {
676        if let Some(result) = relative_selector_match_early(relative_selector, element, context) {
677            if result {
678                return true;
679            }
680            // Early return indicates no match, continue to next selector.
681            continue;
682        }
683
684        let matched = matches_relative_selector(relative_selector, element, context, rightmost);
685        context.selector_caches.relative_selector.add(
686            element.opaque(),
687            relative_selector,
688            if matched {
689                RelativeSelectorCachedMatch::Matched
690            } else {
691                RelativeSelectorCachedMatch::NotMatched
692            },
693        );
694        if matched {
695            return true;
696        }
697    }
698
699    false
700}
701
702fn matches_relative_selector_subtree<E: Element>(
703    selector: &Selector<E::Impl>,
704    element: &E,
705    context: &mut MatchingContext<E::Impl>,
706    rightmost: SubjectOrPseudoElement,
707) -> bool {
708    let mut current = element.first_element_child();
709
710    while let Some(el) = current {
711        if context.needs_selector_flags() {
712            el.apply_selector_flags(
713                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
714            );
715        }
716        if matches_complex_selector(selector.iter(), &el, context, rightmost).to_bool(true) {
717            return true;
718        }
719
720        if matches_relative_selector_subtree(selector, &el, context, rightmost) {
721            return true;
722        }
723
724        current = el.next_sibling_element();
725    }
726
727    false
728}
729
730/// Whether the :hover and :active quirk applies.
731///
732/// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk
733fn hover_and_active_quirk_applies<Impl: SelectorImpl>(
734    selector_iter: &SelectorIter<Impl>,
735    context: &MatchingContext<Impl>,
736    rightmost: SubjectOrPseudoElement,
737) -> bool {
738    debug_assert_eq!(context.quirks_mode(), QuirksMode::Quirks);
739
740    if context.is_nested() {
741        return false;
742    }
743
744    // This compound selector had a pseudo-element to the right that we
745    // intentionally skipped.
746    if rightmost == SubjectOrPseudoElement::Yes
747        && context.matching_mode() == MatchingMode::ForStatelessPseudoElement
748    {
749        return false;
750    }
751
752    selector_iter.clone().all(|simple| match *simple {
753        Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),
754        _ => false,
755    })
756}
757
758#[derive(Clone, Copy, PartialEq)]
759enum SubjectOrPseudoElement {
760    Yes,
761    No,
762}
763
764fn host_for_part<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>
765where
766    E: Element,
767{
768    let scope = context.current_host;
769    let mut curr = element.containing_shadow_host()?;
770    if scope == Some(curr.opaque()) {
771        return Some(curr);
772    }
773    loop {
774        let parent = curr.containing_shadow_host();
775        if parent.as_ref().map(|h| h.opaque()) == scope {
776            return Some(curr);
777        }
778        curr = parent?;
779    }
780}
781
782fn assigned_slot<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>
783where
784    E: Element,
785{
786    debug_assert!(element
787        .assigned_slot()
788        .map_or(true, |s| s.is_html_slot_element()));
789    let scope = context.current_host?;
790    let mut current_slot = element.assigned_slot()?;
791    while current_slot.containing_shadow_host().unwrap().opaque() != scope {
792        current_slot = current_slot.assigned_slot()?;
793    }
794    Some(current_slot)
795}
796
797struct NextElement<E> {
798    next_element: Option<E>,
799    featureless: bool,
800}
801
802impl<E> NextElement<E> {
803    #[inline(always)]
804    fn new(next_element: Option<E>, featureless: bool) -> Self {
805        Self {
806            next_element,
807            featureless,
808        }
809    }
810}
811
812#[inline(always)]
813fn next_element_for_combinator<E>(
814    element: &E,
815    combinator: Combinator,
816    context: &MatchingContext<E::Impl>,
817) -> NextElement<E>
818where
819    E: Element,
820{
821    match combinator {
822        Combinator::NextSibling | Combinator::LaterSibling => {
823            NextElement::new(element.prev_sibling_element(), false)
824        },
825        Combinator::Child | Combinator::Descendant => {
826            if let Some(parent) = element.parent_element() {
827                return NextElement::new(Some(parent), false);
828            }
829
830            let element = if element.parent_node_is_shadow_root() {
831                element.containing_shadow_host()
832            } else {
833                None
834            };
835            NextElement::new(element, true)
836        },
837        Combinator::Part => NextElement::new(host_for_part(element, context), false),
838        Combinator::SlotAssignment => NextElement::new(assigned_slot(element, context), false),
839        Combinator::PseudoElement => {
840            NextElement::new(element.pseudo_element_originating_element(), false)
841        },
842    }
843}
844
845fn matches_complex_selector_internal<E>(
846    mut selector_iter: SelectorIter<E::Impl>,
847    element: &E,
848    context: &mut MatchingContext<E::Impl>,
849    mut rightmost: SubjectOrPseudoElement,
850    mut first_subject_compound: SubjectOrPseudoElement,
851) -> SelectorMatchingResult
852where
853    E: Element,
854{
855    debug!(
856        "Matching complex selector {:?} for {:?}",
857        selector_iter, element
858    );
859
860    let matches_compound_selector =
861        matches_compound_selector(&mut selector_iter, element, context, rightmost);
862
863    let Some(combinator) = selector_iter.next_sequence() else {
864        return match matches_compound_selector {
865            KleeneValue::True => SelectorMatchingResult::Matched,
866            KleeneValue::Unknown => SelectorMatchingResult::Unknown,
867            KleeneValue::False => {
868                SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling
869            },
870        };
871    };
872
873    let is_pseudo_combinator = combinator.is_pseudo_element();
874    if context.featureless() && !is_pseudo_combinator {
875        // A featureless element shouldn't match any further combinator.
876        // TODO(emilio): Maybe we could avoid the compound matching more eagerly.
877        return SelectorMatchingResult::NotMatchedGlobally;
878    }
879
880    let is_sibling_combinator = combinator.is_sibling();
881    if is_sibling_combinator && context.needs_selector_flags() {
882        // We need the flags even if we don't match.
883        element.apply_selector_flags(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);
884    }
885
886    if matches_compound_selector == KleeneValue::False {
887        // We don't short circuit unknown here, since the rest of the selector
888        // to the left of this compound may still return false.
889        return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
890    }
891
892    if !is_pseudo_combinator {
893        rightmost = SubjectOrPseudoElement::No;
894        first_subject_compound = SubjectOrPseudoElement::No;
895    }
896
897    // Stop matching :visited as soon as we find a link, or a combinator for
898    // something that isn't an ancestor.
899    let mut visited_handling = if is_sibling_combinator {
900        VisitedHandlingMode::AllLinksUnvisited
901    } else {
902        context.visited_handling()
903    };
904
905    let candidate_not_found = if is_sibling_combinator {
906        SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
907    } else {
908        SelectorMatchingResult::NotMatchedGlobally
909    };
910
911    let mut element = element.clone();
912    loop {
913        if element.is_link() {
914            visited_handling = VisitedHandlingMode::AllLinksUnvisited;
915        }
916
917        let NextElement {
918            next_element,
919            featureless,
920        } = next_element_for_combinator(&element, combinator, &context);
921        element = match next_element {
922            None => return candidate_not_found,
923            Some(e) => e,
924        };
925
926        let result = context.with_visited_handling_mode(visited_handling, |context| {
927            context.with_featureless(featureless, |context| {
928                matches_complex_selector_internal(
929                    selector_iter.clone(),
930                    &element,
931                    context,
932                    rightmost,
933                    first_subject_compound,
934                )
935            })
936        });
937
938        // Return the status immediately if it is one of the global states.
939        match result {
940            SelectorMatchingResult::Matched => {
941                debug_assert!(
942                    matches_compound_selector.to_bool(true),
943                    "Compound didn't match?"
944                );
945                if !matches_compound_selector.to_bool(false) {
946                    return SelectorMatchingResult::Unknown;
947                }
948                return result;
949            },
950            SelectorMatchingResult::Unknown | SelectorMatchingResult::NotMatchedGlobally => {
951                return result
952            },
953            _ => {},
954        }
955
956        match combinator {
957            Combinator::Descendant => {
958                // The Descendant combinator and the status is
959                // NotMatchedAndRestartFromClosestLaterSibling or
960                // NotMatchedAndRestartFromClosestDescendant, or the LaterSibling combinator and
961                // the status is NotMatchedAndRestartFromClosestDescendant, we can continue to
962                // matching on the next candidate element.
963            },
964            Combinator::Child => {
965                // Upgrade the failure status to NotMatchedAndRestartFromClosestDescendant.
966                return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant;
967            },
968            Combinator::LaterSibling => {
969                // If the failure status is NotMatchedAndRestartFromClosestDescendant and combinator is
970                // LaterSibling, give up this LaterSibling matching and restart from the closest
971                // descendant combinator.
972                if matches!(
973                    result,
974                    SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
975                ) {
976                    return result;
977                }
978            },
979            Combinator::NextSibling
980            | Combinator::PseudoElement
981            | Combinator::Part
982            | Combinator::SlotAssignment => {
983                // NOTE(emilio): Conceptually, PseudoElement / Part / SlotAssignment should return
984                // `candidate_not_found`, but it doesn't matter in practice since they don't have
985                // sibling / descendant combinators to the right of them. This hopefully saves one
986                // branch.
987                return result;
988            },
989        }
990
991        if featureless {
992            // A featureless element didn't match the selector, we can stop matching now rather
993            // than looking at following elements for our combinator.
994            return candidate_not_found;
995        }
996    }
997}
998
999#[inline]
1000fn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool
1001where
1002    E: Element,
1003{
1004    let name = select_name(element, &local_name.name, &local_name.lower_name).borrow();
1005    element.has_local_name(name)
1006}
1007
1008fn matches_part<E>(
1009    element: &E,
1010    parts: &[<E::Impl as SelectorImpl>::Identifier],
1011    context: &mut MatchingContext<E::Impl>,
1012) -> bool
1013where
1014    E: Element,
1015{
1016    let mut hosts = SmallVec::<[E; 4]>::new();
1017
1018    let mut host = match element.containing_shadow_host() {
1019        Some(h) => h,
1020        None => return false,
1021    };
1022
1023    let current_host = context.current_host;
1024    if current_host != Some(host.opaque()) {
1025        loop {
1026            let outer_host = host.containing_shadow_host();
1027            if outer_host.as_ref().map(|h| h.opaque()) == current_host {
1028                break;
1029            }
1030            let outer_host = match outer_host {
1031                Some(h) => h,
1032                None => return false,
1033            };
1034            // TODO(emilio): if worth it, we could early return if
1035            // host doesn't have the exportparts attribute.
1036            hosts.push(host);
1037            host = outer_host;
1038        }
1039    }
1040
1041    // Translate the part into the right scope.
1042    parts.iter().all(|part| {
1043        let mut part = part.clone();
1044        for host in hosts.iter().rev() {
1045            part = match host.imported_part(&part) {
1046                Some(p) => p,
1047                None => return false,
1048            };
1049        }
1050        element.is_part(&part)
1051    })
1052}
1053
1054fn matches_host<E>(
1055    element: &E,
1056    selector: Option<&Selector<E::Impl>>,
1057    context: &mut MatchingContext<E::Impl>,
1058    rightmost: SubjectOrPseudoElement,
1059) -> KleeneValue
1060where
1061    E: Element,
1062{
1063    let host = match context.shadow_host() {
1064        Some(h) => h,
1065        None => return KleeneValue::False,
1066    };
1067    if host != element.opaque() {
1068        return KleeneValue::False;
1069    }
1070    let Some(selector) = selector else {
1071        return KleeneValue::True;
1072    };
1073    context.nest(|context| {
1074        context.with_featureless(false, |context| {
1075            matches_complex_selector(selector.iter(), element, context, rightmost)
1076        })
1077    })
1078}
1079
1080fn matches_slotted<E>(
1081    element: &E,
1082    selector: &Selector<E::Impl>,
1083    context: &mut MatchingContext<E::Impl>,
1084    rightmost: SubjectOrPseudoElement,
1085) -> KleeneValue
1086where
1087    E: Element,
1088{
1089    // <slots> are never flattened tree slottables.
1090    if element.is_html_slot_element() {
1091        return KleeneValue::False;
1092    }
1093    context.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))
1094}
1095
1096fn matches_rare_attribute_selector<E>(
1097    element: &E,
1098    attr_sel: &AttrSelectorWithOptionalNamespace<E::Impl>,
1099) -> bool
1100where
1101    E: Element,
1102{
1103    let empty_string;
1104    let namespace = match attr_sel.namespace() {
1105        Some(ns) => ns,
1106        None => {
1107            empty_string = crate::parser::namespace_empty_string::<E::Impl>();
1108            NamespaceConstraint::Specific(&empty_string)
1109        },
1110    };
1111    element.attr_matches(
1112        &namespace,
1113        select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower),
1114        &match attr_sel.operation {
1115            ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists,
1116            ParsedAttrSelectorOperation::WithValue {
1117                operator,
1118                case_sensitivity,
1119                ref value,
1120            } => AttrSelectorOperation::WithValue {
1121                operator,
1122                case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),
1123                value,
1124            },
1125        },
1126    )
1127}
1128
1129/// There are relatively few selectors in a given compound that may match a featureless element.
1130/// Instead of adding a check to every selector that may not match, we handle it here in an out of
1131/// line path.
1132pub(crate) fn compound_matches_featureless_host<Impl: SelectorImpl>(
1133    iter: &mut SelectorIter<Impl>,
1134    scope_matches_featureless_host: bool,
1135) -> MatchesFeaturelessHost {
1136    let mut matches = MatchesFeaturelessHost::Only;
1137    for component in iter {
1138        match component {
1139            Component::Scope | Component::ImplicitScope if scope_matches_featureless_host => {},
1140            // :host only matches featureless elements.
1141            Component::Host(..) => {},
1142            // Pseudo-elements are allowed to match as well.
1143            Component::PseudoElement(..) => {},
1144            // We allow logical pseudo-classes, but we'll fail matching of the inner selectors if
1145            // necessary.
1146            Component::Is(ref l) | Component::Where(ref l) => {
1147                let mut any_yes = false;
1148                let mut any_no = false;
1149                for selector in l.slice() {
1150                    match selector.matches_featureless_host(scope_matches_featureless_host) {
1151                        MatchesFeaturelessHost::Never => {
1152                            any_no = true;
1153                        },
1154                        MatchesFeaturelessHost::Yes => {
1155                            any_yes = true;
1156                            any_no = true;
1157                        },
1158                        MatchesFeaturelessHost::Only => {
1159                            any_yes = true;
1160                        },
1161                    }
1162                }
1163                if !any_yes {
1164                    return MatchesFeaturelessHost::Never;
1165                }
1166                if any_no {
1167                    // Potentially downgrade since we might match non-featureless elements too.
1168                    matches = MatchesFeaturelessHost::Yes;
1169                }
1170            },
1171            Component::Negation(ref l) => {
1172                // For now preserving behavior, see
1173                // https://github.com/w3c/csswg-drafts/issues/10179 for existing resolutions that
1174                // tweak this behavior.
1175                for selector in l.slice() {
1176                    if selector.matches_featureless_host(scope_matches_featureless_host)
1177                        != MatchesFeaturelessHost::Only
1178                    {
1179                        return MatchesFeaturelessHost::Never;
1180                    }
1181                }
1182            },
1183            // Other components don't match the host scope.
1184            _ => return MatchesFeaturelessHost::Never,
1185        }
1186    }
1187    matches
1188}
1189
1190/// Determines whether the given element matches the given compound selector.
1191#[inline]
1192fn matches_compound_selector<E>(
1193    selector_iter: &mut SelectorIter<E::Impl>,
1194    element: &E,
1195    context: &mut MatchingContext<E::Impl>,
1196    rightmost: SubjectOrPseudoElement,
1197) -> KleeneValue
1198where
1199    E: Element,
1200{
1201    if context.featureless()
1202        && compound_matches_featureless_host(
1203            &mut selector_iter.clone(),
1204            /* scope_matches_featureless_host = */ true,
1205        ) == MatchesFeaturelessHost::Never
1206    {
1207        return KleeneValue::False;
1208    }
1209    let quirks_data = if context.quirks_mode() == QuirksMode::Quirks {
1210        Some(selector_iter.clone())
1211    } else {
1212        None
1213    };
1214    let mut local_context = LocalMatchingContext {
1215        shared: context,
1216        rightmost,
1217        quirks_data,
1218    };
1219    KleeneValue::any_false(selector_iter, |simple| {
1220        matches_simple_selector(simple, element, &mut local_context)
1221    })
1222}
1223
1224/// Determines whether the given element matches the given single selector.
1225fn matches_simple_selector<E>(
1226    selector: &Component<E::Impl>,
1227    element: &E,
1228    context: &mut LocalMatchingContext<E::Impl>,
1229) -> KleeneValue
1230where
1231    E: Element,
1232{
1233    debug_assert!(context.shared.is_nested() || !context.shared.in_negation());
1234    let rightmost = context.rightmost;
1235    KleeneValue::from(match *selector {
1236        Component::ID(ref id) => {
1237            element.has_id(id, context.shared.classes_and_ids_case_sensitivity())
1238        },
1239        Component::Class(ref class) => {
1240            element.has_class(class, context.shared.classes_and_ids_case_sensitivity())
1241        },
1242        Component::LocalName(ref local_name) => matches_local_name(element, local_name),
1243        Component::AttributeInNoNamespaceExists {
1244            ref local_name,
1245            ref local_name_lower,
1246        } => element.has_attr_in_no_namespace(select_name(element, local_name, local_name_lower)),
1247        Component::AttributeInNoNamespace {
1248            ref local_name,
1249            ref value,
1250            operator,
1251            case_sensitivity,
1252        } => element.attr_matches(
1253            &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
1254            local_name,
1255            &AttrSelectorOperation::WithValue {
1256                operator,
1257                case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),
1258                value,
1259            },
1260        ),
1261        Component::AttributeOther(ref attr_sel) => {
1262            matches_rare_attribute_selector(element, attr_sel)
1263        },
1264        Component::Part(ref parts) => matches_part(element, parts, &mut context.shared),
1265        Component::Slotted(ref selector) => {
1266            return matches_slotted(element, selector, &mut context.shared, rightmost);
1267        },
1268        Component::PseudoElement(ref pseudo) => {
1269            element.match_pseudo_element(pseudo, context.shared)
1270        },
1271        Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true,
1272        Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
1273            element.has_namespace(&url.borrow())
1274        },
1275        Component::ExplicitNoNamespace => {
1276            let ns = crate::parser::namespace_empty_string::<E::Impl>();
1277            element.has_namespace(&ns.borrow())
1278        },
1279        Component::NonTSPseudoClass(ref pc) => {
1280            if let Some(ref iter) = context.quirks_data {
1281                if pc.is_active_or_hover()
1282                    && !element.is_link()
1283                    && hover_and_active_quirk_applies(iter, context.shared, context.rightmost)
1284                {
1285                    return KleeneValue::False;
1286                }
1287            }
1288            element.match_non_ts_pseudo_class(pc, &mut context.shared)
1289        },
1290        Component::Root => element.is_root(),
1291        Component::Empty => {
1292            if context.shared.needs_selector_flags() {
1293                element.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);
1294            }
1295            element.is_empty()
1296        },
1297        Component::Host(ref selector) => {
1298            return matches_host(element, selector.as_ref(), &mut context.shared, rightmost);
1299        },
1300        Component::ParentSelector => match context.shared.scope_element {
1301            Some(ref scope_element) => element.opaque() == *scope_element,
1302            None => element.is_root(),
1303        },
1304        Component::Scope | Component::ImplicitScope => {
1305            if let Some(may_return_unknown) = context.shared.matching_for_invalidation_comparison()
1306            {
1307                return if may_return_unknown {
1308                    KleeneValue::Unknown
1309                } else {
1310                    KleeneValue::from(!context.shared.in_negation())
1311                };
1312            } else {
1313                match context.shared.scope_element {
1314                    Some(ref scope_element) => element.opaque() == *scope_element,
1315                    None => element.is_root(),
1316                }
1317            }
1318        },
1319        Component::Nth(ref nth_data) => {
1320            return matches_generic_nth_child(element, context.shared, nth_data, &[], rightmost);
1321        },
1322        Component::NthOf(ref nth_of_data) => {
1323            return context.shared.nest(|context| {
1324                matches_generic_nth_child(
1325                    element,
1326                    context,
1327                    nth_of_data.nth_data(),
1328                    nth_of_data.selectors(),
1329                    rightmost,
1330                )
1331            })
1332        },
1333        Component::Is(ref list) | Component::Where(ref list) => {
1334            return context.shared.nest(|context| {
1335                matches_complex_selector_list(list.slice(), element, context, rightmost)
1336            })
1337        },
1338        Component::Negation(ref list) => {
1339            return context.shared.nest_for_negation(|context| {
1340                !matches_complex_selector_list(list.slice(), element, context, rightmost)
1341            })
1342        },
1343        Component::Has(ref relative_selectors) => {
1344            return match_relative_selectors(
1345                relative_selectors,
1346                element,
1347                context.shared,
1348                rightmost,
1349            );
1350        },
1351        Component::Combinator(_) => unsafe {
1352            debug_unreachable!("Shouldn't try to selector-match combinators")
1353        },
1354        Component::RelativeSelectorAnchor => {
1355            let anchor = context.shared.relative_selector_anchor();
1356            // We may match inner relative selectors, in which case we want to always match.
1357            anchor.map_or(true, |a| a == element.opaque())
1358        },
1359        Component::Invalid(..) => false,
1360    })
1361}
1362
1363#[inline(always)]
1364pub fn select_name<'a, E: Element, T: PartialEq>(
1365    element: &E,
1366    local_name: &'a T,
1367    local_name_lower: &'a T,
1368) -> &'a T {
1369    if local_name == local_name_lower || element.is_html_element_in_html_document() {
1370        local_name_lower
1371    } else {
1372        local_name
1373    }
1374}
1375
1376#[inline(always)]
1377pub fn to_unconditional_case_sensitivity<'a, E: Element>(
1378    parsed: ParsedCaseSensitivity,
1379    element: &E,
1380) -> CaseSensitivity {
1381    match parsed {
1382        ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => {
1383            CaseSensitivity::CaseSensitive
1384        },
1385        ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
1386        ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
1387            if element.is_html_element_in_html_document() {
1388                CaseSensitivity::AsciiCaseInsensitive
1389            } else {
1390                CaseSensitivity::CaseSensitive
1391            }
1392        },
1393    }
1394}
1395
1396fn matches_generic_nth_child<E>(
1397    element: &E,
1398    context: &mut MatchingContext<E::Impl>,
1399    nth_data: &NthSelectorData,
1400    selectors: &[Selector<E::Impl>],
1401    rightmost: SubjectOrPseudoElement,
1402) -> KleeneValue
1403where
1404    E: Element,
1405{
1406    if element.ignores_nth_child_selectors() {
1407        return KleeneValue::False;
1408    }
1409    let has_selectors = !selectors.is_empty();
1410    let selectors_match = !has_selectors
1411        || matches_complex_selector_list(selectors, element, context, rightmost).to_bool(true);
1412    if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {
1413        // Skip expensive indexing math in invalidation.
1414        return if selectors_match && may_return_unknown {
1415            KleeneValue::Unknown
1416        } else {
1417            KleeneValue::from(selectors_match && !context.in_negation())
1418        };
1419    }
1420
1421    let NthSelectorData { ty, an_plus_b, .. } = *nth_data;
1422    let is_of_type = ty.is_of_type();
1423    if ty.is_only() {
1424        debug_assert!(
1425            !has_selectors,
1426            ":only-child and :only-of-type cannot have a selector list!"
1427        );
1428        return KleeneValue::from(
1429            matches_generic_nth_child(
1430                element,
1431                context,
1432                &NthSelectorData::first(is_of_type),
1433                selectors,
1434                rightmost,
1435            )
1436            .to_bool(true)
1437                && matches_generic_nth_child(
1438                    element,
1439                    context,
1440                    &NthSelectorData::last(is_of_type),
1441                    selectors,
1442                    rightmost,
1443                )
1444                .to_bool(true),
1445        );
1446    }
1447
1448    let is_from_end = ty.is_from_end();
1449
1450    // It's useful to know whether this can only select the first/last element
1451    // child for optimization purposes, see the `HAS_EDGE_CHILD_SELECTOR` flag.
1452    let is_edge_child_selector = nth_data.is_simple_edge() && !has_selectors;
1453
1454    if context.needs_selector_flags() {
1455        let mut flags = if is_edge_child_selector {
1456            ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR
1457        } else if is_from_end {
1458            ElementSelectorFlags::HAS_SLOW_SELECTOR
1459        } else {
1460            ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS
1461        };
1462        flags |= if has_selectors {
1463            ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF
1464        } else {
1465            ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH
1466        };
1467        element.apply_selector_flags(flags);
1468    }
1469
1470    if !selectors_match {
1471        return KleeneValue::False;
1472    }
1473
1474    // :first/last-child are rather trivial to match, don't bother with the
1475    // cache.
1476    if is_edge_child_selector {
1477        return if is_from_end {
1478            element.next_sibling_element()
1479        } else {
1480            element.prev_sibling_element()
1481        }
1482        .is_none()
1483        .into();
1484    }
1485
1486    // Lookup or compute the index.
1487    let index = if let Some(i) = context
1488        .nth_index_cache(is_of_type, is_from_end, selectors)
1489        .lookup(element.opaque())
1490    {
1491        i
1492    } else {
1493        let i = nth_child_index(
1494            element,
1495            context,
1496            selectors,
1497            is_of_type,
1498            is_from_end,
1499            /* check_cache = */ true,
1500            rightmost,
1501        );
1502        context
1503            .nth_index_cache(is_of_type, is_from_end, selectors)
1504            .insert(element.opaque(), i);
1505        i
1506    };
1507    debug_assert_eq!(
1508        index,
1509        nth_child_index(
1510            element,
1511            context,
1512            selectors,
1513            is_of_type,
1514            is_from_end,
1515            /* check_cache = */ false,
1516            rightmost,
1517        ),
1518        "invalid cache"
1519    );
1520
1521    an_plus_b.matches_index(index).into()
1522}
1523
1524#[inline]
1525fn nth_child_index<E>(
1526    element: &E,
1527    context: &mut MatchingContext<E::Impl>,
1528    selectors: &[Selector<E::Impl>],
1529    is_of_type: bool,
1530    is_from_end: bool,
1531    check_cache: bool,
1532    rightmost: SubjectOrPseudoElement,
1533) -> i32
1534where
1535    E: Element,
1536{
1537    // The traversal mostly processes siblings left to right. So when we walk
1538    // siblings to the right when computing NthLast/NthLastOfType we're unlikely
1539    // to get cache hits along the way. As such, we take the hit of walking the
1540    // siblings to the left checking the cache in the is_from_end case (this
1541    // matches what Gecko does). The indices-from-the-left is handled during the
1542    // regular look further below.
1543    if check_cache
1544        && is_from_end
1545        && !context
1546            .nth_index_cache(is_of_type, is_from_end, selectors)
1547            .is_empty()
1548    {
1549        let mut index: i32 = 1;
1550        let mut curr = element.clone();
1551        while let Some(e) = curr.prev_sibling_element() {
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 let Some(i) = context
1564                .nth_index_cache(is_of_type, is_from_end, selectors)
1565                .lookup(curr.opaque())
1566            {
1567                return i - index;
1568            }
1569            index += 1;
1570        }
1571    }
1572
1573    let mut index: i32 = 1;
1574    let mut curr = element.clone();
1575    let next = |e: E| {
1576        if is_from_end {
1577            e.next_sibling_element()
1578        } else {
1579            e.prev_sibling_element()
1580        }
1581    };
1582    while let Some(e) = next(curr) {
1583        curr = e;
1584        let matches = if is_of_type {
1585            element.is_same_type(&curr)
1586        } else if !selectors.is_empty() {
1587            matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)
1588        } else {
1589            true
1590        };
1591        if !matches {
1592            continue;
1593        }
1594        // If we're computing indices from the left, check each element in the
1595        // cache. We handle the indices-from-the-right case at the top of this
1596        // function.
1597        if !is_from_end && check_cache {
1598            if let Some(i) = context
1599                .nth_index_cache(is_of_type, is_from_end, selectors)
1600                .lookup(curr.opaque())
1601            {
1602                return i + index;
1603            }
1604        }
1605        index += 1;
1606    }
1607
1608    index
1609}