1use std::{
4 cell::RefCell,
5 time::{SystemTime, UNIX_EPOCH},
6};
7
8use debug_timer::debug_timer;
9use parley::LayoutContext;
10use selectors::Element as _;
11use style::dom::TDocument;
12
13#[cfg(feature = "parallel-construct")]
14use rayon::prelude::*;
15
16thread_local! {
19 pub(crate) static LAYOUT_CTX: RefCell<Option<Box<LayoutContext<TextBrush>>>> = const { RefCell::new(None) };
20}
21
22#[cfg(feature = "incremental")]
23use style::selector_parser::RestyleDamage;
24use taffy::AvailableSpace;
25
26use crate::{
27 BaseDocument, NON_INCREMENTAL,
28 events::ScrollAnimationState,
29 layout::{
30 construct::{
31 ConstructionTask, ConstructionTaskData, ConstructionTaskResult,
32 ConstructionTaskResultData, build_inline_layout_into, collect_layout_children,
33 },
34 damage::{ALL_DAMAGE, CONSTRUCT_BOX, CONSTRUCT_DESCENDENT, CONSTRUCT_FC},
35 },
36 node::TextBrush,
37};
38
39impl BaseDocument {
40 pub fn resolve(&mut self, current_time_for_animations: f64) {
42 if TDocument::as_node(&&self.nodes[0])
43 .first_element_child()
44 .is_none()
45 {
46 #[cfg(feature = "tracing")]
47 tracing::warn!("No DOM - not resolving");
48 return;
49 }
50
51 self.handle_messages();
53
54 self.resolve_scroll_animation();
55
56 let root_node_id = self.root_element().id;
57 debug_timer!(timer, feature = "log_phase_times");
58
59 self.resolve_stylist(current_time_for_animations);
61 timer.record_time("style");
62
63 #[cfg(feature = "incremental")]
65 self.propagate_damage_flags(root_node_id, RestyleDamage::empty());
66 #[cfg(feature = "incremental")]
67 timer.record_time("damage");
68
69 self.resolve_layout_children();
71 timer.record_time("construct");
72
73 self.resolve_deferred_tasks();
74 timer.record_time("pconstruct");
75
76 self.flush_styles_to_layout(root_node_id);
78 timer.record_time("flush");
79
80 self.resolve_layout();
82 timer.record_time("layout");
83
84 #[cfg(feature = "incremental")]
86 {
87 for (_, node) in self.nodes.iter_mut() {
88 node.clear_damage_mut();
89 node.unset_dirty_descendants();
90 }
91 timer.record_time("c_damage");
92 }
93
94 let mut subdoc_is_animating = false;
95 for &node_id in &self.sub_document_nodes {
96 let node = &mut self.nodes[node_id];
97 let size = node.final_layout.size;
98 if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
99 let mut sub_viewport = sub_doc.viewport_mut();
102 sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
103 sub_viewport.zoom = self.viewport.zoom;
104 sub_viewport.color_scheme = self.viewport.color_scheme;
105
106 let viewport_scale = self.viewport.scale();
107 sub_viewport.window_size = (
108 (size.width * viewport_scale) as u32,
109 (size.height * viewport_scale) as u32,
110 );
111 drop(sub_viewport);
112
113 sub_doc.resolve(current_time_for_animations);
114
115 subdoc_is_animating |= sub_doc.is_animating();
116 }
117 }
118 self.subdoc_is_animating = subdoc_is_animating;
119 timer.record_time("subdocs");
120
121 timer.print_times(&format!("Resolve({}): ", self.id()));
122 }
123
124 pub fn resolve_scroll_animation(&mut self) {
125 match &mut self.scroll_animation {
126 ScrollAnimationState::Fling(fling_state) => {
127 let time_ms = SystemTime::now()
128 .duration_since(UNIX_EPOCH)
129 .unwrap()
130 .as_millis() as u64 as f64;
131
132 let time_diff_ms = time_ms - fling_state.last_seen_time;
133
134 let deceleration = 1.0 - ((0.05 / 16.66666) * time_diff_ms);
136
137 fling_state.x_velocity *= deceleration;
138 fling_state.y_velocity *= deceleration;
139 fling_state.last_seen_time = time_ms;
140 let fling_state = fling_state.clone();
141
142 let dx = fling_state.x_velocity * time_diff_ms;
143 let dy = fling_state.y_velocity * time_diff_ms;
144
145 self.scroll_by(Some(fling_state.target), dx, dy, &mut |_| {});
146 if fling_state.x_velocity.abs() < 0.1 && fling_state.y_velocity.abs() < 0.1 {
147 self.scroll_animation = ScrollAnimationState::None;
148 }
149 }
150 ScrollAnimationState::None => {
151 }
153 }
154 }
155
156 pub fn resolve_layout_children(&mut self) {
158 resolve_layout_children_recursive(self, self.root_node().id);
159
160 fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: usize) {
161 if doc.nodes.get(node_id).is_none() {
164 return;
165 }
166
167 let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
168 let _flags = doc.nodes[node_id].flags;
169
170 if NON_INCREMENTAL || damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
171 let mut layout_children = Vec::new();
173 let mut anonymous_block: Option<usize> = None;
174 collect_layout_children(doc, node_id, &mut layout_children, &mut anonymous_block);
175
176 for child_id in layout_children.iter().copied() {
178 resolve_layout_children_recursive(doc, child_id);
179 doc.nodes[child_id].layout_parent.set(Some(node_id));
180 if let Some(mut data) = doc.nodes[child_id].stylo_element_data.get_mut() {
181 data.damage
182 .remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
183 }
184 }
185
186 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
187 damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
190 } else {
192 let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
194 if let Some(layout_children) = layout_children {
195 for child_id in layout_children.iter().copied() {
196 if !doc.nodes.contains(child_id) {
199 continue;
200 }
201 resolve_layout_children_recursive(doc, child_id);
202 doc.nodes[child_id].layout_parent.set(Some(node_id));
203 }
204
205 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
206 }
207
208 }
211
212 doc.nodes[node_id].set_damage(damage);
213 }
214 }
215
216 pub fn resolve_deferred_tasks(&mut self) {
217 let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
218
219 deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
221 deferred_construction_nodes.dedup_by_key(|task| task.node_id);
222
223 #[cfg(feature = "parallel-construct")]
224 let iter = deferred_construction_nodes.into_par_iter();
225 #[cfg(not(feature = "parallel-construct"))]
226 let iter = deferred_construction_nodes.into_iter();
227
228 let results: Vec<ConstructionTaskResult> = iter
229 .map(|task: ConstructionTask| match task.data {
230 ConstructionTaskData::InlineLayout(mut layout) => {
231 #[cfg(feature = "parallel-construct")]
232 let mut layout_ctx = LAYOUT_CTX
233 .take()
234 .unwrap_or_else(|| Box::new(LayoutContext::new()));
235 #[cfg(feature = "parallel-construct")]
236 let layout_ctx_mut = &mut layout_ctx;
237
238 #[cfg(feature = "parallel-construct")]
239 let mut font_ctx = self
240 .thread_font_contexts
241 .get_or(|| RefCell::new(Box::new(self.font_ctx.lock().unwrap().clone())))
242 .borrow_mut();
243 #[cfg(feature = "parallel-construct")]
244 let font_ctx_mut = &mut *font_ctx;
245
246 #[cfg(not(feature = "parallel-construct"))]
247 let layout_ctx_mut = &mut self.layout_ctx;
248 #[cfg(not(feature = "parallel-construct"))]
249 let font_ctx_mut = &mut *self.font_ctx.lock().unwrap();
250
251 layout.content_widths = None;
252 build_inline_layout_into(
253 &self.nodes,
254 layout_ctx_mut,
255 font_ctx_mut,
256 &mut layout,
257 self.viewport.scale(),
258 task.node_id,
259 );
260
261 #[cfg(feature = "parallel-construct")]
262 {
263 LAYOUT_CTX.set(Some(layout_ctx));
264 }
265
266 ConstructionTaskResult {
273 node_id: task.node_id,
274 data: ConstructionTaskResultData::InlineLayout(layout),
275 }
276 }
277 })
278 .collect();
279
280 for result in results {
281 match result.data {
282 ConstructionTaskResultData::InlineLayout(layout) => {
283 self.nodes[result.node_id].cache.clear();
284 self.nodes[result.node_id]
285 .element_data_mut()
286 .unwrap()
287 .inline_layout_data = Some(layout);
288 }
289 }
290 }
291
292 self.deferred_construction_nodes.clear();
293 }
294
295 pub fn resolve_layout(&mut self) {
300 let size = self.stylist.device().au_viewport_size();
301
302 let available_space = taffy::Size {
303 width: AvailableSpace::Definite(size.width.to_f32_px()),
304 height: AvailableSpace::Definite(size.height.to_f32_px()),
305 };
306
307 let root_element_id = taffy::NodeId::from(self.root_element().id);
308
309 taffy::compute_root_layout(self, root_element_id, available_space);
312 taffy::round_layout(self, root_element_id);
313
314 }
317}