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 hashbrown::HashMap;
18use selectors::matching::SelectorCaches;
19#[cfg(feature = "gecko")]
20use selectors::parser::PseudoElement as PseudoElementTrait;
21use smallvec::SmallVec;
22
23pub type UndisplayedStyleCache =
25 HashMap<selectors::OpaqueElement, servo_arc::Arc<crate::properties::ComputedValues>>;
26
27pub struct PreTraverseToken<E: TElement>(Option<E>);
30impl<E: TElement> PreTraverseToken<E> {
31 pub fn should_traverse(&self) -> bool {
33 self.0.is_some()
34 }
35
36 pub(crate) fn traversal_root(self) -> Option<E> {
38 self.0
39 }
40}
41
42pub trait DomTraversal<E: TElement>: Sync {
45 fn process_preorder<F>(
50 &self,
51 context: &mut StyleContext<E>,
52 node: E::ConcreteNode,
53 note_child: F,
54 ) where
55 F: FnMut(E::ConcreteNode);
56
57 fn process_postorder(&self, contect: &mut StyleContext<E>, node: E::ConcreteNode);
61
62 fn needs_postorder_traversal() -> bool {
67 true
68 }
69
70 fn handle_postorder_traversal(
84 &self,
85 context: &mut StyleContext<E>,
86 root: OpaqueNode,
87 mut node: E::ConcreteNode,
88 children_to_process: isize,
89 ) {
90 if !Self::needs_postorder_traversal() {
92 return;
93 }
94
95 if children_to_process == 0 {
96 loop {
98 self.process_postorder(context, node);
99 if node.opaque() == root {
100 break;
101 }
102 let parent = node.traversal_parent().unwrap();
103 let remaining = parent.did_process_child();
104 if remaining != 0 {
105 break;
109 }
110
111 node = parent.as_node();
112 }
113 } else {
114 node.as_element()
117 .unwrap()
118 .store_children_to_process(children_to_process);
119 }
120 }
121
122 fn pre_traverse(root: E, shared_context: &SharedStyleContext) -> PreTraverseToken<E> {
127 use crate::invalidation::element::state_and_attributes::propagate_dirty_bit_up_to;
128
129 let traversal_flags = shared_context.traversal_flags;
130
131 let mut data = root.mutate_data();
132 let mut data = data.as_deref_mut();
133
134 if let Some(ref mut data) = data
135 && !traversal_flags.for_animation_only()
136 {
137 let invalidation_result = data.invalidate_style_if_needed(
140 root,
141 shared_context,
142 None,
143 &mut SelectorCaches::default(),
144 );
145
146 if invalidation_result.has_invalidated_siblings() {
147 let actual_root = root.as_node().parent_element_or_host().expect(
148 "How in the world can you invalidate \
149 siblings without a parent?",
150 );
151 propagate_dirty_bit_up_to(actual_root, root);
152 return PreTraverseToken(Some(actual_root));
153 }
154 }
155
156 let should_traverse =
157 Self::element_needs_traversal(root, traversal_flags, data.as_mut().map(|d| &**d));
158
159 if !should_traverse && data.is_some() {
162 clear_state_after_traversing(root, data.unwrap(), traversal_flags);
163 }
164
165 PreTraverseToken(if should_traverse { Some(root) } else { None })
166 }
167
168 fn element_needs_traversal(
170 el: E,
171 traversal_flags: TraversalFlags,
172 data: Option<&ElementData>,
173 ) -> bool {
174 debug!(
175 "element_needs_traversal({:?}, {:?}, {:?})",
176 el, traversal_flags, data
177 );
178
179 let data = match data {
181 Some(d) if d.has_styles() => d,
182 _ => return true,
183 };
184
185 if traversal_flags.for_animation_only() {
186 return el.has_animation_only_dirty_descendants()
189 || data.hint.has_animation_hint_or_recascade();
190 }
191
192 if el.has_dirty_descendants() {
195 return true;
196 }
197
198 if !data.hint.is_empty() {
206 return true;
207 }
208
209 if cfg!(feature = "servo") && !data.damage.is_empty() {
213 return true;
214 }
215
216 trace!("{:?} doesn't need traversal", el);
217 false
218 }
219
220 fn shared_context(&self) -> &SharedStyleContext<'_>;
222}
223
224pub fn resolve_style<E>(
229 context: &mut StyleContext<E>,
230 element: E,
231 rule_inclusion: RuleInclusion,
232 pseudo: Option<&PseudoElement>,
233 mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
234) -> ElementStyles
235where
236 E: TElement,
237{
238 debug_assert!(
239 rule_inclusion == RuleInclusion::DefaultOnly
240 || pseudo.is_some_and(|p| p.is_before_or_after())
241 || element.borrow_data().is_none_or(|d| !d.has_styles()),
242 "Why are we here?"
243 );
244 debug_assert!(
245 rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
246 "can't use the cache for default styles only"
247 );
248
249 let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
250
251 context.thread_local.bloom_filter.clear();
253
254 let mut style = None;
255 let mut ancestor = element.traversal_parent();
256 while let Some(current) = ancestor {
257 if rule_inclusion == RuleInclusion::All
258 && let Some(data) = current.borrow_data()
259 && let Some(ancestor_style) = data.styles.get_primary()
260 {
261 style = Some(ancestor_style.clone());
262 break;
263 }
264 if let Some(ref mut cache) = undisplayed_style_cache
265 && let Some(s) = cache.get(¤t.opaque())
266 {
267 style = Some(s.clone());
268 break;
269 }
270 ancestors_requiring_style_resolution.push(current);
271 ancestor = current.traversal_parent();
272 }
273
274 if let Some(ancestor) = ancestor {
275 context.thread_local.bloom_filter.rebuild(ancestor);
276 context.thread_local.bloom_filter.push(ancestor);
277 }
278
279 let mut layout_parent_style = style.clone();
280 while let Some(style) = layout_parent_style.take() {
281 if !style.is_display_contents() {
282 layout_parent_style = Some(style);
283 break;
284 }
285
286 ancestor = ancestor.unwrap().traversal_parent();
287 layout_parent_style =
288 ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
289 }
290
291 for ancestor in ancestors_requiring_style_resolution.iter().rev() {
292 context.thread_local.bloom_filter.assert_complete(*ancestor);
293 context.thread_local.current_dom_depth = context.thread_local.bloom_filter.matching_depth();
294
295 let primary_style = StyleResolverForElement::new(
298 *ancestor,
299 context,
300 rule_inclusion,
301 PseudoElementResolution::IfApplicable,
302 )
303 .resolve_primary_style(style.as_deref(), layout_parent_style.as_deref());
304
305 let is_display_contents = primary_style.style().is_display_contents();
306
307 style = Some(primary_style.style.0);
308 if !is_display_contents {
309 layout_parent_style = style.clone();
310 }
311
312 if let Some(ref mut cache) = undisplayed_style_cache {
313 cache.insert(ancestor.opaque(), style.clone().unwrap());
314 }
315 context.thread_local.bloom_filter.push(*ancestor);
316 }
317
318 context.thread_local.bloom_filter.assert_complete(element);
319 context.thread_local.current_dom_depth = context.thread_local.bloom_filter.matching_depth();
320 let styles: ElementStyles = StyleResolverForElement::new(
321 element,
322 context,
323 rule_inclusion,
324 PseudoElementResolution::Force,
325 )
326 .resolve_style(style.as_deref(), layout_parent_style.as_deref())
327 .into();
328
329 if let Some(ref mut cache) = undisplayed_style_cache {
330 cache.insert(element.opaque(), styles.primary().clone());
331 }
332
333 styles
334}
335
336#[inline]
338#[allow(unsafe_code)]
339pub fn recalc_style_at<E, D, F>(
340 _traversal: &D,
341 context: &mut StyleContext<E>,
342 element: E,
343 data: &mut ElementData,
344 note_child: F,
345) where
346 E: TElement,
347 D: DomTraversal<E>,
348 F: FnMut(E::ConcreteNode),
349{
350 let flags = context.shared.traversal_flags;
351 let is_initial_style = !data.has_styles();
352
353 context.thread_local.statistics.elements_traversed += 1;
354 debug_assert!(
355 flags.intersects(TraversalFlags::AnimationOnly)
356 || is_initial_style
357 || !element.has_snapshot()
358 || element.handled_snapshot(),
359 "Should've handled snapshots here already"
360 );
361
362 let restyle_kind = data.restyle_kind(context.shared);
363 debug!(
364 "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
365 element,
366 restyle_kind,
367 element.has_dirty_descendants(),
368 data
369 );
370
371 let mut child_restyle_hint = RestyleHint::empty();
372
373 if let Some(restyle_kind) = restyle_kind {
375 child_restyle_hint = compute_style(context, element, data, restyle_kind);
376
377 if !element.matches_user_and_content_rules() {
378 child_restyle_hint |= RestyleHint::RECASCADE_SELF;
382 }
383
384 if data.styles.is_display_none() {
387 debug!(
388 "{:?} style is display:none - clearing data from descendants.",
389 element
390 );
391 unsafe {
392 clear_descendant_data(element);
393 }
394 }
395
396 notify_paint_worklet(context, data);
400 } else {
401 debug_assert!(data.has_styles());
402 data.set_traversed_without_styling();
403 }
404
405 debug_assert!(
410 flags.for_animation_only() || !data.hint.has_animation_hint(),
411 "animation restyle hint should be handled during \
412 animation-only restyles"
413 );
414 let mut propagated_hint = data.hint.propagate(&flags);
415 trace!(
416 "propagated_hint={:?}, restyle_requirement={:?}, \
417 is_display_none={:?}, implementing_pseudo={:?}",
418 propagated_hint,
419 child_restyle_hint,
420 data.styles.is_display_none(),
421 element.implemented_pseudo_element()
422 );
423
424 propagated_hint |= child_restyle_hint;
426
427 let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
428 element.has_animation_only_dirty_descendants()
429 } else {
430 element.has_dirty_descendants()
431 };
432
433 let mut traverse_children =
444 has_dirty_descendants_for_this_restyle || !propagated_hint.is_empty();
445
446 traverse_children = traverse_children && !data.styles.is_display_none();
447
448 if traverse_children {
450 note_children::<E, D, F>(
451 context,
452 element,
453 propagated_hint,
454 is_initial_style,
455 note_child,
456 );
457 }
458
459 if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
461 debug_assert!(!element.has_dirty_descendants());
462 debug_assert!(!element.has_animation_only_dirty_descendants());
463 }
464
465 clear_state_after_traversing(element, data, flags);
466}
467
468fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
469where
470 E: TElement,
471{
472 if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
473 debug_assert!(flags.for_animation_only());
474 data.clear_restyle_flags_and_damage();
475 unsafe {
476 element.unset_animation_only_dirty_descendants();
477 }
478 }
479}
480
481fn compute_style<E>(
482 context: &mut StyleContext<E>,
483 element: E,
484 data: &mut ElementData,
485 kind: RestyleKind,
486) -> RestyleHint
487where
488 E: TElement,
489{
490 use crate::data::RestyleKind::*;
491
492 context.thread_local.statistics.elements_styled += 1;
493 debug!("compute_style: {:?} (kind={:?})", element, kind);
494
495 if data.has_styles() {
496 data.set_restyled();
497 }
498
499 let mut important_rules_changed = false;
500 let new_styles = match kind {
501 MatchAndCascade => {
502 debug_assert!(
503 !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
504 "MatchAndCascade shouldn't normally be processed during animation-only traversal"
505 );
506 context
508 .thread_local
509 .bloom_filter
510 .insert_parents_recovering(element, context.thread_local.current_dom_depth);
511
512 context.thread_local.bloom_filter.assert_complete(element);
513 debug_assert_eq!(
514 context.thread_local.bloom_filter.matching_depth(),
515 context.thread_local.current_dom_depth
516 );
517
518 important_rules_changed = true;
520
521 let mut target = StyleSharingTarget::new(element);
522
523 match target.share_style_if_possible(context) {
526 Some(shared_styles) => {
527 context.thread_local.statistics.styles_shared += 1;
528 shared_styles
529 },
530 None => {
531 context.thread_local.statistics.elements_matched += 1;
532 let new_styles = {
534 let mut resolver = StyleResolverForElement::new(
535 element,
536 context,
537 RuleInclusion::All,
538 PseudoElementResolution::IfApplicable,
539 );
540
541 resolver.resolve_style_with_default_parents()
542 };
543
544 let dom_depth = context.thread_local.current_dom_depth;
545 context.thread_local.sharing_cache.insert_if_possible(
546 &element,
547 &new_styles.primary,
548 Some(&mut target),
549 dom_depth,
550 context.shared,
551 );
552
553 new_styles
554 },
555 }
556 },
557 CascadeWithReplacements(flags) => {
558 let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
560 important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
561
562 let mut resolver = StyleResolverForElement::new(
563 element,
564 context,
565 RuleInclusion::All,
566 PseudoElementResolution::IfApplicable,
567 );
568
569 resolver.cascade_styles_with_default_parents(cascade_inputs)
570 },
571 CascadeOnly => {
572 let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
574
575 let new_styles = {
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
586 if !new_styles.primary.reused_via_rule_node {
602 context.thread_local.sharing_cache.insert_if_possible(
603 &element,
604 &new_styles.primary,
605 None,
606 context.thread_local.current_dom_depth,
607 context.shared,
608 );
609 }
610
611 new_styles
612 },
613 };
614
615 element.finish_restyle(context, data, new_styles, important_rules_changed)
616}
617
618#[cfg(feature = "servo")]
619fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
620where
621 E: TElement,
622{
623 use crate::values::generics::image::Image;
624 use style_traits::ToCss;
625
626 if let Some(ref values) = data.styles.primary {
631 for image in &values.get_background().background_image.0 {
632 let (name, arguments) = match *image {
633 Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
634 _ => continue,
635 };
636 let painter = match context.shared.registered_speculative_painters.get(name) {
637 Some(painter) => painter,
638 None => continue,
639 };
640 let properties = painter
641 .properties()
642 .iter()
643 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
644 .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
645 .collect();
646 let arguments = arguments
647 .iter()
648 .map(|argument| argument.to_css_string())
649 .collect();
650 debug!("Notifying paint worklet {}.", painter.name());
651 painter.speculatively_draw_a_paint_image(properties, arguments);
652 }
653 }
654}
655
656#[cfg(not(feature = "servo"))]
657fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
658where
659 E: TElement,
660{
661 }
663
664fn note_children<E, D, F>(
665 context: &mut StyleContext<E>,
666 element: E,
667 propagated_hint: RestyleHint,
668 is_initial_style: bool,
669 mut note_child: F,
670) where
671 E: TElement,
672 D: DomTraversal<E>,
673 F: FnMut(E::ConcreteNode),
674{
675 trace!("note_children: {:?}", element);
676 let flags = context.shared.traversal_flags;
677
678 for child_node in element.traversal_children() {
680 let Some(child) = child_node.as_element() else {
681 continue;
682 };
683
684 let mut child_data = child.mutate_data();
685 let mut child_data = child_data.as_deref_mut();
686 trace!(
687 " > {:?} -> {:?} + {:?}, pseudo: {:?}",
688 child,
689 child_data.as_ref().map(|d| d.hint),
690 propagated_hint,
691 child.implemented_pseudo_element()
692 );
693
694 if let Some(ref mut child_data) = child_data {
695 child_data.hint.insert(propagated_hint);
696
697 child_data.invalidate_style_if_needed(
702 child,
703 context.shared,
704 Some(&context.thread_local.stack_limit_checker),
705 &mut context.thread_local.selector_caches,
706 );
707 }
708
709 if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
710 note_child(child_node);
711
712 if !is_initial_style {
718 if flags.for_animation_only() {
719 unsafe {
720 element.set_animation_only_dirty_descendants();
721 }
722 } else {
723 unsafe {
724 element.set_dirty_descendants();
725 }
726 }
727 }
728 }
729 }
730}
731
732pub unsafe fn clear_descendant_data<E>(root: E)
737where
738 E: TElement,
739{
740 let mut parents = SmallVec::<[E; 32]>::new();
741 parents.push(root);
742 while let Some(p) = parents.pop() {
743 for kid in p.traversal_children() {
744 if let Some(kid) = kid.as_element() {
745 if kid.has_data() {
751 unsafe {
752 kid.clear_data();
753 }
754 parents.push(kid);
755 }
756 }
757 }
758 }
759
760 unsafe {
762 root.clear_descendant_bits();
763 }
764}