tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dom_apis.rs (30153B)


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