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 note_scope_dependency_force_at_subject, DescendantInvalidationLists, InvalidationAddOverride,
15 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
461impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E>
462where
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 #[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 || matches!(
568 dependency.invalidation_kind(),
569 DependencyInvalidationKind::Scope(_)
570 )
571 {
572 return self.note_dependency(dependency);
573 }
574 }
575
576 fn note_dependency(&mut self, dependency: &'selectors Dependency) {
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], false);
587 } else {
588 self.invalidates_self = true;
589 }
590 return;
591 }
592
593 if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind {
594 if dependency.selector_offset == 0 {
595 if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd {
596 let invalidations = note_scope_dependency_force_at_subject(
597 dependency,
598 self.matching_context.current_host.clone(),
599 );
600 for (invalidation, override_type) in invalidations {
601 match override_type {
602 InvalidationAddOverride::Descendant => self
603 .descendant_invalidations
604 .dom_descendants
605 .push(invalidation),
606 InvalidationAddOverride::Sibling => {
607 self.sibling_invalidations.push(invalidation)
608 },
609 }
610 }
611 self.invalidates_self = true;
612 } else if let Some(ref next) = dependency.next {
613 for dep in next.as_ref().slice() {
614 self.scan_dependency(dep, true);
615 }
616 }
617 } else {
618 let invalidation = Invalidation::new(
619 &dependency,
620 self.matching_context.current_host.clone(),
621 None,
622 );
623
624 let combinator = dependency
625 .selector
626 .combinator_at_match_order(dependency.selector_offset - 1);
627 if combinator.is_sibling() {
628 self.sibling_invalidations.push(invalidation);
629 } else {
630 self.descendant_invalidations
631 .dom_descendants
632 .push(invalidation);
633 }
634 }
635 return;
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 self.invalidates_self |= push_invalidation(
648 invalidation,
649 invalidation_kind,
650 self.descendant_invalidations,
651 self.sibling_invalidations,
652 );
653 }
654
655 fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {
658 match dependency.invalidation_kind() {
659 DependencyInvalidationKind::FullSelector | DependencyInvalidationKind::Relative(_) => {
660 unreachable!()
661 },
662 DependencyInvalidationKind::Scope(_) => true,
663 DependencyInvalidationKind::Normal(kind) => match kind {
664 NormalDependencyInvalidationKind::Element => !self.invalidates_self,
665 NormalDependencyInvalidationKind::SlottedElements => {
666 self.element.is_html_slot_element()
667 },
668 NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),
669 NormalDependencyInvalidationKind::ElementAndDescendants
670 | NormalDependencyInvalidationKind::Siblings
671 | NormalDependencyInvalidationKind::Descendants => true,
672 },
673 }
674 }
675}
676
677pub(crate) fn push_invalidation<'a>(
678 invalidation: Invalidation<'a>,
679 invalidation_kind: DependencyInvalidationKind,
680 descendant_invalidations: &mut DescendantInvalidationLists<'a>,
681 sibling_invalidations: &mut InvalidationVector<'a>,
682) -> bool {
683 match invalidation_kind {
684 DependencyInvalidationKind::FullSelector => unreachable!(),
685 DependencyInvalidationKind::Relative(_) => unreachable!(),
686 DependencyInvalidationKind::Scope(_) => {
687 descendant_invalidations.dom_descendants.push(invalidation);
688 true
689 },
690 DependencyInvalidationKind::Normal(kind) => match kind {
691 NormalDependencyInvalidationKind::Element => unreachable!(),
692 NormalDependencyInvalidationKind::ElementAndDescendants => {
693 descendant_invalidations.dom_descendants.push(invalidation);
694 true
695 },
696 NormalDependencyInvalidationKind::Descendants => {
697 descendant_invalidations.dom_descendants.push(invalidation);
698 false
699 },
700 NormalDependencyInvalidationKind::Siblings => {
701 sibling_invalidations.push(invalidation);
702 false
703 },
704 NormalDependencyInvalidationKind::Parts => {
705 descendant_invalidations.parts.push(invalidation);
706 false
707 },
708 NormalDependencyInvalidationKind::SlottedElements => {
709 descendant_invalidations
710 .slotted_descendants
711 .push(invalidation);
712 false
713 },
714 },
715 }
716}
717
718pub(crate) fn dependency_may_be_relevant<E: TElement>(
719 dependency: &Dependency,
720 element: &E,
721 already_invalidated_self: bool,
722) -> bool {
723 match dependency.invalidation_kind() {
724 DependencyInvalidationKind::FullSelector => unreachable!(),
725 DependencyInvalidationKind::Relative(_) => unreachable!(),
726 DependencyInvalidationKind::Scope(_) => true,
727 DependencyInvalidationKind::Normal(kind) => match kind {
728 NormalDependencyInvalidationKind::Element => !already_invalidated_self,
729 NormalDependencyInvalidationKind::SlottedElements => element.is_html_slot_element(),
730 NormalDependencyInvalidationKind::Parts => element.shadow_root().is_some(),
731 NormalDependencyInvalidationKind::ElementAndDescendants
732 | NormalDependencyInvalidationKind::Siblings
733 | NormalDependencyInvalidationKind::Descendants => true,
734 },
735 }
736}