Skip to main content

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