1use std::{
4 cell::RefCell,
5 time::{SystemTime, UNIX_EPOCH},
6};
7
8use debug_timer::debug_timer;
9use kurbo::{Affine, Rect};
10use parley::LayoutContext;
11use selectors::Element as _;
12use style::dom::TDocument;
13
14#[cfg(feature = "parallel-construct")]
15use rayon::prelude::*;
16
17thread_local! {
20 pub(crate) static LAYOUT_CTX: RefCell<Option<Box<LayoutContext<TextBrush>>>> = const { RefCell::new(None) };
21}
22
23use style::selector_parser::RestyleDamage;
24use taffy::AvailableSpace;
25
26use crate::{
27 BaseDocument,
28 events::ScrollAnimationState,
29 layout::{
30 construct::{
31 ConstructionTask, ConstructionTaskData, ConstructionTaskResult,
32 ConstructionTaskResultData, LayoutChildren, build_inline_layout_into,
33 collect_layout_children,
34 },
35 damage::{ALL_DAMAGE, CONSTRUCT_BOX, CONSTRUCT_DESCENDENT, CONSTRUCT_FC},
36 },
37 node::TextBrush,
38};
39
40impl BaseDocument {
41 pub fn resolve(&mut self, current_time_for_animations: f64) {
43 if TDocument::as_node(&&self.nodes[0])
44 .first_element_child()
45 .is_none()
46 {
47 #[cfg(feature = "tracing")]
48 tracing::warn!("No DOM - not resolving");
49 return;
50 }
51
52 self.handle_messages();
54
55 self.resolve_scroll_animation();
56
57 {
60 use crate::node::scrollbar::{FADE_DELAY, FADE_DURATION};
61 self.scrollbar_activity
62 .retain(|_, last| last.elapsed() < FADE_DELAY + FADE_DURATION);
63 }
64
65 let root_node_id = self.root_element().id;
66 debug_timer!(timer, feature = "log-phase-times");
67
68 self.resolve_stylist(current_time_for_animations);
70 timer.record_time("style");
71
72 if self.incremental_layout {
74 self.propagate_damage_flags(root_node_id, RestyleDamage::empty());
75 timer.record_time("damage");
76 }
77
78 self.resolve_layout_children();
80 timer.record_time("construct");
81
82 self.resolve_deferred_tasks();
83 timer.record_time("pconstruct");
84
85 self.flush_styles_to_layout(root_node_id);
87 timer.record_time("flush");
88
89 self.resolve_layout();
91 timer.record_time("layout");
92
93 self.resolve_transforms(root_node_id);
94 timer.record_time("transform");
95
96 if self.incremental_layout {
98 for (_, node) in self.nodes.iter_mut() {
99 node.clear_damage_mut();
100 node.unset_dirty_descendants();
101 }
102 timer.record_time("c_damage");
103 }
104
105 let mut subdoc_is_animating = false;
106 for &node_id in &self.sub_document_nodes {
107 let node = &mut self.nodes[node_id];
108 let size = node.final_layout.size;
109 if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
110 let mut sub_viewport = sub_doc.viewport_mut();
113 sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
114 sub_viewport.zoom = self.viewport.zoom;
115 sub_viewport.color_scheme = self.viewport.color_scheme;
116
117 let viewport_scale = self.viewport.scale();
118 sub_viewport.window_size = (
119 (size.width * viewport_scale) as u32,
120 (size.height * viewport_scale) as u32,
121 );
122 drop(sub_viewport);
123
124 sub_doc.resolve(current_time_for_animations);
125
126 subdoc_is_animating |= sub_doc.is_animating();
127 }
128 }
129 self.subdoc_is_animating = subdoc_is_animating;
130 timer.record_time("subdocs");
131
132 timer.print_times(&format!("Resolve({}): ", self.id()));
133 }
134
135 fn resolve_transforms(&mut self, node_id: usize) -> Rect {
136 if !self.nodes.contains(node_id) {
137 return Rect::ZERO;
138 }
139
140 if !self.nodes[node_id]
141 .damage()
142 .map(|d| d.contains(style::selector_parser::RestyleDamage::RECALCULATE_OVERFLOW))
143 .unwrap_or(false)
144 {
145 return self.nodes[node_id].scrollable_overflow;
146 }
147
148 let scale = self.viewport.scale_f64();
149
150 let transform = self.nodes[node_id].set_transform(scale as f32);
151
152 let w = self.nodes[node_id].final_layout.size.width as f64 * scale;
153 let h = self.nodes[node_id].final_layout.size.height as f64 * scale;
154 let mut overflow = Rect::new(0.0, 0.0, w, h);
155
156 let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
157
158 if let Some(ref children) = layout_children {
159 for &child_id in children {
160 let child_rect_in_self = self.resolve_transforms(child_id);
161 overflow = overflow.union(child_rect_in_self);
162 }
163 }
164 if let Some(before) = self.nodes[node_id].before {
165 let child_rect_in_self = self.resolve_transforms(before);
166 overflow = overflow.union(child_rect_in_self);
167 }
168 if let Some(after) = self.nodes[node_id].after {
169 let child_rect_in_self = self.resolve_transforms(after);
170 overflow = overflow.union(child_rect_in_self);
171 }
172
173 self.nodes[node_id].scrollable_overflow = overflow;
174 *self.nodes[node_id].layout_children.get_mut() = layout_children;
175
176 let scaled_x = self.nodes[node_id].final_layout.location.x as f64 * scale;
177 let scaled_y = self.nodes[node_id].final_layout.location.y as f64 * scale;
178
179 let full = if let Some(t) = transform {
180 Affine::translate((scaled_x, scaled_y)) * t
181 } else {
182 Affine::translate((scaled_x, scaled_y))
183 };
184
185 full.transform_rect_bbox(overflow)
186 }
187
188 pub fn resolve_scroll_animation(&mut self) {
189 match &mut self.scroll_animation {
190 ScrollAnimationState::Fling(fling_state) => {
191 let time_ms = SystemTime::now()
192 .duration_since(UNIX_EPOCH)
193 .unwrap()
194 .as_millis() as u64 as f64;
195
196 let time_diff_ms = time_ms - fling_state.last_seen_time;
197
198 let deceleration = 1.0 - ((0.05 / 16.66666) * time_diff_ms);
200
201 fling_state.x_velocity *= deceleration;
202 fling_state.y_velocity *= deceleration;
203 fling_state.last_seen_time = time_ms;
204 let fling_state = fling_state.clone();
205
206 let dx = fling_state.x_velocity * time_diff_ms;
207 let dy = fling_state.y_velocity * time_diff_ms;
208
209 self.scroll_by(Some(fling_state.target), dx, dy, &mut |_| {});
210 if fling_state.x_velocity.abs() < 0.1 && fling_state.y_velocity.abs() < 0.1 {
211 self.scroll_animation = ScrollAnimationState::None;
212 }
213 }
214 ScrollAnimationState::None => {
215 }
217 }
218 }
219
220 pub fn resolve_layout_children(&mut self) {
222 resolve_layout_children_recursive(self, self.root_node().id);
223
224 fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: usize) {
225 if doc.nodes.get(node_id).is_none() {
228 return;
229 }
230
231 let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
232 let _flags = doc.nodes[node_id].flags;
233
234 if !doc.incremental_layout || damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
235 let mut collected = LayoutChildren::default();
237 collect_layout_children(doc, node_id, &mut collected);
238 let layout_children = collected.children;
239
240 for child_id in layout_children.iter().copied() {
242 resolve_layout_children_recursive(doc, child_id);
243 doc.nodes[child_id].layout_parent.set(Some(node_id));
244 if let Some(mut data) = doc.nodes[child_id].stylo_element_data.get_mut() {
245 data.damage
246 .remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
247 }
248 }
249
250 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
251 damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
254 } else {
256 let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
258 if let Some(layout_children) = layout_children {
259 for child_id in layout_children.iter().copied() {
260 if !doc.nodes.contains(child_id) {
263 continue;
264 }
265 resolve_layout_children_recursive(doc, child_id);
266 doc.nodes[child_id].layout_parent.set(Some(node_id));
267 }
268
269 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
270 }
271
272 }
275
276 doc.nodes[node_id].set_damage(damage);
277 }
278 }
279
280 pub fn resolve_deferred_tasks(&mut self) {
281 let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
282
283 deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
285 deferred_construction_nodes.dedup_by_key(|task| task.node_id);
286
287 #[cfg(feature = "parallel-construct")]
288 let iter = deferred_construction_nodes.into_par_iter();
289 #[cfg(not(feature = "parallel-construct"))]
290 let iter = deferred_construction_nodes.into_iter();
291
292 let results: Vec<ConstructionTaskResult> = iter
293 .map(|task: ConstructionTask| match task.data {
294 ConstructionTaskData::InlineLayout(mut layout) => {
295 #[cfg(feature = "parallel-construct")]
296 let mut layout_ctx = LAYOUT_CTX
297 .take()
298 .unwrap_or_else(|| Box::new(LayoutContext::new()));
299 #[cfg(feature = "parallel-construct")]
300 let layout_ctx_mut = &mut layout_ctx;
301
302 #[cfg(feature = "parallel-construct")]
303 let mut font_ctx = self
304 .thread_font_contexts
305 .get_or(|| RefCell::new(Box::new(self.font_ctx.lock().unwrap().clone())))
306 .borrow_mut();
307 #[cfg(feature = "parallel-construct")]
308 let font_ctx_mut = &mut *font_ctx;
309
310 #[cfg(not(feature = "parallel-construct"))]
311 let layout_ctx_mut = &mut self.layout_ctx;
312 #[cfg(not(feature = "parallel-construct"))]
313 let font_ctx_mut = &mut *self.font_ctx.lock().unwrap();
314
315 layout.content_widths = None;
316 build_inline_layout_into(
317 &self.nodes,
318 layout_ctx_mut,
319 font_ctx_mut,
320 &mut layout,
321 self.viewport.scale(),
322 task.node_id,
323 );
324
325 #[cfg(feature = "parallel-construct")]
326 {
327 LAYOUT_CTX.set(Some(layout_ctx));
328 }
329
330 ConstructionTaskResult {
337 node_id: task.node_id,
338 data: ConstructionTaskResultData::InlineLayout(layout),
339 }
340 }
341 })
342 .collect();
343
344 for result in results {
345 match result.data {
346 ConstructionTaskResultData::InlineLayout(layout) => {
347 self.nodes[result.node_id].cache.clear();
348 self.nodes[result.node_id]
349 .element_data_mut()
350 .unwrap()
351 .inline_layout_data = Some(layout);
352 }
353 }
354 }
355
356 self.deferred_construction_nodes.clear();
357 }
358
359 pub fn resolve_layout(&mut self) {
364 let size = self.stylist.device().au_viewport_size();
365
366 let available_space = taffy::Size {
367 width: AvailableSpace::Definite(size.width.to_f32_px()),
368 height: AvailableSpace::Definite(size.height.to_f32_px()),
369 };
370
371 let root_element_id = taffy::NodeId::from(self.root_element().id);
372
373 taffy::compute_root_layout(self, root_element_id, available_space);
376 taffy::round_layout(self, root_element_id);
377
378 }
381}