--- a/servo/components/style/dom_apis.rs
+++ b/servo/components/style/dom_apis.rs
@@ -1,22 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Generic implementations of some DOM APIs so they can be shared between Servo
//! and Gecko.
+use Atom;
use context::QuirksMode;
use dom::{TDocument, TElement, TNode};
use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector};
use selectors::{Element, NthIndexCache, SelectorList};
use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode};
-use selectors::parser::{Component, LocalName};
+use selectors::parser::{Combinator, Component, LocalName};
use smallvec::SmallVec;
use std::borrow::Borrow;
/// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>(
element: &E,
selector_list: &SelectorList<E::Impl>,
quirks_mode: QuirksMode,
@@ -234,65 +235,94 @@ where
return true;
}
current = n.parent_node();
}
false
}
+fn find_elements_with_id<E, Q, F>(
+ root: E::ConcreteNode,
+ id: &Atom,
+ results: &mut Q::Output,
+ quirks_mode: QuirksMode,
+ mut filter: F,
+)
+where
+ E: TElement,
+ Q: SelectorQuery<E>,
+ F: FnMut(E) -> bool,
+{
+ let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
+ if case_sensitivity == CaseSensitivity::CaseSensitive &&
+ root.is_in_document()
+ {
+ let doc = root.owner_doc();
+ if let Ok(elements) = doc.elements_with_id(id) {
+ if root == doc.as_node() {
+ for element in elements {
+ if !filter(*element) {
+ continue;
+ }
+
+ Q::append_element(results, *element);
+ if Q::should_stop_after_first_match() {
+ break;
+ }
+ }
+ } else {
+ for element in elements {
+ if !element_is_descendant_of(*element, root) {
+ continue;
+ }
+
+ if !filter(*element) {
+ continue;
+ }
+
+ Q::append_element(results, *element);
+ if Q::should_stop_after_first_match() {
+ break;
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ collect_all_elements::<E, Q, _>(root, results, |element| {
+ element.has_id(id, case_sensitivity) && filter(element)
+ });
+}
+
+
/// Fast paths for querySelector with a single simple selector.
fn query_selector_single_query<E, Q>(
root: E::ConcreteNode,
component: &Component<E::Impl>,
results: &mut Q::Output,
quirks_mode: QuirksMode,
) -> Result<(), ()>
where
E: TElement,
Q: SelectorQuery<E>,
{
match *component {
Component::ExplicitUniversalType => {
collect_all_elements::<E, Q, _>(root, results, |_| true)
}
Component::ID(ref id) => {
- let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
-
- if case_sensitivity == CaseSensitivity::CaseSensitive &&
- root.is_in_document()
- {
- let doc = root.owner_doc();
- if let Ok(elements) = doc.elements_with_id(id) {
- if root == doc.as_node() {
- for element in elements {
- Q::append_element(results, *element);
- if Q::should_stop_after_first_match() {
- break;
- }
- }
- } else {
- for element in elements {
- if !element_is_descendant_of(*element, root) {
- continue;
- }
-
- Q::append_element(results, *element);
- if Q::should_stop_after_first_match() {
- break;
- }
- }
- }
- return Ok(())
- }
- }
-
- collect_all_elements::<E, Q, _>(root, results, |element| {
- element.has_id(id, case_sensitivity)
- })
+ find_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| {
element.has_class(class, case_sensitivity)
})
}
Component::LocalName(LocalName { ref name, ref lower_name }) => {
@@ -316,43 +346,95 @@ where
/// Fast paths for a given selector query.
///
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
/// replaced by HolyJit :)
fn query_selector_fast<E, Q>(
root: E::ConcreteNode,
selector_list: &SelectorList<E::Impl>,
results: &mut Q::Output,
- quirks_mode: QuirksMode,
+ matching_context: &mut MatchingContext<E::Impl>,
) -> Result<(), ()>
where
E: TElement,
Q: SelectorQuery<E>,
{
// We need to return elements in document order, and reordering them
// afterwards is kinda silly.
if selector_list.0.len() > 1 {
return Err(());
}
let selector = &selector_list.0[0];
+ let quirks_mode = matching_context.quirks_mode();
// Let's just care about the easy cases for now.
if selector.len() == 1 {
return query_selector_single_query::<E, Q>(
root,
selector.iter().next().unwrap(),
results,
quirks_mode,
);
}
- // FIXME(emilio): Implement better optimizations for compound selectors and
- // such.
- Err(())
+ let mut iter = selector.iter();
+ let mut combinator: Option<Combinator> = None;
+
+ loop {
+ debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
+
+ for component in &mut iter {
+ match *component {
+ Component::ID(ref id) => {
+ if combinator.is_none() {
+ // In the rightmost compound, just find descednants of
+ // root that match the selector list with that id.
+ find_elements_with_id::<E, Q, _>(
+ root,
+ id,
+ results,
+ quirks_mode,
+ |e| {
+ matching::matches_selector_list(
+ selector_list,
+ &e,
+ matching_context,
+ )
+ }
+ );
+
+ return Ok(());
+ }
+
+ // TODO(emilio): Find descendants of `root` that are
+ // descendants of an element with a given `id`.
+ }
+ _ => {},
+ }
+ }
+
+ loop {
+ let next_combinator = match iter.next_sequence() {
+ None => return Err(()),
+ Some(c) => c,
+ };
+
+ // We don't want to scan stuff affected by sibling combinators,
+ // given we scan the subtree of elements with a given id (and we
+ // don't want to care about scanning the siblings' subtrees).
+ if next_combinator.is_sibling() {
+ for _ in &mut iter {}
+ continue;
+ }
+
+ combinator = Some(next_combinator);
+ break;
+ }
+ }
}
// Slow path for a given selector query.
fn query_selector_slow<E, Q>(
root: E::ConcreteNode,
selector_list: &SelectorList<E::Impl>,
results: &mut Q::Output,
matching_context: &mut MatchingContext<E::Impl>,
@@ -374,42 +456,44 @@ pub fn query_selector<E, Q>(
)
where
E: TElement,
Q: SelectorQuery<E>,
{
use invalidation::element::invalidator::TreeStyleInvalidator;
let quirks_mode = root.owner_doc().quirks_mode();
- let fast_result = query_selector_fast::<E, Q>(
- root,
- selector_list,
- results,
- quirks_mode,
- );
- if fast_result.is_ok() {
- return;
- }
-
- // Slow path: Use the invalidation machinery if we're a root, and tree
- // traversal otherwise.
- //
- // See the comment in collect_invalidations to see why only if we're a root.
let mut nth_index_cache = NthIndexCache::default();
let mut matching_context = MatchingContext::new(
MatchingMode::Normal,
None,
Some(&mut nth_index_cache),
quirks_mode,
);
let root_element = root.as_element();
matching_context.scope_element = root_element.map(|e| e.opaque());
+ let fast_result = query_selector_fast::<E, Q>(
+ root,
+ selector_list,
+ results,
+ &mut matching_context,
+ );
+
+ if fast_result.is_ok() {
+ return;
+ }
+
+ // Slow path: Use the invalidation machinery if we're a root, and tree
+ // traversal otherwise.
+ //
+ // See the comment in collect_invalidations to see why only if we're a root.
+ //
// The invalidation mechanism is only useful in presence of combinators.
//
// We could do that check properly here, though checking the length of the
// selectors is a good heuristic.
let invalidation_may_be_useful =
selector_list.0.iter().any(|s| s.len() > 1);
if root_element.is_some() || !invalidation_may_be_useful {