blitz-pdf 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/docs/layout-regressions.md +65 -0
- package/docs/pagination-regressions.md +89 -0
- package/package.json +2 -1
- package/vendor/README.md +35 -0
- package/vendor/blitz-dom/Cargo.toml +113 -0
- package/vendor/blitz-dom/LICENSE-APACHE +176 -0
- package/vendor/blitz-dom/LICENSE-MIT +23 -0
- package/vendor/blitz-dom/assets/default.css +1142 -0
- package/vendor/blitz-dom/assets/moz-bullet-font.otf +0 -0
- package/vendor/blitz-dom/src/accessibility.rs +255 -0
- package/vendor/blitz-dom/src/config.rs +97 -0
- package/vendor/blitz-dom/src/cssom.rs +971 -0
- package/vendor/blitz-dom/src/debug.rs +206 -0
- package/vendor/blitz-dom/src/document.rs +3395 -0
- package/vendor/blitz-dom/src/events/driver.rs +427 -0
- package/vendor/blitz-dom/src/events/focus.rs +40 -0
- package/vendor/blitz-dom/src/events/ime.rs +31 -0
- package/vendor/blitz-dom/src/events/keyboard.rs +174 -0
- package/vendor/blitz-dom/src/events/mod.rs +315 -0
- package/vendor/blitz-dom/src/events/pointer.rs +849 -0
- package/vendor/blitz-dom/src/font_metrics.rs +177 -0
- package/vendor/blitz-dom/src/form.rs +463 -0
- package/vendor/blitz-dom/src/html.rs +38 -0
- package/vendor/blitz-dom/src/iframe.rs +186 -0
- package/vendor/blitz-dom/src/layout/construct.rs +1328 -0
- package/vendor/blitz-dom/src/layout/damage.rs +642 -0
- package/vendor/blitz-dom/src/layout/inline.rs +1160 -0
- package/vendor/blitz-dom/src/layout/list.rs +237 -0
- package/vendor/blitz-dom/src/layout/mod.rs +969 -0
- package/vendor/blitz-dom/src/layout/paint_tree.rs +462 -0
- package/vendor/blitz-dom/src/layout/replaced.rs +439 -0
- package/vendor/blitz-dom/src/layout/table.rs +967 -0
- package/vendor/blitz-dom/src/layout/text_transform.rs +979 -0
- package/vendor/blitz-dom/src/layout/writing_mode.rs +437 -0
- package/vendor/blitz-dom/src/lib.rs +160 -0
- package/vendor/blitz-dom/src/mutator.rs +1818 -0
- package/vendor/blitz-dom/src/net.rs +649 -0
- package/vendor/blitz-dom/src/node/attributes.rs +60 -0
- package/vendor/blitz-dom/src/node/custom_widget.rs +178 -0
- package/vendor/blitz-dom/src/node/element.rs +1106 -0
- package/vendor/blitz-dom/src/node/mod.rs +30 -0
- package/vendor/blitz-dom/src/node/node.rs +2036 -0
- package/vendor/blitz-dom/src/node/scrollbar.rs +237 -0
- package/vendor/blitz-dom/src/node/serialize.rs +225 -0
- package/vendor/blitz-dom/src/node/stylo_data.rs +142 -0
- package/vendor/blitz-dom/src/node/svg.rs +265 -0
- package/vendor/blitz-dom/src/node/text.rs +804 -0
- package/vendor/blitz-dom/src/query_selector.rs +275 -0
- package/vendor/blitz-dom/src/resolve.rs +474 -0
- package/vendor/blitz-dom/src/resolved_style.rs +797 -0
- package/vendor/blitz-dom/src/scrolling.rs +780 -0
- package/vendor/blitz-dom/src/selection.rs +122 -0
- package/vendor/blitz-dom/src/stylo.rs +1518 -0
- package/vendor/blitz-dom/src/stylo_device.rs +88 -0
- package/vendor/blitz-dom/src/stylo_to_cursor_icon.rs +49 -0
- package/vendor/blitz-dom/src/stylo_to_kurbo.rs +129 -0
- package/vendor/blitz-dom/src/stylo_to_parley.rs +655 -0
- package/vendor/blitz-dom/src/traversal.rs +600 -0
- package/vendor/blitz-dom/src/tree.rs +136 -0
- package/vendor/blitz-dom/src/url.rs +63 -0
- package/vendor/blitz-dom/src/util.rs +256 -0
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
//! Resolve style and layout
|
|
2
|
+
|
|
3
|
+
use blitz_traits::node_id::NodeId;
|
|
4
|
+
use std::cell::RefCell;
|
|
5
|
+
|
|
6
|
+
use debug_timer::debug_timer;
|
|
7
|
+
use kurbo::{Affine, Rect};
|
|
8
|
+
use parley::LayoutContext;
|
|
9
|
+
use selectors::Element as _;
|
|
10
|
+
use style::dom::TDocument;
|
|
11
|
+
|
|
12
|
+
#[cfg(feature = "parallel-construct")]
|
|
13
|
+
use rayon::prelude::*;
|
|
14
|
+
|
|
15
|
+
// FIXME: static thread_local FontCtx isn't necessarily correct in multi-document context.
|
|
16
|
+
// Should use thread_local crate with ThreadLocal value store in the Document.
|
|
17
|
+
thread_local! {
|
|
18
|
+
pub(crate) static LAYOUT_CTX: RefCell<Option<Box<LayoutContext<TextBrush>>>> = const { RefCell::new(None) };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
use taffy::AvailableSpace;
|
|
22
|
+
|
|
23
|
+
use crate::{
|
|
24
|
+
BaseDocument,
|
|
25
|
+
layout::{
|
|
26
|
+
LayoutPassState,
|
|
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
|
+
|
|
37
|
+
impl BaseDocument {
|
|
38
|
+
/// Restyle the tree and then relayout it
|
|
39
|
+
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
|
+
// Process messages that have been sent to our message channel (e.g. loaded resource)
|
|
50
|
+
self.handle_messages();
|
|
51
|
+
|
|
52
|
+
// While render-blocking resources (e.g. stylesheets linked from the `<head>`) are
|
|
53
|
+
// still loading, don't resolve styles or layout (matching how browsers block
|
|
54
|
+
// rendering). Resolving styles before the document's stylesheets have loaded would
|
|
55
|
+
// give elements computed styles based on an incomplete cascade, and a later restyle
|
|
56
|
+
// (once the stylesheet loads) would treat those as genuine "before-change styles",
|
|
57
|
+
// spuriously starting CSS transitions from unstyled values. See issue #689.
|
|
58
|
+
//
|
|
59
|
+
// `handle_messages` above must still run so that loaded resources are ingested and
|
|
60
|
+
// this state can clear.
|
|
61
|
+
if self.has_pending_critical_resources() {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
self.resolve_scroll_animation();
|
|
66
|
+
|
|
67
|
+
// Drop scrollbar-activity entries whose fade-out has finished (also
|
|
68
|
+
// sheds entries for removed nodes).
|
|
69
|
+
{
|
|
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
|
+
// Apply any device changes (viewport resize, zoom, color-scheme, etc)
|
|
79
|
+
// accumulated since the last resolve as a single device rebuild.
|
|
80
|
+
self.flush_pending_device_changes();
|
|
81
|
+
|
|
82
|
+
// we need to resolve stylist first since it will need to drive our layout bits
|
|
83
|
+
self.resolve_stylist(current_time_for_animations);
|
|
84
|
+
timer.record_time("style");
|
|
85
|
+
|
|
86
|
+
// Non-incremental mode is "every node is damaged every frame": mark the
|
|
87
|
+
// whole tree and let the same damage pipeline reconstruct and relayout
|
|
88
|
+
// everything.
|
|
89
|
+
if !self.incremental_layout {
|
|
90
|
+
self.mark_all_damaged();
|
|
91
|
+
timer.record_time("mark_all");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Propagate damage flags (from mutation and restyles) up and down the tree
|
|
95
|
+
self.propagate_damage_flags(root_node_id);
|
|
96
|
+
timer.record_time("damage");
|
|
97
|
+
|
|
98
|
+
// Fix up tree for layout (insert anonymous blocks as necessary, etc)
|
|
99
|
+
self.resolve_layout_children();
|
|
100
|
+
#[cfg(debug_assertions)]
|
|
101
|
+
self.assert_layout_parents_consistent();
|
|
102
|
+
timer.record_time("construct");
|
|
103
|
+
|
|
104
|
+
self.resolve_deferred_tasks();
|
|
105
|
+
// Flush background/mask images from style to dedicated storage on the
|
|
106
|
+
// nodes whose style changed (queued by the style traversal and by
|
|
107
|
+
// pseudo-element box construction), fetching any not-yet-loaded images.
|
|
108
|
+
self.flush_pending_style_images();
|
|
109
|
+
timer.record_time("pconstruct");
|
|
110
|
+
|
|
111
|
+
// Next we resolve layout with the data resolved by stlist
|
|
112
|
+
self.resolve_layout();
|
|
113
|
+
timer.record_time("layout");
|
|
114
|
+
self.nodes.bump_geometry_generation();
|
|
115
|
+
|
|
116
|
+
// Resolve transforms
|
|
117
|
+
self.resolve_transforms(root_node_id);
|
|
118
|
+
timer.record_time("transform");
|
|
119
|
+
|
|
120
|
+
// Build paint children / stacking contexts for damaged subtrees. Runs
|
|
121
|
+
// after layout and transforms so that stacking-context roots created by
|
|
122
|
+
// this frame's transforms are picked up, and before damage is cleared.
|
|
123
|
+
self.build_paint_tree(root_node_id);
|
|
124
|
+
timer.record_time("paint_tree");
|
|
125
|
+
|
|
126
|
+
// Clear all damage and dirty flags, walking only subtrees which are
|
|
127
|
+
// marked as (potentially) containing damage.
|
|
128
|
+
let doc_node_id = self.root_node().id;
|
|
129
|
+
self.clear_damage_and_dirty_flags(doc_node_id);
|
|
130
|
+
timer.record_time("c_damage");
|
|
131
|
+
|
|
132
|
+
// Re-resolve the hover node from the pointer position against the fresh
|
|
133
|
+
// layout. This must run *after* the damage/dirty flags are cleared
|
|
134
|
+
// above, so that the restyle hint and ancestor `dirty_descendants`
|
|
135
|
+
// flags set by any resulting hover change survive into the next resolve
|
|
136
|
+
// pass (the clearing loop would otherwise wipe them). Any resulting
|
|
137
|
+
// restyle is picked up on the next resolve pass; a redraw is requested
|
|
138
|
+
// if the hovered node actually changes.
|
|
139
|
+
self.refresh_hover();
|
|
140
|
+
|
|
141
|
+
let mut subdoc_is_animating = false;
|
|
142
|
+
for &node_id in &self.sub_document_nodes {
|
|
143
|
+
let node = &mut self.nodes[node_id];
|
|
144
|
+
let size = node.final_layout().size;
|
|
145
|
+
if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
|
|
146
|
+
// Set viewport
|
|
147
|
+
// viewport_mut handles change detection. So we just unconditionally set the values;
|
|
148
|
+
let mut sub_viewport = sub_doc.viewport_mut();
|
|
149
|
+
sub_viewport.hidpi_scale = self.viewport.hidpi_scale;
|
|
150
|
+
sub_viewport.zoom = self.viewport.zoom;
|
|
151
|
+
sub_viewport.color_scheme = self.viewport.color_scheme;
|
|
152
|
+
|
|
153
|
+
let viewport_scale = self.viewport.scale();
|
|
154
|
+
sub_viewport.window_size = (
|
|
155
|
+
(size.width * viewport_scale) as u32,
|
|
156
|
+
(size.height * viewport_scale) as u32,
|
|
157
|
+
);
|
|
158
|
+
drop(sub_viewport);
|
|
159
|
+
|
|
160
|
+
sub_doc.resolve(current_time_for_animations);
|
|
161
|
+
|
|
162
|
+
subdoc_is_animating |= sub_doc.is_animating();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
self.subdoc_is_animating = subdoc_is_animating;
|
|
166
|
+
timer.record_time("subdocs");
|
|
167
|
+
|
|
168
|
+
timer.print_times(&format!("Resolve({}): ", self.id()));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fn resolve_transforms(&mut self, node_id: NodeId) -> Rect {
|
|
172
|
+
if !self.nodes.contains_key(node_id) {
|
|
173
|
+
return Rect::ZERO;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let scale = self.viewport.scale_f64();
|
|
177
|
+
|
|
178
|
+
if !self.nodes[node_id]
|
|
179
|
+
.damage()
|
|
180
|
+
.map(|d| d.contains(style::selector_parser::RestyleDamage::RECALCULATE_OVERFLOW))
|
|
181
|
+
.unwrap_or(false)
|
|
182
|
+
{
|
|
183
|
+
let node = &self.nodes[node_id];
|
|
184
|
+
let location = node.final_layout().location.map(|v| v as f64 * scale);
|
|
185
|
+
|
|
186
|
+
let mut transform = Affine::translate((location.x, location.y));
|
|
187
|
+
if let Some(t) = node.transform().as_deref() {
|
|
188
|
+
transform *= *t
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let overflow = *node.scrollable_overflow();
|
|
192
|
+
return transform.transform_rect_bbox(overflow);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let transform = self.nodes[node_id].set_transform(scale as f32);
|
|
196
|
+
|
|
197
|
+
let w = self.nodes[node_id].final_layout().size.width as f64 * scale;
|
|
198
|
+
let h = self.nodes[node_id].final_layout().size.height as f64 * scale;
|
|
199
|
+
let mut overflow = Rect::new(0.0, 0.0, w, h);
|
|
200
|
+
|
|
201
|
+
let layout_children = std::mem::take(self.nodes[node_id].layout_children.get_mut());
|
|
202
|
+
|
|
203
|
+
if let Some(ref children) = layout_children {
|
|
204
|
+
for &child_id in children {
|
|
205
|
+
// Out-of-flow children are laid out relative to their containing
|
|
206
|
+
// block, not their DOM parent: they are visited (and their overflow
|
|
207
|
+
// accounted for) via the containing block's hoisted list below.
|
|
208
|
+
if self.nodes[child_id].is_out_of_flow() {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
let child_rect_in_self = self.resolve_transforms(child_id);
|
|
212
|
+
overflow = overflow.union(child_rect_in_self);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
let hoisted_children =
|
|
216
|
+
std::mem::take(&mut *self.nodes[node_id].hoisted_children.borrow_mut());
|
|
217
|
+
for &child_id in &hoisted_children {
|
|
218
|
+
if !self.nodes.contains_key(child_id) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
let child_rect_in_self = self.resolve_transforms(child_id);
|
|
222
|
+
overflow = overflow.union(child_rect_in_self);
|
|
223
|
+
}
|
|
224
|
+
*self.nodes[node_id].hoisted_children.borrow_mut() = hoisted_children;
|
|
225
|
+
for pseudo in [self.nodes[node_id].before(), self.nodes[node_id].after()] {
|
|
226
|
+
let Some(pseudo) = pseudo else { continue };
|
|
227
|
+
if self.nodes[pseudo].is_out_of_flow() {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
let child_rect_in_self = self.resolve_transforms(pseudo);
|
|
231
|
+
overflow = overflow.union(child_rect_in_self);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
*self.nodes[node_id].scrollable_overflow_mut() = overflow;
|
|
235
|
+
*self.nodes[node_id].layout_children.get_mut() = layout_children;
|
|
236
|
+
|
|
237
|
+
let scaled_x = self.nodes[node_id].final_layout().location.x as f64 * scale;
|
|
238
|
+
let scaled_y = self.nodes[node_id].final_layout().location.y as f64 * scale;
|
|
239
|
+
|
|
240
|
+
let full = if let Some(t) = transform {
|
|
241
|
+
Affine::translate((scaled_x, scaled_y)) * t
|
|
242
|
+
} else {
|
|
243
|
+
Affine::translate((scaled_x, scaled_y))
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
full.transform_rect_bbox(overflow)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/// Ensure that the layout_children field is populated for all nodes
|
|
250
|
+
pub fn resolve_layout_children(&mut self) {
|
|
251
|
+
resolve_layout_children_recursive(self, self.root_node().id);
|
|
252
|
+
|
|
253
|
+
fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: NodeId) {
|
|
254
|
+
// Anonymous blocks and pseudo-elements can be removed from the slab
|
|
255
|
+
// between render passes. Bail out rather than panicking on a stale key.
|
|
256
|
+
if doc.nodes.get(node_id).is_none() {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let mut damage = doc.nodes[node_id].damage().unwrap_or(ALL_DAMAGE);
|
|
261
|
+
let _flags = doc.nodes[node_id].flags;
|
|
262
|
+
|
|
263
|
+
if damage.intersects(CONSTRUCT_FC | CONSTRUCT_BOX) {
|
|
264
|
+
//} || flags.contains(NodeFlags::IS_INLINE_ROOT) {
|
|
265
|
+
|
|
266
|
+
// Deallocate the anonymous blocks created for this node in the
|
|
267
|
+
// previous construction round. They live only in the slab, so
|
|
268
|
+
// reconstructing without freeing them would leak a slab entry per
|
|
269
|
+
// anonymous block per reconstruction.
|
|
270
|
+
let old_anonymous_blocks = std::mem::take(&mut doc.nodes[node_id].anonymous_blocks);
|
|
271
|
+
for anon_id in old_anonymous_blocks {
|
|
272
|
+
doc.deallocate_anonymous_block(anon_id);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
let mut collected = LayoutChildren::default();
|
|
276
|
+
collect_layout_children(doc, node_id, &mut collected);
|
|
277
|
+
let layout_children = collected.children;
|
|
278
|
+
doc.nodes[node_id].anonymous_blocks = collected.anonymous_blocks;
|
|
279
|
+
|
|
280
|
+
// Recurse into newly collected layout children
|
|
281
|
+
for child_id in layout_children.iter().copied() {
|
|
282
|
+
resolve_layout_children_recursive(doc, child_id);
|
|
283
|
+
doc.nodes[child_id].layout_parent.set(Some(node_id));
|
|
284
|
+
if let Some(mut data) = doc.nodes[child_id]
|
|
285
|
+
.try_stylo_element_data_mut()
|
|
286
|
+
.and_then(|s| s.get_mut())
|
|
287
|
+
{
|
|
288
|
+
data.damage
|
|
289
|
+
.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
*doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
|
|
294
|
+
// *doc.nodes[node_id].paint_children.borrow_mut() = Some(layout_children);
|
|
295
|
+
|
|
296
|
+
damage.remove(CONSTRUCT_DESCENDENT | CONSTRUCT_FC | CONSTRUCT_BOX);
|
|
297
|
+
// damage.insert(RestyleDamage::RELAYOUT | RestyleDamage::REPAINT);
|
|
298
|
+
} else {
|
|
299
|
+
//if damage.contains(CONSTRUCT_DESCENDENT) {
|
|
300
|
+
let layout_children = doc.nodes[node_id].layout_children.borrow_mut().take();
|
|
301
|
+
if let Some(layout_children) = layout_children {
|
|
302
|
+
for child_id in layout_children.iter().copied() {
|
|
303
|
+
// Anonymous blocks and pseudo-elements can be removed from the
|
|
304
|
+
// slab between render passes; skip stale IDs.
|
|
305
|
+
if !doc.nodes.contains_key(child_id) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
resolve_layout_children_recursive(doc, child_id);
|
|
309
|
+
doc.nodes[child_id].layout_parent.set(Some(node_id));
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
*doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// damage.remove(CONSTRUCT_DESCENDENT);
|
|
316
|
+
// damage.insert(RestyleDamage::RELAYOUT | RestyleDamage::REPAINT);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
doc.nodes[node_id].set_damage(damage);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/// Every layout child must point back at its container via
|
|
324
|
+
/// `layout_parent`, which `propagate_damage_flags` relies on to reach
|
|
325
|
+
/// anonymous boxes.
|
|
326
|
+
#[cfg(debug_assertions)]
|
|
327
|
+
fn assert_layout_parents_consistent(&self) {
|
|
328
|
+
use crate::node::NodeFlags;
|
|
329
|
+
|
|
330
|
+
for (parent_id, node) in self.nodes.iter() {
|
|
331
|
+
if !node.flags.contains(NodeFlags::IS_IN_DOCUMENT) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
let Some(children) = node.layout_children.borrow().clone() else {
|
|
335
|
+
continue;
|
|
336
|
+
};
|
|
337
|
+
for child_id in children {
|
|
338
|
+
let Some(child) = self.nodes.get(child_id) else {
|
|
339
|
+
panic!("layout child {child_id:?} of {parent_id:?} is not in the slab");
|
|
340
|
+
};
|
|
341
|
+
debug_assert_eq!(
|
|
342
|
+
child.layout_parent.get(),
|
|
343
|
+
Some(parent_id),
|
|
344
|
+
"layout_parent of {child_id:?} does not point at its layout container {parent_id:?}"
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
pub fn resolve_deferred_tasks(&mut self) {
|
|
351
|
+
let mut deferred_construction_nodes = std::mem::take(&mut self.deferred_construction_nodes);
|
|
352
|
+
|
|
353
|
+
// Deduplicate deferred tasks by node_id to avoid redundant work
|
|
354
|
+
deferred_construction_nodes.sort_unstable_by_key(|task| task.node_id);
|
|
355
|
+
deferred_construction_nodes.dedup_by_key(|task| task.node_id);
|
|
356
|
+
|
|
357
|
+
#[cfg(feature = "parallel-construct")]
|
|
358
|
+
let iter = deferred_construction_nodes.into_par_iter();
|
|
359
|
+
#[cfg(not(feature = "parallel-construct"))]
|
|
360
|
+
let iter = deferred_construction_nodes.into_iter();
|
|
361
|
+
|
|
362
|
+
let results: Vec<ConstructionTaskResult> = iter
|
|
363
|
+
.map(|task: ConstructionTask| match task.data {
|
|
364
|
+
ConstructionTaskData::InlineLayout(mut layout) => {
|
|
365
|
+
#[cfg(feature = "parallel-construct")]
|
|
366
|
+
let mut layout_ctx = LAYOUT_CTX
|
|
367
|
+
.take()
|
|
368
|
+
.unwrap_or_else(|| Box::new(LayoutContext::new()));
|
|
369
|
+
#[cfg(feature = "parallel-construct")]
|
|
370
|
+
let layout_ctx_mut = &mut layout_ctx;
|
|
371
|
+
|
|
372
|
+
#[cfg(feature = "parallel-construct")]
|
|
373
|
+
let mut font_ctx = self
|
|
374
|
+
.thread_font_contexts
|
|
375
|
+
.get_or(|| RefCell::new(Box::new(self.font_ctx.lock().unwrap().clone())))
|
|
376
|
+
.borrow_mut();
|
|
377
|
+
#[cfg(feature = "parallel-construct")]
|
|
378
|
+
let font_ctx_mut = &mut *font_ctx;
|
|
379
|
+
|
|
380
|
+
#[cfg(not(feature = "parallel-construct"))]
|
|
381
|
+
let layout_ctx_mut = &mut self.layout_ctx;
|
|
382
|
+
#[cfg(not(feature = "parallel-construct"))]
|
|
383
|
+
let font_ctx_mut = &mut *self.font_ctx.lock().unwrap();
|
|
384
|
+
|
|
385
|
+
layout.content_widths = None;
|
|
386
|
+
build_inline_layout_into(
|
|
387
|
+
&self.nodes,
|
|
388
|
+
layout_ctx_mut,
|
|
389
|
+
font_ctx_mut,
|
|
390
|
+
&mut layout,
|
|
391
|
+
self.viewport.scale(),
|
|
392
|
+
task.node_id,
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
#[cfg(feature = "parallel-construct")]
|
|
396
|
+
{
|
|
397
|
+
LAYOUT_CTX.set(Some(layout_ctx));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// If layout doesn't contain any inline boxes, then it is safe to populate the content_widths
|
|
401
|
+
// cache during this parallelized stage.
|
|
402
|
+
// if layout.layout.inline_boxes().is_empty() {
|
|
403
|
+
// layout.content_widths();
|
|
404
|
+
// }
|
|
405
|
+
|
|
406
|
+
ConstructionTaskResult {
|
|
407
|
+
node_id: task.node_id,
|
|
408
|
+
data: ConstructionTaskResultData::InlineLayout(layout),
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
})
|
|
412
|
+
.collect();
|
|
413
|
+
|
|
414
|
+
for result in results {
|
|
415
|
+
match result.data {
|
|
416
|
+
ConstructionTaskResultData::InlineLayout(layout) => {
|
|
417
|
+
self.nodes[result.node_id].clear_layout_cache();
|
|
418
|
+
self.nodes[result.node_id]
|
|
419
|
+
.element_data_mut()
|
|
420
|
+
.unwrap()
|
|
421
|
+
.inline_layout_data = Some(layout);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
self.deferred_construction_nodes.clear();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/// Walk the nodes now that they're properly styled and transfer their styles to the taffy style system
|
|
430
|
+
///
|
|
431
|
+
/// TODO: update taffy to use an associated type instead of slab key
|
|
432
|
+
/// TODO: update taffy to support traited styles so we don't even need to rely on taffy for storage
|
|
433
|
+
pub fn resolve_layout(&mut self) {
|
|
434
|
+
let size = self.stylist.device().au_viewport_size();
|
|
435
|
+
|
|
436
|
+
let available_space = taffy::Size {
|
|
437
|
+
width: AvailableSpace::Definite(size.width.to_f32_px()),
|
|
438
|
+
height: AvailableSpace::Definite(size.height.to_f32_px()),
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
let root_element_id = crate::taffy_node_id(self.root_element().id);
|
|
442
|
+
|
|
443
|
+
// println!("\n\nRESOLVE LAYOUT\n===========\n");
|
|
444
|
+
|
|
445
|
+
let mut state = LayoutPassState::new(self);
|
|
446
|
+
#[cfg(feature = "writing-mode")]
|
|
447
|
+
let available_space = {
|
|
448
|
+
state.layout_wm = state.layout_wm_of(crate::dom_node_id(root_element_id));
|
|
449
|
+
if state.layout_wm.is_vertical() {
|
|
450
|
+
available_space.transpose()
|
|
451
|
+
} else {
|
|
452
|
+
available_space
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
taffy::compute_root_layout(&mut state, root_element_id, available_space);
|
|
456
|
+
#[cfg(feature = "writing-mode")]
|
|
457
|
+
state.physicalise_and_round_layout(root_element_id);
|
|
458
|
+
#[cfg(not(feature = "writing-mode"))]
|
|
459
|
+
if state.round_layout {
|
|
460
|
+
taffy::round_layout(&mut state, root_element_id);
|
|
461
|
+
} else {
|
|
462
|
+
// Publish geometry before resolving transforms and paint bounds, so
|
|
463
|
+
// measurement, pagination and vector painting use the same boxes.
|
|
464
|
+
for (_, node) in state.nodes.iter_mut() {
|
|
465
|
+
if node.element_data().is_some() {
|
|
466
|
+
*node.final_layout_mut() = *node.unrounded_layout();
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// println!("\n\n");
|
|
472
|
+
// taffy::print_tree(self, root_node_id)
|
|
473
|
+
}
|
|
474
|
+
}
|