dom_apis.rs (30153B)
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::{ 12 DescendantInvalidationLists, Invalidation, SiblingTraversalMap, 13 }; 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::attr::{AttrSelectorOperation, NamespaceConstraint}; 19 use selectors::matching::{ 20 self, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, 21 SelectorCaches, 22 }; 23 use selectors::parser::{Combinator, Component, LocalName}; 24 use selectors::{Element, OpaqueElement, SelectorList}; 25 use smallvec::SmallVec; 26 27 /// <https://dom.spec.whatwg.org/#dom-element-matches> 28 pub fn element_matches<E>( 29 element: &E, 30 selector_list: &SelectorList<E::Impl>, 31 quirks_mode: QuirksMode, 32 ) -> bool 33 where 34 E: Element, 35 { 36 let mut selector_caches = SelectorCaches::default(); 37 38 let mut context = MatchingContext::new( 39 MatchingMode::Normal, 40 None, 41 &mut selector_caches, 42 quirks_mode, 43 NeedsSelectorFlags::No, 44 MatchingForInvalidation::No, 45 ); 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) 49 } 50 51 /// <https://dom.spec.whatwg.org/#dom-element-closest> 52 pub fn element_closest<E>( 53 element: E, 54 selector_list: &SelectorList<E::Impl>, 55 quirks_mode: QuirksMode, 56 ) -> Option<E> 57 where 58 E: Element, 59 { 60 let mut selector_caches = SelectorCaches::default(); 61 62 let mut context = MatchingContext::new( 63 MatchingMode::Normal, 64 None, 65 &mut selector_caches, 66 quirks_mode, 67 NeedsSelectorFlags::No, 68 MatchingForInvalidation::No, 69 ); 70 context.scope_element = Some(element.opaque()); 71 context.current_host = element.containing_shadow_host().map(|e| e.opaque()); 72 73 let mut current = Some(element); 74 while let Some(element) = current.take() { 75 if matching::matches_selector_list(selector_list, &element, &mut context) { 76 return Some(element); 77 } 78 current = element.parent_element(); 79 } 80 81 return None; 82 } 83 84 /// A selector query abstraction, in order to be generic over QuerySelector and 85 /// QuerySelectorAll. 86 pub trait SelectorQuery<E: TElement> { 87 /// The output of the query. 88 type Output; 89 90 /// Whether the query should stop after the first element has been matched. 91 fn should_stop_after_first_match() -> bool; 92 93 /// Append an element matching after the first query. 94 fn append_element(output: &mut Self::Output, element: E); 95 96 /// Returns true if the output is empty. 97 fn is_empty(output: &Self::Output) -> bool; 98 } 99 100 /// The result of a querySelectorAll call. 101 pub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>; 102 103 /// A query for all the elements in a subtree. 104 pub struct QueryAll; 105 106 impl<E: TElement> SelectorQuery<E> for QueryAll { 107 type Output = QuerySelectorAllResult<E>; 108 109 fn should_stop_after_first_match() -> bool { 110 false 111 } 112 113 fn append_element(output: &mut Self::Output, element: E) { 114 output.push(element); 115 } 116 117 fn is_empty(output: &Self::Output) -> bool { 118 output.is_empty() 119 } 120 } 121 122 /// A query for the first in-tree match of all the elements in a subtree. 123 pub struct QueryFirst; 124 125 impl<E: TElement> SelectorQuery<E> for QueryFirst { 126 type Output = Option<E>; 127 128 fn should_stop_after_first_match() -> bool { 129 true 130 } 131 132 fn append_element(output: &mut Self::Output, element: E) { 133 if output.is_none() { 134 *output = Some(element) 135 } 136 } 137 138 fn is_empty(output: &Self::Output) -> bool { 139 output.is_none() 140 } 141 } 142 143 struct QuerySelectorProcessor<'a, 'b, E, Q> 144 where 145 E: TElement + 'a, 146 Q: SelectorQuery<E>, 147 Q::Output: 'a, 148 { 149 results: &'a mut Q::Output, 150 matching_context: MatchingContext<'b, E::Impl>, 151 traversal_map: SiblingTraversalMap<E>, 152 dependencies: &'a [Dependency], 153 } 154 155 impl<'a, 'b, E, Q> InvalidationProcessor<'a, 'b, E> for QuerySelectorProcessor<'a, 'b, E, Q> 156 where 157 E: TElement + 'a, 158 Q: SelectorQuery<E>, 159 Q::Output: 'a, 160 { 161 fn light_tree_only(&self) -> bool { 162 true 163 } 164 165 fn check_outer_dependency(&mut self, _: &Dependency, _: E, _: Option<OpaqueElement>) -> bool { 166 debug_assert!( 167 false, 168 "How? We should only have parent-less dependencies here!" 169 ); 170 true 171 } 172 173 fn collect_invalidations( 174 &mut self, 175 element: E, 176 self_invalidations: &mut InvalidationVector<'a>, 177 descendant_invalidations: &mut DescendantInvalidationLists<'a>, 178 _sibling_invalidations: &mut InvalidationVector<'a>, 179 ) -> bool { 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: 183 // 184 // <div id="a"> 185 // <div id="b"> 186 // <div id="c"></div> 187 // </div> 188 // </div> 189 // 190 // b.querySelector('#a div'); // Should return "c". 191 // 192 // For now, assert it's a root element. 193 debug_assert!(element.parent_element().is_none()); 194 195 let target_vector = if self.matching_context.scope_element.is_some() { 196 &mut descendant_invalidations.dom_descendants 197 } else { 198 self_invalidations 199 }; 200 201 for dependency in self.dependencies.iter() { 202 target_vector.push(Invalidation::new( 203 dependency, 204 self.matching_context.current_host.clone(), 205 self.matching_context.scope_element.clone(), 206 )) 207 } 208 209 false 210 } 211 212 fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> { 213 &mut self.matching_context 214 } 215 216 fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> { 217 &self.traversal_map 218 } 219 220 fn should_process_descendants(&mut self, _: E) -> bool { 221 if Q::should_stop_after_first_match() { 222 return Q::is_empty(&self.results); 223 } 224 225 true 226 } 227 228 fn invalidated_self(&mut self, e: E) { 229 Q::append_element(self.results, e); 230 } 231 232 fn invalidated_sibling(&mut self, e: E, _of: E) { 233 Q::append_element(self.results, e); 234 } 235 236 fn recursion_limit_exceeded(&mut self, _e: E) {} 237 fn invalidated_descendants(&mut self, _e: E, _child: E) {} 238 } 239 240 enum Operation { 241 Reject, 242 Accept, 243 RejectSkippingChildren, 244 } 245 246 impl From<bool> for Operation { 247 #[inline(always)] 248 fn from(matches: bool) -> Self { 249 if matches { 250 Operation::Accept 251 } else { 252 Operation::Reject 253 } 254 } 255 } 256 257 fn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F) 258 where 259 E: TElement, 260 Q: SelectorQuery<E>, 261 F: FnMut(E) -> Operation, 262 { 263 let mut iter = root.dom_descendants(); 264 let mut cur = iter.next(); 265 while let Some(node) = cur { 266 let element = match node.as_element() { 267 Some(e) => e, 268 None => { 269 cur = iter.next(); 270 continue; 271 }, 272 }; 273 match filter(element) { 274 // Element matches - add to results and continue traversing its children. 275 Operation::Accept => { 276 Q::append_element(results, element); 277 if Q::should_stop_after_first_match() { 278 return; 279 } 280 }, 281 // Element doesn't match - skip it but continue traversing its children. 282 Operation::Reject => {}, 283 // Element doesn't match and skip entire subtree. 284 Operation::RejectSkippingChildren => { 285 cur = iter.next_skipping_children(); 286 continue; 287 }, 288 } 289 cur = iter.next(); 290 } 291 } 292 293 /// Returns whether a given element connected to `root` is descendant of `root`. 294 /// 295 /// NOTE(emilio): if root == element, this returns false. 296 fn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool 297 where 298 E: TElement, 299 { 300 // Optimize for when the root is a document or a shadow root and the element 301 // is connected to that root. 302 if root.as_document().is_some() { 303 debug_assert!(element.as_node().is_in_document(), "Not connected?"); 304 debug_assert_eq!( 305 root, 306 root.owner_doc().as_node(), 307 "Where did this element come from?", 308 ); 309 return true; 310 } 311 312 if root.as_shadow_root().is_some() { 313 debug_assert_eq!( 314 element.containing_shadow().unwrap().as_node(), 315 root, 316 "Not connected?" 317 ); 318 return true; 319 } 320 321 let mut current = element.as_node().parent_node(); 322 while let Some(n) = current.take() { 323 if n == root { 324 return true; 325 } 326 327 current = n.parent_node(); 328 } 329 false 330 } 331 332 /// Fast path for iterating over every element with a given id in the document 333 /// or shadow root that `root` is connected to. 334 fn fast_connected_elements_with_id<'a, N>( 335 root: N, 336 id: &AtomIdent, 337 case_sensitivity: CaseSensitivity, 338 ) -> Result<&'a [N::ConcreteElement], ()> 339 where 340 N: TNode + 'a, 341 { 342 if case_sensitivity != CaseSensitivity::CaseSensitive { 343 return Err(()); 344 } 345 346 if root.is_in_document() { 347 return root.owner_doc().elements_with_id(id); 348 } 349 350 if let Some(shadow) = root.as_shadow_root() { 351 return shadow.elements_with_id(id); 352 } 353 354 if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) { 355 return shadow.elements_with_id(id); 356 } 357 358 Err(()) 359 } 360 361 /// Collects elements with a given id under `root`, that pass `filter`. 362 fn collect_elements_with_id<E, Q, F>( 363 root: E::ConcreteNode, 364 id: &AtomIdent, 365 results: &mut Q::Output, 366 class_and_id_case_sensitivity: CaseSensitivity, 367 mut filter: F, 368 ) where 369 E: TElement, 370 Q: SelectorQuery<E>, 371 F: FnMut(E) -> bool, 372 { 373 let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) { 374 Ok(elements) => elements, 375 Err(()) => { 376 collect_all_elements::<E, Q, _>(root, results, |e| { 377 Operation::from(e.has_id(id, class_and_id_case_sensitivity) && filter(e)) 378 }); 379 380 return; 381 }, 382 }; 383 384 for element in elements { 385 // If the element is not an actual descendant of the root, even though 386 // it's connected, we don't really care about it. 387 if !connected_element_is_descendant_of(*element, root) { 388 continue; 389 } 390 391 if !filter(*element) { 392 continue; 393 } 394 395 Q::append_element(results, *element); 396 if Q::should_stop_after_first_match() { 397 break; 398 } 399 } 400 } 401 402 fn has_attr<E>(element: E, local_name: &crate::LocalName) -> bool 403 where 404 E: TElement, 405 { 406 let mut found = false; 407 element.each_attr_name(|name| found |= name == local_name); 408 found 409 } 410 411 #[inline(always)] 412 fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool 413 where 414 E: TElement, 415 { 416 let LocalName { 417 ref name, 418 ref lower_name, 419 } = *local_name; 420 421 let chosen_name = if name == lower_name || element.is_html_element_in_html_document() { 422 lower_name 423 } else { 424 name 425 }; 426 427 element.local_name() == &**chosen_name 428 } 429 430 fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> { 431 let (name, name_lower) = match component { 432 Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name), 433 Component::AttributeInNoNamespaceExists { 434 ref local_name, 435 ref local_name_lower, 436 .. 437 } => (local_name, local_name_lower), 438 Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower), 439 _ => return None, 440 }; 441 if name != name_lower { 442 return None; // TODO: Maybe optimize this? 443 } 444 Some(name) 445 } 446 447 fn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> { 448 use selectors::attr::AttrSelectorOperator; 449 Some(match component { 450 Component::ID(ref id) => id, 451 Component::AttributeInNoNamespace { 452 ref operator, 453 ref local_name, 454 ref value, 455 .. 456 } => { 457 if *local_name != local_name!("id") { 458 return None; 459 } 460 if *operator != AttrSelectorOperator::Equal { 461 return None; 462 } 463 AtomIdent::cast(&value.0) 464 }, 465 _ => return None, 466 }) 467 } 468 469 /// Fast paths for querySelector with a single simple selector. 470 fn query_selector_single_query<E, Q>( 471 root: E::ConcreteNode, 472 component: &Component<E::Impl>, 473 results: &mut Q::Output, 474 class_and_id_case_sensitivity: CaseSensitivity, 475 ) -> Result<(), ()> 476 where 477 E: TElement, 478 Q: SelectorQuery<E>, 479 { 480 match *component { 481 Component::ExplicitUniversalType => { 482 collect_all_elements::<E, Q, _>(root, results, |_| Operation::Accept) 483 }, 484 Component::Class(ref class) => { 485 // Bloom filter can only be used when case sensitive. 486 let bloom_hash = if class_and_id_case_sensitivity == CaseSensitivity::CaseSensitive { 487 Some(E::hash_for_bloom_filter(class.0.get_hash())) 488 } else { 489 None 490 }; 491 492 collect_all_elements::<E, Q, _>(root, results, |element| { 493 if bloom_hash.is_some_and(|hash| !element.bloom_may_have_hash(hash)) { 494 return Operation::RejectSkippingChildren; 495 } 496 Operation::from(element.has_class(class, class_and_id_case_sensitivity)) 497 }); 498 }, 499 Component::LocalName(ref local_name) => { 500 collect_all_elements::<E, Q, _>(root, results, |element| { 501 Operation::from(local_name_matches(element, local_name)) 502 }) 503 }, 504 Component::AttributeInNoNamespaceExists { 505 ref local_name, 506 ref local_name_lower, 507 } => { 508 // For HTML elements: C++ hashes lowercase 509 // For XUL/SVG/MathML elements: C++ hashes original case 510 let hash_original = E::hash_for_bloom_filter(local_name.0.get_hash()); 511 let hash_lower = if local_name.0 == local_name_lower.0 { 512 hash_original 513 } else { 514 E::hash_for_bloom_filter(local_name_lower.0.get_hash()) 515 }; 516 517 collect_all_elements::<E, Q, _>(root, results, |element| { 518 // Check bloom filter first 519 let bloom_found_hash = if hash_original == hash_lower 520 || !element.as_node().owner_doc().is_html_document() 521 { 522 element.bloom_may_have_hash(hash_original) 523 } else if element.is_html_element_in_html_document() { 524 // HTML elements store lowercase hashes 525 element.bloom_may_have_hash(hash_lower) 526 } else { 527 // Non-HTML elements in HTML documents might have HTML descendants 528 // with lowercase-only hashes, so check both 529 element.bloom_may_have_hash(hash_original) 530 || element.bloom_may_have_hash(hash_lower) 531 }; 532 533 if !bloom_found_hash { 534 return Operation::RejectSkippingChildren; 535 } 536 537 Operation::from(element.has_attr_in_no_namespace(matching::select_name( 538 &element, 539 local_name, 540 local_name_lower, 541 ))) 542 }); 543 }, 544 Component::AttributeInNoNamespace { 545 ref local_name, 546 ref value, 547 operator, 548 case_sensitivity, 549 } => { 550 let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>(); 551 let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace); 552 553 // Only use bloom filter to check for attribute name existence. 554 let bloom_hash = E::hash_for_bloom_filter(local_name.0.get_hash()); 555 556 collect_all_elements::<E, Q, _>(root, results, |element| { 557 if !element.bloom_may_have_hash(bloom_hash) { 558 return Operation::RejectSkippingChildren; 559 } 560 Operation::from(element.attr_matches( 561 &namespace_constraint, 562 local_name, 563 &AttrSelectorOperation::WithValue { 564 operator, 565 case_sensitivity: matching::to_unconditional_case_sensitivity( 566 case_sensitivity, 567 &element, 568 ), 569 value, 570 }, 571 )) 572 }); 573 }, 574 ref other => { 575 let id = match get_id(other) { 576 Some(id) => id, 577 // TODO(emilio): More fast paths? 578 None => return Err(()), 579 }; 580 collect_elements_with_id::<E, Q, _>( 581 root, 582 id, 583 results, 584 class_and_id_case_sensitivity, 585 |_| true, 586 ); 587 }, 588 } 589 590 Ok(()) 591 } 592 593 enum SimpleFilter<'a> { 594 Class(&'a AtomIdent), 595 Attr(&'a crate::LocalName), 596 LocalName(&'a LocalName<SelectorImpl>), 597 } 598 599 /// Fast paths for a given selector query. 600 /// 601 /// When there's only one component, we go directly to 602 /// `query_selector_single_query`, otherwise, we try to optimize by looking just 603 /// at the subtrees rooted at ids in the selector, and otherwise we try to look 604 /// up by class name or local name in the rightmost compound. 605 /// 606 /// FIXME(emilio, nbp): This may very well be a good candidate for code to be 607 /// replaced by HolyJit :) 608 fn query_selector_fast<E, Q>( 609 root: E::ConcreteNode, 610 selector_list: &SelectorList<E::Impl>, 611 results: &mut Q::Output, 612 matching_context: &mut MatchingContext<E::Impl>, 613 ) -> Result<(), ()> 614 where 615 E: TElement, 616 Q: SelectorQuery<E>, 617 { 618 // We need to return elements in document order, and reordering them 619 // afterwards is kinda silly. 620 if selector_list.len() > 1 { 621 return Err(()); 622 } 623 624 let selector = &selector_list.slice()[0]; 625 let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity(); 626 // Let's just care about the easy cases for now. 627 if selector.len() == 1 { 628 if query_selector_single_query::<E, Q>( 629 root, 630 selector.iter().next().unwrap(), 631 results, 632 class_and_id_case_sensitivity, 633 ) 634 .is_ok() 635 { 636 return Ok(()); 637 } 638 } 639 640 let mut iter = selector.iter(); 641 let mut combinator: Option<Combinator> = None; 642 643 // We want to optimize some cases where there's no id involved whatsoever, 644 // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of 645 // that. 646 let mut simple_filter = None; 647 648 'selector_loop: loop { 649 debug_assert!(combinator.map_or(true, |c| !c.is_sibling())); 650 651 'component_loop: for component in &mut iter { 652 match *component { 653 Component::Class(ref class) => { 654 if combinator.is_none() { 655 simple_filter = Some(SimpleFilter::Class(class)); 656 } 657 }, 658 Component::LocalName(ref local_name) => { 659 if combinator.is_none() { 660 // Prefer to look at class rather than local-name if 661 // both are present. 662 if let Some(SimpleFilter::Class(..)) = simple_filter { 663 continue; 664 } 665 simple_filter = Some(SimpleFilter::LocalName(local_name)); 666 } 667 }, 668 ref other => { 669 if let Some(id) = get_id(other) { 670 if combinator.is_none() { 671 // In the rightmost compound, just find descendants of root that match 672 // the selector list with that id. 673 collect_elements_with_id::<E, Q, _>( 674 root, 675 id, 676 results, 677 class_and_id_case_sensitivity, 678 |e| { 679 matching::matches_selector_list( 680 selector_list, 681 &e, 682 matching_context, 683 ) 684 }, 685 ); 686 return Ok(()); 687 } 688 689 let elements = fast_connected_elements_with_id( 690 root, 691 id, 692 class_and_id_case_sensitivity, 693 )?; 694 if elements.is_empty() { 695 return Ok(()); 696 } 697 698 // Results need to be in document order. Let's not bother 699 // reordering or deduplicating nodes, which we would need to 700 // do if one element with the given id were a descendant of 701 // another element with that given id. 702 if !Q::should_stop_after_first_match() && elements.len() > 1 { 703 continue; 704 } 705 706 for element in elements { 707 // If the element is not a descendant of the root, then 708 // it may have descendants that match our selector that 709 // _are_ descendants of the root, and other descendants 710 // that match our selector that are _not_. 711 // 712 // So we can't just walk over the element's descendants 713 // and match the selector against all of them, nor can 714 // we skip looking at this element's descendants. 715 // 716 // Give up on trying to optimize based on this id and 717 // keep walking our selector. 718 if !connected_element_is_descendant_of(*element, root) { 719 continue 'component_loop; 720 } 721 722 query_selector_slow::<E, Q>( 723 element.as_node(), 724 selector_list, 725 results, 726 matching_context, 727 ); 728 729 if Q::should_stop_after_first_match() && !Q::is_empty(&results) { 730 break; 731 } 732 } 733 734 return Ok(()); 735 } 736 if combinator.is_none() && simple_filter.is_none() { 737 if let Some(attr_name) = get_attr_name(other) { 738 simple_filter = Some(SimpleFilter::Attr(attr_name)); 739 } 740 } 741 }, 742 } 743 } 744 745 loop { 746 let next_combinator = match iter.next_sequence() { 747 None => break 'selector_loop, 748 Some(c) => c, 749 }; 750 751 // We don't want to scan stuff affected by sibling combinators, 752 // given we scan the subtree of elements with a given id (and we 753 // don't want to care about scanning the siblings' subtrees). 754 if next_combinator.is_sibling() { 755 // Advance to the next combinator. 756 for _ in &mut iter {} 757 continue; 758 } 759 760 combinator = Some(next_combinator); 761 break; 762 } 763 } 764 765 // We got here without finding any ID or such that we could handle. Try to 766 // use one of the simple filters. 767 let simple_filter = match simple_filter { 768 Some(f) => f, 769 None => return Err(()), 770 }; 771 772 match simple_filter { 773 SimpleFilter::Class(ref class) => { 774 collect_all_elements::<E, Q, _>(root, results, |element| { 775 Operation::from( 776 element.has_class(class, class_and_id_case_sensitivity) 777 && matching::matches_selector_list( 778 selector_list, 779 &element, 780 matching_context, 781 ), 782 ) 783 }); 784 }, 785 SimpleFilter::LocalName(ref local_name) => { 786 collect_all_elements::<E, Q, _>(root, results, |element| { 787 Operation::from( 788 local_name_matches(element, local_name) 789 && matching::matches_selector_list( 790 selector_list, 791 &element, 792 matching_context, 793 ), 794 ) 795 }); 796 }, 797 SimpleFilter::Attr(ref local_name) => { 798 collect_all_elements::<E, Q, _>(root, results, |element| { 799 Operation::from( 800 has_attr(element, local_name) 801 && matching::matches_selector_list( 802 selector_list, 803 &element, 804 matching_context, 805 ), 806 ) 807 }); 808 }, 809 } 810 811 Ok(()) 812 } 813 814 // Slow path for a given selector query. 815 fn query_selector_slow<E, Q>( 816 root: E::ConcreteNode, 817 selector_list: &SelectorList<E::Impl>, 818 results: &mut Q::Output, 819 matching_context: &mut MatchingContext<E::Impl>, 820 ) where 821 E: TElement, 822 Q: SelectorQuery<E>, 823 { 824 collect_all_elements::<E, Q, _>(root, results, |element| { 825 Operation::from(matching::matches_selector_list( 826 selector_list, 827 &element, 828 matching_context, 829 )) 830 }); 831 } 832 833 /// Whether the invalidation machinery should be used for this query. 834 #[derive(PartialEq)] 835 pub enum MayUseInvalidation { 836 /// We may use it if we deem it useful. 837 Yes, 838 /// Don't use it. 839 No, 840 } 841 842 /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector> 843 pub fn query_selector<E, Q>( 844 root: E::ConcreteNode, 845 selector_list: &SelectorList<E::Impl>, 846 results: &mut Q::Output, 847 may_use_invalidation: MayUseInvalidation, 848 ) where 849 E: TElement, 850 Q: SelectorQuery<E>, 851 { 852 use crate::invalidation::element::invalidator::TreeStyleInvalidator; 853 854 let mut selector_caches = SelectorCaches::default(); 855 let quirks_mode = root.owner_doc().quirks_mode(); 856 857 let mut matching_context = MatchingContext::new( 858 MatchingMode::Normal, 859 None, 860 &mut selector_caches, 861 quirks_mode, 862 NeedsSelectorFlags::No, 863 MatchingForInvalidation::No, 864 ); 865 let root_element = root.as_element(); 866 matching_context.scope_element = root_element.map(|e| e.opaque()); 867 matching_context.current_host = match root_element { 868 Some(root) => root.containing_shadow_host().map(|host| host.opaque()), 869 None => root.as_shadow_root().map(|root| root.host().opaque()), 870 }; 871 872 let fast_result = 873 query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context); 874 875 if fast_result.is_ok() { 876 return; 877 } 878 879 // Slow path: Use the invalidation machinery if we're a root, and tree 880 // traversal otherwise. 881 // 882 // See the comment in collect_invalidations to see why only if we're a root. 883 // 884 // The invalidation mechanism is only useful in presence of combinators. 885 // 886 // We could do that check properly here, though checking the length of the 887 // selectors is a good heuristic. 888 // 889 // A selector with a combinator needs to have a length of at least 3: A 890 // simple selector, a combinator, and another simple selector. 891 let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes 892 && selector_list.slice().iter().any(|s| s.len() > 2); 893 894 if root_element.is_some() || !invalidation_may_be_useful { 895 query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context); 896 } else { 897 let dependencies = selector_list 898 .slice() 899 .iter() 900 .map(|selector| Dependency::for_full_selector_invalidation(selector.clone())) 901 .collect::<SmallVec<[_; 5]>>(); 902 let mut processor = QuerySelectorProcessor::<E, Q> { 903 results, 904 matching_context, 905 traversal_map: SiblingTraversalMap::default(), 906 dependencies: &dependencies, 907 }; 908 909 for node in root.dom_children() { 910 if let Some(e) = node.as_element() { 911 TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor) 912 .invalidate(); 913 } 914 } 915 } 916 }