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