Commit c062dcce authored by Emilio Cobos Álvarez's avatar Emilio Cobos Álvarez
Browse files

Bug 1823642 - Optimize [id=foo] as #foo in querySelector/All. r=zrhoffman

This page seems to rely on this optimization being present. Optimizing
it is straight-forward. Why do they do that instead of using the #foo
syntax? Who knows.

Differential Revision: https://phabricator.services.mozilla.com/D173148
parent 8af8caa1
Loading
Loading
Loading
Loading
+85 −63
Original line number Diff line number Diff line
@@ -7,13 +7,14 @@

use crate::context::QuirksMode;
use crate::dom::{TDocument, TElement, TNode, TShadowRoot};
use crate::selector_parser::SelectorImpl;
use crate::invalidation::element::invalidation_map::Dependency;
use crate::invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation};
use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
use crate::values::AtomIdent;
use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode, NeedsSelectorFlags};
use selectors::parser::{Combinator, Component, LocalName, SelectorImpl};
use selectors::parser::{Combinator, Component, LocalName};
use selectors::{Element, SelectorList};
use smallvec::SmallVec;

@@ -376,6 +377,23 @@ where
    element.local_name() == &**chosen_name
}

fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
    use selectors::attr::AttrSelectorOperator;
    Some(match component {
        Component::ID(ref id) => id,
        Component::AttributeInNoNamespace { ref operator, ref local_name, ref value, .. } => {
            if *local_name != local_name!("id") {
                return None;
            }
            if *operator != AttrSelectorOperator::Equal {
                return None;
            }
            AtomIdent::cast(&value.0)
        },
        _ => return None,
    })
}

/// Fast paths for querySelector with a single simple selector.
fn query_selector_single_query<E, Q>(
    root: E::ConcreteNode,
@@ -387,13 +405,11 @@ where
    E: TElement,
    Q: SelectorQuery<E>,
{
    // TODO: Maybe we could implement a fast path for [name=""]?
    match *component {
        Component::ExplicitUniversalType => {
            collect_all_elements::<E, Q, _>(root, results, |_| true)
        },
        Component::ID(ref id) => {
            collect_elements_with_id::<E, Q, _>(root, id, results, quirks_mode, |_| true);
        },
        Component::Class(ref class) => {
            let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
            collect_all_elements::<E, Q, _>(root, results, |element| {
@@ -405,16 +421,22 @@ where
                local_name_matches(element, local_name)
            })
        },
        ref other => {
            let id = match get_id(other) {
                Some(id) => id,
                // TODO(emilio): More fast paths?
        _ => return Err(()),
                None => return Err(()),
            };
            collect_elements_with_id::<E, Q, _>(root, id, results, quirks_mode, |_| true);
        },
    }

    Ok(())
}

enum SimpleFilter<'a, Impl: SelectorImpl> {
enum SimpleFilter<'a> {
    Class(&'a AtomIdent),
    LocalName(&'a LocalName<Impl>),
    LocalName(&'a LocalName<SelectorImpl>),
}

/// Fast paths for a given selector query.
@@ -468,14 +490,29 @@ where

        'component_loop: for component in &mut iter {
            match *component {
                Component::ID(ref id) => {
                Component::Class(ref class) => {
                    if combinator.is_none() {
                        simple_filter = Some(SimpleFilter::Class(class));
                    }
                },
                Component::LocalName(ref local_name) => {
                    if combinator.is_none() {
                        // Prefer to look at class rather than local-name if
                        // both are present.
                        if let Some(SimpleFilter::Class(..)) = simple_filter {
                            continue;
                        }
                        simple_filter = Some(SimpleFilter::LocalName(local_name));
                    }
                },
                ref other => {
                    if let Some(id) = get_id(other) {
                        if combinator.is_none() {
                        // In the rightmost compound, just find descendants of
                        // root that match the selector list with that id.
                            // In the rightmost compound, just find descendants of root that match
                            // the selector list with that id.
                            collect_elements_with_id::<E, Q, _>(root, id, results, quirks_mode, |e| {
                                matching::matches_selector_list(selector_list, &e, matching_context)
                            });

                            return Ok(());
                        }

@@ -521,23 +558,8 @@ where
                        }

                        return Ok(());
                },
                Component::Class(ref class) => {
                    if combinator.is_none() {
                        simple_filter = Some(SimpleFilter::Class(class));
                    }
                },
                Component::LocalName(ref local_name) => {
                    if combinator.is_none() {
                        // Prefer to look at class rather than local-name if
                        // both are present.
                        if let Some(SimpleFilter::Class(..)) = simple_filter {
                            continue;
                        }
                        simple_filter = Some(SimpleFilter::LocalName(local_name));
                    }
                },
                _ => {},
            }
        }

+8 −0
Original line number Diff line number Diff line
@@ -367,6 +367,14 @@ impl AtomIdent {
            callback(&*atom)
        })
    }

    /// Cast an atom ref to an AtomIdent ref.
    #[inline]
    pub fn cast<'a>(atom: &'a Atom) -> &'a Self {
        let ptr = atom as *const _ as *const Self;
        // safety: repr(transparent)
        unsafe { &*ptr }
    }
}

#[cfg(feature = "gecko")]