1use std::cell::Cell;
6use std::sync::Arc;
7
8use bitflags::Flags;
9use layout_api::{
10 DangerousStyleElement, DangerousStyleNode, LayoutDamage, LayoutElement, LayoutNode,
11};
12use script::layout_dom::ServoLayoutNode;
13use style::context::{SharedStyleContext, StyleContext};
14use style::data::ElementData;
15use style::dom::{NodeInfo, TElement, TNode};
16use style::selector_parser::RestyleDamage;
17use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
18
19use crate::BoxTree;
20use crate::context::LayoutContext;
21use crate::dom::{DOMLayoutData, NodeExt};
22
23pub struct RecalcStyle<'a> {
24 context: &'a LayoutContext<'a>,
25}
26
27impl<'a> RecalcStyle<'a> {
28 pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
29 RecalcStyle { context }
30 }
31
32 pub(crate) fn context(&self) -> &LayoutContext<'a> {
33 self.context
34 }
35}
36
37impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
38where
39 E: DangerousStyleElement<'dom> + TElement,
40 E::ConcreteNode: 'dom + DangerousStyleNode<'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 let Some(dangerous_style_element) = node.as_element() else {
52 return;
53 };
54
55 let layout_element = dangerous_style_element.layout_element();
56 let had_style_data = layout_element.style_data().is_some();
57 layout_element.initialize_style_and_layout_data::<DOMLayoutData>();
58
59 let mut element_data = dangerous_style_element.mutate_data().unwrap();
60 if !had_style_data {
61 element_data.damage = RestyleDamage::reconstruct();
62 }
63
64 recalc_style_at(
65 self,
66 traversal_data,
67 context,
68 dangerous_style_element,
69 &mut element_data,
70 note_child,
71 );
72
73 #[expect(unsafe_code)]
74 unsafe {
75 dangerous_style_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_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#[expect(unsafe_code)]
98#[servo_tracing::instrument(skip_all)]
99pub(crate) fn compute_damage_and_rebuild_box_tree(
100 box_tree: &mut Option<Arc<BoxTree>>,
101 layout_context: &LayoutContext,
102 dirty_root: ServoLayoutNode<'_>,
103 root_node: ServoLayoutNode<'_>,
104 damage_from_environment: RestyleDamage,
105) -> RestyleDamage {
106 let restyle_damage = compute_damage_and_rebuild_box_tree_inner(
107 layout_context,
108 dirty_root,
109 damage_from_environment,
110 );
111
112 let layout_damage: LayoutDamage = restyle_damage.into();
113 if box_tree.is_none() {
114 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
115 return restyle_damage;
116 }
117
118 let needs_fragment_tree_rebuild = restyle_damage.contains(RestyleDamage::RELAYOUT);
126 let needs_overflow_recalculation = restyle_damage.contains(RestyleDamage::RECALCULATE_OVERFLOW);
127 if !needs_fragment_tree_rebuild && !needs_overflow_recalculation {
128 return restyle_damage;
129 }
130
131 let mut needs_box_tree_rebuild = layout_damage.needs_new_box();
134
135 let mut damage_for_ancestors = LayoutDamage::RECOMPUTE_INLINE_CONTENT_SIZES;
136 if restyle_damage.contains(LayoutDamage::layout_affected_by_inflow_descendant()) {
137 damage_for_ancestors.insert(LayoutDamage::LAYOUT_AFFECTED_BY_INFLOW_DESCENDANT);
138 }
139
140 let mut maybe_parent_node = unsafe { dirty_root.dangerous_flat_tree_parent() };
141 while let Some(parent_node) = maybe_parent_node {
142 if needs_box_tree_rebuild &&
144 parent_node.rebuild_box_tree_from_independent_formatting_context(layout_context)
145 {
146 needs_box_tree_rebuild = false;
147 }
148
149 if needs_box_tree_rebuild {
150 parent_node.unset_all_boxes();
154 } else if needs_fragment_tree_rebuild {
155 let new_damage_for_ancestors = Cell::new(LayoutDamage::empty());
158 parent_node.with_layout_box_base_including_pseudos(|base| {
159 new_damage_for_ancestors.set(
160 new_damage_for_ancestors.get() |
161 base.add_damage(
162 Default::default(),
163 damage_for_ancestors,
164 RestyleDamage::empty(),
165 ),
166 );
167 });
168 damage_for_ancestors = new_damage_for_ancestors.get();
169
170 if damage_for_ancestors.contains(LayoutDamage::LAYOUT_AFFECTED_BY_INFLOW_DESCENDANT) &&
174 parent_node.is_absolutely_positioned()
175 {
176 damage_for_ancestors.remove(LayoutDamage::LAYOUT_AFFECTED_BY_INFLOW_DESCENDANT);
177 }
178 } else {
179 assert!(needs_overflow_recalculation);
183 parent_node.with_layout_box_base_including_pseudos(|base| {
184 base.clear_scrollable_overflow_all_on_fragments();
185 });
186 }
187
188 maybe_parent_node = unsafe { parent_node.dangerous_flat_tree_parent() };
189 }
190
191 if needs_box_tree_rebuild {
194 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
195 }
196
197 restyle_damage
198}
199
200pub(crate) fn compute_damage_and_rebuild_box_tree_inner(
201 layout_context: &LayoutContext,
202 node: ServoLayoutNode<'_>,
203 damage_from_parent: RestyleDamage,
204) -> RestyleDamage {
205 let Some(element) = node.as_element() else {
208 return damage_from_parent;
209 };
210
211 let (element_damage, is_display_none) = {
212 let mut element_data = element.element_data_mut();
213 (
214 std::mem::take(&mut element_data.damage),
215 element_data.styles.is_display_none(),
216 )
217 };
218
219 let mut element_and_parent_damage = element_damage | damage_from_parent;
220 if is_display_none {
221 node.unset_all_boxes();
222 return element_and_parent_damage;
223 }
224
225 let mut damage_for_children = element_and_parent_damage;
230 damage_for_children.truncate();
231 let rebuild_children = element_damage.contains(LayoutDamage::box_damage()) ||
232 (damage_from_parent.contains(LayoutDamage::box_damage()) &&
233 !node.isolates_damage_for_damage_propagation());
234 if rebuild_children {
235 damage_for_children.insert(LayoutDamage::box_damage());
236 } else if element_and_parent_damage.contains(RestyleDamage::RELAYOUT) &&
237 !element_damage.contains(RestyleDamage::RELAYOUT) &&
238 node.isolates_damage_for_damage_propagation()
239 {
240 damage_for_children.remove(RestyleDamage::RELAYOUT);
244 element_and_parent_damage.remove(RestyleDamage::RELAYOUT);
245 }
246
247 let mut damage_from_children = RestyleDamage::empty();
248 for child in node.flat_tree_children() {
249 if child.is_element() {
250 damage_from_children |= compute_damage_and_rebuild_box_tree_inner(
251 layout_context,
252 child,
253 damage_for_children,
254 );
255 }
256 }
257
258 let mut layout_damage_for_parent = element_and_parent_damage |
262 (damage_from_children &
263 (RestyleDamage::RELAYOUT | LayoutDamage::layout_affected_by_inflow_descendant()));
264
265 let element_or_ancestors_need_rebuild =
266 element_and_parent_damage.contains(LayoutDamage::descendant_has_box_damage());
267 let descendant_needs_rebuild =
268 damage_from_children.contains(LayoutDamage::descendant_has_box_damage());
269 if element_or_ancestors_need_rebuild || descendant_needs_rebuild {
270 if damage_from_parent.contains(LayoutDamage::descendant_has_box_damage()) ||
271 !node.rebuild_box_tree_from_independent_formatting_context(layout_context)
272 {
273 node.unset_all_boxes();
283 layout_damage_for_parent
284 .insert(LayoutDamage::descendant_has_box_damage() | RestyleDamage::RELAYOUT);
285 } else {
286 layout_damage_for_parent.remove(LayoutDamage::box_damage());
289 layout_damage_for_parent
290 .insert(RestyleDamage::RELAYOUT | LayoutDamage::recompute_inline_content_sizes());
291 }
292 } else {
293 if (element_and_parent_damage | damage_from_children).contains(RestyleDamage::RELAYOUT) {
294 let extra_layout_damage_for_parent = Cell::new(LayoutDamage::empty());
299 node.with_layout_box_base_including_pseudos(|base| {
300 extra_layout_damage_for_parent.set(
301 extra_layout_damage_for_parent.get() |
302 base.add_damage(
303 element_damage.into(),
304 damage_from_children.into(),
305 damage_from_parent,
306 ),
307 );
308 });
309 layout_damage_for_parent.insert(extra_layout_damage_for_parent.get().into());
310 } else if (damage_from_children | element_damage)
311 .contains(RestyleDamage::RECALCULATE_OVERFLOW)
312 {
313 node.with_layout_box_base_including_pseudos(|base| {
317 base.clear_scrollable_overflow_all_on_fragments();
318 });
319 }
320
321 if !element_damage.is_empty() {
325 node.repair_style(&layout_context.style_context);
326 }
327 }
328
329 if !layout_damage_for_parent.contains(LayoutDamage::descendant_has_box_damage()) {
333 if element_damage.contains(RestyleDamage::RELAYOUT) {
334 layout_damage_for_parent.insert(LayoutDamage::layout_affected_by_inflow_descendant());
335 }
336
337 if layout_damage_for_parent.contains(LayoutDamage::layout_affected_by_inflow_descendant()) &&
338 node.is_absolutely_positioned()
339 {
340 layout_damage_for_parent.remove(LayoutDamage::layout_affected_by_inflow_descendant());
341 }
342 }
343
344 layout_damage_for_parent
345}