Backed out 2 changesets (bug 1845095) for causing build bustages related to DummyAtom...
[gecko.git] / servo / components / style / dom_apis.rs
blob56abdab1290dfb3b5f02e93b44f35c96254882d1
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
6 //! and Gecko.
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::{DescendantInvalidationLists, Invalidation};
12 use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
13 use crate::selector_parser::SelectorImpl;
14 use crate::values::AtomIdent;
15 use selectors::attr::CaseSensitivity;
16 use selectors::matching::{
17     self, IgnoreNthChildForInvalidation, MatchingContext, MatchingMode, NeedsSelectorFlags,
18     SelectorCaches,
20 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint};
21 use selectors::parser::{Combinator, Component, LocalName};
22 use selectors::{Element, SelectorList};
23 use smallvec::SmallVec;
25 /// <https://dom.spec.whatwg.org/#dom-element-matches>
26 pub fn element_matches<E>(
27     element: &E,
28     selector_list: &SelectorList<E::Impl>,
29     quirks_mode: QuirksMode,
30 ) -> bool
31 where
32     E: Element,
34     let mut selector_caches = SelectorCaches::default();
36     let mut context = MatchingContext::new(
37         MatchingMode::Normal,
38         None,
39         &mut selector_caches,
40         quirks_mode,
41         NeedsSelectorFlags::No,
42         IgnoreNthChildForInvalidation::No,
43     );
44     context.scope_element = Some(element.opaque());
45     context.current_host = element.containing_shadow_host().map(|e| e.opaque());
46     matching::matches_selector_list(selector_list, element, &mut context)
49 /// <https://dom.spec.whatwg.org/#dom-element-closest>
50 pub fn element_closest<E>(
51     element: E,
52     selector_list: &SelectorList<E::Impl>,
53     quirks_mode: QuirksMode,
54 ) -> Option<E>
55 where
56     E: Element,
58     let mut selector_caches = SelectorCaches::default();
60     let mut context = MatchingContext::new(
61         MatchingMode::Normal,
62         None,
63         &mut selector_caches,
64         quirks_mode,
65         NeedsSelectorFlags::No,
66         IgnoreNthChildForInvalidation::No,
67     );
68     context.scope_element = Some(element.opaque());
69     context.current_host = element.containing_shadow_host().map(|e| e.opaque());
71     let mut current = Some(element);
72     while let Some(element) = current.take() {
73         if matching::matches_selector_list(selector_list, &element, &mut context) {
74             return Some(element);
75         }
76         current = element.parent_element();
77     }
79     return None;
82 /// A selector query abstraction, in order to be generic over QuerySelector and
83 /// QuerySelectorAll.
84 pub trait SelectorQuery<E: TElement> {
85     /// The output of the query.
86     type Output;
88     /// Whether the query should stop after the first element has been matched.
89     fn should_stop_after_first_match() -> bool;
91     /// Append an element matching after the first query.
92     fn append_element(output: &mut Self::Output, element: E);
94     /// Returns true if the output is empty.
95     fn is_empty(output: &Self::Output) -> bool;
98 /// The result of a querySelectorAll call.
99 pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;
101 /// A query for all the elements in a subtree.
102 pub struct QueryAll;
104 impl<E: TElement> SelectorQuery<E> for QueryAll {
105     type Output = QuerySelectorAllResult<E>;
107     fn should_stop_after_first_match() -> bool {
108         false
109     }
111     fn append_element(output: &mut Self::Output, element: E) {
112         output.push(element);
113     }
115     fn is_empty(output: &Self::Output) -> bool {
116         output.is_empty()
117     }
120 /// A query for the first in-tree match of all the elements in a subtree.
121 pub struct QueryFirst;
123 impl<E: TElement> SelectorQuery<E> for QueryFirst {
124     type Output = Option<E>;
126     fn should_stop_after_first_match() -> bool {
127         true
128     }
130     fn append_element(output: &mut Self::Output, element: E) {
131         if output.is_none() {
132             *output = Some(element)
133         }
134     }
136     fn is_empty(output: &Self::Output) -> bool {
137         output.is_none()
138     }
141 struct QuerySelectorProcessor<'a, E, Q>
142 where
143     E: TElement + 'a,
144     Q: SelectorQuery<E>,
145     Q::Output: 'a,
147     results: &'a mut Q::Output,
148     matching_context: MatchingContext<'a, E::Impl>,
149     dependencies: &'a [Dependency],
152 impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
153 where
154     E: TElement + 'a,
155     Q: SelectorQuery<E>,
156     Q::Output: 'a,
158     fn light_tree_only(&self) -> bool {
159         true
160     }
162     fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
163         debug_assert!(
164             false,
165             "How? We should only have parent-less dependencies here!"
166         );
167         true
168     }
170     fn collect_invalidations(
171         &mut self,
172         element: E,
173         self_invalidations: &mut InvalidationVector<'a>,
174         descendant_invalidations: &mut DescendantInvalidationLists<'a>,
175         _sibling_invalidations: &mut InvalidationVector<'a>,
176     ) -> bool {
177         // TODO(emilio): If the element is not a root element, and
178         // selector_list has any descendant combinator, we need to do extra work
179         // in order to handle properly things like:
180         //
181         //   <div id="a">
182         //     <div id="b">
183         //       <div id="c"></div>
184         //     </div>
185         //   </div>
186         //
187         // b.querySelector('#a div'); // Should return "c".
188         //
189         // For now, assert it's a root element.
190         debug_assert!(element.parent_element().is_none());
192         let target_vector = if self.matching_context.scope_element.is_some() {
193             &mut descendant_invalidations.dom_descendants
194         } else {
195             self_invalidations
196         };
198         for dependency in self.dependencies.iter() {
199             target_vector.push(Invalidation::new(
200                 dependency,
201                 self.matching_context.current_host.clone(),
202             ))
203         }
205         false
206     }
208     fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
209         &mut self.matching_context
210     }
212     fn should_process_descendants(&mut self, _: E) -> bool {
213         if Q::should_stop_after_first_match() {
214             return Q::is_empty(&self.results);
215         }
217         true
218     }
220     fn invalidated_self(&mut self, e: E) {
221         Q::append_element(self.results, e);
222     }
224     fn invalidated_sibling(&mut self, e: E, _of: E) {
225         Q::append_element(self.results, e);
226     }
228     fn recursion_limit_exceeded(&mut self, _e: E) {}
229     fn invalidated_descendants(&mut self, _e: E, _child: E) {}
232 fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)
233 where
234     E: TElement,
235     Q: SelectorQuery<E>,
236     F: FnMut(E) -> bool,
238     for node in root.dom_descendants() {
239         let element = match node.as_element() {
240             Some(e) => e,
241             None => continue,
242         };
244         if !filter(element) {
245             continue;
246         }
248         Q::append_element(results, element);
249         if Q::should_stop_after_first_match() {
250             return;
251         }
252     }
255 /// Returns whether a given element connected to `root` is descendant of `root`.
257 /// NOTE(emilio): if root == element, this returns false.
258 fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
259 where
260     E: TElement,
262     // Optimize for when the root is a document or a shadow root and the element
263     // is connected to that root.
264     if root.as_document().is_some() {
265         debug_assert!(element.as_node().is_in_document(), "Not connected?");
266         debug_assert_eq!(
267             root,
268             root.owner_doc().as_node(),
269             "Where did this element come from?",
270         );
271         return true;
272     }
274     if root.as_shadow_root().is_some() {
275         debug_assert_eq!(
276             element.containing_shadow().unwrap().as_node(),
277             root,
278             "Not connected?"
279         );
280         return true;
281     }
283     let mut current = element.as_node().parent_node();
284     while let Some(n) = current.take() {
285         if n == root {
286             return true;
287         }
289         current = n.parent_node();
290     }
291     false
294 /// Fast path for iterating over every element with a given id in the document
295 /// or shadow root that `root` is connected to.
296 fn fast_connected_elements_with_id<'a, N>(
297     root: N,
298     id: &AtomIdent,
299     case_sensitivity: CaseSensitivity,
300 ) -> Result<&'a [N::ConcreteElement], ()>
301 where
302     N: TNode + 'a,
304     if case_sensitivity != CaseSensitivity::CaseSensitive {
305         return Err(());
306     }
308     if root.is_in_document() {
309         return root.owner_doc().elements_with_id(id);
310     }
312     if let Some(shadow) = root.as_shadow_root() {
313         return shadow.elements_with_id(id);
314     }
316     if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
317         return shadow.elements_with_id(id);
318     }
320     Err(())
323 /// Collects elements with a given id under `root`, that pass `filter`.
324 fn collect_elements_with_id<E, Q, F>(
325     root: E::ConcreteNode,
326     id: &AtomIdent,
327     results: &mut Q::Output,
328     class_and_id_case_sensitivity: CaseSensitivity,
329     mut filter: F,
330 ) where
331     E: TElement,
332     Q: SelectorQuery<E>,
333     F: FnMut(E) -> bool,
335     let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) {
336         Ok(elements) => elements,
337         Err(()) => {
338             collect_all_elements::<E, Q, _>(root, results, |e| {
339                 e.has_id(id, class_and_id_case_sensitivity) && filter(e)
340             });
342             return;
343         },
344     };
346     for element in elements {
347         // If the element is not an actual descendant of the root, even though
348         // it's connected, we don't really care about it.
349         if !connected_element_is_descendant_of(*element, root) {
350             continue;
351         }
353         if !filter(*element) {
354             continue;
355         }
357         Q::append_element(results, *element);
358         if Q::should_stop_after_first_match() {
359             break;
360         }
361     }
364 fn has_attr<E>(element: E, local_name: &AtomIdent) -> bool
365 where
366     E: TElement,
368     let mut found = false;
369     element.each_attr_name(|name| found |= name == local_name);
370     found
373 #[inline(always)]
374 fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool
375 where
376     E: TElement,
378     let LocalName {
379         ref name,
380         ref lower_name,
381     } = *local_name;
383     let chosen_name = if name == lower_name || element.is_html_element_in_html_document() {
384         lower_name
385     } else {
386         name
387     };
389     element.local_name() == &**chosen_name
392 fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
393     let (name, name_lower) = match component {
394         Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),
395         Component::AttributeInNoNamespaceExists {
396             ref local_name,
397             ref local_name_lower,
398             ..
399         } => (local_name, local_name_lower),
400         Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower),
401         _ => return None,
402     };
403     if name != name_lower {
404         return None; // TODO: Maybe optimize this?
405     }
406     Some(name)
409 fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
410     use selectors::attr::AttrSelectorOperator;
411     Some(match component {
412         Component::ID(ref id) => id,
413         Component::AttributeInNoNamespace {
414             ref operator,
415             ref local_name,
416             ref value,
417             ..
418         } => {
419             if *local_name != local_name!("id") {
420                 return None;
421             }
422             if *operator != AttrSelectorOperator::Equal {
423                 return None;
424             }
425             AtomIdent::cast(&value.0)
426         },
427         _ => return None,
428     })
431 /// Fast paths for querySelector with a single simple selector.
432 fn query_selector_single_query<E, Q>(
433     root: E::ConcreteNode,
434     component: &Component<E::Impl>,
435     results: &mut Q::Output,
436     class_and_id_case_sensitivity: CaseSensitivity,
437 ) -> Result<(), ()>
438 where
439     E: TElement,
440     Q: SelectorQuery<E>,
442     match *component {
443         Component::ExplicitUniversalType => {
444             collect_all_elements::<E, Q, _>(root, results, |_| true)
445         },
446         Component::Class(ref class) => collect_all_elements::<E, Q, _>(root, results, |element| {
447             element.has_class(class, class_and_id_case_sensitivity)
448         }),
449         Component::LocalName(ref local_name) => {
450             collect_all_elements::<E, Q, _>(root, results, |element| {
451                 local_name_matches(element, local_name)
452             })
453         },
454         Component::AttributeInNoNamespaceExists {
455             ref local_name,
456             ref local_name_lower,
457         } => {
458             collect_all_elements::<E, Q, _>(root, results, |element| {
459                 element.has_attr_in_no_namespace(matching::select_name(&element, local_name, local_name_lower))
460             })
461         },
462         Component::AttributeInNoNamespace {
463             ref local_name,
464             ref value,
465             operator,
466             case_sensitivity,
467         } => {
468             let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>();
469             let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace);
470             collect_all_elements::<E, Q, _>(root, results, |element| {
471                 element.attr_matches(
472                     &namespace_constraint,
473                     local_name,
474                     &AttrSelectorOperation::WithValue {
475                         operator,
476                         case_sensitivity: matching::to_unconditional_case_sensitivity(case_sensitivity, &element),
477                         value,
478                     },
479                 )
480             })
481         },
482         ref other => {
483             let id = match get_id(other) {
484                 Some(id) => id,
485                 // TODO(emilio): More fast paths?
486                 None => return Err(()),
487             };
488             collect_elements_with_id::<E, Q, _>(
489                 root,
490                 id,
491                 results,
492                 class_and_id_case_sensitivity,
493                 |_| true,
494             );
495         },
496     }
498     Ok(())
501 enum SimpleFilter<'a> {
502     Class(&'a AtomIdent),
503     Attr(&'a AtomIdent),
504     LocalName(&'a LocalName<SelectorImpl>),
507 /// Fast paths for a given selector query.
509 /// When there's only one component, we go directly to
510 /// `query_selector_single_query`, otherwise, we try to optimize by looking just
511 /// at the subtrees rooted at ids in the selector, and otherwise we try to look
512 /// up by class name or local name in the rightmost compound.
514 /// FIXME(emilio, nbp): This may very well be a good candidate for code to be
515 /// replaced by HolyJit :)
516 fn query_selector_fast<E, Q>(
517     root: E::ConcreteNode,
518     selector_list: &SelectorList<E::Impl>,
519     results: &mut Q::Output,
520     matching_context: &mut MatchingContext<E::Impl>,
521 ) -> Result<(), ()>
522 where
523     E: TElement,
524     Q: SelectorQuery<E>,
526     // We need to return elements in document order, and reordering them
527     // afterwards is kinda silly.
528     if selector_list.0.len() > 1 {
529         return Err(());
530     }
532     let selector = &selector_list.0[0];
533     let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();
534     // Let's just care about the easy cases for now.
535     if selector.len() == 1 {
536         if query_selector_single_query::<E, Q>(
537             root,
538             selector.iter().next().unwrap(),
539             results,
540             class_and_id_case_sensitivity,
541         )
542         .is_ok()
543         {
544             return Ok(());
545         }
546     }
548     let mut iter = selector.iter();
549     let mut combinator: Option<Combinator> = None;
551     // We want to optimize some cases where there's no id involved whatsoever,
552     // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
553     // that.
554     let mut simple_filter = None;
556     'selector_loop: loop {
557         debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
559         'component_loop: for component in &mut iter {
560             match *component {
561                 Component::Class(ref class) => {
562                     if combinator.is_none() {
563                         simple_filter = Some(SimpleFilter::Class(class));
564                     }
565                 },
566                 Component::LocalName(ref local_name) => {
567                     if combinator.is_none() {
568                         // Prefer to look at class rather than local-name if
569                         // both are present.
570                         if let Some(SimpleFilter::Class(..)) = simple_filter {
571                             continue;
572                         }
573                         simple_filter = Some(SimpleFilter::LocalName(local_name));
574                     }
575                 },
576                 ref other => {
577                     if let Some(id) = get_id(other) {
578                         if combinator.is_none() {
579                             // In the rightmost compound, just find descendants of root that match
580                             // the selector list with that id.
581                             collect_elements_with_id::<E, Q, _>(
582                                 root,
583                                 id,
584                                 results,
585                                 class_and_id_case_sensitivity,
586                                 |e| {
587                                     matching::matches_selector_list(
588                                         selector_list,
589                                         &e,
590                                         matching_context,
591                                     )
592                                 },
593                             );
594                             return Ok(());
595                         }
597                         let elements = fast_connected_elements_with_id(
598                             root,
599                             id,
600                             class_and_id_case_sensitivity,
601                         )?;
602                         if elements.is_empty() {
603                             return Ok(());
604                         }
606                         // Results need to be in document order. Let's not bother
607                         // reordering or deduplicating nodes, which we would need to
608                         // do if one element with the given id were a descendant of
609                         // another element with that given id.
610                         if !Q::should_stop_after_first_match() && elements.len() > 1 {
611                             continue;
612                         }
614                         for element in elements {
615                             // If the element is not a descendant of the root, then
616                             // it may have descendants that match our selector that
617                             // _are_ descendants of the root, and other descendants
618                             // that match our selector that are _not_.
619                             //
620                             // So we can't just walk over the element's descendants
621                             // and match the selector against all of them, nor can
622                             // we skip looking at this element's descendants.
623                             //
624                             // Give up on trying to optimize based on this id and
625                             // keep walking our selector.
626                             if !connected_element_is_descendant_of(*element, root) {
627                                 continue 'component_loop;
628                             }
630                             query_selector_slow::<E, Q>(
631                                 element.as_node(),
632                                 selector_list,
633                                 results,
634                                 matching_context,
635                             );
637                             if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
638                                 break;
639                             }
640                         }
642                         return Ok(());
643                     }
644                     if combinator.is_none() && simple_filter.is_none() {
645                         if let Some(attr_name) = get_attr_name(other) {
646                             simple_filter = Some(SimpleFilter::Attr(attr_name));
647                         }
648                     }
649                 },
650             }
651         }
653         loop {
654             let next_combinator = match iter.next_sequence() {
655                 None => break 'selector_loop,
656                 Some(c) => c,
657             };
659             // We don't want to scan stuff affected by sibling combinators,
660             // given we scan the subtree of elements with a given id (and we
661             // don't want to care about scanning the siblings' subtrees).
662             if next_combinator.is_sibling() {
663                 // Advance to the next combinator.
664                 for _ in &mut iter {}
665                 continue;
666             }
668             combinator = Some(next_combinator);
669             break;
670         }
671     }
673     // We got here without finding any ID or such that we could handle. Try to
674     // use one of the simple filters.
675     let simple_filter = match simple_filter {
676         Some(f) => f,
677         None => return Err(()),
678     };
680     match simple_filter {
681         SimpleFilter::Class(ref class) => {
682             collect_all_elements::<E, Q, _>(root, results, |element| {
683                 element.has_class(class, class_and_id_case_sensitivity) &&
684                     matching::matches_selector_list(selector_list, &element, matching_context)
685             });
686         },
687         SimpleFilter::LocalName(ref local_name) => {
688             collect_all_elements::<E, Q, _>(root, results, |element| {
689                 local_name_matches(element, local_name) &&
690                     matching::matches_selector_list(selector_list, &element, matching_context)
691             });
692         },
693         SimpleFilter::Attr(ref local_name) => {
694             collect_all_elements::<E, Q, _>(root, results, |element| {
695                 has_attr(element, local_name) &&
696                     matching::matches_selector_list(selector_list, &element, matching_context)
697             });
698         },
699     }
701     Ok(())
704 // Slow path for a given selector query.
705 fn query_selector_slow<E, Q>(
706     root: E::ConcreteNode,
707     selector_list: &SelectorList<E::Impl>,
708     results: &mut Q::Output,
709     matching_context: &mut MatchingContext<E::Impl>,
710 ) where
711     E: TElement,
712     Q: SelectorQuery<E>,
714     collect_all_elements::<E, Q, _>(root, results, |element| {
715         matching::matches_selector_list(selector_list, &element, matching_context)
716     });
719 /// Whether the invalidation machinery should be used for this query.
720 #[derive(PartialEq)]
721 pub enum MayUseInvalidation {
722     /// We may use it if we deem it useful.
723     Yes,
724     /// Don't use it.
725     No,
728 /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
729 pub fn query_selector<E, Q>(
730     root: E::ConcreteNode,
731     selector_list: &SelectorList<E::Impl>,
732     results: &mut Q::Output,
733     may_use_invalidation: MayUseInvalidation,
734 ) where
735     E: TElement,
736     Q: SelectorQuery<E>,
738     use crate::invalidation::element::invalidator::TreeStyleInvalidator;
740     let mut selector_caches = SelectorCaches::default();
741     let quirks_mode = root.owner_doc().quirks_mode();
743     let mut matching_context = MatchingContext::new(
744         MatchingMode::Normal,
745         None,
746         &mut selector_caches,
747         quirks_mode,
748         NeedsSelectorFlags::No,
749         IgnoreNthChildForInvalidation::No,
750     );
751     let root_element = root.as_element();
752     matching_context.scope_element = root_element.map(|e| e.opaque());
753     matching_context.current_host = match root_element {
754         Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
755         None => root.as_shadow_root().map(|root| root.host().opaque()),
756     };
758     let fast_result =
759         query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);
761     if fast_result.is_ok() {
762         return;
763     }
765     // Slow path: Use the invalidation machinery if we're a root, and tree
766     // traversal otherwise.
767     //
768     // See the comment in collect_invalidations to see why only if we're a root.
769     //
770     // The invalidation mechanism is only useful in presence of combinators.
771     //
772     // We could do that check properly here, though checking the length of the
773     // selectors is a good heuristic.
774     //
775     // A selector with a combinator needs to have a length of at least 3: A
776     // simple selector, a combinator, and another simple selector.
777     let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes &&
778         selector_list.0.iter().any(|s| s.len() > 2);
780     if root_element.is_some() || !invalidation_may_be_useful {
781         query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
782     } else {
783         let dependencies = selector_list
784             .0
785             .iter()
786             .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
787             .collect::<SmallVec<[_; 5]>>();
788         let mut processor = QuerySelectorProcessor::<E, Q> {
789             results,
790             matching_context,
791             dependencies: &dependencies,
792         };
794         for node in root.dom_children() {
795             if let Some(e) = node.as_element() {
796                 TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)
797                     .invalidate();
798             }
799         }
800     }