1use std::sync::Arc;
6
7use layout_api::{
8 DangerousStyleElement, DangerousStyleNode, LayoutDamage, LayoutElement, LayoutNode,
9};
10use script::layout_dom::ServoLayoutNode;
11use style::context::{SharedStyleContext, StyleContext};
12use style::dom::{NodeInfo, TElement, TNode};
13use style::selector_parser::RestyleDamage;
14use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
15
16use crate::BoxTree;
17use crate::context::LayoutContext;
18use crate::dom::{DOMLayoutData, NodeExt};
19use crate::layout_root::LayoutRoot;
20
21pub struct RecalcStyle<'a> {
22 context: &'a LayoutContext<'a>,
23}
24
25impl<'a> RecalcStyle<'a> {
26 pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
27 RecalcStyle { context }
28 }
29
30 pub(crate) fn context(&self) -> &LayoutContext<'a> {
31 self.context
32 }
33}
34
35impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
36where
37 E: DangerousStyleElement<'dom> + TElement,
38 E::ConcreteNode: 'dom + DangerousStyleNode<'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 let Some(dangerous_style_element) = node.as_element() else {
50 return;
51 };
52
53 let layout_element = dangerous_style_element.layout_element();
54 let had_style_data = layout_element.style_data().is_some();
55 layout_element.initialize_style_and_layout_data::<DOMLayoutData>();
56
57 let mut element_data = dangerous_style_element.mutate_data().unwrap();
58 if !had_style_data {
59 element_data.damage = RestyleDamage::reconstruct();
60 }
61
62 recalc_style_at(
63 self,
64 traversal_data,
65 context,
66 dangerous_style_element,
67 &mut element_data,
68 note_child,
69 );
70 }
71
72 #[inline]
73 fn needs_postorder_traversal() -> bool {
74 false
75 }
76
77 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
78 panic!("this should never be called")
79 }
80
81 fn shared_context(&self) -> &SharedStyleContext<'_> {
82 &self.context.style_context
83 }
84}
85
86#[servo_tracing::instrument(skip_all)]
87pub(crate) fn compute_damage_and_rebuild_box_tree<'dom>(
88 box_tree: &mut Option<Arc<BoxTree>>,
89 layout_context: &LayoutContext,
90 dirty_root: ServoLayoutNode<'dom>,
91 root_node: ServoLayoutNode<'dom>,
92 damage_from_environment: LayoutDamage,
93 layout_roots: &mut Vec<LayoutRoot<'dom>>,
94) -> LayoutDamage {
95 let layout_damage = compute_damage_and_rebuild_box_tree_below_dirty_root(
98 layout_context,
99 dirty_root,
100 damage_from_environment,
101 layout_roots,
102 );
103
104 if box_tree.is_none() {
107 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
108 return layout_damage;
109 }
110
111 let layout_damage = compute_damage_and_rebuild_box_tree_above_dirty_root(
115 layout_context,
116 dirty_root,
117 layout_damage,
118 layout_roots,
119 );
120
121 if layout_damage.contains(LayoutDamage::DescendantHasBoxDamage) {
124 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
125 }
126
127 layout_damage
128}
129
130#[expect(unsafe_code)]
131#[servo_tracing::instrument(skip_all)]
132pub(crate) fn compute_damage_and_rebuild_box_tree_above_dirty_root<'dom>(
133 layout_context: &LayoutContext,
134 dirty_root: ServoLayoutNode<'dom>,
135 layout_damage: LayoutDamage,
136 layout_roots: &mut Vec<LayoutRoot<'dom>>,
137) -> LayoutDamage {
138 let needs_fragment_tree_rebuild = layout_damage.contains(LayoutDamage::Relayout);
149 let needs_overflow_recalculation = layout_damage.contains(LayoutDamage::RecalculateOverflow);
150 if !needs_fragment_tree_rebuild && !needs_overflow_recalculation {
151 assert!(!layout_damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot));
152 return layout_damage;
153 }
154
155 let mut damage_for_parent = layout_damage;
156 let mut maybe_parent_node = unsafe { dirty_root.dangerous_flat_tree_parent() };
157 while let Some(parent_node) = maybe_parent_node {
158 let damage_set = ElementDamageSet {
159 node: parent_node,
160 from_parent: LayoutDamage::empty(),
161 on_element: LayoutDamage::empty(),
162 from_children: damage_for_parent,
163 incoming_layout_root_count: layout_roots.len(),
167 };
168
169 damage_for_parent = damage_set.apply_damage(layout_context, layout_roots);
170 maybe_parent_node = unsafe { parent_node.dangerous_flat_tree_parent() };
171 }
172
173 damage_for_parent
174}
175
176pub(crate) fn compute_damage_and_rebuild_box_tree_below_dirty_root<'dom>(
177 layout_context: &LayoutContext,
178 node: ServoLayoutNode<'dom>,
179 damage_from_parent: LayoutDamage,
180 layout_roots: &mut Vec<LayoutRoot<'dom>>,
181) -> LayoutDamage {
182 let Some(element) = node.as_element() else {
185 return damage_from_parent;
186 };
187
188 let (element_damage, is_display_none) = {
189 let mut element_data = element.element_data_mut();
190 (
191 LayoutDamage::from(std::mem::take(&mut element_data.damage)),
192 element_data.styles.is_display_none(),
193 )
194 };
195
196 let has_dirty_descendants;
197 #[expect(unsafe_code)]
198 unsafe {
199 let dangerous_style_element = element.dangerous_style_element();
200 has_dirty_descendants = dangerous_style_element.has_dirty_descendants();
201 dangerous_style_element.unset_dirty_descendants();
202 };
203
204 if is_display_none {
205 node.unset_all_boxes();
206 return element_damage | damage_from_parent;
207 }
208
209 let mut damage_set = ElementDamageSet {
210 node,
211 from_parent: damage_from_parent,
212 on_element: element_damage,
213 from_children: LayoutDamage::empty(),
214 incoming_layout_root_count: layout_roots.len(),
215 };
216
217 let damage_for_children = damage_set.isolate_incoming_damage();
220
221 damage_set.propagate_damage_to_children(
223 layout_context,
224 has_dirty_descendants,
225 damage_for_children,
226 layout_roots,
227 );
228
229 damage_set.apply_damage(layout_context, layout_roots)
232}
233
234enum BoxDamageAction<'a> {
235 RebuildAncestor,
236 TryRebuild,
237 InvalidateFragmentTreeBelowLayoutRoot,
238 CollectLayoutRoot(LayoutRoot<'a>),
239 InvalidateFragmentTreeAboveLayoutRoot,
240 InvalidateScrollableOverflow,
241 None,
242}
243
244impl BoxDamageAction<'_> {
245 fn rebuilds_box(&self) -> bool {
246 matches!(self, Self::RebuildAncestor | Self::TryRebuild)
247 }
248}
249
250pub(crate) struct ElementDamageSet<'a> {
251 node: ServoLayoutNode<'a>,
252 pub from_parent: LayoutDamage,
253 pub on_element: LayoutDamage,
254 pub from_children: LayoutDamage,
255 pub incoming_layout_root_count: usize,
256}
257
258impl<'a> ElementDamageSet<'a> {
259 fn isolate_incoming_damage(&mut self) -> LayoutDamage {
262 let mut damage_for_children = (self.on_element | self.from_parent).only_layout_modes();
267 let rebuild_children = self.on_element.contains(LayoutDamage::BoxDamage) ||
268 (self.from_parent.contains(LayoutDamage::BoxDamage) &&
269 !self.node.isolates_damage_for_damage_propagation());
270
271 if rebuild_children {
272 damage_for_children.insert(LayoutDamage::BoxDamage);
273 } else if self.from_parent.contains(LayoutDamage::Relayout) &&
274 !self.on_element.contains(LayoutDamage::Relayout) &&
275 self.node.isolates_damage_for_damage_propagation()
276 {
277 damage_for_children.remove(LayoutDamage::Relayout);
281 self.from_parent.remove(LayoutDamage::Relayout);
282 }
283
284 damage_for_children
285 }
286
287 fn propagate_damage_to_children(
291 &mut self,
292 layout_context: &LayoutContext<'_>,
293 has_dirty_descendants: bool,
294 damage_for_children: LayoutDamage,
295 layout_roots: &mut Vec<LayoutRoot<'a>>,
296 ) {
297 if has_dirty_descendants ||
305 damage_for_children.intersects(LayoutDamage::BoxDamage | LayoutDamage::Relayout)
306 {
307 for child in self.node.flat_tree_children() {
308 if child.is_element() {
309 self.from_children |= compute_damage_and_rebuild_box_tree_below_dirty_root(
310 layout_context,
311 child,
312 damage_for_children,
313 layout_roots,
314 );
315 }
316 }
317 }
318 }
319
320 fn apply_damage(
323 self,
324 layout_context: &LayoutContext<'_>,
325 layout_roots: &mut Vec<LayoutRoot<'a>>,
326 ) -> LayoutDamage {
327 let only_layout_mode_damage =
328 (self.from_parent | self.on_element | self.from_children).only_layout_modes();
329
330 let invalidate_for_rebuild = || {
331 self.node.unset_all_boxes();
332 LayoutDamage::DescendantHasBoxDamage | LayoutDamage::Relayout
333 };
334
335 let discard_any_descendant_layout_roots = |layout_roots: &mut Vec<LayoutRoot>| {
337 layout_roots.truncate(self.incoming_layout_root_count);
338 };
339
340 let action = self.box_damage_action();
341 let will_rebuild_box = action.rebuilds_box();
342 let damage_for_parent = match action {
343 BoxDamageAction::TryRebuild => {
344 discard_any_descendant_layout_roots(layout_roots);
345
346 if self
347 .node
348 .rebuild_box_tree_from_independent_formatting_context(layout_context)
349 {
350 LayoutDamage::Relayout | LayoutDamage::RecomputeInlineContentSizes
353 } else {
354 invalidate_for_rebuild()
359 }
360 },
361 BoxDamageAction::RebuildAncestor => {
362 discard_any_descendant_layout_roots(layout_roots);
369 invalidate_for_rebuild()
370 },
371 BoxDamageAction::InvalidateFragmentTreeBelowLayoutRoot => {
372 discard_any_descendant_layout_roots(layout_roots);
377
378 let mut damage_for_parent =
379 (self.on_element | self.from_children) | self.from_parent.only_layout_modes();
380
381 damage_for_parent.remove(LayoutDamage::DescendantCollectedAsLayoutRoot);
386
387 let mut inline_size_depends_on_content = false;
388 self.node.with_layout_box_base_including_pseudos(|base| {
389 inline_size_depends_on_content |=
390 base.invalidate_caches_for_fragment_tree_layout(&self);
391 });
392
393 self.adjust_inline_content_size_damage(
394 &mut damage_for_parent,
395 inline_size_depends_on_content,
396 );
397
398 damage_for_parent
399 },
400 BoxDamageAction::CollectLayoutRoot(layout_root) => {
401 debug_assert!(!self.from_parent.contains(LayoutDamage::Relayout));
408
409 discard_any_descendant_layout_roots(layout_roots);
413 layout_roots.push(layout_root);
414
415 self.node.with_layout_box_base_including_pseudos(|base| {
416 base.invalidate_caches(&self);
417 base.mark_fragments_as_descendants_changed();
418 });
419 LayoutDamage::RecalculateOverflow |
420 LayoutDamage::DescendantCollectedAsLayoutRoot |
421 LayoutDamage::RecomputeInlineContentSizes
422 },
423 BoxDamageAction::InvalidateFragmentTreeAboveLayoutRoot => {
424 let mut damage_for_parent = LayoutDamage::RecalculateOverflow |
428 LayoutDamage::DescendantCollectedAsLayoutRoot;
429
430 let mut inline_size_depends_on_content = false;
431 self.node.with_layout_box_base_including_pseudos(|base| {
432 inline_size_depends_on_content |= base.invalidate_caches(&self);
433 base.mark_fragments_as_descendants_changed();
434 });
435 self.adjust_inline_content_size_damage(
436 &mut damage_for_parent,
437 inline_size_depends_on_content,
438 );
439
440 damage_for_parent
441 },
442 BoxDamageAction::InvalidateScrollableOverflow => {
443 self.node.with_layout_box_base_including_pseudos(|base| {
447 base.clear_scrollable_overflow_all_on_fragments();
448 });
449 only_layout_mode_damage
450 },
451 BoxDamageAction::None => only_layout_mode_damage,
452 };
453
454 if !self.on_element.is_empty() && !will_rebuild_box {
458 self.node.repair_style(&layout_context.style_context);
459 }
460
461 damage_for_parent
462 }
463
464 fn box_damage_action(&self) -> BoxDamageAction<'a> {
465 if self
467 .from_parent
468 .contains(LayoutDamage::DescendantHasBoxDamage)
469 {
470 return BoxDamageAction::RebuildAncestor;
471 }
472
473 let element_and_children_damage = self.on_element | self.from_children;
476 if element_and_children_damage.contains(LayoutDamage::DescendantHasBoxDamage) {
477 return BoxDamageAction::TryRebuild;
478 }
479
480 if element_and_children_damage.contains(LayoutDamage::Relayout) &&
481 !self.from_parent.contains(LayoutDamage::Relayout) &&
482 let Ok(layout_root) = LayoutRoot::try_from(self.node)
483 {
484 return BoxDamageAction::CollectLayoutRoot(layout_root);
485 }
486
487 if (self.from_parent | element_and_children_damage).contains(LayoutDamage::Relayout) {
490 return BoxDamageAction::InvalidateFragmentTreeBelowLayoutRoot;
491 }
492
493 if self
496 .from_children
497 .contains(LayoutDamage::DescendantCollectedAsLayoutRoot)
498 {
499 return BoxDamageAction::InvalidateFragmentTreeAboveLayoutRoot;
500 }
501
502 if element_and_children_damage.contains(LayoutDamage::RecalculateOverflow) {
505 return BoxDamageAction::InvalidateScrollableOverflow;
506 }
507
508 BoxDamageAction::None
509 }
510
511 fn adjust_inline_content_size_damage(
512 &self,
513 damage_for_parent: &mut LayoutDamage,
514 inline_size_depends_on_content: bool,
515 ) {
516 let children_need_inline_content_size_recalculation = self
517 .from_children
518 .contains(LayoutDamage::RecomputeInlineContentSizes) &&
519 inline_size_depends_on_content;
520 damage_for_parent.set(
521 LayoutDamage::RecomputeInlineContentSizes,
522 !self.on_element.is_empty() || children_need_inline_content_size_recalculation,
523 );
524 }
525}