1use crate::context::SharedStyleContext;
9use crate::data::ElementData;
10use crate::dom::{TElement, TNode};
11use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper, Snapshots};
12use crate::invalidation::element::invalidation_map::*;
13use crate::invalidation::element::invalidator::{
14 DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,
15 any_next_has_scope_in_negation, note_scope_dependency_force_at_subject,
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::OpaqueElement;
26use selectors::attr::CaseSensitivity;
27use selectors::kleene_value::KleeneValue;
28use selectors::matching::{
29 MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, SelectorCaches,
30 VisitedHandlingMode, matches_selector_kleene,
31};
32use smallvec::SmallVec;
33
34struct Collector<'a, 'b: 'a, 'selectors: 'a, E>
36where
37 E: TElement,
38{
39 element: E,
40 wrapper: ElementWrapper<'a, '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 snapshots: Snapshots<'a>,
61 element: E,
62 data: &'a mut ElementData,
63 matching_context: MatchingContext<'a, E::Impl>,
64 traversal_map: SiblingTraversalMap<E>,
65}
66
67impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> {
68 pub fn new(
70 shared_context: &'a SharedStyleContext<'b>,
71 element: E,
72 data: &'a mut ElementData,
73 selector_caches: &'a mut SelectorCaches,
74 ) -> Self {
75 let matching_context = MatchingContext::new_for_visited(
76 MatchingMode::Normal,
77 None,
78 selector_caches,
79 VisitedHandlingMode::AllLinksVisitedAndUnvisited,
80 shared_context.quirks_mode(),
81 NeedsSelectorFlags::No,
82 MatchingForInvalidation::Yes,
83 );
84
85 Self {
86 shared_context,
87 snapshots: Snapshots::new(shared_context.snapshot_map),
88 element,
89 data,
90 matching_context,
91 traversal_map: SiblingTraversalMap::default(),
92 }
93 }
94}
95
96pub fn check_dependency<E, W>(
99 dependency: &Dependency,
100 element: E,
101 wrapper: W,
102 context: &mut MatchingContext<'_, E::Impl>,
103 scope: Option<OpaqueElement>,
104) -> bool
105where
106 E: TElement,
107 W: selectors::Element<Impl = E::Impl>,
108{
109 context.for_invalidation_comparison(|context| {
110 context.nest_for_scope_condition(scope, |context| {
111 let matches_now = matches_selector_kleene(
112 &dependency.selector,
113 dependency.selector_offset,
114 None,
115 element,
116 context,
117 );
118
119 if scope.is_some() && matches_now != KleeneValue::False {
124 return true;
125 }
126
127 let matched_then = matches_selector_kleene(
128 &dependency.selector,
129 dependency.selector_offset,
130 None,
131 wrapper,
132 context,
133 );
134
135 matched_then != matches_now || matches_now == KleeneValue::Unknown
136 })
137 })
138}
139
140pub fn should_process_descendants(data: &ElementData) -> bool {
143 !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS)
144}
145
146pub fn propagate_dirty_bit_up_to<E>(ancestor: E, child: E)
148where
149 E: TElement,
150{
151 let mut current = child.traversal_parent();
157 while let Some(parent) = current.take() {
158 unsafe { parent.set_dirty_descendants() };
159 current = parent.traversal_parent();
160
161 if parent == ancestor {
162 return;
163 }
164 }
165 debug_assert!(
166 false,
167 "Should've found {:?} as an ancestor of {:?}",
168 ancestor, child
169 );
170}
171
172pub fn invalidated_descendants<E>(element: E, child: E)
174where
175 E: TElement,
176{
177 if !child.has_data() {
178 return;
179 }
180 propagate_dirty_bit_up_to(element, child)
181}
182
183pub fn invalidated_self<E>(element: E) -> bool
186where
187 E: TElement,
188{
189 let mut data = match element.mutate_data() {
190 Some(data) => data,
191 None => return false,
192 };
193 data.hint.insert(RestyleHint::RESTYLE_SELF);
194 true
195}
196
197pub fn invalidated_sibling<E>(element: E, of: E)
199where
200 E: TElement,
201{
202 debug_assert_eq!(
203 element.as_node().parent_node(),
204 of.as_node().parent_node(),
205 "Should be siblings"
206 );
207 if !invalidated_self(element) {
208 return;
209 }
210 if element.traversal_parent() != of.traversal_parent() {
211 let parent = element.as_node().parent_element_or_host();
212 debug_assert!(
213 parent.is_some(),
214 "How can we have siblings without parent nodes?"
215 );
216 if let Some(e) = parent {
217 propagate_dirty_bit_up_to(e, element)
218 }
219 }
220}
221
222impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, 'a, E>
223 for StateAndAttrInvalidationProcessor<'a, 'b, E>
224where
225 E: TElement,
226{
227 fn invalidates_on_pseudo_element(&self) -> bool {
231 true
232 }
233
234 fn check_outer_dependency(
235 &mut self,
236 dependency: &Dependency,
237 element: E,
238 scope: Option<OpaqueElement>,
239 ) -> bool {
240 let wrapper = ElementWrapper::new(element, &self.snapshots);
244 check_dependency(
245 dependency,
246 element,
247 wrapper,
248 &mut self.matching_context,
249 scope,
250 )
251 }
252
253 fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {
254 &mut self.matching_context
255 }
256
257 fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {
258 &self.traversal_map
259 }
260
261 fn collect_invalidations(
262 &mut self,
263 element: E,
264 _self_invalidations: &mut InvalidationVector<'a>,
265 descendant_invalidations: &mut DescendantInvalidationLists<'a>,
266 sibling_invalidations: &mut InvalidationVector<'a>,
267 ) -> bool {
268 debug_assert_eq!(element, self.element);
269 debug_assert!(element.has_snapshot(), "Why bothering?");
270
271 let wrapper = ElementWrapper::new(element, &self.snapshots);
272
273 let state_changes = wrapper.state_changes();
274 let Some(snapshot) = wrapper.snapshot() else {
275 return false;
276 };
277
278 if !snapshot.has_attrs() && !snapshot.has_custom_states() && state_changes.is_empty() {
279 return false;
280 }
281
282 let mut classes_removed = SmallVec::<[Atom; 8]>::new();
283 let mut classes_added = SmallVec::<[Atom; 8]>::new();
284 if snapshot.class_changed() {
285 snapshot.each_class(|c| {
287 if !element.has_class(c, CaseSensitivity::CaseSensitive) {
288 classes_removed.push(c.0.clone())
289 }
290 });
291
292 element.each_class(|c| {
293 if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
294 classes_added.push(c.0.clone())
295 }
296 })
297 }
298
299 let mut custom_states_removed = SmallVec::<[AtomIdent; 8]>::new();
300 let mut custom_states_added = SmallVec::<[AtomIdent; 8]>::new();
301 if snapshot.has_custom_states() {
302 snapshot.each_custom_state(|s| {
303 if !element.has_custom_state(s) {
304 custom_states_removed.push(s.clone())
305 }
306 });
307 element.each_custom_state(|s| {
308 if !snapshot.has_custom_state(s) {
309 custom_states_added.push(s.clone())
310 }
311 })
312 }
313
314 let mut id_removed = None;
315 let mut id_added = None;
316 if snapshot.id_changed() {
317 let old_id = snapshot.id_attr();
318 let current_id = element.id();
319
320 if old_id != current_id {
321 id_removed = old_id;
322 id_added = current_id;
323 }
324 }
325
326 if log_enabled!(::log::Level::Debug) {
327 debug!("Collecting changes for: {:?}", element);
328 if !state_changes.is_empty() {
329 debug!(" > state: {:?}", state_changes);
330 }
331 if snapshot.id_changed() {
332 debug!(" > id changed: +{:?} -{:?}", id_added, id_removed);
333 }
334 if snapshot.class_changed() {
335 debug!(
336 " > class changed: +{:?} -{:?}",
337 classes_added, classes_removed
338 );
339 }
340 let mut attributes_changed = false;
341 snapshot.each_attr_changed(|_| {
342 attributes_changed = true;
343 });
344 if attributes_changed {
345 debug!(
346 " > attributes changed, old: {}",
347 snapshot.debug_list_attributes()
348 )
349 }
350 }
351
352 let lookup_element = if element.implemented_pseudo_element().is_some() {
353 element.pseudo_element_originating_element().unwrap()
354 } else {
355 element
356 };
357
358 let mut shadow_rule_datas = SmallVec::<[_; 3]>::new();
359 let matches_document_author_rules =
360 element.each_applicable_non_document_style_rule_data(|data, host| {
361 shadow_rule_datas.push((data, host.opaque()))
362 });
363
364 let invalidated_self = {
365 let mut collector = Collector {
366 wrapper,
367 lookup_element,
368 state_changes,
369 element,
370 snapshot,
371 matching_context: &mut self.matching_context,
372 removed_id: id_removed,
373 added_id: id_added,
374 classes_removed: &classes_removed,
375 classes_added: &classes_added,
376 custom_states_removed: &custom_states_removed,
377 custom_states_added: &custom_states_added,
378 descendant_invalidations,
379 sibling_invalidations,
380 invalidates_self: false,
381 };
382
383 let document_origins = if !matches_document_author_rules {
384 OriginSet::ORIGIN_USER_AGENT | OriginSet::ORIGIN_USER
385 } else {
386 OriginSet::all()
387 };
388
389 for (cascade_data, origin) in self.shared_context.stylist.iter_origins() {
390 if document_origins.contains(origin.into()) {
391 collector
392 .collect_dependencies_in_invalidation_map(cascade_data.invalidation_map());
393 }
394 }
395
396 for &(data, ref host) in &shadow_rule_datas {
397 collector.matching_context.current_host = Some(*host);
398 collector.collect_dependencies_in_invalidation_map(data.invalidation_map());
399 }
400
401 collector.invalidates_self
402 };
403
404 if descendant_invalidations.dom_descendants.len() > 150 {
415 self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
416 }
417
418 if invalidated_self {
419 self.data.hint.insert(RestyleHint::RESTYLE_SELF);
420 }
421
422 invalidated_self
423 }
424
425 fn should_process_descendants(&mut self, element: E) -> bool {
426 if element == self.element {
427 return should_process_descendants(self.data);
428 }
429
430 match element.borrow_data() {
431 Some(d) => should_process_descendants(&d),
432 None => false,
433 }
434 }
435
436 fn recursion_limit_exceeded(&mut self, element: E) {
437 if element == self.element {
438 self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
439 return;
440 }
441
442 if let Some(mut data) = element.mutate_data() {
443 data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);
444 }
445 }
446
447 fn invalidated_descendants(&mut self, element: E, child: E) {
448 invalidated_descendants(element, child)
449 }
450
451 fn invalidated_self(&mut self, element: E) {
452 debug_assert_ne!(element, self.element);
453 invalidated_self(element);
454 }
455
456 fn invalidated_sibling(&mut self, element: E, of: E) {
457 debug_assert_ne!(element, self.element);
458 invalidated_sibling(element, of);
459 }
460
461 fn invalidated_highlight_pseudo(&mut self, element: E) {
462 element.note_highlight_pseudo_style_invalidated();
463 }
464}
465
466impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E>
467where
468 E: TElement,
469 'selectors: 'a,
470{
471 fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) {
472 let quirks_mode = self.matching_context.quirks_mode();
473 let removed_id = self.removed_id;
474 if let Some(id) = removed_id
475 && let Some(deps) = map.id_to_selector.get(id, quirks_mode)
476 {
477 for dep in deps {
478 self.scan_dependency(dep, false);
479 }
480 }
481
482 let added_id = self.added_id;
483 if let Some(id) = added_id
484 && let Some(deps) = map.id_to_selector.get(id, quirks_mode)
485 {
486 for dep in deps {
487 self.scan_dependency(dep, false);
488 }
489 }
490
491 for class in self.classes_added.iter().chain(self.classes_removed.iter()) {
492 if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {
493 for dep in deps {
494 self.scan_dependency(dep, false);
495 }
496 }
497 }
498
499 for state in self
500 .custom_states_added
501 .iter()
502 .chain(self.custom_states_removed.iter())
503 {
504 if let Some(deps) = map.custom_state_affecting_selectors.get(state) {
505 for dep in deps {
506 self.scan_dependency(dep, false);
507 }
508 }
509 }
510
511 self.snapshot.each_attr_changed(|attribute| {
512 if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {
513 for dep in deps {
514 self.scan_dependency(dep, false);
515 }
516 }
517 });
518
519 self.collect_state_dependencies(&map.state_affecting_selectors)
520 }
521
522 fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap<StateDependency>) {
523 if self.state_changes.is_empty() {
524 return;
525 }
526 map.lookup_with_additional(
527 self.lookup_element,
528 self.matching_context.quirks_mode(),
529 self.removed_id,
530 self.classes_removed,
531 self.state_changes,
532 |dependency| {
533 if !dependency.state.intersects(self.state_changes) {
534 return true;
535 }
536 self.scan_dependency(&dependency.dep, false);
537 true
538 },
539 );
540 }
541
542 #[inline]
544 fn check_dependency(&mut self, dependency: &Dependency, set_scope: bool) -> bool {
545 check_dependency(
546 dependency,
547 self.element,
548 self.wrapper,
549 self.matching_context,
550 set_scope.then(|| self.element.opaque()),
551 )
552 }
553
554 fn scan_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {
555 debug_assert!(
556 matches!(
557 dependency.invalidation_kind(),
558 DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_)
559 ),
560 "Found unexpected dependency invalidation kind"
561 );
562 debug!(
563 "TreeStyleInvalidator::scan_dependency({:?}, {:?})",
564 self.element, dependency
565 );
566
567 if !self.dependency_may_be_relevant(dependency) {
568 return;
569 }
570
571 if self.check_dependency(dependency, set_scope) {
572 self.note_dependency(dependency, set_scope)
573 }
574 }
575
576 fn note_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {
577 debug_assert!(self.dependency_may_be_relevant(dependency));
578 let invalidation_kind = dependency.invalidation_kind();
579 if matches!(
580 invalidation_kind,
581 DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)
582 ) {
583 if let Some(ref next) = dependency.next {
584 self.scan_dependency(&next.as_ref().slice()[0], set_scope);
587 } else {
588 self.invalidates_self = true;
589 }
590 return;
591 }
592
593 if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind {
594 if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope
595 && let Some(ref next) = dependency.next
596 {
597 for dep in next.as_ref().slice() {
602 let invalidation = Invalidation::new_always_effective_for_next_descendant(
603 dep,
604 self.matching_context.current_host,
605 self.matching_context.scope_element,
606 );
607
608 self.descendant_invalidations
609 .dom_descendants
610 .push(invalidation);
611 }
612 return;
613 }
614
615 if dependency.selector.is_rightmost(dependency.selector_offset) {
616 let force_add = any_next_has_scope_in_negation(dependency);
617 if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {
618 let invalidations = note_scope_dependency_force_at_subject(
619 dependency,
620 self.matching_context.current_host,
621 self.matching_context.scope_element,
622 force_add,
623 );
624 for invalidation in invalidations {
625 self.descendant_invalidations
626 .dom_descendants
627 .push(invalidation);
628 }
629 self.invalidates_self = true;
630 } else if let Some(ref next) = dependency.next {
631 for dep in next.as_ref().slice() {
632 self.scan_dependency(dep, true);
633 }
634 }
635 return;
636 }
637 }
638
639 debug_assert_ne!(dependency.selector_offset, 0);
640 debug_assert_ne!(dependency.selector_offset, dependency.selector.len());
641
642 let invalidation = Invalidation::new(
643 dependency,
644 self.matching_context.current_host,
645 self.matching_context.scope_element,
646 );
647
648 let invalidated_self = push_invalidation(
649 invalidation,
650 invalidation_kind,
651 self.descendant_invalidations,
652 self.sibling_invalidations,
653 );
654
655 if invalidated_self
659 && dependency
660 .selector
661 .pseudo_element()
662 .is_some_and(|p| p.is_lazy_painted_highlight_pseudo())
663 {
664 self.element.note_highlight_pseudo_style_invalidated();
665 }
666
667 self.invalidates_self |= invalidated_self;
668 }
669
670 fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
673 match dependency.invalidation_kind() {
674 DependencyInvalidationKind::FullSelector | DependencyInvalidationKind::Relative(_) => {
675 unreachable!()
676 },
677 DependencyInvalidationKind::Scope(_) => true,
678 DependencyInvalidationKind::Normal(kind) => match kind {
679 NormalDependencyInvalidationKind::Element => !self.invalidates_self,
680 NormalDependencyInvalidationKind::SlottedElements => {
681 self.element.is_html_slot_element()
682 },
683 NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
684 NormalDependencyInvalidationKind::ElementAndDescendants
685 | NormalDependencyInvalidationKind::Siblings
686 | NormalDependencyInvalidationKind::Descendants => true,
687 },
688 }
689 }
690}
691
692pub(crate) fn push_invalidation<'a>(
693 invalidation: Invalidation<'a>,
694 invalidation_kind: DependencyInvalidationKind,
695 descendant_invalidations: &mut DescendantInvalidationLists<'a>,
696 sibling_invalidations: &mut InvalidationVector<'a>,
697) -> bool {
698 match invalidation_kind {
699 DependencyInvalidationKind::FullSelector => unreachable!(),
700 DependencyInvalidationKind::Relative(_) => unreachable!(),
701 DependencyInvalidationKind::Scope(_) => {
702 let combinator = invalidation.combinator_to_right();
705 if combinator.is_sibling() {
706 sibling_invalidations.push(invalidation);
707 } else {
708 descendant_invalidations.dom_descendants.push(invalidation);
709 }
710 true
711 },
712 DependencyInvalidationKind::Normal(kind) => match kind {
713 NormalDependencyInvalidationKind::Element => unreachable!(),
714 NormalDependencyInvalidationKind::ElementAndDescendants => {
715 descendant_invalidations.dom_descendants.push(invalidation);
716 true
717 },
718 NormalDependencyInvalidationKind::Descendants => {
719 descendant_invalidations.dom_descendants.push(invalidation);
720 false
721 },
722 NormalDependencyInvalidationKind::Siblings => {
723 sibling_invalidations.push(invalidation);
724 false
725 },
726 NormalDependencyInvalidationKind::Parts => {
727 descendant_invalidations.parts.push(invalidation);
728 false
729 },
730 NormalDependencyInvalidationKind::SlottedElements => {
731 descendant_invalidations
732 .slotted_descendants
733 .push(invalidation);
734 false
735 },
736 },
737 }
738}
739
740pub(crate) fn dependency_may_be_relevant<E: TElement>(
741 dependency: &Dependency,
742 element: &E,
743 already_invalidated_self: bool,
744) -> bool {
745 match dependency.invalidation_kind() {
746 DependencyInvalidationKind::FullSelector => unreachable!(),
747 DependencyInvalidationKind::Relative(_) => unreachable!(),
748 DependencyInvalidationKind::Scope(_) => true,
749 DependencyInvalidationKind::Normal(kind) => match kind {
750 NormalDependencyInvalidationKind::Element => !already_invalidated_self,
751 NormalDependencyInvalidationKind::SlottedElements => element.is_html_slot_element(),
752 NormalDependencyInvalidationKind::Parts => element.shadow_root().is_some(),
753 NormalDependencyInvalidationKind::ElementAndDescendants
754 | NormalDependencyInvalidationKind::Siblings
755 | NormalDependencyInvalidationKind::Descendants => true,
756 },
757 }
758}