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::{ChildRestyleRequirement, 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(
326 style.as_deref(),
327 layout_parent_style.as_deref(),
328 selectors::matching::IncludeStartingStyle::No,
329 );
330
331 let is_display_contents = primary_style.style().is_display_contents();
332
333 style = Some(primary_style.style.0);
334 if !is_display_contents {
335 layout_parent_style = style.clone();
336 }
337
338 if let Some(ref mut cache) = undisplayed_style_cache {
339 cache.insert(ancestor.opaque(), style.clone().unwrap());
340 }
341 context.thread_local.bloom_filter.push(*ancestor);
342 }
343
344 context.thread_local.bloom_filter.assert_complete(element);
345 let styles: ElementStyles = StyleResolverForElement::new(
346 element,
347 context,
348 rule_inclusion,
349 PseudoElementResolution::Force,
350 )
351 .resolve_style(style.as_deref(), layout_parent_style.as_deref())
352 .into();
353
354 if let Some(ref mut cache) = undisplayed_style_cache {
355 cache.insert(element.opaque(), styles.primary().clone());
356 }
357
358 styles
359}
360
361#[inline]
363#[allow(unsafe_code)]
364pub fn recalc_style_at<E, D, F>(
365 _traversal: &D,
366 traversal_data: &PerLevelTraversalData,
367 context: &mut StyleContext<E>,
368 element: E,
369 data: &mut ElementData,
370 note_child: F,
371) where
372 E: TElement,
373 D: DomTraversal<E>,
374 F: FnMut(E::ConcreteNode),
375{
376 use std::cmp;
377
378 let flags = context.shared.traversal_flags;
379 let is_initial_style = !data.has_styles();
380
381 context.thread_local.statistics.elements_traversed += 1;
382 debug_assert!(
383 flags.intersects(TraversalFlags::AnimationOnly)
384 || is_initial_style
385 || !element.has_snapshot()
386 || element.handled_snapshot(),
387 "Should've handled snapshots here already"
388 );
389
390 let restyle_kind = data.restyle_kind(&context.shared);
391 debug!(
392 "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
393 element,
394 restyle_kind,
395 element.has_dirty_descendants(),
396 data
397 );
398
399 let mut child_restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
400
401 if let Some(restyle_kind) = restyle_kind {
403 child_restyle_requirement =
404 compute_style(traversal_data, context, element, data, restyle_kind);
405
406 if !element.matches_user_and_content_rules() {
407 child_restyle_requirement = cmp::max(
411 child_restyle_requirement,
412 ChildRestyleRequirement::MustCascadeChildren,
413 );
414 }
415
416 if data.styles.is_display_none() {
419 debug!(
420 "{:?} style is display:none - clearing data from descendants.",
421 element
422 );
423 unsafe {
424 clear_descendant_data(element);
425 }
426 }
427
428 notify_paint_worklet(context, data);
432 } else {
433 debug_assert!(data.has_styles());
434 data.set_traversed_without_styling();
435 }
436
437 debug_assert!(
442 flags.for_animation_only() || !data.hint.has_animation_hint(),
443 "animation restyle hint should be handled during \
444 animation-only restyles"
445 );
446 let mut propagated_hint = data.hint.propagate(&flags);
447 trace!(
448 "propagated_hint={:?}, restyle_requirement={:?}, \
449 is_display_none={:?}, implementing_pseudo={:?}",
450 propagated_hint,
451 child_restyle_requirement,
452 data.styles.is_display_none(),
453 element.implemented_pseudo_element()
454 );
455
456 match child_restyle_requirement {
458 ChildRestyleRequirement::CanSkipCascade => {},
459 ChildRestyleRequirement::MustCascadeDescendants => {
460 propagated_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
461 },
462 ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle => {
463 propagated_hint |= RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE;
464 },
465 ChildRestyleRequirement::MustCascadeChildren => {
466 propagated_hint |= RestyleHint::RECASCADE_SELF;
467 },
468 ChildRestyleRequirement::MustMatchDescendants => {
469 propagated_hint |= RestyleHint::restyle_subtree();
470 },
471 }
472
473 let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
474 element.has_animation_only_dirty_descendants()
475 } else {
476 element.has_dirty_descendants()
477 };
478
479 let mut traverse_children = has_dirty_descendants_for_this_restyle
490 || !propagated_hint.is_empty();
491
492 traverse_children = traverse_children && !data.styles.is_display_none();
493
494 if traverse_children {
496 note_children::<E, D, F>(
497 context,
498 element,
499 data,
500 propagated_hint,
501 is_initial_style,
502 note_child,
503 );
504 }
505
506 if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
508 debug_assert!(!element.has_dirty_descendants());
509 debug_assert!(!element.has_animation_only_dirty_descendants());
510 }
511
512 clear_state_after_traversing(element, data, flags);
513}
514
515fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
516where
517 E: TElement,
518{
519 if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
520 debug_assert!(flags.for_animation_only());
521 data.clear_restyle_flags_and_damage();
522 unsafe {
523 element.unset_animation_only_dirty_descendants();
524 }
525 }
526}
527
528fn compute_style<E>(
529 traversal_data: &PerLevelTraversalData,
530 context: &mut StyleContext<E>,
531 element: E,
532 data: &mut ElementData,
533 kind: RestyleKind,
534) -> ChildRestyleRequirement
535where
536 E: TElement,
537{
538 use crate::data::RestyleKind::*;
539
540 context.thread_local.statistics.elements_styled += 1;
541 debug!("compute_style: {:?} (kind={:?})", element, kind);
542
543 if data.has_styles() {
544 data.set_restyled();
545 }
546
547 let mut important_rules_changed = false;
548 let new_styles = match kind {
549 MatchAndCascade => {
550 debug_assert!(
551 !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
552 "MatchAndCascade shouldn't normally be processed during animation-only traversal"
553 );
554 context
556 .thread_local
557 .bloom_filter
558 .insert_parents_recovering(element, traversal_data.current_dom_depth);
559
560 context.thread_local.bloom_filter.assert_complete(element);
561 debug_assert_eq!(
562 context.thread_local.bloom_filter.matching_depth(),
563 traversal_data.current_dom_depth
564 );
565
566 important_rules_changed = true;
568
569 let mut target = StyleSharingTarget::new(element);
570
571 match target.share_style_if_possible(context) {
574 Some(shared_styles) => {
575 context.thread_local.statistics.styles_shared += 1;
576 shared_styles
577 },
578 None => {
579 context.thread_local.statistics.elements_matched += 1;
580 let new_styles = {
582 let mut resolver = StyleResolverForElement::new(
583 element,
584 context,
585 RuleInclusion::All,
586 PseudoElementResolution::IfApplicable,
587 );
588
589 resolver.resolve_style_with_default_parents()
590 };
591
592 context.thread_local.sharing_cache.insert_if_possible(
593 &element,
594 &new_styles.primary,
595 Some(&mut target),
596 traversal_data.current_dom_depth,
597 &context.shared,
598 );
599
600 new_styles
601 },
602 }
603 },
604 CascadeWithReplacements(flags) => {
605 let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
607 important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
608
609 let mut resolver = StyleResolverForElement::new(
610 element,
611 context,
612 RuleInclusion::All,
613 PseudoElementResolution::IfApplicable,
614 );
615
616 resolver
617 .cascade_styles_with_default_parents(cascade_inputs, data.may_have_starting_style())
618 },
619 CascadeOnly => {
620 let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
622
623 let new_styles = {
624 let mut resolver = StyleResolverForElement::new(
625 element,
626 context,
627 RuleInclusion::All,
628 PseudoElementResolution::IfApplicable,
629 );
630
631 resolver.cascade_styles_with_default_parents(
632 cascade_inputs,
633 data.may_have_starting_style(),
634 )
635 };
636
637 if !new_styles.primary.reused_via_rule_node {
653 context.thread_local.sharing_cache.insert_if_possible(
654 &element,
655 &new_styles.primary,
656 None,
657 traversal_data.current_dom_depth,
658 &context.shared,
659 );
660 }
661
662 new_styles
663 },
664 };
665
666 element.finish_restyle(context, data, new_styles, important_rules_changed)
667}
668
669#[cfg(feature = "servo")]
670fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
671where
672 E: TElement,
673{
674 use crate::values::generics::image::Image;
675 use style_traits::ToCss;
676
677 if let Some(ref values) = data.styles.primary {
682 for image in &values.get_background().background_image.0 {
683 let (name, arguments) = match *image {
684 Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
685 _ => continue,
686 };
687 let painter = match context.shared.registered_speculative_painters.get(name) {
688 Some(painter) => painter,
689 None => continue,
690 };
691 let properties = painter
692 .properties()
693 .iter()
694 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
695 .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
696 .collect();
697 let arguments = arguments
698 .iter()
699 .map(|argument| argument.to_css_string())
700 .collect();
701 debug!("Notifying paint worklet {}.", painter.name());
702 painter.speculatively_draw_a_paint_image(properties, arguments);
703 }
704 }
705}
706
707#[cfg(not(feature = "servo"))]
708fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
709where
710 E: TElement,
711{
712 }
714
715fn note_children<E, D, F>(
716 context: &mut StyleContext<E>,
717 element: E,
718 data: &ElementData,
719 propagated_hint: RestyleHint,
720 is_initial_style: bool,
721 mut note_child: F,
722) where
723 E: TElement,
724 D: DomTraversal<E>,
725 F: FnMut(E::ConcreteNode),
726{
727 trace!("note_children: {:?}", element);
728 let flags = context.shared.traversal_flags;
729
730 for child_node in element.traversal_children() {
732 let child = match child_node.as_element() {
733 Some(el) => el,
734 None => {
735 if D::text_node_needs_traversal(child_node, data) {
736 note_child(child_node);
737 }
738 continue;
739 },
740 };
741
742 let mut child_data = child.mutate_data();
743 let mut child_data = child_data.as_mut().map(|d| &mut **d);
744 trace!(
745 " > {:?} -> {:?} + {:?}, pseudo: {:?}",
746 child,
747 child_data.as_ref().map(|d| d.hint),
748 propagated_hint,
749 child.implemented_pseudo_element()
750 );
751
752 if let Some(ref mut child_data) = child_data {
753 child_data.hint.insert(propagated_hint);
754
755 child_data.invalidate_style_if_needed(
760 child,
761 &context.shared,
762 Some(&context.thread_local.stack_limit_checker),
763 &mut context.thread_local.selector_caches,
764 );
765 }
766
767 if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
768 note_child(child_node);
769
770 if !is_initial_style {
776 if flags.for_animation_only() {
777 unsafe {
778 element.set_animation_only_dirty_descendants();
779 }
780 } else {
781 unsafe {
782 element.set_dirty_descendants();
783 }
784 }
785 }
786 }
787 }
788}
789
790pub unsafe fn clear_descendant_data<E>(root: E)
795where
796 E: TElement,
797{
798 let mut parents = SmallVec::<[E; 32]>::new();
799 parents.push(root);
800 while let Some(p) = parents.pop() {
801 for kid in p.traversal_children() {
802 if let Some(kid) = kid.as_element() {
803 if kid.has_data() {
809 kid.clear_data();
810 parents.push(kid);
811 }
812 }
813 }
814 }
815
816 root.clear_descendant_bits();
818}