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::{Element, OpaqueElement, SelectorList};
26use smallvec::SmallVec;
27
28/// <https://dom.spec.whatwg.org/#dom-element-matches>
29pub fn element_matches<E>(
30    element: &E,
31    selector_list: &SelectorList<E::Impl>,
32    quirks_mode: QuirksMode,
33) -> bool
34where
35    E: Element,
36{
37    let mut selector_caches = SelectorCaches::default();
38
39    let mut context = MatchingContext::new(
40        MatchingMode::Normal,
41        None,
42        &mut selector_caches,
43        quirks_mode,
44        NeedsSelectorFlags::No,
45        MatchingForInvalidation::No,
46    );
47    context.scope_element = Some(element.opaque());
48    context.current_host = element.containing_shadow_host().map(|e| e.opaque());
49    matching::matches_selector_list(selector_list, element, &mut context)
50}
51
52/// <https://dom.spec.whatwg.org/#dom-element-closest>
53pub fn element_closest<E>(
54    element: E,
55    selector_list: &SelectorList<E::Impl>,
56    quirks_mode: QuirksMode,
57) -> Option<E>
58where
59    E: Element,
60{
61    let mut selector_caches = SelectorCaches::default();
62
63    let mut context = MatchingContext::new(
64        MatchingMode::Normal,
65        None,
66        &mut selector_caches,
67        quirks_mode,
68        NeedsSelectorFlags::No,
69        MatchingForInvalidation::No,
70    );
71    context.scope_element = Some(element.opaque());
72    context.current_host = element.containing_shadow_host().map(|e| e.opaque());
73
74    let mut current = Some(element);
75    while let Some(element) = current.take() {
76        if matching::matches_selector_list(selector_list, &element, &mut context) {
77            return Some(element);
78        }
79        current = element.parent_element();
80    }
81
82    None
83}
84
85/// A selector query abstraction, in order to be generic over QuerySelector and
86/// QuerySelectorAll.
87pub trait SelectorQuery<E: TElement> {
88    /// The output of the query.
89    type Output;
90
91    /// Whether the query should stop after the first element has been matched.
92    fn should_stop_after_first_match() -> bool;
93
94    /// Append an element matching after the first query.
95    fn append_element(output: &mut Self::Output, element: E);
96
97    /// Returns true if the output is empty.
98    fn is_empty(output: &Self::Output) -> bool;
99}
100
101/// The result of a querySelectorAll call.
102pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;
103
104/// A query for all the elements in a subtree.
105pub struct QueryAll;
106
107impl<E: TElement> SelectorQuery<E> for QueryAll {
108    type Output = QuerySelectorAllResult<E>;
109
110    fn should_stop_after_first_match() -> bool {
111        false
112    }
113
114    fn append_element(output: &mut Self::Output, element: E) {
115        output.push(element);
116    }
117
118    fn is_empty(output: &Self::Output) -> bool {
119        output.is_empty()
120    }
121}
122
123/// A query for the first in-tree match of all the elements in a subtree.
124pub struct QueryFirst;
125
126impl<E: TElement> SelectorQuery<E> for QueryFirst {
127    type Output = Option<E>;
128
129    fn should_stop_after_first_match() -> bool {
130        true
131    }
132
133    fn append_element(output: &mut Self::Output, element: E) {
134        if output.is_none() {
135            *output = Some(element)
136        }
137    }
138
139    fn is_empty(output: &Self::Output) -> bool {
140        output.is_none()
141    }
142}
143
144struct QuerySelectorProcessor<'a, 'b, E, Q>
145where
146    E: TElement + 'a,
147    Q: SelectorQuery<E>,
148    Q::Output: 'a,
149{
150    results: &'a mut Q::Output,
151    matching_context: MatchingContext<'b, E::Impl>,
152    traversal_map: SiblingTraversalMap<E>,
153    dependencies: &'a [Dependency],
154}
155
156impl<'a, 'b, E, Q> InvalidationProcessor<'a, 'b, E> for QuerySelectorProcessor<'a, 'b, E, Q>
157where
158    E: TElement + 'a,
159    Q: SelectorQuery<E>,
160    Q::Output: 'a,
161{
162    fn light_tree_only(&self) -> bool {
163        true
164    }
165
166    fn check_outer_dependency(&mut self, _: &Dependency, _: E, _: Option<OpaqueElement>) -> bool {
167        debug_assert!(
168            false,
169            "How? We should only have parent-less dependencies here!"
170        );
171        true
172    }
173
174    fn collect_invalidations(
175        &mut self,
176        element: E,
177        self_invalidations: &mut InvalidationVector<'a>,
178        descendant_invalidations: &mut DescendantInvalidationLists<'a>,
179        _sibling_invalidations: &mut InvalidationVector<'a>,
180    ) -> bool {
181        // TODO(emilio): If the element is not a root element, and
182        // selector_list has any descendant combinator, we need to do extra work
183        // in order to handle properly things like:
184        //
185        //   <div id="a">
186        //     <div id="b">
187        //       <div id="c"></div>
188        //     </div>
189        //   </div>
190        //
191        // b.querySelector('#a div'); // Should return "c".
192        //
193        // For now, assert it's a root element.
194        debug_assert!(element.parent_element().is_none());
195
196        let target_vector = if self.matching_context.scope_element.is_some() {
197            &mut descendant_invalidations.dom_descendants
198        } else {
199            self_invalidations
200        };
201
202        for dependency in self.dependencies.iter() {
203            target_vector.push(Invalidation::new(
204                dependency,
205                self.matching_context.current_host,
206                self.matching_context.scope_element,
207            ))
208        }
209
210        false
211    }
212
213    fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> {
214        &mut self.matching_context
215    }
216
217    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
218        &self.traversal_map
219    }
220
221    fn should_process_descendants(&mut self, _: E) -> bool {
222        if Q::should_stop_after_first_match() {
223            return Q::is_empty(self.results);
224        }
225
226        true
227    }
228
229    fn invalidated_self(&mut self, e: E) {
230        Q::append_element(self.results, e);
231    }
232
233    fn invalidated_sibling(&mut self, e: E, _of: E) {
234        Q::append_element(self.results, e);
235    }
236
237    fn recursion_limit_exceeded(&mut self, _e: E) {}
238    fn invalidated_descendants(&mut self, _e: E, _child: E) {}
239}
240
241enum Operation {
242    Reject,
243    Accept,
244    RejectSkippingChildren,
245}
246
247impl From<bool> for Operation {
248    #[inline(always)]
249    fn from(matches: bool) -> Self {
250        if matches {
251            Operation::Accept
252        } else {
253            Operation::Reject
254        }
255    }
256}
257
258fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)
259where
260    E: TElement,
261    Q: SelectorQuery<E>,
262    F: FnMut(E) -> Operation,
263{
264    let mut iter = root.dom_descendants();
265    let mut cur = iter.next();
266    while let Some(node) = cur {
267        let element = match node.as_element() {
268            Some(e) => e,
269            None => {
270                cur = iter.next();
271                continue;
272            },
273        };
274        match filter(element) {
275            // Element matches - add to results and continue traversing its children.
276            Operation::Accept => {
277                Q::append_element(results, element);
278                if Q::should_stop_after_first_match() {
279                    return;
280                }
281            },
282            // Element doesn't match - skip it but continue traversing its children.
283            Operation::Reject => {},
284            // Element doesn't match and skip entire subtree.
285            Operation::RejectSkippingChildren => {
286                cur = iter.next_skipping_children();
287                continue;
288            },
289        }
290        cur = iter.next();
291    }
292}
293
294/// Returns whether a given element connected to `root` is descendant of `root`.
295///
296/// NOTE(emilio): if root == element, this returns false.
297fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
298where
299    E: TElement,
300{
301    // Optimize for when the root is a document or a shadow root and the element
302    // is connected to that root.
303    if root.as_document().is_some() {
304        debug_assert!(element.as_node().is_in_document(), "Not connected?");
305        debug_assert_eq!(
306            root,
307            root.owner_doc().as_node(),
308            "Where did this element come from?",
309        );
310        return true;
311    }
312
313    if root.as_shadow_root().is_some() {
314        debug_assert_eq!(
315            element.containing_shadow().unwrap().as_node(),
316            root,
317            "Not connected?"
318        );
319        return true;
320    }
321
322    let mut current = element.as_node().parent_node();
323    while let Some(n) = current.take() {
324        if n == root {
325            return true;
326        }
327
328        current = n.parent_node();
329    }
330    false
331}
332
333/// Fast path for iterating over every element with a given id in the document
334/// or shadow root that `root` is connected to.
335fn fast_connected_elements_with_id<'a, N>(
336    root: N,
337    id: &AtomIdent,
338    case_sensitivity: CaseSensitivity,
339) -> Result<&'a [N::ConcreteElement], ()>
340where
341    N: TNode + 'a,
342{
343    if case_sensitivity != CaseSensitivity::CaseSensitive {
344        return Err(());
345    }
346
347    if root.is_in_document() {
348        return root.owner_doc().elements_with_id(id);
349    }
350
351    if let Some(shadow) = root.as_shadow_root() {
352        return shadow.elements_with_id(id);
353    }
354
355    if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
356        return shadow.elements_with_id(id);
357    }
358
359    Err(())
360}
361
362/// Collects elements with a given id under `root`, that pass `filter`.
363fn collect_elements_with_id<E, Q, F>(
364    root: E::ConcreteNode,
365    id: &AtomIdent,
366    results: &mut Q::Output,
367    class_and_id_case_sensitivity: CaseSensitivity,
368    mut filter: F,
369) where
370    E: TElement,
371    Q: SelectorQuery<E>,
372    F: FnMut(E) -> bool,
373{
374    let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) {
375        Ok(elements) => elements,
376        Err(()) => {
377            collect_all_elements::<E, Q, _>(root, results, |e| {
378                Operation::from(e.has_id(id, class_and_id_case_sensitivity) && filter(e))
379            });
380
381            return;
382        },
383    };
384
385    for element in elements {
386        // If the element is not an actual descendant of the root, even though
387        // it's connected, we don't really care about it.
388        if !connected_element_is_descendant_of(*element, root) {
389            continue;
390        }
391
392        if !filter(*element) {
393            continue;
394        }
395
396        Q::append_element(results, *element);
397        if Q::should_stop_after_first_match() {
398            break;
399        }
400    }
401}
402
403fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> {
404    let (name, name_lower) = match component {
405        Component::AttributeInNoNamespace { local_name, .. } => return Some(local_name),
406        Component::AttributeInNoNamespaceExists {
407            local_name,
408            local_name_lower,
409            ..
410        } => (local_name, local_name_lower),
411        Component::AttributeOther(attr) => {
412            if attr.namespace.is_some() {
413                return None;
414            }
415            (&attr.local_name, &attr.local_name_lower)
416        },
417        _ => return None,
418    };
419    if name != name_lower {
420        return None; // TODO: Maybe optimize this?
421    }
422    Some(name)
423}
424
425fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
426    use selectors::attr::AttrSelectorOperator;
427    Some(match component {
428        Component::ID(id) => id,
429        Component::AttributeInNoNamespace {
430            operator,
431            local_name,
432            value,
433            ..
434        } => {
435            if *local_name != local_name!("id") {
436                return None;
437            }
438            if *operator != AttrSelectorOperator::Equal {
439                return None;
440            }
441            AtomIdent::cast(&value.0)
442        },
443        _ => return None,
444    })
445}
446
447/// Fast paths for querySelector with a single simple selector.
448fn query_selector_single_query<E, Q>(
449    root: E::ConcreteNode,
450    component: &Component<E::Impl>,
451    results: &mut Q::Output,
452    class_and_id_case_sensitivity: CaseSensitivity,
453) -> Result<(), ()>
454where
455    E: TElement,
456    Q: SelectorQuery<E>,
457{
458    match *component {
459        Component::ExplicitUniversalType => {
460            collect_all_elements::<E, Q, _>(root, results, |_| Operation::Accept)
461        },
462        Component::Class(ref class) => {
463            // Bloom filter can only be used when case sensitive.
464            let bloom_hash = if class_and_id_case_sensitivity == CaseSensitivity::CaseSensitive {
465                Some(E::hash_for_bloom_filter(class.0.get_hash32()))
466            } else {
467                None
468            };
469
470            collect_all_elements::<E, Q, _>(root, results, |element| {
471                if bloom_hash.is_some_and(|hash| !element.bloom_may_have_hash(hash)) {
472                    return Operation::RejectSkippingChildren;
473                }
474                Operation::from(element.has_class(class, class_and_id_case_sensitivity))
475            });
476        },
477        Component::LocalName(ref local_name) => {
478            let hash = E::hash_for_bloom_filter(local_name.name.0.get_hash32());
479            let hash_lower = if local_name.name == local_name.lower_name {
480                hash
481            } else {
482                E::hash_for_bloom_filter(local_name.lower_name.0.get_hash32())
483            };
484            collect_all_elements::<E, Q, _>(root, results, |element| {
485                if !element.bloom_may_have_hash(hash)
486                    && (hash == hash_lower || !element.bloom_may_have_hash(hash_lower))
487                {
488                    return Operation::RejectSkippingChildren;
489                }
490                Operation::from(
491                    *element.local_name()
492                        == ***matching::select_name(
493                            &element,
494                            &local_name.name,
495                            &local_name.lower_name,
496                        ),
497                )
498            })
499        },
500        Component::AttributeInNoNamespaceExists {
501            ref local_name,
502            ref local_name_lower,
503        } => {
504            // For HTML elements: C++ hashes lowercase
505            // For XUL/SVG/MathML elements: C++ hashes original case
506            let hash_original = E::hash_for_bloom_filter(local_name.0.get_hash32());
507            let hash_lower = if local_name.0 == local_name_lower.0 {
508                hash_original
509            } else {
510                E::hash_for_bloom_filter(local_name_lower.0.get_hash32())
511            };
512
513            collect_all_elements::<E, Q, _>(root, results, |element| {
514                // Check bloom filter first
515                let bloom_found_hash = if hash_original == hash_lower
516                    || !element.as_node().owner_doc().is_html_document()
517                {
518                    element.bloom_may_have_hash(hash_original)
519                } else if element.is_html_element_in_html_document() {
520                    // HTML elements store lowercase hashes
521                    element.bloom_may_have_hash(hash_lower)
522                } else {
523                    // Non-HTML elements in HTML documents might have HTML descendants
524                    // with lowercase-only hashes, so check both
525                    element.bloom_may_have_hash(hash_original)
526                        || element.bloom_may_have_hash(hash_lower)
527                };
528
529                if !bloom_found_hash {
530                    return Operation::RejectSkippingChildren;
531                }
532
533                Operation::from(element.has_attr_in_no_namespace(matching::select_name(
534                    &element,
535                    local_name,
536                    local_name_lower,
537                )))
538            });
539        },
540        Component::AttributeInNoNamespace {
541            ref local_name,
542            ref value,
543            operator,
544            case_sensitivity,
545        } => {
546            let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>();
547            let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace);
548
549            // Only use bloom filter to check for attribute name existence.
550            let bloom_hash = E::hash_for_bloom_filter(local_name.0.get_hash32());
551
552            collect_all_elements::<E, Q, _>(root, results, |element| {
553                if !element.bloom_may_have_hash(bloom_hash) {
554                    return Operation::RejectSkippingChildren;
555                }
556                Operation::from(element.attr_matches(
557                    &namespace_constraint,
558                    local_name,
559                    &AttrSelectorOperation::WithValue {
560                        operator,
561                        case_sensitivity: matching::to_unconditional_case_sensitivity(
562                            case_sensitivity,
563                            &element,
564                        ),
565                        value,
566                    },
567                ))
568            });
569        },
570        ref other => {
571            let id = match get_id(other) {
572                Some(id) => id,
573                // TODO(emilio): More fast paths?
574                None => return Err(()),
575            };
576            collect_elements_with_id::<E, Q, _>(
577                root,
578                id,
579                results,
580                class_and_id_case_sensitivity,
581                |_| true,
582            );
583        },
584    }
585
586    Ok(())
587}
588
589enum SimpleFilter<'a> {
590    Class(&'a AtomIdent),
591    Attr(&'a crate::LocalName),
592    LocalName(&'a LocalName<SelectorImpl>),
593}
594
595/// Fast paths for a given selector query.
596///
597/// When there's only one component, we go directly to
598/// `query_selector_single_query`, otherwise, we try to optimize by looking just
599/// at the subtrees rooted at ids in the selector, and otherwise we try to look
600/// up by class name or local name in the rightmost compound.
601///
602/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
603/// replaced by HolyJit :)
604fn query_selector_fast<E, Q>(
605    root: E::ConcreteNode,
606    selector_list: &SelectorList<E::Impl>,
607    results: &mut Q::Output,
608    matching_context: &mut MatchingContext<E::Impl>,
609) -> Result<(), ()>
610where
611    E: TElement,
612    Q: SelectorQuery<E>,
613{
614    // We need to return elements in document order, and reordering them
615    // afterwards is kinda silly.
616    if selector_list.len() > 1 {
617        return Err(());
618    }
619
620    let selector = &selector_list.slice()[0];
621    let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
622    // Let's just care about the easy cases for now.
623    if selector.len() == 1
624        && query_selector_single_query::<E, Q>(
625            root,
626            selector.iter().next().unwrap(),
627            results,
628            class_and_id_case_sensitivity,
629        )
630        .is_ok()
631    {
632        return Ok(());
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.is_none_or(|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(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_hash32()))
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(local_name) => {
790            let hash = E::hash_for_bloom_filter(local_name.name.0.get_hash32());
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_hash32())
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(local_name) => {
815            let hash = E::hash_for_bloom_filter(local_name.0.get_hash32());
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}