1use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext};
8use crate::data::{ElementData, ElementStyles, RestyleKind};
9use crate::dom::{OpaqueNode, TElement, TNode};
10use crate::invalidation::element::restyle_hints::RestyleHint;
11use crate::matching::MatchMethods;
12use crate::selector_parser::PseudoElement;
13use crate::sharing::StyleSharingTarget;
14use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
15use crate::stylist::RuleInclusion;
16use crate::traversal_flags::TraversalFlags;
17use selectors::matching::SelectorCaches;
18#[cfg(feature = "gecko")]
19use selectors::parser::PseudoElement as PseudoElementTrait;
20use smallvec::SmallVec;
21use std::collections::HashMap;
22
23pub type UndisplayedStyleCache =
25 HashMap<selectors::OpaqueElement, servo_arc::Arc<crate::properties::ComputedValues>>;
26
27#[derive(Clone, Copy, Debug)]
32pub struct PerLevelTraversalData {
33 pub current_dom_depth: usize,
38}
39
40pub struct PreTraverseToken<E: TElement>(Option<E>);
43impl<E: TElement> PreTraverseToken<E> {
44 pub fn should_traverse(&self) -> bool {
46 self.0.is_some()
47 }
48
49 pub(crate) fn traversal_root(self) -> Option<E> {
51 self.0
52 }
53}
54
55pub trait DomTraversal<E: TElement>: Sync {
58 fn process_preorder<F>(
63 &self,
64 data: &PerLevelTraversalData,
65 context: &mut StyleContext<E>,
66 node: E::ConcreteNode,
67 note_child: F,
68 ) where
69 F: FnMut(E::ConcreteNode);
70
71 fn process_postorder(&self, contect: &mut StyleContext<E>, node: E::ConcreteNode);
75
76 fn needs_postorder_traversal() -> bool {
81 true
82 }
83
84 fn handle_postorder_traversal(
98 &self,
99 context: &mut StyleContext<E>,
100 root: OpaqueNode,
101 mut node: E::ConcreteNode,
102 children_to_process: isize,
103 ) {
104 if !Self::needs_postorder_traversal() {
106 return;
107 }
108
109 if children_to_process == 0 {
110 loop {
112 self.process_postorder(context, node);
113 if node.opaque() == root {
114 break;
115 }
116 let parent = node.traversal_parent().unwrap();
117 let remaining = parent.did_process_child();
118 if remaining != 0 {
119 break;
123 }
124
125 node = parent.as_node();
126 }
127 } else {
128 node.as_element()
131 .unwrap()
132 .store_children_to_process(children_to_process);
133 }
134 }
135
136 fn pre_traverse(root: E, shared_context: &SharedStyleContext) -> PreTraverseToken<E> {
141 use crate::invalidation::element::state_and_attributes::propagate_dirty_bit_up_to;
142
143 let traversal_flags = shared_context.traversal_flags;
144
145 let mut data = root.mutate_data();
146 let mut data = data.as_mut().map(|d| &mut **d);
147
148 if let Some(ref mut data) = data {
149 if !traversal_flags.for_animation_only() {
150 let invalidation_result = data.invalidate_style_if_needed(
153 root,
154 shared_context,
155 None,
156 &mut SelectorCaches::default(),
157 );
158
159 if invalidation_result.has_invalidated_siblings() {
160 let actual_root = root.as_node().parent_element_or_host().expect(
161 "How in the world can you invalidate \
162 siblings without a parent?",
163 );
164 propagate_dirty_bit_up_to(actual_root, root);
165 return PreTraverseToken(Some(actual_root));
166 }
167 }
168 }
169
170 let should_traverse =
171 Self::element_needs_traversal(root, traversal_flags, data.as_mut().map(|d| &**d));
172
173 if !should_traverse && data.is_some() {
176 clear_state_after_traversing(root, data.unwrap(), traversal_flags);
177 }
178
179 PreTraverseToken(if should_traverse { Some(root) } else { None })
180 }
181
182 fn element_needs_traversal(
184 el: E,
185 traversal_flags: TraversalFlags,
186 data: Option<&ElementData>,
187 ) -> bool {
188 debug!(
189 "element_needs_traversal({:?}, {:?}, {:?})",
190 el, traversal_flags, data
191 );
192
193 let data = match data {
195 Some(d) if d.has_styles() => d,
196 _ => return true,
197 };
198
199 if traversal_flags.for_animation_only() {
200 return el.has_animation_only_dirty_descendants()
203 || data.hint.has_animation_hint_or_recascade();
204 }
205
206 if el.has_dirty_descendants() {
209 return true;
210 }
211
212 if !data.hint.is_empty() {
220 return true;
221 }
222
223 if cfg!(feature = "servo") && !data.damage.is_empty() {
227 return true;
228 }
229
230 trace!("{:?} doesn't need traversal", el);
231 false
232 }
233
234 fn shared_context(&self) -> &SharedStyleContext<'_>;
236}
237
238pub fn resolve_style<E>(
243 context: &mut StyleContext<E>,
244 element: E,
245 rule_inclusion: RuleInclusion,
246 pseudo: Option<&PseudoElement>,
247 mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
248) -> ElementStyles
249where
250 E: TElement,
251{
252 debug_assert!(
253 rule_inclusion == RuleInclusion::DefaultOnly
254 || pseudo.map_or(false, |p| p.is_before_or_after())
255 || element.borrow_data().map_or(true, |d| !d.has_styles()),
256 "Why are we here?"
257 );
258 debug_assert!(
259 rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
260 "can't use the cache for default styles only"
261 );
262
263 let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
264
265 context.thread_local.bloom_filter.clear();
267
268 let mut style = None;
269 let mut ancestor = element.traversal_parent();
270 while let Some(current) = ancestor {
271 if rule_inclusion == RuleInclusion::All {
272 if let Some(data) = current.borrow_data() {
273 if let Some(ancestor_style) = data.styles.get_primary() {
274 style = Some(ancestor_style.clone());
275 break;
276 }
277 }
278 }
279 if let Some(ref mut cache) = undisplayed_style_cache {
280 if let Some(s) = cache.get(¤t.opaque()) {
281 style = Some(s.clone());
282 break;
283 }
284 }
285 ancestors_requiring_style_resolution.push(current);
286 ancestor = current.traversal_parent();
287 }
288
289 if let Some(ancestor) = ancestor {
290 context.thread_local.bloom_filter.rebuild(ancestor);
291 context.thread_local.bloom_filter.push(ancestor);
292 }
293
294 let mut layout_parent_style = style.clone();
295 while let Some(style) = layout_parent_style.take() {
296 if !style.is_display_contents() {
297 layout_parent_style = Some(style);
298 break;
299 }
300
301 ancestor = ancestor.unwrap().traversal_parent();
302 layout_parent_style =
303 ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
304 }
305
306 for ancestor in ancestors_requiring_style_resolution.iter().rev() {
307 context.thread_local.bloom_filter.assert_complete(*ancestor);
308
309 let primary_style = StyleResolverForElement::new(
312 *ancestor,
313 context,
314 rule_inclusion,
315 PseudoElementResolution::IfApplicable,
316 )
317 .resolve_primary_style(style.as_deref(), layout_parent_style.as_deref());
318
319 let is_display_contents = primary_style.style().is_display_contents();
320
321 style = Some(primary_style.style.0);
322 if !is_display_contents {
323 layout_parent_style = style.clone();
324 }
325
326 if let Some(ref mut cache) = undisplayed_style_cache {
327 cache.insert(ancestor.opaque(), style.clone().unwrap());
328 }
329 context.thread_local.bloom_filter.push(*ancestor);
330 }
331
332 context.thread_local.bloom_filter.assert_complete(element);
333 let styles: ElementStyles = StyleResolverForElement::new(
334 element,
335 context,
336 rule_inclusion,
337 PseudoElementResolution::Force,
338 )
339 .resolve_style(style.as_deref(), layout_parent_style.as_deref())
340 .into();
341
342 if let Some(ref mut cache) = undisplayed_style_cache {
343 cache.insert(element.opaque(), styles.primary().clone());
344 }
345
346 styles
347}
348
349#[inline]
351#[allow(unsafe_code)]
352pub fn recalc_style_at<E, D, F>(
353 _traversal: &D,
354 traversal_data: &PerLevelTraversalData,
355 context: &mut StyleContext<E>,
356 element: E,
357 data: &mut ElementData,
358 note_child: F,
359) where
360 E: TElement,
361 D: DomTraversal<E>,
362 F: FnMut(E::ConcreteNode),
363{
364 let flags = context.shared.traversal_flags;
365 let is_initial_style = !data.has_styles();
366
367 context.thread_local.statistics.elements_traversed += 1;
368 debug_assert!(
369 flags.intersects(TraversalFlags::AnimationOnly)
370 || is_initial_style
371 || !element.has_snapshot()
372 || element.handled_snapshot(),
373 "Should've handled snapshots here already"
374 );
375
376 let restyle_kind = data.restyle_kind(&context.shared);
377 debug!(
378 "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
379 element,
380 restyle_kind,
381 element.has_dirty_descendants(),
382 data
383 );
384
385 let mut child_restyle_hint = RestyleHint::empty();
386
387 if let Some(restyle_kind) = restyle_kind {
389 child_restyle_hint = compute_style(traversal_data, context, element, data, restyle_kind);
390
391 if !element.matches_user_and_content_rules() {
392 child_restyle_hint |= RestyleHint::RECASCADE_SELF;
396 }
397
398 if data.styles.is_display_none() {
401 debug!(
402 "{:?} style is display:none - clearing data from descendants.",
403 element
404 );
405 unsafe {
406 clear_descendant_data(element);
407 }
408 }
409
410 notify_paint_worklet(context, data);
414 } else {
415 debug_assert!(data.has_styles());
416 data.set_traversed_without_styling();
417 }
418
419 debug_assert!(
424 flags.for_animation_only() || !data.hint.has_animation_hint(),
425 "animation restyle hint should be handled during \
426 animation-only restyles"
427 );
428 let mut propagated_hint = data.hint.propagate(&flags);
429 trace!(
430 "propagated_hint={:?}, restyle_requirement={:?}, \
431 is_display_none={:?}, implementing_pseudo={:?}",
432 propagated_hint,
433 child_restyle_hint,
434 data.styles.is_display_none(),
435 element.implemented_pseudo_element()
436 );
437
438 propagated_hint |= child_restyle_hint;
440
441 let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
442 element.has_animation_only_dirty_descendants()
443 } else {
444 element.has_dirty_descendants()
445 };
446
447 let mut traverse_children =
458 has_dirty_descendants_for_this_restyle || !propagated_hint.is_empty();
459
460 traverse_children = traverse_children && !data.styles.is_display_none();
461
462 if traverse_children {
464 note_children::<E, D, F>(
465 context,
466 element,
467 propagated_hint,
468 is_initial_style,
469 note_child,
470 );
471 }
472
473 if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
475 debug_assert!(!element.has_dirty_descendants());
476 debug_assert!(!element.has_animation_only_dirty_descendants());
477 }
478
479 clear_state_after_traversing(element, data, flags);
480}
481
482fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
483where
484 E: TElement,
485{
486 if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
487 debug_assert!(flags.for_animation_only());
488 data.clear_restyle_flags_and_damage();
489 unsafe {
490 element.unset_animation_only_dirty_descendants();
491 }
492 }
493}
494
495fn compute_style<E>(
496 traversal_data: &PerLevelTraversalData,
497 context: &mut StyleContext<E>,
498 element: E,
499 data: &mut ElementData,
500 kind: RestyleKind,
501) -> RestyleHint
502where
503 E: TElement,
504{
505 use crate::data::RestyleKind::*;
506
507 context.thread_local.statistics.elements_styled += 1;
508 debug!("compute_style: {:?} (kind={:?})", element, kind);
509
510 if data.has_styles() {
511 data.set_restyled();
512 }
513
514 let mut important_rules_changed = false;
515 let new_styles = match kind {
516 MatchAndCascade => {
517 debug_assert!(
518 !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
519 "MatchAndCascade shouldn't normally be processed during animation-only traversal"
520 );
521 context
523 .thread_local
524 .bloom_filter
525 .insert_parents_recovering(element, traversal_data.current_dom_depth);
526
527 context.thread_local.bloom_filter.assert_complete(element);
528 debug_assert_eq!(
529 context.thread_local.bloom_filter.matching_depth(),
530 traversal_data.current_dom_depth
531 );
532
533 important_rules_changed = true;
535
536 let mut target = StyleSharingTarget::new(element);
537
538 match target.share_style_if_possible(context) {
541 Some(shared_styles) => {
542 context.thread_local.statistics.styles_shared += 1;
543 shared_styles
544 },
545 None => {
546 context.thread_local.statistics.elements_matched += 1;
547 let new_styles = {
549 let mut resolver = StyleResolverForElement::new(
550 element,
551 context,
552 RuleInclusion::All,
553 PseudoElementResolution::IfApplicable,
554 );
555
556 resolver.resolve_style_with_default_parents()
557 };
558
559 context.thread_local.sharing_cache.insert_if_possible(
560 &element,
561 &new_styles.primary,
562 Some(&mut target),
563 traversal_data.current_dom_depth,
564 &context.shared,
565 );
566
567 new_styles
568 },
569 }
570 },
571 CascadeWithReplacements(flags) => {
572 let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
574 important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
575
576 let mut resolver = StyleResolverForElement::new(
577 element,
578 context,
579 RuleInclusion::All,
580 PseudoElementResolution::IfApplicable,
581 );
582
583 resolver.cascade_styles_with_default_parents(cascade_inputs)
584 },
585 CascadeOnly => {
586 let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
588
589 let new_styles = {
590 let mut resolver = StyleResolverForElement::new(
591 element,
592 context,
593 RuleInclusion::All,
594 PseudoElementResolution::IfApplicable,
595 );
596
597 resolver.cascade_styles_with_default_parents(cascade_inputs)
598 };
599
600 if !new_styles.primary.reused_via_rule_node {
616 context.thread_local.sharing_cache.insert_if_possible(
617 &element,
618 &new_styles.primary,
619 None,
620 traversal_data.current_dom_depth,
621 &context.shared,
622 );
623 }
624
625 new_styles
626 },
627 };
628
629 element.finish_restyle(context, data, new_styles, important_rules_changed)
630}
631
632#[cfg(feature = "servo")]
633fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
634where
635 E: TElement,
636{
637 use crate::values::generics::image::Image;
638 use style_traits::ToCss;
639
640 if let Some(ref values) = data.styles.primary {
645 for image in &values.get_background().background_image.0 {
646 let (name, arguments) = match *image {
647 Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
648 _ => continue,
649 };
650 let painter = match context.shared.registered_speculative_painters.get(name) {
651 Some(painter) => painter,
652 None => continue,
653 };
654 let properties = painter
655 .properties()
656 .iter()
657 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
658 .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
659 .collect();
660 let arguments = arguments
661 .iter()
662 .map(|argument| argument.to_css_string())
663 .collect();
664 debug!("Notifying paint worklet {}.", painter.name());
665 painter.speculatively_draw_a_paint_image(properties, arguments);
666 }
667 }
668}
669
670#[cfg(not(feature = "servo"))]
671fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
672where
673 E: TElement,
674{
675 }
677
678fn note_children<E, D, F>(
679 context: &mut StyleContext<E>,
680 element: E,
681 propagated_hint: RestyleHint,
682 is_initial_style: bool,
683 mut note_child: F,
684) where
685 E: TElement,
686 D: DomTraversal<E>,
687 F: FnMut(E::ConcreteNode),
688{
689 trace!("note_children: {:?}", element);
690 let flags = context.shared.traversal_flags;
691
692 for child_node in element.traversal_children() {
694 let Some(child) = child_node.as_element() else {
695 continue;
696 };
697
698 let mut child_data = child.mutate_data();
699 let mut child_data = child_data.as_mut().map(|d| &mut **d);
700 trace!(
701 " > {:?} -> {:?} + {:?}, pseudo: {:?}",
702 child,
703 child_data.as_ref().map(|d| d.hint),
704 propagated_hint,
705 child.implemented_pseudo_element()
706 );
707
708 if let Some(ref mut child_data) = child_data {
709 child_data.hint.insert(propagated_hint);
710
711 child_data.invalidate_style_if_needed(
716 child,
717 &context.shared,
718 Some(&context.thread_local.stack_limit_checker),
719 &mut context.thread_local.selector_caches,
720 );
721 }
722
723 if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
724 note_child(child_node);
725
726 if !is_initial_style {
732 if flags.for_animation_only() {
733 unsafe {
734 element.set_animation_only_dirty_descendants();
735 }
736 } else {
737 unsafe {
738 element.set_dirty_descendants();
739 }
740 }
741 }
742 }
743 }
744}
745
746pub unsafe fn clear_descendant_data<E>(root: E)
751where
752 E: TElement,
753{
754 let mut parents = SmallVec::<[E; 32]>::new();
755 parents.push(root);
756 while let Some(p) = parents.pop() {
757 for kid in p.traversal_children() {
758 if let Some(kid) = kid.as_element() {
759 if kid.has_data() {
765 kid.clear_data();
766 parents.push(kid);
767 }
768 }
769 }
770 }
771
772 root.clear_descendant_bits();
774}