1use std::cell::Cell;
6use std::sync::atomic::Ordering;
7
8use bitflags::Flags;
9use layout_api::LayoutDamage;
10use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
11use script::layout_dom::ServoThreadSafeLayoutNode;
12use style::context::{SharedStyleContext, StyleContext};
13use style::data::ElementData;
14use style::dom::{NodeInfo, TElement, TNode};
15use style::selector_parser::RestyleDamage;
16use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
17use style::values::computed::Display;
18
19use crate::context::LayoutContext;
20use crate::dom::{DOMLayoutData, NodeExt};
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
36#[expect(unsafe_code)]
37impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
38where
39 E: TElement,
40 E::ConcreteNode: 'dom + LayoutNode<'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 if node.is_text_node() {
52 return;
53 }
54
55 let had_style_data = node.style_data().is_some();
56 unsafe {
57 node.initialize_style_and_layout_data::<DOMLayoutData>();
58 }
59
60 let element = node.as_element().unwrap();
61 let mut element_data = element.mutate_data().unwrap();
62
63 if !had_style_data {
64 element_data.damage = RestyleDamage::reconstruct();
65 }
66
67 recalc_style_at(
68 self,
69 traversal_data,
70 context,
71 element,
72 &mut element_data,
73 note_child,
74 );
75
76 unsafe {
77 element.unset_dirty_descendants();
78 }
79 }
80
81 #[inline]
82 fn needs_postorder_traversal() -> bool {
83 false
84 }
85
86 fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
87 panic!("this should never be called")
88 }
89
90 fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
91 node.layout_data().is_none() || !parent_data.damage.is_empty()
92 }
93
94 fn shared_context(&self) -> &SharedStyleContext<'_> {
95 &self.context.style_context
96 }
97}
98
99#[servo_tracing::instrument(skip_all)]
100pub(crate) fn compute_damage_and_repair_style(
101 context: &SharedStyleContext,
102 node: ServoThreadSafeLayoutNode<'_>,
103 damage_from_environment: RestyleDamage,
104) -> RestyleDamage {
105 compute_damage_and_repair_style_inner(context, node, damage_from_environment)
106}
107
108pub(crate) fn compute_damage_and_repair_style_inner(
109 context: &SharedStyleContext,
110 node: ServoThreadSafeLayoutNode<'_>,
111 damage_from_parent: RestyleDamage,
112) -> RestyleDamage {
113 let mut element_damage;
114 let original_element_damage;
115 let element_data = &node
116 .style_data()
117 .expect("Should not run `compute_damage` before styling.")
118 .element_data;
119
120 {
121 let mut element_data = element_data.borrow_mut();
122 original_element_damage = element_data.damage;
123 element_damage = original_element_damage | damage_from_parent;
124
125 if let Some(ref style) = element_data.styles.primary {
126 if style.get_box().display == Display::None {
127 element_data.damage = element_damage;
128 return element_damage;
129 }
130 }
131 }
132
133 let mut damage_for_children = element_damage;
136 if !element_damage.contains(LayoutDamage::rebuild_box_tree()) {
137 damage_for_children.truncate();
138 }
139
140 let mut damage_from_children = RestyleDamage::empty();
141 for child in node.children() {
142 if child.is_element() {
143 damage_from_children |=
144 compute_damage_and_repair_style_inner(context, child, damage_for_children);
145 }
146 }
147
148 if damage_from_children.contains(LayoutDamage::recollect_box_tree_children()) {
151 element_damage.insert(LayoutDamage::recollect_box_tree_children());
152 }
153
154 let element_layout_damage = LayoutDamage::from(element_damage);
156 if element_layout_damage.has_box_damage() {
157 element_damage.insert(RestyleDamage::RELAYOUT);
158 }
159
160 let mut damage_for_parent = element_damage | (damage_from_children & RestyleDamage::RELAYOUT);
163
164 if element_damage != RestyleDamage::reconstruct() &&
170 damage_for_parent.contains(RestyleDamage::RELAYOUT)
171 {
172 let outer_inline_content_sizes_depend_on_content = Cell::new(false);
173 node.with_layout_box_base_including_pseudos(|base| {
174 base.clear_fragments();
175 if original_element_damage.contains(RestyleDamage::RELAYOUT) {
176 *base.cached_layout_result.borrow_mut() = None;
179 *base.cached_inline_content_size.borrow_mut() = None;
180 } else if damage_from_children.contains(RestyleDamage::RELAYOUT) {
181 *base.cached_layout_result.borrow_mut() = None;
184 if !damage_from_children.contains(LayoutDamage::recompute_inline_content_sizes()) {
185 return;
188 }
189 *base.cached_inline_content_size.borrow_mut() = None;
190 }
191
192 if !base.base_fragment_info.is_anonymous() {
198 outer_inline_content_sizes_depend_on_content.set(
200 outer_inline_content_sizes_depend_on_content.get() ||
201 base.outer_inline_content_sizes_depend_on_content
202 .load(Ordering::Relaxed),
203 );
204 }
205 });
206
207 if outer_inline_content_sizes_depend_on_content.get() {
211 damage_for_parent.insert(LayoutDamage::recompute_inline_content_sizes())
212 }
213 }
214
215 if !element_layout_damage.has_box_damage() {
219 if !original_element_damage.is_empty() {
220 node.repair_style(context);
221 }
222
223 element_damage = RestyleDamage::empty();
224 }
225
226 if element_damage != original_element_damage {
227 element_data.borrow_mut().damage = element_damage;
228 }
229
230 damage_for_parent
231}