style/
dom_apis.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
5//! Generic implementations of some DOM APIs so they can be shared between Servo
6//! and Gecko.
7
8use crate::context::QuirksMode;
9use crate::dom::{TDocument, TElement, TNode, TShadowRoot};
10use crate::invalidation::element::invalidation_map::Dependency;
11use crate::invalidation::element::invalidator::{
12    DescendantInvalidationLists, Invalidation, SiblingTraversalMap,
13};
14use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
15use crate::selector_parser::SelectorImpl;
16use crate::values::AtomIdent;
17use selectors::attr::CaseSensitivity;
18use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
19use selectors::matching::{
20    self, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags,
21    SelectorCaches,
22};
23use selectors::parser::{Combinator, Component, LocalName};
24use selectors::{Element, OpaqueElement, SelectorList};
25use smallvec::SmallVec;
26
27/// <https://dom.spec.whatwg.org/#dom-element-matches>
28pub fn element_matches<E>(
29    element: &E,
30    selector_list: &SelectorList<E::Impl>,
31    quirks_mode: QuirksMode,
32) -> bool
33where
34    E: Element,
35{
36    let mut selector_caches = SelectorCaches::default();
37
38    let mut context = MatchingContext::new(
39        MatchingMode::Normal,
40        None,
41        &mut selector_caches,
42        quirks_mode,
43        NeedsSelectorFlags::No,
44        MatchingForInvalidation::No,
45    );
46    context.scope_element = Some(element.opaque());
47    context.current_host = element.containing_shadow_host().map(|e| e.opaque());
48    matching::matches_selector_list(selector_list, element, &mut context)
49}
50
51/// <https://dom.spec.whatwg.org/#dom-element-closest>
52pub fn element_closest<E>(
53    element: E,
54    selector_list: &SelectorList<E::Impl>,
55    quirks_mode: QuirksMode,
56) -> Option<E>
57where
58    E: Element,
59{
60    let mut selector_caches = SelectorCaches::default();
61
62    let mut context = MatchingContext::new(
63        MatchingMode::Normal,
64        None,
65        &mut selector_caches,
66        quirks_mode,
67        NeedsSelectorFlags::No,
68        MatchingForInvalidation::No,
69    );
70    context.scope_element = Some(element.opaque());
71    context.current_host = element.containing_shadow_host().map(|e| e.opaque());
72
73    let mut current = Some(element);
74    while let Some(element) = current.take() {
75        if matching::matches_selector_list(selector_list, &element, &mut context) {
76            return Some(element);
77        }
78        current = element.parent_element();
79    }
80
81    return None;
82}
83
84/// A selector query abstraction, in order to be generic over QuerySelector and
85/// QuerySelectorAll.
86pub trait SelectorQuery<E: TElement> {
87    /// The output of the query.
88    type Output;
89
90    /// Whether the query should stop after the first element has been matched.
91    fn should_stop_after_first_match() -> bool;
92
93    /// Append an element matching after the first query.
94    fn append_element(output: &mut Self::Output, element: E);
95
96    /// Returns true if the output is empty.
97    fn is_empty(output: &Self::Output) -> bool;
98}
99
100/// The result of a querySelectorAll call.
101pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;
102
103/// A query for all the elements in a subtree.
104pub struct QueryAll;
105
106impl<E: TElement> SelectorQuery<E> for QueryAll {
107    type Output = QuerySelectorAllResult<E>;
108
109    fn should_stop_after_first_match() -> bool {
110        false
111    }
112
113    fn append_element(output: &mut Self::Output, element: E) {
114        output.push(element);
115    }
116
117    fn is_empty(output: &Self::Output) -> bool {
118        output.is_empty()
119    }
120}
121
122/// A query for the first in-tree match of all the elements in a subtree.
123pub struct QueryFirst;
124
125impl<E: TElement> SelectorQuery<E> for QueryFirst {
126    type Output = Option<E>;
127
128    fn should_stop_after_first_match() -> bool {
129        true
130    }
131
132    fn append_element(output: &mut Self::Output, element: E) {
133        if output.is_none() {
134            *output = Some(element)
135        }
136    }
137
138    fn is_empty(output: &Self::Output) -> bool {
139        output.is_none()
140    }
141}
142
143struct QuerySelectorProcessor<'a, 'b, E, Q>
144where
145    E: TElement + 'a,
146    Q: SelectorQuery<E>,
147    Q::Output: 'a,
148{
149    results: &'a mut Q::Output,
150    matching_context: MatchingContext<'b, E::Impl>,
151    traversal_map: SiblingTraversalMap<E>,
152    dependencies: &'a [Dependency],
153}
154
155impl<'a, 'b, E, Q> InvalidationProcessor<'a, 'b, E> for QuerySelectorProcessor<'a, 'b, E, Q>
156where
157    E: TElement + 'a,
158    Q: SelectorQuery<E>,
159    Q::Output: 'a,
160{
161    fn light_tree_only(&self) -> bool {
162        true
163    }
164
165    fn check_outer_dependency(&mut self, _: &Dependency, _: E, _: Option<OpaqueElement>) -> bool {
166        debug_assert!(
167            false,
168            "How? We should only have parent-less dependencies here!"
169        );
170        true
171    }
172
173    fn collect_invalidations(
174        &mut self,
175        element: E,
176        self_invalidations: &mut InvalidationVector<'a>,
177        descendant_invalidations: &mut DescendantInvalidationLists<'a>,
178        _sibling_invalidations: &mut InvalidationVector<'a>,
179    ) -> bool {
180        // TODO(emilio): If the element is not a root element, and
181        // selector_list has any descendant combinator, we need to do extra work
182        // in order to handle properly things like:
183        //
184        //   <div id="a">
185        //     <div id="b">
186        //       <div id="c"></div>
187        //     </div>
188        //   </div>
189        //
190        // b.querySelector('#a div'); // Should return "c".
191        //
192        // For now, assert it's a root element.
193        debug_assert!(element.parent_element().is_none());
194
195        let target_vector = if self.matching_context.scope_element.is_some() {
196            &mut descendant_invalidations.dom_descendants
197        } else {
198            self_invalidations
199        };
200
201        for dependency in self.dependencies.iter() {
202            target_vector.push(Invalidation::new(
203                dependency,
204                self.matching_context.current_host.clone(),
205                self.matching_context.scope_element.clone(),
206            ))
207        }
208
209        false
210    }
211
212    fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> {
213        &mut self.matching_context
214    }
215
216    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
217        &self.traversal_map
218    }
219
220    fn should_process_descendants(&mut self, _: E) -> bool {
221        if Q::should_stop_after_first_match() {
222            return Q::is_empty(&self.results);
223        }
224
225        true
226    }
227
228    fn invalidated_self(&mut self, e: E) {
229        Q::append_element(self.results, e);
230    }
231
232    fn invalidated_sibling(&mut self, e: E, _of: E) {
233        Q::append_element(self.results, e);
234    }
235
236    fn recursion_limit_exceeded(&mut self, _e: E) {}
237    fn invalidated_descendants(&mut self, _e: E, _child: E) {}
238}
239
240enum Operation {
241    Reject,
242    Accept,
243    RejectSkippingChildren,
244}
245
246impl From<bool> for Operation {
247    #[inline(always)]
248    fn from(matches: bool) -> Self {
249        if matches {
250            Operation::Accept
251        } else {
252            Operation::Reject
253        }
254    }
255}
256
257fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)
258where
259    E: TElement,
260    Q: SelectorQuery<E>,
261    F: FnMut(E) -> Operation,
262{
263    let mut iter = root.dom_descendants();
264    let mut cur = iter.next();
265    while let Some(node) = cur {
266        let element = match node.as_element() {
267            Some(e) => e,
268            None => {
269                cur = iter.next();
270                continue;
271            },
272        };
273        match filter(element) {
274            // Element matches - add to results and continue traversing its children.
275            Operation::Accept => {
276                Q::append_element(results, element);
277                if Q::should_stop_after_first_match() {
278                    return;
279                }
280            },
281            // Element doesn't match - skip it but continue traversing its children.
282            Operation::Reject => {},
283            // Element doesn't match and skip entire subtree.
284            Operation::RejectSkippingChildren => {
285                cur = iter.next_skipping_children();
286                continue;
287            },
288        }
289        cur = iter.next();
290    }
291}
292
293/// Returns whether a given element connected to `root` is descendant of `root`.
294///
295/// NOTE(emilio): if root == element, this returns false.
296fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
297where
298    E: TElement,
299{
300    // Optimize for when the root is a document or a shadow root and the element
301    // is connected to that root.
302    if root.as_document().is_some() {
303        debug_assert!(element.as_node().is_in_document(), "Not connected?");
304        debug_assert_eq!(
305            root,
306            root.owner_doc().as_node(),
307            "Where did this element come from?",
308        );
309        return true;
310    }
311
312    if root.as_shadow_root().is_some() {
313        debug_assert_eq!(
314            element.containing_shadow().unwrap().as_node(),
315            root,
316            "Not connected?"
317        );
318        return true;
319    }
320
321    let mut current = element.as_node().parent_node();
322    while let Some(n) = current.take() {
323        if n == root {
324            return true;
325        }
326
327        current = n.parent_node();
328    }
329    false
330}
331
332/// Fast path for iterating over every element with a given id in the document
333/// or shadow root that `root` is connected to.
334fn fast_connected_elements_with_id<'a, N>(
335    root: N,
336    id: &AtomIdent,
337    case_sensitivity: CaseSensitivity,
338) -> Result<&'a [N::ConcreteElement], ()>
339where
340    N: TNode + 'a,
341{
342    if case_sensitivity != CaseSensitivity::CaseSensitive {
343        return Err(());
344    }
345
346    if root.is_in_document() {
347        return root.owner_doc().elements_with_id(id);
348    }
349
350    if let Some(shadow) = root.as_shadow_root() {
351        return shadow.elements_with_id(id);
352    }
353
354    if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
355        return shadow.elements_with_id(id);
356    }
357
358    Err(())
359}
360
361/// Collects elements with a given id under `root`, that pass `filter`.
362fn collect_elements_with_id<E, Q, F>(
363    root: E::ConcreteNode,
364    id: &AtomIdent,
365    results: &mut Q::Output,
366    class_and_id_case_sensitivity: CaseSensitivity,
367    mut filter: F,
368) where
369    E: TElement,
370    Q: SelectorQuery<E>,
371    F: FnMut(E) -> bool,
372{
373    let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) {
374        Ok(elements) => elements,
375        Err(()) => {
376            collect_all_elements::<E, Q, _>(root, results, |e| {
377                Operation::from(e.has_id(id, class_and_id_case_sensitivity) && filter(e))
378            });
379
380            return;
381        },
382    };
383
384    for element in elements {
385        // If the element is not an actual descendant of the root, even though
386        // it's connected, we don't really care about it.
387        if !connected_element_is_descendant_of(*element, root) {
388            continue;
389        }
390
391        if !filter(*element) {
392            continue;
393        }
394
395        Q::append_element(results, *element);
396        if Q::should_stop_after_first_match() {
397            break;
398        }
399    }
400}
401
402fn has_attr<E>(element: E, local_name: &crate::LocalName) -> bool
403where
404    E: TElement,
405{
406    let mut found = false;
407    element.each_attr_name(|name| found |= name == local_name);
408    found
409}
410
411#[inline(always)]
412fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool
413where
414    E: TElement,
415{
416    let LocalName {
417        ref name,
418        ref lower_name,
419    } = *local_name;
420
421    let chosen_name = if name == lower_name || element.is_html_element_in_html_document() {
422        lower_name
423    } else {
424        name
425    };
426
427    element.local_name() == &**chosen_name
428}
429
430fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> {
431    let (name, name_lower) = match component {
432        Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),
433        Component::AttributeInNoNamespaceExists {
434            ref local_name,
435            ref local_name_lower,
436            ..
437        } => (local_name, local_name_lower),
438        Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower),
439        _ => return None,
440    };
441    if name != name_lower {
442        return None; // TODO: Maybe optimize this?
443    }
444    Some(name)
445}
446
447fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
448    use selectors::attr::AttrSelectorOperator;
449    Some(match component {
450        Component::ID(ref id) => id,
451        Component::AttributeInNoNamespace {
452            ref operator,
453            ref local_name,
454            ref value,
455            ..
456        } => {
457            if *local_name != local_name!("id") {
458                return None;
459            }
460            if *operator != AttrSelectorOperator::Equal {
461                return None;
462            }
463            AtomIdent::cast(&value.0)
464        },
465        _ => return None,
466    })
467}
468
469/// Fast paths for querySelector with a single simple selector.
470fn query_selector_single_query<E, Q>(
471    root: E::ConcreteNode,
472    component: &Component<E::Impl>,
473    results: &mut Q::Output,
474    class_and_id_case_sensitivity: CaseSensitivity,
475) -> Result<(), ()>
476where
477    E: TElement,
478    Q: SelectorQuery<E>,
479{
480    match *component {
481        Component::ExplicitUniversalType => {
482            collect_all_elements::<E, Q, _>(root, results, |_| Operation::Accept)
483        },
484        Component::Class(ref class) => {
485            // Bloom filter can only be used when case sensitive.
486            let bloom_hash = if class_and_id_case_sensitivity == CaseSensitivity::CaseSensitive {
487                Some(E::hash_for_bloom_filter(class.0.get_hash()))
488            } else {
489                None
490            };
491
492            collect_all_elements::<E, Q, _>(root, results, |element| {
493                if bloom_hash.is_some_and(|hash| !element.bloom_may_have_hash(hash)) {
494                    return Operation::RejectSkippingChildren;
495                }
496                Operation::from(element.has_class(class, class_and_id_case_sensitivity))
497            });
498        },
499        Component::LocalName(ref local_name) => {
500            collect_all_elements::<E, Q, _>(root, results, |element| {
501                Operation::from(local_name_matches(element, local_name))
502            })
503        },
504        Component::AttributeInNoNamespaceExists {
505            ref local_name,
506            ref local_name_lower,
507        } => {
508            // For HTML elements: C++ hashes lowercase
509            // For XUL/SVG/MathML elements: C++ hashes original case
510            let hash_original = E::hash_for_bloom_filter(local_name.0.get_hash());
511            let hash_lower = if local_name.0 == local_name_lower.0 {
512                hash_original
513            } else {
514                E::hash_for_bloom_filter(local_name_lower.0.get_hash())
515            };
516
517            collect_all_elements::<E, Q, _>(root, results, |element| {
518                // Check bloom filter first
519                let bloom_found_hash = if hash_original == hash_lower
520                    || !element.as_node().owner_doc().is_html_document()
521                {
522                    element.bloom_may_have_hash(hash_original)
523                } else if element.is_html_element_in_html_document() {
524                    // HTML elements store lowercase hashes
525                    element.bloom_may_have_hash(hash_lower)
526                } else {
527                    // Non-HTML elements in HTML documents might have HTML descendants
528                    // with lowercase-only hashes, so check both
529                    element.bloom_may_have_hash(hash_original)
530                        || element.bloom_may_have_hash(hash_lower)
531                };
532
533                if !bloom_found_hash {
534                    return Operation::RejectSkippingChildren;
535                }
536
537                Operation::from(element.has_attr_in_no_namespace(matching::select_name(
538                    &element,
539                    local_name,
540                    local_name_lower,
541                )))
542            });
543        },
544        Component::AttributeInNoNamespace {
545            ref local_name,
546            ref value,
547            operator,
548            case_sensitivity,
549        } => {
550            let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>();
551            let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace);
552
553            // Only use bloom filter to check for attribute name existence.
554            let bloom_hash = E::hash_for_bloom_filter(local_name.0.get_hash());
555
556            collect_all_elements::<E, Q, _>(root, results, |element| {
557                if !element.bloom_may_have_hash(bloom_hash) {
558                    return Operation::RejectSkippingChildren;
559                }
560                Operation::from(element.attr_matches(
561                    &namespace_constraint,
562                    local_name,
563                    &AttrSelectorOperation::WithValue {
564                        operator,
565                        case_sensitivity: matching::to_unconditional_case_sensitivity(
566                            case_sensitivity,
567                            &element,
568                        ),
569                        value,
570                    },
571                ))
572            });
573        },
574        ref other => {
575            let id = match get_id(other) {
576                Some(id) => id,
577                // TODO(emilio): More fast paths?
578                None => return Err(()),
579            };
580            collect_elements_with_id::<E, Q, _>(
581                root,
582                id,
583                results,
584                class_and_id_case_sensitivity,
585                |_| true,
586            );
587        },
588    }
589
590    Ok(())
591}
592
593enum SimpleFilter<'a> {
594    Class(&'a AtomIdent),
595    Attr(&'a crate::LocalName),
596    LocalName(&'a LocalName<SelectorImpl>),
597}
598
599/// Fast paths for a given selector query.
600///
601/// When there's only one component, we go directly to
602/// `query_selector_single_query`, otherwise, we try to optimize by looking just
603/// at the subtrees rooted at ids in the selector, and otherwise we try to look
604/// up by class name or local name in the rightmost compound.
605///
606/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
607/// replaced by HolyJit :)
608fn query_selector_fast<E, Q>(
609    root: E::ConcreteNode,
610    selector_list: &SelectorList<E::Impl>,
611    results: &mut Q::Output,
612    matching_context: &mut MatchingContext<E::Impl>,
613) -> Result<(), ()>
614where
615    E: TElement,
616    Q: SelectorQuery<E>,
617{
618    // We need to return elements in document order, and reordering them
619    // afterwards is kinda silly.
620    if selector_list.len() > 1 {
621        return Err(());
622    }
623
624    let selector = &selector_list.slice()[0];
625    let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
626    // Let's just care about the easy cases for now.
627    if selector.len() == 1 {
628        if query_selector_single_query::<E, Q>(
629            root,
630            selector.iter().next().unwrap(),
631            results,
632            class_and_id_case_sensitivity,
633        )
634        .is_ok()
635        {
636            return Ok(());
637        }
638    }
639
640    let mut iter = selector.iter();
641    let mut combinator: Option<Combinator> = None;
642
643    // We want to optimize some cases where there's no id involved whatsoever,
644    // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
645    // that.
646    let mut simple_filter = None;
647
648    'selector_loop: loop {
649        debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
650
651        'component_loop: for component in &mut iter {
652            match *component {
653                Component::Class(ref class) => {
654                    if combinator.is_none() {
655                        simple_filter = Some(SimpleFilter::Class(class));
656                    }
657                },
658                Component::LocalName(ref local_name) => {
659                    if combinator.is_none() {
660                        // Prefer to look at class rather than local-name if
661                        // both are present.
662                        if let Some(SimpleFilter::Class(..)) = simple_filter {
663                            continue;
664                        }
665                        simple_filter = Some(SimpleFilter::LocalName(local_name));
666                    }
667                },
668                ref other => {
669                    if let Some(id) = get_id(other) {
670                        if combinator.is_none() {
671                            // In the rightmost compound, just find descendants of root that match
672                            // the selector list with that id.
673                            collect_elements_with_id::<E, Q, _>(
674                                root,
675                                id,
676                                results,
677                                class_and_id_case_sensitivity,
678                                |e| {
679                                    matching::matches_selector_list(
680                                        selector_list,
681                                        &e,
682                                        matching_context,
683                                    )
684                                },
685                            );
686                            return Ok(());
687                        }
688
689                        let elements = fast_connected_elements_with_id(
690                            root,
691                            id,
692                            class_and_id_case_sensitivity,
693                        )?;
694                        if elements.is_empty() {
695                            return Ok(());
696                        }
697
698                        // Results need to be in document order. Let's not bother
699                        // reordering or deduplicating nodes, which we would need to
700                        // do if one element with the given id were a descendant of
701                        // another element with that given id.
702                        if !Q::should_stop_after_first_match() && elements.len() > 1 {
703                            continue;
704                        }
705
706                        for element in elements {
707                            // If the element is not a descendant of the root, then
708                            // it may have descendants that match our selector that
709                            // _are_ descendants of the root, and other descendants
710                            // that match our selector that are _not_.
711                            //
712                            // So we can't just walk over the element's descendants
713                            // and match the selector against all of them, nor can
714                            // we skip looking at this element's descendants.
715                            //
716                            // Give up on trying to optimize based on this id and
717                            // keep walking our selector.
718                            if !connected_element_is_descendant_of(*element, root) {
719                                continue 'component_loop;
720                            }
721
722                            query_selector_slow::<E, Q>(
723                                element.as_node(),
724                                selector_list,
725                                results,
726                                matching_context,
727                            );
728
729                            if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
730                                break;
731                            }
732                        }
733
734                        return Ok(());
735                    }
736                    if combinator.is_none() && simple_filter.is_none() {
737                        if let Some(attr_name) = get_attr_name(other) {
738                            simple_filter = Some(SimpleFilter::Attr(attr_name));
739                        }
740                    }
741                },
742            }
743        }
744
745        loop {
746            let next_combinator = match iter.next_sequence() {
747                None => break 'selector_loop,
748                Some(c) => c,
749            };
750
751            // We don't want to scan stuff affected by sibling combinators,
752            // given we scan the subtree of elements with a given id (and we
753            // don't want to care about scanning the siblings' subtrees).
754            if next_combinator.is_sibling() {
755                // Advance to the next combinator.
756                for _ in &mut iter {}
757                continue;
758            }
759
760            combinator = Some(next_combinator);
761            break;
762        }
763    }
764
765    // We got here without finding any ID or such that we could handle. Try to
766    // use one of the simple filters.
767    let simple_filter = match simple_filter {
768        Some(f) => f,
769        None => return Err(()),
770    };
771
772    match simple_filter {
773        SimpleFilter::Class(ref class) => {
774            collect_all_elements::<E, Q, _>(root, results, |element| {
775                Operation::from(
776                    element.has_class(class, class_and_id_case_sensitivity)
777                        && matching::matches_selector_list(
778                            selector_list,
779                            &element,
780                            matching_context,
781                        ),
782                )
783            });
784        },
785        SimpleFilter::LocalName(ref local_name) => {
786            collect_all_elements::<E, Q, _>(root, results, |element| {
787                Operation::from(
788                    local_name_matches(element, local_name)
789                        && matching::matches_selector_list(
790                            selector_list,
791                            &element,
792                            matching_context,
793                        ),
794                )
795            });
796        },
797        SimpleFilter::Attr(ref local_name) => {
798            collect_all_elements::<E, Q, _>(root, results, |element| {
799                Operation::from(
800                    has_attr(element, local_name)
801                        && matching::matches_selector_list(
802                            selector_list,
803                            &element,
804                            matching_context,
805                        ),
806                )
807            });
808        },
809    }
810
811    Ok(())
812}
813
814// Slow path for a given selector query.
815fn query_selector_slow<E, Q>(
816    root: E::ConcreteNode,
817    selector_list: &SelectorList<E::Impl>,
818    results: &mut Q::Output,
819    matching_context: &mut MatchingContext<E::Impl>,
820) where
821    E: TElement,
822    Q: SelectorQuery<E>,
823{
824    collect_all_elements::<E, Q, _>(root, results, |element| {
825        Operation::from(matching::matches_selector_list(
826            selector_list,
827            &element,
828            matching_context,
829        ))
830    });
831}
832
833/// Whether the invalidation machinery should be used for this query.
834#[derive(PartialEq)]
835pub enum MayUseInvalidation {
836    /// We may use it if we deem it useful.
837    Yes,
838    /// Don't use it.
839    No,
840}
841
842/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
843pub fn query_selector<E, Q>(
844    root: E::ConcreteNode,
845    selector_list: &SelectorList<E::Impl>,
846    results: &mut Q::Output,
847    may_use_invalidation: MayUseInvalidation,
848) where
849    E: TElement,
850    Q: SelectorQuery<E>,
851{
852    use crate::invalidation::element::invalidator::TreeStyleInvalidator;
853
854    let mut selector_caches = SelectorCaches::default();
855    let quirks_mode = root.owner_doc().quirks_mode();
856
857    let mut matching_context = MatchingContext::new(
858        MatchingMode::Normal,
859        None,
860        &mut selector_caches,
861        quirks_mode,
862        NeedsSelectorFlags::No,
863        MatchingForInvalidation::No,
864    );
865    let root_element = root.as_element();
866    matching_context.scope_element = root_element.map(|e| e.opaque());
867    matching_context.current_host = match root_element {
868        Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
869        None => root.as_shadow_root().map(|root| root.host().opaque()),
870    };
871
872    let fast_result =
873        query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);
874
875    if fast_result.is_ok() {
876        return;
877    }
878
879    // Slow path: Use the invalidation machinery if we're a root, and tree
880    // traversal otherwise.
881    //
882    // See the comment in collect_invalidations to see why only if we're a root.
883    //
884    // The invalidation mechanism is only useful in presence of combinators.
885    //
886    // We could do that check properly here, though checking the length of the
887    // selectors is a good heuristic.
888    //
889    // A selector with a combinator needs to have a length of at least 3: A
890    // simple selector, a combinator, and another simple selector.
891    let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes
892        && selector_list.slice().iter().any(|s| s.len() > 2);
893
894    if root_element.is_some() || !invalidation_may_be_useful {
895        query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
896    } else {
897        let dependencies = selector_list
898            .slice()
899            .iter()
900            .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
901            .collect::<SmallVec<[_; 5]>>();
902        let mut processor = QuerySelectorProcessor::<E, Q> {
903            results,
904            matching_context,
905            traversal_map: SiblingTraversalMap::default(),
906            dependencies: &dependencies,
907        };
908
909        for node in root.dom_children() {
910            if let Some(e) = node.as_element() {
911                TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)
912                    .invalidate();
913            }
914        }
915    }
916}