1use crate::node::{ImageData, NodeData, SpecialElementData};
8use crate::{document::BaseDocument, node::Node};
9use markup5ever::local_name;
10use std::cell::Ref;
11use std::sync::Arc;
12use style::Atom;
13use style::values::computed::CSSPixelLength;
14use style::values::computed::length_percentage::CalcLengthPercentage;
15use taffy::{
16 CollapsibleMarginSet, FlexDirection, LayoutPartialTree, NodeId, ResolveOrZero, RoundTree,
17 Style, TraversePartialTree, TraverseTree, compute_block_layout, compute_cached_layout,
18 compute_flexbox_layout, compute_grid_layout, compute_leaf_layout, prelude::*,
19};
20
21pub(crate) mod construct;
22pub(crate) mod damage;
23pub(crate) mod inline;
24pub(crate) mod list;
25pub(crate) mod replaced;
26pub(crate) mod table;
27
28use self::replaced::{ReplacedContext, replaced_measure_function};
29use self::table::TableTreeWrapper;
30
31pub(crate) fn resolve_calc_value(calc_ptr: *const (), parent_size: f32) -> f32 {
32 let calc = unsafe { &*(calc_ptr as *const CalcLengthPercentage) };
33 let result = calc.resolve(CSSPixelLength::new(parent_size));
34 result.px()
35}
36
37impl BaseDocument {
38 fn node_from_id(&self, node_id: taffy::prelude::NodeId) -> &Node {
39 &self.nodes[node_id.into()]
40 }
41 fn node_from_id_mut(&mut self, node_id: taffy::prelude::NodeId) -> &mut Node {
42 &mut self.nodes[node_id.into()]
43 }
44}
45
46impl TraversePartialTree for BaseDocument {
47 type ChildIter<'a> = RefCellChildIter<'a>;
48
49 fn child_ids(&self, node_id: NodeId) -> Self::ChildIter<'_> {
50 let layout_children = self.node_from_id(node_id).layout_children.borrow(); RefCellChildIter::new(Ref::map(layout_children, |children| {
52 children.as_ref().map(|c| c.as_slice()).unwrap_or(&[])
53 }))
54 }
55
56 fn child_count(&self, node_id: NodeId) -> usize {
57 self.node_from_id(node_id)
58 .layout_children
59 .borrow()
60 .as_ref()
61 .map(|c| c.len())
62 .unwrap_or(0)
63 }
64
65 fn get_child_id(&self, node_id: NodeId, index: usize) -> NodeId {
66 NodeId::from(
67 self.node_from_id(node_id)
68 .layout_children
69 .borrow()
70 .as_ref()
71 .unwrap()[index],
72 )
73 }
74}
75impl TraverseTree for BaseDocument {}
76
77impl LayoutPartialTree for BaseDocument {
78 type CoreContainerStyle<'a>
79 = &'a taffy::Style<Atom>
80 where
81 Self: 'a;
82
83 type CustomIdent = Atom;
84
85 fn get_core_container_style(&self, node_id: NodeId) -> &Style<Atom> {
86 &self.node_from_id(node_id).style
87 }
88
89 fn set_unrounded_layout(&mut self, node_id: NodeId, layout: &Layout) {
90 self.node_from_id_mut(node_id).unrounded_layout = *layout;
91 }
92
93 fn resolve_calc_value(&self, calc_ptr: *const (), parent_size: f32) -> f32 {
94 resolve_calc_value(calc_ptr, parent_size)
95 }
96
97 fn compute_child_layout(
98 &mut self,
99 node_id: NodeId,
100 inputs: taffy::tree::LayoutInput,
101 ) -> taffy::tree::LayoutOutput {
102 compute_cached_layout(self, node_id, inputs, |tree, node_id, inputs| {
103 let node = &mut tree.nodes[node_id.into()];
104
105 let font_styles = node.primary_styles().map(|style| {
106 use style::values::computed::font::LineHeight;
107
108 let font_size = style.clone_font_size().used_size().px();
109 let line_height = match style.clone_line_height() {
110 LineHeight::Normal => font_size * 1.2,
111 LineHeight::Number(num) => font_size * num.0,
112 LineHeight::Length(value) => value.0.px(),
113 };
114
115 (font_size, line_height)
116 });
117 let font_size = font_styles.map(|s| s.0);
118 let resolved_line_height = font_styles.map(|s| s.1);
119
120 match &mut node.data {
121 NodeData::Text(data) => {
122 println!(
125 "ERROR: Tried to lay out text node individually ({})",
126 usize::from(node_id)
127 );
128 dbg!(data);
129 taffy::LayoutOutput::HIDDEN
130 }
149 NodeData::Element(element_data) | NodeData::AnonymousBlock(element_data) => {
150 if *element_data.name.local == *"textarea" {
152 let rows = element_data
153 .attr(local_name!("rows"))
154 .and_then(|val| val.parse::<f32>().ok())
155 .unwrap_or(2.0);
156
157 let cols = element_data
158 .attr(local_name!("cols"))
159 .and_then(|val| val.parse::<f32>().ok());
160
161 return compute_leaf_layout(
162 inputs,
163 &node.style,
164 resolve_calc_value,
165 |_known_size, _available_space| taffy::Size {
166 width: cols
167 .map(|cols| cols * font_size.unwrap_or(16.0) * 0.6)
168 .unwrap_or(300.0),
169 height: resolved_line_height.unwrap_or(16.0) * rows,
170 },
171 );
172 }
173
174 if *element_data.name.local == *"input" {
175 match element_data.attr(local_name!("type")) {
176 Some("hidden") => {
178 node.style.display = Display::None;
179 return taffy::LayoutOutput::HIDDEN;
180 }
181 Some("checkbox") => {
182 return compute_leaf_layout(
183 inputs,
184 &node.style,
185 resolve_calc_value,
186 |_known_size, _available_space| {
187 let width = node.style.size.width.resolve_or_zero(
188 inputs.parent_size.width,
189 resolve_calc_value,
190 );
191 let height = node.style.size.height.resolve_or_zero(
192 inputs.parent_size.height,
193 resolve_calc_value,
194 );
195 let min_size = width.min(height);
196 taffy::Size {
197 width: min_size,
198 height: min_size,
199 }
200 },
201 );
202 }
203 None | Some("text" | "password" | "email") => {
204 return compute_leaf_layout(
205 inputs,
206 &node.style,
207 resolve_calc_value,
208 |_known_size, _available_space| taffy::Size {
209 width: 300.0,
210 height: resolved_line_height.unwrap_or(16.0),
211 },
212 );
213 }
214 _ => {}
215 }
216 }
217
218 if *element_data.name.local == *"img"
219 || *element_data.name.local == *"canvas"
220 || (cfg!(feature = "svg") && *element_data.name.local == *"svg")
221 {
222 let attr_size = taffy::Size {
227 width: element_data
228 .attr(local_name!("width"))
229 .and_then(|val| val.parse::<f32>().ok()),
230 height: element_data
231 .attr(local_name!("height"))
232 .and_then(|val| val.parse::<f32>().ok()),
233 };
234
235 let inherent_size = match &element_data.special_data {
237 SpecialElementData::Image(image_data) => match &**image_data {
238 ImageData::Raster(image) => taffy::Size {
239 width: image.width as f32,
240 height: image.height as f32,
241 },
242 #[cfg(feature = "svg")]
243 ImageData::Svg(svg) => {
244 let size = svg.size();
245 taffy::Size {
246 width: size.width(),
247 height: size.height(),
248 }
249 }
250 ImageData::None => taffy::Size::ZERO,
251 },
252 SpecialElementData::Canvas(_) => taffy::Size::ZERO,
253 SpecialElementData::None => taffy::Size::ZERO,
254 _ => unreachable!(),
255 };
256
257 let replaced_context = ReplacedContext {
258 inherent_size,
259 attr_size,
260 };
261
262 let computed = replaced_measure_function(
263 inputs.known_dimensions,
264 inputs.parent_size,
265 inputs.available_space,
266 &replaced_context,
267 &node.style,
268 false,
269 );
270
271 return taffy::LayoutOutput {
272 size: computed,
273 content_size: computed,
274 first_baselines: taffy::Point::NONE,
275 top_margin: CollapsibleMarginSet::ZERO,
276 bottom_margin: CollapsibleMarginSet::ZERO,
277 margins_can_collapse_through: false,
278 };
279 }
280
281 if node.flags.is_table_root() {
282 let SpecialElementData::TableRoot(context) = &tree.nodes[node_id.into()]
283 .data
284 .downcast_element()
285 .unwrap()
286 .special_data
287 else {
288 panic!("Node marked as table root but doesn't have TableContext");
289 };
290 let context = Arc::clone(context);
291
292 let mut table_wrapper = TableTreeWrapper {
293 doc: tree,
294 ctx: context,
295 };
296 return compute_grid_layout(&mut table_wrapper, node_id, inputs);
297 }
298
299 if node.flags.is_inline_root() {
300 return tree.compute_inline_layout(usize::from(node_id), inputs);
301 }
302
303 match node.style.display {
305 Display::Block => compute_block_layout(tree, node_id, inputs),
306 Display::Flex => compute_flexbox_layout(tree, node_id, inputs),
307 Display::Grid => compute_grid_layout(tree, node_id, inputs),
308 Display::None => taffy::LayoutOutput::HIDDEN,
309 }
310 }
311 NodeData::Document => compute_block_layout(tree, node_id, inputs),
312
313 _ => taffy::LayoutOutput::HIDDEN,
314 }
315 })
316 }
317}
318
319impl taffy::CacheTree for BaseDocument {
320 #[inline]
321 fn cache_get(
322 &self,
323 node_id: NodeId,
324 known_dimensions: Size<Option<f32>>,
325 available_space: Size<AvailableSpace>,
326 run_mode: taffy::RunMode,
327 ) -> Option<taffy::LayoutOutput> {
328 self.node_from_id(node_id)
329 .cache
330 .get(known_dimensions, available_space, run_mode)
331 }
332
333 #[inline]
334 fn cache_store(
335 &mut self,
336 node_id: NodeId,
337 known_dimensions: Size<Option<f32>>,
338 available_space: Size<AvailableSpace>,
339 run_mode: taffy::RunMode,
340 layout_output: taffy::LayoutOutput,
341 ) {
342 self.node_from_id_mut(node_id).cache.store(
343 known_dimensions,
344 available_space,
345 run_mode,
346 layout_output,
347 );
348 }
349
350 #[inline]
351 fn cache_clear(&mut self, node_id: NodeId) {
352 self.node_from_id_mut(node_id).cache.clear();
353 }
354}
355
356impl taffy::LayoutBlockContainer for BaseDocument {
357 type BlockContainerStyle<'a>
358 = &'a Style<Atom>
359 where
360 Self: 'a;
361
362 type BlockItemStyle<'a>
363 = &'a Style<Atom>
364 where
365 Self: 'a;
366
367 fn get_block_container_style(&self, node_id: NodeId) -> Self::BlockContainerStyle<'_> {
368 self.get_core_container_style(node_id)
369 }
370
371 fn get_block_child_style(&self, child_node_id: NodeId) -> Self::BlockItemStyle<'_> {
372 self.get_core_container_style(child_node_id)
373 }
374}
375
376impl taffy::LayoutFlexboxContainer for BaseDocument {
377 type FlexboxContainerStyle<'a>
378 = &'a Style<Atom>
379 where
380 Self: 'a;
381
382 type FlexboxItemStyle<'a>
383 = &'a Style<Atom>
384 where
385 Self: 'a;
386
387 fn get_flexbox_container_style(&self, node_id: NodeId) -> Self::FlexboxContainerStyle<'_> {
388 self.get_core_container_style(node_id)
389 }
390
391 fn get_flexbox_child_style(&self, child_node_id: NodeId) -> Self::FlexboxItemStyle<'_> {
392 self.get_core_container_style(child_node_id)
393 }
394}
395
396impl taffy::LayoutGridContainer for BaseDocument {
397 type GridContainerStyle<'a>
398 = &'a Style<Atom>
399 where
400 Self: 'a;
401
402 type GridItemStyle<'a>
403 = &'a Style<Atom>
404 where
405 Self: 'a;
406
407 fn get_grid_container_style(&self, node_id: NodeId) -> Self::GridContainerStyle<'_> {
408 self.get_core_container_style(node_id)
409 }
410
411 fn get_grid_child_style(&self, child_node_id: NodeId) -> Self::GridItemStyle<'_> {
412 self.get_core_container_style(child_node_id)
413 }
414}
415
416impl RoundTree for BaseDocument {
417 fn get_unrounded_layout(&self, node_id: NodeId) -> Layout {
418 self.node_from_id(node_id).unrounded_layout
419 }
420
421 fn set_final_layout(&mut self, node_id: NodeId, layout: &Layout) {
422 self.node_from_id_mut(node_id).final_layout = *layout;
423 }
424}
425
426impl PrintTree for BaseDocument {
427 fn get_debug_label(&self, node_id: NodeId) -> &'static str {
428 let node = &self.node_from_id(node_id);
429 let style = &node.style;
430
431 match node.data {
432 NodeData::Document => "DOCUMENT",
433 NodeData::Text { .. } => node.node_debug_str().leak(),
435 NodeData::Comment => "COMMENT",
436 NodeData::AnonymousBlock(_) => "ANONYMOUS BLOCK",
437 NodeData::Element(_) => {
438 let display = match style.display {
439 Display::Flex => match style.flex_direction {
440 FlexDirection::Row | FlexDirection::RowReverse => "FLEX ROW",
441 FlexDirection::Column | FlexDirection::ColumnReverse => "FLEX COL",
442 },
443 Display::Grid => "GRID",
444 Display::Block => "BLOCK",
445 Display::None => "NONE",
446 };
447 format!("{} ({})", node.node_debug_str(), display).leak()
448 } }
450 }
451
452 fn get_final_layout(&self, node_id: NodeId) -> Layout {
453 self.node_from_id(node_id).final_layout
454 }
455}
456
457pub struct RefCellChildIter<'a> {
466 items: Ref<'a, [usize]>,
467 idx: usize,
468}
469impl<'a> RefCellChildIter<'a> {
470 fn new(items: Ref<'a, [usize]>) -> RefCellChildIter<'a> {
471 RefCellChildIter { items, idx: 0 }
472 }
473}
474
475impl Iterator for RefCellChildIter<'_> {
476 type Item = NodeId;
477 fn next(&mut self) -> Option<Self::Item> {
478 self.items.get(self.idx).map(|id| {
479 self.idx += 1;
480 NodeId::from(*id)
481 })
482 }
483}