mirror of
https://github.com/servo/servo.git
synced 2025-06-18 21:34:30 +00:00
Optimize selector matching for some common cases.
This commit is contained in:
parent
7de87c487b
commit
d0fd92221c
2 changed files with 92 additions and 26 deletions
|
@ -8,6 +8,7 @@ use nth_index_cache::NthIndexCacheInner;
|
||||||
use parser::{AncestorHashes, Combinator, Component, LocalName};
|
use parser::{AncestorHashes, Combinator, Component, LocalName};
|
||||||
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList};
|
use parser::{Selector, SelectorImpl, SelectorIter, SelectorList};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
use std::iter;
|
||||||
use tree::Element;
|
use tree::Element;
|
||||||
|
|
||||||
pub use context::*;
|
pub use context::*;
|
||||||
|
@ -222,7 +223,7 @@ pub enum CompoundSelectorMatchingResult {
|
||||||
///
|
///
|
||||||
/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
|
/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
|
||||||
/// complex selector, but it happens to be the case we don't need it.
|
/// complex selector, but it happens to be the case we don't need it.
|
||||||
pub fn matches_compound_selector<E>(
|
pub fn matches_compound_selector_from<E>(
|
||||||
selector: &Selector<E::Impl>,
|
selector: &Selector<E::Impl>,
|
||||||
mut from_offset: usize,
|
mut from_offset: usize,
|
||||||
context: &mut MatchingContext<E::Impl>,
|
context: &mut MatchingContext<E::Impl>,
|
||||||
|
@ -426,31 +427,21 @@ where
|
||||||
{
|
{
|
||||||
debug!("Matching complex selector {:?} for {:?}", selector_iter, element);
|
debug!("Matching complex selector {:?} for {:?}", selector_iter, element);
|
||||||
|
|
||||||
let matches_all_simple_selectors = {
|
let matches_compound_selector = matches_compound_selector(
|
||||||
let matches_hover_and_active_quirk =
|
&mut selector_iter,
|
||||||
matches_hover_and_active_quirk(&selector_iter, context, rightmost);
|
|
||||||
let mut local_context =
|
|
||||||
LocalMatchingContext {
|
|
||||||
shared: context,
|
|
||||||
visited_handling,
|
|
||||||
matches_hover_and_active_quirk,
|
|
||||||
};
|
|
||||||
selector_iter.all(|simple| {
|
|
||||||
matches_simple_selector(
|
|
||||||
simple,
|
|
||||||
element,
|
element,
|
||||||
&mut local_context,
|
context,
|
||||||
|
visited_handling,
|
||||||
flags_setter,
|
flags_setter,
|
||||||
)
|
rightmost
|
||||||
})
|
);
|
||||||
};
|
|
||||||
|
|
||||||
let combinator = selector_iter.next_sequence();
|
let combinator = selector_iter.next_sequence();
|
||||||
if combinator.map_or(false, |c| c.is_sibling()) {
|
if combinator.map_or(false, |c| c.is_sibling()) {
|
||||||
flags_setter(element, ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
flags_setter(element, ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches_all_simple_selectors {
|
if !matches_compound_selector {
|
||||||
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
|
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,8 +532,84 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the given element matches the given single selector.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
fn matches_local_name<E>(
|
||||||
|
element: &E,
|
||||||
|
local_name: &LocalName<E::Impl>
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: Element,
|
||||||
|
{
|
||||||
|
let name = select_name(
|
||||||
|
element.is_html_element_in_html_document(),
|
||||||
|
&local_name.name,
|
||||||
|
&local_name.lower_name
|
||||||
|
).borrow();
|
||||||
|
element.get_local_name() == name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether the given element matches the given compound selector.
|
||||||
|
#[inline]
|
||||||
|
fn matches_compound_selector<E, F>(
|
||||||
|
selector_iter: &mut SelectorIter<E::Impl>,
|
||||||
|
element: &E,
|
||||||
|
context: &mut MatchingContext<E::Impl>,
|
||||||
|
visited_handling: VisitedHandlingMode,
|
||||||
|
flags_setter: &mut F,
|
||||||
|
rightmost: Rightmost,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: Element,
|
||||||
|
F: FnMut(&E, ElementSelectorFlags),
|
||||||
|
{
|
||||||
|
let matches_hover_and_active_quirk =
|
||||||
|
matches_hover_and_active_quirk(&selector_iter, context, rightmost);
|
||||||
|
|
||||||
|
// Handle some common cases first.
|
||||||
|
// We may want to get rid of this at some point if we can make the
|
||||||
|
// generic case fast enough.
|
||||||
|
let mut selector = selector_iter.next();
|
||||||
|
if let Some(&Component::LocalName(ref local_name)) = selector {
|
||||||
|
if !matches_local_name(element, local_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
selector = selector_iter.next();
|
||||||
|
}
|
||||||
|
let class_and_id_case_sensitivity = context.classes_and_ids_case_sensitivity();
|
||||||
|
if let Some(&Component::ID(ref id)) = selector {
|
||||||
|
if !element.has_id(id, class_and_id_case_sensitivity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
selector = selector_iter.next();
|
||||||
|
}
|
||||||
|
while let Some(&Component::Class(ref class)) = selector {
|
||||||
|
if !element.has_class(class, class_and_id_case_sensitivity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
selector = selector_iter.next();
|
||||||
|
}
|
||||||
|
let selector = match selector {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut local_context =
|
||||||
|
LocalMatchingContext {
|
||||||
|
shared: context,
|
||||||
|
visited_handling,
|
||||||
|
matches_hover_and_active_quirk,
|
||||||
|
};
|
||||||
|
iter::once(selector).chain(selector_iter).all(|simple| {
|
||||||
|
matches_simple_selector(
|
||||||
|
simple,
|
||||||
|
element,
|
||||||
|
&mut local_context,
|
||||||
|
flags_setter,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines whether the given element matches the given single selector.
|
||||||
fn matches_simple_selector<E, F>(
|
fn matches_simple_selector<E, F>(
|
||||||
selector: &Component<E::Impl>,
|
selector: &Component<E::Impl>,
|
||||||
element: &E,
|
element: &E,
|
||||||
|
@ -571,9 +638,8 @@ where
|
||||||
Component::PseudoElement(ref pseudo) => {
|
Component::PseudoElement(ref pseudo) => {
|
||||||
element.match_pseudo_element(pseudo, context.shared)
|
element.match_pseudo_element(pseudo, context.shared)
|
||||||
}
|
}
|
||||||
Component::LocalName(LocalName { ref name, ref lower_name }) => {
|
Component::LocalName(ref local_name) => {
|
||||||
let is_html = element.is_html_element_in_html_document();
|
matches_local_name(element, local_name)
|
||||||
element.get_local_name() == select_name(is_html, name, lower_name).borrow()
|
|
||||||
}
|
}
|
||||||
Component::ExplicitUniversalType |
|
Component::ExplicitUniversalType |
|
||||||
Component::ExplicitAnyNamespace => {
|
Component::ExplicitAnyNamespace => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use context::StackLimitChecker;
|
||||||
use dom::{TElement, TNode};
|
use dom::{TElement, TNode};
|
||||||
use selector_parser::SelectorImpl;
|
use selector_parser::SelectorImpl;
|
||||||
use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
|
use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
|
||||||
use selectors::matching::matches_compound_selector;
|
use selectors::matching::matches_compound_selector_from;
|
||||||
use selectors::parser::{Combinator, Component, Selector};
|
use selectors::parser::{Combinator, Component, Selector};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -691,7 +691,7 @@ where
|
||||||
debug!("TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
|
debug!("TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})",
|
||||||
self.element, invalidation, invalidation_kind);
|
self.element, invalidation, invalidation_kind);
|
||||||
|
|
||||||
let matching_result = matches_compound_selector(
|
let matching_result = matches_compound_selector_from(
|
||||||
&invalidation.selector,
|
&invalidation.selector,
|
||||||
invalidation.offset,
|
invalidation.offset,
|
||||||
self.processor.matching_context(),
|
self.processor.matching_context(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue