1use crate::context::SharedStyleContext;
9use crate::data::ElementData;
10use crate::dom::{TElement, TNode};
11use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
12use crate::invalidation::element::invalidation_map::*;
13use crate::invalidation::element::invalidator::{
14 any_next_has_scope_in_negation, note_scope_dependency_force_at_subject,
15 DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
16};
17use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};
18use crate::invalidation::element::restyle_hints::RestyleHint;
19use crate::selector_map::SelectorMap;
20use crate::selector_parser::Snapshot;
21use crate::stylesheets::origin::OriginSet;
22use crate::values::AtomIdent;
23use crate::{Atom, WeakAtom};
24use dom::ElementState;
25use selectors::attr::CaseSensitivity;
26use selectors::kleene_value::KleeneValue;
27use selectors::matching::{
28 matches_selector_kleene, IncludeStartingStyle, MatchingContext, MatchingForInvalidation,
29 MatchingMode, NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode,
30};
31use selectors::OpaqueElement;
32use smallvec::SmallVec;
33
34struct Collector<'a, 'b: 'a, 'selectors: 'a, E>
36where
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
56pub 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
66impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> {
67 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
95pub 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
104where
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 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
139pub fn should_process_descendants(data: &ElementData) -> bool {
142 !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS)
143}
144
145pub fn propagate_dirty_bit_up_to<E>(ancestor: E, child: E)
147where
148 E: TElement,
149{
150 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
171pub fn invalidated_descendants<E>(element: E, child: E)
173where
174 E: TElement,
175{
176 if !child.has_data() {
177 return;
178 }
179 propagate_dirty_bit_up_to(element, child)
180}
181
182pub fn invalidated_self<E>(element: E) -> bool
185where
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
196pub fn invalidated_sibling<E>(element: E, of: E)
198where
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
221impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, 'a, E>
222 for StateAndAttrInvalidationProcessor<'a, 'b, E>
223where
224 E: TElement,
225{
226 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 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 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 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 fn invalidated_highlight_pseudo(&mut self, element: E) {
461 element.note_highlight_pseudo_style_invalidated();
462 }
463}
464
465impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E>
466where
467 E: TElement,
468 'selectors: 'a,
469{
470 fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) {
471 let quirks_mode = self.matching_context.quirks_mode();
472 let removed_id = self.removed_id;
473 if let Some(ref id) = removed_id {
474 if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
475 for dep in deps {
476 self.scan_dependency(dep, false);
477 }
478 }
479 }
480
481 let added_id = self.added_id;
482 if let Some(ref id) = added_id {
483 if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {
484 for dep in deps {
485 self.scan_dependency(dep, false);
486 }
487 }
488 }
489
490 for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
491 if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
492 for dep in deps {
493 self.scan_dependency(dep, false);
494 }
495 }
496 }
497
498 for state in self
499 .custom_states_added
500 .iter()
501 .chain(self.custom_states_removed.iter())
502 {
503 if let Some(deps) = map.custom_state_affecting_selectors.get(state) {
504 for dep in deps {
505 self.scan_dependency(dep, false);
506 }
507 }
508 }
509
510 self.snapshot.each_attr_changed(|attribute| {
511 if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {
512 for dep in deps {
513 self.scan_dependency(dep, false);
514 }
515 }
516 });
517
518 self.collect_state_dependencies(&map.state_affecting_selectors)
519 }
520
521 fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap<StateDependency>) {
522 if self.state_changes.is_empty() {
523 return;
524 }
525 map.lookup_with_additional(
526 self.lookup_element,
527 self.matching_context.quirks_mode(),
528 self.removed_id,
529 self.classes_removed,
530 self.state_changes,
531 |dependency| {
532 if !dependency.state.intersects(self.state_changes) {
533 return true;
534 }
535 self.scan_dependency(&dependency.dep, false);
536 true
537 },
538 );
539 }
540
541 #[inline]
543 fn check_dependency(&mut self, dependency: &Dependency, set_scope: bool) -> bool {
544 check_dependency(
545 dependency,
546 &self.element,
547 &self.wrapper,
548 &mut self.matching_context,
549 set_scope.then(|| self.element.opaque()),
550 )
551 }
552
553 fn scan_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {
554 debug_assert!(
555 matches!(
556 dependency.invalidation_kind(),
557 DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_)
558 ),
559 "Found unexpected dependency invalidation kind"
560 );
561 debug!(
562 "TreeStyleInvalidator::scan_dependency({:?}, {:?})",
563 self.element, dependency
564 );
565
566 if !self.dependency_may_be_relevant(dependency) {
567 return;
568 }
569
570 if self.check_dependency(dependency, set_scope) {
571 return self.note_dependency(dependency, set_scope);
572 }
573 }
574
575 fn note_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {
576 debug_assert!(self.dependency_may_be_relevant(dependency));
577 let invalidation_kind = dependency.invalidation_kind();
578 if matches!(
579 invalidation_kind,
580 DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)
581 ) {
582 if let Some(ref next) = dependency.next {
583 self.scan_dependency(&next.as_ref().slice()[0], set_scope);
586 } else {
587 self.invalidates_self = true;
588 }
589 return;
590 }
591
592 if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind {
593 if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope {
594 if let Some(ref next) = dependency.next {
595 for dep in next.as_ref().slice() {
600 let invalidation = Invalidation::new_always_effective_for_next_descendant(
601 dep,
602 self.matching_context.current_host.clone(),
603 self.matching_context.scope_element,
604 );
605
606 self.descendant_invalidations
607 .dom_descendants
608 .push(invalidation);
609 }
610 return;
611 }
612 }
613
614 if dependency.selector.is_rightmost(dependency.selector_offset) {
615 let force_add = any_next_has_scope_in_negation(dependency);
616 if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {
617 let invalidations = note_scope_dependency_force_at_subject(
618 dependency,
619 self.matching_context.current_host.clone(),
620 self.matching_context.scope_element,
621 force_add,
622 );
623 for invalidation in invalidations {
624 self.descendant_invalidations
625 .dom_descendants
626 .push(invalidation);
627 }
628 self.invalidates_self = true;
629 } else if let Some(ref next) = dependency.next {
630 for dep in next.as_ref().slice() {
631 self.scan_dependency(dep, true);
632 }
633 }
634 return;
635 }
636 }
637
638 debug_assert_ne!(dependency.selector_offset, 0);
639 debug_assert_ne!(dependency.selector_offset, dependency.selector.len());
640
641 let invalidation = Invalidation::new(
642 &dependency,
643 self.matching_context.current_host.clone(),
644 self.matching_context.scope_element.clone(),
645 );
646
647 let invalidated_self = push_invalidation(
648 invalidation,
649 invalidation_kind,
650 self.descendant_invalidations,
651 self.sibling_invalidations,
652 );
653
654 if invalidated_self
658 && dependency
659 .selector
660 .pseudo_element()
661 .is_some_and(|p| p.is_lazy_painted_highlight_pseudo())
662 {
663 self.element.note_highlight_pseudo_style_invalidated();
664 }
665
666 self.invalidates_self |= invalidated_self;
667 }
668
669 fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
672 match dependency.invalidation_kind() {
673 DependencyInvalidationKind::FullSelector | DependencyInvalidationKind::Relative(_) => {
674 unreachable!()
675 },
676 DependencyInvalidationKind::Scope(_) => true,
677 DependencyInvalidationKind::Normal(kind) => match kind {
678 NormalDependencyInvalidationKind::Element => !self.invalidates_self,
679 NormalDependencyInvalidationKind::SlottedElements => {
680 self.element.is_html_slot_element()
681 },
682 NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
683 NormalDependencyInvalidationKind::ElementAndDescendants
684 | NormalDependencyInvalidationKind::Siblings
685 | NormalDependencyInvalidationKind::Descendants => true,
686 },
687 }
688 }
689}
690
691pub(crate) fn push_invalidation<'a>(
692 invalidation: Invalidation<'a>,
693 invalidation_kind: DependencyInvalidationKind,
694 descendant_invalidations: &mut DescendantInvalidationLists<'a>,
695 sibling_invalidations: &mut InvalidationVector<'a>,
696) -> bool {
697 match invalidation_kind {
698 DependencyInvalidationKind::FullSelector => unreachable!(),
699 DependencyInvalidationKind::Relative(_) => unreachable!(),
700 DependencyInvalidationKind::Scope(_) => {
701 let combinator = invalidation.combinator_to_right();
704 if combinator.is_sibling() {
705 sibling_invalidations.push(invalidation);
706 } else {
707 descendant_invalidations.dom_descendants.push(invalidation);
708 }
709 true
710 },
711 DependencyInvalidationKind::Normal(kind) => match kind {
712 NormalDependencyInvalidationKind::Element => unreachable!(),
713 NormalDependencyInvalidationKind::ElementAndDescendants => {
714 descendant_invalidations.dom_descendants.push(invalidation);
715 true
716 },
717 NormalDependencyInvalidationKind::Descendants => {
718 descendant_invalidations.dom_descendants.push(invalidation);
719 false
720 },
721 NormalDependencyInvalidationKind::Siblings => {
722 sibling_invalidations.push(invalidation);
723 false
724 },
725 NormalDependencyInvalidationKind::Parts => {
726 descendant_invalidations.parts.push(invalidation);
727 false
728 },
729 NormalDependencyInvalidationKind::SlottedElements => {
730 descendant_invalidations
731 .slotted_descendants
732 .push(invalidation);
733 false
734 },
735 },
736 }
737}
738
739pub(crate) fn dependency_may_be_relevant<E: TElement>(
740 dependency: &Dependency,
741 element: &E,
742 already_invalidated_self: bool,
743) -> bool {
744 match dependency.invalidation_kind() {
745 DependencyInvalidationKind::FullSelector => unreachable!(),
746 DependencyInvalidationKind::Relative(_) => unreachable!(),
747 DependencyInvalidationKind::Scope(_) => true,
748 DependencyInvalidationKind::Normal(kind) => match kind {
749 NormalDependencyInvalidationKind::Element => !already_invalidated_self,
750 NormalDependencyInvalidationKind::SlottedElements => element.is_html_slot_element(),
751 NormalDependencyInvalidationKind::Parts => element.shadow_root().is_some(),
752 NormalDependencyInvalidationKind::ElementAndDescendants
753 | NormalDependencyInvalidationKind::Siblings
754 | NormalDependencyInvalidationKind::Descendants => true,
755 },
756 }
757}