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/. */
4
5 //! Generic implementations of some DOM APIs so they can be shared between Servo
6 //! and Gecko.
7
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::values::AtomIdent;
14 use selectors::attr::CaseSensitivity;
15 use selectors::matching::{self, MatchingContext, MatchingMode};
16 use selectors::parser::{Combinator, Component, LocalName, SelectorImpl};
17 use selectors::{Element, NthIndexCache, SelectorList};
18 use smallvec::SmallVec;
19
20 /// <https://dom.spec.whatwg.org/#dom-element-matches>
element_matches<E>( element: &E, selector_list: &SelectorList<E::Impl>, quirks_mode: QuirksMode, ) -> bool where E: Element,21 pub fn element_matches<E>(
22 element: &E,
23 selector_list: &SelectorList<E::Impl>,
24 quirks_mode: QuirksMode,
25 ) -> bool
26 where
27 E: Element,
28 {
29 let mut context = MatchingContext::new(MatchingMode::Normal, None, None, quirks_mode);
30 context.scope_element = Some(element.opaque());
31 context.current_host = element.containing_shadow_host().map(|e| e.opaque());
32 matching::matches_selector_list(selector_list, element, &mut context)
33 }
34
35 /// <https://dom.spec.whatwg.org/#dom-element-closest>
element_closest<E>( element: E, selector_list: &SelectorList<E::Impl>, quirks_mode: QuirksMode, ) -> Option<E> where E: Element,36 pub fn element_closest<E>(
37 element: E,
38 selector_list: &SelectorList<E::Impl>,
39 quirks_mode: QuirksMode,
40 ) -> Option<E>
41 where
42 E: Element,
43 {
44 let mut nth_index_cache = NthIndexCache::default();
45
46 let mut context = MatchingContext::new(
47 MatchingMode::Normal,
48 None,
49 Some(&mut nth_index_cache),
50 quirks_mode,
51 );
52 context.scope_element = Some(element.opaque());
53 context.current_host = element.containing_shadow_host().map(|e| e.opaque());
54
55 let mut current = Some(element);
56 while let Some(element) = current.take() {
57 if matching::matches_selector_list(selector_list, &element, &mut context) {
58 return Some(element);
59 }
60 current = element.parent_element();
61 }
62
63 return None;
64 }
65
66 /// A selector query abstraction, in order to be generic over QuerySelector and
67 /// QuerySelectorAll.
68 pub trait SelectorQuery<E: TElement> {
69 /// The output of the query.
70 type Output;
71
72 /// Whether the query should stop after the first element has been matched.
should_stop_after_first_match() -> bool73 fn should_stop_after_first_match() -> bool;
74
75 /// Append an element matching after the first query.
append_element(output: &mut Self::Output, element: E)76 fn append_element(output: &mut Self::Output, element: E);
77
78 /// Returns true if the output is empty.
is_empty(output: &Self::Output) -> bool79 fn is_empty(output: &Self::Output) -> bool;
80 }
81
82 /// The result of a querySelectorAll call.
83 pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;
84
85 /// A query for all the elements in a subtree.
86 pub struct QueryAll;
87
88 impl<E: TElement> SelectorQuery<E> for QueryAll {
89 type Output = QuerySelectorAllResult<E>;
90
should_stop_after_first_match() -> bool91 fn should_stop_after_first_match() -> bool {
92 false
93 }
94
append_element(output: &mut Self::Output, element: E)95 fn append_element(output: &mut Self::Output, element: E) {
96 output.push(element);
97 }
98
is_empty(output: &Self::Output) -> bool99 fn is_empty(output: &Self::Output) -> bool {
100 output.is_empty()
101 }
102 }
103
104 /// A query for the first in-tree match of all the elements in a subtree.
105 pub struct QueryFirst;
106
107 impl<E: TElement> SelectorQuery<E> for QueryFirst {
108 type Output = Option<E>;
109
should_stop_after_first_match() -> bool110 fn should_stop_after_first_match() -> bool {
111 true
112 }
113
append_element(output: &mut Self::Output, element: E)114 fn append_element(output: &mut Self::Output, element: E) {
115 if output.is_none() {
116 *output = Some(element)
117 }
118 }
119
is_empty(output: &Self::Output) -> bool120 fn is_empty(output: &Self::Output) -> bool {
121 output.is_none()
122 }
123 }
124
125 struct QuerySelectorProcessor<'a, E, Q>
126 where
127 E: TElement + 'a,
128 Q: SelectorQuery<E>,
129 Q::Output: 'a,
130 {
131 results: &'a mut Q::Output,
132 matching_context: MatchingContext<'a, E::Impl>,
133 dependencies: &'a [Dependency],
134 }
135
136 impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q>
137 where
138 E: TElement + 'a,
139 Q: SelectorQuery<E>,
140 Q::Output: 'a,
141 {
light_tree_only(&self) -> bool142 fn light_tree_only(&self) -> bool {
143 true
144 }
145
check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool146 fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool {
147 debug_assert!(
148 false,
149 "How? We should only have parent-less dependencies here!"
150 );
151 true
152 }
153
collect_invalidations( &mut self, element: E, self_invalidations: &mut InvalidationVector<'a>, descendant_invalidations: &mut DescendantInvalidationLists<'a>, _sibling_invalidations: &mut InvalidationVector<'a>, ) -> bool154 fn collect_invalidations(
155 &mut self,
156 element: E,
157 self_invalidations: &mut InvalidationVector<'a>,
158 descendant_invalidations: &mut DescendantInvalidationLists<'a>,
159 _sibling_invalidations: &mut InvalidationVector<'a>,
160 ) -> bool {
161 // TODO(emilio): If the element is not a root element, and
162 // selector_list has any descendant combinator, we need to do extra work
163 // in order to handle properly things like:
164 //
165 // <div id="a">
166 // <div id="b">
167 // <div id="c"></div>
168 // </div>
169 // </div>
170 //
171 // b.querySelector('#a div'); // Should return "c".
172 //
173 // For now, assert it's a root element.
174 debug_assert!(element.parent_element().is_none());
175
176 let target_vector = if self.matching_context.scope_element.is_some() {
177 &mut descendant_invalidations.dom_descendants
178 } else {
179 self_invalidations
180 };
181
182 for dependency in self.dependencies.iter() {
183 target_vector.push(Invalidation::new(
184 dependency,
185 self.matching_context.current_host.clone(),
186 ))
187 }
188
189 false
190 }
191
matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl>192 fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
193 &mut self.matching_context
194 }
195
should_process_descendants(&mut self, _: E) -> bool196 fn should_process_descendants(&mut self, _: E) -> bool {
197 if Q::should_stop_after_first_match() {
198 return Q::is_empty(&self.results);
199 }
200
201 true
202 }
203
invalidated_self(&mut self, e: E)204 fn invalidated_self(&mut self, e: E) {
205 Q::append_element(self.results, e);
206 }
207
recursion_limit_exceeded(&mut self, _e: E)208 fn recursion_limit_exceeded(&mut self, _e: E) {}
invalidated_descendants(&mut self, _e: E, _child: E)209 fn invalidated_descendants(&mut self, _e: E, _child: E) {}
210 }
211
collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F) where E: TElement, Q: SelectorQuery<E>, F: FnMut(E) -> bool,212 fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)
213 where
214 E: TElement,
215 Q: SelectorQuery<E>,
216 F: FnMut(E) -> bool,
217 {
218 for node in root.dom_descendants() {
219 let element = match node.as_element() {
220 Some(e) => e,
221 None => continue,
222 };
223
224 if !filter(element) {
225 continue;
226 }
227
228 Q::append_element(results, element);
229 if Q::should_stop_after_first_match() {
230 return;
231 }
232 }
233 }
234
235 /// Returns whether a given element connected to `root` is descendant of `root`.
236 ///
237 /// NOTE(emilio): if root == element, this returns false.
connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool where E: TElement,238 fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool
239 where
240 E: TElement,
241 {
242 // Optimize for when the root is a document or a shadow root and the element
243 // is connected to that root.
244 if root.as_document().is_some() {
245 debug_assert!(element.as_node().is_in_document(), "Not connected?");
246 debug_assert_eq!(
247 root,
248 root.owner_doc().as_node(),
249 "Where did this element come from?",
250 );
251 return true;
252 }
253
254 if root.as_shadow_root().is_some() {
255 debug_assert_eq!(
256 element.containing_shadow().unwrap().as_node(),
257 root,
258 "Not connected?"
259 );
260 return true;
261 }
262
263 let mut current = element.as_node().parent_node();
264 while let Some(n) = current.take() {
265 if n == root {
266 return true;
267 }
268
269 current = n.parent_node();
270 }
271 false
272 }
273
274 /// Fast path for iterating over every element with a given id in the document
275 /// or shadow root that `root` is connected to.
fast_connected_elements_with_id<'a, N>( root: N, id: &AtomIdent, quirks_mode: QuirksMode, ) -> Result<&'a [N::ConcreteElement], ()> where N: TNode + 'a,276 fn fast_connected_elements_with_id<'a, N>(
277 root: N,
278 id: &AtomIdent,
279 quirks_mode: QuirksMode,
280 ) -> Result<&'a [N::ConcreteElement], ()>
281 where
282 N: TNode + 'a,
283 {
284 let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
285 if case_sensitivity != CaseSensitivity::CaseSensitive {
286 return Err(());
287 }
288
289 if root.is_in_document() {
290 return root.owner_doc().elements_with_id(id);
291 }
292
293 if let Some(shadow) = root.as_shadow_root() {
294 return shadow.elements_with_id(id);
295 }
296
297 if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {
298 return shadow.elements_with_id(id);
299 }
300
301 Err(())
302 }
303
304 /// Collects elements with a given id under `root`, that pass `filter`.
collect_elements_with_id<E, Q, F>( root: E::ConcreteNode, id: &AtomIdent, results: &mut Q::Output, quirks_mode: QuirksMode, mut filter: F, ) where E: TElement, Q: SelectorQuery<E>, F: FnMut(E) -> bool,305 fn collect_elements_with_id<E, Q, F>(
306 root: E::ConcreteNode,
307 id: &AtomIdent,
308 results: &mut Q::Output,
309 quirks_mode: QuirksMode,
310 mut filter: F,
311 ) where
312 E: TElement,
313 Q: SelectorQuery<E>,
314 F: FnMut(E) -> bool,
315 {
316 let elements = match fast_connected_elements_with_id(root, id, quirks_mode) {
317 Ok(elements) => elements,
318 Err(()) => {
319 let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
320
321 collect_all_elements::<E, Q, _>(root, results, |e| {
322 e.has_id(id, case_sensitivity) && filter(e)
323 });
324
325 return;
326 },
327 };
328
329 for element in elements {
330 // If the element is not an actual descendant of the root, even though
331 // it's connected, we don't really care about it.
332 if !connected_element_is_descendant_of(*element, root) {
333 continue;
334 }
335
336 if !filter(*element) {
337 continue;
338 }
339
340 Q::append_element(results, *element);
341 if Q::should_stop_after_first_match() {
342 break;
343 }
344 }
345 }
346
347 #[inline(always)]
local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool where E: TElement,348 fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool
349 where
350 E: TElement,
351 {
352 let LocalName {
353 ref name,
354 ref lower_name,
355 } = *local_name;
356
357 let chosen_name = if element.is_html_element_in_html_document() {
358 lower_name
359 } else {
360 name
361 };
362
363 element.local_name() == &**chosen_name
364 }
365
366 /// Fast paths for querySelector with a single simple selector.
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>,367 fn query_selector_single_query<E, Q>(
368 root: E::ConcreteNode,
369 component: &Component<E::Impl>,
370 results: &mut Q::Output,
371 quirks_mode: QuirksMode,
372 ) -> Result<(), ()>
373 where
374 E: TElement,
375 Q: SelectorQuery<E>,
376 {
377 match *component {
378 Component::ExplicitUniversalType => {
379 collect_all_elements::<E, Q, _>(root, results, |_| true)
380 },
381 Component::ID(ref id) => {
382 collect_elements_with_id::<E, Q, _>(root, id, results, quirks_mode, |_| true);
383 },
384 Component::Class(ref class) => {
385 let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
386 collect_all_elements::<E, Q, _>(root, results, |element| {
387 element.has_class(class, case_sensitivity)
388 })
389 },
390 Component::LocalName(ref local_name) => {
391 collect_all_elements::<E, Q, _>(root, results, |element| {
392 local_name_matches(element, local_name)
393 })
394 },
395 // TODO(emilio): More fast paths?
396 _ => return Err(()),
397 }
398
399 Ok(())
400 }
401
402 enum SimpleFilter<'a, Impl: SelectorImpl> {
403 Class(&'a AtomIdent),
404 LocalName(&'a LocalName<Impl>),
405 }
406
407 /// Fast paths for a given selector query.
408 ///
409 /// When there's only one component, we go directly to
410 /// `query_selector_single_query`, otherwise, we try to optimize by looking just
411 /// at the subtrees rooted at ids in the selector, and otherwise we try to look
412 /// up by class name or local name in the rightmost compound.
413 ///
414 /// FIXME(emilio, nbp): This may very well be a good candidate for code to be
415 /// replaced by HolyJit :)
query_selector_fast<E, Q>( root: E::ConcreteNode, selector_list: &SelectorList<E::Impl>, results: &mut Q::Output, matching_context: &mut MatchingContext<E::Impl>, ) -> Result<(), ()> where E: TElement, Q: SelectorQuery<E>,416 fn query_selector_fast<E, Q>(
417 root: E::ConcreteNode,
418 selector_list: &SelectorList<E::Impl>,
419 results: &mut Q::Output,
420 matching_context: &mut MatchingContext<E::Impl>,
421 ) -> Result<(), ()>
422 where
423 E: TElement,
424 Q: SelectorQuery<E>,
425 {
426 // We need to return elements in document order, and reordering them
427 // afterwards is kinda silly.
428 if selector_list.0.len() > 1 {
429 return Err(());
430 }
431
432 let selector = &selector_list.0[0];
433 let quirks_mode = matching_context.quirks_mode();
434
435 // Let's just care about the easy cases for now.
436 if selector.len() == 1 {
437 return query_selector_single_query::<E, Q>(
438 root,
439 selector.iter().next().unwrap(),
440 results,
441 quirks_mode,
442 );
443 }
444
445 let mut iter = selector.iter();
446 let mut combinator: Option<Combinator> = None;
447
448 // We want to optimize some cases where there's no id involved whatsoever,
449 // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
450 // that.
451 let mut simple_filter = None;
452
453 'selector_loop: loop {
454 debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
455
456 'component_loop: for component in &mut iter {
457 match *component {
458 Component::ID(ref id) => {
459 if combinator.is_none() {
460 // In the rightmost compound, just find descendants of
461 // root that match the selector list with that id.
462 collect_elements_with_id::<E, Q, _>(root, id, results, quirks_mode, |e| {
463 matching::matches_selector_list(selector_list, &e, matching_context)
464 });
465
466 return Ok(());
467 }
468
469 let elements = fast_connected_elements_with_id(root, id, quirks_mode)?;
470 if elements.is_empty() {
471 return Ok(());
472 }
473
474 // Results need to be in document order. Let's not bother
475 // reordering or deduplicating nodes, which we would need to
476 // do if one element with the given id were a descendant of
477 // another element with that given id.
478 if !Q::should_stop_after_first_match() && elements.len() > 1 {
479 continue;
480 }
481
482 for element in elements {
483 // If the element is not a descendant of the root, then
484 // it may have descendants that match our selector that
485 // _are_ descendants of the root, and other descendants
486 // that match our selector that are _not_.
487 //
488 // So we can't just walk over the element's descendants
489 // and match the selector against all of them, nor can
490 // we skip looking at this element's descendants.
491 //
492 // Give up on trying to optimize based on this id and
493 // keep walking our selector.
494 if !connected_element_is_descendant_of(*element, root) {
495 continue 'component_loop;
496 }
497
498 query_selector_slow::<E, Q>(
499 element.as_node(),
500 selector_list,
501 results,
502 matching_context,
503 );
504
505 if Q::should_stop_after_first_match() && !Q::is_empty(&results) {
506 break;
507 }
508 }
509
510 return Ok(());
511 },
512 Component::Class(ref class) => {
513 if combinator.is_none() {
514 simple_filter = Some(SimpleFilter::Class(class));
515 }
516 },
517 Component::LocalName(ref local_name) => {
518 if combinator.is_none() {
519 // Prefer to look at class rather than local-name if
520 // both are present.
521 if let Some(SimpleFilter::Class(..)) = simple_filter {
522 continue;
523 }
524 simple_filter = Some(SimpleFilter::LocalName(local_name));
525 }
526 },
527 _ => {},
528 }
529 }
530
531 loop {
532 let next_combinator = match iter.next_sequence() {
533 None => break 'selector_loop,
534 Some(c) => c,
535 };
536
537 // We don't want to scan stuff affected by sibling combinators,
538 // given we scan the subtree of elements with a given id (and we
539 // don't want to care about scanning the siblings' subtrees).
540 if next_combinator.is_sibling() {
541 // Advance to the next combinator.
542 for _ in &mut iter {}
543 continue;
544 }
545
546 combinator = Some(next_combinator);
547 break;
548 }
549 }
550
551 // We got here without finding any ID or such that we could handle. Try to
552 // use one of the simple filters.
553 let simple_filter = match simple_filter {
554 Some(f) => f,
555 None => return Err(()),
556 };
557
558 match simple_filter {
559 SimpleFilter::Class(ref class) => {
560 let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
561 collect_all_elements::<E, Q, _>(root, results, |element| {
562 element.has_class(class, case_sensitivity) &&
563 matching::matches_selector_list(selector_list, &element, matching_context)
564 });
565 },
566 SimpleFilter::LocalName(ref local_name) => {
567 collect_all_elements::<E, Q, _>(root, results, |element| {
568 local_name_matches(element, local_name) &&
569 matching::matches_selector_list(selector_list, &element, matching_context)
570 });
571 },
572 }
573
574 Ok(())
575 }
576
577 // Slow path for a given selector query.
query_selector_slow<E, Q>( root: E::ConcreteNode, selector_list: &SelectorList<E::Impl>, results: &mut Q::Output, matching_context: &mut MatchingContext<E::Impl>, ) where E: TElement, Q: SelectorQuery<E>,578 fn query_selector_slow<E, Q>(
579 root: E::ConcreteNode,
580 selector_list: &SelectorList<E::Impl>,
581 results: &mut Q::Output,
582 matching_context: &mut MatchingContext<E::Impl>,
583 ) where
584 E: TElement,
585 Q: SelectorQuery<E>,
586 {
587 collect_all_elements::<E, Q, _>(root, results, |element| {
588 matching::matches_selector_list(selector_list, &element, matching_context)
589 });
590 }
591
592 /// Whether the invalidation machinery should be used for this query.
593 #[derive(PartialEq)]
594 pub enum MayUseInvalidation {
595 /// We may use it if we deem it useful.
596 Yes,
597 /// Don't use it.
598 No,
599 }
600
601 /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
query_selector<E, Q>( root: E::ConcreteNode, selector_list: &SelectorList<E::Impl>, results: &mut Q::Output, may_use_invalidation: MayUseInvalidation, ) where E: TElement, Q: SelectorQuery<E>,602 pub fn query_selector<E, Q>(
603 root: E::ConcreteNode,
604 selector_list: &SelectorList<E::Impl>,
605 results: &mut Q::Output,
606 may_use_invalidation: MayUseInvalidation,
607 ) where
608 E: TElement,
609 Q: SelectorQuery<E>,
610 {
611 use crate::invalidation::element::invalidator::TreeStyleInvalidator;
612
613 let quirks_mode = root.owner_doc().quirks_mode();
614
615 let mut nth_index_cache = NthIndexCache::default();
616 let mut matching_context = MatchingContext::new(
617 MatchingMode::Normal,
618 None,
619 Some(&mut nth_index_cache),
620 quirks_mode,
621 );
622
623 let root_element = root.as_element();
624 matching_context.scope_element = root_element.map(|e| e.opaque());
625 matching_context.current_host = match root_element {
626 Some(root) => root.containing_shadow_host().map(|host| host.opaque()),
627 None => root.as_shadow_root().map(|root| root.host().opaque()),
628 };
629
630 let fast_result =
631 query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);
632
633 if fast_result.is_ok() {
634 return;
635 }
636
637 // Slow path: Use the invalidation machinery if we're a root, and tree
638 // traversal otherwise.
639 //
640 // See the comment in collect_invalidations to see why only if we're a root.
641 //
642 // The invalidation mechanism is only useful in presence of combinators.
643 //
644 // We could do that check properly here, though checking the length of the
645 // selectors is a good heuristic.
646 //
647 // A selector with a combinator needs to have a length of at least 3: A
648 // simple selector, a combinator, and another simple selector.
649 let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes &&
650 selector_list.0.iter().any(|s| s.len() > 2);
651
652 if root_element.is_some() || !invalidation_may_be_useful {
653 query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);
654 } else {
655 let dependencies = selector_list
656 .0
657 .iter()
658 .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))
659 .collect::<SmallVec<[_; 5]>>();
660 let mut processor = QuerySelectorProcessor::<E, Q> {
661 results,
662 matching_context,
663 dependencies: &dependencies,
664 };
665
666 for node in root.dom_children() {
667 if let Some(e) = node.as_element() {
668 TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)
669 .invalidate();
670 }
671 }
672 }
673 }
674