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 println!("No DOM - not resolving");
47 return;
48 }
49
50 self.handle_messages();
52
53 self.resolve_scroll_animation();
54
55 debug_assert!(
66 self.root_element().is_some(),
67 "resolve: 'no DOM' guard above lost its effect — root_element became None when an element child was expected"
68 );
69 let root_node_id = self.root_element().map(|r| r.id).unwrap_or(0);
70 debug_timer!(timer, feature = "log_phase_times");
71
72 self.resolve_stylist(current_time_for_animations);
74 timer.record_time("style");
75
76 #[cfg(feature = "incremental")]
78 self.propagate_damage_flags(root_node_id, RestyleDamage::empty());
79 #[cfg(feature = "incremental")]
80 timer.record_time("damage");
81
82 self.resolve_layout_children();
84 timer.record_time("construct");
85
86 self.resolve_deferred_tasks();
87 timer.record_time("pconstruct");
88
89 self.flush_styles_to_layout(root_node_id);
91 timer.record_time("flush");
92
93 self.resolve_layout();
95 timer.record_time("layout");
96
97 #[cfg(feature = "incremental")]
99 {
100 for (_, node) in self.nodes.iter_mut() {
101 node.clear_damage_mut();
102 node.unset_dirty_descendants();
103 }
104 timer.record_time("c_damage");
105 }
106
107 let mut subdoc_is_animating = false;
108 for &node_id in &self.sub_document_nodes {
109 let node = &mut self.nodes[node_id];
110 let size = node.final_layout.size;
111 if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
112 let mut sub_viewport = sub_doc.viewport_mut();
115 sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
116 sub_viewport.zoom = self.viewport.zoom;
117 sub_viewport.color_scheme = self.viewport.color_scheme;
118
119 let viewport_scale = self.viewport.scale();
120 sub_viewport.window_size = (
121 (size.width * viewport_scale) as u32,
122 (size.height * viewport_scale) as u32,
123 );
124 drop(sub_viewport);
125
126 sub_doc.resolve(current_time_for_animations);
127
128 subdoc_is_animating |= sub_doc.is_animating();
129 }
130 }
131 self.subdoc_is_animating = subdoc_is_animating;
132 timer.record_time("subdocs");
133
134 timer.print_times(&format!("Resolve({}): ", self.id()));
135 }
136
137 pub fn resolve_scroll_animation(&mut self) {
138 match &mut self.scroll_animation {
139 ScrollAnimationState::Fling(fling_state) => {
140 let time_ms = SystemTime::now()
141 .duration_since(UNIX_EPOCH)
142 .unwrap_or_default()
143 .as_millis() as u64 as f64;
144
145 let time_diff_ms = time_ms - fling_state.last_seen_time;
146
147 let deceleration = 1.0 - ((0.05 / 16.66666) * time_diff_ms);
149
150 fling_state.x_velocity *= deceleration;
151 fling_state.y_velocity *= deceleration;
152 fling_state.last_seen_time = time_ms;
153 let fling_state = fling_state.clone();
154
155 let dx = fling_state.x_velocity * time_diff_ms;
156 let dy = fling_state.y_velocity * time_diff_ms;
157
158 self.scroll_by(Some(fling_state.target), dx, dy, &mut |_| {});
159 if fling_state.x_velocity.abs() < 0.1 && fling_state.y_velocity.abs() < 0.1 {
160 self.scroll_animation = ScrollAnimationState::None;
161 }
162 }
163 ScrollAnimationState::None => {
164 }
166 }
167 }
168
169 pub fn resolve_layout_children(&mut self) {
171 resolve_layout_children_recursive(self, self.root_node().id);
172
173 fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: usize) {
174 let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
175 let _flags = doc.nodes[node_id].flags;
176
177 if NON_INCREMENTAL || damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
178 let mut layout_children = Vec::new();
180 let mut anonymous_block: Option<usize> = None;
181 collect_layout_children(doc, node_id, &mut layout_children, &mut anonymous_block);
182
183 for child_id in layout_children.iter().copied() {
185 resolve_layout_children_recursive(doc, child_id);
186 doc.nodes[child_id].layout_parent.set(Some(node_id));
187 if let Some(data) = doc.nodes[child_id].stylo_element_data.get_mut() {
188 data.damage
189 .remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
190 }
191 }
192
193 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
194 damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
197 } else {
199 let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
201 if let Some(layout_children) = layout_children {
202 for child_id in layout_children.iter().copied() {
204 resolve_layout_children_recursive(doc, child_id);
205 doc.nodes[child_id].layout_parent.set(Some(node_id));
206 }
207
208 *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
209 }
210
211 }
214
215 doc.nodes[node_id].set_damage(damage);
216 }
217 }
218
219 pub fn resolve_deferred_tasks(&mut self) {
220 let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
221
222 deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
224 deferred_construction_nodes.dedup_by_key(|task| task.node_id);
225
226 #[cfg(feature = "parallel-construct")]
227 let iter = deferred_construction_nodes.into_par_iter();
228 #[cfg(not(feature = "parallel-construct"))]
229 let iter = deferred_construction_nodes.into_iter();
230
231 let results: Vec<ConstructionTaskResult> = iter
232 .map(|task: ConstructionTask| match task.data {
233 ConstructionTaskData::InlineLayout(mut layout) => {
234 #[cfg(feature = "parallel-construct")]
235 let mut layout_ctx = LAYOUT_CTX
236 .take()
237 .unwrap_or_else(|| Box::new(LayoutContext::new()));
238 #[cfg(feature = "parallel-construct")]
239 let layout_ctx_mut = &mut layout_ctx;
240
241 #[cfg(feature = "parallel-construct")]
242 let mut font_ctx = self
243 .thread_font_contexts
244 .get_or(|| {
245 RefCell::new(Box::new(
246 self.font_ctx
247 .lock()
248 .unwrap_or_else(|e| e.into_inner())
249 .clone(),
250 ))
251 })
252 .borrow_mut();
253 #[cfg(feature = "parallel-construct")]
254 let font_ctx_mut = &mut *font_ctx;
255
256 #[cfg(not(feature = "parallel-construct"))]
257 let layout_ctx_mut = &mut self.layout_ctx;
258 #[cfg(not(feature = "parallel-construct"))]
259 let font_ctx_mut =
260 &mut *self.font_ctx.lock().unwrap_or_else(|e| e.into_inner());
261
262 layout.content_widths = None;
263 build_inline_layout_into(
264 &self.nodes,
265 layout_ctx_mut,
266 font_ctx_mut,
267 &mut layout,
268 self.viewport.scale(),
269 task.node_id,
270 );
271
272 #[cfg(feature = "parallel-construct")]
273 {
274 LAYOUT_CTX.set(Some(layout_ctx));
275 }
276
277 if layout.layout.inline_boxes().is_empty() {
284 layout.content_widths();
285 }
286
287 ConstructionTaskResult {
288 node_id: task.node_id,
289 data: ConstructionTaskResultData::InlineLayout(layout),
290 }
291 }
292 })
293 .collect();
294
295 for result in results {
296 match result.data {
297 ConstructionTaskResultData::InlineLayout(layout) => {
298 self.nodes[result.node_id].cache.clear();
299 if let Some(elem) = self.nodes[result.node_id].element_data_mut() {
300 elem.inline_layout_data = Some(layout);
301 }
302 }
303 }
304 }
305
306 self.deferred_construction_nodes.clear();
307 }
308
309 pub fn resolve_layout(&mut self) {
314 let size = self.stylist.device().au_viewport_size();
315
316 let available_space = taffy::Size {
317 width: AvailableSpace::Definite(size.width.to_f32_px()),
318 height: AvailableSpace::Definite(size.height.to_f32_px()),
319 };
320
321 debug_assert!(
325 self.root_element().is_some(),
326 "resolve_layout: 'no DOM' guard above lost its effect — root_element became None when an element child was expected"
327 );
328 let root_element_id =
329 taffy::NodeId::from(self.root_element().map(|r| r.id).unwrap_or(0));
330
331 taffy::compute_root_layout(self, root_element_id, available_space);
334 taffy::round_layout(self, root_element_id);
335
336 }
339}