state_and_attributes.rs (26585B)
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 //! An invalidation processor for style changes due to state and attribute 6 //! changes. 7 8 use crate::context::SharedStyleContext; 9 use crate::data::ElementData; 10 use crate::dom::{TElement, TNode}; 11 use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; 12 use crate::invalidation::element::invalidation_map::*; 13 use crate::invalidation::element::invalidator::{ 14 any_next_has_scope_in_negation, note_scope_dependency_force_at_subject, 15 DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap, 16 }; 17 use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor}; 18 use crate::invalidation::element::restyle_hints::RestyleHint; 19 use crate::selector_map::SelectorMap; 20 use crate::selector_parser::Snapshot; 21 use crate::stylesheets::origin::OriginSet; 22 use crate::values::AtomIdent; 23 use crate::{Atom, WeakAtom}; 24 use dom::ElementState; 25 use selectors::attr::CaseSensitivity; 26 use selectors::kleene_value::KleeneValue; 27 use selectors::matching::{ 28 matches_selector_kleene, IncludeStartingStyle, MatchingContext, MatchingForInvalidation, 29 MatchingMode, NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode, 30 }; 31 use selectors::OpaqueElement; 32 use smallvec::SmallVec; 33 34 /// The collector implementation. 35 struct Collector<'a, 'b: 'a, 'selectors: 'a, E> 36 where 37 E: TElement, 38 { 39 element: E, 40 wrapper: ElementWrapper<'b, E>, 41 snapshot: &'a Snapshot, 42 matching_context: &'a mut MatchingContext<'b, E::Impl>, 43 lookup_element: E, 44 removed_id: Option<&'a WeakAtom>, 45 added_id: Option<&'a WeakAtom>, 46 classes_removed: &'a SmallVec<[Atom; 8]>, 47 classes_added: &'a SmallVec<[Atom; 8]>, 48 custom_states_removed: &'a SmallVec<[AtomIdent; 8]>, 49 custom_states_added: &'a SmallVec<[AtomIdent; 8]>, 50 state_changes: ElementState, 51 descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>, 52 sibling_invalidations: &'a mut InvalidationVector<'selectors>, 53 invalidates_self: bool, 54 } 55 56 /// An invalidation processor for style changes due to state and attribute 57 /// changes. 58 pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { 59 shared_context: &'a SharedStyleContext<'b>, 60 element: E, 61 data: &'a mut ElementData, 62 matching_context: MatchingContext<'a, E::Impl>, 63 traversal_map: SiblingTraversalMap<E>, 64 } 65 66 impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> { 67 /// Creates a new StateAndAttrInvalidationProcessor. 68 pub fn new( 69 shared_context: &'a SharedStyleContext<'b>, 70 element: E, 71 data: &'a mut ElementData, 72 selector_caches: &'a mut SelectorCaches, 73 ) -> Self { 74 let matching_context = MatchingContext::new_for_visited( 75 MatchingMode::Normal, 76 None, 77 selector_caches, 78 VisitedHandlingMode::AllLinksVisitedAndUnvisited, 79 IncludeStartingStyle::No, 80 shared_context.quirks_mode(), 81 NeedsSelectorFlags::No, 82 MatchingForInvalidation::Yes, 83 ); 84 85 Self { 86 shared_context, 87 element, 88 data, 89 matching_context, 90 traversal_map: SiblingTraversalMap::default(), 91 } 92 } 93 } 94 95 /// Checks a dependency against a given element and wrapper, to see if something 96 /// changed. 97 pub fn check_dependency<E, W>( 98 dependency: &Dependency, 99 element: &E, 100 wrapper: &W, 101 context: &mut MatchingContext<'_, E::Impl>, 102 scope: Option<OpaqueElement>, 103 ) -> bool 104 where 105 E: TElement, 106 W: selectors::Element<Impl = E::Impl>, 107 { 108 context.for_invalidation_comparison(|context| { 109 context.nest_for_scope_condition(scope, |context| { 110 let matches_now = matches_selector_kleene( 111 &dependency.selector, 112 dependency.selector_offset, 113 None, 114 element, 115 context, 116 ); 117 118 // If the previous dependency was a scope dependency (i.e. by `scope` is set), 119 // possible change in scope element is encapsulated in `:scope`, whose 120 // matching value will not change. We instead check that the change in scope 121 // element can propagate (i.e. This selector matches). 122 if scope.is_some() && matches_now != KleeneValue::False { 123 return true; 124 } 125 126 let matched_then = matches_selector_kleene( 127 &dependency.selector, 128 dependency.selector_offset, 129 None, 130 wrapper, 131 context, 132 ); 133 134 matched_then != matches_now || matches_now == KleeneValue::Unknown 135 }) 136 }) 137 } 138 139 /// Whether we should process the descendants of a given element for style 140 /// invalidation. 141 pub fn should_process_descendants(data: &ElementData) -> bool { 142 !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS) 143 } 144 145 /// Propagates the bits after invalidating a descendant child. 146 pub fn propagate_dirty_bit_up_to<E>(ancestor: E, child: E) 147 where 148 E: TElement, 149 { 150 // The child may not be a flattened tree child of the current element, 151 // but may be arbitrarily deep. 152 // 153 // Since we keep the traversal flags in terms of the flattened tree, 154 // we need to propagate it as appropriate. 155 let mut current = child.traversal_parent(); 156 while let Some(parent) = current.take() { 157 unsafe { parent.set_dirty_descendants() }; 158 current = parent.traversal_parent(); 159 160 if parent == ancestor { 161 return; 162 } 163 } 164 debug_assert!( 165 false, 166 "Should've found {:?} as an ancestor of {:?}", 167 ancestor, child 168 ); 169 } 170 171 /// Propagates the bits after invalidating a descendant child, if needed. 172 pub fn invalidated_descendants<E>(element: E, child: E) 173 where 174 E: TElement, 175 { 176 if !child.has_data() { 177 return; 178 } 179 propagate_dirty_bit_up_to(element, child) 180 } 181 182 /// Sets the appropriate restyle hint after invalidating the style of a given 183 /// element. 184 pub fn invalidated_self<E>(element: E) -> bool 185 where 186 E: TElement, 187 { 188 let mut data = match element.mutate_data() { 189 Some(data) => data, 190 None => return false, 191 }; 192 data.hint.insert(RestyleHint::RESTYLE_SELF); 193 true 194 } 195 196 /// Sets the appropriate hint after invalidating the style of a sibling. 197 pub fn invalidated_sibling<E>(element: E, of: E) 198 where 199 E: TElement, 200 { 201 debug_assert_eq!( 202 element.as_node().parent_node(), 203 of.as_node().parent_node(), 204 "Should be siblings" 205 ); 206 if !invalidated_self(element) { 207 return; 208 } 209 if element.traversal_parent() != of.traversal_parent() { 210 let parent = element.as_node().parent_element_or_host(); 211 debug_assert!( 212 parent.is_some(), 213 "How can we have siblings without parent nodes?" 214 ); 215 if let Some(e) = parent { 216 propagate_dirty_bit_up_to(e, element) 217 } 218 } 219 } 220 221 impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, 'a, E> 222 for StateAndAttrInvalidationProcessor<'a, 'b, E> 223 where 224 E: TElement, 225 { 226 /// We need to invalidate style on pseudo-elements, in order to process 227 /// changes that could otherwise end up in ::before or ::after content being 228 /// generated, and invalidate lazy pseudo caches. 229 fn invalidates_on_pseudo_element(&self) -> bool { 230 true 231 } 232 233 fn check_outer_dependency( 234 &mut self, 235 dependency: &Dependency, 236 element: E, 237 scope: Option<OpaqueElement>, 238 ) -> bool { 239 // We cannot assert about `element` having a snapshot here (in fact it 240 // most likely won't), because it may be an arbitrary descendant or 241 // later-sibling of the element we started invalidating with. 242 let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map); 243 check_dependency( 244 dependency, 245 &element, 246 &wrapper, 247 &mut self.matching_context, 248 scope, 249 ) 250 } 251 252 fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> { 253 &mut self.matching_context 254 } 255 256 fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> { 257 &self.traversal_map 258 } 259 260 fn collect_invalidations( 261 &mut self, 262 element: E, 263 _self_invalidations: &mut InvalidationVector<'a>, 264 descendant_invalidations: &mut DescendantInvalidationLists<'a>, 265 sibling_invalidations: &mut InvalidationVector<'a>, 266 ) -> bool { 267 debug_assert_eq!(element, self.element); 268 debug_assert!(element.has_snapshot(), "Why bothering?"); 269 270 let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map); 271 272 let state_changes = wrapper.state_changes(); 273 let Some(snapshot) = wrapper.snapshot() else { 274 return false; 275 }; 276 277 if !snapshot.has_attrs() && !snapshot.has_custom_states() && state_changes.is_empty() { 278 return false; 279 } 280 281 let mut classes_removed = SmallVec::<[Atom; 8]>::new(); 282 let mut classes_added = SmallVec::<[Atom; 8]>::new(); 283 if snapshot.class_changed() { 284 // TODO(emilio): Do this more efficiently! 285 snapshot.each_class(|c| { 286 if !element.has_class(c, CaseSensitivity::CaseSensitive) { 287 classes_removed.push(c.0.clone()) 288 } 289 }); 290 291 element.each_class(|c| { 292 if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) { 293 classes_added.push(c.0.clone()) 294 } 295 }) 296 } 297 298 let mut custom_states_removed = SmallVec::<[AtomIdent; 8]>::new(); 299 let mut custom_states_added = SmallVec::<[AtomIdent; 8]>::new(); 300 if snapshot.has_custom_states() { 301 snapshot.each_custom_state(|s| { 302 if !element.has_custom_state(s) { 303 custom_states_removed.push(s.clone()) 304 } 305 }); 306 element.each_custom_state(|s| { 307 if !snapshot.has_custom_state(s) { 308 custom_states_added.push(s.clone()) 309 } 310 }) 311 } 312 313 let mut id_removed = None; 314 let mut id_added = None; 315 if snapshot.id_changed() { 316 let old_id = snapshot.id_attr(); 317 let current_id = element.id(); 318 319 if old_id != current_id { 320 id_removed = old_id; 321 id_added = current_id; 322 } 323 } 324 325 if log_enabled!(::log::Level::Debug) { 326 debug!("Collecting changes for: {:?}", element); 327 if !state_changes.is_empty() { 328 debug!(" > state: {:?}", state_changes); 329 } 330 if snapshot.id_changed() { 331 debug!(" > id changed: +{:?} -{:?}", id_added, id_removed); 332 } 333 if snapshot.class_changed() { 334 debug!( 335 " > class changed: +{:?} -{:?}", 336 classes_added, classes_removed 337 ); 338 } 339 let mut attributes_changed = false; 340 snapshot.each_attr_changed(|_| { 341 attributes_changed = true; 342 }); 343 if attributes_changed { 344 debug!( 345 " > attributes changed, old: {}", 346 snapshot.debug_list_attributes() 347 ) 348 } 349 } 350 351 let lookup_element = if element.implemented_pseudo_element().is_some() { 352 element.pseudo_element_originating_element().unwrap() 353 } else { 354 element 355 }; 356 357 let mut shadow_rule_datas = SmallVec::<[_; 3]>::new(); 358 let matches_document_author_rules = 359 element.each_applicable_non_document_style_rule_data(|data, host| { 360 shadow_rule_datas.push((data, host.opaque())) 361 }); 362 363 let invalidated_self = { 364 let mut collector = Collector { 365 wrapper, 366 lookup_element, 367 state_changes, 368 element, 369 snapshot: &snapshot, 370 matching_context: &mut self.matching_context, 371 removed_id: id_removed, 372 added_id: id_added, 373 classes_removed: &classes_removed, 374 classes_added: &classes_added, 375 custom_states_removed: &custom_states_removed, 376 custom_states_added: &custom_states_added, 377 descendant_invalidations, 378 sibling_invalidations, 379 invalidates_self: false, 380 }; 381 382 let document_origins = if !matches_document_author_rules { 383 OriginSet::ORIGIN_USER_AGENT | OriginSet::ORIGIN_USER 384 } else { 385 OriginSet::all() 386 }; 387 388 for (cascade_data, origin) in self.shared_context.stylist.iter_origins() { 389 if document_origins.contains(origin.into()) { 390 collector 391 .collect_dependencies_in_invalidation_map(cascade_data.invalidation_map()); 392 } 393 } 394 395 for &(ref data, ref host) in &shadow_rule_datas { 396 collector.matching_context.current_host = Some(host.clone()); 397 collector.collect_dependencies_in_invalidation_map(data.invalidation_map()); 398 } 399 400 collector.invalidates_self 401 }; 402 403 // If we generated a ton of descendant invalidations, it's probably not 404 // worth to go ahead and try to process them. 405 // 406 // Just restyle the descendants directly. 407 // 408 // This number is completely made-up, but the page that made us add this 409 // code generated 1960+ invalidations (bug 1420741). 410 // 411 // We don't look at slotted_descendants because those don't propagate 412 // down more than one level anyway. 413 if descendant_invalidations.dom_descendants.len() > 150 { 414 self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS); 415 } 416 417 if invalidated_self { 418 self.data.hint.insert(RestyleHint::RESTYLE_SELF); 419 } 420 421 invalidated_self 422 } 423 424 fn should_process_descendants(&mut self, element: E) -> bool { 425 if element == self.element { 426 return should_process_descendants(&self.data); 427 } 428 429 match element.borrow_data() { 430 Some(d) => should_process_descendants(&d), 431 None => return false, 432 } 433 } 434 435 fn recursion_limit_exceeded(&mut self, element: E) { 436 if element == self.element { 437 self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS); 438 return; 439 } 440 441 if let Some(mut data) = element.mutate_data() { 442 data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS); 443 } 444 } 445 446 fn invalidated_descendants(&mut self, element: E, child: E) { 447 invalidated_descendants(element, child) 448 } 449 450 fn invalidated_self(&mut self, element: E) { 451 debug_assert_ne!(element, self.element); 452 invalidated_self(element); 453 } 454 455 fn invalidated_sibling(&mut self, element: E, of: E) { 456 debug_assert_ne!(element, self.element); 457 invalidated_sibling(element, of); 458 } 459 } 460 461 impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E> 462 where 463 E: TElement, 464 'selectors: 'a, 465 { 466 fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) { 467 let quirks_mode = self.matching_context.quirks_mode(); 468 let removed_id = self.removed_id; 469 if let Some(ref id) = removed_id { 470 if let Some(deps) = map.id_to_selector.get(id, quirks_mode) { 471 for dep in deps { 472 self.scan_dependency(dep, false); 473 } 474 } 475 } 476 477 let added_id = self.added_id; 478 if let Some(ref id) = added_id { 479 if let Some(deps) = map.id_to_selector.get(id, quirks_mode) { 480 for dep in deps { 481 self.scan_dependency(dep, false); 482 } 483 } 484 } 485 486 for class in self.classes_added.iter().chain(self.classes_removed.iter()) { 487 if let Some(deps) = map.class_to_selector.get(class, quirks_mode) { 488 for dep in deps { 489 self.scan_dependency(dep, false); 490 } 491 } 492 } 493 494 for state in self 495 .custom_states_added 496 .iter() 497 .chain(self.custom_states_removed.iter()) 498 { 499 if let Some(deps) = map.custom_state_affecting_selectors.get(state) { 500 for dep in deps { 501 self.scan_dependency(dep, false); 502 } 503 } 504 } 505 506 self.snapshot.each_attr_changed(|attribute| { 507 if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) { 508 for dep in deps { 509 self.scan_dependency(dep, false); 510 } 511 } 512 }); 513 514 self.collect_state_dependencies(&map.state_affecting_selectors) 515 } 516 517 fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap<StateDependency>) { 518 if self.state_changes.is_empty() { 519 return; 520 } 521 map.lookup_with_additional( 522 self.lookup_element, 523 self.matching_context.quirks_mode(), 524 self.removed_id, 525 self.classes_removed, 526 self.state_changes, 527 |dependency| { 528 if !dependency.state.intersects(self.state_changes) { 529 return true; 530 } 531 self.scan_dependency(&dependency.dep, false); 532 true 533 }, 534 ); 535 } 536 537 /// Check whether a dependency should be taken into account. 538 #[inline] 539 fn check_dependency(&mut self, dependency: &Dependency, set_scope: bool) -> bool { 540 check_dependency( 541 dependency, 542 &self.element, 543 &self.wrapper, 544 &mut self.matching_context, 545 set_scope.then(|| self.element.opaque()), 546 ) 547 } 548 549 fn scan_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) { 550 debug_assert!( 551 matches!( 552 dependency.invalidation_kind(), 553 DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_) 554 ), 555 "Found unexpected dependency invalidation kind" 556 ); 557 debug!( 558 "TreeStyleInvalidator::scan_dependency({:?}, {:?})", 559 self.element, dependency 560 ); 561 562 if !self.dependency_may_be_relevant(dependency) { 563 return; 564 } 565 566 if self.check_dependency(dependency, set_scope) { 567 return self.note_dependency(dependency, set_scope); 568 } 569 } 570 571 fn note_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) { 572 debug_assert!(self.dependency_may_be_relevant(dependency)); 573 let invalidation_kind = dependency.invalidation_kind(); 574 if matches!( 575 invalidation_kind, 576 DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element) 577 ) { 578 if let Some(ref next) = dependency.next { 579 // We know something changed in the inner selector, go outwards 580 // now. 581 self.scan_dependency(&next.as_ref().slice()[0], set_scope); 582 } else { 583 self.invalidates_self = true; 584 } 585 return; 586 } 587 588 if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind { 589 if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope { 590 if let Some(ref next) = dependency.next { 591 // When we reach an implicit scope dependency, we know there's an 592 // element matching that implicit scope somewhere in the descendant. 593 // We need to go find it so that we can continue the invalidation from 594 // its next dependencies. 595 for dep in next.as_ref().slice() { 596 let invalidation = Invalidation::new_always_effective_for_next_descendant( 597 dep, 598 self.matching_context.current_host.clone(), 599 self.matching_context.scope_element, 600 ); 601 602 self.descendant_invalidations 603 .dom_descendants 604 .push(invalidation); 605 } 606 return; 607 } 608 } 609 610 if dependency.selector.is_rightmost(dependency.selector_offset) { 611 let force_add = any_next_has_scope_in_negation(dependency); 612 if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add { 613 let invalidations = note_scope_dependency_force_at_subject( 614 dependency, 615 self.matching_context.current_host.clone(), 616 self.matching_context.scope_element, 617 force_add, 618 ); 619 for invalidation in invalidations { 620 self.descendant_invalidations 621 .dom_descendants 622 .push(invalidation); 623 } 624 self.invalidates_self = true; 625 } else if let Some(ref next) = dependency.next { 626 for dep in next.as_ref().slice() { 627 self.scan_dependency(dep, true); 628 } 629 } 630 return; 631 } 632 } 633 634 debug_assert_ne!(dependency.selector_offset, 0); 635 debug_assert_ne!(dependency.selector_offset, dependency.selector.len()); 636 637 let invalidation = Invalidation::new( 638 &dependency, 639 self.matching_context.current_host.clone(), 640 self.matching_context.scope_element.clone(), 641 ); 642 643 self.invalidates_self |= push_invalidation( 644 invalidation, 645 invalidation_kind, 646 self.descendant_invalidations, 647 self.sibling_invalidations, 648 ); 649 } 650 651 /// Returns whether `dependency` may cause us to invalidate the style of 652 /// more elements than what we've already invalidated. 653 fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool { 654 match dependency.invalidation_kind() { 655 DependencyInvalidationKind::FullSelector | DependencyInvalidationKind::Relative(_) => { 656 unreachable!() 657 }, 658 DependencyInvalidationKind::Scope(_) => true, 659 DependencyInvalidationKind::Normal(kind) => match kind { 660 NormalDependencyInvalidationKind::Element => !self.invalidates_self, 661 NormalDependencyInvalidationKind::SlottedElements => { 662 self.element.is_html_slot_element() 663 }, 664 NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(), 665 NormalDependencyInvalidationKind::ElementAndDescendants 666 | NormalDependencyInvalidationKind::Siblings 667 | NormalDependencyInvalidationKind::Descendants => true, 668 }, 669 } 670 } 671 } 672 673 pub(crate) fn push_invalidation<'a>( 674 invalidation: Invalidation<'a>, 675 invalidation_kind: DependencyInvalidationKind, 676 descendant_invalidations: &mut DescendantInvalidationLists<'a>, 677 sibling_invalidations: &mut InvalidationVector<'a>, 678 ) -> bool { 679 match invalidation_kind { 680 DependencyInvalidationKind::FullSelector => unreachable!(), 681 DependencyInvalidationKind::Relative(_) => unreachable!(), 682 DependencyInvalidationKind::Scope(_) => { 683 // Scope invalidation kind matters only upon reaching the subject. 684 // Examine the combinator to the right of the compound. 685 let combinator = invalidation.combinator_to_right(); 686 if combinator.is_sibling() { 687 sibling_invalidations.push(invalidation); 688 } else { 689 descendant_invalidations.dom_descendants.push(invalidation); 690 } 691 true 692 }, 693 DependencyInvalidationKind::Normal(kind) => match kind { 694 NormalDependencyInvalidationKind::Element => unreachable!(), 695 NormalDependencyInvalidationKind::ElementAndDescendants => { 696 descendant_invalidations.dom_descendants.push(invalidation); 697 true 698 }, 699 NormalDependencyInvalidationKind::Descendants => { 700 descendant_invalidations.dom_descendants.push(invalidation); 701 false 702 }, 703 NormalDependencyInvalidationKind::Siblings => { 704 sibling_invalidations.push(invalidation); 705 false 706 }, 707 NormalDependencyInvalidationKind::Parts => { 708 descendant_invalidations.parts.push(invalidation); 709 false 710 }, 711 NormalDependencyInvalidationKind::SlottedElements => { 712 descendant_invalidations 713 .slotted_descendants 714 .push(invalidation); 715 false 716 }, 717 }, 718 } 719 } 720 721 pub(crate) fn dependency_may_be_relevant<E: TElement>( 722 dependency: &Dependency, 723 element: &E, 724 already_invalidated_self: bool, 725 ) -> bool { 726 match dependency.invalidation_kind() { 727 DependencyInvalidationKind::FullSelector => unreachable!(), 728 DependencyInvalidationKind::Relative(_) => unreachable!(), 729 DependencyInvalidationKind::Scope(_) => true, 730 DependencyInvalidationKind::Normal(kind) => match kind { 731 NormalDependencyInvalidationKind::Element => !already_invalidated_self, 732 NormalDependencyInvalidationKind::SlottedElements => element.is_html_slot_element(), 733 NormalDependencyInvalidationKind::Parts => element.shadow_root().is_some(), 734 NormalDependencyInvalidationKind::ElementAndDescendants 735 | NormalDependencyInvalidationKind::Siblings 736 | NormalDependencyInvalidationKind::Descendants => true, 737 }, 738 } 739 }