--- a/servo/components/style/dom_apis.rs
+++ b/servo/components/style/dom_apis.rs
@@ -224,106 +224,102 @@ where
/// Returns whether a given element is descendant of a given `root` node.
///
/// NOTE(emilio): if root == element, this returns false.
fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
where
E: TElement,
{
+ if element.as_node().is_in_document() && root == root.owner_doc().as_node() {
+ return true;
+ }
+
let mut current = element.as_node().parent_node();
while let Some(n) = current.take() {
if n == root {
return true;
}
current = n.parent_node();
}
false
}
-/// Execute `callback` on each element with a given `id` under `root`.
-///
-/// If `callback` returns false, iteration will stop immediately.
-fn each_element_with_id_under<E, F>(
- root: E::ConcreteNode,
+/// Fast path for iterating over every element with a given id in the document
+/// connected to `root`.
+fn fast_connected_elements_with_id<'a, D>(
+ doc: &'a D,
+ root: D::ConcreteNode,
id: &Atom,
quirks_mode: QuirksMode,
- mut callback: F,
-)
+) -> Result<&'a [<D::ConcreteNode as TNode>::ConcreteElement], ()>
where
- E: TElement,
- F: FnMut(E) -> bool,
+ D: TDocument,
{
+ debug_assert_eq!(root.owner_doc().as_node(), doc.as_node());
+
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 !callback(*element) {
- return;
- }
- }
- } else {
- for element in elements {
- if !element_is_descendant_of(*element, root) {
- continue;
- }
-
- if !callback(*element) {
- return;
- }
- }
- }
- }
- return;
+ if case_sensitivity != CaseSensitivity::CaseSensitive {
+ return Err(());
}
- for node in root.dom_descendants() {
- let element = match node.as_element() {
- Some(e) => e,
- None => continue,
- };
+ if !root.is_in_document() {
+ return Err(());
+ }
- if !element.has_id(id, case_sensitivity) {
- continue;
- }
-
- if !callback(element) {
- return;
- }
- }
+ doc.elements_with_id(id)
}
+/// Collects elements with a given id under `root`, that pass `filter`.
fn collect_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,
{
- each_element_with_id_under::<E, _>(root, id, quirks_mode, |element| {
- if !filter(element) {
- return true;
+ let doc = root.owner_doc();
+ let elements = match fast_connected_elements_with_id(&doc, root, id, quirks_mode) {
+ Ok(elements) => elements,
+ Err(()) => {
+ let case_sensitivity =
+ quirks_mode.classes_and_ids_case_sensitivity();
+
+ collect_all_elements::<E, Q, _>(root, results, |e| {
+ e.has_id(id, case_sensitivity) && filter(e)
+ });
+
+ return;
+ }
+ };
+
+ for element in elements {
+ // If the element is not an actual descendant of the root, even though
+ // it's connected, we don't really care about it.
+ if !element_is_descendant_of(*element, root) {
+ continue;
}
- Q::append_element(results, element);
- return !Q::should_stop_after_first_match()
- })
+ if !filter(*element) {
+ continue;
+ }
+
+ Q::append_element(results, *element);
+ if Q::should_stop_after_first_match() {
+ break;
+ }
+ }
}
-
/// 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
@@ -336,17 +332,17 @@ where
}
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| {
element.has_class(class, case_sensitivity)
})
}
Component::LocalName(LocalName { ref name, ref lower_name }) => {
@@ -401,17 +397,17 @@ where
}
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 {
+ 'component_loop: 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.
collect_elements_with_id::<E, Q, _>(
root,
id,
@@ -424,18 +420,52 @@ where
matching_context,
)
}
);
return Ok(());
}
- // TODO(emilio): Find descendants of `root` that are
- // descendants of an element with a given `id`.
+ let doc = root.owner_doc();
+ let elements =
+ fast_connected_elements_with_id(&doc, root, id, quirks_mode)?;
+
+ if elements.is_empty() {
+ return Ok(());
+ }
+
+ // Results need to be in document order. Let's not bother
+ // reordering or deduplicating nodes.
+ if !Q::should_stop_after_first_match() && elements.len() > 1 {
+ continue;
+ }
+
+ for element in elements {
+ // If the element is not an actual descendant of the
+ // root, even though it's connected, we may end up
+ // finding stuff outside of the scope of `root`, which
+ // we don't really want.
+ if !element_is_descendant_of(element, root) {
+ continue 'component_loop;
+ }
+
+ query_selector_slow::<E, Q>(
+ element.as_node(),
+ selector_list,
+ results,
+ matching_context,
+ );
+
+ if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
+ break;
+ }
+ }
+
+ return Ok(());
}
_ => {},
}
}
loop {
let next_combinator = match iter.next_sequence() {
None => return Err(()),