Skip to main content

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