style: Speed up selector matching with already-lowercase local name selectors

This makes relatively simple changes so that we check lowercase-ness of
local-name selectors first. If so, we don't need to check whether we're
an HTML element in an HTML document, which requires a fair bit of
pointer-chasing.

Differential Revision: https://phabricator.services.mozilla.com/D163627
This commit is contained in:
Emilio Cobos Álvarez 2022-12-03 11:25:09 +00:00 committed by Martin Robinson
parent dbba87d73e
commit b6d9b77a15
3 changed files with 47 additions and 46 deletions

View file

@ -145,35 +145,16 @@ pub static SELECTOR_WHITESPACE: &[char] = &[' ', '\t', '\n', '\r', '\x0C'];
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "shmem", derive(ToShmem))] #[cfg_attr(feature = "shmem", derive(ToShmem))]
pub enum ParsedCaseSensitivity { pub enum ParsedCaseSensitivity {
// 's' was specified. /// 's' was specified.
ExplicitCaseSensitive, ExplicitCaseSensitive,
// 'i' was specified. /// 'i' was specified.
AsciiCaseInsensitive, AsciiCaseInsensitive,
// No flags were specified and HTML says this is a case-sensitive attribute. /// No flags were specified and HTML says this is a case-sensitive attribute.
CaseSensitive, CaseSensitive,
// No flags were specified and HTML says this is a case-insensitive attribute. /// No flags were specified and HTML says this is a case-insensitive attribute.
AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument, AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
} }
impl ParsedCaseSensitivity {
pub fn to_unconditional(self, is_html_element_in_html_document: bool) -> CaseSensitivity {
match self {
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
if is_html_element_in_html_document =>
{
CaseSensitivity::AsciiCaseInsensitive
},
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CaseSensitivity { pub enum CaseSensitivity {
CaseSensitive, CaseSensitive,

View file

@ -2,7 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::attr::{AttrSelectorOperation, NamespaceConstraint, ParsedAttrSelectorOperation}; use crate::attr::{
AttrSelectorOperation, CaseSensitivity, NamespaceConstraint, ParsedAttrSelectorOperation,
ParsedCaseSensitivity,
};
use crate::bloom::{BloomFilter, BLOOM_HASH_MASK}; use crate::bloom::{BloomFilter, BLOOM_HASH_MASK};
use crate::nth_index_cache::NthIndexCacheInner; use crate::nth_index_cache::NthIndexCacheInner;
use crate::parser::{AncestorHashes, Combinator, Component, LocalName}; use crate::parser::{AncestorHashes, Combinator, Component, LocalName};
@ -580,12 +583,7 @@ fn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool
where where
E: Element, E: Element,
{ {
let name = select_name( let name = select_name(element, &local_name.name, &local_name.lower_name).borrow();
element.is_html_element_in_html_document(),
&local_name.name,
&local_name.lower_name,
)
.borrow();
element.has_local_name(name) element.has_local_name(name)
} }
@ -665,14 +663,11 @@ where
Component::AttributeInNoNamespaceExists { Component::AttributeInNoNamespaceExists {
ref local_name, ref local_name,
ref local_name_lower, ref local_name_lower,
} => { } => element.attr_matches(
let is_html = element.is_html_element_in_html_document();
element.attr_matches(
&NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()), &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
select_name(is_html, local_name, local_name_lower), select_name(element, local_name, local_name_lower),
&AttrSelectorOperation::Exists, &AttrSelectorOperation::Exists,
) ),
},
Component::AttributeInNoNamespace { Component::AttributeInNoNamespace {
ref local_name, ref local_name,
ref value, ref value,
@ -683,13 +678,12 @@ where
if never_matches { if never_matches {
return false; return false;
} }
let is_html = element.is_html_element_in_html_document();
element.attr_matches( element.attr_matches(
&NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()), &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
local_name, local_name,
&AttrSelectorOperation::WithValue { &AttrSelectorOperation::WithValue {
operator, operator,
case_sensitivity: case_sensitivity.to_unconditional(is_html), case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),
expected_value: value, expected_value: value,
}, },
) )
@ -698,7 +692,6 @@ where
if attr_sel.never_matches { if attr_sel.never_matches {
return false; return false;
} }
let is_html = element.is_html_element_in_html_document();
let empty_string; let empty_string;
let namespace = match attr_sel.namespace() { let namespace = match attr_sel.namespace() {
Some(ns) => ns, Some(ns) => ns,
@ -709,7 +702,7 @@ where
}; };
element.attr_matches( element.attr_matches(
&namespace, &namespace,
select_name(is_html, &attr_sel.local_name, &attr_sel.local_name_lower), select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower),
&match attr_sel.operation { &match attr_sel.operation {
ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists, ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists,
ParsedAttrSelectorOperation::WithValue { ParsedAttrSelectorOperation::WithValue {
@ -718,7 +711,10 @@ where
ref expected_value, ref expected_value,
} => AttrSelectorOperation::WithValue { } => AttrSelectorOperation::WithValue {
operator, operator,
case_sensitivity: case_sensitivity.to_unconditional(is_html), case_sensitivity: to_unconditional_case_sensitivity(
case_sensitivity,
element,
),
expected_value, expected_value,
}, },
}, },
@ -867,14 +863,38 @@ where
} }
#[inline(always)] #[inline(always)]
fn select_name<'a, T>(is_html: bool, local_name: &'a T, local_name_lower: &'a T) -> &'a T { fn select_name<'a, E: Element, T: PartialEq>(
if is_html { element: &E,
local_name: &'a T,
local_name_lower: &'a T,
) -> &'a T {
if local_name == local_name_lower || element.is_html_element_in_html_document() {
local_name_lower local_name_lower
} else { } else {
local_name local_name
} }
} }
#[inline(always)]
fn to_unconditional_case_sensitivity<'a, E: Element>(
parsed: ParsedCaseSensitivity,
element: &E,
) -> CaseSensitivity {
match parsed {
ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
if element.is_html_element_in_html_document() {
CaseSensitivity::AsciiCaseInsensitive
} else {
CaseSensitivity::CaseSensitive
}
},
}
}
#[inline] #[inline]
fn matches_generic_nth_child<E>( fn matches_generic_nth_child<E>(
element: &E, element: &E,

View file

@ -365,7 +365,7 @@ where
ref lower_name, ref lower_name,
} = *local_name; } = *local_name;
let chosen_name = if element.is_html_element_in_html_document() { let chosen_name = if name == lower_name || element.is_html_element_in_html_document() {
lower_name lower_name
} else { } else {
name name