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,969 @@
|
|
|
1
|
+
//! Enable the dom to lay itself out using taffy
|
|
2
|
+
//!
|
|
3
|
+
//! In servo, style and layout happen together during traversal
|
|
4
|
+
//! However, in Blitz, we do a style pass then a layout pass.
|
|
5
|
+
//! This is slower, yes, but happens fast enough that it's not a huge issue.
|
|
6
|
+
|
|
7
|
+
use crate::node::{ComputedStyleRef, ImageData, NodeData, SpecialElementData};
|
|
8
|
+
use crate::{document::BaseDocument, dom_node_id, node::Node, taffy_node_id};
|
|
9
|
+
use markup5ever::{LocalName, local_name};
|
|
10
|
+
use std::cell::Ref;
|
|
11
|
+
use std::ops::{Deref, DerefMut};
|
|
12
|
+
use std::sync::Arc;
|
|
13
|
+
use style::Atom;
|
|
14
|
+
use style::values::computed::CSSPixelLength;
|
|
15
|
+
use style::values::computed::length_percentage::CalcLengthPercentage;
|
|
16
|
+
use stylo_taffy::TaffyStyloStyle;
|
|
17
|
+
use taffy::{
|
|
18
|
+
AxisStaticEdge, AxisStaticPosition, BlockContext, CoreStyle as _, DetailedLayoutInfo,
|
|
19
|
+
FlexDirection, LayoutContainingBlock, LayoutPartialTree, MaybeMath as _, NodeId, OofCandidate,
|
|
20
|
+
ResolveOrZero, RoundTree, RunMode, TraversePartialTree, TraverseTree, compute_block_layout,
|
|
21
|
+
compute_cached_layout, compute_flexbox_layout, compute_grid_layout, compute_leaf_layout,
|
|
22
|
+
compute_oof_layout, prelude::*,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
pub(crate) mod construct;
|
|
26
|
+
pub(crate) mod damage;
|
|
27
|
+
pub(crate) mod inline;
|
|
28
|
+
pub(crate) mod list;
|
|
29
|
+
pub(crate) mod paint_tree;
|
|
30
|
+
pub(crate) mod replaced;
|
|
31
|
+
pub(crate) mod table;
|
|
32
|
+
pub(crate) mod text_transform;
|
|
33
|
+
#[cfg(feature = "writing-mode")]
|
|
34
|
+
pub(crate) mod writing_mode;
|
|
35
|
+
|
|
36
|
+
use self::replaced::{
|
|
37
|
+
IntrinsicSizes, ReplacedContext, compute_replaced_layout, is_replaced_element,
|
|
38
|
+
};
|
|
39
|
+
use self::table::TableTreeWrapper;
|
|
40
|
+
|
|
41
|
+
/// The default object size for replaced elements
|
|
42
|
+
/// (https://drafts.csswg.org/css-images/#default-object-size).
|
|
43
|
+
const DEFAULT_OBJECT_SIZE: taffy::Size<f32> = taffy::Size {
|
|
44
|
+
width: 300.0,
|
|
45
|
+
height: 150.0,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/// The intrinsic dimensions and default object size for a replaced element
|
|
49
|
+
/// whose intrinsic dimensions are determined by its tag: an image with no
|
|
50
|
+
/// loaded resource has no intrinsic dimensions and a zero default object size;
|
|
51
|
+
/// a canvas has an intrinsic size and aspect ratio given by its width/height
|
|
52
|
+
/// attributes (defaulting to 300x150); other replaced elements (video, iframe,
|
|
53
|
+
/// embed) have no intrinsic dimensions and the 300x150 default object size.
|
|
54
|
+
fn tag_intrinsic_sizes(
|
|
55
|
+
tag_name: &LocalName,
|
|
56
|
+
attr_size: taffy::Size<Option<f32>>,
|
|
57
|
+
) -> (IntrinsicSizes, taffy::Size<f32>) {
|
|
58
|
+
if *tag_name == local_name!("img") || *tag_name == local_name!("svg") {
|
|
59
|
+
return (IntrinsicSizes::default(), taffy::Size::ZERO);
|
|
60
|
+
}
|
|
61
|
+
if *tag_name == local_name!("canvas") {
|
|
62
|
+
let width = attr_size.width.unwrap_or(300.0);
|
|
63
|
+
let height = attr_size.height.unwrap_or(150.0);
|
|
64
|
+
return (
|
|
65
|
+
IntrinsicSizes {
|
|
66
|
+
width: Some(width),
|
|
67
|
+
height: Some(height),
|
|
68
|
+
ratio: Some(width / height),
|
|
69
|
+
},
|
|
70
|
+
DEFAULT_OBJECT_SIZE,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
(IntrinsicSizes::default(), DEFAULT_OBJECT_SIZE)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub(crate) fn resolve_calc_value(calc_ptr: *const (), parent_size: f32) -> f32 {
|
|
77
|
+
let calc = unsafe { &*(calc_ptr as *const CalcLengthPercentage) };
|
|
78
|
+
let result = calc.resolve(CSSPixelLength::new(parent_size));
|
|
79
|
+
result.px()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// Per-pass layout state: wraps the document for the duration of a layout pass and carries
|
|
83
|
+
/// the Taffy tree trait implementations. Derefs to [`BaseDocument`].
|
|
84
|
+
pub(crate) struct LayoutPassState<'doc> {
|
|
85
|
+
doc: &'doc mut BaseDocument,
|
|
86
|
+
/// The writing mode of the box whose layout algorithm is currently running (see
|
|
87
|
+
/// `layout::writing_mode`). Child styles read during that algorithm are expressed in its axes.
|
|
88
|
+
#[cfg(feature = "writing-mode")]
|
|
89
|
+
pub(crate) layout_wm: stylo_taffy::WritingMode,
|
|
90
|
+
/// The root element and the writing mode its algorithm runs in (see `BaseDocument::root_layout_wm`).
|
|
91
|
+
#[cfg(feature = "writing-mode")]
|
|
92
|
+
root_id: Option<crate::NodeId>,
|
|
93
|
+
#[cfg(feature = "writing-mode")]
|
|
94
|
+
root_wm: stylo_taffy::WritingMode,
|
|
95
|
+
/// Whether the node whose layout algorithm is running aligns its children's `align-self` in its
|
|
96
|
+
/// inline axis (a column flex container).
|
|
97
|
+
#[cfg(feature = "writing-mode")]
|
|
98
|
+
pub(crate) current_align_axis_is_inline: bool,
|
|
99
|
+
/// The box currently being laid out in an orthogonal flow and its containing block's inline
|
|
100
|
+
/// size, against which its percentage padding resolves (see `layout::writing_mode`).
|
|
101
|
+
#[cfg(feature = "writing-mode")]
|
|
102
|
+
pub(crate) orthogonal_percent_basis: Option<(crate::NodeId, f32)>,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
impl<'doc> LayoutPassState<'doc> {
|
|
106
|
+
/// The containing block's inline size when `dom_id` is the box currently laid out in an orthogonal
|
|
107
|
+
/// flow: its percentage padding resolves against it (see `layout::writing_mode`).
|
|
108
|
+
#[inline]
|
|
109
|
+
fn orthogonal_percent_basis_of(&self, dom_id: crate::NodeId) -> Option<f32> {
|
|
110
|
+
#[cfg(feature = "writing-mode")]
|
|
111
|
+
{
|
|
112
|
+
self.orthogonal_percent_basis
|
|
113
|
+
.filter(|(node, _)| *node == dom_id)
|
|
114
|
+
.map(|(_, basis)| basis)
|
|
115
|
+
}
|
|
116
|
+
#[cfg(not(feature = "writing-mode"))]
|
|
117
|
+
{
|
|
118
|
+
let _ = dom_id;
|
|
119
|
+
None
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub(crate) fn new(doc: &'doc mut BaseDocument) -> Self {
|
|
124
|
+
#[cfg(feature = "writing-mode")]
|
|
125
|
+
let root_id = doc.try_root_element().map(|root| root.id);
|
|
126
|
+
Self {
|
|
127
|
+
#[cfg(feature = "writing-mode")]
|
|
128
|
+
root_wm: root_id.map_or(stylo_taffy::WritingMode::empty(), |id| {
|
|
129
|
+
doc.root_layout_wm(id)
|
|
130
|
+
}),
|
|
131
|
+
doc,
|
|
132
|
+
#[cfg(feature = "writing-mode")]
|
|
133
|
+
root_id,
|
|
134
|
+
#[cfg(feature = "writing-mode")]
|
|
135
|
+
layout_wm: stylo_taffy::WritingMode::empty(),
|
|
136
|
+
#[cfg(feature = "writing-mode")]
|
|
137
|
+
current_align_axis_is_inline: false,
|
|
138
|
+
#[cfg(feature = "writing-mode")]
|
|
139
|
+
orthogonal_percent_basis: None,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
impl Deref for LayoutPassState<'_> {
|
|
145
|
+
type Target = BaseDocument;
|
|
146
|
+
fn deref(&self) -> &BaseDocument {
|
|
147
|
+
self.doc
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
impl DerefMut for LayoutPassState<'_> {
|
|
152
|
+
fn deref_mut(&mut self) -> &mut BaseDocument {
|
|
153
|
+
self.doc
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
impl LayoutPassState<'_> {
|
|
158
|
+
fn node_from_id(&self, node_id: taffy::prelude::NodeId) -> &Node {
|
|
159
|
+
&self.nodes[dom_node_id(node_id)]
|
|
160
|
+
}
|
|
161
|
+
fn node_from_id_mut(&mut self, node_id: taffy::prelude::NodeId) -> &mut Node {
|
|
162
|
+
&mut self.nodes[dom_node_id(node_id)]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
impl LayoutPassState<'_> {
|
|
167
|
+
/// Run the node's layout algorithm, then lay out the out-of-flow (absolute/fixed)
|
|
168
|
+
/// boxes for which it is the containing block. Must be called inside the layout
|
|
169
|
+
/// cache wrapper so that cache hits do not re-run the out-of-flow pass.
|
|
170
|
+
fn compute_child_layout_internal(
|
|
171
|
+
&mut self,
|
|
172
|
+
node_id: NodeId,
|
|
173
|
+
inputs: taffy::tree::LayoutInput,
|
|
174
|
+
block_ctx: Option<&mut BlockContext<'_>>,
|
|
175
|
+
) -> taffy::tree::LayoutOutput {
|
|
176
|
+
#[cfg(feature = "writing-mode")]
|
|
177
|
+
{
|
|
178
|
+
self.compute_child_layout_in_own_wm(node_id, inputs, block_ctx)
|
|
179
|
+
}
|
|
180
|
+
#[cfg(not(feature = "writing-mode"))]
|
|
181
|
+
{
|
|
182
|
+
self.compute_child_layout_in_current_wm(node_id, inputs, block_ctx)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/// Lay out `node_id` in the writing mode of the currently running algorithm (its parent's)
|
|
187
|
+
pub(crate) fn compute_child_layout_in_current_wm(
|
|
188
|
+
&mut self,
|
|
189
|
+
node_id: NodeId,
|
|
190
|
+
inputs: taffy::tree::LayoutInput,
|
|
191
|
+
block_ctx: Option<&mut BlockContext<'_>>,
|
|
192
|
+
) -> taffy::tree::LayoutOutput {
|
|
193
|
+
let mut output = self.dispatch_child_layout(node_id, inputs, block_ctx);
|
|
194
|
+
if inputs.run_mode == RunMode::PerformLayout {
|
|
195
|
+
compute_oof_layout(self, node_id, &mut output);
|
|
196
|
+
}
|
|
197
|
+
output
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/// The style of a node being laid out *by* the node whose algorithm is currently running,
|
|
201
|
+
/// expressed in that node's writing mode (see `layout::writing_mode`).
|
|
202
|
+
#[inline]
|
|
203
|
+
pub(crate) fn child_layout_style<'a>(
|
|
204
|
+
&self,
|
|
205
|
+
node: &'a Node,
|
|
206
|
+
) -> stylo_taffy::TaffyStyloStyle<ComputedStyleRef<'a>> {
|
|
207
|
+
#[cfg(feature = "writing-mode")]
|
|
208
|
+
{
|
|
209
|
+
let mut style = node.layout_style_in(self.layout_wm);
|
|
210
|
+
style.align_axis_is_inline = self.current_align_axis_is_inline;
|
|
211
|
+
style
|
|
212
|
+
}
|
|
213
|
+
#[cfg(not(feature = "writing-mode"))]
|
|
214
|
+
{
|
|
215
|
+
node.layout_style()
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fn dispatch_child_layout(
|
|
220
|
+
&mut self,
|
|
221
|
+
node_id: NodeId,
|
|
222
|
+
inputs: taffy::tree::LayoutInput,
|
|
223
|
+
block_ctx: Option<&mut BlockContext<'_>>,
|
|
224
|
+
) -> taffy::tree::LayoutOutput {
|
|
225
|
+
let orthogonal_percent_basis = self.orthogonal_percent_basis_of(dom_node_id(node_id));
|
|
226
|
+
let node = &mut self.nodes[dom_node_id(node_id)];
|
|
227
|
+
|
|
228
|
+
let font_styles = node.primary_styles().map(|style| {
|
|
229
|
+
use style::values::computed::font::LineHeight;
|
|
230
|
+
|
|
231
|
+
let font_size = style.clone_font_size().used_size().px();
|
|
232
|
+
let line_height = match style.clone_line_height() {
|
|
233
|
+
LineHeight::Normal => font_size * 1.2,
|
|
234
|
+
LineHeight::Number(num) => font_size * num.0,
|
|
235
|
+
LineHeight::Length(value) => value.0.px(),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
(font_size, line_height)
|
|
239
|
+
});
|
|
240
|
+
let font_size = font_styles.map(|s| s.0);
|
|
241
|
+
let resolved_line_height = font_styles.map(|s| s.1);
|
|
242
|
+
|
|
243
|
+
match &mut node.data {
|
|
244
|
+
NodeData::Text(data) => {
|
|
245
|
+
// With the new "inline context" architecture all text nodes should be wrapped in an "inline layout context"
|
|
246
|
+
// and should therefore never be measured individually.
|
|
247
|
+
#[cfg(feature = "tracing")]
|
|
248
|
+
tracing::error!(
|
|
249
|
+
node_id = ?dom_node_id(node_id),
|
|
250
|
+
data = ?data,
|
|
251
|
+
"Tried to lay out text node individually",
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
#[cfg(not(feature = "tracing"))]
|
|
255
|
+
let _ = data;
|
|
256
|
+
|
|
257
|
+
taffy::LayoutOutput::HIDDEN
|
|
258
|
+
// unreachable!();
|
|
259
|
+
|
|
260
|
+
// compute_leaf_layout(inputs, &node.style, |known_dimensions, available_space| {
|
|
261
|
+
// let context = TextContext {
|
|
262
|
+
// text_content: &data.content.trim(),
|
|
263
|
+
// writing_mode: WritingMode::Horizontal,
|
|
264
|
+
// };
|
|
265
|
+
// let font_metrics = FontMetrics {
|
|
266
|
+
// char_width: 8.0,
|
|
267
|
+
// char_height: 16.0,
|
|
268
|
+
// };
|
|
269
|
+
// text_measure_function(
|
|
270
|
+
// known_dimensions,
|
|
271
|
+
// available_space,
|
|
272
|
+
// &context,
|
|
273
|
+
// &font_metrics,
|
|
274
|
+
// )
|
|
275
|
+
// })
|
|
276
|
+
}
|
|
277
|
+
NodeData::Element(element_data) | NodeData::AnonymousBlock(element_data) => {
|
|
278
|
+
// TODO: deduplicate with single-line text input
|
|
279
|
+
if *element_data.name.local == *"textarea" {
|
|
280
|
+
let rows = element_data
|
|
281
|
+
.attr(local_name!("rows"))
|
|
282
|
+
.and_then(|val| val.parse::<f32>().ok())
|
|
283
|
+
.unwrap_or(2.0);
|
|
284
|
+
|
|
285
|
+
let cols = element_data
|
|
286
|
+
.attr(local_name!("cols"))
|
|
287
|
+
.and_then(|val| val.parse::<f32>().ok());
|
|
288
|
+
|
|
289
|
+
let output = compute_leaf_layout(
|
|
290
|
+
inputs,
|
|
291
|
+
&node.layout_style(),
|
|
292
|
+
resolve_calc_value,
|
|
293
|
+
|_known_size, _available_space| taffy::Size {
|
|
294
|
+
width: cols
|
|
295
|
+
.map(|cols| cols * font_size.unwrap_or(16.0) * 0.6)
|
|
296
|
+
.unwrap_or(300.0),
|
|
297
|
+
height: resolved_line_height.unwrap_or(16.0) * rows,
|
|
298
|
+
},
|
|
299
|
+
);
|
|
300
|
+
if inputs.run_mode == RunMode::PerformLayout {
|
|
301
|
+
let pb = {
|
|
302
|
+
let style = node.layout_style();
|
|
303
|
+
style
|
|
304
|
+
.padding()
|
|
305
|
+
.resolve_or_zero(inputs.parent_size.width, resolve_calc_value)
|
|
306
|
+
+ style
|
|
307
|
+
.border()
|
|
308
|
+
.resolve_or_zero(inputs.parent_size.width, resolve_calc_value)
|
|
309
|
+
};
|
|
310
|
+
let content_width = (output.size.width - pb.horizontal_axis_sum()).max(0.0);
|
|
311
|
+
let doc = &mut *self.doc;
|
|
312
|
+
let scale = doc.viewport.scale();
|
|
313
|
+
let node = &mut doc.nodes[dom_node_id(node_id)];
|
|
314
|
+
if let Some(input) = node
|
|
315
|
+
.data
|
|
316
|
+
.downcast_element_mut()
|
|
317
|
+
.and_then(|el| el.text_input_data_mut())
|
|
318
|
+
{
|
|
319
|
+
input.editor.set_width(Some(content_width * scale));
|
|
320
|
+
input.editor.refresh_layout(
|
|
321
|
+
&mut doc.font_ctx.lock().unwrap(),
|
|
322
|
+
&mut doc.layout_ctx,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return output;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if *element_data.name.local == *"input" {
|
|
330
|
+
match element_data.attr(local_name!("type")) {
|
|
331
|
+
// if the input type is hidden, hide it
|
|
332
|
+
Some("hidden") => {
|
|
333
|
+
return taffy::LayoutOutput::HIDDEN;
|
|
334
|
+
}
|
|
335
|
+
Some("checkbox") => {
|
|
336
|
+
return compute_leaf_layout(
|
|
337
|
+
inputs,
|
|
338
|
+
&node.layout_style(),
|
|
339
|
+
resolve_calc_value,
|
|
340
|
+
|_known_size, _available_space| {
|
|
341
|
+
let size = node.layout_style().size();
|
|
342
|
+
let width = size.width.resolve_or_zero(
|
|
343
|
+
inputs.parent_size.width,
|
|
344
|
+
resolve_calc_value,
|
|
345
|
+
);
|
|
346
|
+
let height = size.height.resolve_or_zero(
|
|
347
|
+
inputs.parent_size.height,
|
|
348
|
+
resolve_calc_value,
|
|
349
|
+
);
|
|
350
|
+
let min_size = width.min(height);
|
|
351
|
+
taffy::Size {
|
|
352
|
+
width: min_size,
|
|
353
|
+
height: min_size,
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
None | Some("text" | "password" | "email" | "tel" | "url" | "search") => {
|
|
359
|
+
return compute_leaf_layout(
|
|
360
|
+
inputs,
|
|
361
|
+
&node.layout_style(),
|
|
362
|
+
resolve_calc_value,
|
|
363
|
+
|_known_size, _available_space| taffy::Size {
|
|
364
|
+
width: match inputs.available_space.width {
|
|
365
|
+
AvailableSpace::Definite(limit) => limit.min(300.0),
|
|
366
|
+
AvailableSpace::MinContent => 0.0,
|
|
367
|
+
AvailableSpace::MaxContent => 300.0,
|
|
368
|
+
},
|
|
369
|
+
height: resolved_line_height.unwrap_or(16.0),
|
|
370
|
+
},
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
_ => {}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if is_replaced_element(&element_data.name.local) {
|
|
378
|
+
// Width/height attributes are presentational hints mapped to the CSS
|
|
379
|
+
// width/height properties by
|
|
380
|
+
// `synthesize_presentational_hints_for_legacy_attributes`, so they
|
|
381
|
+
// are already part of the style. They are only read here for the
|
|
382
|
+
// elements whose attributes determine their *intrinsic* size
|
|
383
|
+
// (canvas, and custom widgets on canvas tags).
|
|
384
|
+
let attr_size = taffy::Size {
|
|
385
|
+
width: element_data
|
|
386
|
+
.attr(local_name!("width"))
|
|
387
|
+
.and_then(|val| val.parse::<f32>().ok()),
|
|
388
|
+
height: element_data
|
|
389
|
+
.attr(local_name!("height"))
|
|
390
|
+
.and_then(|val| val.parse::<f32>().ok()),
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// Get the element's intrinsic dimensions and default object size
|
|
394
|
+
let (intrinsic_sizes, default_object_size) = match &element_data.special_data {
|
|
395
|
+
SpecialElementData::Image(image_data) => match &**image_data {
|
|
396
|
+
ImageData::Raster(image) => {
|
|
397
|
+
let (width, height) = (image.width as f32, image.height as f32);
|
|
398
|
+
(
|
|
399
|
+
IntrinsicSizes {
|
|
400
|
+
width: Some(width),
|
|
401
|
+
height: Some(height),
|
|
402
|
+
ratio: Some(width / height),
|
|
403
|
+
},
|
|
404
|
+
DEFAULT_OBJECT_SIZE,
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
#[cfg(feature = "svg")]
|
|
408
|
+
ImageData::Svg(svg) => {
|
|
409
|
+
let mut width = svg.intrinsic_width();
|
|
410
|
+
let mut height = svg.intrinsic_height();
|
|
411
|
+
// An SVG with no declared dimensions and no viewBox has no
|
|
412
|
+
// intrinsic dimensions per CSS, but usvg still resolves a
|
|
413
|
+
// concrete size; use it in place of the default object size.
|
|
414
|
+
if width.is_none()
|
|
415
|
+
&& height.is_none()
|
|
416
|
+
&& svg.viewbox_aspect_ratio().is_none()
|
|
417
|
+
{
|
|
418
|
+
let size = svg.tree.size();
|
|
419
|
+
width = Some(size.width());
|
|
420
|
+
height = Some(size.height());
|
|
421
|
+
}
|
|
422
|
+
(
|
|
423
|
+
IntrinsicSizes {
|
|
424
|
+
width,
|
|
425
|
+
height,
|
|
426
|
+
ratio: Some(svg.aspect_ratio()),
|
|
427
|
+
},
|
|
428
|
+
DEFAULT_OBJECT_SIZE,
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
ImageData::None => (IntrinsicSizes::default(), taffy::Size::ZERO),
|
|
432
|
+
},
|
|
433
|
+
SpecialElementData::Canvas(_)
|
|
434
|
+
| SpecialElementData::SubDocument(_)
|
|
435
|
+
| SpecialElementData::None => {
|
|
436
|
+
tag_intrinsic_sizes(&element_data.name.local, attr_size)
|
|
437
|
+
}
|
|
438
|
+
#[cfg(feature = "custom-widget")]
|
|
439
|
+
SpecialElementData::CustomWidget(widget_data) => {
|
|
440
|
+
let (fallback, default_object_size) =
|
|
441
|
+
tag_intrinsic_sizes(&element_data.name.local, attr_size);
|
|
442
|
+
// A canvas's content attributes determine its intrinsic size,
|
|
443
|
+
// overriding the widget-reported one; the widget-reported size
|
|
444
|
+
// in turn overrides the tag's fallback.
|
|
445
|
+
let attr_intrinsic =
|
|
446
|
+
if *element_data.name.local == local_name!("canvas") {
|
|
447
|
+
attr_size
|
|
448
|
+
} else {
|
|
449
|
+
taffy::Size::NONE
|
|
450
|
+
};
|
|
451
|
+
let attr_ratio = match (attr_intrinsic.width, attr_intrinsic.height) {
|
|
452
|
+
(Some(w), Some(h)) => Some(w / h),
|
|
453
|
+
_ => None,
|
|
454
|
+
};
|
|
455
|
+
let widget_sizes = widget_data.widget.intrinsic_sizes();
|
|
456
|
+
(
|
|
457
|
+
IntrinsicSizes {
|
|
458
|
+
width: attr_intrinsic
|
|
459
|
+
.width
|
|
460
|
+
.or(widget_sizes.width)
|
|
461
|
+
.or(fallback.width),
|
|
462
|
+
height: attr_intrinsic
|
|
463
|
+
.height
|
|
464
|
+
.or(widget_sizes.height)
|
|
465
|
+
.or(fallback.height),
|
|
466
|
+
ratio: attr_ratio.or(widget_sizes.ratio).or(fallback.ratio),
|
|
467
|
+
},
|
|
468
|
+
default_object_size,
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
_ => unreachable!(),
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
#[cfg(feature = "writing-mode")]
|
|
475
|
+
let (intrinsic_sizes, default_object_size) =
|
|
476
|
+
if node.writing_mode().is_vertical() {
|
|
477
|
+
// Intrinsic sizes are physical; the element's algorithm runs in its own writing mode
|
|
478
|
+
(
|
|
479
|
+
crate::layout::replaced::IntrinsicSizes {
|
|
480
|
+
width: intrinsic_sizes.height,
|
|
481
|
+
height: intrinsic_sizes.width,
|
|
482
|
+
ratio: intrinsic_sizes.ratio.map(|ratio| 1.0 / ratio),
|
|
483
|
+
},
|
|
484
|
+
default_object_size.transpose(),
|
|
485
|
+
)
|
|
486
|
+
} else {
|
|
487
|
+
(intrinsic_sizes, default_object_size)
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
let replaced_context = ReplacedContext {
|
|
491
|
+
intrinsic_sizes,
|
|
492
|
+
default_object_size,
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
let mut style = node.layout_style();
|
|
496
|
+
style.set_percent_basis(orthogonal_percent_basis);
|
|
497
|
+
return compute_replaced_layout(
|
|
498
|
+
inputs,
|
|
499
|
+
&style,
|
|
500
|
+
resolve_calc_value,
|
|
501
|
+
&replaced_context,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if node.flags.is_table_root() {
|
|
506
|
+
// The space available to an absolutely positioned table never exceeds
|
|
507
|
+
// the size of its containing block (less the table's margins), even
|
|
508
|
+
// when negative insets would otherwise add to it
|
|
509
|
+
// (https://drafts.csswg.org/css-tables-3/#abspos).
|
|
510
|
+
let mut inputs = inputs;
|
|
511
|
+
if self.nodes[dom_node_id(node_id)].is_out_of_flow() {
|
|
512
|
+
let margin = self.nodes[dom_node_id(node_id)]
|
|
513
|
+
.layout_style()
|
|
514
|
+
.margin()
|
|
515
|
+
.resolve_or_zero(inputs.parent_size.width, resolve_calc_value);
|
|
516
|
+
let max_available_space = inputs
|
|
517
|
+
.parent_size
|
|
518
|
+
.maybe_sub(margin.sum_axes())
|
|
519
|
+
.maybe_max(taffy::Size::ZERO);
|
|
520
|
+
inputs.available_space =
|
|
521
|
+
inputs.available_space.maybe_min(max_available_space);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
let SpecialElementData::TableRoot(context) = &self.nodes[dom_node_id(node_id)]
|
|
525
|
+
.data
|
|
526
|
+
.downcast_element()
|
|
527
|
+
.unwrap()
|
|
528
|
+
.special_data
|
|
529
|
+
else {
|
|
530
|
+
panic!("Node marked as table root but doesn't have TableContext");
|
|
531
|
+
};
|
|
532
|
+
let context = Arc::clone(context);
|
|
533
|
+
|
|
534
|
+
let mut table_wrapper = TableTreeWrapper {
|
|
535
|
+
doc: self,
|
|
536
|
+
ctx: context,
|
|
537
|
+
};
|
|
538
|
+
let mut output = compute_grid_layout(&mut table_wrapper, node_id, inputs);
|
|
539
|
+
|
|
540
|
+
// Absolutely positioned children take no part in the table grid. Their
|
|
541
|
+
// static position is the top-left of the table's content box.
|
|
542
|
+
let context = table_wrapper.ctx;
|
|
543
|
+
if inputs.run_mode == RunMode::PerformLayout && !context.oof_children.is_empty()
|
|
544
|
+
{
|
|
545
|
+
let style = &context.style;
|
|
546
|
+
let parent_width = inputs.parent_size.width;
|
|
547
|
+
let padding = style
|
|
548
|
+
.padding
|
|
549
|
+
.resolve_or_zero(parent_width, resolve_calc_value);
|
|
550
|
+
let border = style
|
|
551
|
+
.border
|
|
552
|
+
.resolve_or_zero(parent_width, resolve_calc_value);
|
|
553
|
+
let static_position = taffy::Point {
|
|
554
|
+
x: padding.left + border.left,
|
|
555
|
+
y: padding.top + border.top,
|
|
556
|
+
}
|
|
557
|
+
.map(|pos| AxisStaticPosition::from_edge(pos, AxisStaticEdge::Start));
|
|
558
|
+
let in_flow_count = context.cells.len();
|
|
559
|
+
for (idx, (child_id, position)) in context.oof_children.iter().enumerate() {
|
|
560
|
+
output.oof_candidates.push(OofCandidate {
|
|
561
|
+
node: taffy_node_id(*child_id),
|
|
562
|
+
order: (in_flow_count + idx) as u32,
|
|
563
|
+
position: *position,
|
|
564
|
+
static_position,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// HACK: Cap scrollable overflow at node size to prevent scrolling
|
|
570
|
+
output.scrollable_overflow_rect.left = 0.0;
|
|
571
|
+
output.scrollable_overflow_rect.top = 0.0;
|
|
572
|
+
output.scrollable_overflow_rect.right =
|
|
573
|
+
output.scrollable_overflow_rect.right.min(output.size.width);
|
|
574
|
+
output.scrollable_overflow_rect.bottom = output
|
|
575
|
+
.scrollable_overflow_rect
|
|
576
|
+
.bottom
|
|
577
|
+
.min(output.size.height);
|
|
578
|
+
|
|
579
|
+
return output;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if node.flags.is_inline_root() {
|
|
583
|
+
return self.compute_inline_layout(dom_node_id(node_id), inputs, block_ctx);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// The default CSS file will set
|
|
587
|
+
match node.taffy_display() {
|
|
588
|
+
Display::Block => compute_block_layout(self, node_id, inputs, block_ctx),
|
|
589
|
+
Display::FlowRoot => compute_block_layout(self, node_id, inputs, None),
|
|
590
|
+
Display::Flex => {
|
|
591
|
+
#[cfg(feature = "writing-mode")]
|
|
592
|
+
{
|
|
593
|
+
self.current_align_axis_is_inline = node.is_column_flex_container();
|
|
594
|
+
}
|
|
595
|
+
compute_flexbox_layout(self, node_id, inputs)
|
|
596
|
+
}
|
|
597
|
+
Display::Grid => compute_grid_layout(self, node_id, inputs),
|
|
598
|
+
Display::None => taffy::LayoutOutput::HIDDEN,
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
NodeData::Document(_) => compute_block_layout(self, node_id, inputs, None),
|
|
602
|
+
|
|
603
|
+
_ => taffy::LayoutOutput::HIDDEN,
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
impl TraversePartialTree for LayoutPassState<'_> {
|
|
609
|
+
type ChildIter<'a>
|
|
610
|
+
= RefCellChildIter<'a>
|
|
611
|
+
where
|
|
612
|
+
Self: 'a;
|
|
613
|
+
|
|
614
|
+
fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
|
|
615
|
+
let layout_children = self.node_from_id(node_id).layout_children.borrow(); //.unwrap().as_ref();
|
|
616
|
+
RefCellChildIter::new(Ref::map(layout_children, |children| {
|
|
617
|
+
children.as_ref().map(|c| c.as_slice()).unwrap_or(&[])
|
|
618
|
+
}))
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
fn child_count(&self, node_id: NodeId) -> usize {
|
|
622
|
+
self.node_from_id(node_id)
|
|
623
|
+
.layout_children
|
|
624
|
+
.borrow()
|
|
625
|
+
.as_ref()
|
|
626
|
+
.map(|c| c.len())
|
|
627
|
+
.unwrap_or(0)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
fn get_child_id(&self, node_id: NodeId, index: usize) -> NodeId {
|
|
631
|
+
taffy_node_id(
|
|
632
|
+
self.node_from_id(node_id)
|
|
633
|
+
.layout_children
|
|
634
|
+
.borrow()
|
|
635
|
+
.as_ref()
|
|
636
|
+
.unwrap()[index],
|
|
637
|
+
)
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
impl TraverseTree for LayoutPassState<'_> {}
|
|
641
|
+
|
|
642
|
+
impl LayoutPartialTree for LayoutPassState<'_> {
|
|
643
|
+
type CoreContainerStyle<'a>
|
|
644
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
645
|
+
where
|
|
646
|
+
Self: 'a;
|
|
647
|
+
|
|
648
|
+
type CustomIdent = Atom;
|
|
649
|
+
|
|
650
|
+
fn get_core_container_style(&self, node_id: NodeId) -> Self::CoreContainerStyle<'_> {
|
|
651
|
+
#[cfg(feature = "writing-mode")]
|
|
652
|
+
{
|
|
653
|
+
// Container styles are read by the node's own algorithm (or, for the containing block
|
|
654
|
+
// of an out-of-flow box, by that box's algorithm) and are expressed in the node's writing mode.
|
|
655
|
+
let dom_id = dom_node_id(node_id);
|
|
656
|
+
let mut style = self.nodes[dom_id].layout_style_in(self.layout_wm_of(dom_id));
|
|
657
|
+
style.set_percent_basis(self.orthogonal_percent_basis_of(dom_id));
|
|
658
|
+
style
|
|
659
|
+
}
|
|
660
|
+
#[cfg(not(feature = "writing-mode"))]
|
|
661
|
+
{
|
|
662
|
+
self.node_from_id(node_id).layout_style()
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
|
|
667
|
+
*self.node_from_id_mut(node_id).unrounded_layout_mut() = *layout;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
fn resolve_calc_value(&self, calc_ptr: *const (), parent_size: f32) -> f32 {
|
|
671
|
+
resolve_calc_value(calc_ptr, parent_size)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
#[inline(always)]
|
|
675
|
+
fn compute_child_layout(
|
|
676
|
+
&mut self,
|
|
677
|
+
node_id: NodeId,
|
|
678
|
+
inputs: taffy::LayoutInput,
|
|
679
|
+
) -> taffy::LayoutOutput {
|
|
680
|
+
compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
|
|
681
|
+
tree.compute_child_layout_internal(node_id, inputs, None)
|
|
682
|
+
})
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
impl LayoutContainingBlock for LayoutPassState<'_> {
|
|
687
|
+
type OofItemStyle<'a>
|
|
688
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
689
|
+
where
|
|
690
|
+
Self: 'a;
|
|
691
|
+
|
|
692
|
+
fn get_oof_item_style(&self, node_id: NodeId) -> Self::OofItemStyle<'_> {
|
|
693
|
+
self.child_layout_style(self.node_from_id(node_id))
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
fn clear_hoisted_children(&mut self, node_id: NodeId) {
|
|
697
|
+
let containing_block = dom_node_id(node_id);
|
|
698
|
+
let mut hoisted = self.node_from_id(node_id).hoisted_children.borrow_mut();
|
|
699
|
+
for &hoisted_id in hoisted.iter() {
|
|
700
|
+
// A box which has since been claimed by another containing block
|
|
701
|
+
// must keep pointing at that one.
|
|
702
|
+
if let Some(node) = self.nodes.get(hoisted_id) {
|
|
703
|
+
if node.oof_containing_block.get() == Some(containing_block) {
|
|
704
|
+
node.oof_containing_block.set(None);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
hoisted.clear();
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
fn add_hoisted_children(&mut self, node_id: NodeId, hoisted: &[NodeId]) {
|
|
712
|
+
let containing_block = dom_node_id(node_id);
|
|
713
|
+
let node = self.node_from_id(node_id);
|
|
714
|
+
node.hoisted_children
|
|
715
|
+
.borrow_mut()
|
|
716
|
+
.extend(hoisted.iter().copied().map(dom_node_id));
|
|
717
|
+
for &hoisted_id in hoisted {
|
|
718
|
+
self.node_from_id(hoisted_id)
|
|
719
|
+
.oof_containing_block
|
|
720
|
+
.set(Some(containing_block));
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
fn get_detailed_layout_info(&self, node_id: NodeId) -> &DetailedLayoutInfo<Atom> {
|
|
725
|
+
self.node_from_id(node_id)
|
|
726
|
+
.element_data()
|
|
727
|
+
.map(|element| &element.detailed_layout_info)
|
|
728
|
+
.unwrap_or(&DetailedLayoutInfo::None)
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
impl taffy::CacheTree for LayoutPassState<'_> {
|
|
733
|
+
#[inline]
|
|
734
|
+
fn cache_get(
|
|
735
|
+
&mut self,
|
|
736
|
+
node_id: NodeId,
|
|
737
|
+
inputs: &taffy::LayoutInput,
|
|
738
|
+
) -> Option<taffy::LayoutOutput> {
|
|
739
|
+
self.node_from_id_mut(node_id)
|
|
740
|
+
.try_layout_data_mut()?
|
|
741
|
+
.cache
|
|
742
|
+
.get(inputs)
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
#[inline]
|
|
746
|
+
fn cache_store(
|
|
747
|
+
&mut self,
|
|
748
|
+
node_id: NodeId,
|
|
749
|
+
inputs: &taffy::LayoutInput,
|
|
750
|
+
layout_output: taffy::LayoutOutput,
|
|
751
|
+
) {
|
|
752
|
+
self.node_from_id_mut(node_id)
|
|
753
|
+
.cache_mut()
|
|
754
|
+
.store(inputs, layout_output);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
#[inline]
|
|
758
|
+
fn cache_clear(&mut self, node_id: NodeId) {
|
|
759
|
+
self.node_from_id_mut(node_id).clear_layout_cache();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
impl taffy::LayoutBlockContainer for LayoutPassState<'_> {
|
|
764
|
+
type BlockContainerStyle<'a>
|
|
765
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
766
|
+
where
|
|
767
|
+
Self: 'a;
|
|
768
|
+
|
|
769
|
+
type BlockItemStyle<'a>
|
|
770
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
771
|
+
where
|
|
772
|
+
Self: 'a;
|
|
773
|
+
|
|
774
|
+
fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> {
|
|
775
|
+
self.get_core_container_style(node_id)
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_> {
|
|
779
|
+
self.child_layout_style(self.node_from_id(child_node_id))
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
#[inline(always)]
|
|
783
|
+
fn compute_block_child_layout(
|
|
784
|
+
&mut self,
|
|
785
|
+
node_id: NodeId,
|
|
786
|
+
inputs: taffy::LayoutInput,
|
|
787
|
+
block_ctx: Option<&mut BlockContext<'_>>,
|
|
788
|
+
) -> taffy::LayoutOutput {
|
|
789
|
+
compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
|
|
790
|
+
tree.compute_child_layout_internal(node_id, inputs, block_ctx)
|
|
791
|
+
})
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
impl taffy::LayoutFlexboxContainer for LayoutPassState<'_> {
|
|
796
|
+
type FlexboxContainerStyle<'a>
|
|
797
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
798
|
+
where
|
|
799
|
+
Self: 'a;
|
|
800
|
+
|
|
801
|
+
type FlexboxItemStyle<'a>
|
|
802
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
803
|
+
where
|
|
804
|
+
Self: 'a;
|
|
805
|
+
|
|
806
|
+
fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
|
|
807
|
+
self.get_core_container_style(node_id)
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
|
|
811
|
+
self.child_layout_style(self.node_from_id(child_node_id))
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
impl taffy::LayoutGridContainer for LayoutPassState<'_> {
|
|
816
|
+
type GridContainerStyle<'a>
|
|
817
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
818
|
+
where
|
|
819
|
+
Self: 'a;
|
|
820
|
+
|
|
821
|
+
type GridItemStyle<'a>
|
|
822
|
+
= TaffyStyloStyle<ComputedStyleRef<'a>>
|
|
823
|
+
where
|
|
824
|
+
Self: 'a;
|
|
825
|
+
|
|
826
|
+
fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
|
|
827
|
+
self.get_core_container_style(node_id)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
|
|
831
|
+
self.child_layout_style(self.node_from_id(child_node_id))
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
fn set_detailed_grid_info(
|
|
835
|
+
&mut self,
|
|
836
|
+
node_id: NodeId,
|
|
837
|
+
detailed_grid_info: taffy::DetailedGridInfo<Atom>,
|
|
838
|
+
) {
|
|
839
|
+
let node = self.node_from_id_mut(node_id);
|
|
840
|
+
if let Some(element) = node.element_data_mut() {
|
|
841
|
+
element.detailed_layout_info = DetailedLayoutInfo::Grid(Box::new(detailed_grid_info));
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
impl RoundTree for LayoutPassState<'_> {
|
|
847
|
+
fn get_unrounded_layout(&self, node_id: NodeId) -> Layout {
|
|
848
|
+
*self.node_from_id(node_id).unrounded_layout()
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout) {
|
|
852
|
+
*self.node_from_id_mut(node_id).final_layout_mut() = *layout;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
fn is_out_of_flow(&self, node_id: NodeId) -> bool {
|
|
856
|
+
self.node_from_id(node_id).is_out_of_flow()
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
fn hoisted_child_count(&self, node_id: NodeId) -> usize {
|
|
860
|
+
self.node_from_id(node_id).hoisted_children.borrow().len()
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
fn get_hoisted_child_id(&self, node_id: NodeId, index: usize) -> NodeId {
|
|
864
|
+
taffy_node_id(self.node_from_id(node_id).hoisted_children.borrow()[index])
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
pub(crate) struct TaffyDebugTree<'doc>(pub(crate) &'doc BaseDocument);
|
|
869
|
+
|
|
870
|
+
impl TaffyDebugTree<'_> {
|
|
871
|
+
fn node_from_id(&self, node_id: NodeId) -> &Node {
|
|
872
|
+
&self.0.nodes[dom_node_id(node_id)]
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
impl TraversePartialTree for TaffyDebugTree<'_> {
|
|
877
|
+
type ChildIter<'a>
|
|
878
|
+
= RefCellChildIter<'a>
|
|
879
|
+
where
|
|
880
|
+
Self: 'a;
|
|
881
|
+
|
|
882
|
+
fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
|
|
883
|
+
let layout_children = self.node_from_id(node_id).layout_children.borrow();
|
|
884
|
+
RefCellChildIter::new(Ref::map(layout_children, |children| {
|
|
885
|
+
children.as_ref().map(|c| c.as_slice()).unwrap_or(&[])
|
|
886
|
+
}))
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
fn child_count(&self, node_id: NodeId) -> usize {
|
|
890
|
+
self.node_from_id(node_id)
|
|
891
|
+
.layout_children
|
|
892
|
+
.borrow()
|
|
893
|
+
.as_ref()
|
|
894
|
+
.map(|c| c.len())
|
|
895
|
+
.unwrap_or(0)
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
fn get_child_id(&self, node_id: NodeId, index: usize) -> NodeId {
|
|
899
|
+
taffy_node_id(
|
|
900
|
+
self.node_from_id(node_id)
|
|
901
|
+
.layout_children
|
|
902
|
+
.borrow()
|
|
903
|
+
.as_ref()
|
|
904
|
+
.unwrap()[index],
|
|
905
|
+
)
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
impl TraverseTree for TaffyDebugTree<'_> {}
|
|
910
|
+
|
|
911
|
+
impl PrintTree for TaffyDebugTree<'_> {
|
|
912
|
+
fn get_debug_label(&self, node_id: NodeId) -> &'static str {
|
|
913
|
+
let node = &self.node_from_id(node_id);
|
|
914
|
+
|
|
915
|
+
match node.data {
|
|
916
|
+
NodeData::Document(_) => "DOCUMENT",
|
|
917
|
+
// NodeData::Doctype { .. } => return "DOCTYPE",
|
|
918
|
+
NodeData::Text { .. } => node.node_debug_str().leak(),
|
|
919
|
+
NodeData::Comment { .. } => "COMMENT",
|
|
920
|
+
NodeData::AnonymousBlock(_) => "ANONYMOUS BLOCK",
|
|
921
|
+
NodeData::Element(_) => {
|
|
922
|
+
let style = node.layout_style();
|
|
923
|
+
let display = match node.taffy_display() {
|
|
924
|
+
Display::Flex => match taffy::FlexboxContainerStyle::flex_direction(&style) {
|
|
925
|
+
FlexDirection::Row | FlexDirection::RowReverse => "FLEX ROW",
|
|
926
|
+
FlexDirection::Column | FlexDirection::ColumnReverse => "FLEX COL",
|
|
927
|
+
},
|
|
928
|
+
Display::Grid => "GRID",
|
|
929
|
+
Display::Block => "BLOCK",
|
|
930
|
+
Display::FlowRoot => "FLOW ROOT",
|
|
931
|
+
Display::None => "NONE",
|
|
932
|
+
};
|
|
933
|
+
format!("{} ({})", node.node_debug_str(), display).leak()
|
|
934
|
+
} // NodeData::ProcessingInstruction { .. } => return "PROCESSING INSTRUCTION",
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
fn get_final_layout(&self, node_id: NodeId) -> Layout {
|
|
939
|
+
*self.node_from_id(node_id).final_layout()
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// pub struct ChildIter<'a>(std::slice::Iter<'a, usize>);
|
|
944
|
+
// impl<'a> Iterator for ChildIter<'a> {
|
|
945
|
+
// type Item = NodeId;
|
|
946
|
+
// fn next(&mut self) -> Option<Self::Item> {
|
|
947
|
+
// self.0.next().copied().map(NodeId::from)
|
|
948
|
+
// }
|
|
949
|
+
// }
|
|
950
|
+
|
|
951
|
+
pub struct RefCellChildIter<'a> {
|
|
952
|
+
items: Ref<'a, [crate::NodeId]>,
|
|
953
|
+
idx: usize,
|
|
954
|
+
}
|
|
955
|
+
impl<'a> RefCellChildIter<'a> {
|
|
956
|
+
fn new(items: Ref<'a, [crate::NodeId]>) -> RefCellChildIter<'a> {
|
|
957
|
+
RefCellChildIter { items, idx: 0 }
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
impl Iterator for RefCellChildIter<'_> {
|
|
962
|
+
type Item = NodeId;
|
|
963
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
964
|
+
self.items.get(self.idx).map(|id| {
|
|
965
|
+
self.idx += 1;
|
|
966
|
+
taffy_node_id(*id)
|
|
967
|
+
})
|
|
968
|
+
}
|
|
969
|
+
}
|