Merge "Updated NanoSVG to latest version (9 July 2017)" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / third-party / facebook-flexbox / layout.c
1 /**
2  * Copyright (c) 2014, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree. An additional grant
7  * of patent rights can be found in the PATENTS file in the same directory.
8  */
9
10 #include <math.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 // in concatenated header, don't include Layout.h it's already at the top
16 #ifndef CSS_LAYOUT_IMPLEMENTATION
17 #include "layout.h"
18 #endif
19
20 #ifdef _MSC_VER
21 #include <float.h>
22 #define isnan _isnan
23
24 /* define fmaxf if < VC12 */
25 #if _MSC_VER < 1800
26 __forceinline const float fmaxf(const float a, const float b) {
27   return (a > b) ? a : b;
28 }
29 #endif
30 #endif
31
32 bool isUndefined(float value) {
33   return isnan(value);
34 }
35
36 static bool eq(float a, float b) {
37   if (isUndefined(a)) {
38     return isUndefined(b);
39   }
40   return fabs(a - b) < 0.0001;
41 }
42
43 void init_css_node(css_node_t *node) {
44   node->style.align_items = CSS_ALIGN_STRETCH;
45   node->style.align_content = CSS_ALIGN_FLEX_START;
46
47   node->style.direction = CSS_DIRECTION_INHERIT;
48   node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN;
49
50   // Some of the fields default to undefined and not 0
51   node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
52   node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
53
54   node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED;
55   node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
56
57   node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED;
58   node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
59
60   node->style.position[CSS_LEFT] = CSS_UNDEFINED;
61   node->style.position[CSS_TOP] = CSS_UNDEFINED;
62   node->style.position[CSS_RIGHT] = CSS_UNDEFINED;
63   node->style.position[CSS_BOTTOM] = CSS_UNDEFINED;
64
65   node->style.margin[CSS_START] = CSS_UNDEFINED;
66   node->style.margin[CSS_END] = CSS_UNDEFINED;
67   node->style.padding[CSS_START] = CSS_UNDEFINED;
68   node->style.padding[CSS_END] = CSS_UNDEFINED;
69   node->style.border[CSS_START] = CSS_UNDEFINED;
70   node->style.border[CSS_END] = CSS_UNDEFINED;
71
72   node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
73   node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
74
75   // Such that the comparison is always going to be false
76   node->layout.last_requested_dimensions[CSS_WIDTH] = -1;
77   node->layout.last_requested_dimensions[CSS_HEIGHT] = -1;
78   node->layout.last_parent_max_width = -1;
79   node->layout.last_parent_max_height = -1;
80   node->layout.last_direction = (css_direction_t)-1;
81   node->layout.should_update = true;
82 }
83
84 css_node_t *new_css_node() {
85   css_node_t *node = (css_node_t *)calloc(1, sizeof(*node));
86   init_css_node(node);
87   return node;
88 }
89
90 void free_css_node(css_node_t *node) {
91   free(node);
92 }
93
94 static void indent(int n) {
95   int i;
96   for (i = 0; i < n; ++i) {
97     printf("  ");
98   }
99 }
100
101 static void print_number_0(const char *str, float number) {
102   if (!eq(number, 0)) {
103     printf("%s: %g, ", str, number);
104   }
105 }
106
107 static void print_number_nan(const char *str, float number) {
108   if (!isnan(number)) {
109     printf("%s: %g, ", str, number);
110   }
111 }
112
113 static bool four_equal(float four[4]) {
114   return
115     eq(four[0], four[1]) &&
116     eq(four[0], four[2]) &&
117     eq(four[0], four[3]);
118 }
119
120
121 static void print_css_node_rec(
122   css_node_t *node,
123   css_print_options_t options,
124   int level
125 ) {
126   indent(level);
127   printf("{");
128
129   if (node->print) {
130     node->print(node->context);
131   }
132
133   if (options & CSS_PRINT_LAYOUT) {
134     printf("layout: {");
135     printf("width: %g, ", node->layout.dimensions[CSS_WIDTH]);
136     printf("height: %g, ", node->layout.dimensions[CSS_HEIGHT]);
137     printf("top: %g, ", node->layout.position[CSS_TOP]);
138     printf("left: %g", node->layout.position[CSS_LEFT]);
139     printf("}, ");
140   }
141
142   if (options & CSS_PRINT_STYLE) {
143     if (node->style.direction == CSS_DIRECTION_INHERIT) {
144       printf("direction: 'inherit', ");
145     } else if (node->style.direction == CSS_DIRECTION_LTR) {
146       printf("direction: 'LTR', ");
147     } else if (node->style.direction == CSS_DIRECTION_RTL) {
148       printf("direction: 'RTL', ");
149     }
150
151     if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) {
152       printf("flexDirection: 'column', ");
153     } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
154       printf("flexDirection: 'columnReverse', ");
155     } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) {
156       printf("flexDirection: 'row', ");
157     } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) {
158       printf("flexDirection: 'rowReverse', ");
159     }
160
161     if (node->style.justify_content == CSS_JUSTIFY_FLEX_START) {
162       printf("justifyContent: 'flex-start', ");
163     } else if (node->style.justify_content == CSS_JUSTIFY_CENTER) {
164       printf("justifyContent: 'center', ");
165     } else if (node->style.justify_content == CSS_JUSTIFY_FLEX_END) {
166       printf("justifyContent: 'flex-end', ");
167     } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_AROUND) {
168       printf("justifyContent: 'space-around', ");
169     } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_BETWEEN) {
170       printf("justifyContent: 'space-between', ");
171     }
172
173     if (node->style.align_items == CSS_ALIGN_FLEX_START) {
174       printf("alignItems: 'flex-start', ");
175     } else if (node->style.align_items == CSS_ALIGN_CENTER) {
176       printf("alignItems: 'center', ");
177     } else if (node->style.align_items == CSS_ALIGN_FLEX_END) {
178       printf("alignItems: 'flex-end', ");
179     } else if (node->style.align_items == CSS_ALIGN_STRETCH) {
180       printf("alignItems: 'stretch', ");
181     }
182
183     if (node->style.align_content == CSS_ALIGN_FLEX_START) {
184       printf("alignContent: 'flex-start', ");
185     } else if (node->style.align_content == CSS_ALIGN_CENTER) {
186       printf("alignContent: 'center', ");
187     } else if (node->style.align_content == CSS_ALIGN_FLEX_END) {
188       printf("alignContent: 'flex-end', ");
189     } else if (node->style.align_content == CSS_ALIGN_STRETCH) {
190       printf("alignContent: 'stretch', ");
191     }
192
193     if (node->style.align_self == CSS_ALIGN_FLEX_START) {
194       printf("alignSelf: 'flex-start', ");
195     } else if (node->style.align_self == CSS_ALIGN_CENTER) {
196       printf("alignSelf: 'center', ");
197     } else if (node->style.align_self == CSS_ALIGN_FLEX_END) {
198       printf("alignSelf: 'flex-end', ");
199     } else if (node->style.align_self == CSS_ALIGN_STRETCH) {
200       printf("alignSelf: 'stretch', ");
201     } else if (node->style.align_self == CSS_ALIGN_AUTO) {
202       printf("alignSelf: 'auto', ");
203     }
204
205     if (node->style.flex_wrap == CSS_NOWRAP) {
206       printf("flexWrap: 'no-wrap', ");
207     } else if (node->style.flex_wrap == CSS_WRAP) {
208       printf("flexWrap: 'wrap', ");
209     }
210
211     print_number_nan("flex", node->style.flex);
212
213     if (four_equal(node->style.margin)) {
214       print_number_0("margin", node->style.margin[CSS_LEFT]);
215     } else {
216       print_number_0("marginLeft", node->style.margin[CSS_LEFT]);
217       print_number_0("marginRight", node->style.margin[CSS_RIGHT]);
218       print_number_0("marginTop", node->style.margin[CSS_TOP]);
219       print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]);
220       print_number_0("marginStart", node->style.margin[CSS_START]);
221       print_number_0("marginEnd", node->style.margin[CSS_END]);
222     }
223
224     if (four_equal(node->style.padding)) {
225       print_number_0("padding", node->style.margin[CSS_LEFT]);
226     } else {
227       print_number_0("paddingLeft", node->style.padding[CSS_LEFT]);
228       print_number_0("paddingRight", node->style.padding[CSS_RIGHT]);
229       print_number_0("paddingTop", node->style.padding[CSS_TOP]);
230       print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]);
231       print_number_0("paddingStart", node->style.padding[CSS_START]);
232       print_number_0("paddingEnd", node->style.padding[CSS_END]);
233     }
234
235     if (four_equal(node->style.border)) {
236       print_number_0("borderWidth", node->style.border[CSS_LEFT]);
237     } else {
238       print_number_0("borderLeftWidth", node->style.border[CSS_LEFT]);
239       print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]);
240       print_number_0("borderTopWidth", node->style.border[CSS_TOP]);
241       print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]);
242       print_number_0("borderStartWidth", node->style.border[CSS_START]);
243       print_number_0("borderEndWidth", node->style.border[CSS_END]);
244     }
245
246     print_number_nan("width", node->style.dimensions[CSS_WIDTH]);
247     print_number_nan("height", node->style.dimensions[CSS_HEIGHT]);
248
249     if(node->style.minDimensions[CSS_WIDTH] != CSS_UNDEFINED) {
250       print_number_nan("minWidth", node->style.minDimensions[CSS_WIDTH]);
251     }
252     if(node->style.minDimensions[CSS_HEIGHT] != CSS_UNDEFINED) {
253       print_number_nan("minHeight", node->style.minDimensions[CSS_HEIGHT]);
254     }
255     if(node->style.maxDimensions[CSS_WIDTH] != CSS_UNDEFINED) {
256       print_number_nan("maxWidth", node->style.maxDimensions[CSS_WIDTH]);
257     }
258     if(node->style.maxDimensions[CSS_HEIGHT] != CSS_UNDEFINED) {
259       print_number_nan("maxHeight", node->style.maxDimensions[CSS_HEIGHT]);
260     }
261
262     if (node->style.position_type == CSS_POSITION_ABSOLUTE) {
263       printf("position: 'absolute', ");
264     }
265     else if (node->style.position_type == CSS_POSITION_RELATIVE) {
266       printf("position: 'relative', ");
267     }
268
269     print_number_nan("position-left", node->style.position[CSS_LEFT]);
270     print_number_nan("position-right", node->style.position[CSS_RIGHT]);
271     print_number_nan("position-top", node->style.position[CSS_TOP]);
272     print_number_nan("position-bottom", node->style.position[CSS_BOTTOM]);
273   }
274
275   if (options & CSS_PRINT_CHILDREN && node->children_count > 0) {
276     printf("children: [\n");
277     int i;
278     for (i = 0; i < node->children_count; ++i) {
279       print_css_node_rec(node->get_child(node->context, i), options, level + 1);
280     }
281     indent(level);
282     printf("]},\n");
283   } else {
284     printf("},\n");
285   }
286 }
287
288 void print_css_node(css_node_t *node, css_print_options_t options) {
289   print_css_node_rec(node, options, 0);
290 }
291
292
293 static css_position_t leading[4] = {
294   /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP,
295   /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM,
296   /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT,
297   /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT
298 };
299 static css_position_t trailing[4] = {
300   /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM,
301   /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP,
302   /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT,
303   /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT
304 };
305 static css_position_t pos[4] = {
306   /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP,
307   /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM,
308   /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT,
309   /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT
310 };
311 static css_dimension_t dim[4] = {
312   /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT,
313   /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT,
314   /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH,
315   /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH
316 };
317
318 static bool isRowDirection(css_flex_direction_t flex_direction) {
319   return flex_direction == CSS_FLEX_DIRECTION_ROW ||
320          flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE;
321 }
322
323 static bool isColumnDirection(css_flex_direction_t flex_direction) {
324   return flex_direction == CSS_FLEX_DIRECTION_COLUMN ||
325          flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE;
326 }
327
328 static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) {
329   if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) {
330     return node->style.margin[CSS_START];
331   }
332
333   return node->style.margin[leading[axis]];
334 }
335
336 static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) {
337   if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) {
338     return node->style.margin[CSS_END];
339   }
340
341   return node->style.margin[trailing[axis]];
342 }
343
344 static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) {
345   if (isRowDirection(axis) &&
346       !isUndefined(node->style.padding[CSS_START]) &&
347       node->style.padding[CSS_START] >= 0) {
348     return node->style.padding[CSS_START];
349   }
350
351   if (node->style.padding[leading[axis]] >= 0) {
352     return node->style.padding[leading[axis]];
353   }
354
355   return 0;
356 }
357
358 static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) {
359   if (isRowDirection(axis) &&
360       !isUndefined(node->style.padding[CSS_END]) &&
361       node->style.padding[CSS_END] >= 0) {
362     return node->style.padding[CSS_END];
363   }
364
365   if (node->style.padding[trailing[axis]] >= 0) {
366     return node->style.padding[trailing[axis]];
367   }
368
369   return 0;
370 }
371
372 static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) {
373   if (isRowDirection(axis) &&
374       !isUndefined(node->style.border[CSS_START]) &&
375       node->style.border[CSS_START] >= 0) {
376     return node->style.border[CSS_START];
377   }
378
379   if (node->style.border[leading[axis]] >= 0) {
380     return node->style.border[leading[axis]];
381   }
382
383   return 0;
384 }
385
386 static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) {
387   if (isRowDirection(axis) &&
388       !isUndefined(node->style.border[CSS_END]) &&
389       node->style.border[CSS_END] >= 0) {
390     return node->style.border[CSS_END];
391   }
392
393   if (node->style.border[trailing[axis]] >= 0) {
394     return node->style.border[trailing[axis]];
395   }
396
397   return 0;
398 }
399
400 static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) {
401   return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);
402 }
403
404 static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) {
405   return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);
406 }
407
408 static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) {
409   return getLeadingBorder(node, axis) + getTrailingBorder(node, axis);
410 }
411
412 static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) {
413   return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);
414 }
415
416 static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) {
417   return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis);
418 }
419
420 static css_align_t getAlignItem(css_node_t *node, css_node_t *child) {
421   if (child->style.align_self != CSS_ALIGN_AUTO) {
422     return child->style.align_self;
423   }
424   return node->style.align_items;
425 }
426
427 static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) {
428   css_direction_t direction = node->style.direction;
429
430   if (direction == CSS_DIRECTION_INHERIT) {
431     direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR;
432   }
433
434   return direction;
435 }
436
437 static css_flex_direction_t getFlexDirection(css_node_t *node) {
438   return node->style.flex_direction;
439 }
440
441 static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) {
442   if (direction == CSS_DIRECTION_RTL) {
443     if (flex_direction == CSS_FLEX_DIRECTION_ROW) {
444       return CSS_FLEX_DIRECTION_ROW_REVERSE;
445     } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) {
446       return CSS_FLEX_DIRECTION_ROW;
447     }
448   }
449
450   return flex_direction;
451 }
452
453 static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) {
454   if (isColumnDirection(flex_direction)) {
455     return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
456   } else {
457     return CSS_FLEX_DIRECTION_COLUMN;
458   }
459 }
460
461 static float getFlex(css_node_t *node) {
462   return node->style.flex;
463 }
464
465 static bool isFlex(css_node_t *node) {
466   return (
467     node->style.position_type == CSS_POSITION_RELATIVE &&
468     getFlex(node) > 0
469   );
470 }
471
472 static bool isFlexWrap(css_node_t *node) {
473   return node->style.flex_wrap == CSS_WRAP;
474 }
475
476 static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
477   return node->layout.dimensions[dim[axis]] +
478     getLeadingMargin(node, axis) +
479     getTrailingMargin(node, axis);
480 }
481
482 static bool isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) {
483   float value = node->style.dimensions[dim[axis]];
484   return !isUndefined(value) && value >= 0.0;
485 }
486
487 static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) {
488   float value = node->layout.dimensions[dim[axis]];
489   return !isUndefined(value) && value >= 0.0;
490 }
491
492 static bool isPosDefined(css_node_t *node, css_position_t position) {
493   return !isUndefined(node->style.position[position]);
494 }
495
496 static bool isMeasureDefined(css_node_t *node) {
497   return node->measure;
498 }
499
500 static float getPosition(css_node_t *node, css_position_t position) {
501   float result = node->style.position[position];
502   if (!isUndefined(result)) {
503     return result;
504   }
505   return 0;
506 }
507
508 static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) {
509   float min = CSS_UNDEFINED;
510   float max = CSS_UNDEFINED;
511
512   if (isColumnDirection(axis)) {
513     min = node->style.minDimensions[CSS_HEIGHT];
514     max = node->style.maxDimensions[CSS_HEIGHT];
515   } else if (isRowDirection(axis)) {
516     min = node->style.minDimensions[CSS_WIDTH];
517     max = node->style.maxDimensions[CSS_WIDTH];
518   }
519
520   float boundValue = value;
521
522   if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
523     boundValue = max;
524   }
525   if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
526     boundValue = min;
527   }
528
529   return boundValue;
530 }
531
532 // When the user specifically sets a value for width or height
533 static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) {
534   // The parent already computed us a width or height. We just skip it
535   if (isLayoutDimDefined(node, axis)) {
536     return;
537   }
538   // We only run if there's a width or height defined
539   if (!isStyleDimDefined(node, axis)) {
540     return;
541   }
542
543   // The dimensions can never be smaller than the padding and border
544   node->layout.dimensions[dim[axis]] = fmaxf(
545     boundAxis(node, axis, node->style.dimensions[dim[axis]]),
546     getPaddingAndBorderAxis(node, axis)
547   );
548 }
549
550 static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) {
551     child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] -
552       child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]];
553   }
554
555 // If both left and right are defined, then use left. Otherwise return
556 // +left or -right depending on which is defined.
557 static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) {
558   float lead = node->style.position[leading[axis]];
559   if (!isUndefined(lead)) {
560     return lead;
561   }
562   return -getPosition(node, trailing[axis]);
563 }
564
565 static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) {
566   /** START_GENERATED **/
567   css_direction_t direction = resolveDirection(node, parentDirection);
568   css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction);
569   css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction);
570   css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
571
572   // Handle width and height style attributes
573   setDimensionFromStyle(node, mainAxis);
574   setDimensionFromStyle(node, crossAxis);
575
576   // Set the resolved resolution in the node's layout
577   node->layout.direction = direction;
578
579   // The position is set by the parent, but we need to complete it with a
580   // delta composed of the margin and left/top/right/bottom
581   node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) +
582     getRelativePosition(node, mainAxis);
583   node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) +
584     getRelativePosition(node, mainAxis);
585   node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) +
586     getRelativePosition(node, crossAxis);
587   node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) +
588     getRelativePosition(node, crossAxis);
589
590   // Inline immutable values from the target node to avoid excessive method
591   // invocations during the layout calculation.
592   int childCount = node->children_count;
593   float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis);
594   float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
595
596   if (isMeasureDefined(node)) {
597     bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis);
598
599     float width = CSS_UNDEFINED;
600     css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED;
601     if (isStyleDimDefined(node, resolvedRowAxis)) {
602       width = node->style.dimensions[CSS_WIDTH];
603       widthMode = CSS_MEASURE_MODE_EXACTLY;
604     } else if (isResolvedRowDimDefined) {
605       width = node->layout.dimensions[dim[resolvedRowAxis]];
606       widthMode = CSS_MEASURE_MODE_EXACTLY;
607     } else {
608       width = parentMaxWidth -
609         getMarginAxis(node, resolvedRowAxis);
610       widthMode = CSS_MEASURE_MODE_AT_MOST;
611     }
612     width -= paddingAndBorderAxisResolvedRow;
613     if (isUndefined(width)) {
614       widthMode = CSS_MEASURE_MODE_UNDEFINED;
615     }
616
617     float height = CSS_UNDEFINED;
618     css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED;
619     if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
620       height = node->style.dimensions[CSS_HEIGHT];
621       heightMode = CSS_MEASURE_MODE_EXACTLY;
622     } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
623       height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]];
624       heightMode = CSS_MEASURE_MODE_EXACTLY;
625     } else {
626       height = parentMaxHeight -
627         getMarginAxis(node, resolvedRowAxis);
628       heightMode = CSS_MEASURE_MODE_AT_MOST;
629     }
630     height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
631     if (isUndefined(height)) {
632       heightMode = CSS_MEASURE_MODE_UNDEFINED;
633     }
634
635     // We only need to give a dimension for the text if we haven't got any
636     // for it computed yet. It can either be from the style attribute or because
637     // the element is flexible.
638     bool isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
639     bool isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&
640       isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
641
642     // Let's not measure the text if we already know both dimensions
643     if (isRowUndefined || isColumnUndefined) {
644       css_dim_t measureDim = node->measure(
645         node->context,
646         width,
647         widthMode,
648         height,
649         heightMode
650       );
651       if (isRowUndefined) {
652         node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
653           paddingAndBorderAxisResolvedRow;
654       }
655       if (isColumnUndefined) {
656         node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
657           paddingAndBorderAxisColumn;
658       }
659     }
660     if (childCount == 0) {
661       return;
662     }
663   }
664
665   bool isNodeFlexWrap = isFlexWrap(node);
666
667   css_justify_t justifyContent = node->style.justify_content;
668
669   float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);
670   float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);
671   float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);
672   float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);
673
674   bool isMainDimDefined = isLayoutDimDefined(node, mainAxis);
675   bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis);
676   bool isMainRowDirection = isRowDirection(mainAxis);
677
678   int i;
679   int ii;
680   css_node_t* child;
681   css_flex_direction_t axis;
682
683   css_node_t* firstAbsoluteChild = NULL;
684   css_node_t* currentAbsoluteChild = NULL;
685
686   float definedMainDim = CSS_UNDEFINED;
687   if (isMainDimDefined) {
688     definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
689   }
690
691   // We want to execute the next two loops one per line with flex-wrap
692   int startLine = 0;
693   int endLine = 0;
694   // int nextOffset = 0;
695   int alreadyComputedNextLayout = 0;
696   // We aggregate the total dimensions of the container in those two variables
697   float linesCrossDim = 0;
698   float linesMainDim = 0;
699   int linesCount = 0;
700   while (endLine < childCount) {
701     // <Loop A> Layout non flexible children and count children by type
702
703     // mainContentDim is accumulation of the dimensions and margin of all the
704     // non flexible children. This will be used in order to either set the
705     // dimensions of the node if none already exist, or to compute the
706     // remaining space left for the flexible children.
707     float mainContentDim = 0;
708
709     // There are three kind of children, non flexible, flexible and absolute.
710     // We need to know how many there are in order to distribute the space.
711     int flexibleChildrenCount = 0;
712     float totalFlexible = 0;
713     int nonFlexibleChildrenCount = 0;
714
715     // Use the line loop to position children in the main axis for as long
716     // as they are using a simple stacking behaviour. Children that are
717     // immediately stacked in the initial loop will not be touched again
718     // in <Loop C>.
719     bool isSimpleStackMain =
720         (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) ||
721         (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER);
722     int firstComplexMain = (isSimpleStackMain ? childCount : startLine);
723
724     // Use the initial line loop to position children in the cross axis for
725     // as long as they are relatively positioned with alignment STRETCH or
726     // FLEX_START. Children that are immediately stacked in the initial loop
727     // will not be touched again in <Loop D>.
728     bool isSimpleStackCross = true;
729     int firstComplexCross = childCount;
730
731     css_node_t* firstFlexChild = NULL;
732     css_node_t* currentFlexChild = NULL;
733
734     float mainDim = leadingPaddingAndBorderMain;
735     float crossDim = 0;
736
737     float maxWidth = CSS_UNDEFINED;
738     float maxHeight = CSS_UNDEFINED;
739     for (i = startLine; i < childCount; ++i) {
740       child = node->get_child(node->context, i);
741       child->line_index = linesCount;
742
743       child->next_absolute_child = NULL;
744       child->next_flex_child = NULL;
745
746       css_align_t alignItem = getAlignItem(node, child);
747
748       // Pre-fill cross axis dimensions when the child is using stretch before
749       // we call the recursive layout pass
750       if (alignItem == CSS_ALIGN_STRETCH &&
751           child->style.position_type == CSS_POSITION_RELATIVE &&
752           isCrossDimDefined &&
753           !isStyleDimDefined(child, crossAxis)) {
754         child->layout.dimensions[dim[crossAxis]] = fmaxf(
755           boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
756             paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
757           // You never want to go smaller than padding
758           getPaddingAndBorderAxis(child, crossAxis)
759         );
760       } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
761         // Store a private linked list of absolutely positioned children
762         // so that we can efficiently traverse them later.
763         if (firstAbsoluteChild == NULL) {
764           firstAbsoluteChild = child;
765         }
766         if (currentAbsoluteChild != NULL) {
767           currentAbsoluteChild->next_absolute_child = child;
768         }
769         currentAbsoluteChild = child;
770
771         // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
772         // left and right or top and bottom).
773         for (ii = 0; ii < 2; ii++) {
774           axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
775           if (isLayoutDimDefined(node, axis) &&
776               !isStyleDimDefined(child, axis) &&
777               isPosDefined(child, leading[axis]) &&
778               isPosDefined(child, trailing[axis])) {
779             child->layout.dimensions[dim[axis]] = fmaxf(
780               boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
781                 getPaddingAndBorderAxis(node, axis) -
782                 getMarginAxis(child, axis) -
783                 getPosition(child, leading[axis]) -
784                 getPosition(child, trailing[axis])),
785               // You never want to go smaller than padding
786               getPaddingAndBorderAxis(child, axis)
787             );
788           }
789         }
790       }
791
792       float nextContentDim = 0;
793
794       // It only makes sense to consider a child flexible if we have a computed
795       // dimension for the node->
796       if (isMainDimDefined && isFlex(child)) {
797         flexibleChildrenCount++;
798         totalFlexible += child->style.flex;
799
800         // Store a private linked list of flexible children so that we can
801         // efficiently traverse them later.
802         if (firstFlexChild == NULL) {
803           firstFlexChild = child;
804         }
805         if (currentFlexChild != NULL) {
806           currentFlexChild->next_flex_child = child;
807         }
808         currentFlexChild = child;
809
810         // Even if we don't know its exact size yet, we already know the padding,
811         // border and margin. We'll use this partial information, which represents
812         // the smallest possible size for the child, to compute the remaining
813         // available space.
814         nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +
815           getMarginAxis(child, mainAxis);
816
817       } else {
818         maxWidth = CSS_UNDEFINED;
819         maxHeight = CSS_UNDEFINED;
820
821         if (!isMainRowDirection) {
822           if (isLayoutDimDefined(node, resolvedRowAxis)) {
823             maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
824               paddingAndBorderAxisResolvedRow;
825           } else {
826             maxWidth = parentMaxWidth -
827               getMarginAxis(node, resolvedRowAxis) -
828               paddingAndBorderAxisResolvedRow;
829           }
830         } else {
831           if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
832             maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
833                 paddingAndBorderAxisColumn;
834           } else {
835             maxHeight = parentMaxHeight -
836               getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -
837               paddingAndBorderAxisColumn;
838           }
839         }
840
841         // This is the main recursive call. We layout non flexible children.
842         if (alreadyComputedNextLayout == 0) {
843           layoutNode(child, maxWidth, maxHeight, direction);
844         }
845
846         // Absolute positioned elements do not take part of the layout, so we
847         // don't use them to compute mainContentDim
848         if (child->style.position_type == CSS_POSITION_RELATIVE) {
849           nonFlexibleChildrenCount++;
850           // At this point we know the final size and margin of the element.
851           nextContentDim = getDimWithMargin(child, mainAxis);
852         }
853       }
854
855       // The element we are about to add would make us go to the next line
856       if (isNodeFlexWrap &&
857           isMainDimDefined &&
858           mainContentDim + nextContentDim > definedMainDim &&
859           // If there's only one element, then it's bigger than the content
860           // and needs its own line
861           i != startLine) {
862         nonFlexibleChildrenCount--;
863         alreadyComputedNextLayout = 1;
864         break;
865       }
866
867       // Disable simple stacking in the main axis for the current line as
868       // we found a non-trivial child-> The remaining children will be laid out
869       // in <Loop C>.
870       if (isSimpleStackMain &&
871           (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) {
872         isSimpleStackMain = false;
873         firstComplexMain = i;
874       }
875
876       // Disable simple stacking in the cross axis for the current line as
877       // we found a non-trivial child-> The remaining children will be laid out
878       // in <Loop D>.
879       if (isSimpleStackCross &&
880           (child->style.position_type != CSS_POSITION_RELATIVE ||
881               (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) ||
882               (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) {
883         isSimpleStackCross = false;
884         firstComplexCross = i;
885       }
886
887       if (isSimpleStackMain) {
888         child->layout.position[pos[mainAxis]] += mainDim;
889         if (isMainDimDefined) {
890           setTrailingPosition(node, child, mainAxis);
891         }
892
893         mainDim += getDimWithMargin(child, mainAxis);
894         crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
895       }
896
897       if (isSimpleStackCross) {
898         child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
899         if (isCrossDimDefined) {
900           setTrailingPosition(node, child, crossAxis);
901         }
902       }
903
904       alreadyComputedNextLayout = 0;
905       mainContentDim += nextContentDim;
906       endLine = i + 1;
907     }
908
909     // <Loop B> Layout flexible children and allocate empty space
910
911     // In order to position the elements in the main axis, we have two
912     // controls. The space between the beginning and the first element
913     // and the space between each two elements.
914     float leadingMainDim = 0;
915     float betweenMainDim = 0;
916
917     // The remaining available space that needs to be allocated
918     float remainingMainDim = 0;
919     if (isMainDimDefined) {
920       remainingMainDim = definedMainDim - mainContentDim;
921     } else {
922       remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;
923     }
924
925     // If there are flexible children in the mix, they are going to fill the
926     // remaining space
927     if (flexibleChildrenCount != 0) {
928       float flexibleMainDim = remainingMainDim / totalFlexible;
929       float baseMainDim;
930       float boundMainDim;
931
932       // If the flex share of remaining space doesn't meet min/max bounds,
933       // remove this child from flex calculations.
934       currentFlexChild = firstFlexChild;
935       while (currentFlexChild != NULL) {
936         baseMainDim = flexibleMainDim * currentFlexChild->style.flex +
937             getPaddingAndBorderAxis(currentFlexChild, mainAxis);
938         boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
939
940         if (baseMainDim != boundMainDim) {
941           remainingMainDim -= boundMainDim;
942           totalFlexible -= currentFlexChild->style.flex;
943         }
944
945         currentFlexChild = currentFlexChild->next_flex_child;
946       }
947       flexibleMainDim = remainingMainDim / totalFlexible;
948
949       // The non flexible children can overflow the container, in this case
950       // we should just assume that there is no space available.
951       if (flexibleMainDim < 0) {
952         flexibleMainDim = 0;
953       }
954
955       currentFlexChild = firstFlexChild;
956       while (currentFlexChild != NULL) {
957         // At this point we know the final size of the element in the main
958         // dimension
959         currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
960           flexibleMainDim * currentFlexChild->style.flex +
961               getPaddingAndBorderAxis(currentFlexChild, mainAxis)
962         );
963
964         maxWidth = CSS_UNDEFINED;
965         if (isLayoutDimDefined(node, resolvedRowAxis)) {
966           maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
967             paddingAndBorderAxisResolvedRow;
968         } else if (!isMainRowDirection) {
969           maxWidth = parentMaxWidth -
970             getMarginAxis(node, resolvedRowAxis) -
971             paddingAndBorderAxisResolvedRow;
972         }
973         maxHeight = CSS_UNDEFINED;
974         if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
975           maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
976             paddingAndBorderAxisColumn;
977         } else if (isMainRowDirection) {
978           maxHeight = parentMaxHeight -
979             getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -
980             paddingAndBorderAxisColumn;
981         }
982
983         // And we recursively call the layout algorithm for this child
984         layoutNode(currentFlexChild, maxWidth, maxHeight, direction);
985
986         child = currentFlexChild;
987         currentFlexChild = currentFlexChild->next_flex_child;
988         child->next_flex_child = NULL;
989       }
990
991     // We use justifyContent to figure out how to allocate the remaining
992     // space available
993     } else if (justifyContent != CSS_JUSTIFY_FLEX_START) {
994       if (justifyContent == CSS_JUSTIFY_CENTER) {
995         leadingMainDim = remainingMainDim / 2;
996       } else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
997         leadingMainDim = remainingMainDim;
998       } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) {
999         remainingMainDim = fmaxf(remainingMainDim, 0);
1000         if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
1001           betweenMainDim = remainingMainDim /
1002             (flexibleChildrenCount + nonFlexibleChildrenCount - 1);
1003         } else {
1004           betweenMainDim = 0;
1005         }
1006       } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) {
1007         // Space on the edges is half of the space between elements
1008         betweenMainDim = remainingMainDim /
1009           (flexibleChildrenCount + nonFlexibleChildrenCount);
1010         leadingMainDim = betweenMainDim / 2;
1011       }
1012     }
1013
1014     // <Loop C> Position elements in the main axis and compute dimensions
1015
1016     // At this point, all the children have their dimensions set. We need to
1017     // find their position. In order to do that, we accumulate data in
1018     // variables that are also useful to compute the total dimensions of the
1019     // container!
1020     mainDim += leadingMainDim;
1021
1022     for (i = firstComplexMain; i < endLine; ++i) {
1023       child = node->get_child(node->context, i);
1024
1025       if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
1026           isPosDefined(child, leading[mainAxis])) {
1027         // In case the child is position absolute and has left/top being
1028         // defined, we override the position to whatever the user said
1029         // (and margin/border).
1030         child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +
1031           getLeadingBorder(node, mainAxis) +
1032           getLeadingMargin(child, mainAxis);
1033       } else {
1034         // If the child is position absolute (without top/left) or relative,
1035         // we put it at the current accumulated offset.
1036         child->layout.position[pos[mainAxis]] += mainDim;
1037
1038         // Define the trailing position accordingly.
1039         if (isMainDimDefined) {
1040           setTrailingPosition(node, child, mainAxis);
1041         }
1042
1043         // Now that we placed the element, we need to update the variables
1044         // We only need to do that for relative elements. Absolute elements
1045         // do not take part in that phase.
1046         if (child->style.position_type == CSS_POSITION_RELATIVE) {
1047           // The main dimension is the sum of all the elements dimension plus
1048           // the spacing.
1049           mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
1050           // The cross dimension is the max of the elements dimension since there
1051           // can only be one element in that cross dimension.
1052           crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
1053         }
1054       }
1055     }
1056
1057     float containerCrossAxis = node->layout.dimensions[dim[crossAxis]];
1058     if (!isCrossDimDefined) {
1059       containerCrossAxis = fmaxf(
1060         // For the cross dim, we add both sides at the end because the value
1061         // is aggregate via a max function. Intermediate negative values
1062         // can mess this computation otherwise
1063         boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
1064         paddingAndBorderAxisCross
1065       );
1066     }
1067
1068     // <Loop D> Position elements in the cross axis
1069     for (i = firstComplexCross; i < endLine; ++i) {
1070       child = node->get_child(node->context, i);
1071
1072       if (child->style.position_type == CSS_POSITION_ABSOLUTE &&
1073           isPosDefined(child, leading[crossAxis])) {
1074         // In case the child is absolutely positionned and has a
1075         // top/left/bottom/right being set, we override all the previously
1076         // computed positions to set it correctly.
1077         child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +
1078           getLeadingBorder(node, crossAxis) +
1079           getLeadingMargin(child, crossAxis);
1080
1081       } else {
1082         float leadingCrossDim = leadingPaddingAndBorderCross;
1083
1084         // For a relative children, we're either using alignItems (parent) or
1085         // alignSelf (child) in order to determine the position in the cross axis
1086         if (child->style.position_type == CSS_POSITION_RELATIVE) {
1087           /*eslint-disable */
1088           // This variable is intentionally re-defined as the code is transpiled to a block scope language
1089           css_align_t alignItem = getAlignItem(node, child);
1090           /*eslint-enable */
1091           if (alignItem == CSS_ALIGN_STRETCH) {
1092             // You can only stretch if the dimension has not already been defined
1093             // previously.
1094             if (!isStyleDimDefined(child, crossAxis)) {
1095               float dimCrossAxis = child->layout.dimensions[dim[crossAxis]];
1096               child->layout.dimensions[dim[crossAxis]] = fmaxf(
1097                 boundAxis(child, crossAxis, containerCrossAxis -
1098                   paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
1099                 // You never want to go smaller than padding
1100                 getPaddingAndBorderAxis(child, crossAxis)
1101               );
1102
1103               // If the size has changed, and this child has children we need to re-layout this child
1104               if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) {
1105                 // Reset child margins before re-layout as they are added back in layoutNode and would be doubled
1106                 child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) +
1107                   getRelativePosition(child, mainAxis);
1108                 child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) +
1109                   getRelativePosition(child, mainAxis);
1110                 child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) +
1111                   getRelativePosition(child, crossAxis);
1112                 child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) +
1113                   getRelativePosition(child, crossAxis);
1114
1115                 layoutNode(child, maxWidth, maxHeight, direction);
1116               }
1117             }
1118           } else if (alignItem != CSS_ALIGN_FLEX_START) {
1119             // The remaining space between the parent dimensions+padding and child
1120             // dimensions+margin.
1121             float remainingCrossDim = containerCrossAxis -
1122               paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis);
1123
1124             if (alignItem == CSS_ALIGN_CENTER) {
1125               leadingCrossDim += remainingCrossDim / 2;
1126             } else { // CSS_ALIGN_FLEX_END
1127               leadingCrossDim += remainingCrossDim;
1128             }
1129           }
1130         }
1131
1132         // And we apply the position
1133         child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
1134
1135         // Define the trailing position accordingly.
1136         if (isCrossDimDefined) {
1137           setTrailingPosition(node, child, crossAxis);
1138         }
1139       }
1140     }
1141
1142     linesCrossDim += crossDim;
1143     linesMainDim = fmaxf(linesMainDim, mainDim);
1144     linesCount += 1;
1145     startLine = endLine;
1146   }
1147
1148   // <Loop E>
1149   //
1150   // Note(prenaux): More than one line, we need to layout the crossAxis
1151   // according to alignContent.
1152   //
1153   // Note that we could probably remove <Loop D> and handle the one line case
1154   // here too, but for the moment this is safer since it won't interfere with
1155   // previously working code.
1156   //
1157   // See specs:
1158   // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
1159   // section 9.4
1160   //
1161   if (linesCount > 1 && isCrossDimDefined) {
1162     float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] -
1163         paddingAndBorderAxisCross;
1164     float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
1165
1166     float crossDimLead = 0;
1167     float currentLead = leadingPaddingAndBorderCross;
1168
1169     css_align_t alignContent = node->style.align_content;
1170     if (alignContent == CSS_ALIGN_FLEX_END) {
1171       currentLead += remainingAlignContentDim;
1172     } else if (alignContent == CSS_ALIGN_CENTER) {
1173       currentLead += remainingAlignContentDim / 2;
1174     } else if (alignContent == CSS_ALIGN_STRETCH) {
1175       if (nodeCrossAxisInnerSize > linesCrossDim) {
1176         crossDimLead = (remainingAlignContentDim / linesCount);
1177       }
1178     }
1179
1180     int endIndex = 0;
1181     for (i = 0; i < linesCount; ++i) {
1182       int startIndex = endIndex;
1183
1184       // compute the line's height and find the endIndex
1185       float lineHeight = 0;
1186       for (ii = startIndex; ii < childCount; ++ii) {
1187         child = node->get_child(node->context, ii);
1188         if (child->style.position_type != CSS_POSITION_RELATIVE) {
1189           continue;
1190         }
1191         if (child->line_index != i) {
1192           break;
1193         }
1194         if (isLayoutDimDefined(child, crossAxis)) {
1195           lineHeight = fmaxf(
1196             lineHeight,
1197             child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis)
1198           );
1199         }
1200       }
1201       endIndex = ii;
1202       lineHeight += crossDimLead;
1203
1204       for (ii = startIndex; ii < endIndex; ++ii) {
1205         child = node->get_child(node->context, ii);
1206         if (child->style.position_type != CSS_POSITION_RELATIVE) {
1207           continue;
1208         }
1209
1210         css_align_t alignContentAlignItem = getAlignItem(node, child);
1211         if (alignContentAlignItem == CSS_ALIGN_FLEX_START) {
1212           child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);
1213         } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) {
1214           child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]];
1215         } else if (alignContentAlignItem == CSS_ALIGN_CENTER) {
1216           float childHeight = child->layout.dimensions[dim[crossAxis]];
1217           child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
1218         } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) {
1219           child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);
1220           // TODO(prenaux): Correctly set the height of items with undefined
1221           //                (auto) crossAxis dimension.
1222         }
1223       }
1224
1225       currentLead += lineHeight;
1226     }
1227   }
1228
1229   bool needsMainTrailingPos = false;
1230   bool needsCrossTrailingPos = false;
1231
1232   // If the user didn't specify a width or height, and it has not been set
1233   // by the container, then we set it via the children.
1234   if (!isMainDimDefined) {
1235     node->layout.dimensions[dim[mainAxis]] = fmaxf(
1236       // We're missing the last padding at this point to get the final
1237       // dimension
1238       boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),
1239       // We can never assign a width smaller than the padding and borders
1240       paddingAndBorderAxisMain
1241     );
1242
1243     if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
1244         mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
1245       needsMainTrailingPos = true;
1246     }
1247   }
1248
1249   if (!isCrossDimDefined) {
1250     node->layout.dimensions[dim[crossAxis]] = fmaxf(
1251       // For the cross dim, we add both sides at the end because the value
1252       // is aggregate via a max function. Intermediate negative values
1253       // can mess this computation otherwise
1254       boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
1255       paddingAndBorderAxisCross
1256     );
1257
1258     if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE ||
1259         crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
1260       needsCrossTrailingPos = true;
1261     }
1262   }
1263
1264   // <Loop F> Set trailing position if necessary
1265   if (needsMainTrailingPos || needsCrossTrailingPos) {
1266     for (i = 0; i < childCount; ++i) {
1267       child = node->get_child(node->context, i);
1268
1269       if (needsMainTrailingPos) {
1270         setTrailingPosition(node, child, mainAxis);
1271       }
1272
1273       if (needsCrossTrailingPos) {
1274         setTrailingPosition(node, child, crossAxis);
1275       }
1276     }
1277   }
1278
1279   // <Loop G> Calculate dimensions for absolutely positioned elements
1280   currentAbsoluteChild = firstAbsoluteChild;
1281   while (currentAbsoluteChild != NULL) {
1282     // Pre-fill dimensions when using absolute position and both offsets for
1283     // the axis are defined (either both left and right or top and bottom).
1284     for (ii = 0; ii < 2; ii++) {
1285       axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
1286
1287       if (isLayoutDimDefined(node, axis) &&
1288           !isStyleDimDefined(currentAbsoluteChild, axis) &&
1289           isPosDefined(currentAbsoluteChild, leading[axis]) &&
1290           isPosDefined(currentAbsoluteChild, trailing[axis])) {
1291         currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf(
1292           boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] -
1293             getBorderAxis(node, axis) -
1294             getMarginAxis(currentAbsoluteChild, axis) -
1295             getPosition(currentAbsoluteChild, leading[axis]) -
1296             getPosition(currentAbsoluteChild, trailing[axis])
1297           ),
1298           // You never want to go smaller than padding
1299           getPaddingAndBorderAxis(currentAbsoluteChild, axis)
1300         );
1301       }
1302
1303       if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
1304           !isPosDefined(currentAbsoluteChild, leading[axis])) {
1305         currentAbsoluteChild->layout.position[leading[axis]] =
1306           node->layout.dimensions[dim[axis]] -
1307           currentAbsoluteChild->layout.dimensions[dim[axis]] -
1308           getPosition(currentAbsoluteChild, trailing[axis]);
1309       }
1310     }
1311
1312     child = currentAbsoluteChild;
1313     currentAbsoluteChild = currentAbsoluteChild->next_absolute_child;
1314     child->next_absolute_child = NULL;
1315   }
1316   /** END_GENERATED **/
1317 }
1318
1319 void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) {
1320   css_layout_t *layout = &node->layout;
1321   css_direction_t direction = node->style.direction;
1322   layout->should_update = true;
1323
1324   bool skipLayout =
1325     !node->is_dirty(node->context) &&
1326     eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) &&
1327     eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) &&
1328     eq(layout->last_parent_max_width, parentMaxWidth) &&
1329     eq(layout->last_parent_max_height, parentMaxHeight) &&
1330     eq(layout->last_direction, direction);
1331
1332   if (skipLayout) {
1333     layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH];
1334     layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT];
1335     layout->position[CSS_TOP] = layout->last_position[CSS_TOP];
1336     layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT];
1337   } else {
1338     layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH];
1339     layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT];
1340     layout->last_parent_max_width = parentMaxWidth;
1341     layout->last_parent_max_height = parentMaxHeight;
1342     layout->last_direction = direction;
1343
1344     int i, childCount;
1345     for (i = 0, childCount = node->children_count; i < childCount; i++) {
1346       resetNodeLayout(node->get_child(node->context, i));
1347     }
1348
1349     layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection);
1350
1351     layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH];
1352     layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT];
1353     layout->last_position[CSS_TOP] = layout->position[CSS_TOP];
1354     layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT];
1355   }
1356
1357 #if defined(FLEXBOX_LAYOUT_NODE_DEBUG)
1358   printf("Input:    ");
1359   print_css_node(node, (css_print_options_t)(CSS_PRINT_STYLE | CSS_PRINT_CHILDREN));
1360   printf("Output:   ");
1361   print_css_node(node, (css_print_options_t)(CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN));
1362   printf("\n");
1363 #endif // defined(FLEXBOX_LAYOUT_NODE_DEBUG)
1364 }
1365
1366 void resetNodeLayout(css_node_t *node) {
1367   node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
1368   node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
1369   node->layout.position[CSS_LEFT] = 0;
1370   node->layout.position[CSS_TOP] = 0;
1371 }