1use blitz_traits::node_id::NodeId;
4use std::cell::RefCell;
5
6use debug_timer::debug_timer;
7use kurbo::{Affine, Rect};
8use parley::LayoutContext;
9use selectors::Element as _;
10use style::dom::TDocument;
11
12#[cfg(feature = "parallel-construct")]
13use rayon::prelude::*;
14
15thread_local! {
18 pub(crate) static LAYOUT_CTX: RefCell<Option<Box<LayoutContext<TextBrush>>>> = const { RefCell::new(None) };
19}
20
21use style::selector_parser::RestyleDamage;
22use taffy::AvailableSpace;
23
24use crate::{
25 BaseDocument,
26 layout::{
27 construct::{
28 ConstructionTask, ConstructionTaskData, ConstructionTaskResult,
29 ConstructionTaskResultData, LayoutChildren, build_inline_layout_into,
30 collect_layout_children,
31 },
32 damage::{ALL_DAMAGE, CONSTRUCT_BOX, CONSTRUCT_DESCENDENT, CONSTRUCT_FC},
33 },
34 node::TextBrush,
35};
36
37impl BaseDocument {
38 pub fn resolve(&mut self, current_time_for_animations: f64) {
40 if TDocument::as_node(&self.root_node())
41 .first_element_child()
42 .is_none()
43 {
44 #[cfg(feature = "tracing")]
45 tracing::warn!("No DOM - not resolving");
46 return;
47 }
48
49 self.handle_messages();
51
52 if self.has_pending_critical_resources() {
62 return;
63 }
64
65 self.resolve_scroll_animation();
66
67 {
70 use crate::node::scrollbar::{FADE_DELAY, FADE_DURATION};
71 self.scrollbar_activity
72 .retain(|_, last| last.elapsed() < FADE_DELAY + FADE_DURATION);
73 }
74
75 let root_node_id = self.root_element().id;
76 debug_timer!(timer, feature = "log-phase-times");
77
78 self.resolve_stylist(current_time_for_animations);
80 timer.record_time("style");
81
82 if self.incremental_layout {
84 self.propagate_damage_flags(root_node_id, RestyleDamage::empty());
85 timer.record_time("damage");
86 }
87
88 self.resolve_layout_children();
90 timer.record_time("construct");
91
92 self.resolve_deferred_tasks();
93 self.flush_pending_style_images();
97 timer.record_time("pconstruct");
98
99 self.flush_styles_to_layout(root_node_id);
101 timer.record_time("flush");
102
103 self.resolve_layout();
105 timer.record_time("layout");
106
107 self.resolve_transforms(root_node_id);
109 timer.record_time("transform");
110
111 if self.incremental_layout {
114 let doc_node_id = self.root_node().id;
115 self.clear_damage_and_dirty_flags(doc_node_id);
116 timer.record_time("c_damage");
117 }
118
119 self.refresh_hover();
127
128 let mut subdoc_is_animating = false;
129 for &node_id in &self.sub_document_nodes {
130 let node = &mut self.nodes[node_id];
131 let size = node.final_layout().size;
132 if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
133 let mut sub_viewport = sub_doc.viewport_mut();
136 sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
137 sub_viewport.zoom = self.viewport.zoom;
138 sub_viewport.color_scheme = self.viewport.color_scheme;
139
140 let viewport_scale = self.viewport.scale();
141 sub_viewport.window_size = (
142 (size.width * viewport_scale) as u32,
143 (size.height * viewport_scale) as u32,
144 );
145 drop(sub_viewport);
146
147 sub_doc.resolve(current_time_for_animations);
148
149 subdoc_is_animating |= sub_doc.is_animating();
150 }
151 }
152 self.subdoc_is_animating = subdoc_is_animating;
153 timer.record_time("subdocs");
154
155 timer.print_times(&format!("Resolve({}): ", self.id()));
156 }
157
158 fn resolve_transforms(&mut self, node_id: NodeId) -> Rect {
159 if !self.nodes.contains_key(node_id) {
160 return Rect::ZERO;
161 }
162
163 if !self.nodes[node_id]
164 .damage()
165 .map(|d| d.contains(style::selector_parser::RestyleDamage::RECALCULATE_OVERFLOW))
166 .unwrap_or(false)
167 {
168 return *self.nodes[node_id].scrollable_overflow();
169 }
170
171 let scale = self.viewport.scale_f64();
172
173 let transform = self.nodes[node_id].set_transform(scale as f32);
174
175 let w = self.nodes[node_id].final_layout().size.width as f64 * scale;
176 let h = self.nodes[node_id].final_layout().size.height as f64 * scale;
177 let mut overflow = Rect::new(0.0, 0.0, w, h);
178
179 let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
180
181 if let Some(ref children) = layout_children {
182 for &child_id in children {
183 let child_rect_in_self = self.resolve_transforms(child_id);
184 overflow = overflow.union(child_rect_in_self);
185 }
186 }
187 if let Some(before) = self.nodes[node_id].before() {
188 let child_rect_in_self = self.resolve_transforms(before);
189 overflow = overflow.union(child_rect_in_self);
190 }
191 if let Some(after) = self.nodes[node_id].after() {
192 let child_rect_in_self = self.resolve_transforms(after);
193 overflow = overflow.union(child_rect_in_self);
194 }
195
196 *self.nodes[node_id].scrollable_overflow_mut() = overflow;
197 *self.nodes[node_id].layout_children.get_mut() = layout_children;
198
199 let scaled_x = self.nodes[node_id].final_layout().location.x as f64 * scale;
200 let scaled_y = self.nodes[node_id].final_layout().location.y as f64 * scale;
201
202 let full = if let Some(t) = transform {
203 Affine::translate((scaled_x, scaled_y)) * t
204 } else {
205 Affine::translate((scaled_x, scaled_y))
206 };
207
208 full.transform_rect_bbox(overflow)
209 }
210
211 pub fn resolve_layout_children(&mut self) {
213 resolve_layout_children_recursive(self, self.root_node().id);
214
215 fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: NodeId) {
216 if doc.nodes.get(node_id).is_none() {
219 return;
220 }
221
222 let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
223 let _flags = doc.nodes[node_id].flags;
224
225 if !doc.incremental_layout || damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
226 let old_anonymous_blocks = std::mem::take(&mut doc.nodes[node_id].anonymous_blocks);
233 for anon_id in old_anonymous_blocks {
234 doc.deallocate_anonymous_block(anon_id);
235 }
236
237 let mut collected = LayoutChildren::default();
238 collect_layout_children(doc, node_id, &mut collected);
239 let layout_children = collected.children;
240 doc.nodes[node_id].anonymous_blocks = collected.anonymous_blocks;
241
242 for child_id in layout_children.iter().copied() {
244 resolve_layout_children_recursive(doc, child_id);
245 doc.nodes[child_id].layout_parent.set(Some(node_id));
246 if let Some(mut data) = doc.nodes[child_id]
247 .stylo_element_data_opt_mut()
248 .and_then(|s| s.get_mut())
249 {
250 data.damage
251 .remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
252 }
253 }
254
255 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
256 damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
259 } else {
261 let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
263 if let Some(layout_children) = layout_children {
264 for child_id in layout_children.iter().copied() {
265 if !doc.nodes.contains_key(child_id) {
268 continue;
269 }
270 resolve_layout_children_recursive(doc, child_id);
271 doc.nodes[child_id].layout_parent.set(Some(node_id));
272 }
273
274 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
275 }
276
277 }
280
281 doc.nodes[node_id].set_damage(damage);
282 }
283 }
284
285 pub fn resolve_deferred_tasks(&mut self) {
286 let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
287
288 deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
290 deferred_construction_nodes.dedup_by_key(|task| task.node_id);
291
292 #[cfg(feature = "parallel-construct")]
293 let iter = deferred_construction_nodes.into_par_iter();
294 #[cfg(not(feature = "parallel-construct"))]
295 let iter = deferred_construction_nodes.into_iter();
296
297 let results: Vec<ConstructionTaskResult> = iter
298 .map(|task: ConstructionTask| match task.data {
299 ConstructionTaskData::InlineLayout(mut layout) => {
300 #[cfg(feature = "parallel-construct")]
301 let mut layout_ctx = LAYOUT_CTX
302 .take()
303 .unwrap_or_else(|| Box::new(LayoutContext::new()));
304 #[cfg(feature = "parallel-construct")]
305 let layout_ctx_mut = &mut layout_ctx;
306
307 #[cfg(feature = "parallel-construct")]
308 let mut font_ctx = self
309 .thread_font_contexts
310 .get_or(|| RefCell::new(Box::new(self.font_ctx.lock().unwrap().clone())))
311 .borrow_mut();
312 #[cfg(feature = "parallel-construct")]
313 let font_ctx_mut = &mut *font_ctx;
314
315 #[cfg(not(feature = "parallel-construct"))]
316 let layout_ctx_mut = &mut self.layout_ctx;
317 #[cfg(not(feature = "parallel-construct"))]
318 let font_ctx_mut = &mut *self.font_ctx.lock().unwrap();
319
320 layout.content_widths = None;
321 build_inline_layout_into(
322 &self.nodes,
323 layout_ctx_mut,
324 font_ctx_mut,
325 &mut layout,
326 self.viewport.scale(),
327 task.node_id,
328 );
329
330 #[cfg(feature = "parallel-construct")]
331 {
332 LAYOUT_CTX.set(Some(layout_ctx));
333 }
334
335 ConstructionTaskResult {
342 node_id: task.node_id,
343 data: ConstructionTaskResultData::InlineLayout(layout),
344 }
345 }
346 })
347 .collect();
348
349 for result in results {
350 match result.data {
351 ConstructionTaskResultData::InlineLayout(layout) => {
352 self.nodes[result.node_id].cache_mut().clear();
353 self.nodes[result.node_id]
354 .element_data_mut()
355 .unwrap()
356 .inline_layout_data = Some(layout);
357 }
358 }
359 }
360
361 self.deferred_construction_nodes.clear();
362 }
363
364 pub fn resolve_layout(&mut self) {
369 let size = self.stylist.device().au_viewport_size();
370
371 let available_space = taffy::Size {
372 width: AvailableSpace::Definite(size.width.to_f32_px()),
373 height: AvailableSpace::Definite(size.height.to_f32_px()),
374 };
375
376 let root_element_id = crate::taffy_node_id(self.root_element().id);
377
378 taffy::compute_root_layout(self, root_element_id, available_space);
381 taffy::round_layout(self, root_element_id);
382
383 }
386}