Rebase of facebook flexbox to yoga
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / third-party / yoga / Yoga.cpp
1 /**
2  * Copyright (c) 2014-present, Facebook, Inc.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7
8 #include "Yoga.h"
9 #include <float.h>
10 #include <string.h>
11 #include <algorithm>
12 #include "Utils.h"
13 #include "YGNode.h"
14 #include "YGNodePrint.h"
15 #include "Yoga-internal.h"
16 #ifdef _MSC_VER
17 #include <float.h>
18
19 /* define fmaxf if < VC12 */
20 #if _MSC_VER < 1800
21 __forceinline const float fmaxf(const float a, const float b) {
22   if (!YGFloatIsUndefined(a) && !YGFloatIsUndefined(b)) {
23     return (a > b) ? a : b;
24   }
25   return YGFloatIsUndefined(a) ? b : a;
26 }
27 #endif
28 #endif
29
30 #ifdef ANDROID
31 static int YGAndroidLog(const YGConfigRef config,
32                         const YGNodeRef node,
33                         YGLogLevel level,
34                         const char *format,
35                         va_list args);
36 #else
37 static int YGDefaultLog(const YGConfigRef config,
38                         const YGNodeRef node,
39                         YGLogLevel level,
40                         const char *format,
41                         va_list args);
42 #endif
43
44 const YGValue YGValueZero = {0, YGUnitPoint};
45 const YGValue YGValueUndefined = {YGUndefined, YGUnitUndefined};
46 const YGValue YGValueAuto = {YGUndefined, YGUnitAuto};
47
48 #ifdef ANDROID
49 #include <android/log.h>
50 static int YGAndroidLog(const YGConfigRef config,
51                         const YGNodeRef node,
52                         YGLogLevel level,
53                         const char *format,
54                         va_list args) {
55   int androidLevel = YGLogLevelDebug;
56   switch (level) {
57     case YGLogLevelFatal:
58       androidLevel = ANDROID_LOG_FATAL;
59       break;
60     case YGLogLevelError:
61       androidLevel = ANDROID_LOG_ERROR;
62       break;
63     case YGLogLevelWarn:
64       androidLevel = ANDROID_LOG_WARN;
65       break;
66     case YGLogLevelInfo:
67       androidLevel = ANDROID_LOG_INFO;
68       break;
69     case YGLogLevelDebug:
70       androidLevel = ANDROID_LOG_DEBUG;
71       break;
72     case YGLogLevelVerbose:
73       androidLevel = ANDROID_LOG_VERBOSE;
74       break;
75   }
76   const int result = __android_log_vprint(androidLevel, "yoga", format, args);
77   return result;
78 }
79 #else
80 #define YG_UNUSED(x) (void)(x);
81
82 static int YGDefaultLog(const YGConfigRef config,
83                         const YGNodeRef node,
84                         YGLogLevel level,
85                         const char *format,
86                         va_list args) {
87   YG_UNUSED(config);
88   YG_UNUSED(node);
89   switch (level) {
90     case YGLogLevelError:
91     case YGLogLevelFatal:
92       return vfprintf(stderr, format, args);
93     case YGLogLevelWarn:
94     case YGLogLevelInfo:
95     case YGLogLevelDebug:
96     case YGLogLevelVerbose:
97     default:
98       return vprintf(format, args);
99   }
100 }
101
102 #undef YG_UNUSED
103 #endif
104
105 bool YGFloatIsUndefined(const float value) {
106   // Value of a float in the case of it being not defined is 10.1E20. Earlier
107   // it used to be NAN, the benefit of which was that if NAN is involved in any
108   // mathematical expression the result was NAN. But since we want to have
109   // `-ffast-math` flag being used by compiler which assumes that the floating
110   // point values are not NAN and Inf, we represent YGUndefined as 10.1E20. But
111   // now if YGUndefined is involved in any mathematical operations this
112   // value(10.1E20) would change. So the following check makes sure that if the
113   // value is outside a range (-10E8, 10E8) then it is undefined.
114   return value >= 10E8 || value <= -10E8;
115 }
116
117 const YGValue* YGComputedEdgeValue(
118     const std::array<YGValue, YGEdgeCount>& edges,
119     const YGEdge edge,
120     const YGValue* const defaultValue) {
121   if (edges[edge].unit != YGUnitUndefined) {
122     return &edges[edge];
123   }
124
125   if ((edge == YGEdgeTop || edge == YGEdgeBottom) &&
126       edges[YGEdgeVertical].unit != YGUnitUndefined) {
127     return &edges[YGEdgeVertical];
128   }
129
130   if ((edge == YGEdgeLeft || edge == YGEdgeRight || edge == YGEdgeStart || edge == YGEdgeEnd) &&
131       edges[YGEdgeHorizontal].unit != YGUnitUndefined) {
132     return &edges[YGEdgeHorizontal];
133   }
134
135   if (edges[YGEdgeAll].unit != YGUnitUndefined) {
136     return &edges[YGEdgeAll];
137   }
138
139   if (edge == YGEdgeStart || edge == YGEdgeEnd) {
140     return &YGValueUndefined;
141   }
142
143   return defaultValue;
144 }
145
146 void* YGNodeGetContext(YGNodeRef node) {
147   return node->getContext();
148 }
149
150 void YGNodeSetContext(YGNodeRef node, void* context) {
151   return node->setContext(context);
152 }
153
154 YGMeasureFunc YGNodeGetMeasureFunc(YGNodeRef node) {
155   return node->getMeasure();
156 }
157
158 void YGNodeSetMeasureFunc(YGNodeRef node, YGMeasureFunc measureFunc) {
159   node->setMeasureFunc(measureFunc);
160 }
161
162 YGBaselineFunc YGNodeGetBaselineFunc(YGNodeRef node) {
163   return node->getBaseline();
164 }
165
166 void YGNodeSetBaselineFunc(YGNodeRef node, YGBaselineFunc baselineFunc) {
167   node->setBaseLineFunc(baselineFunc);
168 }
169
170 YGDirtiedFunc YGNodeGetDirtiedFunc(YGNodeRef node) {
171   return node->getDirtied();
172 }
173
174 void YGNodeSetDirtiedFunc(YGNodeRef node, YGDirtiedFunc dirtiedFunc) {
175   node->setDirtiedFunc(dirtiedFunc);
176 }
177
178 YGPrintFunc YGNodeGetPrintFunc(YGNodeRef node) {
179   return node->getPrintFunc();
180 }
181
182 void YGNodeSetPrintFunc(YGNodeRef node, YGPrintFunc printFunc) {
183   node->setPrintFunc(printFunc);
184 }
185
186 bool YGNodeGetHasNewLayout(YGNodeRef node) {
187   return node->getHasNewLayout();
188 }
189
190 void YGNodeSetHasNewLayout(YGNodeRef node, bool hasNewLayout) {
191   node->setHasNewLayout(hasNewLayout);
192 }
193
194 YGNodeType YGNodeGetNodeType(YGNodeRef node) {
195   return node->getNodeType();
196 }
197
198 void YGNodeSetNodeType(YGNodeRef node, YGNodeType nodeType) {
199   return node->setNodeType(nodeType);
200 }
201
202 bool YGNodeIsDirty(YGNodeRef node) {
203   return node->isDirty();
204 }
205
206 bool YGNodeLayoutGetDidUseLegacyFlag(const YGNodeRef node) {
207   return node->didUseLegacyFlag();
208 }
209
210 void YGNodeMarkDirtyAndPropogateToDescendants(const YGNodeRef node) {
211   return node->markDirtyAndPropogateDownwards();
212 }
213
214 int32_t gNodeInstanceCount = 0;
215 int32_t gConfigInstanceCount = 0;
216
217 WIN_EXPORT YGNodeRef YGNodeNewWithConfig(const YGConfigRef config) {
218   const YGNodeRef node = new YGNode();
219   YGAssertWithConfig(
220       config, node != nullptr, "Could not allocate memory for node");
221   gNodeInstanceCount++;
222
223   if (config->useWebDefaults) {
224     node->setStyleFlexDirection(YGFlexDirectionRow);
225     node->setStyleAlignContent(YGAlignStretch);
226   }
227   node->setConfig(config);
228   return node;
229 }
230
231 YGConfigRef YGConfigGetDefault() {
232   static YGConfigRef defaultConfig = YGConfigNew();
233   return defaultConfig;
234 }
235
236 YGNodeRef YGNodeNew(void) {
237   return YGNodeNewWithConfig(YGConfigGetDefault());
238 }
239
240 YGNodeRef YGNodeClone(YGNodeRef oldNode) {
241   YGNodeRef node = new YGNode(*oldNode);
242   YGAssertWithConfig(
243       oldNode->getConfig(),
244       node != nullptr,
245       "Could not allocate memory for node");
246   gNodeInstanceCount++;
247   node->setOwner(nullptr);
248   return node;
249 }
250
251 static YGConfigRef YGConfigClone(const YGConfig& oldConfig) {
252   const YGConfigRef config = new YGConfig(oldConfig);
253   YGAssert(config != nullptr, "Could not allocate memory for config");
254   if (config == nullptr) {
255     abort();
256   }
257   gConfigInstanceCount++;
258   return config;
259 }
260
261 static YGNodeRef YGNodeDeepClone(YGNodeRef oldNode) {
262   YGNodeRef node = YGNodeClone(oldNode);
263   YGVector vec = YGVector();
264   vec.reserve(oldNode->getChildren().size());
265   YGNodeRef childNode = nullptr;
266   for (auto& item : oldNode->getChildren()) {
267     childNode = YGNodeDeepClone(item);
268     childNode->setOwner(node);
269     vec.push_back(childNode);
270   }
271   node->setChildren(vec);
272
273   if (oldNode->getConfig() != nullptr) {
274     node->setConfig(YGConfigClone(*(oldNode->getConfig())));
275   }
276
277   if (oldNode->getNextChild() != nullptr) {
278     node->setNextChild(YGNodeDeepClone(oldNode->getNextChild()));
279   }
280
281   return node;
282 }
283
284 void YGNodeFree(const YGNodeRef node) {
285   if (YGNodeRef owner = node->getOwner()) {
286     owner->removeChild(node);
287     node->setOwner(nullptr);
288   }
289
290   const uint32_t childCount = YGNodeGetChildCount(node);
291   for (uint32_t i = 0; i < childCount; i++) {
292     const YGNodeRef child = YGNodeGetChild(node, i);
293     child->setOwner(nullptr);
294   }
295
296   node->clearChildren();
297   delete node;
298   gNodeInstanceCount--;
299 }
300
301 static void YGConfigFreeRecursive(const YGNodeRef root) {
302   if (root->getConfig() != nullptr) {
303     gConfigInstanceCount--;
304     delete root->getConfig();
305   }
306   // Delete configs recursively for childrens
307   for (uint32_t i = 0; i < root->getChildrenCount(); ++i) {
308     YGConfigFreeRecursive(root->getChild(i));
309   }
310 }
311
312 void YGNodeFreeRecursive(const YGNodeRef root) {
313   while (YGNodeGetChildCount(root) > 0) {
314     const YGNodeRef child = YGNodeGetChild(root, 0);
315     if (child->getOwner() != root) {
316       // Don't free shared nodes that we don't own.
317       break;
318     }
319     YGNodeRemoveChild(root, child);
320     YGNodeFreeRecursive(child);
321   }
322   YGNodeFree(root);
323 }
324
325 void YGNodeReset(const YGNodeRef node) {
326   YGAssertWithNode(node,
327                    YGNodeGetChildCount(node) == 0,
328                    "Cannot reset a node which still has children attached");
329   YGAssertWithNode(
330       node,
331       node->getOwner() == nullptr,
332       "Cannot reset a node still attached to a owner");
333
334   node->clearChildren();
335
336   const YGConfigRef config = node->getConfig();
337   *node = YGNode();
338   if (config->useWebDefaults) {
339     node->setStyleFlexDirection(YGFlexDirectionRow);
340     node->setStyleAlignContent(YGAlignStretch);
341   }
342   node->setConfig(config);
343 }
344
345 int32_t YGNodeGetInstanceCount(void) {
346   return gNodeInstanceCount;
347 }
348
349 int32_t YGConfigGetInstanceCount(void) {
350   return gConfigInstanceCount;
351 }
352
353 YGConfigRef YGConfigNew(void) {
354   #ifdef ANDROID
355   const YGConfigRef config = new YGConfig(YGAndroidLog);
356   #else
357   const YGConfigRef config = new YGConfig(YGDefaultLog);
358   #endif
359   gConfigInstanceCount++;
360   return config;
361 }
362
363 void YGConfigFree(const YGConfigRef config) {
364   free(config);
365   gConfigInstanceCount--;
366 }
367
368 void YGConfigCopy(const YGConfigRef dest, const YGConfigRef src) {
369   memcpy(dest, src, sizeof(YGConfig));
370 }
371
372 void YGNodeInsertChild(const YGNodeRef node, const YGNodeRef child, const uint32_t index) {
373   YGAssertWithNode(
374       node,
375       child->getOwner() == nullptr,
376       "Child already has a owner, it must be removed first.");
377
378   YGAssertWithNode(
379       node,
380       node->getMeasure() == nullptr,
381       "Cannot add child: Nodes with measure functions cannot have children.");
382
383   node->cloneChildrenIfNeeded();
384   node->insertChild(child, index);
385   YGNodeRef owner = child->getOwner() ? nullptr : node;
386   child->setOwner(owner);
387   node->markDirtyAndPropogate();
388 }
389
390 void YGNodeInsertSharedChild(
391     const YGNodeRef node,
392     const YGNodeRef child,
393     const uint32_t index) {
394   YGAssertWithNode(
395       node,
396       node->getMeasure() == nullptr,
397       "Cannot add child: Nodes with measure functions cannot have children.");
398
399   node->insertChild(child, index);
400   child->setOwner(nullptr);
401   node->markDirtyAndPropogate();
402 }
403
404 void YGNodeRemoveChild(const YGNodeRef owner, const YGNodeRef excludedChild) {
405   // This algorithm is a forked variant from cloneChildrenIfNeeded in YGNode
406   // that excludes a child.
407   const uint32_t childCount = YGNodeGetChildCount(owner);
408
409   if (childCount == 0) {
410     // This is an empty set. Nothing to remove.
411     return;
412   }
413   const YGNodeRef firstChild = YGNodeGetChild(owner, 0);
414   if (firstChild->getOwner() == owner) {
415     // If the first child has this node as its owner, we assume that it is already unique.
416     // We can now try to delete a child in this list.
417     if (owner->removeChild(excludedChild)) {
418       excludedChild->setLayout(
419           YGNode().getLayout()); // layout is no longer valid
420       excludedChild->setOwner(nullptr);
421       owner->markDirtyAndPropogate();
422     }
423     return;
424   }
425   // Otherwise we have to clone the node list except for the child we're trying to delete.
426   // We don't want to simply clone all children, because then the host will need to free
427   // the clone of the child that was just deleted.
428   const YGCloneNodeFunc cloneNodeCallback =
429       owner->getConfig()->cloneNodeCallback;
430   uint32_t nextInsertIndex = 0;
431   for (uint32_t i = 0; i < childCount; i++) {
432     const YGNodeRef oldChild = owner->getChild(i);
433     if (excludedChild == oldChild) {
434       // Ignore the deleted child. Don't reset its layout or owner since it is still valid
435       // in the other owner. However, since this owner has now changed, we need to mark it
436       // as dirty.
437       owner->markDirtyAndPropogate();
438       continue;
439     }
440     YGNodeRef newChild = nullptr;
441     if (cloneNodeCallback) {
442       newChild = cloneNodeCallback(oldChild, owner, nextInsertIndex);
443     }
444     if (newChild == nullptr) {
445       newChild = YGNodeClone(oldChild);
446     }
447     owner->replaceChild(newChild, nextInsertIndex);
448     newChild->setOwner(owner);
449
450     nextInsertIndex++;
451   }
452   while (nextInsertIndex < childCount) {
453     owner->removeChild(nextInsertIndex);
454     nextInsertIndex++;
455   }
456 }
457
458 void YGNodeRemoveAllChildren(const YGNodeRef owner) {
459   const uint32_t childCount = YGNodeGetChildCount(owner);
460   if (childCount == 0) {
461     // This is an empty set already. Nothing to do.
462     return;
463   }
464   const YGNodeRef firstChild = YGNodeGetChild(owner, 0);
465   if (firstChild->getOwner() == owner) {
466     // If the first child has this node as its owner, we assume that this child set is unique.
467     for (uint32_t i = 0; i < childCount; i++) {
468       const YGNodeRef oldChild = YGNodeGetChild(owner, i);
469       oldChild->setLayout(YGNode().getLayout()); // layout is no longer valid
470       oldChild->setOwner(nullptr);
471     }
472     owner->clearChildren();
473     owner->markDirtyAndPropogate();
474     return;
475   }
476   // Otherwise, we are not the owner of the child set. We don't have to do anything to clear it.
477   owner->setChildren(YGVector());
478   owner->markDirtyAndPropogate();
479 }
480
481 static void YGNodeSetChildrenInternal(YGNodeRef const owner, const std::vector<YGNodeRef> &children)
482 {
483   if (!owner) {
484     return;
485   }
486   if (children.size() == 0) {
487     if (YGNodeGetChildCount(owner) > 0) {
488       for (YGNodeRef const child : owner->getChildren()) {
489         child->setLayout(YGLayout());
490         child->setOwner(nullptr);
491       }
492       owner->setChildren(YGVector());
493       owner->markDirtyAndPropogate();
494     }
495   } else {
496     if (YGNodeGetChildCount(owner) > 0) {
497       for (YGNodeRef const oldChild : owner->getChildren()) {
498         // Our new children may have nodes in common with the old children. We don't reset these common nodes.
499         if (std::find(children.begin(), children.end(), oldChild) == children.end()) {
500           oldChild->setLayout(YGLayout());
501           oldChild->setOwner(nullptr);
502         }
503       }
504     }
505     owner->setChildren(children);
506     for (YGNodeRef child : children) {
507       child->setOwner(owner);
508     }
509     owner->markDirtyAndPropogate();
510   }
511 }
512
513 void YGNodeSetChildren(YGNodeRef const owner, const YGNodeRef c[], const uint32_t count) {
514   const YGVector children = {c, c + count};
515   YGNodeSetChildrenInternal(owner, children);
516 }
517
518 void YGNodeSetChildren(YGNodeRef const owner, const std::vector<YGNodeRef> &children)
519 {
520   YGNodeSetChildrenInternal(owner, children);
521 }
522
523 YGNodeRef YGNodeGetChild(const YGNodeRef node, const uint32_t index) {
524   if (index < node->getChildren().size()) {
525     return node->getChild(index);
526   }
527   return nullptr;
528 }
529
530 uint32_t YGNodeGetChildCount(const YGNodeRef node) {
531   return static_cast<uint32_t>(node->getChildren().size());
532 }
533
534 YGNodeRef YGNodeGetOwner(const YGNodeRef node) {
535   return node->getOwner();
536 }
537
538 void YGNodeMarkDirty(const YGNodeRef node) {
539   YGAssertWithNode(
540       node,
541       node->getMeasure() != nullptr,
542       "Only leaf nodes with custom measure functions"
543       "should manually mark themselves as dirty");
544
545   node->markDirtyAndPropogate();
546 }
547
548 void YGNodeCopyStyle(const YGNodeRef dstNode, const YGNodeRef srcNode) {
549   if (!(dstNode->getStyle() == srcNode->getStyle())) {
550     dstNode->setStyle(srcNode->getStyle());
551     dstNode->markDirtyAndPropogate();
552   }
553 }
554
555 float YGNodeStyleGetFlexGrow(const YGNodeRef node) {
556   return node->getStyle().flexGrow.isUndefined()
557       ? kDefaultFlexGrow
558       : node->getStyle().flexGrow.getValue();
559 }
560
561 float YGNodeStyleGetFlexShrink(const YGNodeRef node) {
562   return node->getStyle().flexShrink.isUndefined()
563       ? (node->getConfig()->useWebDefaults ? kWebDefaultFlexShrink
564                                            : kDefaultFlexShrink)
565       : node->getStyle().flexShrink.getValue();
566 }
567
568 #define YG_NODE_STYLE_PROPERTY_SETTER_IMPL(                               \
569     type, name, paramName, instanceName)                                  \
570   void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \
571     if (node->getStyle().instanceName != paramName) {                     \
572       YGStyle style = node->getStyle();                                   \
573       style.instanceName = paramName;                                     \
574       node->setStyle(style);                                              \
575       node->markDirtyAndPropogate();                                      \
576     }                                                                     \
577   }
578
579 #define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(                          \
580     type, name, paramName, instanceName)                                  \
581   void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) { \
582     YGValue value = {                                                     \
583         YGFloatSanitize(paramName),                                       \
584         YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint,    \
585     };                                                                    \
586     if ((node->getStyle().instanceName.value != value.value &&            \
587          value.unit != YGUnitUndefined) ||                                \
588         node->getStyle().instanceName.unit != value.unit) {               \
589       YGStyle style = node->getStyle();                                   \
590       style.instanceName = value;                                         \
591       node->setStyle(style);                                              \
592       node->markDirtyAndPropogate();                                      \
593     }                                                                     \
594   }                                                                       \
595                                                                           \
596   void YGNodeStyleSet##name##Percent(                                     \
597       const YGNodeRef node, const type paramName) {                       \
598     YGValue value = {                                                     \
599         YGFloatSanitize(paramName),                                       \
600         YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent,  \
601     };                                                                    \
602     if ((node->getStyle().instanceName.value != value.value &&            \
603          value.unit != YGUnitUndefined) ||                                \
604         node->getStyle().instanceName.unit != value.unit) {               \
605       YGStyle style = node->getStyle();                                   \
606                                                                           \
607       style.instanceName = value;                                         \
608       node->setStyle(style);                                              \
609       node->markDirtyAndPropogate();                                      \
610     }                                                                     \
611   }
612
613 #define YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(                        \
614     type, name, paramName, instanceName)                                     \
615   void YGNodeStyleSet##name(const YGNodeRef node, const type paramName) {    \
616     YGValue value = {                                                        \
617         YGFloatSanitize(paramName),                                          \
618         YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint,       \
619     };                                                                       \
620     if ((node->getStyle().instanceName.value != value.value &&               \
621          value.unit != YGUnitUndefined) ||                                   \
622         node->getStyle().instanceName.unit != value.unit) {                  \
623       YGStyle style = node->getStyle();                                      \
624       style.instanceName = value;                                            \
625       node->setStyle(style);                                                 \
626       node->markDirtyAndPropogate();                                         \
627     }                                                                        \
628   }                                                                          \
629                                                                              \
630   void YGNodeStyleSet##name##Percent(                                        \
631       const YGNodeRef node, const type paramName) {                          \
632     if (node->getStyle().instanceName.value != YGFloatSanitize(paramName) || \
633         node->getStyle().instanceName.unit != YGUnitPercent) {               \
634       YGStyle style = node->getStyle();                                      \
635       style.instanceName.value = YGFloatSanitize(paramName);                 \
636       style.instanceName.unit =                                              \
637           YGFloatIsUndefined(paramName) ? YGUnitAuto : YGUnitPercent;        \
638       node->setStyle(style);                                                 \
639       node->markDirtyAndPropogate();                                         \
640     }                                                                        \
641   }                                                                          \
642                                                                              \
643   void YGNodeStyleSet##name##Auto(const YGNodeRef node) {                    \
644     if (node->getStyle().instanceName.unit != YGUnitAuto) {                  \
645       YGStyle style = node->getStyle();                                      \
646       style.instanceName.value = 0;                                          \
647       style.instanceName.unit = YGUnitAuto;                                  \
648       node->setStyle(style);                                                 \
649       node->markDirtyAndPropogate();                                         \
650     }                                                                        \
651   }
652
653 #define YG_NODE_STYLE_PROPERTY_IMPL(type, name, paramName, instanceName)  \
654   YG_NODE_STYLE_PROPERTY_SETTER_IMPL(type, name, paramName, instanceName) \
655                                                                           \
656   type YGNodeStyleGet##name(const YGNodeRef node) {                       \
657     return node->getStyle().instanceName;                                 \
658   }
659
660 #define YG_NODE_STYLE_PROPERTY_UNIT_IMPL(type, name, paramName, instanceName) \
661   YG_NODE_STYLE_PROPERTY_SETTER_UNIT_IMPL(                                    \
662       float, name, paramName, instanceName)                                   \
663                                                                               \
664   type YGNodeStyleGet##name(const YGNodeRef node) {                           \
665     YGValue value = node->getStyle().instanceName;                            \
666     if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) {          \
667       value.value = YGUndefined;                                              \
668     }                                                                         \
669     return value;                                                             \
670   }
671
672 #define YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(                       \
673     type, name, paramName, instanceName)                             \
674   YG_NODE_STYLE_PROPERTY_SETTER_UNIT_AUTO_IMPL(                      \
675       float, name, paramName, instanceName)                          \
676                                                                      \
677   type YGNodeStyleGet##name(const YGNodeRef node) {                  \
678     YGValue value = node->getStyle().instanceName;                   \
679     if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) { \
680       value.value = YGUndefined;                                     \
681     }                                                                \
682     return value;                                                    \
683   }
684
685 #define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(type, name, instanceName) \
686   void YGNodeStyleSet##name##Auto(const YGNodeRef node, const YGEdge edge) { \
687     if (node->getStyle().instanceName[edge].unit != YGUnitAuto) {            \
688       YGStyle style = node->getStyle();                                      \
689       style.instanceName[edge].value = 0;                                    \
690       style.instanceName[edge].unit = YGUnitAuto;                            \
691       node->setStyle(style);                                                 \
692       node->markDirtyAndPropogate();                                         \
693     }                                                                        \
694   }
695
696 #define YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(                           \
697     type, name, paramName, instanceName)                                 \
698   void YGNodeStyleSet##name(                                             \
699       const YGNodeRef node, const YGEdge edge, const float paramName) {  \
700     YGValue value = {                                                    \
701         YGFloatSanitize(paramName),                                      \
702         YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPoint,   \
703     };                                                                   \
704     if ((node->getStyle().instanceName[edge].value != value.value &&     \
705          value.unit != YGUnitUndefined) ||                               \
706         node->getStyle().instanceName[edge].unit != value.unit) {        \
707       YGStyle style = node->getStyle();                                  \
708       style.instanceName[edge] = value;                                  \
709       node->setStyle(style);                                             \
710       node->markDirtyAndPropogate();                                     \
711     }                                                                    \
712   }                                                                      \
713                                                                          \
714   void YGNodeStyleSet##name##Percent(                                    \
715       const YGNodeRef node, const YGEdge edge, const float paramName) {  \
716     YGValue value = {                                                    \
717         YGFloatSanitize(paramName),                                      \
718         YGFloatIsUndefined(paramName) ? YGUnitUndefined : YGUnitPercent, \
719     };                                                                   \
720     if ((node->getStyle().instanceName[edge].value != value.value &&     \
721          value.unit != YGUnitUndefined) ||                               \
722         node->getStyle().instanceName[edge].unit != value.unit) {        \
723       YGStyle style = node->getStyle();                                  \
724       style.instanceName[edge] = value;                                  \
725       node->setStyle(style);                                             \
726       node->markDirtyAndPropogate();                                     \
727     }                                                                    \
728   }                                                                      \
729                                                                          \
730   WIN_STRUCT(type)                                                       \
731   YGNodeStyleGet##name(const YGNodeRef node, const YGEdge edge) {        \
732     YGValue value = node->getStyle().instanceName[edge];                 \
733     if (value.unit == YGUnitUndefined || value.unit == YGUnitAuto) {     \
734       value.value = YGUndefined;                                         \
735     }                                                                    \
736     return WIN_STRUCT_REF(value);                                        \
737   }
738
739 #define YG_NODE_LAYOUT_PROPERTY_IMPL(type, name, instanceName) \
740   type YGNodeLayoutGet##name(const YGNodeRef node) {           \
741     return node->getLayout().instanceName;                     \
742   }
743
744 #define YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(type, name, instanceName) \
745   type YGNodeLayoutGet##name(const YGNodeRef node, const YGEdge edge) { \
746     YGAssertWithNode(                                                   \
747         node,                                                           \
748         edge <= YGEdgeEnd,                                              \
749         "Cannot get layout properties of multi-edge shorthands");       \
750                                                                         \
751     if (edge == YGEdgeLeft) {                                           \
752       if (node->getLayout().direction == YGDirectionRTL) {              \
753         return node->getLayout().instanceName[YGEdgeEnd];               \
754       } else {                                                          \
755         return node->getLayout().instanceName[YGEdgeStart];             \
756       }                                                                 \
757     }                                                                   \
758                                                                         \
759     if (edge == YGEdgeRight) {                                          \
760       if (node->getLayout().direction == YGDirectionRTL) {              \
761         return node->getLayout().instanceName[YGEdgeStart];             \
762       } else {                                                          \
763         return node->getLayout().instanceName[YGEdgeEnd];               \
764       }                                                                 \
765     }                                                                   \
766                                                                         \
767     return node->getLayout().instanceName[edge];                        \
768   }
769
770 // YG_NODE_PROPERTY_IMPL(void *, Context, context, context);
771 // YG_NODE_PROPERTY_IMPL(YGPrintFunc, PrintFunc, printFunc, print);
772 // YG_NODE_PROPERTY_IMPL(bool, HasNewLayout, hasNewLayout, hasNewLayout);
773 // YG_NODE_PROPERTY_IMPL(YGNodeType, NodeType, nodeType, nodeType);
774
775 YG_NODE_STYLE_PROPERTY_IMPL(YGDirection, Direction, direction, direction);
776 YG_NODE_STYLE_PROPERTY_IMPL(YGFlexDirection, FlexDirection, flexDirection, flexDirection);
777 YG_NODE_STYLE_PROPERTY_IMPL(YGJustify, JustifyContent, justifyContent, justifyContent);
778 YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignContent, alignContent, alignContent);
779 YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignItems, alignItems, alignItems);
780 YG_NODE_STYLE_PROPERTY_IMPL(YGAlign, AlignSelf, alignSelf, alignSelf);
781 YG_NODE_STYLE_PROPERTY_IMPL(YGPositionType, PositionType, positionType, positionType);
782 YG_NODE_STYLE_PROPERTY_IMPL(YGWrap, FlexWrap, flexWrap, flexWrap);
783 YG_NODE_STYLE_PROPERTY_IMPL(YGOverflow, Overflow, overflow, overflow);
784 YG_NODE_STYLE_PROPERTY_IMPL(YGDisplay, Display, display, display);
785
786 // TODO(T26792433): Change the API to accept YGFloatOptional.
787 void YGNodeStyleSetFlex(const YGNodeRef node, const float flex) {
788   if (node->getStyle().flex != flex) {
789     YGStyle style = node->getStyle();
790     if (YGFloatIsUndefined(flex)) {
791       style.flex = YGFloatOptional();
792     } else {
793       style.flex = YGFloatOptional(flex);
794     }
795     node->setStyle(style);
796     node->markDirtyAndPropogate();
797   }
798 }
799
800 // TODO(T26792433): Change the API to accept YGFloatOptional.
801 float YGNodeStyleGetFlex(const YGNodeRef node) {
802   return node->getStyle().flex.isUndefined() ? YGUndefined
803                                              : node->getStyle().flex.getValue();
804 }
805
806 // TODO(T26792433): Change the API to accept YGFloatOptional.
807 void YGNodeStyleSetFlexGrow(const YGNodeRef node, const float flexGrow) {
808   if (node->getStyle().flexGrow != flexGrow) {
809     YGStyle style = node->getStyle();
810     if (YGFloatIsUndefined(flexGrow)) {
811       style.flexGrow = YGFloatOptional();
812     } else {
813       style.flexGrow = YGFloatOptional(flexGrow);
814     }
815     node->setStyle(style);
816     node->markDirtyAndPropogate();
817   }
818 }
819
820 // TODO(T26792433): Change the API to accept YGFloatOptional.
821 void YGNodeStyleSetFlexShrink(const YGNodeRef node, const float flexShrink) {
822   if (node->getStyle().flexShrink != flexShrink) {
823     YGStyle style = node->getStyle();
824     if (YGFloatIsUndefined(flexShrink)) {
825       style.flexShrink = YGFloatOptional();
826     } else {
827       style.flexShrink = YGFloatOptional(flexShrink);
828     }
829     node->setStyle(style);
830     node->markDirtyAndPropogate();
831   }
832 }
833
834 YGValue YGNodeStyleGetFlexBasis(const YGNodeRef node) {
835   YGValue flexBasis = node->getStyle().flexBasis;
836   if (flexBasis.unit == YGUnitUndefined || flexBasis.unit == YGUnitAuto) {
837     // TODO(T26792433): Get rid off the use of YGUndefined at client side
838     flexBasis.value = YGUndefined;
839   }
840   return flexBasis;
841 }
842
843 void YGNodeStyleSetFlexBasis(const YGNodeRef node, const float flexBasis) {
844   YGValue value = {
845       YGFloatSanitize(flexBasis),
846       YGFloatIsUndefined(flexBasis) ? YGUnitUndefined : YGUnitPoint,
847   };
848   if ((node->getStyle().flexBasis.value != value.value &&
849        value.unit != YGUnitUndefined) ||
850       node->getStyle().flexBasis.unit != value.unit) {
851     YGStyle style = node->getStyle();
852     style.flexBasis = value;
853     node->setStyle(style);
854     node->markDirtyAndPropogate();
855   }
856 }
857
858 void YGNodeStyleSetFlexBasisPercent(
859     const YGNodeRef node,
860     const float flexBasisPercent) {
861   if (node->getStyle().flexBasis.value != flexBasisPercent ||
862       node->getStyle().flexBasis.unit != YGUnitPercent) {
863     YGStyle style = node->getStyle();
864     style.flexBasis.value = YGFloatSanitize(flexBasisPercent);
865     style.flexBasis.unit =
866         YGFloatIsUndefined(flexBasisPercent) ? YGUnitAuto : YGUnitPercent;
867     node->setStyle(style);
868     node->markDirtyAndPropogate();
869   }
870 }
871
872 void YGNodeStyleSetFlexBasisAuto(const YGNodeRef node) {
873   if (node->getStyle().flexBasis.unit != YGUnitAuto) {
874     YGStyle style = node->getStyle();
875     style.flexBasis.value = 0;
876     style.flexBasis.unit = YGUnitAuto;
877     node->setStyle(style);
878     node->markDirtyAndPropogate();
879   }
880 }
881
882 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Position, position, position);
883 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Margin, margin, margin);
884 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Margin, margin);
885 YG_NODE_STYLE_EDGE_PROPERTY_UNIT_IMPL(YGValue, Padding, padding, padding);
886
887 // TODO(T26792433): Change the API to accept YGFloatOptional.
888 void YGNodeStyleSetBorder(
889     const YGNodeRef node,
890     const YGEdge edge,
891     const float border) {
892   YGValue value = {
893       YGFloatSanitize(border),
894       YGFloatIsUndefined(border) ? YGUnitUndefined : YGUnitPoint,
895   };
896   if ((node->getStyle().border[edge].value != value.value &&
897        value.unit != YGUnitUndefined) ||
898       node->getStyle().border[edge].unit != value.unit) {
899     YGStyle style = node->getStyle();
900     style.border[edge] = value;
901     node->setStyle(style);
902     node->markDirtyAndPropogate();
903   }
904 }
905
906 float YGNodeStyleGetBorder(const YGNodeRef node, const YGEdge edge) {
907   if (node->getStyle().border[edge].unit == YGUnitUndefined ||
908       node->getStyle().border[edge].unit == YGUnitAuto) {
909     // TODO(T26792433): Rather than returning YGUndefined, change the api to
910     // return YGFloatOptional.
911     return YGUndefined;
912   }
913
914   return node->getStyle().border[edge].value;
915 }
916
917 // Yoga specific properties, not compatible with flexbox specification
918
919 // TODO(T26792433): Change the API to accept YGFloatOptional.
920 float YGNodeStyleGetAspectRatio(const YGNodeRef node) {
921   const YGFloatOptional op = node->getStyle().aspectRatio;
922   return op.isUndefined() ? YGUndefined : op.getValue();
923 }
924
925 // TODO(T26792433): Change the API to accept YGFloatOptional.
926 void YGNodeStyleSetAspectRatio(const YGNodeRef node, const float aspectRatio) {
927   if (node->getStyle().aspectRatio != aspectRatio) {
928     YGStyle style = node->getStyle();
929     style.aspectRatio = YGFloatOptional(aspectRatio);
930     node->setStyle(style);
931     node->markDirtyAndPropogate();
932   }
933 }
934
935 YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Width, width, dimensions[YGDimensionWidth]);
936 YG_NODE_STYLE_PROPERTY_UNIT_AUTO_IMPL(YGValue, Height, height, dimensions[YGDimensionHeight]);
937 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinWidth, minWidth, minDimensions[YGDimensionWidth]);
938 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MinHeight, minHeight, minDimensions[YGDimensionHeight]);
939 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxWidth, maxWidth, maxDimensions[YGDimensionWidth]);
940 YG_NODE_STYLE_PROPERTY_UNIT_IMPL(YGValue, MaxHeight, maxHeight, maxDimensions[YGDimensionHeight]);
941 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Left, position[YGEdgeLeft]);
942 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Top, position[YGEdgeTop]);
943 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Right, position[YGEdgeRight]);
944 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Bottom, position[YGEdgeBottom]);
945 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Width, dimensions[YGDimensionWidth]);
946 YG_NODE_LAYOUT_PROPERTY_IMPL(float, Height, dimensions[YGDimensionHeight]);
947 YG_NODE_LAYOUT_PROPERTY_IMPL(YGDirection, Direction, direction);
948 YG_NODE_LAYOUT_PROPERTY_IMPL(bool, HadOverflow, hadOverflow);
949
950 YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Margin, margin);
951 YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Border, border);
952 YG_NODE_LAYOUT_RESOLVED_PROPERTY_IMPL(float, Padding, padding);
953
954 bool YGNodeLayoutGetDidLegacyStretchFlagAffectLayout(const YGNodeRef node) {
955   return node->getLayout().doesLegacyStretchFlagAffectsLayout;
956 }
957
958 uint32_t gCurrentGenerationCount = 0;
959
960 bool YGLayoutNodeInternal(const YGNodeRef node,
961                           const float availableWidth,
962                           const float availableHeight,
963                           const YGDirection ownerDirection,
964                           const YGMeasureMode widthMeasureMode,
965                           const YGMeasureMode heightMeasureMode,
966                           const float ownerWidth,
967                           const float ownerHeight,
968                           const bool performLayout,
969                           const char *reason,
970                           const YGConfigRef config);
971
972 static void YGNodePrintInternal(const YGNodeRef node,
973                                 const YGPrintOptions options) {
974   std::string str;
975   facebook::yoga::YGNodeToString(&str, node, options, 0);
976   YGLog(node, YGLogLevelDebug, str.c_str());
977 }
978
979 void YGNodePrint(const YGNodeRef node, const YGPrintOptions options) {
980   YGNodePrintInternal(node, options);
981 }
982
983 const std::array<YGEdge, 4> leading = {
984     {YGEdgeTop, YGEdgeBottom, YGEdgeLeft, YGEdgeRight}};
985
986 const std::array<YGEdge, 4> trailing = {
987     {YGEdgeBottom, YGEdgeTop, YGEdgeRight, YGEdgeLeft}};
988 static const std::array<YGEdge, 4> pos = {{
989     YGEdgeTop,
990     YGEdgeBottom,
991     YGEdgeLeft,
992     YGEdgeRight,
993 }};
994
995 static const std::array<YGDimension, 4> dim = {
996     {YGDimensionHeight, YGDimensionHeight, YGDimensionWidth, YGDimensionWidth}};
997
998 static inline float YGNodePaddingAndBorderForAxis(const YGNodeRef node,
999                                                   const YGFlexDirection axis,
1000                                                   const float widthSize) {
1001   return YGUnwrapFloatOptional(
1002       node->getLeadingPaddingAndBorder(axis, widthSize) +
1003       node->getTrailingPaddingAndBorder(axis, widthSize));
1004 }
1005
1006 static inline YGAlign YGNodeAlignItem(const YGNodeRef node, const YGNodeRef child) {
1007   const YGAlign align = child->getStyle().alignSelf == YGAlignAuto
1008       ? node->getStyle().alignItems
1009       : child->getStyle().alignSelf;
1010   if (align == YGAlignBaseline &&
1011       YGFlexDirectionIsColumn(node->getStyle().flexDirection)) {
1012     return YGAlignFlexStart;
1013   }
1014   return align;
1015 }
1016
1017 static float YGBaseline(const YGNodeRef node) {
1018   if (node->getBaseline() != nullptr) {
1019     const float baseline = node->getBaseline()(
1020         node,
1021         node->getLayout().measuredDimensions[YGDimensionWidth],
1022         node->getLayout().measuredDimensions[YGDimensionHeight]);
1023     YGAssertWithNode(node,
1024                      !YGFloatIsUndefined(baseline),
1025                      "Expect custom baseline function to not return NaN");
1026     return baseline;
1027   }
1028
1029   YGNodeRef baselineChild = nullptr;
1030   const uint32_t childCount = YGNodeGetChildCount(node);
1031   for (uint32_t i = 0; i < childCount; i++) {
1032     const YGNodeRef child = YGNodeGetChild(node, i);
1033     if (child->getLineIndex() > 0) {
1034       break;
1035     }
1036     if (child->getStyle().positionType == YGPositionTypeAbsolute) {
1037       continue;
1038     }
1039     if (YGNodeAlignItem(node, child) == YGAlignBaseline) {
1040       baselineChild = child;
1041       break;
1042     }
1043
1044     if (baselineChild == nullptr) {
1045       baselineChild = child;
1046     }
1047   }
1048
1049   if (baselineChild == nullptr) {
1050     return node->getLayout().measuredDimensions[YGDimensionHeight];
1051   }
1052
1053   const float baseline = YGBaseline(baselineChild);
1054   return baseline + baselineChild->getLayout().position[YGEdgeTop];
1055 }
1056
1057 static bool YGIsBaselineLayout(const YGNodeRef node) {
1058   if (YGFlexDirectionIsColumn(node->getStyle().flexDirection)) {
1059     return false;
1060   }
1061   if (node->getStyle().alignItems == YGAlignBaseline) {
1062     return true;
1063   }
1064   const uint32_t childCount = YGNodeGetChildCount(node);
1065   for (uint32_t i = 0; i < childCount; i++) {
1066     const YGNodeRef child = YGNodeGetChild(node, i);
1067     if (child->getStyle().positionType == YGPositionTypeRelative &&
1068         child->getStyle().alignSelf == YGAlignBaseline) {
1069       return true;
1070     }
1071   }
1072
1073   return false;
1074 }
1075
1076 static inline float YGNodeDimWithMargin(const YGNodeRef node,
1077                                         const YGFlexDirection axis,
1078                                         const float widthSize) {
1079   return node->getLayout().measuredDimensions[dim[axis]] +
1080       YGUnwrapFloatOptional(
1081              node->getLeadingMargin(axis, widthSize) +
1082              node->getTrailingMargin(axis, widthSize));
1083 }
1084
1085 static inline bool YGNodeIsStyleDimDefined(const YGNodeRef node,
1086                                            const YGFlexDirection axis,
1087                                            const float ownerSize) {
1088   bool isUndefined =
1089       YGFloatIsUndefined(node->getResolvedDimension(dim[axis]).value);
1090   return !(
1091       node->getResolvedDimension(dim[axis]).unit == YGUnitAuto ||
1092       node->getResolvedDimension(dim[axis]).unit == YGUnitUndefined ||
1093       (node->getResolvedDimension(dim[axis]).unit == YGUnitPoint &&
1094        !isUndefined && node->getResolvedDimension(dim[axis]).value < 0.0f) ||
1095       (node->getResolvedDimension(dim[axis]).unit == YGUnitPercent &&
1096        !isUndefined &&
1097        (node->getResolvedDimension(dim[axis]).value < 0.0f ||
1098         YGFloatIsUndefined(ownerSize))));
1099 }
1100
1101 static inline bool YGNodeIsLayoutDimDefined(const YGNodeRef node, const YGFlexDirection axis) {
1102   const float value = node->getLayout().measuredDimensions[dim[axis]];
1103   return !YGFloatIsUndefined(value) && value >= 0.0f;
1104 }
1105
1106 static YGFloatOptional YGNodeBoundAxisWithinMinAndMax(
1107     const YGNodeRef node,
1108     const YGFlexDirection& axis,
1109     const float& value,
1110     const float& axisSize) {
1111   YGFloatOptional min;
1112   YGFloatOptional max;
1113
1114   if (YGFlexDirectionIsColumn(axis)) {
1115     min = YGResolveValue(
1116         node->getStyle().minDimensions[YGDimensionHeight], axisSize);
1117     max = YGResolveValue(
1118         node->getStyle().maxDimensions[YGDimensionHeight], axisSize);
1119   } else if (YGFlexDirectionIsRow(axis)) {
1120     min = YGResolveValue(
1121         node->getStyle().minDimensions[YGDimensionWidth], axisSize);
1122     max = YGResolveValue(
1123         node->getStyle().maxDimensions[YGDimensionWidth], axisSize);
1124   }
1125
1126   if (!max.isUndefined() && max.getValue() >= 0 && value > max.getValue()) {
1127     return max;
1128   }
1129
1130   if (!min.isUndefined() && min.getValue() >= 0 && value < min.getValue()) {
1131     return min;
1132   }
1133
1134   return YGFloatOptional(value);
1135 }
1136
1137 // Like YGNodeBoundAxisWithinMinAndMax but also ensures that the value doesn't go
1138 // below the
1139 // padding and border amount.
1140 static inline float YGNodeBoundAxis(const YGNodeRef node,
1141                                     const YGFlexDirection axis,
1142                                     const float value,
1143                                     const float axisSize,
1144                                     const float widthSize) {
1145   return YGFloatMax(
1146       YGUnwrapFloatOptional(
1147           YGNodeBoundAxisWithinMinAndMax(node, axis, value, axisSize)),
1148       YGNodePaddingAndBorderForAxis(node, axis, widthSize));
1149 }
1150
1151 static void YGNodeSetChildTrailingPosition(const YGNodeRef node,
1152                                            const YGNodeRef child,
1153                                            const YGFlexDirection axis) {
1154   const float size = child->getLayout().measuredDimensions[dim[axis]];
1155   child->setLayoutPosition(
1156       node->getLayout().measuredDimensions[dim[axis]] - size -
1157           child->getLayout().position[pos[axis]],
1158       trailing[axis]);
1159 }
1160
1161 static void YGConstrainMaxSizeForMode(const YGNodeRef node,
1162                                       const enum YGFlexDirection axis,
1163                                       const float ownerAxisSize,
1164                                       const float ownerWidth,
1165                                       YGMeasureMode *mode,
1166                                       float *size) {
1167   const YGFloatOptional maxSize =
1168       YGResolveValue(
1169           node->getStyle().maxDimensions[dim[axis]], ownerAxisSize) +
1170       YGFloatOptional(node->getMarginForAxis(axis, ownerWidth));
1171   switch (*mode) {
1172     case YGMeasureModeExactly:
1173     case YGMeasureModeAtMost:
1174       *size = (maxSize.isUndefined() || *size < maxSize.getValue())
1175           ? *size
1176           : maxSize.getValue();
1177       break;
1178     case YGMeasureModeUndefined:
1179       if (!maxSize.isUndefined()) {
1180         *mode = YGMeasureModeAtMost;
1181         *size = maxSize.getValue();
1182       }
1183       break;
1184   }
1185 }
1186
1187 static void YGNodeComputeFlexBasisForChild(const YGNodeRef node,
1188                                            const YGNodeRef child,
1189                                            const float width,
1190                                            const YGMeasureMode widthMode,
1191                                            const float height,
1192                                            const float ownerWidth,
1193                                            const float ownerHeight,
1194                                            const YGMeasureMode heightMode,
1195                                            const YGDirection direction,
1196                                            const YGConfigRef config) {
1197   const YGFlexDirection mainAxis =
1198       YGResolveFlexDirection(node->getStyle().flexDirection, direction);
1199   const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
1200   const float mainAxisSize = isMainAxisRow ? width : height;
1201   const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
1202
1203   float childWidth;
1204   float childHeight;
1205   YGMeasureMode childWidthMeasureMode;
1206   YGMeasureMode childHeightMeasureMode;
1207
1208   const YGFloatOptional resolvedFlexBasis =
1209       YGResolveValue(child->resolveFlexBasisPtr(), mainAxisownerSize);
1210   const bool isRowStyleDimDefined = YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, ownerWidth);
1211   const bool isColumnStyleDimDefined =
1212       YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, ownerHeight);
1213
1214   if (!resolvedFlexBasis.isUndefined() && !YGFloatIsUndefined(mainAxisSize)) {
1215     if (child->getLayout().computedFlexBasis.isUndefined() ||
1216         (YGConfigIsExperimentalFeatureEnabled(
1217              child->getConfig(), YGExperimentalFeatureWebFlexBasis) &&
1218          child->getLayout().computedFlexBasisGeneration !=
1219              gCurrentGenerationCount)) {
1220       const YGFloatOptional& paddingAndBorder = YGFloatOptional(
1221           YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth));
1222       child->setLayoutComputedFlexBasis(
1223           YGFloatOptionalMax(resolvedFlexBasis, paddingAndBorder));
1224     }
1225   } else if (isMainAxisRow && isRowStyleDimDefined) {
1226     // The width is definite, so use that as the flex basis.
1227     const YGFloatOptional& paddingAndBorder = YGFloatOptional(
1228         YGNodePaddingAndBorderForAxis(child, YGFlexDirectionRow, ownerWidth));
1229
1230     child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
1231         YGResolveValue(
1232             child->getResolvedDimension(YGDimensionWidth), ownerWidth),
1233         paddingAndBorder));
1234   } else if (!isMainAxisRow && isColumnStyleDimDefined) {
1235     // The height is definite, so use that as the flex basis.
1236     const YGFloatOptional& paddingAndBorder =
1237         YGFloatOptional(YGNodePaddingAndBorderForAxis(
1238             child, YGFlexDirectionColumn, ownerWidth));
1239     child->setLayoutComputedFlexBasis(YGFloatOptionalMax(
1240         YGResolveValue(
1241             child->getResolvedDimension(YGDimensionHeight), ownerHeight),
1242         paddingAndBorder));
1243   } else {
1244     // Compute the flex basis and hypothetical main size (i.e. the clamped
1245     // flex basis).
1246     childWidth = YGUndefined;
1247     childHeight = YGUndefined;
1248     childWidthMeasureMode = YGMeasureModeUndefined;
1249     childHeightMeasureMode = YGMeasureModeUndefined;
1250
1251     const float& marginRow = YGUnwrapFloatOptional(
1252         child->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
1253     const float& marginColumn = YGUnwrapFloatOptional(
1254         child->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
1255
1256     if (isRowStyleDimDefined) {
1257       childWidth =
1258           YGUnwrapFloatOptional(YGResolveValue(
1259               child->getResolvedDimension(YGDimensionWidth), ownerWidth)) +
1260           marginRow;
1261       childWidthMeasureMode = YGMeasureModeExactly;
1262     }
1263     if (isColumnStyleDimDefined) {
1264       childHeight =
1265           YGUnwrapFloatOptional(YGResolveValue(
1266               child->getResolvedDimension(YGDimensionHeight), ownerHeight)) +
1267           marginColumn;
1268       childHeightMeasureMode = YGMeasureModeExactly;
1269     }
1270
1271     // The W3C spec doesn't say anything about the 'overflow' property,
1272     // but all major browsers appear to implement the following logic.
1273     if ((!isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) ||
1274         node->getStyle().overflow != YGOverflowScroll) {
1275       if (YGFloatIsUndefined(childWidth) && !YGFloatIsUndefined(width)) {
1276         childWidth = width;
1277         childWidthMeasureMode = YGMeasureModeAtMost;
1278       }
1279     }
1280
1281     if ((isMainAxisRow && node->getStyle().overflow == YGOverflowScroll) ||
1282         node->getStyle().overflow != YGOverflowScroll) {
1283       if (YGFloatIsUndefined(childHeight) && !YGFloatIsUndefined(height)) {
1284         childHeight = height;
1285         childHeightMeasureMode = YGMeasureModeAtMost;
1286       }
1287     }
1288
1289     if (!child->getStyle().aspectRatio.isUndefined()) {
1290       if (!isMainAxisRow && childWidthMeasureMode == YGMeasureModeExactly) {
1291         childHeight = marginColumn +
1292             (childWidth - marginRow) / child->getStyle().aspectRatio.getValue();
1293         childHeightMeasureMode = YGMeasureModeExactly;
1294       } else if (isMainAxisRow && childHeightMeasureMode == YGMeasureModeExactly) {
1295         childWidth = marginRow +
1296             (childHeight - marginColumn) *
1297                 child->getStyle().aspectRatio.getValue();
1298         childWidthMeasureMode = YGMeasureModeExactly;
1299       }
1300     }
1301
1302     // If child has no defined size in the cross axis and is set to stretch,
1303     // set the cross
1304     // axis to be measured exactly with the available inner width
1305
1306     const bool hasExactWidth = !YGFloatIsUndefined(width) && widthMode == YGMeasureModeExactly;
1307     const bool childWidthStretch = YGNodeAlignItem(node, child) == YGAlignStretch &&
1308                                    childWidthMeasureMode != YGMeasureModeExactly;
1309     if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && childWidthStretch) {
1310       childWidth = width;
1311       childWidthMeasureMode = YGMeasureModeExactly;
1312       if (!child->getStyle().aspectRatio.isUndefined()) {
1313         childHeight =
1314             (childWidth - marginRow) / child->getStyle().aspectRatio.getValue();
1315         childHeightMeasureMode = YGMeasureModeExactly;
1316       }
1317     }
1318
1319     const bool hasExactHeight = !YGFloatIsUndefined(height) && heightMode == YGMeasureModeExactly;
1320     const bool childHeightStretch = YGNodeAlignItem(node, child) == YGAlignStretch &&
1321                                     childHeightMeasureMode != YGMeasureModeExactly;
1322     if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && childHeightStretch) {
1323       childHeight = height;
1324       childHeightMeasureMode = YGMeasureModeExactly;
1325
1326       if (!child->getStyle().aspectRatio.isUndefined()) {
1327         childWidth = (childHeight - marginColumn) *
1328             child->getStyle().aspectRatio.getValue();
1329         childWidthMeasureMode = YGMeasureModeExactly;
1330       }
1331     }
1332
1333     YGConstrainMaxSizeForMode(
1334         child, YGFlexDirectionRow, ownerWidth, ownerWidth, &childWidthMeasureMode, &childWidth);
1335     YGConstrainMaxSizeForMode(child,
1336                               YGFlexDirectionColumn,
1337                               ownerHeight,
1338                               ownerWidth,
1339                               &childHeightMeasureMode,
1340                               &childHeight);
1341
1342     // Measure the child
1343     YGLayoutNodeInternal(child,
1344                          childWidth,
1345                          childHeight,
1346                          direction,
1347                          childWidthMeasureMode,
1348                          childHeightMeasureMode,
1349                          ownerWidth,
1350                          ownerHeight,
1351                          false,
1352                          "measure",
1353                          config);
1354
1355     child->setLayoutComputedFlexBasis(YGFloatOptional(YGFloatMax(
1356         child->getLayout().measuredDimensions[dim[mainAxis]],
1357         YGNodePaddingAndBorderForAxis(child, mainAxis, ownerWidth))));
1358   }
1359   child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount);
1360 }
1361
1362 static void YGNodeAbsoluteLayoutChild(const YGNodeRef node,
1363                                       const YGNodeRef child,
1364                                       const float width,
1365                                       const YGMeasureMode widthMode,
1366                                       const float height,
1367                                       const YGDirection direction,
1368                                       const YGConfigRef config) {
1369   const YGFlexDirection mainAxis =
1370       YGResolveFlexDirection(node->getStyle().flexDirection, direction);
1371   const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
1372   const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
1373
1374   float childWidth = YGUndefined;
1375   float childHeight = YGUndefined;
1376   YGMeasureMode childWidthMeasureMode = YGMeasureModeUndefined;
1377   YGMeasureMode childHeightMeasureMode = YGMeasureModeUndefined;
1378
1379   const float& marginRow =
1380       YGUnwrapFloatOptional(child->getMarginForAxis(YGFlexDirectionRow, width));
1381   const float& marginColumn = YGUnwrapFloatOptional(
1382       child->getMarginForAxis(YGFlexDirectionColumn, width));
1383
1384   if (YGNodeIsStyleDimDefined(child, YGFlexDirectionRow, width)) {
1385     childWidth =
1386         YGUnwrapFloatOptional(YGResolveValue(child->getResolvedDimension(YGDimensionWidth), width)) +
1387         marginRow;
1388   } else {
1389     // If the child doesn't have a specified width, compute the width based
1390     // on the left/right
1391     // offsets if they're defined.
1392     if (child->isLeadingPositionDefined(YGFlexDirectionRow) &&
1393         child->isTrailingPosDefined(YGFlexDirectionRow)) {
1394       childWidth = node->getLayout().measuredDimensions[YGDimensionWidth] -
1395           (node->getLeadingBorder(YGFlexDirectionRow) +
1396            node->getTrailingBorder(YGFlexDirectionRow)) -
1397           YGUnwrapFloatOptional(
1398                        child->getLeadingPosition(YGFlexDirectionRow, width) +
1399                        child->getTrailingPosition(YGFlexDirectionRow, width));
1400       childWidth = YGNodeBoundAxis(child, YGFlexDirectionRow, childWidth, width, width);
1401     }
1402   }
1403
1404   if (YGNodeIsStyleDimDefined(child, YGFlexDirectionColumn, height)) {
1405     childHeight =
1406         YGUnwrapFloatOptional(YGResolveValue(child->getResolvedDimension(YGDimensionHeight), height)) +
1407         marginColumn;
1408   } else {
1409     // If the child doesn't have a specified height, compute the height
1410     // based on the top/bottom
1411     // offsets if they're defined.
1412     if (child->isLeadingPositionDefined(YGFlexDirectionColumn) &&
1413         child->isTrailingPosDefined(YGFlexDirectionColumn)) {
1414       childHeight =
1415           node->getLayout().measuredDimensions[YGDimensionHeight] -
1416           (node->getLeadingBorder(YGFlexDirectionColumn) +
1417            node->getTrailingBorder(YGFlexDirectionColumn)) -
1418           YGUnwrapFloatOptional(
1419               child->getLeadingPosition(YGFlexDirectionColumn, height) +
1420               child->getTrailingPosition(YGFlexDirectionColumn, height));
1421       childHeight = YGNodeBoundAxis(child, YGFlexDirectionColumn, childHeight, height, width);
1422     }
1423   }
1424
1425   // Exactly one dimension needs to be defined for us to be able to do aspect ratio
1426   // calculation. One dimension being the anchor and the other being flexible.
1427   if (YGFloatIsUndefined(childWidth) ^ YGFloatIsUndefined(childHeight)) {
1428     if (!child->getStyle().aspectRatio.isUndefined()) {
1429       if (YGFloatIsUndefined(childWidth)) {
1430         childWidth = marginRow +
1431             (childHeight - marginColumn) *
1432                 child->getStyle().aspectRatio.getValue();
1433       } else if (YGFloatIsUndefined(childHeight)) {
1434         childHeight = marginColumn +
1435             (childWidth - marginRow) / child->getStyle().aspectRatio.getValue();
1436       }
1437     }
1438   }
1439
1440   // If we're still missing one or the other dimension, measure the content.
1441   if (YGFloatIsUndefined(childWidth) || YGFloatIsUndefined(childHeight)) {
1442     childWidthMeasureMode =
1443         YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined : YGMeasureModeExactly;
1444     childHeightMeasureMode =
1445         YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined : YGMeasureModeExactly;
1446
1447     // If the size of the owner is defined then try to constrain the absolute child to that size
1448     // as well. This allows text within the absolute child to wrap to the size of its owner.
1449     // This is the same behavior as many browsers implement.
1450     if (!isMainAxisRow && YGFloatIsUndefined(childWidth) &&
1451         widthMode != YGMeasureModeUndefined && !YGFloatIsUndefined(width) &&
1452         width > 0) {
1453       childWidth = width;
1454       childWidthMeasureMode = YGMeasureModeAtMost;
1455     }
1456
1457     YGLayoutNodeInternal(child,
1458                          childWidth,
1459                          childHeight,
1460                          direction,
1461                          childWidthMeasureMode,
1462                          childHeightMeasureMode,
1463                          childWidth,
1464                          childHeight,
1465                          false,
1466                          "abs-measure",
1467                          config);
1468     childWidth = child->getLayout().measuredDimensions[YGDimensionWidth] +
1469         YGUnwrapFloatOptional(
1470                      child->getMarginForAxis(YGFlexDirectionRow, width));
1471     childHeight = child->getLayout().measuredDimensions[YGDimensionHeight] +
1472         YGUnwrapFloatOptional(
1473                       child->getMarginForAxis(YGFlexDirectionColumn, width));
1474   }
1475
1476   YGLayoutNodeInternal(child,
1477                        childWidth,
1478                        childHeight,
1479                        direction,
1480                        YGMeasureModeExactly,
1481                        YGMeasureModeExactly,
1482                        childWidth,
1483                        childHeight,
1484                        true,
1485                        "abs-layout",
1486                        config);
1487
1488   if (child->isTrailingPosDefined(mainAxis) &&
1489       !child->isLeadingPositionDefined(mainAxis)) {
1490     child->setLayoutPosition(
1491         node->getLayout().measuredDimensions[dim[mainAxis]] -
1492             child->getLayout().measuredDimensions[dim[mainAxis]] -
1493             node->getTrailingBorder(mainAxis) -
1494             YGUnwrapFloatOptional(child->getTrailingMargin(mainAxis, width)) -
1495             YGUnwrapFloatOptional(child->getTrailingPosition(
1496                 mainAxis, isMainAxisRow ? width : height)),
1497         leading[mainAxis]);
1498   } else if (
1499       !child->isLeadingPositionDefined(mainAxis) &&
1500       node->getStyle().justifyContent == YGJustifyCenter) {
1501     child->setLayoutPosition(
1502         (node->getLayout().measuredDimensions[dim[mainAxis]] -
1503          child->getLayout().measuredDimensions[dim[mainAxis]]) /
1504             2.0f,
1505         leading[mainAxis]);
1506   } else if (
1507       !child->isLeadingPositionDefined(mainAxis) &&
1508       node->getStyle().justifyContent == YGJustifyFlexEnd) {
1509     child->setLayoutPosition(
1510         (node->getLayout().measuredDimensions[dim[mainAxis]] -
1511          child->getLayout().measuredDimensions[dim[mainAxis]]),
1512         leading[mainAxis]);
1513   }
1514
1515   if (child->isTrailingPosDefined(crossAxis) &&
1516       !child->isLeadingPositionDefined(crossAxis)) {
1517     child->setLayoutPosition(
1518         node->getLayout().measuredDimensions[dim[crossAxis]] -
1519             child->getLayout().measuredDimensions[dim[crossAxis]] -
1520             node->getTrailingBorder(crossAxis) -
1521             YGUnwrapFloatOptional(child->getTrailingMargin(crossAxis, width)) -
1522             YGUnwrapFloatOptional(child->getTrailingPosition(
1523                 crossAxis, isMainAxisRow ? height : width)),
1524         leading[crossAxis]);
1525
1526   } else if (
1527       !child->isLeadingPositionDefined(crossAxis) &&
1528       YGNodeAlignItem(node, child) == YGAlignCenter) {
1529     child->setLayoutPosition(
1530         (node->getLayout().measuredDimensions[dim[crossAxis]] -
1531          child->getLayout().measuredDimensions[dim[crossAxis]]) /
1532             2.0f,
1533         leading[crossAxis]);
1534   } else if (
1535       !child->isLeadingPositionDefined(crossAxis) &&
1536       ((YGNodeAlignItem(node, child) == YGAlignFlexEnd) ^
1537        (node->getStyle().flexWrap == YGWrapWrapReverse))) {
1538     child->setLayoutPosition(
1539         (node->getLayout().measuredDimensions[dim[crossAxis]] -
1540          child->getLayout().measuredDimensions[dim[crossAxis]]),
1541         leading[crossAxis]);
1542   }
1543 }
1544
1545 static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node,
1546                                                        const float availableWidth,
1547                                                        const float availableHeight,
1548                                                        const YGMeasureMode widthMeasureMode,
1549                                                        const YGMeasureMode heightMeasureMode,
1550                                                        const float ownerWidth,
1551                                                        const float ownerHeight) {
1552   YGAssertWithNode(
1553       node,
1554       node->getMeasure() != nullptr,
1555       "Expected node to have custom measure function");
1556
1557   const float paddingAndBorderAxisRow =
1558       YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, availableWidth);
1559   const float paddingAndBorderAxisColumn =
1560       YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, availableWidth);
1561   const float marginAxisRow = YGUnwrapFloatOptional(
1562       node->getMarginForAxis(YGFlexDirectionRow, availableWidth));
1563   const float marginAxisColumn = YGUnwrapFloatOptional(
1564       node->getMarginForAxis(YGFlexDirectionColumn, availableWidth));
1565
1566   // We want to make sure we don't call measure with negative size
1567   const float innerWidth = YGFloatIsUndefined(availableWidth)
1568       ? availableWidth
1569       : YGFloatMax(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow);
1570   const float innerHeight = YGFloatIsUndefined(availableHeight)
1571       ? availableHeight
1572       : YGFloatMax(
1573             0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn);
1574
1575   if (widthMeasureMode == YGMeasureModeExactly &&
1576       heightMeasureMode == YGMeasureModeExactly) {
1577     // Don't bother sizing the text if both dimensions are already defined.
1578     node->setLayoutMeasuredDimension(
1579         YGNodeBoundAxis(
1580             node,
1581             YGFlexDirectionRow,
1582             availableWidth - marginAxisRow,
1583             ownerWidth,
1584             ownerWidth),
1585         YGDimensionWidth);
1586     node->setLayoutMeasuredDimension(
1587         YGNodeBoundAxis(
1588             node,
1589             YGFlexDirectionColumn,
1590             availableHeight - marginAxisColumn,
1591             ownerHeight,
1592             ownerWidth),
1593         YGDimensionHeight);
1594   } else {
1595     // Measure the text under the current constraints.
1596     const YGSize measuredSize = node->getMeasure()(
1597         node, innerWidth, widthMeasureMode, innerHeight, heightMeasureMode);
1598
1599     node->setLayoutMeasuredDimension(
1600         YGNodeBoundAxis(
1601             node,
1602             YGFlexDirectionRow,
1603             (widthMeasureMode == YGMeasureModeUndefined ||
1604              widthMeasureMode == YGMeasureModeAtMost)
1605                 ? measuredSize.width + paddingAndBorderAxisRow
1606                 : availableWidth - marginAxisRow,
1607             ownerWidth,
1608             ownerWidth),
1609         YGDimensionWidth);
1610
1611     node->setLayoutMeasuredDimension(
1612         YGNodeBoundAxis(
1613             node,
1614             YGFlexDirectionColumn,
1615             (heightMeasureMode == YGMeasureModeUndefined ||
1616              heightMeasureMode == YGMeasureModeAtMost)
1617                 ? measuredSize.height + paddingAndBorderAxisColumn
1618                 : availableHeight - marginAxisColumn,
1619             ownerHeight,
1620             ownerWidth),
1621         YGDimensionHeight);
1622   }
1623 }
1624
1625 // For nodes with no children, use the available values if they were provided,
1626 // or the minimum size as indicated by the padding and border sizes.
1627 static void YGNodeEmptyContainerSetMeasuredDimensions(const YGNodeRef node,
1628                                                       const float availableWidth,
1629                                                       const float availableHeight,
1630                                                       const YGMeasureMode widthMeasureMode,
1631                                                       const YGMeasureMode heightMeasureMode,
1632                                                       const float ownerWidth,
1633                                                       const float ownerHeight) {
1634   const float paddingAndBorderAxisRow =
1635       YGNodePaddingAndBorderForAxis(node, YGFlexDirectionRow, ownerWidth);
1636   const float paddingAndBorderAxisColumn =
1637       YGNodePaddingAndBorderForAxis(node, YGFlexDirectionColumn, ownerWidth);
1638   const float marginAxisRow = YGUnwrapFloatOptional(
1639       node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
1640   const float marginAxisColumn = YGUnwrapFloatOptional(
1641       node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
1642
1643   node->setLayoutMeasuredDimension(
1644       YGNodeBoundAxis(
1645           node,
1646           YGFlexDirectionRow,
1647           (widthMeasureMode == YGMeasureModeUndefined ||
1648            widthMeasureMode == YGMeasureModeAtMost)
1649               ? paddingAndBorderAxisRow
1650               : availableWidth - marginAxisRow,
1651           ownerWidth,
1652           ownerWidth),
1653       YGDimensionWidth);
1654
1655   node->setLayoutMeasuredDimension(
1656       YGNodeBoundAxis(
1657           node,
1658           YGFlexDirectionColumn,
1659           (heightMeasureMode == YGMeasureModeUndefined ||
1660            heightMeasureMode == YGMeasureModeAtMost)
1661               ? paddingAndBorderAxisColumn
1662               : availableHeight - marginAxisColumn,
1663           ownerHeight,
1664           ownerWidth),
1665       YGDimensionHeight);
1666 }
1667
1668 static bool YGNodeFixedSizeSetMeasuredDimensions(const YGNodeRef node,
1669                                                  const float availableWidth,
1670                                                  const float availableHeight,
1671                                                  const YGMeasureMode widthMeasureMode,
1672                                                  const YGMeasureMode heightMeasureMode,
1673                                                  const float ownerWidth,
1674                                                  const float ownerHeight) {
1675   if ((!YGFloatIsUndefined(availableWidth) &&
1676        widthMeasureMode == YGMeasureModeAtMost && availableWidth <= 0.0f) ||
1677       (!YGFloatIsUndefined(availableHeight) &&
1678        heightMeasureMode == YGMeasureModeAtMost && availableHeight <= 0.0f) ||
1679       (widthMeasureMode == YGMeasureModeExactly &&
1680        heightMeasureMode == YGMeasureModeExactly)) {
1681     const float& marginAxisColumn = YGUnwrapFloatOptional(
1682         node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
1683     const float& marginAxisRow = YGUnwrapFloatOptional(
1684         node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
1685
1686     node->setLayoutMeasuredDimension(
1687         YGNodeBoundAxis(
1688             node,
1689             YGFlexDirectionRow,
1690             YGFloatIsUndefined(availableWidth) ||
1691                     (widthMeasureMode == YGMeasureModeAtMost &&
1692                      availableWidth < 0.0f)
1693                 ? 0.0f
1694                 : availableWidth - marginAxisRow,
1695             ownerWidth,
1696             ownerWidth),
1697         YGDimensionWidth);
1698
1699     node->setLayoutMeasuredDimension(
1700         YGNodeBoundAxis(
1701             node,
1702             YGFlexDirectionColumn,
1703             YGFloatIsUndefined(availableHeight) ||
1704                     (heightMeasureMode == YGMeasureModeAtMost &&
1705                      availableHeight < 0.0f)
1706                 ? 0.0f
1707                 : availableHeight - marginAxisColumn,
1708             ownerHeight,
1709             ownerWidth),
1710         YGDimensionHeight);
1711     return true;
1712   }
1713
1714   return false;
1715 }
1716
1717 static void YGZeroOutLayoutRecursivly(const YGNodeRef node) {
1718   memset(&(node->getLayout()), 0, sizeof(YGLayout));
1719   node->setHasNewLayout(true);
1720   node->cloneChildrenIfNeeded();
1721   const uint32_t childCount = YGNodeGetChildCount(node);
1722   for (uint32_t i = 0; i < childCount; i++) {
1723     const YGNodeRef child = node->getChild(i);
1724     YGZeroOutLayoutRecursivly(child);
1725   }
1726 }
1727
1728 static float YGNodeCalculateAvailableInnerDim(
1729     const YGNodeRef node,
1730     YGFlexDirection axis,
1731     float availableDim,
1732     float ownerDim) {
1733   YGFlexDirection direction =
1734       YGFlexDirectionIsRow(axis) ? YGFlexDirectionRow : YGFlexDirectionColumn;
1735   YGDimension dimension =
1736       YGFlexDirectionIsRow(axis) ? YGDimensionWidth : YGDimensionHeight;
1737
1738   const float margin =
1739       YGUnwrapFloatOptional(node->getMarginForAxis(direction, ownerDim));
1740   const float paddingAndBorder =
1741       YGNodePaddingAndBorderForAxis(node, direction, ownerDim);
1742
1743   float availableInnerDim = availableDim - margin - paddingAndBorder;
1744   // Max dimension overrides predefined dimension value; Min dimension in turn
1745   // overrides both of the above
1746   if (!YGFloatIsUndefined(availableInnerDim)) {
1747     // We want to make sure our available height does not violate min and max
1748     // constraints
1749     const YGFloatOptional minDimensionOptional = YGResolveValue(node->getStyle().minDimensions[dimension], ownerDim);
1750     const float minInnerDim = minDimensionOptional.isUndefined()
1751         ? 0.0f
1752         : minDimensionOptional.getValue() - paddingAndBorder;
1753
1754     const YGFloatOptional maxDimensionOptional = YGResolveValue(node->getStyle().maxDimensions[dimension], ownerDim) ;
1755
1756     const float maxInnerDim = maxDimensionOptional.isUndefined()
1757         ? FLT_MAX
1758         : maxDimensionOptional.getValue() - paddingAndBorder;
1759     availableInnerDim =
1760         YGFloatMax(YGFloatMin(availableInnerDim, maxInnerDim), minInnerDim);
1761   }
1762
1763   return availableInnerDim;
1764 }
1765
1766 static void YGNodeComputeFlexBasisForChildren(
1767     const YGNodeRef node,
1768     const float availableInnerWidth,
1769     const float availableInnerHeight,
1770     YGMeasureMode widthMeasureMode,
1771     YGMeasureMode heightMeasureMode,
1772     YGDirection direction,
1773     YGFlexDirection mainAxis,
1774     const YGConfigRef config,
1775     bool performLayout,
1776     float& totalOuterFlexBasis) {
1777   YGNodeRef singleFlexChild = nullptr;
1778   YGVector children = node->getChildren();
1779   YGMeasureMode measureModeMainDim =
1780       YGFlexDirectionIsRow(mainAxis) ? widthMeasureMode : heightMeasureMode;
1781   // If there is only one child with flexGrow + flexShrink it means we can set
1782   // the computedFlexBasis to 0 instead of measuring and shrinking / flexing the
1783   // child to exactly match the remaining space
1784   if (measureModeMainDim == YGMeasureModeExactly) {
1785     for (auto child : children) {
1786       if (singleFlexChild != nullptr) {
1787         if (child->isNodeFlexible()) {
1788           // There is already a flexible child, abort
1789           singleFlexChild = nullptr;
1790           break;
1791         }
1792       } else if (
1793           child->resolveFlexGrow() > 0.0f &&
1794           child->resolveFlexShrink() > 0.0f) {
1795         singleFlexChild = child;
1796       }
1797     }
1798   }
1799
1800   for (auto child : children) {
1801     child->resolveDimension();
1802     if (child->getStyle().display == YGDisplayNone) {
1803       YGZeroOutLayoutRecursivly(child);
1804       child->setHasNewLayout(true);
1805       child->setDirty(false);
1806       continue;
1807     }
1808     if (performLayout) {
1809       // Set the initial position (relative to the owner).
1810       const YGDirection childDirection = child->resolveDirection(direction);
1811       const float mainDim = YGFlexDirectionIsRow(mainAxis)
1812           ? availableInnerWidth
1813           : availableInnerHeight;
1814       const float crossDim = YGFlexDirectionIsRow(mainAxis)
1815           ? availableInnerHeight
1816           : availableInnerWidth;
1817       child->setPosition(
1818           childDirection, mainDim, crossDim, availableInnerWidth);
1819     }
1820
1821     if (child->getStyle().positionType == YGPositionTypeAbsolute) {
1822       continue;
1823     }
1824     if (child == singleFlexChild) {
1825       child->setLayoutComputedFlexBasisGeneration(gCurrentGenerationCount);
1826       child->setLayoutComputedFlexBasis(YGFloatOptional(0));
1827     } else {
1828       YGNodeComputeFlexBasisForChild(
1829           node,
1830           child,
1831           availableInnerWidth,
1832           widthMeasureMode,
1833           availableInnerHeight,
1834           availableInnerWidth,
1835           availableInnerHeight,
1836           heightMeasureMode,
1837           direction,
1838           config);
1839     }
1840
1841     totalOuterFlexBasis += YGUnwrapFloatOptional(
1842         child->getLayout().computedFlexBasis +
1843         child->getMarginForAxis(mainAxis, availableInnerWidth));
1844   }
1845 }
1846
1847 // This function assumes that all the children of node have their
1848 // computedFlexBasis properly computed(To do this use
1849 // YGNodeComputeFlexBasisForChildren function).
1850 // This function calculates YGCollectFlexItemsRowMeasurement
1851 static YGCollectFlexItemsRowValues YGCalculateCollectFlexItemsRowValues(
1852     const YGNodeRef& node,
1853     const YGDirection ownerDirection,
1854     const float mainAxisownerSize,
1855     const float availableInnerWidth,
1856     const float availableInnerMainDim,
1857     const uint32_t startOfLineIndex,
1858     const uint32_t lineCount) {
1859   YGCollectFlexItemsRowValues flexAlgoRowMeasurement = {};
1860   flexAlgoRowMeasurement.relativeChildren.reserve(node->getChildren().size());
1861
1862   float sizeConsumedOnCurrentLineIncludingMinConstraint = 0;
1863   const YGFlexDirection mainAxis = YGResolveFlexDirection(
1864       node->getStyle().flexDirection, node->resolveDirection(ownerDirection));
1865   const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
1866
1867   // Add items to the current line until it's full or we run out of items.
1868   uint32_t endOfLineIndex = startOfLineIndex;
1869   for (; endOfLineIndex < node->getChildrenCount(); endOfLineIndex++) {
1870     const YGNodeRef child = node->getChild(endOfLineIndex);
1871     if (child->getStyle().display == YGDisplayNone ||
1872         child->getStyle().positionType == YGPositionTypeAbsolute) {
1873       continue;
1874     }
1875     child->setLineIndex(lineCount);
1876     const float childMarginMainAxis = YGUnwrapFloatOptional(
1877         child->getMarginForAxis(mainAxis, availableInnerWidth));
1878     const float flexBasisWithMinAndMaxConstraints =
1879         YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
1880             child,
1881             mainAxis,
1882             YGUnwrapFloatOptional(child->getLayout().computedFlexBasis),
1883             mainAxisownerSize));
1884
1885     // If this is a multi-line flow and this item pushes us over the
1886     // available size, we've
1887     // hit the end of the current line. Break out of the loop and lay out
1888     // the current line.
1889     if (sizeConsumedOnCurrentLineIncludingMinConstraint +
1890                 flexBasisWithMinAndMaxConstraints + childMarginMainAxis >
1891             availableInnerMainDim &&
1892         isNodeFlexWrap && flexAlgoRowMeasurement.itemsOnLine > 0) {
1893       break;
1894     }
1895
1896     sizeConsumedOnCurrentLineIncludingMinConstraint +=
1897         flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
1898     flexAlgoRowMeasurement.sizeConsumedOnCurrentLine +=
1899         flexBasisWithMinAndMaxConstraints + childMarginMainAxis;
1900     flexAlgoRowMeasurement.itemsOnLine++;
1901
1902     if (child->isNodeFlexible()) {
1903       flexAlgoRowMeasurement.totalFlexGrowFactors += child->resolveFlexGrow();
1904
1905       // Unlike the grow factor, the shrink factor is scaled relative to the
1906       // child dimension.
1907       flexAlgoRowMeasurement.totalFlexShrinkScaledFactors +=
1908           -child->resolveFlexShrink() *
1909           YGUnwrapFloatOptional(child->getLayout().computedFlexBasis);
1910     }
1911
1912     flexAlgoRowMeasurement.relativeChildren.push_back(child);
1913   }
1914
1915   // The total flex factor needs to be floored to 1.
1916   if (flexAlgoRowMeasurement.totalFlexGrowFactors > 0 &&
1917       flexAlgoRowMeasurement.totalFlexGrowFactors < 1) {
1918     flexAlgoRowMeasurement.totalFlexGrowFactors = 1;
1919   }
1920
1921   // The total flex shrink factor needs to be floored to 1.
1922   if (flexAlgoRowMeasurement.totalFlexShrinkScaledFactors > 0 &&
1923       flexAlgoRowMeasurement.totalFlexShrinkScaledFactors < 1) {
1924     flexAlgoRowMeasurement.totalFlexShrinkScaledFactors = 1;
1925   }
1926   flexAlgoRowMeasurement.endOfLineIndex = endOfLineIndex;
1927   return flexAlgoRowMeasurement;
1928 }
1929
1930 // It distributes the free space to the flexible items and ensures that the size
1931 // of the flex items abide the min and max constraints. At the end of this
1932 // function the child nodes would have proper size. Prior using this function
1933 // please ensure that YGDistributeFreeSpaceFirstPass is called.
1934 static float YGDistributeFreeSpaceSecondPass(
1935     YGCollectFlexItemsRowValues& collectedFlexItemsValues,
1936     const YGNodeRef node,
1937     const YGFlexDirection mainAxis,
1938     const YGFlexDirection crossAxis,
1939     const float mainAxisownerSize,
1940     const float availableInnerMainDim,
1941     const float availableInnerCrossDim,
1942     const float availableInnerWidth,
1943     const float availableInnerHeight,
1944     const bool flexBasisOverflows,
1945     const YGMeasureMode measureModeCrossDim,
1946     const bool performLayout,
1947     const YGConfigRef config) {
1948   float childFlexBasis = 0;
1949   float flexShrinkScaledFactor = 0;
1950   float flexGrowFactor = 0;
1951   float deltaFreeSpace = 0;
1952   const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
1953   const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
1954
1955   for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
1956     childFlexBasis = YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
1957         currentRelativeChild,
1958         mainAxis,
1959         YGUnwrapFloatOptional(
1960             currentRelativeChild->getLayout().computedFlexBasis),
1961         mainAxisownerSize));
1962     float updatedMainSize = childFlexBasis;
1963
1964     if (!YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
1965         collectedFlexItemsValues.remainingFreeSpace < 0) {
1966       flexShrinkScaledFactor =
1967           -currentRelativeChild->resolveFlexShrink() * childFlexBasis;
1968       // Is this child able to shrink?
1969       if (flexShrinkScaledFactor != 0) {
1970         float childSize;
1971
1972         if (!YGFloatIsUndefined(
1973                 collectedFlexItemsValues.totalFlexShrinkScaledFactors) &&
1974             collectedFlexItemsValues.totalFlexShrinkScaledFactors == 0) {
1975           childSize = childFlexBasis + flexShrinkScaledFactor;
1976         } else {
1977           childSize = childFlexBasis +
1978               (collectedFlexItemsValues.remainingFreeSpace /
1979                collectedFlexItemsValues.totalFlexShrinkScaledFactors) *
1980                   flexShrinkScaledFactor;
1981         }
1982
1983         updatedMainSize = YGNodeBoundAxis(
1984             currentRelativeChild,
1985             mainAxis,
1986             childSize,
1987             availableInnerMainDim,
1988             availableInnerWidth);
1989       }
1990     } else if (
1991         !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
1992         collectedFlexItemsValues.remainingFreeSpace > 0) {
1993       flexGrowFactor = currentRelativeChild->resolveFlexGrow();
1994
1995       // Is this child able to grow?
1996       if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
1997         updatedMainSize = YGNodeBoundAxis(
1998             currentRelativeChild,
1999             mainAxis,
2000             childFlexBasis +
2001                 collectedFlexItemsValues.remainingFreeSpace /
2002                     collectedFlexItemsValues.totalFlexGrowFactors *
2003                     flexGrowFactor,
2004             availableInnerMainDim,
2005             availableInnerWidth);
2006       }
2007     }
2008
2009     deltaFreeSpace += updatedMainSize - childFlexBasis;
2010
2011     const float marginMain = YGUnwrapFloatOptional(
2012         currentRelativeChild->getMarginForAxis(mainAxis, availableInnerWidth));
2013     const float marginCross = YGUnwrapFloatOptional(
2014         currentRelativeChild->getMarginForAxis(crossAxis, availableInnerWidth));
2015
2016     float childCrossSize;
2017     float childMainSize = updatedMainSize + marginMain;
2018     YGMeasureMode childCrossMeasureMode;
2019     YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
2020
2021     if (!currentRelativeChild->getStyle().aspectRatio.isUndefined()) {
2022       childCrossSize = isMainAxisRow ? (childMainSize - marginMain) /
2023               currentRelativeChild->getStyle().aspectRatio.getValue()
2024                                      : (childMainSize - marginMain) *
2025               currentRelativeChild->getStyle().aspectRatio.getValue();
2026       childCrossMeasureMode = YGMeasureModeExactly;
2027
2028       childCrossSize += marginCross;
2029     } else if (
2030         !YGFloatIsUndefined(availableInnerCrossDim) &&
2031         !YGNodeIsStyleDimDefined(
2032             currentRelativeChild, crossAxis, availableInnerCrossDim) &&
2033         measureModeCrossDim == YGMeasureModeExactly &&
2034         !(isNodeFlexWrap && flexBasisOverflows) &&
2035         YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
2036         currentRelativeChild->marginLeadingValue(crossAxis).unit !=
2037             YGUnitAuto &&
2038         currentRelativeChild->marginTrailingValue(crossAxis).unit !=
2039             YGUnitAuto) {
2040       childCrossSize = availableInnerCrossDim;
2041       childCrossMeasureMode = YGMeasureModeExactly;
2042     } else if (!YGNodeIsStyleDimDefined(
2043                    currentRelativeChild, crossAxis, availableInnerCrossDim)) {
2044       childCrossSize = availableInnerCrossDim;
2045       childCrossMeasureMode = YGFloatIsUndefined(childCrossSize)
2046           ? YGMeasureModeUndefined
2047           : YGMeasureModeAtMost;
2048     } else {
2049       childCrossSize =
2050           YGUnwrapFloatOptional(YGResolveValue(
2051               currentRelativeChild->getResolvedDimension(dim[crossAxis]),
2052               availableInnerCrossDim)) +
2053           marginCross;
2054       const bool isLoosePercentageMeasurement =
2055           currentRelativeChild->getResolvedDimension(dim[crossAxis]).unit ==
2056               YGUnitPercent &&
2057           measureModeCrossDim != YGMeasureModeExactly;
2058       childCrossMeasureMode =
2059           YGFloatIsUndefined(childCrossSize) || isLoosePercentageMeasurement
2060           ? YGMeasureModeUndefined
2061           : YGMeasureModeExactly;
2062     }
2063
2064     YGConstrainMaxSizeForMode(
2065         currentRelativeChild,
2066         mainAxis,
2067         availableInnerMainDim,
2068         availableInnerWidth,
2069         &childMainMeasureMode,
2070         &childMainSize);
2071     YGConstrainMaxSizeForMode(
2072         currentRelativeChild,
2073         crossAxis,
2074         availableInnerCrossDim,
2075         availableInnerWidth,
2076         &childCrossMeasureMode,
2077         &childCrossSize);
2078
2079     const bool requiresStretchLayout =
2080         !YGNodeIsStyleDimDefined(
2081             currentRelativeChild, crossAxis, availableInnerCrossDim) &&
2082         YGNodeAlignItem(node, currentRelativeChild) == YGAlignStretch &&
2083         currentRelativeChild->marginLeadingValue(crossAxis).unit !=
2084             YGUnitAuto &&
2085         currentRelativeChild->marginTrailingValue(crossAxis).unit != YGUnitAuto;
2086
2087     const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
2088     const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
2089
2090     const YGMeasureMode childWidthMeasureMode =
2091         isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
2092     const YGMeasureMode childHeightMeasureMode =
2093         !isMainAxisRow ? childMainMeasureMode : childCrossMeasureMode;
2094
2095     // Recursively call the layout algorithm for this child with the updated
2096     // main size.
2097     YGLayoutNodeInternal(
2098         currentRelativeChild,
2099         childWidth,
2100         childHeight,
2101         node->getLayout().direction,
2102         childWidthMeasureMode,
2103         childHeightMeasureMode,
2104         availableInnerWidth,
2105         availableInnerHeight,
2106         performLayout && !requiresStretchLayout,
2107         "flex",
2108         config);
2109     node->setLayoutHadOverflow(
2110         node->getLayout().hadOverflow |
2111         currentRelativeChild->getLayout().hadOverflow);
2112   }
2113   return deltaFreeSpace;
2114 }
2115
2116 // It distributes the free space to the flexible items.For those flexible items
2117 // whose min and max constraints are triggered, those flex item's clamped size
2118 // is removed from the remaingfreespace.
2119 static void YGDistributeFreeSpaceFirstPass(
2120     YGCollectFlexItemsRowValues& collectedFlexItemsValues,
2121     const YGFlexDirection mainAxis,
2122     const float mainAxisownerSize,
2123     const float availableInnerMainDim,
2124     const float availableInnerWidth) {
2125   float flexShrinkScaledFactor = 0;
2126   float flexGrowFactor = 0;
2127   float baseMainSize = 0;
2128   float boundMainSize = 0;
2129   float deltaFreeSpace = 0;
2130
2131   for (auto currentRelativeChild : collectedFlexItemsValues.relativeChildren) {
2132     float childFlexBasis = YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
2133         currentRelativeChild,
2134         mainAxis,
2135         YGUnwrapFloatOptional(
2136             currentRelativeChild->getLayout().computedFlexBasis),
2137         mainAxisownerSize));
2138
2139     if (collectedFlexItemsValues.remainingFreeSpace < 0) {
2140       flexShrinkScaledFactor =
2141           -currentRelativeChild->resolveFlexShrink() * childFlexBasis;
2142
2143       // Is this child able to shrink?
2144       if (!YGFloatIsUndefined(flexShrinkScaledFactor) &&
2145           flexShrinkScaledFactor != 0) {
2146         baseMainSize = childFlexBasis +
2147             collectedFlexItemsValues.remainingFreeSpace /
2148                 collectedFlexItemsValues.totalFlexShrinkScaledFactors *
2149                 flexShrinkScaledFactor;
2150         boundMainSize = YGNodeBoundAxis(
2151             currentRelativeChild,
2152             mainAxis,
2153             baseMainSize,
2154             availableInnerMainDim,
2155             availableInnerWidth);
2156         if (!YGFloatIsUndefined(baseMainSize) &&
2157             !YGFloatIsUndefined(boundMainSize) &&
2158             baseMainSize != boundMainSize) {
2159           // By excluding this item's size and flex factor from remaining,
2160           // this item's
2161           // min/max constraints should also trigger in the second pass
2162           // resulting in the
2163           // item's size calculation being identical in the first and second
2164           // passes.
2165           deltaFreeSpace += boundMainSize - childFlexBasis;
2166           collectedFlexItemsValues.totalFlexShrinkScaledFactors -=
2167               flexShrinkScaledFactor;
2168         }
2169       }
2170     } else if (
2171         !YGFloatIsUndefined(collectedFlexItemsValues.remainingFreeSpace) &&
2172         collectedFlexItemsValues.remainingFreeSpace > 0) {
2173       flexGrowFactor = currentRelativeChild->resolveFlexGrow();
2174
2175       // Is this child able to grow?
2176       if (!YGFloatIsUndefined(flexGrowFactor) && flexGrowFactor != 0) {
2177         baseMainSize = childFlexBasis +
2178             collectedFlexItemsValues.remainingFreeSpace /
2179                 collectedFlexItemsValues.totalFlexGrowFactors * flexGrowFactor;
2180         boundMainSize = YGNodeBoundAxis(
2181             currentRelativeChild,
2182             mainAxis,
2183             baseMainSize,
2184             availableInnerMainDim,
2185             availableInnerWidth);
2186
2187         if (!YGFloatIsUndefined(baseMainSize) &&
2188             !YGFloatIsUndefined(boundMainSize) &&
2189             baseMainSize != boundMainSize) {
2190           // By excluding this item's size and flex factor from remaining,
2191           // this item's
2192           // min/max constraints should also trigger in the second pass
2193           // resulting in the
2194           // item's size calculation being identical in the first and second
2195           // passes.
2196           deltaFreeSpace += boundMainSize - childFlexBasis;
2197           collectedFlexItemsValues.totalFlexGrowFactors -= flexGrowFactor;
2198         }
2199       }
2200     }
2201   }
2202   collectedFlexItemsValues.remainingFreeSpace -= deltaFreeSpace;
2203 }
2204
2205 // Do two passes over the flex items to figure out how to distribute the
2206 // remaining space.
2207 // The first pass finds the items whose min/max constraints trigger,
2208 // freezes them at those
2209 // sizes, and excludes those sizes from the remaining space. The second
2210 // pass sets the size
2211 // of each flexible item. It distributes the remaining space amongst the
2212 // items whose min/max
2213 // constraints didn't trigger in pass 1. For the other items, it sets
2214 // their sizes by forcing
2215 // their min/max constraints to trigger again.
2216 //
2217 // This two pass approach for resolving min/max constraints deviates from
2218 // the spec. The
2219 // spec (https://www.w3.org/TR/YG-flexbox-1/#resolve-flexible-lengths)
2220 // describes a process
2221 // that needs to be repeated a variable number of times. The algorithm
2222 // implemented here
2223 // won't handle all cases but it was simpler to implement and it mitigates
2224 // performance
2225 // concerns because we know exactly how many passes it'll do.
2226 //
2227 // At the end of this function the child nodes would have the proper size
2228 // assigned to them.
2229 //
2230 static void YGResolveFlexibleLength(
2231     const YGNodeRef node,
2232     YGCollectFlexItemsRowValues& collectedFlexItemsValues,
2233     const YGFlexDirection mainAxis,
2234     const YGFlexDirection crossAxis,
2235     const float mainAxisownerSize,
2236     const float availableInnerMainDim,
2237     const float availableInnerCrossDim,
2238     const float availableInnerWidth,
2239     const float availableInnerHeight,
2240     const bool flexBasisOverflows,
2241     const YGMeasureMode measureModeCrossDim,
2242     const bool performLayout,
2243     const YGConfigRef config) {
2244   const float originalFreeSpace = collectedFlexItemsValues.remainingFreeSpace;
2245   // First pass: detect the flex items whose min/max constraints trigger
2246   YGDistributeFreeSpaceFirstPass(
2247       collectedFlexItemsValues,
2248       mainAxis,
2249       mainAxisownerSize,
2250       availableInnerMainDim,
2251       availableInnerWidth);
2252
2253   // Second pass: resolve the sizes of the flexible items
2254   const float distributedFreeSpace = YGDistributeFreeSpaceSecondPass(
2255       collectedFlexItemsValues,
2256       node,
2257       mainAxis,
2258       crossAxis,
2259       mainAxisownerSize,
2260       availableInnerMainDim,
2261       availableInnerCrossDim,
2262       availableInnerWidth,
2263       availableInnerHeight,
2264       flexBasisOverflows,
2265       measureModeCrossDim,
2266       performLayout,
2267       config);
2268
2269   collectedFlexItemsValues.remainingFreeSpace =
2270       originalFreeSpace - distributedFreeSpace;
2271 }
2272
2273 static void YGJustifyMainAxis(
2274     const YGNodeRef node,
2275     YGCollectFlexItemsRowValues& collectedFlexItemsValues,
2276     const uint32_t& startOfLineIndex,
2277     const YGFlexDirection& mainAxis,
2278     const YGFlexDirection& crossAxis,
2279     const YGMeasureMode& measureModeMainDim,
2280     const YGMeasureMode& measureModeCrossDim,
2281     const float& mainAxisownerSize,
2282     const float& ownerWidth,
2283     const float& availableInnerMainDim,
2284     const float& availableInnerCrossDim,
2285     const float& availableInnerWidth,
2286     const bool& performLayout) {
2287   const YGStyle style = node->getStyle();
2288
2289   // If we are using "at most" rules in the main axis. Calculate the remaining
2290   // space when constraint by the min size defined for the main axis.
2291   if (measureModeMainDim == YGMeasureModeAtMost &&
2292       collectedFlexItemsValues.remainingFreeSpace > 0) {
2293     if (style.minDimensions[dim[mainAxis]].unit != YGUnitUndefined &&
2294         !YGResolveValue(style.minDimensions[dim[mainAxis]], mainAxisownerSize)
2295              .isUndefined()) {
2296       collectedFlexItemsValues.remainingFreeSpace = YGFloatMax(
2297           0,
2298           YGUnwrapFloatOptional(YGResolveValue(
2299               style.minDimensions[dim[mainAxis]], mainAxisownerSize)) -
2300               (availableInnerMainDim -
2301                collectedFlexItemsValues.remainingFreeSpace));
2302     } else {
2303       collectedFlexItemsValues.remainingFreeSpace = 0;
2304     }
2305   }
2306
2307   int numberOfAutoMarginsOnCurrentLine = 0;
2308   for (uint32_t i = startOfLineIndex;
2309        i < collectedFlexItemsValues.endOfLineIndex;
2310        i++) {
2311     const YGNodeRef child = node->getChild(i);
2312     if (child->getStyle().positionType == YGPositionTypeRelative) {
2313       if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
2314         numberOfAutoMarginsOnCurrentLine++;
2315       }
2316       if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
2317         numberOfAutoMarginsOnCurrentLine++;
2318       }
2319     }
2320   }
2321
2322   // In order to position the elements in the main axis, we have two
2323   // controls. The space between the beginning and the first element
2324   // and the space between each two elements.
2325   float leadingMainDim = 0;
2326   float betweenMainDim = 0;
2327   const YGJustify justifyContent = node->getStyle().justifyContent;
2328
2329   if (numberOfAutoMarginsOnCurrentLine == 0) {
2330     switch (justifyContent) {
2331       case YGJustifyCenter:
2332         leadingMainDim = collectedFlexItemsValues.remainingFreeSpace / 2;
2333         break;
2334       case YGJustifyFlexEnd:
2335         leadingMainDim = collectedFlexItemsValues.remainingFreeSpace;
2336         break;
2337       case YGJustifySpaceBetween:
2338         if (collectedFlexItemsValues.itemsOnLine > 1) {
2339           betweenMainDim =
2340               YGFloatMax(collectedFlexItemsValues.remainingFreeSpace, 0) /
2341               (collectedFlexItemsValues.itemsOnLine - 1);
2342         } else {
2343           betweenMainDim = 0;
2344         }
2345         break;
2346       case YGJustifySpaceEvenly:
2347         // Space is distributed evenly across all elements
2348         betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
2349             (collectedFlexItemsValues.itemsOnLine + 1);
2350         leadingMainDim = betweenMainDim;
2351         break;
2352       case YGJustifySpaceAround:
2353         // Space on the edges is half of the space between elements
2354         betweenMainDim = collectedFlexItemsValues.remainingFreeSpace /
2355             collectedFlexItemsValues.itemsOnLine;
2356         leadingMainDim = betweenMainDim / 2;
2357         break;
2358       case YGJustifyFlexStart:
2359         break;
2360     }
2361   }
2362
2363   const float leadingPaddingAndBorderMain = YGUnwrapFloatOptional(
2364       node->getLeadingPaddingAndBorder(mainAxis, ownerWidth));
2365   collectedFlexItemsValues.mainDim =
2366       leadingPaddingAndBorderMain + leadingMainDim;
2367   collectedFlexItemsValues.crossDim = 0;
2368
2369   for (uint32_t i = startOfLineIndex;
2370        i < collectedFlexItemsValues.endOfLineIndex;
2371        i++) {
2372     const YGNodeRef child = node->getChild(i);
2373     const YGStyle childStyle = child->getStyle();
2374     const YGLayout childLayout = child->getLayout();
2375     if (childStyle.display == YGDisplayNone) {
2376       continue;
2377     }
2378     if (childStyle.positionType == YGPositionTypeAbsolute &&
2379         child->isLeadingPositionDefined(mainAxis)) {
2380       if (performLayout) {
2381         // In case the child is position absolute and has left/top being
2382         // defined, we override the position to whatever the user said
2383         // (and margin/border).
2384         child->setLayoutPosition(
2385             YGUnwrapFloatOptional(
2386                 child->getLeadingPosition(mainAxis, availableInnerMainDim)) +
2387                 node->getLeadingBorder(mainAxis) +
2388                 YGUnwrapFloatOptional(
2389                     child->getLeadingMargin(mainAxis, availableInnerWidth)),
2390             pos[mainAxis]);
2391       }
2392     } else {
2393       // Now that we placed the element, we need to update the variables.
2394       // We need to do that only for relative elements. Absolute elements
2395       // do not take part in that phase.
2396       if (childStyle.positionType == YGPositionTypeRelative) {
2397         if (child->marginLeadingValue(mainAxis).unit == YGUnitAuto) {
2398           collectedFlexItemsValues.mainDim +=
2399               collectedFlexItemsValues.remainingFreeSpace /
2400               numberOfAutoMarginsOnCurrentLine;
2401         }
2402
2403         if (performLayout) {
2404           child->setLayoutPosition(
2405               childLayout.position[pos[mainAxis]] +
2406                   collectedFlexItemsValues.mainDim,
2407               pos[mainAxis]);
2408         }
2409
2410         if (child->marginTrailingValue(mainAxis).unit == YGUnitAuto) {
2411           collectedFlexItemsValues.mainDim +=
2412               collectedFlexItemsValues.remainingFreeSpace /
2413               numberOfAutoMarginsOnCurrentLine;
2414         }
2415         bool canSkipFlex =
2416             !performLayout && measureModeCrossDim == YGMeasureModeExactly;
2417         if (canSkipFlex) {
2418           // If we skipped the flex step, then we can't rely on the
2419           // measuredDims because
2420           // they weren't computed. This means we can't call
2421           // YGNodeDimWithMargin.
2422           collectedFlexItemsValues.mainDim += betweenMainDim +
2423               YGUnwrapFloatOptional(child->getMarginForAxis(
2424                   mainAxis, availableInnerWidth)) +
2425               YGUnwrapFloatOptional(childLayout.computedFlexBasis);
2426           collectedFlexItemsValues.crossDim = availableInnerCrossDim;
2427         } else {
2428           // The main dimension is the sum of all the elements dimension plus
2429           // the spacing.
2430           collectedFlexItemsValues.mainDim += betweenMainDim +
2431               YGNodeDimWithMargin(child, mainAxis, availableInnerWidth);
2432
2433           // The cross dimension is the max of the elements dimension since
2434           // there can only be one element in that cross dimension.
2435           collectedFlexItemsValues.crossDim = YGFloatMax(
2436               collectedFlexItemsValues.crossDim,
2437               YGNodeDimWithMargin(child, crossAxis, availableInnerWidth));
2438         }
2439       } else if (performLayout) {
2440         child->setLayoutPosition(
2441             childLayout.position[pos[mainAxis]] +
2442                 node->getLeadingBorder(mainAxis) + leadingMainDim,
2443             pos[mainAxis]);
2444       }
2445     }
2446   }
2447   collectedFlexItemsValues.mainDim += YGUnwrapFloatOptional(
2448       node->getTrailingPaddingAndBorder(mainAxis, ownerWidth));
2449 }
2450
2451 //
2452 // This is the main routine that implements a subset of the flexbox layout
2453 // algorithm
2454 // described in the W3C YG documentation: https://www.w3.org/TR/YG3-flexbox/.
2455 //
2456 // Limitations of this algorithm, compared to the full standard:
2457 //  * Display property is always assumed to be 'flex' except for Text nodes,
2458 //  which
2459 //    are assumed to be 'inline-flex'.
2460 //  * The 'zIndex' property (or any form of z ordering) is not supported. Nodes
2461 //  are
2462 //    stacked in document order.
2463 //  * The 'order' property is not supported. The order of flex items is always
2464 //  defined
2465 //    by document order.
2466 //  * The 'visibility' property is always assumed to be 'visible'. Values of
2467 //  'collapse'
2468 //    and 'hidden' are not supported.
2469 //  * There is no support for forced breaks.
2470 //  * It does not support vertical inline directions (top-to-bottom or
2471 //  bottom-to-top text).
2472 //
2473 // Deviations from standard:
2474 //  * Section 4.5 of the spec indicates that all flex items have a default
2475 //  minimum
2476 //    main size. For text blocks, for example, this is the width of the widest
2477 //    word.
2478 //    Calculating the minimum width is expensive, so we forego it and assume a
2479 //    default
2480 //    minimum main size of 0.
2481 //  * Min/Max sizes in the main axis are not honored when resolving flexible
2482 //  lengths.
2483 //  * The spec indicates that the default value for 'flexDirection' is 'row',
2484 //  but
2485 //    the algorithm below assumes a default of 'column'.
2486 //
2487 // Input parameters:
2488 //    - node: current node to be sized and layed out
2489 //    - availableWidth & availableHeight: available size to be used for sizing
2490 //    the node
2491 //      or YGUndefined if the size is not available; interpretation depends on
2492 //      layout
2493 //      flags
2494 //    - ownerDirection: the inline (text) direction within the owner
2495 //    (left-to-right or
2496 //      right-to-left)
2497 //    - widthMeasureMode: indicates the sizing rules for the width (see below
2498 //    for explanation)
2499 //    - heightMeasureMode: indicates the sizing rules for the height (see below
2500 //    for explanation)
2501 //    - performLayout: specifies whether the caller is interested in just the
2502 //    dimensions
2503 //      of the node or it requires the entire node and its subtree to be layed
2504 //      out
2505 //      (with final positions)
2506 //
2507 // Details:
2508 //    This routine is called recursively to lay out subtrees of flexbox
2509 //    elements. It uses the
2510 //    information in node.style, which is treated as a read-only input. It is
2511 //    responsible for
2512 //    setting the layout.direction and layout.measuredDimensions fields for the
2513 //    input node as well
2514 //    as the layout.position and layout.lineIndex fields for its child nodes.
2515 //    The
2516 //    layout.measuredDimensions field includes any border or padding for the
2517 //    node but does
2518 //    not include margins.
2519 //
2520 //    The spec describes four different layout modes: "fill available", "max
2521 //    content", "min
2522 //    content",
2523 //    and "fit content". Of these, we don't use "min content" because we don't
2524 //    support default
2525 //    minimum main sizes (see above for details). Each of our measure modes maps
2526 //    to a layout mode
2527 //    from the spec (https://www.w3.org/TR/YG3-sizing/#terms):
2528 //      - YGMeasureModeUndefined: max content
2529 //      - YGMeasureModeExactly: fill available
2530 //      - YGMeasureModeAtMost: fit content
2531 //
2532 //    When calling YGNodelayoutImpl and YGLayoutNodeInternal, if the caller passes
2533 //    an available size of
2534 //    undefined then it must also pass a measure mode of YGMeasureModeUndefined
2535 //    in that dimension.
2536 //
2537 static void YGNodelayoutImpl(const YGNodeRef node,
2538                              const float availableWidth,
2539                              const float availableHeight,
2540                              const YGDirection ownerDirection,
2541                              const YGMeasureMode widthMeasureMode,
2542                              const YGMeasureMode heightMeasureMode,
2543                              const float ownerWidth,
2544                              const float ownerHeight,
2545                              const bool performLayout,
2546                              const YGConfigRef config) {
2547   YGAssertWithNode(node,
2548                    YGFloatIsUndefined(availableWidth) ? widthMeasureMode == YGMeasureModeUndefined
2549                                                       : true,
2550                    "availableWidth is indefinite so widthMeasureMode must be "
2551                    "YGMeasureModeUndefined");
2552   YGAssertWithNode(node,
2553                    YGFloatIsUndefined(availableHeight) ? heightMeasureMode == YGMeasureModeUndefined
2554                                                        : true,
2555                    "availableHeight is indefinite so heightMeasureMode must be "
2556                    "YGMeasureModeUndefined");
2557
2558   // Set the resolved resolution in the node's layout.
2559   const YGDirection direction = node->resolveDirection(ownerDirection);
2560   node->setLayoutDirection(direction);
2561
2562   const YGFlexDirection flexRowDirection = YGResolveFlexDirection(YGFlexDirectionRow, direction);
2563   const YGFlexDirection flexColumnDirection =
2564       YGResolveFlexDirection(YGFlexDirectionColumn, direction);
2565
2566   node->setLayoutMargin(
2567       YGUnwrapFloatOptional(
2568           node->getLeadingMargin(flexRowDirection, ownerWidth)),
2569       YGEdgeStart);
2570   node->setLayoutMargin(
2571       YGUnwrapFloatOptional(
2572           node->getTrailingMargin(flexRowDirection, ownerWidth)),
2573       YGEdgeEnd);
2574   node->setLayoutMargin(
2575       YGUnwrapFloatOptional(
2576           node->getLeadingMargin(flexColumnDirection, ownerWidth)),
2577       YGEdgeTop);
2578   node->setLayoutMargin(
2579       YGUnwrapFloatOptional(
2580           node->getTrailingMargin(flexColumnDirection, ownerWidth)),
2581       YGEdgeBottom);
2582
2583   node->setLayoutBorder(node->getLeadingBorder(flexRowDirection), YGEdgeStart);
2584   node->setLayoutBorder(node->getTrailingBorder(flexRowDirection), YGEdgeEnd);
2585   node->setLayoutBorder(node->getLeadingBorder(flexColumnDirection), YGEdgeTop);
2586   node->setLayoutBorder(
2587       node->getTrailingBorder(flexColumnDirection), YGEdgeBottom);
2588
2589   node->setLayoutPadding(
2590       YGUnwrapFloatOptional(
2591           node->getLeadingPadding(flexRowDirection, ownerWidth)),
2592       YGEdgeStart);
2593   node->setLayoutPadding(
2594       YGUnwrapFloatOptional(
2595           node->getTrailingPadding(flexRowDirection, ownerWidth)),
2596       YGEdgeEnd);
2597   node->setLayoutPadding(
2598       YGUnwrapFloatOptional(
2599           node->getLeadingPadding(flexColumnDirection, ownerWidth)),
2600       YGEdgeTop);
2601   node->setLayoutPadding(
2602       YGUnwrapFloatOptional(
2603           node->getTrailingPadding(flexColumnDirection, ownerWidth)),
2604       YGEdgeBottom);
2605
2606   if (node->getMeasure() != nullptr) {
2607     YGNodeWithMeasureFuncSetMeasuredDimensions(node,
2608                                                availableWidth,
2609                                                availableHeight,
2610                                                widthMeasureMode,
2611                                                heightMeasureMode,
2612                                                ownerWidth,
2613                                                ownerHeight);
2614     return;
2615   }
2616
2617   const uint32_t childCount = YGNodeGetChildCount(node);
2618   if (childCount == 0) {
2619     YGNodeEmptyContainerSetMeasuredDimensions(node,
2620                                               availableWidth,
2621                                               availableHeight,
2622                                               widthMeasureMode,
2623                                               heightMeasureMode,
2624                                               ownerWidth,
2625                                               ownerHeight);
2626     return;
2627   }
2628
2629   // If we're not being asked to perform a full layout we can skip the algorithm if we already know
2630   // the size
2631   if (!performLayout && YGNodeFixedSizeSetMeasuredDimensions(node,
2632                                                              availableWidth,
2633                                                              availableHeight,
2634                                                              widthMeasureMode,
2635                                                              heightMeasureMode,
2636                                                              ownerWidth,
2637                                                              ownerHeight)) {
2638     return;
2639   }
2640
2641   // At this point we know we're going to perform work. Ensure that each child has a mutable copy.
2642   node->cloneChildrenIfNeeded();
2643   // Reset layout flags, as they could have changed.
2644   node->setLayoutHadOverflow(false);
2645
2646   // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
2647   const YGFlexDirection mainAxis =
2648       YGResolveFlexDirection(node->getStyle().flexDirection, direction);
2649   const YGFlexDirection crossAxis = YGFlexDirectionCross(mainAxis, direction);
2650   const bool isMainAxisRow = YGFlexDirectionIsRow(mainAxis);
2651   const bool isNodeFlexWrap = node->getStyle().flexWrap != YGWrapNoWrap;
2652
2653   const float mainAxisownerSize = isMainAxisRow ? ownerWidth : ownerHeight;
2654   const float crossAxisownerSize = isMainAxisRow ? ownerHeight : ownerWidth;
2655
2656   const float leadingPaddingAndBorderCross = YGUnwrapFloatOptional(
2657       node->getLeadingPaddingAndBorder(crossAxis, ownerWidth));
2658   const float paddingAndBorderAxisMain = YGNodePaddingAndBorderForAxis(node, mainAxis, ownerWidth);
2659   const float paddingAndBorderAxisCross =
2660       YGNodePaddingAndBorderForAxis(node, crossAxis, ownerWidth);
2661
2662   YGMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;
2663   YGMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;
2664
2665   const float paddingAndBorderAxisRow =
2666       isMainAxisRow ? paddingAndBorderAxisMain : paddingAndBorderAxisCross;
2667   const float paddingAndBorderAxisColumn =
2668       isMainAxisRow ? paddingAndBorderAxisCross : paddingAndBorderAxisMain;
2669
2670   const float marginAxisRow = YGUnwrapFloatOptional(
2671       node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
2672   const float marginAxisColumn = YGUnwrapFloatOptional(
2673       node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
2674
2675   const float minInnerWidth =
2676       YGUnwrapFloatOptional(YGResolveValue(node->getStyle().minDimensions[YGDimensionWidth], ownerWidth)) -
2677       paddingAndBorderAxisRow;
2678   const float maxInnerWidth =
2679       YGUnwrapFloatOptional(YGResolveValue(node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)) -
2680       paddingAndBorderAxisRow;
2681   const float minInnerHeight =
2682       YGUnwrapFloatOptional(YGResolveValue(node->getStyle().minDimensions[YGDimensionHeight], ownerHeight)) -
2683       paddingAndBorderAxisColumn;
2684   const float maxInnerHeight =
2685       YGUnwrapFloatOptional(YGResolveValue(
2686           node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight)) -
2687       paddingAndBorderAxisColumn;
2688
2689   const float minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight;
2690   const float maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight;
2691
2692   // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
2693
2694   float availableInnerWidth = YGNodeCalculateAvailableInnerDim(
2695       node, YGFlexDirectionRow, availableWidth, ownerWidth);
2696   float availableInnerHeight = YGNodeCalculateAvailableInnerDim(
2697       node, YGFlexDirectionColumn, availableHeight, ownerHeight);
2698
2699   float availableInnerMainDim =
2700       isMainAxisRow ? availableInnerWidth : availableInnerHeight;
2701   const float availableInnerCrossDim =
2702       isMainAxisRow ? availableInnerHeight : availableInnerWidth;
2703
2704   float totalOuterFlexBasis = 0;
2705
2706   // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
2707
2708   YGNodeComputeFlexBasisForChildren(
2709       node,
2710       availableInnerWidth,
2711       availableInnerHeight,
2712       widthMeasureMode,
2713       heightMeasureMode,
2714       direction,
2715       mainAxis,
2716       config,
2717       performLayout,
2718       totalOuterFlexBasis);
2719
2720   const bool flexBasisOverflows = measureModeMainDim == YGMeasureModeUndefined
2721       ? false
2722       : totalOuterFlexBasis > availableInnerMainDim;
2723   if (isNodeFlexWrap && flexBasisOverflows &&
2724       measureModeMainDim == YGMeasureModeAtMost) {
2725     measureModeMainDim = YGMeasureModeExactly;
2726   }
2727   // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
2728
2729   // Indexes of children that represent the first and last items in the line.
2730   uint32_t startOfLineIndex = 0;
2731   uint32_t endOfLineIndex = 0;
2732
2733   // Number of lines.
2734   uint32_t lineCount = 0;
2735
2736   // Accumulated cross dimensions of all lines so far.
2737   float totalLineCrossDim = 0;
2738
2739   // Max main dimension of all the lines.
2740   float maxLineMainDim = 0;
2741   YGCollectFlexItemsRowValues collectedFlexItemsValues;
2742   for (; endOfLineIndex < childCount;
2743        lineCount++, startOfLineIndex = endOfLineIndex) {
2744     collectedFlexItemsValues = YGCalculateCollectFlexItemsRowValues(
2745         node,
2746         ownerDirection,
2747         mainAxisownerSize,
2748         availableInnerWidth,
2749         availableInnerMainDim,
2750         startOfLineIndex,
2751         lineCount);
2752     endOfLineIndex = collectedFlexItemsValues.endOfLineIndex;
2753
2754     // If we don't need to measure the cross axis, we can skip the entire flex
2755     // step.
2756     const bool canSkipFlex =
2757         !performLayout && measureModeCrossDim == YGMeasureModeExactly;
2758
2759     // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
2760     // Calculate the remaining available space that needs to be allocated.
2761     // If the main dimension size isn't known, it is computed based on
2762     // the line length, so there's no more space left to distribute.
2763
2764     bool sizeBasedOnContent = false;
2765     // If we don't measure with exact main dimension we want to ensure we don't violate min and max
2766     if (measureModeMainDim != YGMeasureModeExactly) {
2767       if (!YGFloatIsUndefined(minInnerMainDim) &&
2768           collectedFlexItemsValues.sizeConsumedOnCurrentLine <
2769               minInnerMainDim) {
2770         availableInnerMainDim = minInnerMainDim;
2771       } else if (
2772           !YGFloatIsUndefined(maxInnerMainDim) &&
2773           collectedFlexItemsValues.sizeConsumedOnCurrentLine >
2774               maxInnerMainDim) {
2775         availableInnerMainDim = maxInnerMainDim;
2776       } else {
2777         if (!node->getConfig()->useLegacyStretchBehaviour &&
2778             ((YGFloatIsUndefined(
2779                   collectedFlexItemsValues.totalFlexGrowFactors) &&
2780               collectedFlexItemsValues.totalFlexGrowFactors == 0) ||
2781              (YGFloatIsUndefined(node->resolveFlexGrow()) &&
2782               node->resolveFlexGrow() == 0))) {
2783           // If we don't have any children to flex or we can't flex the node
2784           // itself, space we've used is all space we need. Root node also
2785           // should be shrunk to minimum
2786           availableInnerMainDim =
2787               collectedFlexItemsValues.sizeConsumedOnCurrentLine;
2788         }
2789
2790         if (node->getConfig()->useLegacyStretchBehaviour) {
2791           node->setLayoutDidUseLegacyFlag(true);
2792         }
2793         sizeBasedOnContent = !node->getConfig()->useLegacyStretchBehaviour;
2794       }
2795     }
2796
2797     if (!sizeBasedOnContent && !YGFloatIsUndefined(availableInnerMainDim)) {
2798       collectedFlexItemsValues.remainingFreeSpace = availableInnerMainDim -
2799           collectedFlexItemsValues.sizeConsumedOnCurrentLine;
2800     } else if (collectedFlexItemsValues.sizeConsumedOnCurrentLine < 0) {
2801       // availableInnerMainDim is indefinite which means the node is being sized based on its
2802       // content.
2803       // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 points for
2804       // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.
2805       collectedFlexItemsValues.remainingFreeSpace =
2806           -collectedFlexItemsValues.sizeConsumedOnCurrentLine;
2807     }
2808
2809     if (!canSkipFlex) {
2810       YGResolveFlexibleLength(
2811           node,
2812           collectedFlexItemsValues,
2813           mainAxis,
2814           crossAxis,
2815           mainAxisownerSize,
2816           availableInnerMainDim,
2817           availableInnerCrossDim,
2818           availableInnerWidth,
2819           availableInnerHeight,
2820           flexBasisOverflows,
2821           measureModeCrossDim,
2822           performLayout,
2823           config);
2824     }
2825
2826     node->setLayoutHadOverflow(
2827         node->getLayout().hadOverflow |
2828         (collectedFlexItemsValues.remainingFreeSpace < 0));
2829
2830     // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
2831
2832     // At this point, all the children have their dimensions set in the main
2833     // axis.
2834     // Their dimensions are also set in the cross axis with the exception of
2835     // items
2836     // that are aligned "stretch". We need to compute these stretch values and
2837     // set the final positions.
2838
2839     YGJustifyMainAxis(
2840         node,
2841         collectedFlexItemsValues,
2842         startOfLineIndex,
2843         mainAxis,
2844         crossAxis,
2845         measureModeMainDim,
2846         measureModeCrossDim,
2847         mainAxisownerSize,
2848         ownerWidth,
2849         availableInnerMainDim,
2850         availableInnerCrossDim,
2851         availableInnerWidth,
2852         performLayout);
2853
2854     float containerCrossAxis = availableInnerCrossDim;
2855     if (measureModeCrossDim == YGMeasureModeUndefined ||
2856         measureModeCrossDim == YGMeasureModeAtMost) {
2857       // Compute the cross axis from the max cross dimension of the children.
2858       containerCrossAxis =
2859           YGNodeBoundAxis(
2860               node,
2861               crossAxis,
2862               collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
2863               crossAxisownerSize,
2864               ownerWidth) -
2865           paddingAndBorderAxisCross;
2866     }
2867
2868     // If there's no flex wrap, the cross dimension is defined by the container.
2869     if (!isNodeFlexWrap && measureModeCrossDim == YGMeasureModeExactly) {
2870       collectedFlexItemsValues.crossDim = availableInnerCrossDim;
2871     }
2872
2873     // Clamp to the min/max size specified on the container.
2874     collectedFlexItemsValues.crossDim =
2875         YGNodeBoundAxis(
2876             node,
2877             crossAxis,
2878             collectedFlexItemsValues.crossDim + paddingAndBorderAxisCross,
2879             crossAxisownerSize,
2880             ownerWidth) -
2881         paddingAndBorderAxisCross;
2882
2883     // STEP 7: CROSS-AXIS ALIGNMENT
2884     // We can skip child alignment if we're just measuring the container.
2885     if (performLayout) {
2886       for (uint32_t i = startOfLineIndex; i < endOfLineIndex; i++) {
2887         const YGNodeRef child = node->getChild(i);
2888         if (child->getStyle().display == YGDisplayNone) {
2889           continue;
2890         }
2891         if (child->getStyle().positionType == YGPositionTypeAbsolute) {
2892           // If the child is absolutely positioned and has a
2893           // top/left/bottom/right set, override
2894           // all the previously computed positions to set it correctly.
2895           const bool isChildLeadingPosDefined =
2896               child->isLeadingPositionDefined(crossAxis);
2897           if (isChildLeadingPosDefined) {
2898             child->setLayoutPosition(
2899                 YGUnwrapFloatOptional(child->getLeadingPosition(
2900                     crossAxis, availableInnerCrossDim)) +
2901                     node->getLeadingBorder(crossAxis) +
2902                     YGUnwrapFloatOptional(child->getLeadingMargin(
2903                         crossAxis, availableInnerWidth)),
2904                 pos[crossAxis]);
2905           }
2906           // If leading position is not defined or calculations result in Nan, default to border + margin
2907           if (!isChildLeadingPosDefined ||
2908               YGFloatIsUndefined(child->getLayout().position[pos[crossAxis]])) {
2909             child->setLayoutPosition(
2910                 node->getLeadingBorder(crossAxis) +
2911                     YGUnwrapFloatOptional(child->getLeadingMargin(
2912                         crossAxis, availableInnerWidth)),
2913                 pos[crossAxis]);
2914           }
2915         } else {
2916           float leadingCrossDim = leadingPaddingAndBorderCross;
2917
2918           // For a relative children, we're either using alignItems (owner) or
2919           // alignSelf (child) in order to determine the position in the cross
2920           // axis
2921           const YGAlign alignItem = YGNodeAlignItem(node, child);
2922
2923           // If the child uses align stretch, we need to lay it out one more
2924           // time, this time
2925           // forcing the cross-axis size to be the computed cross size for the
2926           // current line.
2927           if (alignItem == YGAlignStretch &&
2928               child->marginLeadingValue(crossAxis).unit != YGUnitAuto &&
2929               child->marginTrailingValue(crossAxis).unit != YGUnitAuto) {
2930             // If the child defines a definite size for its cross axis, there's
2931             // no need to stretch.
2932             if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) {
2933               float childMainSize =
2934                   child->getLayout().measuredDimensions[dim[mainAxis]];
2935               float childCrossSize =
2936                   !child->getStyle().aspectRatio.isUndefined()
2937                   ? ((YGUnwrapFloatOptional(child->getMarginForAxis(
2938                           crossAxis, availableInnerWidth)) +
2939                       (isMainAxisRow ? childMainSize /
2940                                child->getStyle().aspectRatio.getValue()
2941                                      : childMainSize *
2942                                child->getStyle().aspectRatio.getValue())))
2943                   : collectedFlexItemsValues.crossDim;
2944
2945               childMainSize += YGUnwrapFloatOptional(
2946                   child->getMarginForAxis(mainAxis, availableInnerWidth));
2947
2948               YGMeasureMode childMainMeasureMode = YGMeasureModeExactly;
2949               YGMeasureMode childCrossMeasureMode = YGMeasureModeExactly;
2950               YGConstrainMaxSizeForMode(child,
2951                                         mainAxis,
2952                                         availableInnerMainDim,
2953                                         availableInnerWidth,
2954                                         &childMainMeasureMode,
2955                                         &childMainSize);
2956               YGConstrainMaxSizeForMode(child,
2957                                         crossAxis,
2958                                         availableInnerCrossDim,
2959                                         availableInnerWidth,
2960                                         &childCrossMeasureMode,
2961                                         &childCrossSize);
2962
2963               const float childWidth = isMainAxisRow ? childMainSize : childCrossSize;
2964               const float childHeight = !isMainAxisRow ? childMainSize : childCrossSize;
2965
2966               const YGMeasureMode childWidthMeasureMode =
2967                   YGFloatIsUndefined(childWidth) ? YGMeasureModeUndefined
2968                                                  : YGMeasureModeExactly;
2969               const YGMeasureMode childHeightMeasureMode =
2970                   YGFloatIsUndefined(childHeight) ? YGMeasureModeUndefined
2971                                                   : YGMeasureModeExactly;
2972
2973               YGLayoutNodeInternal(
2974                   child,
2975                   childWidth,
2976                   childHeight,
2977                   direction,
2978                   childWidthMeasureMode,
2979                   childHeightMeasureMode,
2980                   availableInnerWidth,
2981                   availableInnerHeight,
2982                   true,
2983                   "stretch",
2984                   config);
2985             }
2986           } else {
2987             const float remainingCrossDim = containerCrossAxis -
2988                 YGNodeDimWithMargin(child, crossAxis, availableInnerWidth);
2989
2990             if (child->marginLeadingValue(crossAxis).unit == YGUnitAuto &&
2991                 child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
2992               leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim / 2);
2993             } else if (
2994                 child->marginTrailingValue(crossAxis).unit == YGUnitAuto) {
2995               // No-Op
2996             } else if (
2997                 child->marginLeadingValue(crossAxis).unit == YGUnitAuto) {
2998               leadingCrossDim += YGFloatMax(0.0f, remainingCrossDim);
2999             } else if (alignItem == YGAlignFlexStart) {
3000               // No-Op
3001             } else if (alignItem == YGAlignCenter) {
3002               leadingCrossDim += remainingCrossDim / 2;
3003             } else {
3004               leadingCrossDim += remainingCrossDim;
3005             }
3006           }
3007           // And we apply the position
3008           child->setLayoutPosition(
3009               child->getLayout().position[pos[crossAxis]] + totalLineCrossDim +
3010                   leadingCrossDim,
3011               pos[crossAxis]);
3012         }
3013       }
3014     }
3015
3016     totalLineCrossDim += collectedFlexItemsValues.crossDim;
3017     maxLineMainDim =
3018         YGFloatMax(maxLineMainDim, collectedFlexItemsValues.mainDim);
3019   }
3020
3021   // STEP 8: MULTI-LINE CONTENT ALIGNMENT
3022   if (performLayout && (lineCount > 1 || YGIsBaselineLayout(node)) &&
3023       !YGFloatIsUndefined(availableInnerCrossDim)) {
3024     const float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;
3025
3026     float crossDimLead = 0;
3027     float currentLead = leadingPaddingAndBorderCross;
3028
3029     switch (node->getStyle().alignContent) {
3030       case YGAlignFlexEnd:
3031         currentLead += remainingAlignContentDim;
3032         break;
3033       case YGAlignCenter:
3034         currentLead += remainingAlignContentDim / 2;
3035         break;
3036       case YGAlignStretch:
3037         if (availableInnerCrossDim > totalLineCrossDim) {
3038           crossDimLead = remainingAlignContentDim / lineCount;
3039         }
3040         break;
3041       case YGAlignSpaceAround:
3042         if (availableInnerCrossDim > totalLineCrossDim) {
3043           currentLead += remainingAlignContentDim / (2 * lineCount);
3044           if (lineCount > 1) {
3045             crossDimLead = remainingAlignContentDim / lineCount;
3046           }
3047         } else {
3048           currentLead += remainingAlignContentDim / 2;
3049         }
3050         break;
3051       case YGAlignSpaceBetween:
3052         if (availableInnerCrossDim > totalLineCrossDim && lineCount > 1) {
3053           crossDimLead = remainingAlignContentDim / (lineCount - 1);
3054         }
3055         break;
3056       case YGAlignAuto:
3057       case YGAlignFlexStart:
3058       case YGAlignBaseline:
3059         break;
3060     }
3061
3062     uint32_t endIndex = 0;
3063     for (uint32_t i = 0; i < lineCount; i++) {
3064       const uint32_t startIndex = endIndex;
3065       uint32_t ii;
3066
3067       // compute the line's height and find the endIndex
3068       float lineHeight = 0;
3069       float maxAscentForCurrentLine = 0;
3070       float maxDescentForCurrentLine = 0;
3071       for (ii = startIndex; ii < childCount; ii++) {
3072         const YGNodeRef child = node->getChild(ii);
3073         if (child->getStyle().display == YGDisplayNone) {
3074           continue;
3075         }
3076         if (child->getStyle().positionType == YGPositionTypeRelative) {
3077           if (child->getLineIndex() != i) {
3078             break;
3079           }
3080           if (YGNodeIsLayoutDimDefined(child, crossAxis)) {
3081             lineHeight = YGFloatMax(
3082                 lineHeight,
3083                 child->getLayout().measuredDimensions[dim[crossAxis]] +
3084                     YGUnwrapFloatOptional(child->getMarginForAxis(
3085                         crossAxis, availableInnerWidth)));
3086           }
3087           if (YGNodeAlignItem(node, child) == YGAlignBaseline) {
3088             const float ascent = YGBaseline(child) +
3089                 YGUnwrapFloatOptional(child->getLeadingMargin(
3090                     YGFlexDirectionColumn, availableInnerWidth));
3091             const float descent =
3092                 child->getLayout().measuredDimensions[YGDimensionHeight] +
3093                 YGUnwrapFloatOptional(child->getMarginForAxis(
3094                     YGFlexDirectionColumn, availableInnerWidth)) -
3095                 ascent;
3096             maxAscentForCurrentLine =
3097                 YGFloatMax(maxAscentForCurrentLine, ascent);
3098             maxDescentForCurrentLine =
3099                 YGFloatMax(maxDescentForCurrentLine, descent);
3100             lineHeight = YGFloatMax(
3101                 lineHeight, maxAscentForCurrentLine + maxDescentForCurrentLine);
3102           }
3103         }
3104       }
3105       endIndex = ii;
3106       lineHeight += crossDimLead;
3107
3108       if (performLayout) {
3109         for (ii = startIndex; ii < endIndex; ii++) {
3110           const YGNodeRef child = node->getChild(ii);
3111           if (child->getStyle().display == YGDisplayNone) {
3112             continue;
3113           }
3114           if (child->getStyle().positionType == YGPositionTypeRelative) {
3115             switch (YGNodeAlignItem(node, child)) {
3116               case YGAlignFlexStart: {
3117                 child->setLayoutPosition(
3118                     currentLead +
3119                         YGUnwrapFloatOptional(child->getLeadingMargin(
3120                             crossAxis, availableInnerWidth)),
3121                     pos[crossAxis]);
3122                 break;
3123               }
3124               case YGAlignFlexEnd: {
3125                 child->setLayoutPosition(
3126                     currentLead + lineHeight -
3127                         YGUnwrapFloatOptional(child->getTrailingMargin(
3128                             crossAxis, availableInnerWidth)) -
3129                         child->getLayout().measuredDimensions[dim[crossAxis]],
3130                     pos[crossAxis]);
3131                 break;
3132               }
3133               case YGAlignCenter: {
3134                 float childHeight =
3135                     child->getLayout().measuredDimensions[dim[crossAxis]];
3136
3137                 child->setLayoutPosition(
3138                     currentLead + (lineHeight - childHeight) / 2,
3139                     pos[crossAxis]);
3140                 break;
3141               }
3142               case YGAlignStretch: {
3143                 child->setLayoutPosition(
3144                     currentLead +
3145                         YGUnwrapFloatOptional(child->getLeadingMargin(
3146                             crossAxis, availableInnerWidth)),
3147                     pos[crossAxis]);
3148
3149                 // Remeasure child with the line height as it as been only measured with the
3150                 // owners height yet.
3151                 if (!YGNodeIsStyleDimDefined(child, crossAxis, availableInnerCrossDim)) {
3152                   const float childWidth = isMainAxisRow
3153                       ? (child->getLayout()
3154                              .measuredDimensions[YGDimensionWidth] +
3155                          YGUnwrapFloatOptional(child->getMarginForAxis(
3156                              mainAxis, availableInnerWidth)))
3157                       : lineHeight;
3158
3159                   const float childHeight = !isMainAxisRow
3160                       ? (child->getLayout()
3161                              .measuredDimensions[YGDimensionHeight] +
3162                          YGUnwrapFloatOptional(child->getMarginForAxis(
3163                              crossAxis, availableInnerWidth)))
3164                       : lineHeight;
3165
3166                   if (!(YGFloatsEqual(
3167                             childWidth,
3168                             child->getLayout()
3169                                 .measuredDimensions[YGDimensionWidth]) &&
3170                         YGFloatsEqual(
3171                             childHeight,
3172                             child->getLayout()
3173                                 .measuredDimensions[YGDimensionHeight]))) {
3174                     YGLayoutNodeInternal(child,
3175                                          childWidth,
3176                                          childHeight,
3177                                          direction,
3178                                          YGMeasureModeExactly,
3179                                          YGMeasureModeExactly,
3180                                          availableInnerWidth,
3181                                          availableInnerHeight,
3182                                          true,
3183                                          "multiline-stretch",
3184                                          config);
3185                   }
3186                 }
3187                 break;
3188               }
3189               case YGAlignBaseline: {
3190                 child->setLayoutPosition(
3191                     currentLead + maxAscentForCurrentLine - YGBaseline(child) +
3192                         YGUnwrapFloatOptional(child->getLeadingPosition(
3193                             YGFlexDirectionColumn, availableInnerCrossDim)),
3194                     YGEdgeTop);
3195
3196                 break;
3197               }
3198               case YGAlignAuto:
3199               case YGAlignSpaceBetween:
3200               case YGAlignSpaceAround:
3201                 break;
3202             }
3203           }
3204         }
3205       }
3206
3207       currentLead += lineHeight;
3208     }
3209   }
3210
3211   // STEP 9: COMPUTING FINAL DIMENSIONS
3212
3213   node->setLayoutMeasuredDimension(
3214       YGNodeBoundAxis(
3215           node,
3216           YGFlexDirectionRow,
3217           availableWidth - marginAxisRow,
3218           ownerWidth,
3219           ownerWidth),
3220       YGDimensionWidth);
3221
3222   node->setLayoutMeasuredDimension(
3223       YGNodeBoundAxis(
3224           node,
3225           YGFlexDirectionColumn,
3226           availableHeight - marginAxisColumn,
3227           ownerHeight,
3228           ownerWidth),
3229       YGDimensionHeight);
3230
3231   // If the user didn't specify a width or height for the node, set the
3232   // dimensions based on the children.
3233   if (measureModeMainDim == YGMeasureModeUndefined ||
3234       (node->getStyle().overflow != YGOverflowScroll &&
3235        measureModeMainDim == YGMeasureModeAtMost)) {
3236     // Clamp the size to the min/max size, if specified, and make sure it
3237     // doesn't go below the padding and border amount.
3238     node->setLayoutMeasuredDimension(
3239         YGNodeBoundAxis(
3240             node, mainAxis, maxLineMainDim, mainAxisownerSize, ownerWidth),
3241         dim[mainAxis]);
3242
3243   } else if (
3244       measureModeMainDim == YGMeasureModeAtMost &&
3245       node->getStyle().overflow == YGOverflowScroll) {
3246     node->setLayoutMeasuredDimension(
3247         YGFloatMax(
3248             YGFloatMin(
3249                 availableInnerMainDim + paddingAndBorderAxisMain,
3250                 YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
3251                     node, mainAxis, maxLineMainDim, mainAxisownerSize))),
3252             paddingAndBorderAxisMain),
3253         dim[mainAxis]);
3254   }
3255
3256   if (measureModeCrossDim == YGMeasureModeUndefined ||
3257       (node->getStyle().overflow != YGOverflowScroll &&
3258        measureModeCrossDim == YGMeasureModeAtMost)) {
3259     // Clamp the size to the min/max size, if specified, and make sure it
3260     // doesn't go below the padding and border amount.
3261
3262     node->setLayoutMeasuredDimension(
3263         YGNodeBoundAxis(
3264             node,
3265             crossAxis,
3266             totalLineCrossDim + paddingAndBorderAxisCross,
3267             crossAxisownerSize,
3268             ownerWidth),
3269         dim[crossAxis]);
3270
3271   } else if (
3272       measureModeCrossDim == YGMeasureModeAtMost &&
3273       node->getStyle().overflow == YGOverflowScroll) {
3274     node->setLayoutMeasuredDimension(
3275         YGFloatMax(
3276             YGFloatMin(
3277                 availableInnerCrossDim + paddingAndBorderAxisCross,
3278                 YGUnwrapFloatOptional(YGNodeBoundAxisWithinMinAndMax(
3279                     node,
3280                     crossAxis,
3281                     totalLineCrossDim + paddingAndBorderAxisCross,
3282                     crossAxisownerSize))),
3283             paddingAndBorderAxisCross),
3284         dim[crossAxis]);
3285   }
3286
3287   // As we only wrapped in normal direction yet, we need to reverse the positions on wrap-reverse.
3288   if (performLayout && node->getStyle().flexWrap == YGWrapWrapReverse) {
3289     for (uint32_t i = 0; i < childCount; i++) {
3290       const YGNodeRef child = YGNodeGetChild(node, i);
3291       if (child->getStyle().positionType == YGPositionTypeRelative) {
3292         child->setLayoutPosition(
3293             node->getLayout().measuredDimensions[dim[crossAxis]] -
3294                 child->getLayout().position[pos[crossAxis]] -
3295                 child->getLayout().measuredDimensions[dim[crossAxis]],
3296             pos[crossAxis]);
3297       }
3298     }
3299   }
3300
3301   if (performLayout) {
3302     // STEP 10: SIZING AND POSITIONING ABSOLUTE CHILDREN
3303     for (auto child : node->getChildren()) {
3304       if (child->getStyle().positionType != YGPositionTypeAbsolute) {
3305         continue;
3306       }
3307       YGNodeAbsoluteLayoutChild(
3308           node,
3309           child,
3310           availableInnerWidth,
3311           isMainAxisRow ? measureModeMainDim : measureModeCrossDim,
3312           availableInnerHeight,
3313           direction,
3314           config);
3315     }
3316
3317     // STEP 11: SETTING TRAILING POSITIONS FOR CHILDREN
3318     const bool needsMainTrailingPos =
3319         mainAxis == YGFlexDirectionRowReverse || mainAxis == YGFlexDirectionColumnReverse;
3320     const bool needsCrossTrailingPos =
3321         crossAxis == YGFlexDirectionRowReverse || crossAxis == YGFlexDirectionColumnReverse;
3322
3323     // Set trailing position if necessary.
3324     if (needsMainTrailingPos || needsCrossTrailingPos) {
3325       for (uint32_t i = 0; i < childCount; i++) {
3326         const YGNodeRef child = node->getChild(i);
3327         if (child->getStyle().display == YGDisplayNone) {
3328           continue;
3329         }
3330         if (needsMainTrailingPos) {
3331           YGNodeSetChildTrailingPosition(node, child, mainAxis);
3332         }
3333
3334         if (needsCrossTrailingPos) {
3335           YGNodeSetChildTrailingPosition(node, child, crossAxis);
3336         }
3337       }
3338     }
3339   }
3340 }
3341
3342 uint32_t gDepth = 0;
3343 bool gPrintTree = false;
3344 bool gPrintChanges = false;
3345 bool gPrintSkips = false;
3346
3347 static const char *spacer = "                                                            ";
3348
3349 static const char *YGSpacer(const unsigned long level) {
3350   const size_t spacerLen = strlen(spacer);
3351   if (level > spacerLen) {
3352     return &spacer[0];
3353   } else {
3354     return &spacer[spacerLen - level];
3355   }
3356 }
3357
3358 static const char *YGMeasureModeName(const YGMeasureMode mode, const bool performLayout) {
3359   const char *kMeasureModeNames[YGMeasureModeCount] = {"UNDEFINED", "EXACTLY", "AT_MOST"};
3360   const char *kLayoutModeNames[YGMeasureModeCount] = {"LAY_UNDEFINED",
3361                                                       "LAY_EXACTLY",
3362                                                       "LAY_AT_"
3363                                                       "MOST"};
3364
3365   if (mode >= YGMeasureModeCount) {
3366     return "";
3367   }
3368
3369   return performLayout ? kLayoutModeNames[mode] : kMeasureModeNames[mode];
3370 }
3371
3372 static inline bool YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(YGMeasureMode sizeMode,
3373                                                                      float size,
3374                                                                      float lastComputedSize) {
3375   return sizeMode == YGMeasureModeExactly && YGFloatsEqual(size, lastComputedSize);
3376 }
3377
3378 static inline bool YGMeasureModeOldSizeIsUnspecifiedAndStillFits(YGMeasureMode sizeMode,
3379                                                                  float size,
3380                                                                  YGMeasureMode lastSizeMode,
3381                                                                  float lastComputedSize) {
3382   return sizeMode == YGMeasureModeAtMost && lastSizeMode == YGMeasureModeUndefined &&
3383          (size >= lastComputedSize || YGFloatsEqual(size, lastComputedSize));
3384 }
3385
3386 static inline bool YGMeasureModeNewMeasureSizeIsStricterAndStillValid(YGMeasureMode sizeMode,
3387                                                                       float size,
3388                                                                       YGMeasureMode lastSizeMode,
3389                                                                       float lastSize,
3390                                                                       float lastComputedSize) {
3391   return lastSizeMode == YGMeasureModeAtMost &&
3392       sizeMode == YGMeasureModeAtMost && !YGFloatIsUndefined(lastSize) &&
3393       !YGFloatIsUndefined(size) && !YGFloatIsUndefined(lastComputedSize) &&
3394       lastSize > size &&
3395       (lastComputedSize <= size || YGFloatsEqual(size, lastComputedSize));
3396 }
3397
3398 float YGRoundValueToPixelGrid(const float value,
3399                               const float pointScaleFactor,
3400                               const bool forceCeil,
3401                               const bool forceFloor) {
3402   float scaledValue = value * pointScaleFactor;
3403   float fractial = fmodf(scaledValue, 1.0f);
3404   if (YGFloatsEqual(fractial, 0)) {
3405     // First we check if the value is already rounded
3406     scaledValue = scaledValue - fractial;
3407   } else if (YGFloatsEqual(fractial, 1.0f)) {
3408     scaledValue = scaledValue - fractial + 1.0f;
3409   } else if (forceCeil) {
3410     // Next we check if we need to use forced rounding
3411     scaledValue = scaledValue - fractial + 1.0f;
3412   } else if (forceFloor) {
3413     scaledValue = scaledValue - fractial;
3414   } else {
3415     // Finally we just round the value
3416     scaledValue = scaledValue - fractial +
3417         (!YGFloatIsUndefined(fractial) &&
3418                  (fractial > 0.5f || YGFloatsEqual(fractial, 0.5f))
3419              ? 1.0f
3420              : 0.0f);
3421   }
3422   return (YGFloatIsUndefined(scaledValue) ||
3423           YGFloatIsUndefined(pointScaleFactor))
3424       ? YGUndefined
3425       : scaledValue / pointScaleFactor;
3426 }
3427
3428 bool YGNodeCanUseCachedMeasurement(const YGMeasureMode widthMode,
3429                                    const float width,
3430                                    const YGMeasureMode heightMode,
3431                                    const float height,
3432                                    const YGMeasureMode lastWidthMode,
3433                                    const float lastWidth,
3434                                    const YGMeasureMode lastHeightMode,
3435                                    const float lastHeight,
3436                                    const float lastComputedWidth,
3437                                    const float lastComputedHeight,
3438                                    const float marginRow,
3439                                    const float marginColumn,
3440                                    const YGConfigRef config) {
3441   if ((!YGFloatIsUndefined(lastComputedHeight) && lastComputedHeight < 0) ||
3442       (!YGFloatIsUndefined(lastComputedWidth) && lastComputedWidth < 0)) {
3443     return false;
3444   }
3445   bool useRoundedComparison =
3446       config != nullptr && config->pointScaleFactor != 0;
3447   const float effectiveWidth =
3448       useRoundedComparison ? YGRoundValueToPixelGrid(width, config->pointScaleFactor, false, false)
3449                            : width;
3450   const float effectiveHeight =
3451       useRoundedComparison ? YGRoundValueToPixelGrid(height, config->pointScaleFactor, false, false)
3452                            : height;
3453   const float effectiveLastWidth =
3454       useRoundedComparison
3455           ? YGRoundValueToPixelGrid(lastWidth, config->pointScaleFactor, false, false)
3456           : lastWidth;
3457   const float effectiveLastHeight =
3458       useRoundedComparison
3459           ? YGRoundValueToPixelGrid(lastHeight, config->pointScaleFactor, false, false)
3460           : lastHeight;
3461
3462   const bool hasSameWidthSpec =
3463       lastWidthMode == widthMode && YGFloatsEqual(effectiveLastWidth, effectiveWidth);
3464   const bool hasSameHeightSpec =
3465       lastHeightMode == heightMode && YGFloatsEqual(effectiveLastHeight, effectiveHeight);
3466
3467   const bool widthIsCompatible =
3468       hasSameWidthSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(widthMode,
3469                                                                             width - marginRow,
3470                                                                             lastComputedWidth) ||
3471       YGMeasureModeOldSizeIsUnspecifiedAndStillFits(widthMode,
3472                                                     width - marginRow,
3473                                                     lastWidthMode,
3474                                                     lastComputedWidth) ||
3475       YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
3476           widthMode, width - marginRow, lastWidthMode, lastWidth, lastComputedWidth);
3477
3478   const bool heightIsCompatible =
3479       hasSameHeightSpec || YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize(heightMode,
3480                                                                              height - marginColumn,
3481                                                                              lastComputedHeight) ||
3482       YGMeasureModeOldSizeIsUnspecifiedAndStillFits(heightMode,
3483                                                     height - marginColumn,
3484                                                     lastHeightMode,
3485                                                     lastComputedHeight) ||
3486       YGMeasureModeNewMeasureSizeIsStricterAndStillValid(
3487           heightMode, height - marginColumn, lastHeightMode, lastHeight, lastComputedHeight);
3488
3489   return widthIsCompatible && heightIsCompatible;
3490 }
3491
3492 //
3493 // This is a wrapper around the YGNodelayoutImpl function. It determines
3494 // whether the layout request is redundant and can be skipped.
3495 //
3496 // Parameters:
3497 //  Input parameters are the same as YGNodelayoutImpl (see above)
3498 //  Return parameter is true if layout was performed, false if skipped
3499 //
3500 bool YGLayoutNodeInternal(const YGNodeRef node,
3501                           const float availableWidth,
3502                           const float availableHeight,
3503                           const YGDirection ownerDirection,
3504                           const YGMeasureMode widthMeasureMode,
3505                           const YGMeasureMode heightMeasureMode,
3506                           const float ownerWidth,
3507                           const float ownerHeight,
3508                           const bool performLayout,
3509                           const char *reason,
3510                           const YGConfigRef config) {
3511   YGLayout* layout = &node->getLayout();
3512
3513   gDepth++;
3514
3515   const bool needToVisitNode =
3516       (node->isDirty() && layout->generationCount != gCurrentGenerationCount) ||
3517       layout->lastOwnerDirection != ownerDirection;
3518
3519   if (needToVisitNode) {
3520     // Invalidate the cached results.
3521     layout->nextCachedMeasurementsIndex = 0;
3522     layout->cachedLayout.widthMeasureMode = (YGMeasureMode) -1;
3523     layout->cachedLayout.heightMeasureMode = (YGMeasureMode) -1;
3524     layout->cachedLayout.computedWidth = -1;
3525     layout->cachedLayout.computedHeight = -1;
3526   }
3527
3528   YGCachedMeasurement* cachedResults = nullptr;
3529
3530   // Determine whether the results are already cached. We maintain a separate
3531   // cache for layouts and measurements. A layout operation modifies the
3532   // positions
3533   // and dimensions for nodes in the subtree. The algorithm assumes that each
3534   // node
3535   // gets layed out a maximum of one time per tree layout, but multiple
3536   // measurements
3537   // may be required to resolve all of the flex dimensions.
3538   // We handle nodes with measure functions specially here because they are the
3539   // most
3540   // expensive to measure, so it's worth avoiding redundant measurements if at
3541   // all possible.
3542   if (node->getMeasure() != nullptr) {
3543     const float marginAxisRow = YGUnwrapFloatOptional(
3544         node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
3545     const float marginAxisColumn = YGUnwrapFloatOptional(
3546         node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
3547
3548     // First, try to use the layout cache.
3549     if (YGNodeCanUseCachedMeasurement(widthMeasureMode,
3550                                       availableWidth,
3551                                       heightMeasureMode,
3552                                       availableHeight,
3553                                       layout->cachedLayout.widthMeasureMode,
3554                                       layout->cachedLayout.availableWidth,
3555                                       layout->cachedLayout.heightMeasureMode,
3556                                       layout->cachedLayout.availableHeight,
3557                                       layout->cachedLayout.computedWidth,
3558                                       layout->cachedLayout.computedHeight,
3559                                       marginAxisRow,
3560                                       marginAxisColumn,
3561                                       config)) {
3562       cachedResults = &layout->cachedLayout;
3563     } else {
3564       // Try to use the measurement cache.
3565       for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
3566         if (YGNodeCanUseCachedMeasurement(widthMeasureMode,
3567                                           availableWidth,
3568                                           heightMeasureMode,
3569                                           availableHeight,
3570                                           layout->cachedMeasurements[i].widthMeasureMode,
3571                                           layout->cachedMeasurements[i].availableWidth,
3572                                           layout->cachedMeasurements[i].heightMeasureMode,
3573                                           layout->cachedMeasurements[i].availableHeight,
3574                                           layout->cachedMeasurements[i].computedWidth,
3575                                           layout->cachedMeasurements[i].computedHeight,
3576                                           marginAxisRow,
3577                                           marginAxisColumn,
3578                                           config)) {
3579           cachedResults = &layout->cachedMeasurements[i];
3580           break;
3581         }
3582       }
3583     }
3584   } else if (performLayout) {
3585     if (YGFloatsEqual(layout->cachedLayout.availableWidth, availableWidth) &&
3586         YGFloatsEqual(layout->cachedLayout.availableHeight, availableHeight) &&
3587         layout->cachedLayout.widthMeasureMode == widthMeasureMode &&
3588         layout->cachedLayout.heightMeasureMode == heightMeasureMode) {
3589       cachedResults = &layout->cachedLayout;
3590     }
3591   } else {
3592     for (uint32_t i = 0; i < layout->nextCachedMeasurementsIndex; i++) {
3593       if (YGFloatsEqual(layout->cachedMeasurements[i].availableWidth, availableWidth) &&
3594           YGFloatsEqual(layout->cachedMeasurements[i].availableHeight, availableHeight) &&
3595           layout->cachedMeasurements[i].widthMeasureMode == widthMeasureMode &&
3596           layout->cachedMeasurements[i].heightMeasureMode == heightMeasureMode) {
3597         cachedResults = &layout->cachedMeasurements[i];
3598         break;
3599       }
3600     }
3601   }
3602
3603   if (!needToVisitNode && cachedResults != nullptr) {
3604     layout->measuredDimensions[YGDimensionWidth] = cachedResults->computedWidth;
3605     layout->measuredDimensions[YGDimensionHeight] = cachedResults->computedHeight;
3606
3607     if (gPrintChanges && gPrintSkips) {
3608       YGLog(node, YGLogLevelVerbose, "%s%d.{[skipped] ", YGSpacer(gDepth), gDepth);
3609       if (node->getPrintFunc() != nullptr) {
3610         node->getPrintFunc()(node);
3611       }
3612       YGLog(
3613           node,
3614           YGLogLevelVerbose,
3615           "wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n",
3616           YGMeasureModeName(widthMeasureMode, performLayout),
3617           YGMeasureModeName(heightMeasureMode, performLayout),
3618           availableWidth,
3619           availableHeight,
3620           cachedResults->computedWidth,
3621           cachedResults->computedHeight,
3622           reason);
3623     }
3624   } else {
3625     if (gPrintChanges) {
3626       YGLog(
3627           node,
3628           YGLogLevelVerbose,
3629           "%s%d.{%s",
3630           YGSpacer(gDepth),
3631           gDepth,
3632           needToVisitNode ? "*" : "");
3633       if (node->getPrintFunc() != nullptr) {
3634         node->getPrintFunc()(node);
3635       }
3636       YGLog(
3637           node,
3638           YGLogLevelVerbose,
3639           "wm: %s, hm: %s, aw: %f ah: %f %s\n",
3640           YGMeasureModeName(widthMeasureMode, performLayout),
3641           YGMeasureModeName(heightMeasureMode, performLayout),
3642           availableWidth,
3643           availableHeight,
3644           reason);
3645     }
3646
3647     YGNodelayoutImpl(node,
3648                      availableWidth,
3649                      availableHeight,
3650                      ownerDirection,
3651                      widthMeasureMode,
3652                      heightMeasureMode,
3653                      ownerWidth,
3654                      ownerHeight,
3655                      performLayout,
3656                      config);
3657
3658     if (gPrintChanges) {
3659       YGLog(
3660           node,
3661           YGLogLevelVerbose,
3662           "%s%d.}%s",
3663           YGSpacer(gDepth),
3664           gDepth,
3665           needToVisitNode ? "*" : "");
3666       if (node->getPrintFunc() != nullptr) {
3667         node->getPrintFunc()(node);
3668       }
3669       YGLog(
3670           node,
3671           YGLogLevelVerbose,
3672           "wm: %s, hm: %s, d: (%f, %f) %s\n",
3673           YGMeasureModeName(widthMeasureMode, performLayout),
3674           YGMeasureModeName(heightMeasureMode, performLayout),
3675           layout->measuredDimensions[YGDimensionWidth],
3676           layout->measuredDimensions[YGDimensionHeight],
3677           reason);
3678     }
3679
3680     layout->lastOwnerDirection = ownerDirection;
3681
3682     if (cachedResults == nullptr) {
3683       if (layout->nextCachedMeasurementsIndex == YG_MAX_CACHED_RESULT_COUNT) {
3684         if (gPrintChanges) {
3685           YGLog(node, YGLogLevelVerbose, "Out of cache entries!\n");
3686         }
3687         layout->nextCachedMeasurementsIndex = 0;
3688       }
3689
3690       YGCachedMeasurement *newCacheEntry;
3691       if (performLayout) {
3692         // Use the single layout cache entry.
3693         newCacheEntry = &layout->cachedLayout;
3694       } else {
3695         // Allocate a new measurement cache entry.
3696         newCacheEntry = &layout->cachedMeasurements[layout->nextCachedMeasurementsIndex];
3697         layout->nextCachedMeasurementsIndex++;
3698       }
3699
3700       newCacheEntry->availableWidth = availableWidth;
3701       newCacheEntry->availableHeight = availableHeight;
3702       newCacheEntry->widthMeasureMode = widthMeasureMode;
3703       newCacheEntry->heightMeasureMode = heightMeasureMode;
3704       newCacheEntry->computedWidth = layout->measuredDimensions[YGDimensionWidth];
3705       newCacheEntry->computedHeight = layout->measuredDimensions[YGDimensionHeight];
3706     }
3707   }
3708
3709   if (performLayout) {
3710     node->setLayoutDimension(
3711         node->getLayout().measuredDimensions[YGDimensionWidth],
3712         YGDimensionWidth);
3713     node->setLayoutDimension(
3714         node->getLayout().measuredDimensions[YGDimensionHeight],
3715         YGDimensionHeight);
3716
3717     node->setHasNewLayout(true);
3718     node->setDirty(false);
3719   }
3720
3721   gDepth--;
3722   layout->generationCount = gCurrentGenerationCount;
3723   return (needToVisitNode || cachedResults == nullptr);
3724 }
3725
3726 void YGConfigSetPointScaleFactor(const YGConfigRef config, const float pixelsInPoint) {
3727   YGAssertWithConfig(config, pixelsInPoint >= 0.0f, "Scale factor should not be less than zero");
3728
3729   // We store points for Pixel as we will use it for rounding
3730   if (pixelsInPoint == 0.0f) {
3731     // Zero is used to skip rounding
3732     config->pointScaleFactor = 0.0f;
3733   } else {
3734     config->pointScaleFactor = pixelsInPoint;
3735   }
3736 }
3737
3738 static void YGRoundToPixelGrid(const YGNodeRef node,
3739                                const float pointScaleFactor,
3740                                const float absoluteLeft,
3741                                const float absoluteTop) {
3742   if (pointScaleFactor == 0.0f) {
3743     return;
3744   }
3745
3746   const float nodeLeft = node->getLayout().position[YGEdgeLeft];
3747   const float nodeTop = node->getLayout().position[YGEdgeTop];
3748
3749   const float nodeWidth = node->getLayout().dimensions[YGDimensionWidth];
3750   const float nodeHeight = node->getLayout().dimensions[YGDimensionHeight];
3751
3752   const float absoluteNodeLeft = absoluteLeft + nodeLeft;
3753   const float absoluteNodeTop = absoluteTop + nodeTop;
3754
3755   const float absoluteNodeRight = absoluteNodeLeft + nodeWidth;
3756   const float absoluteNodeBottom = absoluteNodeTop + nodeHeight;
3757
3758   // If a node has a custom measure function we never want to round down its size as this could
3759   // lead to unwanted text truncation.
3760   const bool textRounding = node->getNodeType() == YGNodeTypeText;
3761
3762   node->setLayoutPosition(
3763       YGRoundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding),
3764       YGEdgeLeft);
3765
3766   node->setLayoutPosition(
3767       YGRoundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding),
3768       YGEdgeTop);
3769
3770   // We multiply dimension by scale factor and if the result is close to the whole number, we don't
3771   // have any fraction
3772   // To verify if the result is close to whole number we want to check both floor and ceil numbers
3773   const bool hasFractionalWidth = !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 0) &&
3774                                   !YGFloatsEqual(fmodf(nodeWidth * pointScaleFactor, 1.0), 1.0);
3775   const bool hasFractionalHeight = !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 0) &&
3776                                    !YGFloatsEqual(fmodf(nodeHeight * pointScaleFactor, 1.0), 1.0);
3777
3778   node->setLayoutDimension(
3779       YGRoundValueToPixelGrid(
3780           absoluteNodeRight,
3781           pointScaleFactor,
3782           (textRounding && hasFractionalWidth),
3783           (textRounding && !hasFractionalWidth)) -
3784           YGRoundValueToPixelGrid(
3785               absoluteNodeLeft, pointScaleFactor, false, textRounding),
3786       YGDimensionWidth);
3787
3788   node->setLayoutDimension(
3789       YGRoundValueToPixelGrid(
3790           absoluteNodeBottom,
3791           pointScaleFactor,
3792           (textRounding && hasFractionalHeight),
3793           (textRounding && !hasFractionalHeight)) -
3794           YGRoundValueToPixelGrid(
3795               absoluteNodeTop, pointScaleFactor, false, textRounding),
3796       YGDimensionHeight);
3797
3798   const uint32_t childCount = YGNodeGetChildCount(node);
3799   for (uint32_t i = 0; i < childCount; i++) {
3800     YGRoundToPixelGrid(
3801         YGNodeGetChild(node, i),
3802         pointScaleFactor,
3803         absoluteNodeLeft,
3804         absoluteNodeTop);
3805   }
3806 }
3807
3808 void YGNodeCalculateLayout(
3809     const YGNodeRef node,
3810     const float ownerWidth,
3811     const float ownerHeight,
3812     const YGDirection ownerDirection) {
3813   // Increment the generation count. This will force the recursive routine to
3814   // visit
3815   // all dirty nodes at least once. Subsequent visits will be skipped if the
3816   // input
3817   // parameters don't change.
3818   gCurrentGenerationCount++;
3819   node->resolveDimension();
3820   float width = YGUndefined;
3821   YGMeasureMode widthMeasureMode = YGMeasureModeUndefined;
3822   if (YGNodeIsStyleDimDefined(node, YGFlexDirectionRow, ownerWidth)) {
3823     width = YGUnwrapFloatOptional(
3824         YGResolveValue(
3825             node->getResolvedDimension(dim[YGFlexDirectionRow]), ownerWidth) +
3826         node->getMarginForAxis(YGFlexDirectionRow, ownerWidth));
3827     widthMeasureMode = YGMeasureModeExactly;
3828   } else if (!YGResolveValue(
3829                   node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth)
3830                   .isUndefined()) {
3831     width = YGUnwrapFloatOptional(YGResolveValue(
3832         node->getStyle().maxDimensions[YGDimensionWidth], ownerWidth));
3833     widthMeasureMode = YGMeasureModeAtMost;
3834   } else {
3835     width = ownerWidth;
3836     widthMeasureMode = YGFloatIsUndefined(width) ? YGMeasureModeUndefined
3837                                                  : YGMeasureModeExactly;
3838   }
3839
3840   float height = YGUndefined;
3841   YGMeasureMode heightMeasureMode = YGMeasureModeUndefined;
3842   if (YGNodeIsStyleDimDefined(node, YGFlexDirectionColumn, ownerHeight)) {
3843     height = YGUnwrapFloatOptional(
3844         YGResolveValue(
3845             node->getResolvedDimension(dim[YGFlexDirectionColumn]),
3846             ownerHeight) +
3847         node->getMarginForAxis(YGFlexDirectionColumn, ownerWidth));
3848     heightMeasureMode = YGMeasureModeExactly;
3849   } else if (!YGResolveValue(
3850                   node->getStyle().maxDimensions[YGDimensionHeight],
3851                   ownerHeight)
3852                   .isUndefined()) {
3853     height = YGUnwrapFloatOptional(YGResolveValue(node->getStyle().maxDimensions[YGDimensionHeight], ownerHeight));
3854     heightMeasureMode = YGMeasureModeAtMost;
3855   } else {
3856     height = ownerHeight;
3857     heightMeasureMode = YGFloatIsUndefined(height) ? YGMeasureModeUndefined
3858                                                    : YGMeasureModeExactly;
3859   }
3860   if (YGLayoutNodeInternal(
3861           node,
3862           width,
3863           height,
3864           ownerDirection,
3865           widthMeasureMode,
3866           heightMeasureMode,
3867           ownerWidth,
3868           ownerHeight,
3869           true,
3870           "initial",
3871           node->getConfig())) {
3872     node->setPosition(
3873         node->getLayout().direction, ownerWidth, ownerHeight, ownerWidth);
3874     YGRoundToPixelGrid(node, node->getConfig()->pointScaleFactor, 0.0f, 0.0f);
3875
3876     if (gPrintTree) {
3877       YGNodePrint(
3878           node,
3879           (YGPrintOptions)(
3880               YGPrintOptionsLayout | YGPrintOptionsChildren |
3881               YGPrintOptionsStyle));
3882     }
3883   }
3884
3885   // We want to get rid off `useLegacyStretchBehaviour` from YGConfig. But we
3886   // aren't sure whether client's of yoga have gotten rid off this flag or not.
3887   // So logging this in YGLayout would help to find out the call sites depending
3888   // on this flag. This check would be removed once we are sure no one is
3889   // dependent on this flag anymore. The flag
3890   // `shouldDiffLayoutWithoutLegacyStretchBehaviour` in YGConfig will help to
3891   // run experiments.
3892   if (node->getConfig()->shouldDiffLayoutWithoutLegacyStretchBehaviour &&
3893       node->didUseLegacyFlag()) {
3894     const YGNodeRef originalNode = YGNodeDeepClone(node);
3895     originalNode->resolveDimension();
3896     // Recursively mark nodes as dirty
3897     originalNode->markDirtyAndPropogateDownwards();
3898     gCurrentGenerationCount++;
3899     // Rerun the layout, and calculate the diff
3900     originalNode->setAndPropogateUseLegacyFlag(false);
3901     if (YGLayoutNodeInternal(
3902             originalNode,
3903             width,
3904             height,
3905             ownerDirection,
3906             widthMeasureMode,
3907             heightMeasureMode,
3908             ownerWidth,
3909             ownerHeight,
3910             true,
3911             "initial",
3912             originalNode->getConfig())) {
3913       originalNode->setPosition(
3914           originalNode->getLayout().direction,
3915           ownerWidth,
3916           ownerHeight,
3917           ownerWidth);
3918       YGRoundToPixelGrid(
3919           originalNode,
3920           originalNode->getConfig()->pointScaleFactor,
3921           0.0f,
3922           0.0f);
3923
3924       // Set whether the two layouts are different or not.
3925       node->setLayoutDoesLegacyFlagAffectsLayout(
3926           !originalNode->isLayoutTreeEqualToNode(*node));
3927
3928       if (gPrintTree) {
3929         YGNodePrint(
3930             originalNode,
3931             (YGPrintOptions)(
3932                 YGPrintOptionsLayout | YGPrintOptionsChildren |
3933                 YGPrintOptionsStyle));
3934       }
3935     }
3936     YGConfigFreeRecursive(originalNode);
3937     YGNodeFreeRecursive(originalNode);
3938   }
3939 }
3940
3941 void YGConfigSetLogger(const YGConfigRef config, YGLogger logger) {
3942   if (logger != nullptr) {
3943     config->logger = logger;
3944   } else {
3945 #ifdef ANDROID
3946     config->logger = &YGAndroidLog;
3947 #else
3948     config->logger = &YGDefaultLog;
3949 #endif
3950   }
3951 }
3952
3953 void YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour(
3954     const YGConfigRef config,
3955     const bool shouldDiffLayout) {
3956   config->shouldDiffLayoutWithoutLegacyStretchBehaviour = shouldDiffLayout;
3957 }
3958
3959 static void YGVLog(const YGConfigRef config,
3960                    const YGNodeRef node,
3961                    YGLogLevel level,
3962                    const char *format,
3963                    va_list args) {
3964   const YGConfigRef logConfig = config != nullptr ? config : YGConfigGetDefault();
3965   logConfig->logger(logConfig, node, level, format, args);
3966
3967   if (level == YGLogLevelFatal) {
3968     abort();
3969   }
3970 }
3971
3972 void YGLogWithConfig(const YGConfigRef config, YGLogLevel level, const char *format, ...) {
3973   va_list args;
3974   va_start(args, format);
3975   YGVLog(config, nullptr, level, format, args);
3976   va_end(args);
3977 }
3978
3979 void YGLog(const YGNodeRef node, YGLogLevel level, const char *format, ...) {
3980   va_list args;
3981   va_start(args, format);
3982   YGVLog(
3983       node == nullptr ? nullptr : node->getConfig(), node, level, format, args);
3984   va_end(args);
3985 }
3986
3987 void YGAssert(const bool condition, const char *message) {
3988   if (!condition) {
3989     YGLog(nullptr, YGLogLevelFatal, "%s\n", message);
3990   }
3991 }
3992
3993 void YGAssertWithNode(const YGNodeRef node, const bool condition, const char *message) {
3994   if (!condition) {
3995     YGLog(node, YGLogLevelFatal, "%s\n", message);
3996   }
3997 }
3998
3999 void YGAssertWithConfig(const YGConfigRef config, const bool condition, const char *message) {
4000   if (!condition) {
4001     YGLogWithConfig(config, YGLogLevelFatal, "%s\n", message);
4002   }
4003 }
4004
4005 void YGConfigSetExperimentalFeatureEnabled(const YGConfigRef config,
4006                                            const YGExperimentalFeature feature,
4007                                            const bool enabled) {
4008   config->experimentalFeatures[feature] = enabled;
4009 }
4010
4011 inline bool YGConfigIsExperimentalFeatureEnabled(const YGConfigRef config,
4012                                                  const YGExperimentalFeature feature) {
4013   return config->experimentalFeatures[feature];
4014 }
4015
4016 void YGConfigSetUseWebDefaults(const YGConfigRef config, const bool enabled) {
4017   config->useWebDefaults = enabled;
4018 }
4019
4020 void YGConfigSetUseLegacyStretchBehaviour(const YGConfigRef config,
4021                                           const bool useLegacyStretchBehaviour) {
4022   config->useLegacyStretchBehaviour = useLegacyStretchBehaviour;
4023 }
4024
4025 bool YGConfigGetUseWebDefaults(const YGConfigRef config) {
4026   return config->useWebDefaults;
4027 }
4028
4029 void YGConfigSetContext(const YGConfigRef config, void *context) {
4030   config->context = context;
4031 }
4032
4033 void *YGConfigGetContext(const YGConfigRef config) {
4034   return config->context;
4035 }
4036
4037 void YGConfigSetCloneNodeFunc(const YGConfigRef config, const YGCloneNodeFunc callback) {
4038   config->cloneNodeCallback = callback;
4039 }
4040
4041 static void YGTraverseChildrenPreOrder(const YGVector& children, const std::function<void(YGNodeRef node)>& f) {
4042   for (YGNodeRef node : children) {
4043     f(node);
4044     YGTraverseChildrenPreOrder(node->getChildren(), f);
4045   }
4046 }
4047
4048 void YGTraversePreOrder(YGNodeRef const node, std::function<void(YGNodeRef node)>&& f) {
4049   if (!node) {
4050     return;
4051   }
4052   f(node);
4053   YGTraverseChildrenPreOrder(node->getChildren(), f);
4054 }