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