1use std::cell::Cell;
6
7use bitflags::Flags;
8use layout_api::LayoutDamage;
9use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
10use script::layout_dom::ServoThreadSafeLayoutNode;
11use style::context::{SharedStyleContext, StyleContext};
12use style::data::ElementData;
13use style::dom::{NodeInfo, TElement, TNode};
14use style::selector_parser::RestyleDamage;
15use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
16
17use crate::context::LayoutContext;
18use crate::dom::{DOMLayoutData, NodeExt};
19
20pub struct RecalcStyle<'a> {
21 context: &'a LayoutContext<'a>,
22}
23
24impl<'a> RecalcStyle<'a> {
25 pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
26 RecalcStyle { context }
27 }
28
29 pub(crate) fn context(&self) -> &LayoutContext<'a> {
30 self.context
31 }
32}
33
34#[expect(unsafe_code)]
35impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
36where
37 E: TElement,
38 E::ConcreteNode: 'dom + LayoutNode<'dom>,
39{
40 fn process_preorder<F>(
41 &self,
42 traversal_data: &PerLevelTraversalData,
43 context: &mut StyleContext<E>,
44 node: E::ConcreteNode,
45 note_child: F,
46 ) where
47 F: FnMut(E::ConcreteNode),
48 {
49 if node.is_text_node() {
50 return;
51 }
52
53 let had_style_data = node.style_data().is_some();
54 unsafe {
55 node.initialize_style_and_layout_data::<DOMLayoutData>();
56 }
57
58 let element = node.as_element().unwrap();
59 let mut element_data = element.mutate_data().unwrap();
60
61 if !had_style_data {
62 element_data.damage = RestyleDamage::reconstruct();
63 }
64
65 recalc_style_at(
66 self,
67 traversal_data,
68 context,
69 element,
70 &mut element_data,
71 note_child,
72 );
73
74 unsafe {
75 element.unset_dirty_descendants();
76 }
77 }
78
79 #[inline]
80 fn needs_postorder_traversal() -> bool {
81 false
82 }
83
84 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
85 panic!("this should never be called")
86 }
87
88 fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
89 node.layout_data().is_none() || !parent_data.damage.is_empty()
90 }
91
92 fn shared_context(&self) -> &SharedStyleContext<'_> {
93 &self.context.style_context
94 }
95}
96
97#[servo_tracing::instrument(skip_all)]
98pub(crate) fn compute_damage_and_repair_style(
99 context: &SharedStyleContext,
100 node: ServoThreadSafeLayoutNode<'_>,
101 damage_from_environment: RestyleDamage,
102) -> RestyleDamage {
103 compute_damage_and_repair_style_inner(context, node, damage_from_environment)
104}
105
106pub(crate) fn compute_damage_and_repair_style_inner(
107 context: &SharedStyleContext,
108 node: ServoThreadSafeLayoutNode<'_>,
109 damage_from_parent: RestyleDamage,
110) -> RestyleDamage {
111 let element_data = &node
112 .style_data()
113 .expect("Should not run `compute_damage` before styling.")
114 .element_data;
115 let (element_damage, is_display_none) = {
116 let mut element_data = element_data.borrow_mut();
117 (
118 std::mem::take(&mut element_data.damage),
119 element_data.styles.is_display_none(),
120 )
121 };
122
123 let element_and_parent_damage = element_damage | damage_from_parent;
124 if is_display_none {
125 node.unset_all_boxes();
126 return element_and_parent_damage;
127 }
128
129 let mut damage_for_children = element_and_parent_damage;
134 damage_for_children.truncate();
135 let rebuild_children = element_damage.contains(LayoutDamage::rebuild_box_tree()) ||
136 (damage_from_parent.contains(LayoutDamage::rebuild_box_tree()) &&
137 !node.isolates_box_tree_rebuild_damage());
138 if rebuild_children {
139 damage_for_children.insert(LayoutDamage::rebuild_box_tree());
140 }
141
142 let mut damage_from_children = RestyleDamage::empty();
143 for child in node.children() {
144 if child.is_element() {
145 damage_from_children |=
146 compute_damage_and_repair_style_inner(context, child, damage_for_children);
147 }
148 }
149
150 let mut layout_damage_for_parent =
153 element_and_parent_damage | (damage_from_children & RestyleDamage::RELAYOUT);
154
155 if damage_from_children.contains(LayoutDamage::recollect_box_tree_children()) ||
156 element_and_parent_damage.contains(LayoutDamage::recollect_box_tree_children())
157 {
158 node.unset_all_boxes();
164 layout_damage_for_parent
165 .insert(LayoutDamage::recollect_box_tree_children() | RestyleDamage::RELAYOUT);
166 } else {
167 if (element_and_parent_damage | damage_from_children).contains(RestyleDamage::RELAYOUT) {
172 let extra_layout_damage_for_parent = Cell::new(LayoutDamage::empty());
173 node.with_layout_box_base_including_pseudos(|base| {
174 extra_layout_damage_for_parent.set(
175 extra_layout_damage_for_parent.get() |
176 base.add_damage(element_damage.into(), damage_from_children.into()),
177 );
178 });
179 layout_damage_for_parent.insert(extra_layout_damage_for_parent.get().into());
180 }
181
182 if !element_damage.is_empty() {
186 node.repair_style(context);
187 }
188 }
189
190 layout_damage_for_parent
191}