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::data::ElementData;
13use style::dom::{NodeInfo, TElement, TNode};
14use style::selector_parser::RestyleDamage;
15use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
16
17use crate::BoxTree;
18use crate::context::LayoutContext;
19use crate::dom::{DOMLayoutData, NodeExt};
20use crate::layout_root::LayoutRoot;
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
36impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
37where
38 E: DangerousStyleElement<'dom> + TElement,
39 E::ConcreteNode: 'dom + DangerousStyleNode<'dom>,
40{
41 fn process_preorder<F>(
42 &self,
43 traversal_data: &PerLevelTraversalData,
44 context: &mut StyleContext<E>,
45 node: E::ConcreteNode,
46 note_child: F,
47 ) where
48 F: FnMut(E::ConcreteNode),
49 {
50 let Some(dangerous_style_element) = node.as_element() else {
51 return;
52 };
53
54 let layout_element = dangerous_style_element.layout_element();
55 let had_style_data = layout_element.style_data().is_some();
56 layout_element.initialize_style_and_layout_data::<DOMLayoutData>();
57
58 let mut element_data = dangerous_style_element.mutate_data().unwrap();
59 if !had_style_data {
60 element_data.damage = RestyleDamage::reconstruct();
61 }
62
63 recalc_style_at(
64 self,
65 traversal_data,
66 context,
67 dangerous_style_element,
68 &mut element_data,
69 note_child,
70 );
71 }
72
73 #[inline]
74 fn needs_postorder_traversal() -> bool {
75 false
76 }
77
78 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
79 panic!("this should never be called")
80 }
81
82 fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
83 node.layout_node().layout_data().is_none() || !parent_data.damage.is_empty()
84 }
85
86 fn shared_context(&self) -> &SharedStyleContext<'_> {
87 &self.context.style_context
88 }
89}
90
91#[servo_tracing::instrument(skip_all)]
92pub(crate) fn compute_damage_and_rebuild_box_tree<'dom>(
93 box_tree: &mut Option<Arc<BoxTree>>,
94 layout_context: &LayoutContext,
95 dirty_root: ServoLayoutNode<'dom>,
96 root_node: ServoLayoutNode<'dom>,
97 damage_from_environment: LayoutDamage,
98 layout_roots: &mut Vec<LayoutRoot<'dom>>,
99) -> LayoutDamage {
100 let layout_damage = compute_damage_and_rebuild_box_tree_below_dirty_root(
103 layout_context,
104 dirty_root,
105 damage_from_environment,
106 layout_roots,
107 );
108
109 if box_tree.is_none() {
112 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
113 return layout_damage;
114 }
115
116 let layout_damage = compute_damage_and_rebuild_box_tree_above_dirty_root(
120 layout_context,
121 dirty_root,
122 layout_damage,
123 layout_roots,
124 );
125
126 if layout_damage.contains(LayoutDamage::DescendantHasBoxDamage) {
129 *box_tree = Some(Arc::new(BoxTree::construct(layout_context, root_node)));
130 }
131
132 layout_damage
133}
134
135#[expect(unsafe_code)]
136#[servo_tracing::instrument(skip_all)]
137pub(crate) fn compute_damage_and_rebuild_box_tree_above_dirty_root<'dom>(
138 layout_context: &LayoutContext,
139 dirty_root: ServoLayoutNode<'dom>,
140 layout_damage: LayoutDamage,
141 layout_roots: &mut Vec<LayoutRoot<'dom>>,
142) -> LayoutDamage {
143 let needs_fragment_tree_rebuild = layout_damage.contains(LayoutDamage::Relayout);
154 let needs_overflow_recalculation = layout_damage.contains(LayoutDamage::RecalculateOverflow);
155 if !needs_fragment_tree_rebuild && !needs_overflow_recalculation {
156 assert!(!layout_damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot));
157 return layout_damage;
158 }
159
160 let mut damage_for_parent = layout_damage;
161 let mut maybe_parent_node = unsafe { dirty_root.dangerous_flat_tree_parent() };
162 while let Some(parent_node) = maybe_parent_node {
163 let damage_set = ElementDamageSet {
164 node: parent_node,
165 from_parent: LayoutDamage::empty(),
166 on_element: LayoutDamage::empty(),
167 from_children: damage_for_parent,
168 incoming_layout_root_count: layout_roots.len(),
172 };
173
174 damage_for_parent = damage_set.apply_damage(layout_context, layout_roots);
175 maybe_parent_node = unsafe { parent_node.dangerous_flat_tree_parent() };
176 }
177
178 damage_for_parent
179}
180
181pub(crate) fn compute_damage_and_rebuild_box_tree_below_dirty_root<'dom>(
182 layout_context: &LayoutContext,
183 node: ServoLayoutNode<'dom>,
184 damage_from_parent: LayoutDamage,
185 layout_roots: &mut Vec<LayoutRoot<'dom>>,
186) -> LayoutDamage {
187 let Some(element) = node.as_element() else {
190 return damage_from_parent;
191 };
192
193 let (element_damage, is_display_none) = {
194 let mut element_data = element.element_data_mut();
195 (
196 LayoutDamage::from(std::mem::take(&mut element_data.damage)),
197 element_data.styles.is_display_none(),
198 )
199 };
200
201 let has_dirty_descendants;
202 #[expect(unsafe_code)]
203 unsafe {
204 let dangerous_style_element = element.dangerous_style_element();
205 has_dirty_descendants = dangerous_style_element.has_dirty_descendants();
206 dangerous_style_element.unset_dirty_descendants();
207 };
208
209 if is_display_none {
210 node.unset_all_boxes();
211 return element_damage | damage_from_parent;
212 }
213
214 let mut damage_set = ElementDamageSet {
215 node,
216 from_parent: damage_from_parent,
217 on_element: element_damage,
218 from_children: LayoutDamage::empty(),
219 incoming_layout_root_count: layout_roots.len(),
220 };
221
222 let damage_for_children = damage_set.isolate_incoming_damage();
225
226 damage_set.propagate_damage_to_children(
228 layout_context,
229 has_dirty_descendants,
230 damage_for_children,
231 layout_roots,
232 );
233
234 damage_set.apply_damage(layout_context, layout_roots)
237}
238
239enum BoxDamageAction<'a> {
240 RebuildAncestor,
241 TryRebuild,
242 InvalidateFragmentTreeBelowLayoutRoot,
243 CollectLayoutRoot(LayoutRoot<'a>),
244 InvalidateFragmentTreeAboveLayoutRoot,
245 InvalidateScrollableOverflow,
246 None,
247}
248
249impl BoxDamageAction<'_> {
250 fn rebuilds_box(&self) -> bool {
251 matches!(self, Self::RebuildAncestor | Self::TryRebuild)
252 }
253}
254
255pub(crate) struct ElementDamageSet<'a> {
256 node: ServoLayoutNode<'a>,
257 pub from_parent: LayoutDamage,
258 pub on_element: LayoutDamage,
259 pub from_children: LayoutDamage,
260 pub incoming_layout_root_count: usize,
261}
262
263impl<'a> ElementDamageSet<'a> {
264 fn isolate_incoming_damage(&mut self) -> LayoutDamage {
267 let mut damage_for_children = (self.on_element | self.from_parent).only_layout_modes();
272 let rebuild_children = self.on_element.contains(LayoutDamage::BoxDamage) ||
273 (self.from_parent.contains(LayoutDamage::BoxDamage) &&
274 !self.node.isolates_damage_for_damage_propagation());
275
276 if rebuild_children {
277 damage_for_children.insert(LayoutDamage::BoxDamage);
278 } else if self.from_parent.contains(LayoutDamage::Relayout) &&
279 !self.on_element.contains(LayoutDamage::Relayout) &&
280 self.node.isolates_damage_for_damage_propagation()
281 {
282 damage_for_children.remove(LayoutDamage::Relayout);
286 self.from_parent.remove(LayoutDamage::Relayout);
287 }
288
289 damage_for_children
290 }
291
292 fn propagate_damage_to_children(
296 &mut self,
297 layout_context: &LayoutContext<'_>,
298 has_dirty_descendants: bool,
299 damage_for_children: LayoutDamage,
300 layout_roots: &mut Vec<LayoutRoot<'a>>,
301 ) {
302 if has_dirty_descendants ||
310 damage_for_children.intersects(LayoutDamage::BoxDamage | LayoutDamage::Relayout)
311 {
312 for child in self.node.flat_tree_children() {
313 if child.is_element() {
314 self.from_children |= compute_damage_and_rebuild_box_tree_below_dirty_root(
315 layout_context,
316 child,
317 damage_for_children,
318 layout_roots,
319 );
320 }
321 }
322 }
323 }
324
325 fn apply_damage(
328 self,
329 layout_context: &LayoutContext<'_>,
330 layout_roots: &mut Vec<LayoutRoot<'a>>,
331 ) -> LayoutDamage {
332 let only_layout_mode_damage =
333 (self.from_parent | self.on_element | self.from_children).only_layout_modes();
334
335 let invalidate_for_rebuild = || {
336 self.node.unset_all_boxes();
337 LayoutDamage::DescendantHasBoxDamage | LayoutDamage::Relayout
338 };
339
340 let discard_any_descendant_layout_roots = |layout_roots: &mut Vec<LayoutRoot>| {
342 layout_roots.truncate(self.incoming_layout_root_count);
343 };
344
345 let action = self.box_damage_action();
346 let will_rebuild_box = action.rebuilds_box();
347 let damage_for_parent = match action {
348 BoxDamageAction::TryRebuild => {
349 discard_any_descendant_layout_roots(layout_roots);
350
351 if self
352 .node
353 .rebuild_box_tree_from_independent_formatting_context(layout_context)
354 {
355 LayoutDamage::Relayout | LayoutDamage::RecomputeInlineContentSizes
358 } else {
359 invalidate_for_rebuild()
364 }
365 },
366 BoxDamageAction::RebuildAncestor => {
367 discard_any_descendant_layout_roots(layout_roots);
374 invalidate_for_rebuild()
375 },
376 BoxDamageAction::InvalidateFragmentTreeBelowLayoutRoot => {
377 discard_any_descendant_layout_roots(layout_roots);
382
383 let mut damage_for_parent =
384 (self.on_element | self.from_children) | self.from_parent.only_layout_modes();
385
386 damage_for_parent.remove(LayoutDamage::DescendantCollectedAsLayoutRoot);
391
392 let mut inline_size_depends_on_content = false;
393 self.node.with_layout_box_base_including_pseudos(|base| {
394 inline_size_depends_on_content |=
395 base.invalidate_caches_for_fragment_tree_layout(&self);
396 });
397
398 self.adjust_inline_content_size_damage(
399 &mut damage_for_parent,
400 inline_size_depends_on_content,
401 );
402
403 damage_for_parent
404 },
405 BoxDamageAction::CollectLayoutRoot(layout_root) => {
406 debug_assert!(!self.from_parent.contains(LayoutDamage::Relayout));
413
414 discard_any_descendant_layout_roots(layout_roots);
418 layout_roots.push(layout_root);
419
420 self.node.with_layout_box_base_including_pseudos(|base| {
421 base.invalidate_caches(&self);
422 base.mark_fragments_as_descendants_changed();
423 });
424 LayoutDamage::RecalculateOverflow |
425 LayoutDamage::DescendantCollectedAsLayoutRoot |
426 LayoutDamage::RecomputeInlineContentSizes
427 },
428 BoxDamageAction::InvalidateFragmentTreeAboveLayoutRoot => {
429 let mut damage_for_parent = LayoutDamage::RecalculateOverflow |
433 LayoutDamage::DescendantCollectedAsLayoutRoot;
434
435 let mut inline_size_depends_on_content = false;
436 self.node.with_layout_box_base_including_pseudos(|base| {
437 inline_size_depends_on_content |= base.invalidate_caches(&self);
438 base.mark_fragments_as_descendants_changed();
439 });
440 self.adjust_inline_content_size_damage(
441 &mut damage_for_parent,
442 inline_size_depends_on_content,
443 );
444
445 damage_for_parent
446 },
447 BoxDamageAction::InvalidateScrollableOverflow => {
448 self.node.with_layout_box_base_including_pseudos(|base| {
452 base.clear_scrollable_overflow_all_on_fragments();
453 });
454 only_layout_mode_damage
455 },
456 BoxDamageAction::None => only_layout_mode_damage,
457 };
458
459 if !self.on_element.is_empty() && !will_rebuild_box {
463 self.node.repair_style(&layout_context.style_context);
464 }
465
466 damage_for_parent
467 }
468
469 fn box_damage_action(&self) -> BoxDamageAction<'a> {
470 if self
472 .from_parent
473 .contains(LayoutDamage::DescendantHasBoxDamage)
474 {
475 return BoxDamageAction::RebuildAncestor;
476 }
477
478 let element_and_children_damage = self.on_element | self.from_children;
481 if element_and_children_damage.contains(LayoutDamage::DescendantHasBoxDamage) {
482 return BoxDamageAction::TryRebuild;
483 }
484
485 if element_and_children_damage.contains(LayoutDamage::Relayout) &&
486 !self.from_parent.contains(LayoutDamage::Relayout) &&
487 let Ok(layout_root) = LayoutRoot::try_from(self.node)
488 {
489 return BoxDamageAction::CollectLayoutRoot(layout_root);
490 }
491
492 if (self.from_parent | element_and_children_damage).contains(LayoutDamage::Relayout) {
495 return BoxDamageAction::InvalidateFragmentTreeBelowLayoutRoot;
496 }
497
498 if self
501 .from_children
502 .contains(LayoutDamage::DescendantCollectedAsLayoutRoot)
503 {
504 return BoxDamageAction::InvalidateFragmentTreeAboveLayoutRoot;
505 }
506
507 if element_and_children_damage.contains(LayoutDamage::RecalculateOverflow) {
510 return BoxDamageAction::InvalidateScrollableOverflow;
511 }
512
513 BoxDamageAction::None
514 }
515
516 fn adjust_inline_content_size_damage(
517 &self,
518 damage_for_parent: &mut LayoutDamage,
519 inline_size_depends_on_content: bool,
520 ) {
521 let children_need_inline_content_size_recalculation = self
522 .from_children
523 .contains(LayoutDamage::RecomputeInlineContentSizes) &&
524 inline_size_depends_on_content;
525 damage_for_parent.set(
526 LayoutDamage::RecomputeInlineContentSizes,
527 !self.on_element.is_empty() || children_need_inline_content_size_recalculation,
528 );
529 }
530}