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
23#[cfg(feature = "incremental")]
24use style::selector_parser::RestyleDamage;
25use taffy::AvailableSpace;
26
27use crate::{
28 BaseDocument, NON_INCREMENTAL,
29 events::ScrollAnimationState,
30 layout::{
31 construct::{
32 ConstructionTask, ConstructionTaskData, ConstructionTaskResult,
33 ConstructionTaskResultData, build_inline_layout_into, 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 let root_node_id = self.root_element().id;
58 debug_timer!(timer, feature = "log_phase_times");
59
60 self.resolve_stylist(current_time_for_animations);
62 timer.record_time("style");
63
64 #[cfg(feature = "incremental")]
66 self.propagate_damage_flags(root_node_id, RestyleDamage::empty());
67 #[cfg(feature = "incremental")]
68 timer.record_time("damage");
69
70 self.resolve_layout_children();
72 timer.record_time("construct");
73
74 self.resolve_deferred_tasks();
75 timer.record_time("pconstruct");
76
77 self.flush_styles_to_layout(root_node_id);
79 timer.record_time("flush");
80
81 self.resolve_layout();
83 timer.record_time("layout");
84
85 self.resolve_transforms(root_node_id);
86 timer.record_time("transform");
87
88 #[cfg(feature = "incremental")]
90 {
91 for (_, node) in self.nodes.iter_mut() {
92 node.clear_damage_mut();
93 node.unset_dirty_descendants();
94 }
95 timer.record_time("c_damage");
96 }
97
98 let mut subdoc_is_animating = false;
99 for &node_id in &self.sub_document_nodes {
100 let node = &mut self.nodes[node_id];
101 let size = node.final_layout.size;
102 if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
103 let mut sub_viewport = sub_doc.viewport_mut();
106 sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
107 sub_viewport.zoom = self.viewport.zoom;
108 sub_viewport.color_scheme = self.viewport.color_scheme;
109
110 let viewport_scale = self.viewport.scale();
111 sub_viewport.window_size = (
112 (size.width * viewport_scale) as u32,
113 (size.height * viewport_scale) as u32,
114 );
115 drop(sub_viewport);
116
117 sub_doc.resolve(current_time_for_animations);
118
119 subdoc_is_animating |= sub_doc.is_animating();
120 }
121 }
122 self.subdoc_is_animating = subdoc_is_animating;
123 timer.record_time("subdocs");
124
125 timer.print_times(&format!("Resolve({}): ", self.id()));
126 }
127
128 fn resolve_transforms(&mut self, node_id: usize) -> Rect {
129 if !self.nodes.contains(node_id) {
130 return Rect::ZERO;
131 }
132
133 if !self.nodes[node_id]
134 .damage()
135 .map(|d| d.contains(style::selector_parser::RestyleDamage::RECALCULATE_OVERFLOW))
136 .unwrap_or(false)
137 {
138 return self.nodes[node_id].scrollable_overflow;
139 }
140
141 let scale = self.viewport.scale_f64();
142
143 let transform = self.nodes[node_id].set_transform(scale as f32);
144
145 let w = self.nodes[node_id].final_layout.size.width as f64 * scale;
146 let h = self.nodes[node_id].final_layout.size.height as f64 * scale;
147 let mut overflow = Rect::new(0.0, 0.0, w, h);
148
149 let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
150
151 if let Some(ref children) = layout_children {
152 for &child_id in children {
153 let child_rect_in_self = self.resolve_transforms(child_id);
154 overflow = overflow.union(child_rect_in_self);
155 }
156 }
157 if let Some(before) = self.nodes[node_id].before {
158 let child_rect_in_self = self.resolve_transforms(before);
159 overflow = overflow.union(child_rect_in_self);
160 }
161 if let Some(after) = self.nodes[node_id].after {
162 let child_rect_in_self = self.resolve_transforms(after);
163 overflow = overflow.union(child_rect_in_self);
164 }
165
166 self.nodes[node_id].scrollable_overflow = overflow;
167 *self.nodes[node_id].layout_children.get_mut() = layout_children;
168
169 let scaled_x = self.nodes[node_id].final_layout.location.x as f64 * scale;
170 let scaled_y = self.nodes[node_id].final_layout.location.y as f64 * scale;
171
172 let full = if let Some(t) = transform {
173 Affine::translate((scaled_x, scaled_y)) * t
174 } else {
175 Affine::translate((scaled_x, scaled_y))
176 };
177
178 full.transform_rect_bbox(overflow)
179 }
180
181 pub fn resolve_scroll_animation(&mut self) {
182 match &mut self.scroll_animation {
183 ScrollAnimationState::Fling(fling_state) => {
184 let time_ms = SystemTime::now()
185 .duration_since(UNIX_EPOCH)
186 .unwrap()
187 .as_millis() as u64 as f64;
188
189 let time_diff_ms = time_ms - fling_state.last_seen_time;
190
191 let deceleration = 1.0 - ((0.05 / 16.66666) * time_diff_ms);
193
194 fling_state.x_velocity *= deceleration;
195 fling_state.y_velocity *= deceleration;
196 fling_state.last_seen_time = time_ms;
197 let fling_state = fling_state.clone();
198
199 let dx = fling_state.x_velocity * time_diff_ms;
200 let dy = fling_state.y_velocity * time_diff_ms;
201
202 self.scroll_by(Some(fling_state.target), dx, dy, &mut |_| {});
203 if fling_state.x_velocity.abs() < 0.1 && fling_state.y_velocity.abs() < 0.1 {
204 self.scroll_animation = ScrollAnimationState::None;
205 }
206 }
207 ScrollAnimationState::None => {
208 }
210 }
211 }
212
213 pub fn resolve_layout_children(&mut self) {
215 resolve_layout_children_recursive(self, self.root_node().id);
216
217 fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: usize) {
218 if doc.nodes.get(node_id).is_none() {
221 return;
222 }
223
224 let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
225 let _flags = doc.nodes[node_id].flags;
226
227 if NON_INCREMENTAL || damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
228 let mut layout_children = Vec::new();
230 let mut anonymous_block: Option<usize> = None;
231 collect_layout_children(doc, node_id, &mut layout_children, &mut anonymous_block);
232
233 for child_id in layout_children.iter().copied() {
235 resolve_layout_children_recursive(doc, child_id);
236 doc.nodes[child_id].layout_parent.set(Some(node_id));
237 if let Some(mut data) = doc.nodes[child_id].stylo_element_data.get_mut() {
238 data.damage
239 .remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
240 }
241 }
242
243 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
244 damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
247 } else {
249 let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
251 if let Some(layout_children) = layout_children {
252 for child_id in layout_children.iter().copied() {
253 if !doc.nodes.contains(child_id) {
256 continue;
257 }
258 resolve_layout_children_recursive(doc, child_id);
259 doc.nodes[child_id].layout_parent.set(Some(node_id));
260 }
261
262 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
263 }
264
265 }
268
269 doc.nodes[node_id].set_damage(damage);
270 }
271 }
272
273 pub fn resolve_deferred_tasks(&mut self) {
274 let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
275
276 deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
278 deferred_construction_nodes.dedup_by_key(|task| task.node_id);
279
280 #[cfg(feature = "parallel-construct")]
281 let iter = deferred_construction_nodes.into_par_iter();
282 #[cfg(not(feature = "parallel-construct"))]
283 let iter = deferred_construction_nodes.into_iter();
284
285 let results: Vec<ConstructionTaskResult> = iter
286 .map(|task: ConstructionTask| match task.data {
287 ConstructionTaskData::InlineLayout(mut layout) => {
288 #[cfg(feature = "parallel-construct")]
289 let mut layout_ctx = LAYOUT_CTX
290 .take()
291 .unwrap_or_else(|| Box::new(LayoutContext::new()));
292 #[cfg(feature = "parallel-construct")]
293 let layout_ctx_mut = &mut layout_ctx;
294
295 #[cfg(feature = "parallel-construct")]
296 let mut font_ctx = self
297 .thread_font_contexts
298 .get_or(|| RefCell::new(Box::new(self.font_ctx.lock().unwrap().clone())))
299 .borrow_mut();
300 #[cfg(feature = "parallel-construct")]
301 let font_ctx_mut = &mut *font_ctx;
302
303 #[cfg(not(feature = "parallel-construct"))]
304 let layout_ctx_mut = &mut self.layout_ctx;
305 #[cfg(not(feature = "parallel-construct"))]
306 let font_ctx_mut = &mut *self.font_ctx.lock().unwrap();
307
308 layout.content_widths = None;
309 build_inline_layout_into(
310 &self.nodes,
311 layout_ctx_mut,
312 font_ctx_mut,
313 &mut layout,
314 self.viewport.scale(),
315 task.node_id,
316 );
317
318 #[cfg(feature = "parallel-construct")]
319 {
320 LAYOUT_CTX.set(Some(layout_ctx));
321 }
322
323 ConstructionTaskResult {
330 node_id: task.node_id,
331 data: ConstructionTaskResultData::InlineLayout(layout),
332 }
333 }
334 })
335 .collect();
336
337 for result in results {
338 match result.data {
339 ConstructionTaskResultData::InlineLayout(layout) => {
340 self.nodes[result.node_id].cache.clear();
341 self.nodes[result.node_id]
342 .element_data_mut()
343 .unwrap()
344 .inline_layout_data = Some(layout);
345 }
346 }
347 }
348
349 self.deferred_construction_nodes.clear();
350 }
351
352 pub fn resolve_layout(&mut self) {
357 let size = self.stylist.device().au_viewport_size();
358
359 let available_space = taffy::Size {
360 width: AvailableSpace::Definite(size.width.to_f32_px()),
361 height: AvailableSpace::Definite(size.height.to_f32_px()),
362 };
363
364 let root_element_id = taffy::NodeId::from(self.root_element().id);
365
366 taffy::compute_root_layout(self, root_element_id, available_space);
369 taffy::round_layout(self, root_element_id);
370
371 }
374}