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::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 get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> {
403    let (name, name_lower) = match component {
404        Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),
405        Component::AttributeInNoNamespaceExists {
406            ref local_name,
407            ref local_name_lower,
408            ..
409        } => (local_name, local_name_lower),
410        Component::AttributeOther(ref attr) => {
411            if attr.namespace.is_some() {
412                return None;
413            }
414            (&attr.local_name, &attr.local_name_lower)
415        },
416        _ => return None,
417    };
418    if name != name_lower {
419        return None; // TODO: Maybe optimize this?
420    }
421    Some(name)
422}
423
424fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
425    use selectors::attr::AttrSelectorOperator;
426    Some(match component {
427        Component::ID(ref id) => id,
428        Component::AttributeInNoNamespace {
429            ref operator,
430            ref local_name,
431            ref value,
432            ..
433        } => {
434            if *local_name != local_name!("id") {
435                return None;
436            }
437            if *operator != AttrSelectorOperator::Equal {
438                return None;
439            }
440            AtomIdent::cast(&value.0)
441        },
442        _ => return None,
443    })
444}
445
446/// Fast paths for querySelector with a single simple selector.
447fn query_selector_single_query<E, Q>(
448    root: E::ConcreteNode,
449    component: &Component<E::Impl>,
450    results: &mut Q::Output,
451    class_and_id_case_sensitivity: CaseSensitivity,
452) -> Result<(), ()>
453where
454    E: TElement,
455    Q: SelectorQuery<E>,
456{
457    match *component {
458        Component::ExplicitUniversalType => {
459            collect_all_elements::<E, Q, _>(root, results, |_| Operation::Accept)
460        },
461        Component::Class(ref class) => {
462            // Bloom filter can only be used when case sensitive.
463            let bloom_hash = if class_and_id_case_sensitivity == CaseSensitivity::CaseSensitive {
464                Some(E::hash_for_bloom_filter(class.0.get_hash()))
465            } else {
466                None
467            };
468
469            collect_all_elements::<E, Q, _>(root, results, |element| {
470                if bloom_hash.is_some_and(|hash| !element.bloom_may_have_hash(hash)) {
471                    return Operation::RejectSkippingChildren;
472                }
473                Operation::from(element.has_class(class, class_and_id_case_sensitivity))
474            });
475        },
476        Component::LocalName(ref local_name) => {
477            let hash = E::hash_for_bloom_filter(local_name.name.0.get_hash());
478            let hash_lower = if local_name.name == local_name.lower_name {
479                hash
480            } else {
481                E::hash_for_bloom_filter(local_name.lower_name.0.get_hash())
482            };
483            collect_all_elements::<E, Q, _>(root, results, |element| {
484                if !element.bloom_may_have_hash(hash)
485                    && (hash == hash_lower || !element.bloom_may_have_hash(hash_lower))
486                {
487                    return Operation::RejectSkippingChildren;
488                }
489                Operation::from(
490                    *element.local_name()
491                        == ***matching::select_name(
492                            &element,
493                            &local_name.name,
494                            &local_name.lower_name,
495                        ),
496                )
497            })
498        },
499        Component::AttributeInNoNamespaceExists {
500            ref local_name,
501            ref local_name_lower,
502        } => {
503            // For HTML elements: C++ hashes lowercase
504            // For XUL/SVG/MathML elements: C++ hashes original case
505            let hash_original = E::hash_for_bloom_filter(local_name.0.get_hash());
506            let hash_lower = if local_name.0 == local_name_lower.0 {
507                hash_original
508            } else {
509                E::hash_for_bloom_filter(local_name_lower.0.get_hash())
510            };
511
512            collect_all_elements::<E, Q, _>(root, results, |element| {
513                // Check bloom filter first
514                let bloom_found_hash = if hash_original == hash_lower
515                    || !element.as_node().owner_doc().is_html_document()
516                {
517                    element.bloom_may_have_hash(hash_original)
518                } else if element.is_html_element_in_html_document() {
519                    // HTML elements store lowercase hashes
520                    element.bloom_may_have_hash(hash_lower)
521                } else {
522                    // Non-HTML elements in HTML documents might have HTML descendants
523                    // with lowercase-only hashes, so check both
524                    element.bloom_may_have_hash(hash_original)
525                        || element.bloom_may_have_hash(hash_lower)
526                };
527
528                if !bloom_found_hash {
529                    return Operation::RejectSkippingChildren;
530                }
531
532                Operation::from(element.has_attr_in_no_namespace(matching::select_name(
533                    &element,
534                    local_name,
535                    local_name_lower,
536                )))
537            });
538        },
539        Component::AttributeInNoNamespace {
540            ref local_name,
541            ref value,
542            operator,
543            case_sensitivity,
544        } => {
545            let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>();
546            let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace);
547
548            // Only use bloom filter to check for attribute name existence.
549            let bloom_hash = E::hash_for_bloom_filter(local_name.0.get_hash());
550
551            collect_all_elements::<E, Q, _>(root, results, |element| {
552                if !element.bloom_may_have_hash(bloom_hash) {
553                    return Operation::RejectSkippingChildren;
554                }
555                Operation::from(element.attr_matches(
556                    &namespace_constraint,
557                    local_name,
558                    &AttrSelectorOperation::WithValue {
559                        operator,
560                        case_sensitivity: matching::to_unconditional_case_sensitivity(
561                            case_sensitivity,
562                            &element,
563                        ),
564                        value,
565                    },
566                ))
567            });
568        },
569        ref other => {
570            let id = match get_id(other) {
571                Some(id) => id,
572                // TODO(emilio): More fast paths?
573                None => return Err(()),
574            };
575            collect_elements_with_id::<E, Q, _>(
576                root,
577                id,
578                results,
579                class_and_id_case_sensitivity,
580                |_| true,
581            );
582        },
583    }
584
585    Ok(())
586}
587
588enum SimpleFilter<'a> {
589    Class(&'a AtomIdent),
590    Attr(&'a crate::LocalName),
591    LocalName(&'a LocalName<SelectorImpl>),
592}
593
594/// Fast paths for a given selector query.
595///
596/// When there's only one component, we go directly to
597/// `query_selector_single_query`, otherwise, we try to optimize by looking just
598/// at the subtrees rooted at ids in the selector, and otherwise we try to look
599/// up by class name or local name in the rightmost compound.
600///
601/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
602/// replaced by HolyJit :)
603fn query_selector_fast<E, Q>(
604    root: E::ConcreteNode,
605    selector_list: &SelectorList<E::Impl>,
606    results: &mut Q::Output,
607    matching_context: &mut MatchingContext<E::Impl>,
608) -> Result<(), ()>
609where
610    E: TElement,
611    Q: SelectorQuery<E>,
612{
613    // We need to return elements in document order, and reordering them
614    // afterwards is kinda silly.
615    if selector_list.len() > 1 {
616        return Err(());
617    }
618
619    let selector = &selector_list.slice()[0];
620    let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
621    // Let's just care about the easy cases for now.
622    if selector.len() == 1 {
623        if query_selector_single_query::<E, Q>(
624            root,
625            selector.iter().next().unwrap(),
626            results,
627            class_and_id_case_sensitivity,
628        )
629        .is_ok()
630        {
631            return Ok(());
632        }
633    }
634
635    let mut iter = selector.iter();
636    let mut combinator: Option<Combinator> = None;
637
638    // We want to optimize some cases where there's no id involved whatsoever,
639    // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
640    // that.
641    let mut simple_filter = None;
642
643    'selector_loop: loop {
644        debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
645
646        'component_loop: for component in &mut iter {
647            match *component {
648                Component::Class(ref class) => {
649                    if combinator.is_none() {
650                        simple_filter = Some(SimpleFilter::Class(class));
651                    }
652                },
653                Component::LocalName(ref local_name) => {
654                    if combinator.is_none() {
655                        // Prefer to look at class rather than local-name if
656                        // both are present.
657                        if let Some(SimpleFilter::Class(..)) = simple_filter {
658                            continue;
659                        }
660                        simple_filter = Some(SimpleFilter::LocalName(local_name));
661                    }
662                },
663                ref other => {
664                    if let Some(id) = get_id(other) {
665                        if combinator.is_none() {
666                            // In the rightmost compound, just find descendants of root that match
667                            // the selector list with that id.
668                            collect_elements_with_id::<E, Q, _>(
669                                root,
670                                id,
671                                results,
672                                class_and_id_case_sensitivity,
673                                |e| {
674                                    matching::matches_selector_list(
675                                        selector_list,
676                                        &e,
677                                        matching_context,
678                                    )
679                                },
680                            );
681                            return Ok(());
682                        }
683
684                        let elements = fast_connected_elements_with_id(
685                            root,
686                            id,
687                            class_and_id_case_sensitivity,
688                        )?;
689                        if elements.is_empty() {
690                            return Ok(());
691                        }
692
693                        // Results need to be in document order. Let's not bother
694                        // reordering or deduplicating nodes, which we would need to
695                        // do if one element with the given id were a descendant of
696                        // another element with that given id.
697                        if !Q::should_stop_after_first_match() && elements.len() > 1 {
698                            continue;
699                        }
700
701                        for element in elements {
702                            // If the element is not a descendant of the root, then
703                            // it may have descendants that match our selector that
704                            // _are_ descendants of the root, and other descendants
705                            // that match our selector that are _not_.
706                            //
707                            // So we can't just walk over the element's descendants
708                            // and match the selector against all of them, nor can
709                            // we skip looking at this element's descendants.
710                            //
711                            // Give up on trying to optimize based on this id and
712                            // keep walking our selector.
713                            if !connected_element_is_descendant_of(*element, root) {
714                                continue 'component_loop;
715                            }
716
717                            query_selector_slow::<E, Q>(
718                                element.as_node(),
719                                selector_list,
720                                results,
721                                matching_context,
722                            );
723
724                            if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
725                                break;
726                            }
727                        }
728
729                        return Ok(());
730                    }
731                    if combinator.is_none() && simple_filter.is_none() {
732                        if let Some(attr_name) = get_attr_name(other) {
733                            simple_filter = Some(SimpleFilter::Attr(attr_name));
734                        }
735                    }
736                },
737            }
738        }
739
740        loop {
741            let next_combinator = match iter.next_sequence() {
742                None => break 'selector_loop,
743                Some(c) => c,
744            };
745
746            // We don't want to scan stuff affected by sibling combinators,
747            // given we scan the subtree of elements with a given id (and we
748            // don't want to care about scanning the siblings' subtrees).
749            if next_combinator.is_sibling() {
750                // Advance to the next combinator.
751                for _ in &mut iter {}
752                continue;
753            }
754
755            combinator = Some(next_combinator);
756            break;
757        }
758    }
759
760    // We got here without finding any ID or such that we could handle. Try to
761    // use one of the simple filters.
762    let simple_filter = match simple_filter {
763        Some(f) => f,
764        None => return Err(()),
765    };
766
767    match simple_filter {
768        SimpleFilter::Class(ref class) => {
769            // Bloom filter can only be used when case sensitive.
770            let bloom_hash = if class_and_id_case_sensitivity == CaseSensitivity::CaseSensitive {
771                Some(E::hash_for_bloom_filter(class.0.get_hash()))
772            } else {
773                None
774            };
775            collect_all_elements::<E, Q, _>(root, results, |element| {
776                if bloom_hash.is_some_and(|hash| !element.bloom_may_have_hash(hash)) {
777                    return Operation::RejectSkippingChildren;
778                }
779                Operation::from(
780                    element.has_class(class, class_and_id_case_sensitivity)
781                        && matching::matches_selector_list(
782                            selector_list,
783                            &element,
784                            matching_context,
785                        ),
786                )
787            });
788        },
789        SimpleFilter::LocalName(ref local_name) => {
790            let hash = E::hash_for_bloom_filter(local_name.name.0.get_hash());
791            let hash_lower = if local_name.name == local_name.lower_name {
792                hash
793            } else {
794                E::hash_for_bloom_filter(local_name.lower_name.0.get_hash())
795            };
796            collect_all_elements::<E, Q, _>(root, results, |element| {
797                if !element.bloom_may_have_hash(hash)
798                    && (hash == hash_lower || !element.bloom_may_have_hash(hash_lower))
799                {
800                    return Operation::RejectSkippingChildren;
801                }
802                if *element.local_name()
803                    != ***matching::select_name(&element, &local_name.name, &local_name.lower_name)
804                {
805                    return Operation::Reject;
806                }
807                Operation::from(matching::matches_selector_list(
808                    selector_list,
809                    &element,
810                    matching_context,
811                ))
812            });
813        },
814        SimpleFilter::Attr(ref local_name) => {
815            let hash = E::hash_for_bloom_filter(local_name.0.get_hash());
816            collect_all_elements::<E, Q, _>(root, results, |element| {
817                if !element.bloom_may_have_hash(hash) {
818                    return Operation::RejectSkippingChildren;
819                }
820                if !element.has_attr_in_no_namespace(local_name) {
821                    return Operation::Reject;
822                }
823                Operation::from(matching::matches_selector_list(
824                    selector_list,
825                    &element,
826                    matching_context,
827                ))
828            });
829        },
830    }
831
832    Ok(())
833}
834
835// Slow path for a given selector query.
836fn query_selector_slow<E, Q>(
837    root: E::ConcreteNode,
838    selector_list: &SelectorList<E::Impl>,
839    results: &mut Q::Output,
840    matching_context: &mut MatchingContext<E::Impl>,
841) where
842    E: TElement,
843    Q: SelectorQuery<E>,
844{
845    collect_all_elements::<E, Q, _>(root, results, |element| {
846        Operation::from(matching::matches_selector_list(
847            selector_list,
848            &element,
849            matching_context,
850        ))
851    });
852}
853
854/// Whether the invalidation machinery should be used for this query.
855#[derive(PartialEq)]
856pub enum MayUseInvalidation {
857    /// We may use it if we deem it useful.
858    Yes,
859    /// Don't use it.
860    No,
861}
862
863/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
864pub fn query_selector<E, Q>(
865    root: E::ConcreteNode,
866    selector_list: &SelectorList<E::Impl>,
867    results: &mut Q::Output,
868    may_use_invalidation: MayUseInvalidation,
869) where
870    E: TElement,
871    Q: SelectorQuery<E>,
872{
873    use crate::invalidation::element::invalidator::TreeStyleInvalidator;
874
875    let mut selector_caches = SelectorCaches::default();
876    let quirks_mode = root.owner_doc().quirks_mode();
877
878    let mut matching_context = MatchingContext::new(
879        MatchingMode::Normal,
880        None,
881        &mut selector_caches,
882        quirks_mode,
883        NeedsSelectorFlags::No,
884        MatchingForInvalidation::No,
885    );
886    let root_element = root.as_element();
887    matching_context.scope_element = root_element.map(|e| e.opaque());
888    matching_context.current_host = match root_element {
889        Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
890        None => root.as_shadow_root().map(|root| root.host().opaque()),
891    };
892
893    let fast_result =
894        query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);
895
896    if fast_result.is_ok() {
897        return;
898    }
899
900    // Slow path: Use the invalidation machinery if we're a root, and tree
901    // traversal otherwise.
902    //
903    // See the comment in collect_invalidations to see why only if we're a root.
904    //
905    // The invalidation mechanism is only useful in presence of combinators.
906    //
907    // We could do that check properly here, though checking the length of the
908    // selectors is a good heuristic.
909    //
910    // A selector with a combinator needs to have a length of at least 3: A
911    // simple selector, a combinator, and another simple selector.
912    let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes
913        && selector_list.slice().iter().any(|s| s.len() > 2);
914
915    if root_element.is_some() || !invalidation_may_be_useful {
916        query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
917    } else {
918        let dependencies = selector_list
919            .slice()
920            .iter()
921            .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
922            .collect::<SmallVec<[_; 5]>>();
923        let mut processor = QuerySelectorProcessor::<E, Q> {
924            results,
925            matching_context,
926            traversal_map: SiblingTraversalMap::default(),
927            dependencies: &dependencies,
928        };
929
930        for node in root.dom_children() {
931            if let Some(e) = node.as_element() {
932                TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)
933                    .invalidate();
934            }
935        }
936    }
937}