mirror of
https://github.com/servo/servo.git
synced 2025-06-21 07:38:59 +01:00
style: Add a query-selector fast path for #id foo.
MozReview-Commit-ID: DkrLcfQLPga
This commit is contained in:
parent
ea8f493ae6
commit
eea8ecd345
2 changed files with 98 additions and 58 deletions
|
@ -147,7 +147,8 @@ pub trait TDocument : Sized + Copy + Clone {
|
||||||
/// Returns the quirks mode of this document.
|
/// Returns the quirks mode of this document.
|
||||||
fn quirks_mode(&self) -> QuirksMode;
|
fn quirks_mode(&self) -> QuirksMode;
|
||||||
|
|
||||||
/// Get a list of elements with a given ID in this document.
|
/// Get a list of elements with a given ID in this document, sorted by
|
||||||
|
/// document position.
|
||||||
///
|
///
|
||||||
/// Can return an error to signal that this list is not available, or also
|
/// Can return an error to signal that this list is not available, or also
|
||||||
/// return an empty slice.
|
/// return an empty slice.
|
||||||
|
|
|
@ -229,6 +229,10 @@ fn element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
|
||||||
where
|
where
|
||||||
E: TElement,
|
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();
|
let mut current = element.as_node().parent_node();
|
||||||
while let Some(n) = current.take() {
|
while let Some(n) = current.take() {
|
||||||
if n == root {
|
if n == root {
|
||||||
|
@ -240,62 +244,32 @@ where
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute `callback` on each element with a given `id` under `root`.
|
/// Fast path for iterating over every element with a given id in the document
|
||||||
///
|
/// that `root` is connected to.
|
||||||
/// If `callback` returns false, iteration will stop immediately.
|
fn fast_connected_elements_with_id<'a, D>(
|
||||||
fn each_element_with_id_under<E, F>(
|
doc: &'a D,
|
||||||
root: E::ConcreteNode,
|
root: D::ConcreteNode,
|
||||||
id: &Atom,
|
id: &Atom,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
mut callback: F,
|
) -> Result<&'a [<D::ConcreteNode as TNode>::ConcreteElement], ()>
|
||||||
)
|
|
||||||
where
|
where
|
||||||
E: TElement,
|
D: TDocument,
|
||||||
F: FnMut(E) -> bool,
|
|
||||||
{
|
{
|
||||||
|
debug_assert_eq!(root.owner_doc().as_node(), doc.as_node());
|
||||||
|
|
||||||
let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
|
let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
|
||||||
if case_sensitivity == CaseSensitivity::CaseSensitive &&
|
if case_sensitivity != CaseSensitivity::CaseSensitive {
|
||||||
root.is_in_document()
|
return Err(());
|
||||||
{
|
|
||||||
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) {
|
if !root.is_in_document() {
|
||||||
return;
|
return Err(());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for node in root.dom_descendants() {
|
doc.elements_with_id(id)
|
||||||
let element = match node.as_element() {
|
|
||||||
Some(e) => e,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !element.has_id(id, case_sensitivity) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !callback(element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collects elements with a given id under `root`, that pass `filter`.
|
||||||
fn collect_elements_with_id<E, Q, F>(
|
fn collect_elements_with_id<E, Q, F>(
|
||||||
root: E::ConcreteNode,
|
root: E::ConcreteNode,
|
||||||
id: &Atom,
|
id: &Atom,
|
||||||
|
@ -308,16 +282,38 @@ where
|
||||||
Q: SelectorQuery<E>,
|
Q: SelectorQuery<E>,
|
||||||
F: FnMut(E) -> bool,
|
F: FnMut(E) -> bool,
|
||||||
{
|
{
|
||||||
each_element_with_id_under::<E, _>(root, id, quirks_mode, |element| {
|
let doc = root.owner_doc();
|
||||||
if !filter(element) {
|
let elements = match fast_connected_elements_with_id(&doc, root, id, quirks_mode) {
|
||||||
return true;
|
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);
|
if !filter(*element) {
|
||||||
return !Q::should_stop_after_first_match()
|
continue;
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
Q::append_element(results, *element);
|
||||||
|
if Q::should_stop_after_first_match() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fast paths for querySelector with a single simple selector.
|
/// Fast paths for querySelector with a single simple selector.
|
||||||
fn query_selector_single_query<E, Q>(
|
fn query_selector_single_query<E, Q>(
|
||||||
|
@ -341,7 +337,7 @@ where
|
||||||
results,
|
results,
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
|_| true,
|
|_| true,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Component::Class(ref class) => {
|
Component::Class(ref class) => {
|
||||||
let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
|
let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
|
||||||
|
@ -406,7 +402,7 @@ where
|
||||||
loop {
|
loop {
|
||||||
debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
|
debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
|
||||||
|
|
||||||
for component in &mut iter {
|
'component_loop: for component in &mut iter {
|
||||||
match *component {
|
match *component {
|
||||||
Component::ID(ref id) => {
|
Component::ID(ref id) => {
|
||||||
if combinator.is_none() {
|
if combinator.is_none() {
|
||||||
|
@ -429,8 +425,51 @@ where
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(emilio): Find descendants of `root` that are
|
let doc = root.owner_doc();
|
||||||
// descendants of an element with a given `id`.
|
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, which we would need to
|
||||||
|
// do if one element with the given id were a descendant of
|
||||||
|
// another element with that given id.
|
||||||
|
if !Q::should_stop_after_first_match() && elements.len() > 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for element in elements {
|
||||||
|
// If the element is not a descendant of the root, then
|
||||||
|
// it may have descendants that match our selector that
|
||||||
|
// _are_ descendants of the root, and other descendants
|
||||||
|
// that match our selector that are _not_.
|
||||||
|
//
|
||||||
|
// So we can't just walk over the element's descendants
|
||||||
|
// and match the selector against all of them, nor can
|
||||||
|
// we skip looking at this element's descendants.
|
||||||
|
//
|
||||||
|
// Give up on trying to optimize based on this id and
|
||||||
|
// keep walking our selector.
|
||||||
|
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(());
|
||||||
}
|
}
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue