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, 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 context: &mut StyleContext<E>,
43 node: E::ConcreteNode,
44 note_child: F,
45 ) where
46 F: FnMut(E::ConcreteNode),
47 {
48 let Some(dangerous_style_element) = node.as_element() else {
49 return;
50 };
51
52 let layout_element = dangerous_style_element.layout_element();
53 let had_style_data = layout_element.style_data().is_some();
54 layout_element.initialize_style_and_layout_data::<DOMLayoutData>();
55
56 let mut element_data = dangerous_style_element.mutate_data().unwrap();
57 if !had_style_data {
58 element_data.damage = RestyleDamage::reconstruct();
59 }
60
61 recalc_style_at(
62 self,
63 context,
64 dangerous_style_element,
65 &mut element_data,
66 note_child,
67 );
68 }
69
70 #[inline]
71 fn needs_postorder_traversal() -> bool {
72 false
73 }
74
75 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
76 panic!("this should never be called")
77 }
78
79 fn shared_context(&self) -> &SharedStyleContext<'_> {
80 &self.context.style_context
81 }
82}
83
84#[servo_tracing::instrument(skip_all)]
85pub(crate) fn compute_damage_and_rebuild_box_tree<'dom>(
86 box_tree: &mut Option<Arc<BoxTree>>,
87 layout_context: &LayoutContext,
88 dirty_root: ServoLayoutNode<'dom>,
89 root_node: ServoLayoutNode<'dom>,
90 damage_from_environment: LayoutDamage,
91 layout_roots: &mut Vec<LayoutRoot<'dom>>,
92) -> LayoutDamage {
93 let layout_damage = compute_damage_and_rebuild_box_tree_below_dirty_root(
96 layout_context,
97 dirty_root,
98 damage_from_environment,
99 layout_roots,
100 );
101
102 if box_tree.is_none() {
105 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
106 return layout_damage;
107 }
108
109 let layout_damage = compute_damage_and_rebuild_box_tree_above_dirty_root(
113 layout_context,
114 dirty_root,
115 layout_damage,
116 layout_roots,
117 );
118
119 if layout_damage.contains(LayoutDamage::DescendantHasBoxDamage) {
122 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
123 }
124
125 layout_damage
126}
127
128#[expect(unsafe_code)]
129#[servo_tracing::instrument(skip_all)]
130pub(crate) fn compute_damage_and_rebuild_box_tree_above_dirty_root<'dom>(
131 layout_context: &LayoutContext,
132 dirty_root: ServoLayoutNode<'dom>,
133 layout_damage: LayoutDamage,
134 layout_roots: &mut Vec<LayoutRoot<'dom>>,
135) -> LayoutDamage {
136 let needs_fragment_tree_rebuild = layout_damage.contains(LayoutDamage::Relayout);
147 let needs_overflow_recalculation = layout_damage.contains(LayoutDamage::RecalculateOverflow);
148 if !needs_fragment_tree_rebuild && !needs_overflow_recalculation {
149 assert!(!layout_damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot));
150 return layout_damage;
151 }
152
153 let mut damage_for_parent = layout_damage;
154 let mut maybe_parent_node = unsafe { dirty_root.dangerous_flat_tree_parent() };
155 while let Some(parent_node) = maybe_parent_node {
156 let damage_set = ElementDamageSet {
157 node: parent_node,
158 from_parent: LayoutDamage::empty(),
159 on_element: LayoutDamage::empty(),
160 from_children: damage_for_parent,
161 incoming_layout_root_count: layout_roots.len(),
165 };
166
167 damage_for_parent = damage_set.apply_damage(layout_context, layout_roots);
168 maybe_parent_node = unsafe { parent_node.dangerous_flat_tree_parent() };
169 }
170
171 damage_for_parent
172}
173
174pub(crate) fn compute_damage_and_rebuild_box_tree_below_dirty_root<'dom>(
175 layout_context: &LayoutContext,
176 node: ServoLayoutNode<'dom>,
177 damage_from_parent: LayoutDamage,
178 layout_roots: &mut Vec<LayoutRoot<'dom>>,
179) -> LayoutDamage {
180 let Some(element) = node.as_element() else {
183 return damage_from_parent;
184 };
185
186 let (element_damage, is_display_none) = {
187 let mut element_data = element.element_data_mut();
188 (
189 LayoutDamage::from(std::mem::take(&mut element_data.damage)),
190 element_data.styles.is_display_none(),
191 )
192 };
193
194 let has_dirty_descendants;
195 #[expect(unsafe_code)]
196 unsafe {
197 let dangerous_style_element = element.dangerous_style_element();
198 has_dirty_descendants = dangerous_style_element.has_dirty_descendants();
199 dangerous_style_element.unset_dirty_descendants();
200 };
201
202 if is_display_none {
203 node.unset_all_boxes();
204 return element_damage | damage_from_parent;
205 }
206
207 let mut damage_set = ElementDamageSet {
208 node,
209 from_parent: damage_from_parent,
210 on_element: element_damage,
211 from_children: LayoutDamage::empty(),
212 incoming_layout_root_count: layout_roots.len(),
213 };
214
215 let damage_for_children = damage_set.isolate_incoming_damage();
218
219 damage_set.propagate_damage_to_children(
221 layout_context,
222 has_dirty_descendants,
223 damage_for_children,
224 layout_roots,
225 );
226
227 damage_set.apply_damage(layout_context, layout_roots)
230}
231
232enum BoxDamageAction<'a> {
233 RebuildAncestor,
234 TryRebuild,
235 InvalidateFragmentTreeBelowLayoutRoot,
236 CollectLayoutRoot(LayoutRoot<'a>),
237 InvalidateFragmentTreeAboveLayoutRoot,
238 InvalidateScrollableOverflow,
239 None,
240}
241
242impl BoxDamageAction<'_> {
243 fn rebuilds_box(&self) -> bool {
244 matches!(self, Self::RebuildAncestor | Self::TryRebuild)
245 }
246}
247
248pub(crate) struct ElementDamageSet<'a> {
249 node: ServoLayoutNode<'a>,
250 pub from_parent: LayoutDamage,
251 pub on_element: LayoutDamage,
252 pub from_children: LayoutDamage,
253 pub incoming_layout_root_count: usize,
254}
255
256impl<'a> ElementDamageSet<'a> {
257 fn isolate_incoming_damage(&mut self) -> LayoutDamage {
260 let mut damage_for_children = (self.on_element | self.from_parent).only_layout_modes();
265 let rebuild_children = self.on_element.contains(LayoutDamage::BoxDamage) ||
266 (self.from_parent.contains(LayoutDamage::BoxDamage) &&
267 !self.node.isolates_damage_for_damage_propagation());
268
269 if rebuild_children {
270 damage_for_children.insert(LayoutDamage::BoxDamage);
271 } else if self.from_parent.contains(LayoutDamage::Relayout) &&
272 !self.on_element.contains(LayoutDamage::Relayout) &&
273 self.node.isolates_damage_for_damage_propagation()
274 {
275 damage_for_children.remove(LayoutDamage::Relayout);
279 self.from_parent.remove(LayoutDamage::Relayout);
280 }
281
282 damage_for_children
283 }
284
285 fn propagate_damage_to_children(
289 &mut self,
290 layout_context: &LayoutContext<'_>,
291 has_dirty_descendants: bool,
292 damage_for_children: LayoutDamage,
293 layout_roots: &mut Vec<LayoutRoot<'a>>,
294 ) {
295 if has_dirty_descendants ||
303 damage_for_children.intersects(LayoutDamage::BoxDamage | LayoutDamage::Relayout)
304 {
305 for child in self.node.flat_tree_children() {
306 if child.is_element() {
307 self.from_children |= compute_damage_and_rebuild_box_tree_below_dirty_root(
308 layout_context,
309 child,
310 damage_for_children,
311 layout_roots,
312 );
313 }
314 }
315 }
316 }
317
318 fn apply_damage(
321 self,
322 layout_context: &LayoutContext<'_>,
323 layout_roots: &mut Vec<LayoutRoot<'a>>,
324 ) -> LayoutDamage {
325 let only_layout_mode_damage =
326 (self.from_parent | self.on_element | self.from_children).only_layout_modes();
327
328 let invalidate_for_rebuild = || {
329 self.node.unset_all_boxes();
330 LayoutDamage::DescendantHasBoxDamage | LayoutDamage::Relayout
331 };
332
333 let discard_any_descendant_layout_roots = |layout_roots: &mut Vec<LayoutRoot>| {
335 layout_roots.truncate(self.incoming_layout_root_count);
336 };
337
338 let action = self.box_damage_action();
339 let will_rebuild_box = action.rebuilds_box();
340 let damage_for_parent = match action {
341 BoxDamageAction::TryRebuild => {
342 discard_any_descendant_layout_roots(layout_roots);
343
344 if self
345 .node
346 .rebuild_box_tree_from_independent_formatting_context(layout_context)
347 {
348 LayoutDamage::Relayout | LayoutDamage::RecomputeInlineContentSizes
351 } else {
352 invalidate_for_rebuild()
357 }
358 },
359 BoxDamageAction::RebuildAncestor => {
360 discard_any_descendant_layout_roots(layout_roots);
367 invalidate_for_rebuild()
368 },
369 BoxDamageAction::InvalidateFragmentTreeBelowLayoutRoot => {
370 discard_any_descendant_layout_roots(layout_roots);
375
376 let mut damage_for_parent =
377 (self.on_element | self.from_children) | self.from_parent.only_layout_modes();
378
379 damage_for_parent.remove(LayoutDamage::DescendantCollectedAsLayoutRoot);
384
385 let mut inline_size_depends_on_content = false;
386 self.node.with_layout_box_base_including_pseudos(|base| {
387 inline_size_depends_on_content |=
388 base.invalidate_caches_for_fragment_tree_layout(&self);
389 });
390
391 self.adjust_inline_content_size_damage(
392 &mut damage_for_parent,
393 inline_size_depends_on_content,
394 );
395
396 damage_for_parent
397 },
398 BoxDamageAction::CollectLayoutRoot(layout_root) => {
399 debug_assert!(!self.from_parent.contains(LayoutDamage::Relayout));
406
407 discard_any_descendant_layout_roots(layout_roots);
411 layout_roots.push(layout_root);
412
413 self.node.with_layout_box_base_including_pseudos(|base| {
414 base.invalidate_caches(&self);
415 base.mark_fragments_as_descendants_changed();
416 });
417 LayoutDamage::RecalculateOverflow |
418 LayoutDamage::DescendantCollectedAsLayoutRoot |
419 LayoutDamage::RecomputeInlineContentSizes
420 },
421 BoxDamageAction::InvalidateFragmentTreeAboveLayoutRoot => {
422 let mut damage_for_parent = LayoutDamage::RecalculateOverflow |
426 LayoutDamage::DescendantCollectedAsLayoutRoot;
427
428 let mut inline_size_depends_on_content = false;
429 self.node.with_layout_box_base_including_pseudos(|base| {
430 inline_size_depends_on_content |= base.invalidate_caches(&self);
431 base.mark_fragments_as_descendants_changed();
432 });
433 self.adjust_inline_content_size_damage(
434 &mut damage_for_parent,
435 inline_size_depends_on_content,
436 );
437
438 damage_for_parent
439 },
440 BoxDamageAction::InvalidateScrollableOverflow => {
441 self.node.with_layout_box_base_including_pseudos(|base| {
445 base.clear_scrollable_overflow_all_on_fragments();
446 });
447 only_layout_mode_damage
448 },
449 BoxDamageAction::None => only_layout_mode_damage,
450 };
451
452 if !self.on_element.is_empty() && !will_rebuild_box {
456 self.node.repair_style(&layout_context.style_context);
457 }
458
459 damage_for_parent
460 }
461
462 fn box_damage_action(&self) -> BoxDamageAction<'a> {
463 if self
465 .from_parent
466 .contains(LayoutDamage::DescendantHasBoxDamage)
467 {
468 return BoxDamageAction::RebuildAncestor;
469 }
470
471 let element_and_children_damage = self.on_element | self.from_children;
474 if element_and_children_damage.contains(LayoutDamage::DescendantHasBoxDamage) {
475 return BoxDamageAction::TryRebuild;
476 }
477
478 if element_and_children_damage.contains(LayoutDamage::Relayout) &&
479 !self.from_parent.contains(LayoutDamage::Relayout) &&
480 let Ok(layout_root) = LayoutRoot::try_from(self.node)
481 {
482 return BoxDamageAction::CollectLayoutRoot(layout_root);
483 }
484
485 if (self.from_parent | element_and_children_damage).contains(LayoutDamage::Relayout) {
488 return BoxDamageAction::InvalidateFragmentTreeBelowLayoutRoot;
489 }
490
491 if self
494 .from_children
495 .contains(LayoutDamage::DescendantCollectedAsLayoutRoot)
496 {
497 return BoxDamageAction::InvalidateFragmentTreeAboveLayoutRoot;
498 }
499
500 if element_and_children_damage.contains(LayoutDamage::RecalculateOverflow) {
503 return BoxDamageAction::InvalidateScrollableOverflow;
504 }
505
506 BoxDamageAction::None
507 }
508
509 fn adjust_inline_content_size_damage(
510 &self,
511 damage_for_parent: &mut LayoutDamage,
512 inline_size_depends_on_content: bool,
513 ) {
514 let children_need_inline_content_size_recalculation = self
515 .from_children
516 .contains(LayoutDamage::RecomputeInlineContentSizes) &&
517 inline_size_depends_on_content;
518 damage_for_parent.set(
519 LayoutDamage::RecomputeInlineContentSizes,
520 !self.on_element.is_empty() || children_need_inline_content_size_recalculation,
521 );
522 }
523}