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