1 #[cfg(not(feature = "std"))]
2 use alloc::{vec, vec::Vec};
3 #[cfg(not(feature = "std"))]
4 use libm::F32Ext;
5 
6 use core::any::Any;
7 use core::f32;
8 
9 use crate::forest::{Forest, NodeData};
10 use crate::id::NodeId;
11 use crate::result;
12 use crate::style::*;
13 
14 use crate::number::Number::*;
15 use crate::number::*;
16 
17 use crate::geometry::{Point, Rect, Size};
18 
19 #[derive(Debug, Clone)]
20 pub struct ComputeResult {
21     pub size: Size<f32>,
22 }
23 
24 struct FlexItem {
25     node: NodeId,
26 
27     size: Size<Number>,
28     min_size: Size<Number>,
29     max_size: Size<Number>,
30 
31     position: Rect<Number>,
32     margin: Rect<f32>,
33     padding: Rect<f32>,
34     border: Rect<f32>,
35 
36     flex_basis: f32,
37     inner_flex_basis: f32,
38     violation: f32,
39     frozen: bool,
40 
41     hypothetical_inner_size: Size<f32>,
42     hypothetical_outer_size: Size<f32>,
43     target_size: Size<f32>,
44     outer_target_size: Size<f32>,
45 
46     baseline: f32,
47 
48     // temporary values for holding offset in the main / cross direction.
49     // offset is the relative position from the item's natural flow position based on
50     // relative position values, alignment, and justification. Does not include margin/padding/border.
51     offset_main: f32,
52     offset_cross: f32,
53 }
54 
55 struct FlexLine {
56     items: Vec<FlexItem>,
57     cross_size: f32,
58     offset_cross: f32,
59 }
60 
61 impl Forest {
compute(&mut self, root: NodeId, size: Size<Number>) -> Result<(), Box<Any>>62     pub(crate) fn compute(&mut self, root: NodeId, size: Size<Number>) -> Result<(), Box<Any>> {
63         let style = self.nodes[root].style;
64         let has_root_min_max = style.min_size.width.is_defined()
65             || style.min_size.height.is_defined()
66             || style.max_size.width.is_defined()
67             || style.max_size.height.is_defined();
68 
69         let result = if has_root_min_max {
70             let first_pass = self.compute_internal(
71                 root,
72                 Size { width: style.size.width.resolve(size.width), height: style.size.height.resolve(size.height) },
73                 size,
74                 false,
75             )?;
76 
77             self.compute_internal(
78                 root,
79                 Size {
80                     width: first_pass
81                         .size
82                         .width
83                         .maybe_max(style.min_size.width.resolve(size.width))
84                         .maybe_min(style.max_size.width.resolve(size.width))
85                         .to_number(),
86                     height: first_pass
87                         .size
88                         .height
89                         .maybe_max(style.min_size.height.resolve(size.height))
90                         .maybe_min(style.max_size.height.resolve(size.height))
91                         .to_number(),
92                 },
93                 size,
94                 true,
95             )?
96         } else {
97             self.compute_internal(
98                 root,
99                 Size { width: style.size.width.resolve(size.width), height: style.size.height.resolve(size.height) },
100                 size,
101                 true,
102             )?
103         };
104 
105         self.nodes[root].layout = result::Layout {
106             order: 0,
107             size: Size { width: result.size.width, height: result.size.height },
108             location: Point { x: 0.0, y: 0.0 },
109         };
110 
111         Self::round_layout(&mut self.nodes, &self.children, root, 0.0, 0.0);
112         Ok(())
113     }
114 
round_layout(nodes: &mut Vec<NodeData>, children: &Vec<Vec<NodeId>>, root: NodeId, abs_x: f32, abs_y: f32)115     fn round_layout(nodes: &mut Vec<NodeData>, children: &Vec<Vec<NodeId>>, root: NodeId, abs_x: f32, abs_y: f32) {
116         let layout = &mut nodes[root].layout;
117         let abs_x = abs_x + layout.location.x;
118         let abs_y = abs_y + layout.location.y;
119 
120         layout.location.x = layout.location.x.round();
121         layout.location.y = layout.location.y.round();
122         layout.size.width = (abs_x + layout.size.width).round() - abs_x.round();
123         layout.size.height = (abs_y + layout.size.height).round() - abs_y.round();
124         for child in &children[root] {
125             Self::round_layout(nodes, children, *child, abs_x, abs_y);
126         }
127     }
128 
compute_internal( &mut self, node: NodeId, node_size: Size<Number>, parent_size: Size<Number>, perform_layout: bool, ) -> Result<ComputeResult, Box<Any>>129     fn compute_internal(
130         &mut self,
131         node: NodeId,
132         node_size: Size<Number>,
133         parent_size: Size<Number>,
134         perform_layout: bool,
135     ) -> Result<ComputeResult, Box<Any>> {
136         self.nodes[node].is_dirty = false;
137 
138         // First we check if we have a result for the given input
139         if let Some(ref cache) = self.nodes[node].layout_cache {
140             if cache.perform_layout || !perform_layout {
141                 let width_compatible = if let Number::Defined(width) = node_size.width {
142                     (width - cache.result.size.width).abs() < f32::EPSILON
143                 } else {
144                     cache.node_size.width.is_undefined()
145                 };
146 
147                 let height_compatible = if let Number::Defined(height) = node_size.height {
148                     (height - cache.result.size.height).abs() < f32::EPSILON
149                 } else {
150                     cache.node_size.height.is_undefined()
151                 };
152 
153                 if width_compatible && height_compatible {
154                     return Ok(cache.result.clone());
155                 }
156 
157                 if cache.node_size == node_size && cache.parent_size == parent_size {
158                     return Ok(cache.result.clone());
159                 }
160             }
161         }
162 
163         // Define some general constants we will need for the remainder
164         // of the algorithm.
165 
166         let dir = self.nodes[node].style.flex_direction;
167         let is_row = dir.is_row();
168         let is_column = dir.is_column();
169         let is_wrap_reverse = self.nodes[node].style.flex_wrap == FlexWrap::WrapReverse;
170 
171         let margin = self.nodes[node].style.margin.map(|n| n.resolve(parent_size.width).or_else(0.0));
172         let padding = self.nodes[node].style.padding.map(|n| n.resolve(parent_size.width).or_else(0.0));
173         let border = self.nodes[node].style.border.map(|n| n.resolve(parent_size.width).or_else(0.0));
174 
175         let padding_border = Rect {
176             start: padding.start + border.start,
177             end: padding.end + border.end,
178             top: padding.top + border.top,
179             bottom: padding.bottom + border.bottom,
180         };
181 
182         let node_inner_size = Size {
183             width: node_size.width - padding_border.horizontal(),
184             height: node_size.height - padding_border.vertical(),
185         };
186 
187         let mut container_size = Size { width: 0.0, height: 0.0 };
188         let mut inner_container_size = Size { width: 0.0, height: 0.0 };
189 
190         // If this is a leaf node we can skip a lot of this function in some cases
191         if self.children[node].is_empty() {
192             if node_size.width.is_defined() && node_size.height.is_defined() {
193                 return Ok(ComputeResult { size: node_size.map(|s| s.or_else(0.0)) });
194             }
195 
196             if let Some(ref measure) = self.nodes[node].measure {
197                 let result = ComputeResult { size: measure(node_size)? };
198                 self.nodes[node].layout_cache =
199                     Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
200                 return Ok(result);
201             }
202 
203             return Ok(ComputeResult {
204                 size: Size {
205                     width: node_size.width.or_else(0.0) + padding_border.horizontal(),
206                     height: node_size.height.or_else(0.0) + padding_border.vertical(),
207                 },
208             });
209         }
210 
211         // 9.2. Line Length Determination
212 
213         // 1. Generate anonymous flex items as described in §4 Flex Items.
214 
215         // 2. Determine the available main and cross space for the flex items.
216         //    For each dimension, if that dimension of the flex container’s content box
217         //    is a definite size, use that; if that dimension of the flex container is
218         //    being sized under a min or max-content constraint, the available space in
219         //    that dimension is that constraint; otherwise, subtract the flex container’s
220         //    margin, border, and padding from the space available to the flex container
221         //    in that dimension and use that value. This might result in an infinite value.
222 
223         let available_space = Size {
224             width: node_size.width.or_else(parent_size.width - margin.horizontal()) - padding_border.horizontal(),
225             height: node_size.height.or_else(parent_size.height - margin.vertical()) - padding_border.vertical(),
226         };
227 
228         let mut flex_items: Vec<FlexItem> = self.children[node]
229             .iter()
230             .map(|child| (child, &self.nodes[*child].style))
231             .filter(|(_, style)| style.position_type != PositionType::Absolute)
232             .filter(|(_, style)| style.display != Display::None)
233             .map(|(child, child_style)| FlexItem {
234                 node: *child,
235 
236                 size: Size {
237                     width: child_style.size.width.resolve(node_inner_size.width),
238                     height: child_style.size.height.resolve(node_inner_size.height),
239                 },
240 
241                 min_size: Size {
242                     width: child_style.min_size.width.resolve(node_inner_size.width),
243                     height: child_style.min_size.height.resolve(node_inner_size.height),
244                 },
245 
246                 max_size: Size {
247                     width: child_style.max_size.width.resolve(node_inner_size.width),
248                     height: child_style.max_size.height.resolve(node_inner_size.height),
249                 },
250 
251                 position: child_style.position.map(|p| p.resolve(node_inner_size.width)),
252                 margin: child_style.margin.map(|m| m.resolve(node_inner_size.width).or_else(0.0)),
253                 padding: child_style.padding.map(|p| p.resolve(node_inner_size.width).or_else(0.0)),
254                 border: child_style.border.map(|b| b.resolve(node_inner_size.width).or_else(0.0)),
255 
256                 flex_basis: 0.0,
257                 inner_flex_basis: 0.0,
258                 violation: 0.0,
259                 frozen: false,
260 
261                 hypothetical_inner_size: Size { width: 0.0, height: 0.0 },
262                 hypothetical_outer_size: Size { width: 0.0, height: 0.0 },
263                 target_size: Size { width: 0.0, height: 0.0 },
264                 outer_target_size: Size { width: 0.0, height: 0.0 },
265 
266                 baseline: 0.0,
267 
268                 offset_main: 0.0,
269                 offset_cross: 0.0,
270             })
271             .collect();
272 
273         let has_baseline_child = flex_items.iter().fold(false, |result, child| {
274             result || self.nodes[child.node].style.align_self(&self.nodes[node].style) == AlignSelf::Baseline
275         });
276 
277         // TODO - this does not follow spec. See commented out code below
278         // 3. Determine the flex base size and hypothetical main size of each item:
279         flex_items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
280             let child_style = self.nodes[child.node].style;
281 
282             // A. If the item has a definite used flex basis, that’s the flex base size.
283 
284             let flex_basis = child_style.flex_basis.resolve(node_inner_size.main(dir));
285             if flex_basis.is_defined() {
286                 child.flex_basis = flex_basis.or_else(0.0);
287                 return Ok(());
288             };
289 
290             // B. If the flex item has an intrinsic aspect ratio,
291             //    a used flex basis of content, and a definite cross size,
292             //    then the flex base size is calculated from its inner
293             //    cross size and the flex item’s intrinsic aspect ratio.
294 
295             if let Defined(ratio) = child_style.aspect_ratio {
296                 if let Defined(cross) = node_size.cross(dir) {
297                     if child_style.flex_basis == Dimension::Auto {
298                         child.flex_basis = cross * ratio;
299                         return Ok(());
300                     }
301                 }
302             }
303 
304             // C. If the used flex basis is content or depends on its available space,
305             //    and the flex container is being sized under a min-content or max-content
306             //    constraint (e.g. when performing automatic table layout [CSS21]),
307             //    size the item under that constraint. The flex base size is the item’s
308             //    resulting main size.
309 
310             // TODO - Probably need to cover this case in future
311 
312             // D. Otherwise, if the used flex basis is content or depends on its
313             //    available space, the available main size is infinite, and the flex item’s
314             //    inline axis is parallel to the main axis, lay the item out using the rules
315             //    for a box in an orthogonal flow [CSS3-WRITING-MODES]. The flex base size
316             //    is the item’s max-content main size.
317 
318             // TODO - Probably need to cover this case in future
319 
320             // E. Otherwise, size the item into the available space using its used flex basis
321             //    in place of its main size, treating a value of content as max-content.
322             //    If a cross size is needed to determine the main size (e.g. when the
323             //    flex item’s main size is in its block axis) and the flex item’s cross size
324             //    is auto and not definite, in this calculation use fit-content as the
325             //    flex item’s cross size. The flex base size is the item’s resulting main size.
326 
327             let width: Number = if !child.size.width.is_defined()
328                 && child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch
329                 && is_column
330             {
331                 available_space.width
332             } else {
333                 child.size.width
334             };
335 
336             let height: Number = if !child.size.height.is_defined()
337                 && child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch
338                 && is_row
339             {
340                 available_space.height
341             } else {
342                 child.size.height
343             };
344 
345             child.flex_basis = self
346                 .compute_internal(
347                     child.node,
348                     Size {
349                         width: width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
350                         height: height.maybe_max(child.min_size.height).maybe_min(child.max_size.height),
351                     },
352                     available_space,
353                     false,
354                 )?
355                 .size
356                 .main(dir)
357                 .maybe_max(child.min_size.main(dir))
358                 .maybe_min(child.max_size.main(dir));
359 
360             Ok(())
361         })?;
362 
363         // The hypothetical main size is the item’s flex base size clamped according to its
364         // used min and max main sizes (and flooring the content box size at zero).
365 
366         flex_items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
367             child.inner_flex_basis = child.flex_basis - child.padding.main(dir) - child.border.main(dir);
368 
369             // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
370             // The following logic was developed not from the spec but by trail and error looking into how
371             // webkit handled various scenarios. Can probably be solved better by passing in
372             // min-content max-content constraints from the top
373             let min_main = self
374                 .compute_internal(child.node, Size { width: Undefined, height: Undefined }, available_space, false)?
375                 .size
376                 .main(dir)
377                 .maybe_max(child.min_size.main(dir))
378                 .maybe_min(child.size.main(dir))
379                 .to_number();
380 
381             child
382                 .hypothetical_inner_size
383                 .set_main(dir, child.flex_basis.maybe_max(min_main).maybe_min(child.max_size.main(dir)));
384 
385             child
386                 .hypothetical_outer_size
387                 .set_main(dir, child.hypothetical_inner_size.main(dir) + child.margin.main(dir));
388 
389             Ok(())
390         })?;
391 
392         // 9.3. Main Size Determination
393 
394         // 5. Collect flex items into flex lines:
395         //    - If the flex container is single-line, collect all the flex items into
396         //      a single flex line.
397         //    - Otherwise, starting from the first uncollected item, collect consecutive
398         //      items one by one until the first time that the next collected item would
399         //      not fit into the flex container’s inner main size (or until a forced break
400         //      is encountered, see §10 Fragmenting Flex Layout). If the very first
401         //      uncollected item wouldn’t fit, collect just it into the line.
402         //
403         //      For this step, the size of a flex item is its outer hypothetical main size. (Note: This can be negative.)
404         //      Repeat until all flex items have been collected into flex lines
405         //
406         //      Note that the "collect as many" line will collect zero-sized flex items onto
407         //      the end of the previous line even if the last non-zero item exactly "filled up" the line.
408 
409         let mut flex_lines = {
410             let mut lines: Vec<FlexLine> = vec![];
411             let mut line_length = 0.0;
412 
413             if self.nodes[node].style.flex_wrap == FlexWrap::NoWrap {
414                 lines.push(FlexLine { items: flex_items, cross_size: 0.0, offset_cross: 0.0 });
415             } else {
416                 let mut line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
417 
418                 for child in flex_items {
419                     line_length += child.hypothetical_outer_size.main(dir);
420 
421                     if let Defined(main) = available_space.main(dir) {
422                         if line_length > main && !line.items.is_empty() {
423                             line_length = child.hypothetical_outer_size.main(dir);
424                             lines.push(line);
425                             line = FlexLine { items: vec![], cross_size: 0.0, offset_cross: 0.0 };
426                         }
427                     }
428 
429                     line.items.push(child);
430                 }
431 
432                 lines.push(line);
433             }
434 
435             lines
436         };
437 
438         // 6. Resolve the flexible lengths of all the flex items to find their used main size.
439         //    See §9.7 Resolving Flexible Lengths.
440         //
441         // 9.7. Resolving Flexible Lengths
442 
443         flex_lines.iter_mut().try_for_each(|line| -> Result<(), Box<Any>> {
444             // 1. Determine the used flex factor. Sum the outer hypothetical main sizes of all
445             //    items on the line. If the sum is less than the flex container’s inner main size,
446             //    use the flex grow factor for the rest of this algorithm; otherwise, use the
447             //    flex shrink factor.
448 
449             let used_flex_factor: f32 = line.items.iter().map(|child| child.hypothetical_outer_size.main(dir)).sum();
450             let growing = used_flex_factor < node_inner_size.main(dir).or_else(0.0);
451             let shrinking = !growing;
452 
453             // 2. Size inflexible items. Freeze, setting its target main size to its hypothetical main size
454             //    - Any item that has a flex factor of zero
455             //    - If using the flex grow factor: any item that has a flex base size
456             //      greater than its hypothetical main size
457             //    - If using the flex shrink factor: any item that has a flex base size
458             //      smaller than its hypothetical main size
459 
460             line.items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
461                 // TODO - This is not found by reading the spec. Maybe this can be done in some other place
462                 // instead. This was found by trail and error fixing tests to align with webkit output.
463                 if node_inner_size.main(dir).is_undefined() && is_row {
464                     child.target_size.set_main(
465                         dir,
466                         self.compute_internal(
467                             child.node,
468                             Size {
469                                 width: child.size.width.maybe_max(child.min_size.width).maybe_min(child.max_size.width),
470                                 height: child
471                                     .size
472                                     .height
473                                     .maybe_max(child.min_size.height)
474                                     .maybe_min(child.max_size.height),
475                             },
476                             available_space,
477                             false,
478                         )?
479                         .size
480                         .main(dir)
481                         .maybe_max(child.min_size.main(dir))
482                         .maybe_min(child.max_size.main(dir)),
483                     );
484                 } else {
485                     child.target_size.set_main(dir, child.hypothetical_inner_size.main(dir));
486                 }
487 
488                 // TODO this should really only be set inside the if-statement below but
489                 // that causes the target_main_size to never be set for some items
490 
491                 child.outer_target_size.set_main(dir, child.target_size.main(dir) + child.margin.main(dir));
492 
493                 let child_style = &self.nodes[child.node].style;
494                 if (child_style.flex_grow == 0.0 && child_style.flex_shrink == 0.0)
495                     || (growing && child.flex_basis > child.hypothetical_inner_size.main(dir))
496                     || (shrinking && child.flex_basis < child.hypothetical_inner_size.main(dir))
497                 {
498                     child.frozen = true;
499                 }
500 
501                 Ok(())
502             })?;
503 
504             // 3. Calculate initial free space. Sum the outer sizes of all items on the line,
505             //    and subtract this from the flex container’s inner main size. For frozen items,
506             //    use their outer target main size; for other items, use their outer flex base size.
507 
508             let used_space: f32 = line
509                 .items
510                 .iter()
511                 .map(|child| {
512                     child.margin.main(dir) + if child.frozen { child.target_size.main(dir) } else { child.flex_basis }
513                 })
514                 .sum();
515 
516             let initial_free_space = (node_inner_size.main(dir) - used_space).or_else(0.0);
517 
518             // 4. Loop
519 
520             loop {
521                 // a. Check for flexible items. If all the flex items on the line are frozen,
522                 //    free space has been distributed; exit this loop.
523 
524                 let mut frozen: Vec<&mut FlexItem> = vec![];
525                 let mut unfrozen: Vec<&mut FlexItem> = vec![];
526 
527                 line.items.iter_mut().for_each(|child| {
528                     if child.frozen {
529                         frozen.push(child);
530                     } else {
531                         unfrozen.push(child);
532                     }
533                 });
534 
535                 if unfrozen.is_empty() {
536                     break;
537                 }
538 
539                 // b. Calculate the remaining free space as for initial free space, above.
540                 //    If the sum of the unfrozen flex items’ flex factors is less than one,
541                 //    multiply the initial free space by this sum. If the magnitude of this
542                 //    value is less than the magnitude of the remaining free space, use this
543                 //    as the remaining free space.
544 
545                 let used_space: f32 = Iterator::chain(frozen.iter(), unfrozen.iter())
546                     .map(|child| {
547                         child.margin.main(dir)
548                             + if child.frozen { child.target_size.main(dir) } else { child.flex_basis }
549                     })
550                     .sum();
551 
552                 let (sum_flex_grow, sum_flex_shrink): (f32, f32) =
553                     unfrozen.iter().fold((0.0, 0.0), |(flex_grow, flex_shrink), item| {
554                         let style = &self.nodes[item.node].style;
555                         (flex_grow + style.flex_grow, flex_shrink + style.flex_shrink)
556                     });
557 
558                 let free_space = if growing && sum_flex_grow < 1.0 {
559                     (initial_free_space * sum_flex_grow).maybe_min(node_inner_size.main(dir) - used_space)
560                 } else if shrinking && sum_flex_shrink < 1.0 {
561                     (initial_free_space * sum_flex_shrink).maybe_max(node_inner_size.main(dir) - used_space)
562                 } else {
563                     (node_inner_size.main(dir) - used_space).or_else(0.0)
564                 };
565 
566                 // c. Distribute free space proportional to the flex factors.
567                 //    - If the remaining free space is zero
568                 //        Do Nothing
569                 //    - If using the flex grow factor
570                 //        Find the ratio of the item’s flex grow factor to the sum of the
571                 //        flex grow factors of all unfrozen items on the line. Set the item’s
572                 //        target main size to its flex base size plus a fraction of the remaining
573                 //        free space proportional to the ratio.
574                 //    - If using the flex shrink factor
575                 //        For every unfrozen item on the line, multiply its flex shrink factor by
576                 //        its inner flex base size, and note this as its scaled flex shrink factor.
577                 //        Find the ratio of the item’s scaled flex shrink factor to the sum of the
578                 //        scaled flex shrink factors of all unfrozen items on the line. Set the item’s
579                 //        target main size to its flex base size minus a fraction of the absolute value
580                 //        of the remaining free space proportional to the ratio. Note this may result
581                 //        in a negative inner main size; it will be corrected in the next step.
582                 //    - Otherwise
583                 //        Do Nothing
584 
585                 if free_space.is_normal() {
586                     if growing && sum_flex_grow > 0.0 {
587                         unfrozen.iter_mut().for_each(|child| {
588                             child.target_size.set_main(
589                                 dir,
590                                 child.flex_basis
591                                     + free_space * (self.nodes[child.node].style.flex_grow / sum_flex_grow),
592                             );
593                         });
594                     } else if shrinking && sum_flex_shrink > 0.0 {
595                         let sum_scaled_shrink_factor: f32 = unfrozen
596                             .iter()
597                             .map(|child| child.inner_flex_basis * self.nodes[child.node].style.flex_shrink)
598                             .sum();
599 
600                         if sum_scaled_shrink_factor > 0.0 {
601                             unfrozen.iter_mut().for_each(|child| {
602                                 let scaled_shrink_factor =
603                                     child.inner_flex_basis * self.nodes[child.node].style.flex_shrink;
604                                 child.target_size.set_main(
605                                     dir,
606                                     child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor),
607                                 )
608                             });
609                         }
610                     }
611                 }
612 
613                 // d. Fix min/max violations. Clamp each non-frozen item’s target main size by its
614                 //    used min and max main sizes and floor its content-box size at zero. If the
615                 //    item’s target main size was made smaller by this, it’s a max violation.
616                 //    If the item’s target main size was made larger by this, it’s a min violation.
617 
618                 let total_violation = unfrozen.iter_mut().try_fold(0.0, |acc, child| -> Result<f32, Box<Any>> {
619                     // TODO - not really spec abiding but needs to be done somewhere. probably somewhere else though.
620                     // The following logic was developed not from the spec but by trail and error looking into how
621                     // webkit handled various scenarios. Can probably be solved better by passing in
622                     // min-content max-content constraints from the top. Need to figure out correct thing to do here as
623                     // just piling on more conditionals.
624                     let min_main = if is_row && self.nodes[child.node].measure.is_none() {
625                         self.compute_internal(
626                             child.node,
627                             Size { width: Undefined, height: Undefined },
628                             available_space,
629                             false,
630                         )?
631                         .size
632                         .width
633                         .maybe_min(child.size.width)
634                         .maybe_max(child.min_size.width)
635                         .to_number()
636                     } else {
637                         child.min_size.main(dir)
638                     };
639 
640                     let max_main = child.max_size.main(dir);
641                     let clamped = child.target_size.main(dir).maybe_min(max_main).maybe_max(min_main).max(0.0);
642                     child.violation = clamped - child.target_size.main(dir);
643                     child.target_size.set_main(dir, clamped);
644                     child.outer_target_size.set_main(dir, child.target_size.main(dir) + child.margin.main(dir));
645 
646                     Ok(acc + child.violation)
647                 })?;
648 
649                 // e. Freeze over-flexed items. The total violation is the sum of the adjustments
650                 //    from the previous step ∑(clamped size - unclamped size). If the total violation is:
651                 //    - Zero
652                 //        Freeze all items.
653                 //    - Positive
654                 //        Freeze all the items with min violations.
655                 //    - Negative
656                 //        Freeze all the items with max violations.
657 
658                 unfrozen.iter_mut().for_each(|child| match total_violation {
659                     v if v > 0.0 => child.frozen = child.violation > 0.0,
660                     v if v < 0.0 => child.frozen = child.violation < 0.0,
661                     _ => child.frozen = true,
662                 })
663 
664                 // f. Return to the start of this loop.
665             }
666 
667             Ok(())
668         })?;
669 
670         // Not part of the spec from what i can see but seems correct
671         container_size.set_main(
672             dir,
673             node_size.main(dir).or_else({
674                 let longest_line = flex_lines.iter().fold(f32::MIN, |acc, line| {
675                     let length: f32 = line.items.iter().map(|item| item.outer_target_size.main(dir)).sum();
676                     acc.max(length)
677                 });
678 
679                 let size = longest_line + padding_border.main(dir);
680                 match available_space.main(dir) {
681                     Defined(val) if flex_lines.len() > 1 && size < val => val,
682                     _ => size,
683                 }
684             }),
685         );
686 
687         inner_container_size.set_main(dir, container_size.main(dir) - padding_border.main(dir));
688 
689         // 9.4. Cross Size Determination
690 
691         // 7. Determine the hypothetical cross size of each item by performing layout with the
692         //    used main size and the available space, treating auto as fit-content.
693 
694         flex_lines.iter_mut().try_for_each(|line| {
695             line.items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
696                 let child_cross =
697                     child.size.cross(dir).maybe_max(child.min_size.cross(dir)).maybe_min(child.max_size.cross(dir));
698 
699                 child.hypothetical_inner_size.set_cross(
700                     dir,
701                     self.compute_internal(
702                         child.node,
703                         Size {
704                             width: if is_row { child.target_size.width.to_number() } else { child_cross },
705                             height: if is_row { child_cross } else { child.target_size.height.to_number() },
706                         },
707                         Size {
708                             width: if is_row { container_size.main(dir).to_number() } else { available_space.width },
709                             height: if is_row { available_space.height } else { container_size.main(dir).to_number() },
710                         },
711                         false,
712                     )?
713                     .size
714                     .cross(dir)
715                     .maybe_max(child.min_size.cross(dir))
716                     .maybe_min(child.max_size.cross(dir)),
717                 );
718 
719                 child
720                     .hypothetical_outer_size
721                     .set_cross(dir, child.hypothetical_inner_size.cross(dir) + child.margin.cross(dir));
722 
723                 Ok(())
724             })
725         })?;
726 
727         // TODO - probably should move this somewhere else as it doesn't make a ton of sense here but we need it below
728         // TODO - This is expensive and should only be done if we really require a baseline. aka, make it lazy
729 
730         fn calc_baseline(db: &Forest, node: NodeId, layout: &result::Layout) -> f32 {
731             if db.children[node].is_empty() {
732                 layout.size.height
733             } else {
734                 let child = db.children[node][0];
735                 calc_baseline(db, child, &db.nodes[child].layout)
736             }
737         };
738 
739         if has_baseline_child {
740             flex_lines.iter_mut().try_for_each(|line| {
741                 line.items.iter_mut().try_for_each(|child| -> Result<(), Box<Any>> {
742                     let result = self.compute_internal(
743                         child.node,
744                         Size {
745                             width: if is_row {
746                                 child.target_size.width.to_number()
747                             } else {
748                                 child.hypothetical_inner_size.width.to_number()
749                             },
750                             height: if is_row {
751                                 child.hypothetical_inner_size.height.to_number()
752                             } else {
753                                 child.target_size.height.to_number()
754                             },
755                         },
756                         Size {
757                             width: if is_row { container_size.width.to_number() } else { node_size.width },
758                             height: if is_row { node_size.height } else { container_size.height.to_number() },
759                         },
760                         true,
761                     )?;
762 
763                     child.baseline = calc_baseline(
764                         self,
765                         child.node,
766                         &result::Layout {
767                             order: self.children[node].iter().position(|n| *n == child.node).unwrap() as u32,
768                             size: result.size,
769                             location: Point { x: 0.0, y: 0.0 },
770                         },
771                     );
772 
773                     Ok(())
774                 })
775             })?;
776         }
777 
778         // 8. Calculate the cross size of each flex line.
779         //    If the flex container is single-line and has a definite cross size, the cross size
780         //    of the flex line is the flex container’s inner cross size. Otherwise, for each flex line:
781         //
782         //    If the flex container is single-line, then clamp the line’s cross-size to be within
783         //    the container’s computed min and max cross sizes. Note that if CSS 2.1’s definition
784         //    of min/max-width/height applied more generally, this behavior would fall out automatically.
785 
786         if flex_lines.len() == 1 && node_size.cross(dir).is_defined() {
787             flex_lines[0].cross_size = (node_size.cross(dir) - padding_border.cross(dir)).or_else(0.0);
788         } else {
789             flex_lines.iter_mut().for_each(|line| {
790                 //    1. Collect all the flex items whose inline-axis is parallel to the main-axis, whose
791                 //       align-self is baseline, and whose cross-axis margins are both non-auto. Find the
792                 //       largest of the distances between each item’s baseline and its hypothetical outer
793                 //       cross-start edge, and the largest of the distances between each item’s baseline
794                 //       and its hypothetical outer cross-end edge, and sum these two values.
795 
796                 //    2. Among all the items not collected by the previous step, find the largest
797                 //       outer hypothetical cross size.
798 
799                 //    3. The used cross-size of the flex line is the largest of the numbers found in the
800                 //       previous two steps and zero.
801 
802                 let max_baseline: f32 = line.items.iter().map(|child| child.baseline).fold(0.0, |acc, x| acc.max(x));
803                 line.cross_size = line
804                     .items
805                     .iter()
806                     .map(|child| {
807                         let child_style = &self.nodes[child.node].style;
808                         if child_style.align_self(&self.nodes[node].style) == AlignSelf::Baseline
809                             && child_style.cross_margin_start(dir) != Dimension::Auto
810                             && child_style.cross_margin_end(dir) != Dimension::Auto
811                             && child_style.cross_size(dir) == Dimension::Auto
812                         {
813                             max_baseline - child.baseline + child.hypothetical_outer_size.cross(dir)
814                         } else {
815                             child.hypothetical_outer_size.cross(dir)
816                         }
817                     })
818                     .fold(0.0, |acc, x| acc.max(x));
819             });
820         }
821 
822         // 9. Handle 'align-content: stretch'. If the flex container has a definite cross size,
823         //    align-content is stretch, and the sum of the flex lines' cross sizes is less than
824         //    the flex container’s inner cross size, increase the cross size of each flex line
825         //    by equal amounts such that the sum of their cross sizes exactly equals the
826         //    flex container’s inner cross size.
827 
828         if self.nodes[node].style.align_content == AlignContent::Stretch && node_size.cross(dir).is_defined() {
829             let total_cross: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
830             let inner_cross = (node_size.cross(dir) - padding_border.cross(dir)).or_else(0.0);
831 
832             if total_cross < inner_cross {
833                 let remaining = inner_cross - total_cross;
834                 let addition = remaining / flex_lines.len() as f32;
835                 flex_lines.iter_mut().for_each(|line| line.cross_size += addition);
836             }
837         }
838 
839         // 10. Collapse visibility:collapse items. If any flex items have visibility: collapse,
840         //     note the cross size of the line they’re in as the item’s strut size, and restart
841         //     layout from the beginning.
842         //
843         //     In this second layout round, when collecting items into lines, treat the collapsed
844         //     items as having zero main size. For the rest of the algorithm following that step,
845         //     ignore the collapsed items entirely (as if they were display:none) except that after
846         //     calculating the cross size of the lines, if any line’s cross size is less than the
847         //     largest strut size among all the collapsed items in the line, set its cross size to
848         //     that strut size.
849         //
850         //     Skip this step in the second layout round.
851 
852         // TODO implement once (if ever) we support visibility:collapse
853 
854         // 11. Determine the used cross size of each flex item. If a flex item has align-self: stretch,
855         //     its computed cross size property is auto, and neither of its cross-axis margins are auto,
856         //     the used outer cross size is the used cross size of its flex line, clamped according to
857         //     the item’s used min and max cross sizes. Otherwise, the used cross size is the item’s
858         //     hypothetical cross size.
859         //
860         //     If the flex item has align-self: stretch, redo layout for its contents, treating this
861         //     used size as its definite cross size so that percentage-sized children can be resolved.
862         //
863         //     Note that this step does not affect the main size of the flex item, even if it has an
864         //     intrinsic aspect ratio.
865 
866         flex_lines.iter_mut().for_each(|line| {
867             let line_cross_size = line.cross_size;
868 
869             line.items.iter_mut().for_each(|child| {
870                 let child_style = &self.nodes[child.node].style;
871                 child.target_size.set_cross(
872                     dir,
873                     if child_style.align_self(&self.nodes[node].style) == AlignSelf::Stretch
874                         && child_style.cross_margin_start(dir) != Dimension::Auto
875                         && child_style.cross_margin_end(dir) != Dimension::Auto
876                         && child_style.cross_size(dir) == Dimension::Auto
877                     {
878                         (line_cross_size - child.margin.cross(dir))
879                             .maybe_max(child.min_size.cross(dir))
880                             .maybe_min(child.max_size.cross(dir))
881                     } else {
882                         child.hypothetical_inner_size.cross(dir)
883                     },
884                 );
885 
886                 child.outer_target_size.set_cross(dir, child.target_size.cross(dir) + child.margin.cross(dir));
887             });
888         });
889 
890         // 9.5. Main-Axis Alignment
891 
892         // 12. Distribute any remaining free space. For each flex line:
893         //     1. If the remaining free space is positive and at least one main-axis margin on this
894         //        line is auto, distribute the free space equally among these margins. Otherwise,
895         //        set all auto margins to zero.
896         //     2. Align the items along the main-axis per justify-content.
897 
898         flex_lines.iter_mut().for_each(|line| {
899             let used_space: f32 = line.items.iter().map(|child| child.outer_target_size.main(dir)).sum();
900             let free_space = inner_container_size.main(dir) - used_space;
901             let mut num_auto_margins = 0;
902 
903             line.items.iter_mut().for_each(|child| {
904                 let child_style = &self.nodes[child.node].style;
905                 if child_style.main_margin_start(dir) == Dimension::Auto {
906                     num_auto_margins += 1;
907                 }
908                 if child_style.main_margin_end(dir) == Dimension::Auto {
909                     num_auto_margins += 1;
910                 }
911             });
912 
913             if free_space > 0.0 && num_auto_margins > 0 {
914                 let margin = free_space / num_auto_margins as f32;
915 
916                 line.items.iter_mut().for_each(|child| {
917                     let child_style = &self.nodes[child.node].style;
918                     if child_style.main_margin_start(dir) == Dimension::Auto {
919                         if is_row {
920                             child.margin.start = margin;
921                         } else {
922                             child.margin.top = margin;
923                         }
924                     }
925                     if child_style.main_margin_end(dir) == Dimension::Auto {
926                         if is_row {
927                             child.margin.end = margin;
928                         } else {
929                             child.margin.bottom = margin;
930                         }
931                     }
932                 });
933             } else {
934                 let num_items = line.items.len();
935                 let layout_reverse = dir.is_reverse();
936 
937                 let justify_item = |(i, child): (usize, &mut FlexItem)| {
938                     let is_first = i == 0;
939 
940                     child.offset_main = match self.nodes[node].style.justify_content {
941                         JustifyContent::FlexStart => {
942                             if layout_reverse && is_first {
943                                 free_space
944                             } else {
945                                 0.0
946                             }
947                         }
948                         JustifyContent::Center => {
949                             if is_first {
950                                 free_space / 2.0
951                             } else {
952                                 0.0
953                             }
954                         }
955                         JustifyContent::FlexEnd => {
956                             if is_first && !layout_reverse {
957                                 free_space
958                             } else {
959                                 0.0
960                             }
961                         }
962                         JustifyContent::SpaceBetween => {
963                             if is_first {
964                                 0.0
965                             } else {
966                                 free_space / (num_items - 1) as f32
967                             }
968                         }
969                         JustifyContent::SpaceAround => {
970                             if is_first {
971                                 (free_space / num_items as f32) / 2.0
972                             } else {
973                                 free_space / num_items as f32
974                             }
975                         }
976                         JustifyContent::SpaceEvenly => free_space / (num_items + 1) as f32,
977                     };
978                 };
979 
980                 if layout_reverse {
981                     line.items.iter_mut().rev().enumerate().for_each(justify_item);
982                 } else {
983                     line.items.iter_mut().enumerate().for_each(justify_item);
984                 }
985             }
986         });
987 
988         // 9.6. Cross-Axis Alignment
989 
990         // 13. Resolve cross-axis auto margins. If a flex item has auto cross-axis margins:
991         //     - If its outer cross size (treating those auto margins as zero) is less than the
992         //       cross size of its flex line, distribute the difference in those sizes equally
993         //       to the auto margins.
994         //     - Otherwise, if the block-start or inline-start margin (whichever is in the cross axis)
995         //       is auto, set it to zero. Set the opposite margin so that the outer cross size of the
996         //       item equals the cross size of its flex line.
997 
998         flex_lines.iter_mut().for_each(|line| {
999             let line_cross_size = line.cross_size;
1000             let max_baseline: f32 = line.items.iter_mut().map(|child| child.baseline).fold(0.0, |acc, x| acc.max(x));
1001 
1002             line.items.iter_mut().for_each(|child| {
1003                 let free_space = line_cross_size - child.outer_target_size.cross(dir);
1004                 let child_style = &self.nodes[child.node].style;
1005 
1006                 if child_style.cross_margin_start(dir) == Dimension::Auto
1007                     && child_style.cross_margin_end(dir) == Dimension::Auto
1008                 {
1009                     if is_row {
1010                         child.margin.top = free_space / 2.0;
1011                         child.margin.bottom = free_space / 2.0;
1012                     } else {
1013                         child.margin.start = free_space / 2.0;
1014                         child.margin.end = free_space / 2.0;
1015                     }
1016                 } else if child_style.cross_margin_start(dir) == Dimension::Auto {
1017                     if is_row {
1018                         child.margin.top = free_space;
1019                     } else {
1020                         child.margin.start = free_space;
1021                     }
1022                 } else if child_style.cross_margin_end(dir) == Dimension::Auto {
1023                     if is_row {
1024                         child.margin.bottom = free_space;
1025                     } else {
1026                         child.margin.end = free_space;
1027                     }
1028                 } else {
1029                     // 14. Align all flex items along the cross-axis per align-self, if neither of the item’s
1030                     //     cross-axis margins are auto.
1031 
1032                     child.offset_cross = match child_style.align_self(&self.nodes[node].style) {
1033                         AlignSelf::Auto => 0.0, // Should never happen
1034                         AlignSelf::FlexStart => {
1035                             if is_wrap_reverse {
1036                                 free_space
1037                             } else {
1038                                 0.0
1039                             }
1040                         }
1041                         AlignSelf::FlexEnd => {
1042                             if is_wrap_reverse {
1043                                 0.0
1044                             } else {
1045                                 free_space
1046                             }
1047                         }
1048                         AlignSelf::Center => free_space / 2.0,
1049                         AlignSelf::Baseline => {
1050                             if is_row {
1051                                 max_baseline - child.baseline
1052                             } else {
1053                                 // baseline alignment only makes sense if the direction is row
1054                                 // we treat it as flex-start alignment in columns.
1055                                 if is_wrap_reverse {
1056                                     free_space
1057                                 } else {
1058                                     0.0
1059                                 }
1060                             }
1061                         }
1062                         AlignSelf::Stretch => {
1063                             if is_wrap_reverse {
1064                                 free_space
1065                             } else {
1066                                 0.0
1067                             }
1068                         }
1069                     };
1070                 }
1071             });
1072         });
1073 
1074         // 15. Determine the flex container’s used cross size:
1075         //     - If the cross size property is a definite size, use that, clamped by the used
1076         //       min and max cross sizes of the flex container.
1077         //     - Otherwise, use the sum of the flex lines' cross sizes, clamped by the used
1078         //       min and max cross sizes of the flex container.
1079 
1080         let total_cross_size: f32 = flex_lines.iter().map(|line| line.cross_size).sum();
1081         container_size.set_cross(dir, node_size.cross(dir).or_else(total_cross_size + padding_border.cross(dir)));
1082         inner_container_size.set_cross(dir, container_size.cross(dir) - padding_border.cross(dir));
1083 
1084         // We have the container size. If our caller does not care about performing
1085         // layout we are done now.
1086         if !perform_layout {
1087             let result = ComputeResult { size: container_size };
1088             self.nodes[node].layout_cache =
1089                 Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
1090             return Ok(result);
1091         }
1092 
1093         // 16. Align all flex lines per align-content.
1094 
1095         let free_space = inner_container_size.cross(dir) - total_cross_size;
1096         let num_lines = flex_lines.len();
1097 
1098         let align_line = |(i, line): (usize, &mut FlexLine)| {
1099             let is_first = i == 0;
1100 
1101             line.offset_cross = match self.nodes[node].style.align_content {
1102                 AlignContent::FlexStart => {
1103                     if is_first && is_wrap_reverse {
1104                         free_space
1105                     } else {
1106                         0.0
1107                     }
1108                 }
1109                 AlignContent::FlexEnd => {
1110                     if is_first && !is_wrap_reverse {
1111                         free_space
1112                     } else {
1113                         0.0
1114                     }
1115                 }
1116                 AlignContent::Center => {
1117                     if is_first {
1118                         free_space / 2.0
1119                     } else {
1120                         0.0
1121                     }
1122                 }
1123                 AlignContent::Stretch => 0.0,
1124                 AlignContent::SpaceBetween => {
1125                     if is_first {
1126                         0.0
1127                     } else {
1128                         free_space / (num_lines - 1) as f32
1129                     }
1130                 }
1131                 AlignContent::SpaceAround => {
1132                     if is_first {
1133                         (free_space / num_lines as f32) / 2.0
1134                     } else {
1135                         free_space / num_lines as f32
1136                     }
1137                 }
1138             };
1139         };
1140 
1141         if is_wrap_reverse {
1142             flex_lines.iter_mut().rev().enumerate().for_each(align_line);
1143         } else {
1144             flex_lines.iter_mut().enumerate().for_each(align_line);
1145         }
1146 
1147         // Do a final layout pass and gather the resulting layouts
1148         {
1149             let mut lines: Vec<Vec<result::Layout>> = vec![];
1150             let mut total_offset_cross = padding_border.cross_start(dir);
1151 
1152             let layout_line = |line: &mut FlexLine| -> Result<(), Box<Any>> {
1153                 let mut children: Vec<result::Layout> = vec![];
1154                 let mut total_offset_main = padding_border.main_start(dir);
1155                 let line_offset_cross = line.offset_cross;
1156 
1157                 let layout_item = |child: &mut FlexItem| -> Result<(), Box<Any>> {
1158                     let result = self.compute_internal(
1159                         child.node,
1160                         child.target_size.map(|s| s.to_number()),
1161                         container_size.map(|s| s.to_number()),
1162                         true,
1163                     )?;
1164 
1165                     let offset_main = total_offset_main
1166                         + child.offset_main
1167                         + child.margin.main_start(dir)
1168                         + (child.position.main_start(dir).or_else(0.0) - child.position.main_end(dir).or_else(0.0));
1169 
1170                     let offset_cross = total_offset_cross
1171                         + child.offset_cross
1172                         + line_offset_cross
1173                         + child.margin.cross_start(dir)
1174                         + (child.position.cross_start(dir).or_else(0.0) - child.position.cross_end(dir).or_else(0.0));
1175 
1176                     self.nodes[child.node].layout = result::Layout {
1177                         order: self.children[node].iter().position(|n| *n == child.node).unwrap() as u32,
1178                         size: result.size,
1179                         location: Point {
1180                             x: if is_row { offset_main } else { offset_cross },
1181                             y: if is_column { offset_main } else { offset_cross },
1182                         },
1183                     };
1184 
1185                     total_offset_main += child.offset_main + child.margin.main(dir) + result.size.main(dir);
1186 
1187                     Ok(())
1188                 };
1189 
1190                 if dir.is_reverse() {
1191                     line.items.iter_mut().rev().try_for_each(layout_item)?;
1192                 } else {
1193                     line.items.iter_mut().try_for_each(layout_item)?;
1194                 }
1195 
1196                 total_offset_cross += line_offset_cross + line.cross_size;
1197 
1198                 if dir.is_reverse() {
1199                     children.reverse();
1200                 }
1201 
1202                 lines.push(children);
1203 
1204                 Ok(())
1205             };
1206 
1207             if is_wrap_reverse {
1208                 flex_lines.iter_mut().rev().try_for_each(layout_line)?;
1209             } else {
1210                 flex_lines.iter_mut().try_for_each(layout_line)?;
1211             }
1212         }
1213 
1214         // Before returning we perform absolute layout on all absolutely positioned children
1215         {
1216             // TODO: remove number of Vec<_> generated
1217             let candidates = self.children[node]
1218                 .iter()
1219                 .cloned()
1220                 .enumerate()
1221                 .filter(|(_, child)| self.nodes[*child].style.position_type == PositionType::Absolute)
1222                 .collect::<Vec<_>>();
1223 
1224             for (order, child) in candidates {
1225                 let container_width = container_size.width.to_number();
1226                 let container_height = container_size.height.to_number();
1227 
1228                 let child_style = self.nodes[child].style;
1229 
1230                 let start = child_style.position.start.resolve(container_width)
1231                     + child_style.margin.start.resolve(container_width);
1232                 let end =
1233                     child_style.position.end.resolve(container_width) + child_style.margin.end.resolve(container_width);
1234                 let top = child_style.position.top.resolve(container_height)
1235                     + child_style.margin.top.resolve(container_height);
1236                 let bottom = child_style.position.bottom.resolve(container_height)
1237                     + child_style.margin.bottom.resolve(container_height);
1238 
1239                 let (start_main, end_main) = if is_row { (start, end) } else { (top, bottom) };
1240                 let (start_cross, end_cross) = if is_row { (top, bottom) } else { (start, end) };
1241 
1242                 let width = child_style
1243                     .size
1244                     .width
1245                     .resolve(container_width)
1246                     .maybe_max(child_style.min_size.width.resolve(container_width))
1247                     .maybe_min(child_style.max_size.width.resolve(container_width))
1248                     .or_else(if start.is_defined() && end.is_defined() {
1249                         container_width - start - end
1250                     } else {
1251                         Undefined
1252                     });
1253 
1254                 let height = child_style
1255                     .size
1256                     .height
1257                     .resolve(container_height)
1258                     .maybe_max(child_style.min_size.height.resolve(container_height))
1259                     .maybe_min(child_style.max_size.height.resolve(container_height))
1260                     .or_else(if top.is_defined() && bottom.is_defined() {
1261                         container_height - top - bottom
1262                     } else {
1263                         Undefined
1264                     });
1265 
1266                 let result = self.compute_internal(
1267                     child,
1268                     Size { width, height },
1269                     Size { width: container_width, height: container_height },
1270                     true,
1271                 )?;
1272 
1273                 let free_main_space = container_size.main(dir)
1274                     - result
1275                         .size
1276                         .main(dir)
1277                         .maybe_max(child_style.min_main_size(dir).resolve(node_inner_size.main(dir)))
1278                         .maybe_min(child_style.max_main_size(dir).resolve(node_inner_size.main(dir)));
1279 
1280                 let free_cross_space = container_size.cross(dir)
1281                     - result
1282                         .size
1283                         .cross(dir)
1284                         .maybe_max(child_style.min_cross_size(dir).resolve(node_inner_size.cross(dir)))
1285                         .maybe_min(child_style.max_cross_size(dir).resolve(node_inner_size.cross(dir)));
1286 
1287                 let offset_main = if start_main.is_defined() {
1288                     start_main.or_else(0.0) + border.main_start(dir)
1289                 } else if end_main.is_defined() {
1290                     free_main_space - end_main.or_else(0.0) - border.main_end(dir)
1291                 } else {
1292                     match self.nodes[node].style.justify_content {
1293                         JustifyContent::SpaceBetween | JustifyContent::FlexStart => padding_border.main_start(dir),
1294                         JustifyContent::FlexEnd => free_main_space - padding_border.main_end(dir),
1295                         JustifyContent::SpaceEvenly | JustifyContent::SpaceAround | JustifyContent::Center => {
1296                             free_main_space / 2.0
1297                         }
1298                     }
1299                 };
1300 
1301                 let offset_cross = if start_cross.is_defined() {
1302                     start_cross.or_else(0.0) + border.cross_start(dir)
1303                 } else if end_cross.is_defined() {
1304                     free_cross_space - end_cross.or_else(0.0) - border.cross_end(dir)
1305                 } else {
1306                     match child_style.align_self(&self.nodes[node].style) {
1307                         AlignSelf::Auto => 0.0, // Should never happen
1308                         AlignSelf::FlexStart => {
1309                             if is_wrap_reverse {
1310                                 free_cross_space - padding_border.cross_end(dir)
1311                             } else {
1312                                 padding_border.cross_start(dir)
1313                             }
1314                         }
1315                         AlignSelf::FlexEnd => {
1316                             if is_wrap_reverse {
1317                                 padding_border.cross_start(dir)
1318                             } else {
1319                                 free_cross_space - padding_border.cross_end(dir)
1320                             }
1321                         }
1322                         AlignSelf::Center => free_cross_space / 2.0,
1323                         AlignSelf::Baseline => free_cross_space / 2.0, // Treat as center for now until we have baseline support
1324                         AlignSelf::Stretch => {
1325                             if is_wrap_reverse {
1326                                 free_cross_space - padding_border.cross_end(dir)
1327                             } else {
1328                                 padding_border.cross_start(dir)
1329                             }
1330                         }
1331                     }
1332                 };
1333 
1334                 self.nodes[child].layout = result::Layout {
1335                     order: order as u32,
1336                     size: result.size,
1337                     location: Point {
1338                         x: if is_row { offset_main } else { offset_cross },
1339                         y: if is_column { offset_main } else { offset_cross },
1340                     },
1341                 };
1342             }
1343         }
1344 
1345         fn hidden_layout(nodes: &mut Vec<NodeData>, children: &Vec<Vec<NodeId>>, node: NodeId, order: u32) {
1346             nodes[node].layout =
1347                 result::Layout { order, size: Size { width: 0.0, height: 0.0 }, location: Point { x: 0.0, y: 0.0 } };
1348 
1349             for (order, child) in children[node].iter().enumerate() {
1350                 hidden_layout(nodes, children, *child, order as _);
1351             }
1352         }
1353 
1354         for (order, child) in self.children[node].iter().enumerate() {
1355             if self.nodes[*child].style.display == Display::None {
1356                 hidden_layout(&mut self.nodes, &self.children, *child, order as _);
1357             }
1358         }
1359 
1360         let result = ComputeResult { size: container_size };
1361         self.nodes[node].layout_cache =
1362             Some(result::Cache { node_size, parent_size, perform_layout, result: result.clone() });
1363         Ok(result)
1364     }
1365 }
1366