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
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 if !traversal_flags.for_animation_only() {
136 let invalidation_result = data.invalidate_style_if_needed(
139 root,
140 shared_context,
141 None,
142 &mut SelectorCaches::default(),
143 );
144
145 if invalidation_result.has_invalidated_siblings() {
146 let actual_root = root.as_node().parent_element_or_host().expect(
147 "How in the world can you invalidate \
148 siblings without a parent?",
149 );
150 propagate_dirty_bit_up_to(actual_root, root);
151 return PreTraverseToken(Some(actual_root));
152 }
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 if let Some(data) = current.borrow_data() {
259 if let Some(ancestor_style) = data.styles.get_primary() {
260 style = Some(ancestor_style.clone());
261 break;
262 }
263 }
264 }
265 if let Some(ref mut cache) = undisplayed_style_cache {
266 if let Some(s) = cache.get(¤t.opaque()) {
267 style = Some(s.clone());
268 break;
269 }
270 }
271 ancestors_requiring_style_resolution.push(current);
272 ancestor = current.traversal_parent();
273 }
274
275 if let Some(ancestor) = ancestor {
276 context.thread_local.bloom_filter.rebuild(ancestor);
277 context.thread_local.bloom_filter.push(ancestor);
278 }
279
280 let mut layout_parent_style = style.clone();
281 while let Some(style) = layout_parent_style.take() {
282 if !style.is_display_contents() {
283 layout_parent_style = Some(style);
284 break;
285 }
286
287 ancestor = ancestor.unwrap().traversal_parent();
288 layout_parent_style =
289 ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
290 }
291
292 for ancestor in ancestors_requiring_style_resolution.iter().rev() {
293 context.thread_local.bloom_filter.assert_complete(*ancestor);
294 context.thread_local.current_dom_depth = context.thread_local.bloom_filter.matching_depth();
295
296 let primary_style = StyleResolverForElement::new(
299 *ancestor,
300 context,
301 rule_inclusion,
302 PseudoElementResolution::IfApplicable,
303 )
304 .resolve_primary_style(style.as_deref(), layout_parent_style.as_deref());
305
306 let is_display_contents = primary_style.style().is_display_contents();
307
308 style = Some(primary_style.style.0);
309 if !is_display_contents {
310 layout_parent_style = style.clone();
311 }
312
313 if let Some(ref mut cache) = undisplayed_style_cache {
314 cache.insert(ancestor.opaque(), style.clone().unwrap());
315 }
316 context.thread_local.bloom_filter.push(*ancestor);
317 }
318
319 context.thread_local.bloom_filter.assert_complete(element);
320 context.thread_local.current_dom_depth = context.thread_local.bloom_filter.matching_depth();
321 let styles: ElementStyles = StyleResolverForElement::new(
322 element,
323 context,
324 rule_inclusion,
325 PseudoElementResolution::Force,
326 )
327 .resolve_style(style.as_deref(), layout_parent_style.as_deref())
328 .into();
329
330 if let Some(ref mut cache) = undisplayed_style_cache {
331 cache.insert(element.opaque(), styles.primary().clone());
332 }
333
334 styles
335}
336
337#[inline]
339#[allow(unsafe_code)]
340pub fn recalc_style_at<E, D, F>(
341 _traversal: &D,
342 context: &mut StyleContext<E>,
343 element: E,
344 data: &mut ElementData,
345 note_child: F,
346) where
347 E: TElement,
348 D: DomTraversal<E>,
349 F: FnMut(E::ConcreteNode),
350{
351 let flags = context.shared.traversal_flags;
352 let is_initial_style = !data.has_styles();
353
354 context.thread_local.statistics.elements_traversed += 1;
355 debug_assert!(
356 flags.intersects(TraversalFlags::AnimationOnly)
357 || is_initial_style
358 || !element.has_snapshot()
359 || element.handled_snapshot(),
360 "Should've handled snapshots here already"
361 );
362
363 let restyle_kind = data.restyle_kind(context.shared);
364 debug!(
365 "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
366 element,
367 restyle_kind,
368 element.has_dirty_descendants(),
369 data
370 );
371
372 let mut child_restyle_hint = RestyleHint::empty();
373
374 if let Some(restyle_kind) = restyle_kind {
376 child_restyle_hint = compute_style(context, element, data, restyle_kind);
377
378 if !element.matches_user_and_content_rules() {
379 child_restyle_hint |= RestyleHint::RECASCADE_SELF;
383 }
384
385 if data.styles.is_display_none() {
388 debug!(
389 "{:?} style is display:none - clearing data from descendants.",
390 element
391 );
392 unsafe {
393 clear_descendant_data(element);
394 }
395 }
396
397 notify_paint_worklet(context, data);
401 } else {
402 debug_assert!(data.has_styles());
403 data.set_traversed_without_styling();
404 }
405
406 debug_assert!(
411 flags.for_animation_only() || !data.hint.has_animation_hint(),
412 "animation restyle hint should be handled during \
413 animation-only restyles"
414 );
415 let mut propagated_hint = data.hint.propagate(&flags);
416 trace!(
417 "propagated_hint={:?}, restyle_requirement={:?}, \
418 is_display_none={:?}, implementing_pseudo={:?}",
419 propagated_hint,
420 child_restyle_hint,
421 data.styles.is_display_none(),
422 element.implemented_pseudo_element()
423 );
424
425 propagated_hint |= child_restyle_hint;
427
428 let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
429 element.has_animation_only_dirty_descendants()
430 } else {
431 element.has_dirty_descendants()
432 };
433
434 let mut traverse_children =
445 has_dirty_descendants_for_this_restyle || !propagated_hint.is_empty();
446
447 traverse_children = traverse_children && !data.styles.is_display_none();
448
449 if traverse_children {
451 note_children::<E, D, F>(
452 context,
453 element,
454 propagated_hint,
455 is_initial_style,
456 note_child,
457 );
458 }
459
460 if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
462 debug_assert!(!element.has_dirty_descendants());
463 debug_assert!(!element.has_animation_only_dirty_descendants());
464 }
465
466 clear_state_after_traversing(element, data, flags);
467}
468
469fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
470where
471 E: TElement,
472{
473 if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
474 debug_assert!(flags.for_animation_only());
475 data.clear_restyle_flags_and_damage();
476 unsafe {
477 element.unset_animation_only_dirty_descendants();
478 }
479 }
480}
481
482fn compute_style<E>(
483 context: &mut StyleContext<E>,
484 element: E,
485 data: &mut ElementData,
486 kind: RestyleKind,
487) -> RestyleHint
488where
489 E: TElement,
490{
491 use crate::data::RestyleKind::*;
492
493 context.thread_local.statistics.elements_styled += 1;
494 debug!("compute_style: {:?} (kind={:?})", element, kind);
495
496 if data.has_styles() {
497 data.set_restyled();
498 }
499
500 let mut important_rules_changed = false;
501 let new_styles = match kind {
502 MatchAndCascade => {
503 debug_assert!(
504 !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
505 "MatchAndCascade shouldn't normally be processed during animation-only traversal"
506 );
507 context
509 .thread_local
510 .bloom_filter
511 .insert_parents_recovering(element, context.thread_local.current_dom_depth);
512
513 context.thread_local.bloom_filter.assert_complete(element);
514 debug_assert_eq!(
515 context.thread_local.bloom_filter.matching_depth(),
516 context.thread_local.current_dom_depth
517 );
518
519 important_rules_changed = true;
521
522 let mut target = StyleSharingTarget::new(element);
523
524 match target.share_style_if_possible(context) {
527 Some(shared_styles) => {
528 context.thread_local.statistics.styles_shared += 1;
529 shared_styles
530 },
531 None => {
532 context.thread_local.statistics.elements_matched += 1;
533 let new_styles = {
535 let mut resolver = StyleResolverForElement::new(
536 element,
537 context,
538 RuleInclusion::All,
539 PseudoElementResolution::IfApplicable,
540 );
541
542 resolver.resolve_style_with_default_parents()
543 };
544
545 let dom_depth = context.thread_local.current_dom_depth;
546 context.thread_local.sharing_cache.insert_if_possible(
547 &element,
548 &new_styles.primary,
549 Some(&mut target),
550 dom_depth,
551 context.shared,
552 );
553
554 new_styles
555 },
556 }
557 },
558 CascadeWithReplacements(flags) => {
559 let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
561 important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
562
563 let mut resolver = StyleResolverForElement::new(
564 element,
565 context,
566 RuleInclusion::All,
567 PseudoElementResolution::IfApplicable,
568 );
569
570 resolver.cascade_styles_with_default_parents(cascade_inputs)
571 },
572 CascadeOnly => {
573 let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
575
576 let new_styles = {
577 let mut resolver = StyleResolverForElement::new(
578 element,
579 context,
580 RuleInclusion::All,
581 PseudoElementResolution::IfApplicable,
582 );
583
584 resolver.cascade_styles_with_default_parents(cascade_inputs)
585 };
586
587 if !new_styles.primary.reused_via_rule_node {
603 context.thread_local.sharing_cache.insert_if_possible(
604 &element,
605 &new_styles.primary,
606 None,
607 context.thread_local.current_dom_depth,
608 context.shared,
609 );
610 }
611
612 new_styles
613 },
614 };
615
616 element.finish_restyle(context, data, new_styles, important_rules_changed)
617}
618
619#[cfg(feature = "servo")]
620fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
621where
622 E: TElement,
623{
624 use crate::values::generics::image::Image;
625 use style_traits::ToCss;
626
627 if let Some(ref values) = data.styles.primary {
632 for image in &values.get_background().background_image.0 {
633 let (name, arguments) = match *image {
634 Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
635 _ => continue,
636 };
637 let painter = match context.shared.registered_speculative_painters.get(name) {
638 Some(painter) => painter,
639 None => continue,
640 };
641 let properties = painter
642 .properties()
643 .iter()
644 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
645 .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
646 .collect();
647 let arguments = arguments
648 .iter()
649 .map(|argument| argument.to_css_string())
650 .collect();
651 debug!("Notifying paint worklet {}.", painter.name());
652 painter.speculatively_draw_a_paint_image(properties, arguments);
653 }
654 }
655}
656
657#[cfg(not(feature = "servo"))]
658fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
659where
660 E: TElement,
661{
662 }
664
665fn note_children<E, D, F>(
666 context: &mut StyleContext<E>,
667 element: E,
668 propagated_hint: RestyleHint,
669 is_initial_style: bool,
670 mut note_child: F,
671) where
672 E: TElement,
673 D: DomTraversal<E>,
674 F: FnMut(E::ConcreteNode),
675{
676 trace!("note_children: {:?}", element);
677 let flags = context.shared.traversal_flags;
678
679 for child_node in element.traversal_children() {
681 let Some(child) = child_node.as_element() else {
682 continue;
683 };
684
685 let mut child_data = child.mutate_data();
686 let mut child_data = child_data.as_deref_mut();
687 trace!(
688 " > {:?} -> {:?} + {:?}, pseudo: {:?}",
689 child,
690 child_data.as_ref().map(|d| d.hint),
691 propagated_hint,
692 child.implemented_pseudo_element()
693 );
694
695 if let Some(ref mut child_data) = child_data {
696 child_data.hint.insert(propagated_hint);
697
698 child_data.invalidate_style_if_needed(
703 child,
704 context.shared,
705 Some(&context.thread_local.stack_limit_checker),
706 &mut context.thread_local.selector_caches,
707 );
708 }
709
710 if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
711 note_child(child_node);
712
713 if !is_initial_style {
719 if flags.for_animation_only() {
720 unsafe {
721 element.set_animation_only_dirty_descendants();
722 }
723 } else {
724 unsafe {
725 element.set_dirty_descendants();
726 }
727 }
728 }
729 }
730 }
731}
732
733pub unsafe fn clear_descendant_data<E>(root: E)
738where
739 E: TElement,
740{
741 let mut parents = SmallVec::<[E; 32]>::new();
742 parents.push(root);
743 while let Some(p) = parents.pop() {
744 for kid in p.traversal_children() {
745 if let Some(kid) = kid.as_element() {
746 if kid.has_data() {
752 unsafe {
753 kid.clear_data();
754 }
755 parents.push(kid);
756 }
757 }
758 }
759 }
760
761 unsafe {
763 root.clear_descendant_bits();
764 }
765}