diff --git a/enums.py b/enums.py
index 2c53672a2e..ceef4deed3 100755
--- a/enums.py
+++ b/enums.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env fbpython
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
@@ -66,6 +66,9 @@
"ExperimentalFeature": [
# Mimic web flex-basis behavior (experiment may be broken)
"WebFlexBasis",
+ # Fix flex basis computation to not apply FitContent constraint in the
+ # main axis for non-measure container nodes
+ "FixFlexBasisFitContent",
],
"Gutter": ["Column", "Row", "All"],
"GridTrackType": ["Auto", "Points", "Percent", "Fr", "Minmax"],
diff --git a/gentest/fixtures/YGFlexBasisFitContentTest.html b/gentest/fixtures/YGFlexBasisFitContentTest.html
new file mode 100644
index 0000000000..a9176e4ce6
--- /dev/null
+++ b/gentest/fixtures/YGFlexBasisFitContentTest.html
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/com/facebook/yoga/YogaExperimentalFeature.java b/java/com/facebook/yoga/YogaExperimentalFeature.java
index 3fabbb9172..32e643439e 100644
--- a/java/com/facebook/yoga/YogaExperimentalFeature.java
+++ b/java/com/facebook/yoga/YogaExperimentalFeature.java
@@ -10,7 +10,8 @@
package com.facebook.yoga;
public enum YogaExperimentalFeature {
- WEB_FLEX_BASIS(0);
+ WEB_FLEX_BASIS(0),
+ FIX_FLEX_BASIS_FIT_CONTENT(1);
private final int mIntValue;
@@ -25,6 +26,7 @@ public int intValue() {
public static YogaExperimentalFeature fromInt(int value) {
switch (value) {
case 0: return WEB_FLEX_BASIS;
+ case 1: return FIX_FLEX_BASIS_FIT_CONTENT;
default: throw new IllegalArgumentException("Unknown enum value: " + value);
}
}
diff --git a/java/tests/generated/com/facebook/yoga/YGFlexBasisFitContentTest.java b/java/tests/generated/com/facebook/yoga/YGFlexBasisFitContentTest.java
new file mode 100644
index 0000000000..3941362bb5
--- /dev/null
+++ b/java/tests/generated/com/facebook/yoga/YGFlexBasisFitContentTest.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @generated SignedSource<<43053b7b4f9b08ef644e0bb63c44e78f>>
+ * generated by gentest/src/GentestDriver.ts from gentest/fixtures/YGFlexBasisFitContentTest.html
+ */
+
+package com.facebook.yoga;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import com.facebook.yoga.utils.TestUtils;
+
+@RunWith(Parameterized.class)
+public class YGFlexBasisFitContentTest {
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable nodeFactories() {
+ return TestParametrization.nodeFactories();
+ }
+
+ @Parameterized.Parameter public TestParametrization.NodeFactory mNodeFactory;
+
+ @Test
+ public void test_container_child_overflows_definite_parent_column() {
+ YogaConfig config = YogaConfigFactory.create();
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(200f);
+ root.setHeight(300f);
+
+ final YogaNode root_child0 = createNode(config);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child0_child0 = createNode(config);
+ root_child0_child0.setHeight(500f);
+ root_child0_child0.setWidth(50f);
+ root_child0.addChildAt(root_child0_child0, 0);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(50f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(150f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(50f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0_child0.getLayoutHeight(), 0.0f);
+ }
+
+ @Test
+ public void test_container_child_overflows_definite_parent_row() {
+ YogaConfig config = YogaConfigFactory.create();
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(300f);
+ root.setHeight(200f);
+ root.setFlexDirection(YogaFlexDirection.ROW);
+
+ final YogaNode root_child0 = createNode(config);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child0_child0 = createNode(config);
+ root_child0_child0.setWidth(500f);
+ root_child0_child0.setHeight(50f);
+ root_child0.addChildAt(root_child0_child0, 0);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(300f, root.getLayoutWidth(), 0.0f);
+ assertEquals(200f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(500f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(50f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(300f, root.getLayoutWidth(), 0.0f);
+ assertEquals(200f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(-200f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(500f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(50f, root_child0_child0.getLayoutHeight(), 0.0f);
+ }
+
+ @Test
+ public void test_container_child_within_bounds_column() {
+ YogaConfig config = YogaConfigFactory.create();
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(200f);
+ root.setHeight(300f);
+
+ final YogaNode root_child0 = createNode(config);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child0_child0 = createNode(config);
+ root_child0_child0.setHeight(100f);
+ root_child0_child0.setWidth(50f);
+ root_child0.addChildAt(root_child0_child0, 0);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(100f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(50f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(100f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(100f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(150f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(50f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(100f, root_child0_child0.getLayoutHeight(), 0.0f);
+ }
+
+ @Test
+ public void test_multiple_container_children_overflow_column() {
+ YogaConfig config = YogaConfigFactory.create();
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(200f);
+ root.setHeight(300f);
+
+ final YogaNode root_child0 = createNode(config);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child0_child0 = createNode(config);
+ root_child0_child0.setHeight(400f);
+ root_child0.addChildAt(root_child0_child0, 0);
+
+ final YogaNode root_child1 = createNode(config);
+ root.addChildAt(root_child1, 1);
+
+ final YogaNode root_child1_child0 = createNode(config);
+ root_child1_child0.setHeight(500f);
+ root_child1.addChildAt(root_child1_child0, 0);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(400f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(400f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1.getLayoutX(), 0.0f);
+ assertEquals(400f, root_child1.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child1_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1_child0.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(400f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(400f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1.getLayoutX(), 0.0f);
+ assertEquals(400f, root_child1.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child1_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1_child0.getLayoutHeight(), 0.0f);
+ }
+
+ @Test
+ public void test_scroll_container_column() {
+ YogaConfig config = YogaConfigFactory.create();
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(200f);
+ root.setHeight(300f);
+ root.setOverflow(YogaOverflow.SCROLL);
+
+ final YogaNode root_child0 = createNode(config);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child0_child0 = createNode(config);
+ root_child0_child0.setHeight(500f);
+ root_child0.addChildAt(root_child0_child0, 0);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0_child0.getLayoutHeight(), 0.0f);
+ }
+
+ @Test
+ public void test_explicit_and_container_children_column() {
+ YogaConfig config = YogaConfigFactory.create();
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(200f);
+ root.setHeight(300f);
+
+ final YogaNode root_child0 = createNode(config);
+ root_child0.setHeight(100f);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child1 = createNode(config);
+ root.addChildAt(root_child1, 1);
+
+ final YogaNode root_child1_child0 = createNode(config);
+ root_child1_child0.setHeight(500f);
+ root_child1.addChildAt(root_child1_child0, 0);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(100f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1.getLayoutX(), 0.0f);
+ assertEquals(100f, root_child1.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child1_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1_child0.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(100f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1.getLayoutX(), 0.0f);
+ assertEquals(100f, root_child1.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child1_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child1_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child1_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child1_child0.getLayoutHeight(), 0.0f);
+ }
+
+ @Test
+ public void test_flex_basis_in_scroll_content_container() {
+ YogaConfig config = YogaConfigFactory.create();
+ config.setExperimentalFeatureEnabled(YogaExperimentalFeature.FIX_FLEX_BASIS_FIT_CONTENT, true);
+
+ final YogaNode root = createNode(config);
+ root.setPositionType(YogaPositionType.ABSOLUTE);
+ root.setWidth(200f);
+ root.setHeight(300f);
+ root.setOverflow(YogaOverflow.SCROLL);
+
+ final YogaNode root_child0 = createNode(config);
+ root.addChildAt(root_child0, 0);
+
+ final YogaNode root_child0_child0 = createNode(config);
+ root_child0_child0.setFlexBasis(200f);
+ root_child0.addChildAt(root_child0_child0, 0);
+
+ final YogaNode root_child0_child1 = createNode(config);
+ root_child0_child1.setFlexBasis(300f);
+ root_child0.addChildAt(root_child0_child1, 1);
+ root.setDirection(YogaDirection.LTR);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child1.getLayoutX(), 0.0f);
+ assertEquals(200f, root_child0_child1.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child1.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root_child0_child1.getLayoutHeight(), 0.0f);
+
+ root.setDirection(YogaDirection.RTL);
+ root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
+
+ assertEquals(0f, root.getLayoutX(), 0.0f);
+ assertEquals(0f, root.getLayoutY(), 0.0f);
+ assertEquals(200f, root.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0.getLayoutWidth(), 0.0f);
+ assertEquals(500f, root_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child0.getLayoutX(), 0.0f);
+ assertEquals(0f, root_child0_child0.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutWidth(), 0.0f);
+ assertEquals(200f, root_child0_child0.getLayoutHeight(), 0.0f);
+
+ assertEquals(0f, root_child0_child1.getLayoutX(), 0.0f);
+ assertEquals(200f, root_child0_child1.getLayoutY(), 0.0f);
+ assertEquals(200f, root_child0_child1.getLayoutWidth(), 0.0f);
+ assertEquals(300f, root_child0_child1.getLayoutHeight(), 0.0f);
+ }
+
+ private YogaNode createNode(YogaConfig config) {
+ return mNodeFactory.create(config);
+ }
+}
diff --git a/javascript/src/generated/YGEnums.ts b/javascript/src/generated/YGEnums.ts
index f50aa88a73..17878ab0bb 100644
--- a/javascript/src/generated/YGEnums.ts
+++ b/javascript/src/generated/YGEnums.ts
@@ -67,6 +67,7 @@ export enum Errata {
export enum ExperimentalFeature {
WebFlexBasis = 0,
+ FixFlexBasisFitContent = 1,
}
export enum FlexDirection {
@@ -190,6 +191,7 @@ const constants = {
ERRATA_ALL: Errata.All,
ERRATA_CLASSIC: Errata.Classic,
EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS: ExperimentalFeature.WebFlexBasis,
+ EXPERIMENTAL_FEATURE_FIX_FLEX_BASIS_FIT_CONTENT: ExperimentalFeature.FixFlexBasisFitContent,
FLEX_DIRECTION_COLUMN: FlexDirection.Column,
FLEX_DIRECTION_COLUMN_REVERSE: FlexDirection.ColumnReverse,
FLEX_DIRECTION_ROW: FlexDirection.Row,
diff --git a/javascript/tests/generated/YGFlexBasisFitContentTest.test.ts b/javascript/tests/generated/YGFlexBasisFitContentTest.test.ts
new file mode 100644
index 0000000000..1e5f8c7c44
--- /dev/null
+++ b/javascript/tests/generated/YGFlexBasisFitContentTest.test.ts
@@ -0,0 +1,429 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @generated SignedSource<>
+ * generated by gentest/src/GentestDriver.ts from gentest/fixtures/YGFlexBasisFitContentTest.html
+ */
+
+import { instrinsicSizeMeasureFunc } from '../tools/utils.ts'
+import Yoga from 'yoga-layout';
+import {
+ Align,
+ BoxSizing,
+ Direction,
+ Display,
+ Edge,
+ Errata,
+ ExperimentalFeature,
+ FlexDirection,
+ Gutter,
+ Justify,
+ MeasureMode,
+ Overflow,
+ PositionType,
+ Unit,
+ Wrap,
+} from 'yoga-layout';
+
+test('container_child_overflows_definite_parent_column', () => {
+ const config = Yoga.Config.create();
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(200);
+ root.setHeight(300);
+
+ const root_child0 = Yoga.Node.create(config);
+ root.insertChild(root_child0, 0);
+
+ const root_child0_child0 = Yoga.Node.create(config);
+ root_child0_child0.setHeight(500);
+ root_child0_child0.setWidth(50);
+ root_child0.insertChild(root_child0_child0, 0);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(500);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(50);
+ expect(root_child0_child0.getComputedHeight()).toBe(500);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(500);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(150);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(50);
+ expect(root_child0_child0.getComputedHeight()).toBe(500);
+});
+test('container_child_overflows_definite_parent_row', () => {
+ const config = Yoga.Config.create();
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(300);
+ root.setHeight(200);
+ root.setFlexDirection(FlexDirection.Row);
+
+ const root_child0 = Yoga.Node.create(config);
+ root.insertChild(root_child0, 0);
+
+ const root_child0_child0 = Yoga.Node.create(config);
+ root_child0_child0.setWidth(500);
+ root_child0_child0.setHeight(50);
+ root_child0.insertChild(root_child0_child0, 0);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(300);
+ expect(root.getComputedHeight()).toBe(200);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(500);
+ expect(root_child0.getComputedHeight()).toBe(200);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(500);
+ expect(root_child0_child0.getComputedHeight()).toBe(50);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(300);
+ expect(root.getComputedHeight()).toBe(200);
+
+ expect(root_child0.getComputedLeft()).toBe(-200);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(500);
+ expect(root_child0.getComputedHeight()).toBe(200);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(500);
+ expect(root_child0_child0.getComputedHeight()).toBe(50);
+});
+test('container_child_within_bounds_column', () => {
+ const config = Yoga.Config.create();
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(200);
+ root.setHeight(300);
+
+ const root_child0 = Yoga.Node.create(config);
+ root.insertChild(root_child0, 0);
+
+ const root_child0_child0 = Yoga.Node.create(config);
+ root_child0_child0.setHeight(100);
+ root_child0_child0.setWidth(50);
+ root_child0.insertChild(root_child0_child0, 0);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(100);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(50);
+ expect(root_child0_child0.getComputedHeight()).toBe(100);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(100);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(150);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(50);
+ expect(root_child0_child0.getComputedHeight()).toBe(100);
+});
+test('multiple_container_children_overflow_column', () => {
+ const config = Yoga.Config.create();
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(200);
+ root.setHeight(300);
+
+ const root_child0 = Yoga.Node.create(config);
+ root.insertChild(root_child0, 0);
+
+ const root_child0_child0 = Yoga.Node.create(config);
+ root_child0_child0.setHeight(400);
+ root_child0.insertChild(root_child0_child0, 0);
+
+ const root_child1 = Yoga.Node.create(config);
+ root.insertChild(root_child1, 1);
+
+ const root_child1_child0 = Yoga.Node.create(config);
+ root_child1_child0.setHeight(500);
+ root_child1.insertChild(root_child1_child0, 0);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(400);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(200);
+ expect(root_child0_child0.getComputedHeight()).toBe(400);
+
+ expect(root_child1.getComputedLeft()).toBe(0);
+ expect(root_child1.getComputedTop()).toBe(400);
+ expect(root_child1.getComputedWidth()).toBe(200);
+ expect(root_child1.getComputedHeight()).toBe(500);
+
+ expect(root_child1_child0.getComputedLeft()).toBe(0);
+ expect(root_child1_child0.getComputedTop()).toBe(0);
+ expect(root_child1_child0.getComputedWidth()).toBe(200);
+ expect(root_child1_child0.getComputedHeight()).toBe(500);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(400);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(200);
+ expect(root_child0_child0.getComputedHeight()).toBe(400);
+
+ expect(root_child1.getComputedLeft()).toBe(0);
+ expect(root_child1.getComputedTop()).toBe(400);
+ expect(root_child1.getComputedWidth()).toBe(200);
+ expect(root_child1.getComputedHeight()).toBe(500);
+
+ expect(root_child1_child0.getComputedLeft()).toBe(0);
+ expect(root_child1_child0.getComputedTop()).toBe(0);
+ expect(root_child1_child0.getComputedWidth()).toBe(200);
+ expect(root_child1_child0.getComputedHeight()).toBe(500);
+});
+test('scroll_container_column', () => {
+ const config = Yoga.Config.create();
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(200);
+ root.setHeight(300);
+ root.setOverflow(Overflow.Scroll);
+
+ const root_child0 = Yoga.Node.create(config);
+ root.insertChild(root_child0, 0);
+
+ const root_child0_child0 = Yoga.Node.create(config);
+ root_child0_child0.setHeight(500);
+ root_child0.insertChild(root_child0_child0, 0);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(500);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(200);
+ expect(root_child0_child0.getComputedHeight()).toBe(500);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(500);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(200);
+ expect(root_child0_child0.getComputedHeight()).toBe(500);
+});
+test('explicit_and_container_children_column', () => {
+ const config = Yoga.Config.create();
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(200);
+ root.setHeight(300);
+
+ const root_child0 = Yoga.Node.create(config);
+ root_child0.setHeight(100);
+ root.insertChild(root_child0, 0);
+
+ const root_child1 = Yoga.Node.create(config);
+ root.insertChild(root_child1, 1);
+
+ const root_child1_child0 = Yoga.Node.create(config);
+ root_child1_child0.setHeight(500);
+ root_child1.insertChild(root_child1_child0, 0);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(100);
+
+ expect(root_child1.getComputedLeft()).toBe(0);
+ expect(root_child1.getComputedTop()).toBe(100);
+ expect(root_child1.getComputedWidth()).toBe(200);
+ expect(root_child1.getComputedHeight()).toBe(500);
+
+ expect(root_child1_child0.getComputedLeft()).toBe(0);
+ expect(root_child1_child0.getComputedTop()).toBe(0);
+ expect(root_child1_child0.getComputedWidth()).toBe(200);
+ expect(root_child1_child0.getComputedHeight()).toBe(500);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(100);
+
+ expect(root_child1.getComputedLeft()).toBe(0);
+ expect(root_child1.getComputedTop()).toBe(100);
+ expect(root_child1.getComputedWidth()).toBe(200);
+ expect(root_child1.getComputedHeight()).toBe(500);
+
+ expect(root_child1_child0.getComputedLeft()).toBe(0);
+ expect(root_child1_child0.getComputedTop()).toBe(0);
+ expect(root_child1_child0.getComputedWidth()).toBe(200);
+ expect(root_child1_child0.getComputedHeight()).toBe(500);
+});
+test('flex_basis_in_scroll_content_container', () => {
+ const config = Yoga.Config.create();
+
+ config.setExperimentalFeatureEnabled(ExperimentalFeature.FixFlexBasisFitContent, true);
+
+ const root = Yoga.Node.create(config);
+ root.setPositionType(PositionType.Absolute);
+ root.setWidth(200);
+ root.setHeight(300);
+ root.setOverflow(Overflow.Scroll);
+
+ const root_child0 = Yoga.Node.create(config);
+ root.insertChild(root_child0, 0);
+
+ const root_child0_child0 = Yoga.Node.create(config);
+ root_child0_child0.setFlexBasis(200);
+ root_child0.insertChild(root_child0_child0, 0);
+
+ const root_child0_child1 = Yoga.Node.create(config);
+ root_child0_child1.setFlexBasis(300);
+ root_child0.insertChild(root_child0_child1, 1);
+ root.calculateLayout(undefined, undefined, Direction.LTR);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(500);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(200);
+ expect(root_child0_child0.getComputedHeight()).toBe(200);
+
+ expect(root_child0_child1.getComputedLeft()).toBe(0);
+ expect(root_child0_child1.getComputedTop()).toBe(200);
+ expect(root_child0_child1.getComputedWidth()).toBe(200);
+ expect(root_child0_child1.getComputedHeight()).toBe(300);
+
+ root.calculateLayout(undefined, undefined, Direction.RTL);
+
+ expect(root.getComputedLeft()).toBe(0);
+ expect(root.getComputedTop()).toBe(0);
+ expect(root.getComputedWidth()).toBe(200);
+ expect(root.getComputedHeight()).toBe(300);
+
+ expect(root_child0.getComputedLeft()).toBe(0);
+ expect(root_child0.getComputedTop()).toBe(0);
+ expect(root_child0.getComputedWidth()).toBe(200);
+ expect(root_child0.getComputedHeight()).toBe(500);
+
+ expect(root_child0_child0.getComputedLeft()).toBe(0);
+ expect(root_child0_child0.getComputedTop()).toBe(0);
+ expect(root_child0_child0.getComputedWidth()).toBe(200);
+ expect(root_child0_child0.getComputedHeight()).toBe(200);
+
+ expect(root_child0_child1.getComputedLeft()).toBe(0);
+ expect(root_child0_child1.getComputedTop()).toBe(200);
+ expect(root_child0_child1.getComputedWidth()).toBe(200);
+ expect(root_child0_child1.getComputedHeight()).toBe(300);
+});
diff --git a/tests/YGFlexBasisFitContentTest.cpp b/tests/YGFlexBasisFitContentTest.cpp
new file mode 100644
index 0000000000..7ce7873497
--- /dev/null
+++ b/tests/YGFlexBasisFitContentTest.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+#include
+#include
+#include
+
+static YGSize measureTextLike(
+ YGNodeConstRef /*node*/,
+ float width,
+ YGMeasureMode widthMode,
+ float /*height*/,
+ YGMeasureMode /*heightMode*/) {
+ float measuredWidth = 200.0f;
+ if (widthMode == YGMeasureModeAtMost) {
+ measuredWidth = std::min(measuredWidth, width);
+ }
+ return YGSize{.width = measuredWidth, .height = 20.0f};
+}
+
+class YGFlexBasisFitContentTest : public testing::TestWithParam {
+ protected:
+ void SetUp() override {
+ config_ = YGConfigNew();
+ YGConfigSetExperimentalFeatureEnabled(
+ config_, YGExperimentalFeatureFixFlexBasisFitContent, GetParam());
+ }
+
+ void TearDown() override {
+ if (root_ != nullptr) {
+ YGNodeFreeRecursive(root_);
+ }
+ YGConfigFree(config_);
+ }
+
+ YGConfigRef config_ = nullptr;
+ YGNodeRef root_ = nullptr;
+};
+
+// Auto-height container with a percentage-height child produces the same
+// layout regardless of feature state, because Check 3 preserves percentage
+// resolution when availableInnerHeight is NaN.
+TEST_P(YGFlexBasisFitContentTest, percentage_height_converges) {
+ root_ = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeight(root_, 300);
+ YGNodeStyleSetWidth(root_, 100);
+
+ YGNodeRef container = YGNodeNewWithConfig(config_);
+ YGNodeInsertChild(root_, container, 0);
+
+ YGNodeRef child = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeightPercent(child, 50);
+ YGNodeInsertChild(container, child, 0);
+
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(75, YGNodeLayoutGetHeight(child));
+ ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(container));
+}
+
+// Two auto-height containers with percentage children and flexGrow:1 produce
+// the same layout regardless of feature state.
+TEST_P(YGFlexBasisFitContentTest, percentage_with_flex_grow_converges) {
+ root_ = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeight(root_, 400);
+ YGNodeStyleSetWidth(root_, 100);
+
+ YGNodeRef containerA = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetFlexGrow(containerA, 1);
+ YGNodeInsertChild(root_, containerA, 0);
+
+ YGNodeRef childA = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeightPercent(childA, 25);
+ YGNodeInsertChild(containerA, childA, 0);
+
+ YGNodeRef containerB = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetFlexGrow(containerB, 1);
+ YGNodeInsertChild(root_, containerB, 1);
+
+ YGNodeRef childB = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeightPercent(childB, 50);
+ YGNodeInsertChild(containerB, childB, 0);
+
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(containerA));
+ ASSERT_FLOAT_EQ(250, YGNodeLayoutGetHeight(containerB));
+}
+
+// Auto-height container with flexShrink and a percentage child causing
+// overflow produces the same layout regardless of feature state.
+TEST_P(YGFlexBasisFitContentTest, flex_shrink_overflow_converges) {
+ root_ = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeight(root_, 200);
+ YGNodeStyleSetWidth(root_, 100);
+
+ YGNodeRef container = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetFlexShrink(container, 1);
+ YGNodeInsertChild(root_, container, 0);
+
+ YGNodeRef child = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeightPercent(child, 100);
+ YGNodeInsertChild(container, child, 0);
+
+ YGNodeRef fixed = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeight(fixed, 150);
+ YGNodeInsertChild(root_, fixed, 1);
+
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(container));
+ ASSERT_FLOAT_EQ(150, YGNodeLayoutGetHeight(fixed));
+}
+
+// In a scroll container (column), changing a sibling's height does not cause
+// re-measurement of unaffected subtrees when the feature is enabled.
+TEST_P(YGFlexBasisFitContentTest, scroll_avoids_remeasure) {
+ static uint32_t measureCount = 0;
+ auto measureFunc = [](YGNodeConstRef /*node*/,
+ float /*width*/,
+ YGMeasureMode /*widthMode*/,
+ float /*height*/,
+ YGMeasureMode /*heightMode*/) {
+ measureCount++;
+ return YGSize{.width = 50.0f, .height = 50.0f};
+ };
+
+ measureCount = 0;
+
+ root_ = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetOverflow(root_, YGOverflowScroll);
+ YGNodeStyleSetWidth(root_, 100);
+ YGNodeStyleSetHeight(root_, 500);
+
+ YGNodeRef sibling = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetHeight(sibling, 100);
+ YGNodeInsertChild(root_, sibling, 0);
+
+ YGNodeRef wrapper = YGNodeNewWithConfig(config_);
+ YGNodeInsertChild(root_, wrapper, 1);
+
+ YGNodeRef inner = YGNodeNewWithConfig(config_);
+ YGNodeInsertChild(wrapper, inner, 0);
+
+ YGNodeRef leaf = YGNodeNewWithConfig(config_);
+ YGNodeSetMeasureFunc(leaf, measureFunc);
+ YGNodeInsertChild(inner, leaf, 0);
+
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+ uint32_t firstPassCount = measureCount;
+
+ YGNodeStyleSetHeight(sibling, 200);
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+ uint32_t secondPassCount = measureCount - firstPassCount;
+
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(leaf));
+
+ EXPECT_EQ(0, secondPassCount);
+}
+
+// Row direction is unaffected by the optimization. Width FitContent is always
+// preserved to support text wrapping through container nodes.
+TEST_P(YGFlexBasisFitContentTest, row_direction_unchanged) {
+ root_ = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetWidth(root_, 100);
+ YGNodeStyleSetHeight(root_, 100);
+
+ YGNodeRef container = YGNodeNewWithConfig(config_);
+ YGNodeInsertChild(root_, container, 0);
+
+ YGNodeRef text = YGNodeNewWithConfig(config_);
+ YGNodeSetMeasureFunc(text, measureTextLike);
+ YGNodeInsertChild(container, text, 0);
+
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetWidth(text));
+}
+
+// Scroll container in row direction: width FitContent is skipped for the
+// main axis (row) in scroll containers, matching legacy behavior.
+TEST_P(YGFlexBasisFitContentTest, row_scroll_skips_width) {
+ root_ = YGNodeNewWithConfig(config_);
+ YGNodeStyleSetFlexDirection(root_, YGFlexDirectionRow);
+ YGNodeStyleSetOverflow(root_, YGOverflowScroll);
+ YGNodeStyleSetWidth(root_, 100);
+ YGNodeStyleSetHeight(root_, 100);
+
+ YGNodeRef text = YGNodeNewWithConfig(config_);
+ YGNodeSetMeasureFunc(text, measureTextLike);
+ YGNodeInsertChild(root_, text, 0);
+
+ YGNodeCalculateLayout(root_, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(text));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ YogaTest,
+ YGFlexBasisFitContentTest,
+ testing::Values(false, true));
+
+// Feature toggle invalidates layout cache.
+TEST(YogaTest, flex_basis_fit_content_feature_change_invalidates_cache) {
+ YGConfigRef config = YGConfigNew();
+ YGConfigSetExperimentalFeatureEnabled(
+ config, YGExperimentalFeatureFixFlexBasisFitContent, false);
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root, 300);
+ YGNodeStyleSetWidth(root, 100);
+
+ YGNodeRef container = YGNodeNewWithConfig(config);
+ YGNodeStyleSetFlexGrow(container, 1);
+ YGNodeInsertChild(root, container, 0);
+
+ YGNodeRef child = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeightPercent(child, 50);
+ YGNodeInsertChild(container, child, 0);
+
+ YGNodeRef fixed = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(fixed, 100);
+ YGNodeInsertChild(root, fixed, 1);
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+ float heightBefore = YGNodeLayoutGetHeight(container);
+
+ YGConfigSetExperimentalFeatureEnabled(
+ config, YGExperimentalFeatureFixFlexBasisFitContent, true);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+ float heightAfter = YGNodeLayoutGetHeight(container);
+
+ ASSERT_FLOAT_EQ(heightBefore, heightAfter);
+
+ YGNodeFreeRecursive(root);
+ YGConfigFree(config);
+}
diff --git a/tests/generated/YGFlexBasisFitContentTest.cpp b/tests/generated/YGFlexBasisFitContentTest.cpp
new file mode 100644
index 0000000000..b1dea09665
--- /dev/null
+++ b/tests/generated/YGFlexBasisFitContentTest.cpp
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * clang-format off
+ * @generated SignedSource<>
+ * generated by gentest/src/GentestDriver.ts from gentest/fixtures/YGFlexBasisFitContentTest.html
+ */
+
+#include
+#include
+#include "../util/TestUtil.h"
+
+TEST(YogaTest, container_child_overflows_definite_parent_column) {
+ YGConfigRef config = YGConfigNew();
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 200);
+ YGNodeStyleSetHeight(root, 300);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child0_child0, 500);
+ YGNodeStyleSetWidth(root_child0_child0, 50);
+ YGNodeInsertChild(root_child0, root_child0_child0, 0);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
+
+TEST(YogaTest, container_child_overflows_definite_parent_row) {
+ YGConfigRef config = YGConfigNew();
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 300);
+ YGNodeStyleSetHeight(root, 200);
+ YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetWidth(root_child0_child0, 500);
+ YGNodeStyleSetHeight(root_child0_child0, 50);
+ YGNodeInsertChild(root_child0, root_child0_child0, 0);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(-200, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
+
+TEST(YogaTest, container_child_within_bounds_column) {
+ YGConfigRef config = YGConfigNew();
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 200);
+ YGNodeStyleSetHeight(root, 300);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child0_child0, 100);
+ YGNodeStyleSetWidth(root_child0_child0, 50);
+ YGNodeInsertChild(root_child0, root_child0_child0, 0);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(150, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(50, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
+
+TEST(YogaTest, multiple_container_children_overflow_column) {
+ YGConfigRef config = YGConfigNew();
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 200);
+ YGNodeStyleSetHeight(root, 300);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child0_child0, 400);
+ YGNodeInsertChild(root_child0, root_child0_child0, 0);
+
+ YGNodeRef root_child1 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child1, 1);
+
+ YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child1_child0, 500);
+ YGNodeInsertChild(root_child1, root_child1_child0, 0);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root_child0_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
+ ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1_child0));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(400, YGNodeLayoutGetHeight(root_child0_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
+ ASSERT_FLOAT_EQ(400, YGNodeLayoutGetTop(root_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1_child0));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
+
+TEST(YogaTest, scroll_container_column) {
+ YGConfigRef config = YGConfigNew();
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 200);
+ YGNodeStyleSetHeight(root, 300);
+ YGNodeStyleSetOverflow(root, YGOverflowScroll);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child0_child0, 500);
+ YGNodeInsertChild(root_child0, root_child0_child0, 0);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0_child0));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
+
+TEST(YogaTest, explicit_and_container_children_column) {
+ YGConfigRef config = YGConfigNew();
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 200);
+ YGNodeStyleSetHeight(root, 300);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child0, 100);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child1 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child1, 1);
+
+ YGNodeRef root_child1_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetHeight(root_child1_child0, 500);
+ YGNodeInsertChild(root_child1, root_child1_child0, 0);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1_child0));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1));
+ ASSERT_FLOAT_EQ(100, YGNodeLayoutGetTop(root_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child1_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child1_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child1_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child1_child0));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
+
+TEST(YogaTest, flex_basis_in_scroll_content_container) {
+ YGConfigRef config = YGConfigNew();
+ YGConfigSetExperimentalFeatureEnabled(config, YGExperimentalFeatureFixFlexBasisFitContent, true);
+
+ YGNodeRef root = YGNodeNewWithConfig(config);
+ YGNodeStyleSetPositionType(root, YGPositionTypeAbsolute);
+ YGNodeStyleSetWidth(root, 200);
+ YGNodeStyleSetHeight(root, 300);
+ YGNodeStyleSetOverflow(root, YGOverflowScroll);
+
+ YGNodeRef root_child0 = YGNodeNewWithConfig(config);
+ YGNodeInsertChild(root, root_child0, 0);
+
+ YGNodeRef root_child0_child0 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetFlexBasis(root_child0_child0, 200);
+ YGNodeInsertChild(root_child0, root_child0_child0, 0);
+
+ YGNodeRef root_child0_child1 = YGNodeNewWithConfig(config);
+ YGNodeStyleSetFlexBasis(root_child0_child1, 300);
+ YGNodeInsertChild(root_child0, root_child0_child1, 1);
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child0_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child1));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root_child0_child1));
+
+ YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionRTL);
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0));
+ ASSERT_FLOAT_EQ(500, YGNodeLayoutGetHeight(root_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child0));
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetTop(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child0));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetHeight(root_child0_child0));
+
+ ASSERT_FLOAT_EQ(0, YGNodeLayoutGetLeft(root_child0_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetTop(root_child0_child1));
+ ASSERT_FLOAT_EQ(200, YGNodeLayoutGetWidth(root_child0_child1));
+ ASSERT_FLOAT_EQ(300, YGNodeLayoutGetHeight(root_child0_child1));
+
+ YGNodeFreeRecursive(root);
+
+ YGConfigFree(config);
+}
diff --git a/yoga/YGEnums.cpp b/yoga/YGEnums.cpp
index 9fc4a83a82..538b0b0847 100644
--- a/yoga/YGEnums.cpp
+++ b/yoga/YGEnums.cpp
@@ -129,6 +129,8 @@ const char* YGExperimentalFeatureToString(const YGExperimentalFeature value) {
switch (value) {
case YGExperimentalFeatureWebFlexBasis:
return "web-flex-basis";
+ case YGExperimentalFeatureFixFlexBasisFitContent:
+ return "fix-flex-basis-fit-content";
}
return "unknown";
}
diff --git a/yoga/YGEnums.h b/yoga/YGEnums.h
index 1b69f09318..aa0b1d4350 100644
--- a/yoga/YGEnums.h
+++ b/yoga/YGEnums.h
@@ -73,7 +73,8 @@ YG_DEFINE_ENUM_FLAG_OPERATORS(YGErrata)
YG_ENUM_DECL(
YGExperimentalFeature,
- YGExperimentalFeatureWebFlexBasis)
+ YGExperimentalFeatureWebFlexBasis,
+ YGExperimentalFeatureFixFlexBasisFitContent)
YG_ENUM_DECL(
YGFlexDirection,
diff --git a/yoga/algorithm/CalculateLayout.cpp b/yoga/algorithm/CalculateLayout.cpp
index 0a7224cc0b..d25435071b 100644
--- a/yoga/algorithm/CalculateLayout.cpp
+++ b/yoga/algorithm/CalculateLayout.cpp
@@ -94,7 +94,18 @@ static void computeFlexBasisForChild(
const bool isColumnStyleDimDefined =
child->hasDefiniteLength(Dimension::Height, ownerHeight);
- if (resolvedFlexBasis.isDefined() && yoga::isDefined(mainAxisSize)) {
+ const bool fixFlexBasisFitContent =
+ node->getConfig()->isExperimentalFeatureEnabled(
+ ExperimentalFeature::FixFlexBasisFitContent);
+
+ bool useResolvedFlexBasis =
+ resolvedFlexBasis.isDefined() && yoga::isDefined(mainAxisSize);
+ if (fixFlexBasisFitContent && resolvedFlexBasis.isDefined() &&
+ resolvedFlexBasis.unwrap() > 0) {
+ useResolvedFlexBasis = true;
+ }
+
+ if (useResolvedFlexBasis) {
if (child->getLayout().computedFlexBasis.isUndefined() ||
(child->getConfig()->isExperimentalFeatureEnabled(
ExperimentalFeature::WebFlexBasis) &&
@@ -164,12 +175,27 @@ static void computeFlexBasisForChild(
}
}
- if ((isMainAxisRow && node->style().overflow() == Overflow::Scroll) ||
- node->style().overflow() != Overflow::Scroll) {
- if (yoga::isUndefined(childHeight) && yoga::isDefined(height)) {
- childHeight = height;
- childHeightSizingMode = SizingMode::FitContent;
- }
+ // For height in the main axis (column direction): when the
+ // FixFlexBasisFitContent feature is enabled, skip FitContent for
+ // non-measure container children. This makes the flex basis independent
+ // of the parent's content-determined height, preventing unnecessary
+ // re-measurement cascades when a sibling changes size in a ScrollView.
+ //
+ // We only optimize the height (column) axis because text wrapping depends
+ // on width constraints propagating through container nodes. Removing
+ // FitContent from the width axis would cause text inside nested
+ // containers to stop wrapping.
+ bool applyHeightFitContent =
+ isMainAxisRow || node->style().overflow() != Overflow::Scroll;
+ if (fixFlexBasisFitContent) {
+ applyHeightFitContent = isMainAxisRow ||
+ (child->hasMeasureFunc() &&
+ node->style().overflow() != Overflow::Scroll);
+ }
+ if (applyHeightFitContent && yoga::isUndefined(childHeight) &&
+ yoga::isDefined(height)) {
+ childHeight = height;
+ childHeightSizingMode = SizingMode::FitContent;
}
const auto& childStyle = child->style();
@@ -537,6 +563,8 @@ static float computeFlexBasisForChildren(
yoga::Node* const node,
const float availableInnerWidth,
const float availableInnerHeight,
+ const float ownerWidth,
+ const float ownerHeight,
SizingMode widthSizingMode,
SizingMode heightSizingMode,
Direction direction,
@@ -598,8 +626,8 @@ static float computeFlexBasisForChildren(
availableInnerWidth,
widthSizingMode,
availableInnerHeight,
- availableInnerWidth,
- availableInnerHeight,
+ ownerWidth,
+ ownerHeight,
heightSizingMode,
direction,
layoutMarkerData,
@@ -1429,12 +1457,53 @@ static void calculateLayoutImpl(
// STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
+ // When this node is measured with MaxContent (FixFlexBasisFitContent
+ // behavior), availableInnerHeight is NaN.
+ // To preserve percentage resolution for descendants, derive a definite
+ // owner-size from the parent-provided ownerHeight.
+ float ownerWidthForChildren = availableInnerWidth;
+ float ownerHeightForChildren = availableInnerHeight;
+
+ if (node->getConfig()->isExperimentalFeatureEnabled(
+ ExperimentalFeature::FixFlexBasisFitContent)) {
+ const auto* owner = node->getOwner();
+ const bool isChildOfScrollContainer =
+ owner != nullptr && owner->style().overflow() == Overflow::Scroll;
+
+ if (!isChildOfScrollContainer) {
+ if (yoga::isUndefined(ownerWidthForChildren) &&
+ yoga::isDefined(ownerWidth)) {
+ ownerWidthForChildren = calculateAvailableInnerDimension(
+ node,
+ direction,
+ Dimension::Width,
+ ownerWidth - marginAxisRow,
+ paddingAndBorderAxisRow,
+ ownerWidth,
+ ownerWidth);
+ }
+ if (yoga::isUndefined(ownerHeightForChildren) &&
+ yoga::isDefined(ownerHeight)) {
+ ownerHeightForChildren = calculateAvailableInnerDimension(
+ node,
+ direction,
+ Dimension::Height,
+ ownerHeight - marginAxisColumn,
+ paddingAndBorderAxisColumn,
+ ownerHeight,
+ ownerWidth);
+ }
+ }
+ }
+
// Computed basis + margins + gap
float totalMainDim = 0;
totalMainDim += computeFlexBasisForChildren(
node,
availableInnerWidth,
availableInnerHeight,
+ ownerWidthForChildren,
+ ownerHeightForChildren,
widthSizingMode,
heightSizingMode,
direction,
diff --git a/yoga/enums/ExperimentalFeature.h b/yoga/enums/ExperimentalFeature.h
index bbbf9cda43..7e6c5eb8b6 100644
--- a/yoga/enums/ExperimentalFeature.h
+++ b/yoga/enums/ExperimentalFeature.h
@@ -17,11 +17,12 @@ namespace facebook::yoga {
enum class ExperimentalFeature : uint8_t {
WebFlexBasis = YGExperimentalFeatureWebFlexBasis,
+ FixFlexBasisFitContent = YGExperimentalFeatureFixFlexBasisFitContent,
};
template <>
constexpr int32_t ordinalCount() {
- return 1;
+ return 2;
}
constexpr ExperimentalFeature scopedEnum(YGExperimentalFeature unscoped) {