1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
5 //! Generic implementations of some DOM APIs so they can be shared between Servo
8 use crate::context::QuirksMode;
9 use crate::dom::{TDocument, TElement, TNode, TShadowRoot};
10 use crate::invalidation::element::invalidation_map::Dependency;
11 use crate::invalidation::element::invalidator::{
12 DescendantInvalidationLists, Invalidation, SiblingTraversalMap,
14 use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
15 use crate::selector_parser::SelectorImpl;
16 use crate::values::AtomIdent;
17 use selectors::attr::CaseSensitivity;
18 use selectors::matching::{
19 self, MatchingForInvalidation, MatchingContext, MatchingMode, NeedsSelectorFlags,
22 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
23 use selectors::parser::{Combinator, Component, LocalName};
24 use selectors::{Element, SelectorList};
25 use smallvec::SmallVec;
27 /// <https://dom.spec.whatwg.org/#dom-element-matches>
28 pub fn element_matches<E>(
30 selector_list: &SelectorList<E::Impl>,
31 quirks_mode: QuirksMode,
36 let mut selector_caches = SelectorCaches::default();
38 let mut context = MatchingContext::new(
43 NeedsSelectorFlags::No,
44 MatchingForInvalidation::No,
46 context.scope_element = Some(element.opaque());
47 context.current_host = element.containing_shadow_host().map(|e| e.opaque());
48 matching::matches_selector_list(selector_list, element, &mut context)
51 /// <https://dom.spec.whatwg.org/#dom-element-closest>
52 pub fn element_closest<E>(
54 selector_list: &SelectorList<E::Impl>,
55 quirks_mode: QuirksMode,
60 let mut selector_caches = SelectorCaches::default();
62 let mut context = MatchingContext::new(
67 NeedsSelectorFlags::No,
68 MatchingForInvalidation::No,
70 context.scope_element = Some(element.opaque());
71 context.current_host = element.containing_shadow_host().map(|e| e.opaque());
73 let mut current = Some(element);
74 while let Some(element) = current.take() {
75 if matching::matches_selector_list(selector_list, &element, &mut context) {
78 current = element.parent_element();
84 /// A selector query abstraction, in order to be generic over QuerySelector and
86 pub trait SelectorQuery<E: TElement> {
87 /// The output of the query.
90 /// Whether the query should stop after the first element has been matched.
91 fn should_stop_after_first_match() -> bool;
93 /// Append an element matching after the first query.
94 fn append_element(output: &mut Self::Output, element: E);
96 /// Returns true if the output is empty.
97 fn is_empty(output: &Self::Output) -> bool;
100 /// The result of a querySelectorAll call.
101 pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;
103 /// A query for all the elements in a subtree.
106 impl<E: TElement> SelectorQuery<E> for QueryAll {
107 type Output = QuerySelectorAllResult<E>;
109 fn should_stop_after_first_match() -> bool {
113 fn append_element(output: &mut Self::Output, element: E) {
114 output.push(element);
117 fn is_empty(output: &Self::Output) -> bool {
122 /// A query for the first in-tree match of all the elements in a subtree.
123 pub struct QueryFirst;
125 impl<E: TElement> SelectorQuery<E> for QueryFirst {
126 type Output = Option<E>;
128 fn should_stop_after_first_match() -> bool {
132 fn append_element(output: &mut Self::Output, element: E) {
133 if output.is_none() {
134 *output = Some(element)
138 fn is_empty(output: &Self::Output) -> bool {
143 struct QuerySelectorProcessor<'a, 'b, E, Q>
149 results: &'a mut Q::Output,
150 matching_context: MatchingContext<'b, E::Impl>,
151 traversal_map: SiblingTraversalMap<E>,
152 dependencies: &'a [Dependency],
155 impl<'a, 'b, E, Q> InvalidationProcessor<'a, 'b, E> for QuerySelectorProcessor<'a, 'b, E, Q>
161 fn light_tree_only(&self) -> bool {
165 fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
168 "How? We should only have parent-less dependencies here!"
173 fn collect_invalidations(
176 self_invalidations: &mut InvalidationVector<'a>,
177 descendant_invalidations: &mut DescendantInvalidationLists<'a>,
178 _sibling_invalidations: &mut InvalidationVector<'a>,
180 // TODO(emilio): If the element is not a root element, and
181 // selector_list has any descendant combinator, we need to do extra work
182 // in order to handle properly things like:
186 // <div id="c"></div>
190 // b.querySelector('#a div'); // Should return "c".
192 // For now, assert it's a root element.
193 debug_assert!(element.parent_element().is_none());
195 let target_vector = if self.matching_context.scope_element.is_some() {
196 &mut descendant_invalidations.dom_descendants
201 for dependency in self.dependencies.iter() {
202 target_vector.push(Invalidation::new(
204 self.matching_context.current_host.clone(),
211 fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> {
212 &mut self.matching_context
215 fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
219 fn should_process_descendants(&mut self, _: E) -> bool {
220 if Q::should_stop_after_first_match() {
221 return Q::is_empty(&self.results);
227 fn invalidated_self(&mut self, e: E) {
228 Q::append_element(self.results, e);
231 fn invalidated_sibling(&mut self, e: E, _of: E) {
232 Q::append_element(self.results, e);
235 fn recursion_limit_exceeded(&mut self, _e: E) {}
236 fn invalidated_descendants(&mut self, _e: E, _child: E) {}
239 fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)
245 for node in root.dom_descendants() {
246 let element = match node.as_element() {
251 if !filter(element) {
255 Q::append_element(results, element);
256 if Q::should_stop_after_first_match() {
262 /// Returns whether a given element connected to `root` is descendant of `root`.
264 /// NOTE(emilio): if root == element, this returns false.
265 fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
269 // Optimize for when the root is a document or a shadow root and the element
270 // is connected to that root.
271 if root.as_document().is_some() {
272 debug_assert!(element.as_node().is_in_document(), "Not connected?");
275 root.owner_doc().as_node(),
276 "Where did this element come from?",
281 if root.as_shadow_root().is_some() {
283 element.containing_shadow().unwrap().as_node(),
290 let mut current = element.as_node().parent_node();
291 while let Some(n) = current.take() {
296 current = n.parent_node();
301 /// Fast path for iterating over every element with a given id in the document
302 /// or shadow root that `root` is connected to.
303 fn fast_connected_elements_with_id<'a, N>(
306 case_sensitivity: CaseSensitivity,
307 ) -> Result<&'a [N::ConcreteElement], ()>
311 if case_sensitivity != CaseSensitivity::CaseSensitive {
315 if root.is_in_document() {
316 return root.owner_doc().elements_with_id(id);
319 if let Some(shadow) = root.as_shadow_root() {
320 return shadow.elements_with_id(id);
323 if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
324 return shadow.elements_with_id(id);
330 /// Collects elements with a given id under `root`, that pass `filter`.
331 fn collect_elements_with_id<E, Q, F>(
332 root: E::ConcreteNode,
334 results: &mut Q::Output,
335 class_and_id_case_sensitivity: CaseSensitivity,
342 let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) {
343 Ok(elements) => elements,
345 collect_all_elements::<E, Q, _>(root, results, |e| {
346 e.has_id(id, class_and_id_case_sensitivity) && filter(e)
353 for element in elements {
354 // If the element is not an actual descendant of the root, even though
355 // it's connected, we don't really care about it.
356 if !connected_element_is_descendant_of(*element, root) {
360 if !filter(*element) {
364 Q::append_element(results, *element);
365 if Q::should_stop_after_first_match() {
371 fn has_attr<E>(element: E, local_name: &AtomIdent) -> bool
375 let mut found = false;
376 element.each_attr_name(|name| found |= name == local_name);
381 fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool
390 let chosen_name = if name == lower_name || element.is_html_element_in_html_document() {
396 element.local_name() == &**chosen_name
399 fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
400 let (name, name_lower) = match component {
401 Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),
402 Component::AttributeInNoNamespaceExists {
404 ref local_name_lower,
406 } => (local_name, local_name_lower),
407 Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower),
410 if name != name_lower {
411 return None; // TODO: Maybe optimize this?
416 fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
417 use selectors::attr::AttrSelectorOperator;
418 Some(match component {
419 Component::ID(ref id) => id,
420 Component::AttributeInNoNamespace {
426 if *local_name != local_name!("id") {
429 if *operator != AttrSelectorOperator::Equal {
432 AtomIdent::cast(&value.0)
438 /// Fast paths for querySelector with a single simple selector.
439 fn query_selector_single_query<E, Q>(
440 root: E::ConcreteNode,
441 component: &Component<E::Impl>,
442 results: &mut Q::Output,
443 class_and_id_case_sensitivity: CaseSensitivity,
450 Component::ExplicitUniversalType => {
451 collect_all_elements::<E, Q, _>(root, results, |_| true)
453 Component::Class(ref class) => collect_all_elements::<E, Q, _>(root, results, |element| {
454 element.has_class(class, class_and_id_case_sensitivity)
456 Component::LocalName(ref local_name) => {
457 collect_all_elements::<E, Q, _>(root, results, |element| {
458 local_name_matches(element, local_name)
461 Component::AttributeInNoNamespaceExists {
463 ref local_name_lower,
465 collect_all_elements::<E, Q, _>(root, results, |element| {
466 element.has_attr_in_no_namespace(matching::select_name(&element, local_name, local_name_lower))
469 Component::AttributeInNoNamespace {
475 let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>();
476 let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace);
477 collect_all_elements::<E, Q, _>(root, results, |element| {
478 element.attr_matches(
479 &namespace_constraint,
481 &AttrSelectorOperation::WithValue {
483 case_sensitivity: matching::to_unconditional_case_sensitivity(case_sensitivity, &element),
490 let id = match get_id(other) {
492 // TODO(emilio): More fast paths?
493 None => return Err(()),
495 collect_elements_with_id::<E, Q, _>(
499 class_and_id_case_sensitivity,
508 enum SimpleFilter<'a> {
509 Class(&'a AtomIdent),
511 LocalName(&'a LocalName<SelectorImpl>),
514 /// Fast paths for a given selector query.
516 /// When there's only one component, we go directly to
517 /// `query_selector_single_query`, otherwise, we try to optimize by looking just
518 /// at the subtrees rooted at ids in the selector, and otherwise we try to look
519 /// up by class name or local name in the rightmost compound.
521 /// FIXME(emilio, nbp): This may very well be a good candidate for code to be
522 /// replaced by HolyJit :)
523 fn query_selector_fast<E, Q>(
524 root: E::ConcreteNode,
525 selector_list: &SelectorList<E::Impl>,
526 results: &mut Q::Output,
527 matching_context: &mut MatchingContext<E::Impl>,
533 // We need to return elements in document order, and reordering them
534 // afterwards is kinda silly.
535 if selector_list.0.len() > 1 {
539 let selector = &selector_list.0[0];
540 let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
541 // Let's just care about the easy cases for now.
542 if selector.len() == 1 {
543 if query_selector_single_query::<E, Q>(
545 selector.iter().next().unwrap(),
547 class_and_id_case_sensitivity,
555 let mut iter = selector.iter();
556 let mut combinator: Option<Combinator> = None;
558 // We want to optimize some cases where there's no id involved whatsoever,
559 // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
561 let mut simple_filter = None;
563 'selector_loop: loop {
564 debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
566 'component_loop: for component in &mut iter {
568 Component::Class(ref class) => {
569 if combinator.is_none() {
570 simple_filter = Some(SimpleFilter::Class(class));
573 Component::LocalName(ref local_name) => {
574 if combinator.is_none() {
575 // Prefer to look at class rather than local-name if
577 if let Some(SimpleFilter::Class(..)) = simple_filter {
580 simple_filter = Some(SimpleFilter::LocalName(local_name));
584 if let Some(id) = get_id(other) {
585 if combinator.is_none() {
586 // In the rightmost compound, just find descendants of root that match
587 // the selector list with that id.
588 collect_elements_with_id::<E, Q, _>(
592 class_and_id_case_sensitivity,
594 matching::matches_selector_list(
604 let elements = fast_connected_elements_with_id(
607 class_and_id_case_sensitivity,
609 if elements.is_empty() {
613 // Results need to be in document order. Let's not bother
614 // reordering or deduplicating nodes, which we would need to
615 // do if one element with the given id were a descendant of
616 // another element with that given id.
617 if !Q::should_stop_after_first_match() && elements.len() > 1 {
621 for element in elements {
622 // If the element is not a descendant of the root, then
623 // it may have descendants that match our selector that
624 // _are_ descendants of the root, and other descendants
625 // that match our selector that are _not_.
627 // So we can't just walk over the element's descendants
628 // and match the selector against all of them, nor can
629 // we skip looking at this element's descendants.
631 // Give up on trying to optimize based on this id and
632 // keep walking our selector.
633 if !connected_element_is_descendant_of(*element, root) {
634 continue 'component_loop;
637 query_selector_slow::<E, Q>(
644 if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
651 if combinator.is_none() && simple_filter.is_none() {
652 if let Some(attr_name) = get_attr_name(other) {
653 simple_filter = Some(SimpleFilter::Attr(attr_name));
661 let next_combinator = match iter.next_sequence() {
662 None => break 'selector_loop,
666 // We don't want to scan stuff affected by sibling combinators,
667 // given we scan the subtree of elements with a given id (and we
668 // don't want to care about scanning the siblings' subtrees).
669 if next_combinator.is_sibling() {
670 // Advance to the next combinator.
671 for _ in &mut iter {}
675 combinator = Some(next_combinator);
680 // We got here without finding any ID or such that we could handle. Try to
681 // use one of the simple filters.
682 let simple_filter = match simple_filter {
684 None => return Err(()),
687 match simple_filter {
688 SimpleFilter::Class(ref class) => {
689 collect_all_elements::<E, Q, _>(root, results, |element| {
690 element.has_class(class, class_and_id_case_sensitivity) &&
691 matching::matches_selector_list(selector_list, &element, matching_context)
694 SimpleFilter::LocalName(ref local_name) => {
695 collect_all_elements::<E, Q, _>(root, results, |element| {
696 local_name_matches(element, local_name) &&
697 matching::matches_selector_list(selector_list, &element, matching_context)
700 SimpleFilter::Attr(ref local_name) => {
701 collect_all_elements::<E, Q, _>(root, results, |element| {
702 has_attr(element, local_name) &&
703 matching::matches_selector_list(selector_list, &element, matching_context)
711 // Slow path for a given selector query.
712 fn query_selector_slow<E, Q>(
713 root: E::ConcreteNode,
714 selector_list: &SelectorList<E::Impl>,
715 results: &mut Q::Output,
716 matching_context: &mut MatchingContext<E::Impl>,
721 collect_all_elements::<E, Q, _>(root, results, |element| {
722 matching::matches_selector_list(selector_list, &element, matching_context)
726 /// Whether the invalidation machinery should be used for this query.
728 pub enum MayUseInvalidation {
729 /// We may use it if we deem it useful.
735 /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
736 pub fn query_selector<E, Q>(
737 root: E::ConcreteNode,
738 selector_list: &SelectorList<E::Impl>,
739 results: &mut Q::Output,
740 may_use_invalidation: MayUseInvalidation,
745 use crate::invalidation::element::invalidator::TreeStyleInvalidator;
747 let mut selector_caches = SelectorCaches::default();
748 let quirks_mode = root.owner_doc().quirks_mode();
750 let mut matching_context = MatchingContext::new(
751 MatchingMode::Normal,
753 &mut selector_caches,
755 NeedsSelectorFlags::No,
756 MatchingForInvalidation::No,
758 let root_element = root.as_element();
759 matching_context.scope_element = root_element.map(|e| e.opaque());
760 matching_context.current_host = match root_element {
761 Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
762 None => root.as_shadow_root().map(|root| root.host().opaque()),
766 query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);
768 if fast_result.is_ok() {
772 // Slow path: Use the invalidation machinery if we're a root, and tree
773 // traversal otherwise.
775 // See the comment in collect_invalidations to see why only if we're a root.
777 // The invalidation mechanism is only useful in presence of combinators.
779 // We could do that check properly here, though checking the length of the
780 // selectors is a good heuristic.
782 // A selector with a combinator needs to have a length of at least 3: A
783 // simple selector, a combinator, and another simple selector.
784 let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes &&
785 selector_list.0.iter().any(|s| s.len() > 2);
787 if root_element.is_some() || !invalidation_may_be_useful {
788 query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
790 let dependencies = selector_list
793 .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
794 .collect::<SmallVec<[_; 5]>>();
795 let mut processor = QuerySelectorProcessor::<E, Q> {
798 traversal_map: SiblingTraversalMap::default(),
799 dependencies: &dependencies,
802 for node in root.dom_children() {
803 if let Some(e) = node.as_element() {
804 TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)