1use std::cell::Cell;
6use std::sync::Arc;
7
8use bitflags::Flags;
9use layout_api::LayoutDamage;
10use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
11use script::layout_dom::{ServoLayoutNode, ServoThreadSafeLayoutNode};
12use style::context::{SharedStyleContext, StyleContext};
13use style::data::ElementData;
14use style::dom::{NodeInfo, TElement, TNode};
15use style::selector_parser::RestyleDamage;
16use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
17
18use crate::BoxTree;
19use crate::context::LayoutContext;
20use crate::dom::{DOMLayoutData, NodeExt};
21
22pub struct RecalcStyle<'a> {
23 context: &'a LayoutContext<'a>,
24}
25
26impl<'a> RecalcStyle<'a> {
27 pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
28 RecalcStyle { context }
29 }
30
31 pub(crate) fn context(&self) -> &LayoutContext<'a> {
32 self.context
33 }
34}
35
36#[expect(unsafe_code)]
37impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
38where
39 E: TElement,
40 E::ConcreteNode: 'dom + LayoutNode<'dom>,
41{
42 fn process_preorder<F>(
43 &self,
44 traversal_data: &PerLevelTraversalData,
45 context: &mut StyleContext<E>,
46 node: E::ConcreteNode,
47 note_child: F,
48 ) where
49 F: FnMut(E::ConcreteNode),
50 {
51 if node.is_text_node() {
52 return;
53 }
54
55 let had_style_data = node.style_data().is_some();
56 unsafe {
57 node.initialize_style_and_layout_data::<DOMLayoutData>();
58 }
59
60 let element = node.as_element().unwrap();
61 let mut element_data = element.mutate_data().unwrap();
62
63 if !had_style_data {
64 element_data.damage = RestyleDamage::reconstruct();
65 }
66
67 recalc_style_at(
68 self,
69 traversal_data,
70 context,
71 element,
72 &mut element_data,
73 note_child,
74 );
75
76 unsafe {
77 element.unset_dirty_descendants();
78 }
79 }
80
81 #[inline]
82 fn needs_postorder_traversal() -> bool {
83 false
84 }
85
86 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
87 panic!("this should never be called")
88 }
89
90 fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
91 node.layout_data().is_none() || !parent_data.damage.is_empty()
92 }
93
94 fn shared_context(&self) -> &SharedStyleContext<'_> {
95 &self.context.style_context
96 }
97}
98
99#[servo_tracing::instrument(skip_all)]
100pub(crate) fn compute_damage_and_rebuild_box_tree(
101 box_tree: &mut Option<Arc<BoxTree>>,
102 layout_context: &LayoutContext,
103 dirty_root: ServoLayoutNode<'_>,
104 root_node: ServoLayoutNode<'_>,
105 damage_from_environment: RestyleDamage,
106) -> RestyleDamage {
107 let restyle_damage = compute_damage_and_rebuild_box_tree_inner(
108 layout_context,
109 dirty_root.to_threadsafe(),
110 damage_from_environment,
111 );
112
113 let layout_damage: LayoutDamage = restyle_damage.into();
114 if box_tree.is_none() {
115 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
116 return restyle_damage;
117 }
118
119 if !restyle_damage.contains(RestyleDamage::RELAYOUT) {
127 return restyle_damage;
128 }
129
130 let mut needs_box_tree_rebuild = layout_damage.needs_new_box();
133
134 let mut damage_for_ancestors = LayoutDamage::RECOMPUTE_INLINE_CONTENT_SIZES;
135 let mut maybe_parent_node = dirty_root.traversal_parent();
136 while let Some(parent_node) = maybe_parent_node {
137 let threadsafe_parent_node = parent_node.as_node().to_threadsafe();
138
139 if needs_box_tree_rebuild &&
141 threadsafe_parent_node
142 .rebuild_box_tree_from_independent_formatting_context(layout_context)
143 {
144 needs_box_tree_rebuild = false;
145 }
146
147 if needs_box_tree_rebuild {
148 threadsafe_parent_node.unset_all_boxes();
152 } else {
153 let new_damage_for_ancestors = Cell::new(LayoutDamage::empty());
156 threadsafe_parent_node.with_layout_box_base_including_pseudos(|base| {
157 new_damage_for_ancestors.set(
158 new_damage_for_ancestors.get() |
159 base.add_damage(Default::default(), damage_for_ancestors),
160 );
161 });
162 damage_for_ancestors = new_damage_for_ancestors.get();
163 }
164
165 maybe_parent_node = parent_node.traversal_parent();
166 }
167
168 if needs_box_tree_rebuild {
171 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
172 }
173
174 restyle_damage
175}
176
177pub(crate) fn compute_damage_and_rebuild_box_tree_inner(
178 layout_context: &LayoutContext,
179 node: ServoThreadSafeLayoutNode<'_>,
180 damage_from_parent: RestyleDamage,
181) -> RestyleDamage {
182 let element_data = &node
183 .style_data()
184 .expect("Should not run `compute_damage` before styling.")
185 .element_data;
186 let (element_damage, is_display_none) = {
187 let mut element_data = element_data.borrow_mut();
188 (
189 std::mem::take(&mut element_data.damage),
190 element_data.styles.is_display_none(),
191 )
192 };
193
194 let mut element_and_parent_damage = element_damage | damage_from_parent;
195 if is_display_none {
196 node.unset_all_boxes();
197 return element_and_parent_damage;
198 }
199
200 let mut damage_for_children = element_and_parent_damage;
205 damage_for_children.truncate();
206 let rebuild_children = element_damage.contains(LayoutDamage::box_damage()) ||
207 (damage_from_parent.contains(LayoutDamage::box_damage()) &&
208 !node.isolates_damage_for_damage_propagation());
209 if rebuild_children {
210 damage_for_children.insert(LayoutDamage::box_damage());
211 } else if element_and_parent_damage.contains(RestyleDamage::RELAYOUT) &&
212 !element_damage.contains(RestyleDamage::RELAYOUT) &&
213 node.isolates_damage_for_damage_propagation()
214 {
215 damage_for_children.remove(RestyleDamage::RELAYOUT);
219 element_and_parent_damage.remove(RestyleDamage::RELAYOUT);
220 }
221
222 let mut damage_from_children = RestyleDamage::empty();
223 for child in node.children() {
224 if child.is_element() {
225 damage_from_children |= compute_damage_and_rebuild_box_tree_inner(
226 layout_context,
227 child,
228 damage_for_children,
229 );
230 }
231 }
232
233 let mut layout_damage_for_parent =
236 element_and_parent_damage | (damage_from_children & RestyleDamage::RELAYOUT);
237
238 let element_or_ancestors_need_rebuild =
239 element_and_parent_damage.contains(LayoutDamage::descendant_has_box_damage());
240 let descendant_needs_rebuild =
241 damage_from_children.contains(LayoutDamage::descendant_has_box_damage());
242 if element_or_ancestors_need_rebuild || descendant_needs_rebuild {
243 if damage_from_parent.contains(LayoutDamage::descendant_has_box_damage()) ||
244 !node.rebuild_box_tree_from_independent_formatting_context(layout_context)
245 {
246 node.unset_all_boxes();
256 layout_damage_for_parent
257 .insert(LayoutDamage::descendant_has_box_damage() | RestyleDamage::RELAYOUT);
258 } else {
259 layout_damage_for_parent.remove(LayoutDamage::box_damage());
262 layout_damage_for_parent
263 .insert(RestyleDamage::RELAYOUT | LayoutDamage::recompute_inline_content_sizes());
264 }
265 } else {
266 if (element_and_parent_damage | damage_from_children).contains(RestyleDamage::RELAYOUT) {
271 let extra_layout_damage_for_parent = Cell::new(LayoutDamage::empty());
272 node.with_layout_box_base_including_pseudos(|base| {
273 extra_layout_damage_for_parent.set(
274 extra_layout_damage_for_parent.get() |
275 base.add_damage(element_damage.into(), damage_from_children.into()),
276 );
277 });
278 layout_damage_for_parent.insert(extra_layout_damage_for_parent.get().into());
279 }
280
281 if !element_damage.is_empty() {
285 node.repair_style(&layout_context.style_context);
286 }
287 }
288
289 layout_damage_for_parent
290}