1use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext};
8use crate::data::{ElementData, ElementStyles, RestyleKind};
9use crate::dom::{NodeInfo, 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 text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool {
186 debug_assert!(node.is_text_node());
187 false
188 }
189
190 fn element_needs_traversal(
192 el: E,
193 traversal_flags: TraversalFlags,
194 data: Option<&ElementData>,
195 ) -> bool {
196 debug!(
197 "element_needs_traversal({:?}, {:?}, {:?})",
198 el, traversal_flags, data
199 );
200
201 let data = match data {
203 Some(d) if d.has_styles() => d,
204 _ => return true,
205 };
206
207 if traversal_flags.for_animation_only() {
208 return el.has_animation_only_dirty_descendants()
211 || data.hint.has_animation_hint_or_recascade();
212 }
213
214 if el.has_dirty_descendants() {
217 return true;
218 }
219
220 if !data.hint.is_empty() {
228 return true;
229 }
230
231 if cfg!(feature = "servo") && !data.damage.is_empty() {
235 return true;
236 }
237
238 trace!("{:?} doesn't need traversal", el);
239 false
240 }
241
242 fn shared_context(&self) -> &SharedStyleContext<'_>;
244}
245
246pub fn resolve_style<E>(
251 context: &mut StyleContext<E>,
252 element: E,
253 rule_inclusion: RuleInclusion,
254 pseudo: Option<&PseudoElement>,
255 mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
256) -> ElementStyles
257where
258 E: TElement,
259{
260 debug_assert!(
261 rule_inclusion == RuleInclusion::DefaultOnly
262 || pseudo.map_or(false, |p| p.is_before_or_after())
263 || element.borrow_data().map_or(true, |d| !d.has_styles()),
264 "Why are we here?"
265 );
266 debug_assert!(
267 rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
268 "can't use the cache for default styles only"
269 );
270
271 let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
272
273 context.thread_local.bloom_filter.clear();
275
276 let mut style = None;
277 let mut ancestor = element.traversal_parent();
278 while let Some(current) = ancestor {
279 if rule_inclusion == RuleInclusion::All {
280 if let Some(data) = current.borrow_data() {
281 if let Some(ancestor_style) = data.styles.get_primary() {
282 style = Some(ancestor_style.clone());
283 break;
284 }
285 }
286 }
287 if let Some(ref mut cache) = undisplayed_style_cache {
288 if let Some(s) = cache.get(¤t.opaque()) {
289 style = Some(s.clone());
290 break;
291 }
292 }
293 ancestors_requiring_style_resolution.push(current);
294 ancestor = current.traversal_parent();
295 }
296
297 if let Some(ancestor) = ancestor {
298 context.thread_local.bloom_filter.rebuild(ancestor);
299 context.thread_local.bloom_filter.push(ancestor);
300 }
301
302 let mut layout_parent_style = style.clone();
303 while let Some(style) = layout_parent_style.take() {
304 if !style.is_display_contents() {
305 layout_parent_style = Some(style);
306 break;
307 }
308
309 ancestor = ancestor.unwrap().traversal_parent();
310 layout_parent_style =
311 ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
312 }
313
314 for ancestor in ancestors_requiring_style_resolution.iter().rev() {
315 context.thread_local.bloom_filter.assert_complete(*ancestor);
316
317 let primary_style = StyleResolverForElement::new(
320 *ancestor,
321 context,
322 rule_inclusion,
323 PseudoElementResolution::IfApplicable,
324 )
325 .resolve_primary_style(style.as_deref(), layout_parent_style.as_deref());
326
327 let is_display_contents = primary_style.style().is_display_contents();
328
329 style = Some(primary_style.style.0);
330 if !is_display_contents {
331 layout_parent_style = style.clone();
332 }
333
334 if let Some(ref mut cache) = undisplayed_style_cache {
335 cache.insert(ancestor.opaque(), style.clone().unwrap());
336 }
337 context.thread_local.bloom_filter.push(*ancestor);
338 }
339
340 context.thread_local.bloom_filter.assert_complete(element);
341 let styles: ElementStyles = StyleResolverForElement::new(
342 element,
343 context,
344 rule_inclusion,
345 PseudoElementResolution::Force,
346 )
347 .resolve_style(style.as_deref(), layout_parent_style.as_deref())
348 .into();
349
350 if let Some(ref mut cache) = undisplayed_style_cache {
351 cache.insert(element.opaque(), styles.primary().clone());
352 }
353
354 styles
355}
356
357#[inline]
359#[allow(unsafe_code)]
360pub fn recalc_style_at<E, D, F>(
361 _traversal: &D,
362 traversal_data: &PerLevelTraversalData,
363 context: &mut StyleContext<E>,
364 element: E,
365 data: &mut ElementData,
366 note_child: F,
367) where
368 E: TElement,
369 D: DomTraversal<E>,
370 F: FnMut(E::ConcreteNode),
371{
372 let flags = context.shared.traversal_flags;
373 let is_initial_style = !data.has_styles();
374
375 context.thread_local.statistics.elements_traversed += 1;
376 debug_assert!(
377 flags.intersects(TraversalFlags::AnimationOnly)
378 || is_initial_style
379 || !element.has_snapshot()
380 || element.handled_snapshot(),
381 "Should've handled snapshots here already"
382 );
383
384 let restyle_kind = data.restyle_kind(&context.shared);
385 debug!(
386 "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
387 element,
388 restyle_kind,
389 element.has_dirty_descendants(),
390 data
391 );
392
393 let mut child_restyle_hint = RestyleHint::empty();
394
395 if let Some(restyle_kind) = restyle_kind {
397 child_restyle_hint = compute_style(traversal_data, context, element, data, restyle_kind);
398
399 if !element.matches_user_and_content_rules() {
400 child_restyle_hint |= RestyleHint::RECASCADE_SELF;
404 }
405
406 if data.styles.is_display_none() {
409 debug!(
410 "{:?} style is display:none - clearing data from descendants.",
411 element
412 );
413 unsafe {
414 clear_descendant_data(element);
415 }
416 }
417
418 notify_paint_worklet(context, data);
422 } else {
423 debug_assert!(data.has_styles());
424 data.set_traversed_without_styling();
425 }
426
427 debug_assert!(
432 flags.for_animation_only() || !data.hint.has_animation_hint(),
433 "animation restyle hint should be handled during \
434 animation-only restyles"
435 );
436 let mut propagated_hint = data.hint.propagate(&flags);
437 trace!(
438 "propagated_hint={:?}, restyle_requirement={:?}, \
439 is_display_none={:?}, implementing_pseudo={:?}",
440 propagated_hint,
441 child_restyle_hint,
442 data.styles.is_display_none(),
443 element.implemented_pseudo_element()
444 );
445
446 propagated_hint |= child_restyle_hint;
448
449 let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
450 element.has_animation_only_dirty_descendants()
451 } else {
452 element.has_dirty_descendants()
453 };
454
455 let mut traverse_children = has_dirty_descendants_for_this_restyle
466 || !propagated_hint.is_empty();
467
468 traverse_children = traverse_children && !data.styles.is_display_none();
469
470 if traverse_children {
472 note_children::<E, D, F>(
473 context,
474 element,
475 data,
476 propagated_hint,
477 is_initial_style,
478 note_child,
479 );
480 }
481
482 if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
484 debug_assert!(!element.has_dirty_descendants());
485 debug_assert!(!element.has_animation_only_dirty_descendants());
486 }
487
488 clear_state_after_traversing(element, data, flags);
489}
490
491fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
492where
493 E: TElement,
494{
495 if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
496 debug_assert!(flags.for_animation_only());
497 data.clear_restyle_flags_and_damage();
498 unsafe {
499 element.unset_animation_only_dirty_descendants();
500 }
501 }
502}
503
504fn compute_style<E>(
505 traversal_data: &PerLevelTraversalData,
506 context: &mut StyleContext<E>,
507 element: E,
508 data: &mut ElementData,
509 kind: RestyleKind,
510) -> RestyleHint
511where
512 E: TElement,
513{
514 use crate::data::RestyleKind::*;
515
516 context.thread_local.statistics.elements_styled += 1;
517 debug!("compute_style: {:?} (kind={:?})", element, kind);
518
519 if data.has_styles() {
520 data.set_restyled();
521 }
522
523 let mut important_rules_changed = false;
524 let new_styles = match kind {
525 MatchAndCascade => {
526 debug_assert!(
527 !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
528 "MatchAndCascade shouldn't normally be processed during animation-only traversal"
529 );
530 context
532 .thread_local
533 .bloom_filter
534 .insert_parents_recovering(element, traversal_data.current_dom_depth);
535
536 context.thread_local.bloom_filter.assert_complete(element);
537 debug_assert_eq!(
538 context.thread_local.bloom_filter.matching_depth(),
539 traversal_data.current_dom_depth
540 );
541
542 important_rules_changed = true;
544
545 let mut target = StyleSharingTarget::new(element);
546
547 match target.share_style_if_possible(context) {
550 Some(shared_styles) => {
551 context.thread_local.statistics.styles_shared += 1;
552 shared_styles
553 },
554 None => {
555 context.thread_local.statistics.elements_matched += 1;
556 let new_styles = {
558 let mut resolver = StyleResolverForElement::new(
559 element,
560 context,
561 RuleInclusion::All,
562 PseudoElementResolution::IfApplicable,
563 );
564
565 resolver.resolve_style_with_default_parents()
566 };
567
568 context.thread_local.sharing_cache.insert_if_possible(
569 &element,
570 &new_styles.primary,
571 Some(&mut target),
572 traversal_data.current_dom_depth,
573 &context.shared,
574 );
575
576 new_styles
577 },
578 }
579 },
580 CascadeWithReplacements(flags) => {
581 let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
583 important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
584
585 let mut resolver = StyleResolverForElement::new(
586 element,
587 context,
588 RuleInclusion::All,
589 PseudoElementResolution::IfApplicable,
590 );
591
592 resolver.cascade_styles_with_default_parents(cascade_inputs)
593 },
594 CascadeOnly => {
595 let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
597
598 let new_styles = {
599 let mut resolver = StyleResolverForElement::new(
600 element,
601 context,
602 RuleInclusion::All,
603 PseudoElementResolution::IfApplicable,
604 );
605
606 resolver.cascade_styles_with_default_parents(cascade_inputs)
607 };
608
609 if !new_styles.primary.reused_via_rule_node {
625 context.thread_local.sharing_cache.insert_if_possible(
626 &element,
627 &new_styles.primary,
628 None,
629 traversal_data.current_dom_depth,
630 &context.shared,
631 );
632 }
633
634 new_styles
635 },
636 };
637
638 element.finish_restyle(context, data, new_styles, important_rules_changed)
639}
640
641#[cfg(feature = "servo")]
642fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
643where
644 E: TElement,
645{
646 use crate::values::generics::image::Image;
647 use style_traits::ToCss;
648
649 if let Some(ref values) = data.styles.primary {
654 for image in &values.get_background().background_image.0 {
655 let (name, arguments) = match *image {
656 Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
657 _ => continue,
658 };
659 let painter = match context.shared.registered_speculative_painters.get(name) {
660 Some(painter) => painter,
661 None => continue,
662 };
663 let properties = painter
664 .properties()
665 .iter()
666 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
667 .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
668 .collect();
669 let arguments = arguments
670 .iter()
671 .map(|argument| argument.to_css_string())
672 .collect();
673 debug!("Notifying paint worklet {}.", painter.name());
674 painter.speculatively_draw_a_paint_image(properties, arguments);
675 }
676 }
677}
678
679#[cfg(not(feature = "servo"))]
680fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
681where
682 E: TElement,
683{
684 }
686
687fn note_children<E, D, F>(
688 context: &mut StyleContext<E>,
689 element: E,
690 data: &ElementData,
691 propagated_hint: RestyleHint,
692 is_initial_style: bool,
693 mut note_child: F,
694) where
695 E: TElement,
696 D: DomTraversal<E>,
697 F: FnMut(E::ConcreteNode),
698{
699 trace!("note_children: {:?}", element);
700 let flags = context.shared.traversal_flags;
701
702 for child_node in element.traversal_children() {
704 let child = match child_node.as_element() {
705 Some(el) => el,
706 None => {
707 if D::text_node_needs_traversal(child_node, data) {
708 note_child(child_node);
709 }
710 continue;
711 },
712 };
713
714 let mut child_data = child.mutate_data();
715 let mut child_data = child_data.as_mut().map(|d| &mut **d);
716 trace!(
717 " > {:?} -> {:?} + {:?}, pseudo: {:?}",
718 child,
719 child_data.as_ref().map(|d| d.hint),
720 propagated_hint,
721 child.implemented_pseudo_element()
722 );
723
724 if let Some(ref mut child_data) = child_data {
725 child_data.hint.insert(propagated_hint);
726
727 child_data.invalidate_style_if_needed(
732 child,
733 &context.shared,
734 Some(&context.thread_local.stack_limit_checker),
735 &mut context.thread_local.selector_caches,
736 );
737 }
738
739 if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
740 note_child(child_node);
741
742 if !is_initial_style {
748 if flags.for_animation_only() {
749 unsafe {
750 element.set_animation_only_dirty_descendants();
751 }
752 } else {
753 unsafe {
754 element.set_dirty_descendants();
755 }
756 }
757 }
758 }
759 }
760}
761
762pub unsafe fn clear_descendant_data<E>(root: E)
767where
768 E: TElement,
769{
770 let mut parents = SmallVec::<[E; 32]>::new();
771 parents.push(root);
772 while let Some(p) = parents.pop() {
773 for kid in p.traversal_children() {
774 if let Some(kid) = kid.as_element() {
775 if kid.has_data() {
781 kid.clear_data();
782 parents.push(kid);
783 }
784 }
785 }
786 }
787
788 root.clear_descendant_bits();
790}