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
55#[cfg(feature = "servo")]
59pub static IS_SERVO_NONINCREMENTAL_LAYOUT: std::sync::atomic::AtomicBool =
60 std::sync::atomic::AtomicBool::new(false);
61
62#[cfg(feature = "servo")]
63#[inline]
64fn is_servo_nonincremental_layout() -> bool {
65 use std::sync::atomic::Ordering;
66
67 IS_SERVO_NONINCREMENTAL_LAYOUT.load(Ordering::Relaxed)
68}
69
70#[cfg(not(feature = "servo"))]
71#[inline]
72fn is_servo_nonincremental_layout() -> bool {
73 false
74}
75
76pub trait DomTraversal<E: TElement>: Sync {
79 fn process_preorder<F>(
84 &self,
85 data: &PerLevelTraversalData,
86 context: &mut StyleContext<E>,
87 node: E::ConcreteNode,
88 note_child: F,
89 ) where
90 F: FnMut(E::ConcreteNode);
91
92 fn process_postorder(&self, contect: &mut StyleContext<E>, node: E::ConcreteNode);
96
97 fn needs_postorder_traversal() -> bool {
102 true
103 }
104
105 fn handle_postorder_traversal(
119 &self,
120 context: &mut StyleContext<E>,
121 root: OpaqueNode,
122 mut node: E::ConcreteNode,
123 children_to_process: isize,
124 ) {
125 if !Self::needs_postorder_traversal() {
127 return;
128 }
129
130 if children_to_process == 0 {
131 loop {
133 self.process_postorder(context, node);
134 if node.opaque() == root {
135 break;
136 }
137 let parent = node.traversal_parent().unwrap();
138 let remaining = parent.did_process_child();
139 if remaining != 0 {
140 break;
144 }
145
146 node = parent.as_node();
147 }
148 } else {
149 node.as_element()
152 .unwrap()
153 .store_children_to_process(children_to_process);
154 }
155 }
156
157 fn pre_traverse(root: E, shared_context: &SharedStyleContext) -> PreTraverseToken<E> {
162 use crate::invalidation::element::state_and_attributes::propagate_dirty_bit_up_to;
163
164 let traversal_flags = shared_context.traversal_flags;
165
166 let mut data = root.mutate_data();
167 let mut data = data.as_mut().map(|d| &mut **d);
168
169 if let Some(ref mut data) = data {
170 if !traversal_flags.for_animation_only() {
171 let invalidation_result = data.invalidate_style_if_needed(
174 root,
175 shared_context,
176 None,
177 &mut SelectorCaches::default(),
178 );
179
180 if invalidation_result.has_invalidated_siblings() {
181 let actual_root = root.as_node().parent_element_or_host().expect(
182 "How in the world can you invalidate \
183 siblings without a parent?",
184 );
185 propagate_dirty_bit_up_to(actual_root, root);
186 return PreTraverseToken(Some(actual_root));
187 }
188 }
189 }
190
191 let should_traverse =
192 Self::element_needs_traversal(root, traversal_flags, data.as_mut().map(|d| &**d));
193
194 if !should_traverse && data.is_some() {
197 clear_state_after_traversing(root, data.unwrap(), traversal_flags);
198 }
199
200 PreTraverseToken(if should_traverse { Some(root) } else { None })
201 }
202
203 fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool {
207 debug_assert!(node.is_text_node());
208 false
209 }
210
211 fn element_needs_traversal(
213 el: E,
214 traversal_flags: TraversalFlags,
215 data: Option<&ElementData>,
216 ) -> bool {
217 debug!(
218 "element_needs_traversal({:?}, {:?}, {:?})",
219 el, traversal_flags, data
220 );
221
222 if is_servo_nonincremental_layout() {
224 return true;
225 }
226
227 let data = match data {
229 Some(d) if d.has_styles() => d,
230 _ => return true,
231 };
232
233 if traversal_flags.for_animation_only() {
234 return el.has_animation_only_dirty_descendants()
237 || data.hint.has_animation_hint_or_recascade();
238 }
239
240 if el.has_dirty_descendants() {
243 return true;
244 }
245
246 if !data.hint.is_empty() {
254 return true;
255 }
256
257 if cfg!(feature = "servo") && !data.damage.is_empty() {
261 return true;
262 }
263
264 trace!("{:?} doesn't need traversal", el);
265 false
266 }
267
268 fn shared_context(&self) -> &SharedStyleContext;
270}
271
272pub fn resolve_style<E>(
277 context: &mut StyleContext<E>,
278 element: E,
279 rule_inclusion: RuleInclusion,
280 pseudo: Option<&PseudoElement>,
281 mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
282) -> ElementStyles
283where
284 E: TElement,
285{
286 debug_assert!(
287 rule_inclusion == RuleInclusion::DefaultOnly
288 || pseudo.map_or(false, |p| p.is_before_or_after())
289 || element.borrow_data().map_or(true, |d| !d.has_styles()),
290 "Why are we here?"
291 );
292 debug_assert!(
293 rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
294 "can't use the cache for default styles only"
295 );
296
297 let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
298
299 context.thread_local.bloom_filter.clear();
301
302 let mut style = None;
303 let mut ancestor = element.traversal_parent();
304 while let Some(current) = ancestor {
305 if rule_inclusion == RuleInclusion::All {
306 if let Some(data) = current.borrow_data() {
307 if let Some(ancestor_style) = data.styles.get_primary() {
308 style = Some(ancestor_style.clone());
309 break;
310 }
311 }
312 }
313 if let Some(ref mut cache) = undisplayed_style_cache {
314 if let Some(s) = cache.get(¤t.opaque()) {
315 style = Some(s.clone());
316 break;
317 }
318 }
319 ancestors_requiring_style_resolution.push(current);
320 ancestor = current.traversal_parent();
321 }
322
323 if let Some(ancestor) = ancestor {
324 context.thread_local.bloom_filter.rebuild(ancestor);
325 context.thread_local.bloom_filter.push(ancestor);
326 }
327
328 let mut layout_parent_style = style.clone();
329 while let Some(style) = layout_parent_style.take() {
330 if !style.is_display_contents() {
331 layout_parent_style = Some(style);
332 break;
333 }
334
335 ancestor = ancestor.unwrap().traversal_parent();
336 layout_parent_style =
337 ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));
338 }
339
340 for ancestor in ancestors_requiring_style_resolution.iter().rev() {
341 context.thread_local.bloom_filter.assert_complete(*ancestor);
342
343 let primary_style = StyleResolverForElement::new(
346 *ancestor,
347 context,
348 rule_inclusion,
349 PseudoElementResolution::IfApplicable,
350 )
351 .resolve_primary_style(
352 style.as_deref(),
353 layout_parent_style.as_deref(),
354 selectors::matching::IncludeStartingStyle::No,
355 );
356
357 let is_display_contents = primary_style.style().is_display_contents();
358
359 style = Some(primary_style.style.0);
360 if !is_display_contents {
361 layout_parent_style = style.clone();
362 }
363
364 if let Some(ref mut cache) = undisplayed_style_cache {
365 cache.insert(ancestor.opaque(), style.clone().unwrap());
366 }
367 context.thread_local.bloom_filter.push(*ancestor);
368 }
369
370 context.thread_local.bloom_filter.assert_complete(element);
371 let styles: ElementStyles = StyleResolverForElement::new(
372 element,
373 context,
374 rule_inclusion,
375 PseudoElementResolution::Force,
376 )
377 .resolve_style(style.as_deref(), layout_parent_style.as_deref())
378 .into();
379
380 if let Some(ref mut cache) = undisplayed_style_cache {
381 cache.insert(element.opaque(), styles.primary().clone());
382 }
383
384 styles
385}
386
387#[inline]
389#[allow(unsafe_code)]
390pub fn recalc_style_at<E, D, F>(
391 _traversal: &D,
392 traversal_data: &PerLevelTraversalData,
393 context: &mut StyleContext<E>,
394 element: E,
395 data: &mut ElementData,
396 note_child: F,
397) where
398 E: TElement,
399 D: DomTraversal<E>,
400 F: FnMut(E::ConcreteNode),
401{
402 use std::cmp;
403
404 let flags = context.shared.traversal_flags;
405 let is_initial_style = !data.has_styles();
406
407 context.thread_local.statistics.elements_traversed += 1;
408 debug_assert!(
409 flags.intersects(TraversalFlags::AnimationOnly)
410 || is_initial_style
411 || !element.has_snapshot()
412 || element.handled_snapshot(),
413 "Should've handled snapshots here already"
414 );
415
416 let restyle_kind = data.restyle_kind(&context.shared);
417 debug!(
418 "recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})",
419 element,
420 restyle_kind,
421 element.has_dirty_descendants(),
422 data
423 );
424
425 let mut child_restyle_requirement = ChildRestyleRequirement::CanSkipCascade;
426
427 if let Some(restyle_kind) = restyle_kind {
429 child_restyle_requirement =
430 compute_style(traversal_data, context, element, data, restyle_kind);
431
432 if !element.matches_user_and_content_rules() {
433 child_restyle_requirement = cmp::max(
437 child_restyle_requirement,
438 ChildRestyleRequirement::MustCascadeChildren,
439 );
440 }
441
442 if data.styles.is_display_none() {
445 debug!(
446 "{:?} style is display:none - clearing data from descendants.",
447 element
448 );
449 unsafe {
450 clear_descendant_data(element);
451 }
452 }
453
454 notify_paint_worklet(context, data);
458 } else {
459 debug_assert!(data.has_styles());
460 data.set_traversed_without_styling();
461 }
462
463 debug_assert!(
468 flags.for_animation_only() || !data.hint.has_animation_hint(),
469 "animation restyle hint should be handled during \
470 animation-only restyles"
471 );
472 let mut propagated_hint = data.hint.propagate(&flags);
473 trace!(
474 "propagated_hint={:?}, restyle_requirement={:?}, \
475 is_display_none={:?}, implementing_pseudo={:?}",
476 propagated_hint,
477 child_restyle_requirement,
478 data.styles.is_display_none(),
479 element.implemented_pseudo_element()
480 );
481
482 match child_restyle_requirement {
484 ChildRestyleRequirement::CanSkipCascade => {},
485 ChildRestyleRequirement::MustCascadeDescendants => {
486 propagated_hint |= RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS;
487 },
488 ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle => {
489 propagated_hint |= RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE;
490 },
491 ChildRestyleRequirement::MustCascadeChildren => {
492 propagated_hint |= RestyleHint::RECASCADE_SELF;
493 },
494 ChildRestyleRequirement::MustMatchDescendants => {
495 propagated_hint |= RestyleHint::restyle_subtree();
496 },
497 }
498
499 let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {
500 element.has_animation_only_dirty_descendants()
501 } else {
502 element.has_dirty_descendants()
503 };
504
505 let mut traverse_children = has_dirty_descendants_for_this_restyle
516 || !propagated_hint.is_empty()
517 || is_servo_nonincremental_layout();
518
519 traverse_children = traverse_children && !data.styles.is_display_none();
520
521 if traverse_children {
523 note_children::<E, D, F>(
524 context,
525 element,
526 data,
527 propagated_hint,
528 is_initial_style,
529 note_child,
530 );
531 }
532
533 if cfg!(feature = "gecko") && cfg!(debug_assertions) && data.styles.is_display_none() {
535 debug_assert!(!element.has_dirty_descendants());
536 debug_assert!(!element.has_animation_only_dirty_descendants());
537 }
538
539 clear_state_after_traversing(element, data, flags);
540}
541
542fn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)
543where
544 E: TElement,
545{
546 if flags.intersects(TraversalFlags::FinalAnimationTraversal) {
547 debug_assert!(flags.for_animation_only());
548 data.clear_restyle_flags_and_damage();
549 unsafe {
550 element.unset_animation_only_dirty_descendants();
551 }
552 }
553}
554
555fn compute_style<E>(
556 traversal_data: &PerLevelTraversalData,
557 context: &mut StyleContext<E>,
558 element: E,
559 data: &mut ElementData,
560 kind: RestyleKind,
561) -> ChildRestyleRequirement
562where
563 E: TElement,
564{
565 use crate::data::RestyleKind::*;
566
567 context.thread_local.statistics.elements_styled += 1;
568 debug!("compute_style: {:?} (kind={:?})", element, kind);
569
570 if data.has_styles() {
571 data.set_restyled();
572 }
573
574 let mut important_rules_changed = false;
575 let new_styles = match kind {
576 MatchAndCascade => {
577 debug_assert!(
578 !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),
579 "MatchAndCascade shouldn't normally be processed during animation-only traversal"
580 );
581 context
583 .thread_local
584 .bloom_filter
585 .insert_parents_recovering(element, traversal_data.current_dom_depth);
586
587 context.thread_local.bloom_filter.assert_complete(element);
588 debug_assert_eq!(
589 context.thread_local.bloom_filter.matching_depth(),
590 traversal_data.current_dom_depth
591 );
592
593 important_rules_changed = true;
595
596 let mut target = StyleSharingTarget::new(element);
597
598 match target.share_style_if_possible(context) {
601 Some(shared_styles) => {
602 context.thread_local.statistics.styles_shared += 1;
603 shared_styles
604 },
605 None => {
606 context.thread_local.statistics.elements_matched += 1;
607 let new_styles = {
609 let mut resolver = StyleResolverForElement::new(
610 element,
611 context,
612 RuleInclusion::All,
613 PseudoElementResolution::IfApplicable,
614 );
615
616 resolver.resolve_style_with_default_parents()
617 };
618
619 context.thread_local.sharing_cache.insert_if_possible(
620 &element,
621 &new_styles.primary,
622 Some(&mut target),
623 traversal_data.current_dom_depth,
624 &context.shared,
625 );
626
627 new_styles
628 },
629 }
630 },
631 CascadeWithReplacements(flags) => {
632 let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
634 important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);
635
636 let mut resolver = StyleResolverForElement::new(
637 element,
638 context,
639 RuleInclusion::All,
640 PseudoElementResolution::IfApplicable,
641 );
642
643 resolver
644 .cascade_styles_with_default_parents(cascade_inputs, data.may_have_starting_style())
645 },
646 CascadeOnly => {
647 let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);
649
650 let new_styles = {
651 let mut resolver = StyleResolverForElement::new(
652 element,
653 context,
654 RuleInclusion::All,
655 PseudoElementResolution::IfApplicable,
656 );
657
658 resolver.cascade_styles_with_default_parents(
659 cascade_inputs,
660 data.may_have_starting_style(),
661 )
662 };
663
664 if !new_styles.primary.reused_via_rule_node {
680 context.thread_local.sharing_cache.insert_if_possible(
681 &element,
682 &new_styles.primary,
683 None,
684 traversal_data.current_dom_depth,
685 &context.shared,
686 );
687 }
688
689 new_styles
690 },
691 };
692
693 element.finish_restyle(context, data, new_styles, important_rules_changed)
694}
695
696#[cfg(feature = "servo")]
697fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
698where
699 E: TElement,
700{
701 use crate::values::generics::image::Image;
702 use style_traits::ToCss;
703
704 if let Some(ref values) = data.styles.primary {
709 for image in &values.get_background().background_image.0 {
710 let (name, arguments) = match *image {
711 Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),
712 _ => continue,
713 };
714 let painter = match context.shared.registered_speculative_painters.get(name) {
715 Some(painter) => painter,
716 None => continue,
717 };
718 let properties = painter
719 .properties()
720 .iter()
721 .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))
722 .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))
723 .collect();
724 let arguments = arguments
725 .iter()
726 .map(|argument| argument.to_css_string())
727 .collect();
728 debug!("Notifying paint worklet {}.", painter.name());
729 painter.speculatively_draw_a_paint_image(properties, arguments);
730 }
731 }
732}
733
734#[cfg(not(feature = "servo"))]
735fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
736where
737 E: TElement,
738{
739 }
741
742fn note_children<E, D, F>(
743 context: &mut StyleContext<E>,
744 element: E,
745 data: &ElementData,
746 propagated_hint: RestyleHint,
747 is_initial_style: bool,
748 mut note_child: F,
749) where
750 E: TElement,
751 D: DomTraversal<E>,
752 F: FnMut(E::ConcreteNode),
753{
754 trace!("note_children: {:?}", element);
755 let flags = context.shared.traversal_flags;
756
757 for child_node in element.traversal_children() {
759 let child = match child_node.as_element() {
760 Some(el) => el,
761 None => {
762 if is_servo_nonincremental_layout()
763 || D::text_node_needs_traversal(child_node, data)
764 {
765 note_child(child_node);
766 }
767 continue;
768 },
769 };
770
771 let mut child_data = child.mutate_data();
772 let mut child_data = child_data.as_mut().map(|d| &mut **d);
773 trace!(
774 " > {:?} -> {:?} + {:?}, pseudo: {:?}",
775 child,
776 child_data.as_ref().map(|d| d.hint),
777 propagated_hint,
778 child.implemented_pseudo_element()
779 );
780
781 if let Some(ref mut child_data) = child_data {
782 child_data.hint.insert(propagated_hint);
783
784 child_data.invalidate_style_if_needed(
789 child,
790 &context.shared,
791 Some(&context.thread_local.stack_limit_checker),
792 &mut context.thread_local.selector_caches,
793 );
794 }
795
796 if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {
797 note_child(child_node);
798
799 if !is_initial_style {
805 if flags.for_animation_only() {
806 unsafe {
807 element.set_animation_only_dirty_descendants();
808 }
809 } else {
810 unsafe {
811 element.set_dirty_descendants();
812 }
813 }
814 }
815 }
816 }
817}
818
819pub unsafe fn clear_descendant_data<E>(root: E)
824where
825 E: TElement,
826{
827 let mut parents = SmallVec::<[E; 32]>::new();
828 parents.push(root);
829 while let Some(p) = parents.pop() {
830 for kid in p.traversal_children() {
831 if let Some(kid) = kid.as_element() {
832 if kid.has_data() {
838 kid.clear_data();
839 parents.push(kid);
840 }
841 }
842 }
843 }
844
845 root.clear_descendant_bits();
847}