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