1use bitflags::Flags;
6use layout_api::LayoutDamage;
7use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
8use script::layout_dom::ServoThreadSafeLayoutNode;
9use style::context::{SharedStyleContext, StyleContext};
10use style::data::ElementData;
11use style::dom::{NodeInfo, TElement, TNode};
12use style::selector_parser::RestyleDamage;
13use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
14use style::values::computed::Display;
15
16use crate::context::LayoutContext;
17use crate::dom::{DOMLayoutData, NodeExt};
18use crate::layout_box_base::LayoutBoxBase;
19
20pub struct RecalcStyle<'a> {
21 context: &'a LayoutContext<'a>,
22}
23
24impl<'a> RecalcStyle<'a> {
25 pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
26 RecalcStyle { context }
27 }
28
29 pub(crate) fn context(&self) -> &LayoutContext<'a> {
30 self.context
31 }
32}
33
34#[expect(unsafe_code)]
35impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
36where
37 E: TElement,
38 E::ConcreteNode: 'dom + LayoutNode<'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 if node.is_text_node() {
50 return;
51 }
52
53 let had_style_data = node.style_data().is_some();
54 unsafe {
55 node.initialize_style_and_layout_data::<DOMLayoutData>();
56 }
57
58 let element = node.as_element().unwrap();
59 let mut element_data = element.mutate_data().unwrap();
60
61 if !had_style_data {
62 element_data.damage = RestyleDamage::reconstruct();
63 }
64
65 recalc_style_at(
66 self,
67 traversal_data,
68 context,
69 element,
70 &mut element_data,
71 note_child,
72 );
73
74 unsafe {
75 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_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#[servo_tracing::instrument(skip_all)]
98pub(crate) fn compute_damage_and_repair_style(
99 context: &SharedStyleContext,
100 node: ServoThreadSafeLayoutNode<'_>,
101 damage_from_environment: RestyleDamage,
102) -> RestyleDamage {
103 compute_damage_and_repair_style_inner(context, node, damage_from_environment)
104}
105
106pub(crate) fn compute_damage_and_repair_style_inner(
107 context: &SharedStyleContext,
108 node: ServoThreadSafeLayoutNode<'_>,
109 damage_from_parent: RestyleDamage,
110) -> RestyleDamage {
111 let mut element_damage;
112 let original_element_damage;
113 let element_data = &node
114 .style_data()
115 .expect("Should not run `compute_damage` before styling.")
116 .element_data;
117
118 {
119 let mut element_data = element_data.borrow_mut();
120 original_element_damage = element_data.damage;
121 element_damage = original_element_damage | damage_from_parent;
122
123 if let Some(ref style) = element_data.styles.primary {
124 if style.get_box().display == Display::None {
125 element_data.damage = element_damage;
126 return element_damage;
127 }
128 }
129 }
130
131 let mut damage_for_children = element_damage;
134 if !element_damage.contains(LayoutDamage::rebuild_box_tree()) {
135 damage_for_children.truncate();
136 }
137
138 let mut damage_from_children = RestyleDamage::empty();
139 for child in node.children() {
140 if child.is_element() {
141 damage_from_children |=
142 compute_damage_and_repair_style_inner(context, child, damage_for_children);
143 }
144 }
145
146 if damage_from_children.contains(LayoutDamage::recollect_box_tree_children()) {
149 element_damage.insert(LayoutDamage::recollect_box_tree_children());
150 }
151
152 let element_layout_damage = LayoutDamage::from(element_damage);
154 if element_layout_damage.has_box_damage() {
155 element_damage.insert(RestyleDamage::RELAYOUT);
156 }
157
158 let damage_for_parent = element_damage | (damage_from_children & RestyleDamage::RELAYOUT);
161
162 if element_damage != RestyleDamage::reconstruct() &&
168 damage_for_parent.contains(RestyleDamage::RELAYOUT)
169 {
170 node.with_each_layout_box_base_including_pseudos(
171 if (original_element_damage | damage_from_children).contains(RestyleDamage::RELAYOUT) {
172 LayoutBoxBase::clear_fragments_and_layout_cache
173 } else {
174 LayoutBoxBase::clear_fragments
175 },
176 );
177 }
178
179 if !element_layout_damage.has_box_damage() {
183 if !original_element_damage.is_empty() {
184 node.repair_style(context);
185 }
186
187 element_damage = RestyleDamage::empty();
188 }
189
190 if element_damage != original_element_damage {
191 element_data.borrow_mut().damage = element_damage;
192 }
193
194 damage_for_parent
195}