From 04c2cbf24b6c6450e13278c4684738159fbd4540 Mon Sep 17 00:00:00 2001 From: Yevhenii Date: Fri, 30 Oct 2026 15:49:40 +0200 Subject: [PATCH] Keep Yoga's min-content pass off committed nodes computeMinContentMainSize walks a flex item's subtree through getChild(i) and measures the leaves it reaches without cloning them first. An item's children are still the committed tree's until the item's own layout clones them, whether its measurement comes from its cache or its flex basis from its style, so the pass measures, and memoises into, nodes of the committed tree. A committed Paragraph there keeps content that points at the inline View its own layout replaced, which is freed by then: React Native crashes in ParagraphShadowNode::measureContent. The pass now clones a container's children before descending, as calculateLayoutImpl and layoutAbsoluteDescendants do before writing to them. So that it does not clone every item's subtree on every layout, a memoised min-content now holds as long as the node's cached measurements do: calculateLayoutInternal drops it with them when it lays out a changed node, and a later layout walks only the subtrees that changed. A container's memo is keyed on the owner's width only when a percentage margin or padding in the subtree it walked resolves against it, as a cached layout is since react/yoga#2042, so a new width alone does not walk an item whose layout comes from its cache. Since a memo now outlives a layout, YGNodeSetMinContentWidth and YGNodeSetMinContentHeight mark the node dirty when the value changes, as style setters do, and a min-content measure function's results are kept, as a measure function's are, until the node is marked dirty. React Native's measure connector asserts that the node it measures is not sealed, and Fantom registers LayoutConformance, so a test can lay out under strict conformance. --- .../View-flexboxAutoMinSize-itest.js | 90 ++++++++ .../Text-flexboxAutoMinSize-itest.js | 116 ++++++++++ .../view/YogaLayoutableShadowNode.cpp | 2 + .../ReactCommon/yoga/test/MinContentTest.cpp | 202 ++++++++++++++++++ .../ReactCommon/yoga/yoga/YGNode.cpp | 18 +- .../ReactCommon/yoga/yoga/YGNode.h | 6 +- .../yoga/yoga/algorithm/CalculateLayout.cpp | 117 +++++++--- .../yoga/yoga/node/LayoutResults.h | 13 +- .../ReactCommon/yoga/yoga/style/Style.h | 6 +- .../src/stubs/StubComponentRegistryFactory.h | 2 + .../api-snapshots/ReactAndroidDebugCxx.api | 2 + .../ReactAndroidFrameworksCxx.api | 2 + .../api-snapshots/ReactAndroidNewarchCxx.api | 2 + .../api-snapshots/ReactAndroidReleaseCxx.api | 2 + .../api-snapshots/ReactAppleDebugCxx.api | 2 + .../api-snapshots/ReactAppleFrameworksCxx.api | 2 + .../api-snapshots/ReactAppleNewarchCxx.api | 2 + .../api-snapshots/ReactAppleReleaseCxx.api | 2 + .../api-snapshots/ReactCommonDebugCxx.api | 2 + .../ReactCommonFrameworksCxx.api | 2 + .../api-snapshots/ReactCommonNewarchCxx.api | 2 + .../api-snapshots/ReactCommonReleaseCxx.api | 2 + 22 files changed, 556 insertions(+), 40 deletions(-) create mode 100644 packages/react-native/Libraries/Components/View/__tests__/View-flexboxAutoMinSize-itest.js create mode 100644 packages/react-native/Libraries/Text/__tests__/Text-flexboxAutoMinSize-itest.js create mode 100644 packages/react-native/ReactCommon/yoga/test/MinContentTest.cpp diff --git a/packages/react-native/Libraries/Components/View/__tests__/View-flexboxAutoMinSize-itest.js b/packages/react-native/Libraries/Components/View/__tests__/View-flexboxAutoMinSize-itest.js new file mode 100644 index 000000000000..6a2b6dc787a2 --- /dev/null +++ b/packages/react-native/Libraries/Components/View/__tests__/View-flexboxAutoMinSize-itest.js @@ -0,0 +1,90 @@ +/** + * 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. + * + * @flow strict-local + * @fantom_flags enableFlexboxAutoMinSizeInStrictMode:true + * @format + */ + +import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; + +import type {HostInstance} from 'react-native'; + +import * as Fantom from '@react-native/fantom'; +import nullthrows from 'nullthrows'; +import * as React from 'react'; +import {createRef} from 'react'; +import { + View, + experimental_LayoutConformance as LayoutConformance, +} from 'react-native'; + +type ColumnProps = Readonly<{ + contentPadding: number, + itemRef: React.RefSetter, + minHeight?: number, +}>; + +// Two items asking for 200 in a column of 100 shrink to 50 each, unless the +// first one's automatic minimum size, the padding of the view it holds, is +// larger. +function Column({contentPadding, itemRef, minHeight}: ColumnProps): React.Node { + return ( + + + + + + + + + + + ); +} + +describe('the automatic minimum size of a flex item under strict layout conformance', () => { + it('follows a change to its content', () => { + const root = Fantom.createRoot(); + const itemRef = createRef(); + + const render = (contentPadding: number) => { + Fantom.runTask(() => { + root.render( + , + ); + }); + return nullthrows(itemRef.current).getBoundingClientRect().height; + }; + + expect(render(30)).toBe(50); + expect(render(80)).toBe(80); + }); + + it('follows a change to its content laid out under an explicit minimum height', () => { + const root = Fantom.createRoot(); + const itemRef = createRef(); + + const render = (contentPadding: number, minHeight?: number) => { + Fantom.runTask(() => { + root.render( + , + ); + }); + return nullthrows(itemRef.current).getBoundingClientRect().height; + }; + + expect(render(30)).toBe(50); + // With explicit minimum heights nothing asks for the content's minimum + // size, so the change is laid out without it. + expect(render(80, 0)).toBe(50); + expect(render(80)).toBe(80); + }); +}); diff --git a/packages/react-native/Libraries/Text/__tests__/Text-flexboxAutoMinSize-itest.js b/packages/react-native/Libraries/Text/__tests__/Text-flexboxAutoMinSize-itest.js new file mode 100644 index 000000000000..86822602d8ba --- /dev/null +++ b/packages/react-native/Libraries/Text/__tests__/Text-flexboxAutoMinSize-itest.js @@ -0,0 +1,116 @@ +/** + * 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. + * + * @flow strict-local + * @fantom_flags enableFlexboxAutoMinSizeInStrictMode:true + * @format + */ + +import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; + +import type {HostInstance} from 'react-native'; + +import * as Fantom from '@react-native/fantom'; +import nullthrows from 'nullthrows'; +import * as React from 'react'; +import {createRef} from 'react'; +import { + Text, + View, + experimental_LayoutConformance as LayoutConformance, +} from 'react-native'; + +const paragraph = ( + + Open + +); + +const paragraphWithMinHeight = ( + + Open + +); + +type FooterScreenProps = Readonly<{ + footerHeight: number, + footerRef: React.RefSetter, +}>; + +function FooterScreen({ + footerHeight, + footerRef, +}: FooterScreenProps): React.Node { + return ( + + + {paragraph} + + + + ); +} + +type ItemScreenProps = Readonly<{ + itemMinHeight?: number, + itemRef: React.RefSetter, +}>; + +function ItemScreen({itemMinHeight, itemRef}: ItemScreenProps): React.Node { + return ( + + + + {paragraphWithMinHeight} + + + + ); +} + +describe(' holding a under strict layout conformance', () => { + it('lays out again after a sibling of the view holding it changes', () => { + const root = Fantom.createRoot(); + const footerRef = createRef(); + + const render = (footerHeight: number) => { + Fantom.runTask(() => { + root.render( + , + ); + }); + }; + + render(10); + // Only the footer changes, so the paragraph stays as the first render + // committed it while its container is laid out again. + render(20); + + expect(nullthrows(footerRef.current).getBoundingClientRect().height).toBe( + 20, + ); + }); + + it('lays out again after the view holding it drops its minimum height', () => { + const root = Fantom.createRoot(); + const itemRef = createRef(); + + const render = (itemMinHeight?: number) => { + Fantom.runTask(() => { + root.render( + , + ); + }); + }; + + // Explicit minimum heights keep the min-content pass out of the paragraph, + // so the pass first reaches it once the first render has committed it. + render(0); + render(); + + expect(nullthrows(itemRef.current).getBoundingClientRect().height).toBe(30); + }); +}); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp index 226385a6ec31..91892b2a878f 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp @@ -881,6 +881,8 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector( TraceSection s("YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector"); auto& shadowNode = shadowNodeFromContext(yogaNode); + // Measuring may write to the node, so it must not be a committed one. + shadowNode.ensureUnsealed(); auto minimumSize = Size{.width = 0, .height = 0}; auto maximumSize = Size{ diff --git a/packages/react-native/ReactCommon/yoga/test/MinContentTest.cpp b/packages/react-native/ReactCommon/yoga/test/MinContentTest.cpp new file mode 100644 index 000000000000..ac49cf49c21b --- /dev/null +++ b/packages/react-native/ReactCommon/yoga/test/MinContentTest.cpp @@ -0,0 +1,202 @@ +/* + * 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 + +namespace facebook::yoga { +namespace { + +struct MeasureCalls { + YGNodeConstRef original = nullptr; + int ofOriginal = 0; + int ofCopies = 0; +}; + +YGSize measureWord( + YGNodeConstRef node, + float /*width*/, + YGMeasureMode /*widthMode*/, + float /*height*/, + YGMeasureMode /*heightMode*/) { + auto* calls = static_cast(YGNodeGetContext(node)); + ++(node == calls->original ? calls->ofOriginal : calls->ofCopies); + return YGSize{.width = 30.0f, .height = 10.0f}; +} + +YGConfigRef autoMinSizeConfig() { + YGConfigRef config = YGConfigNew(); + YGConfigSetErrata(config, YGErrataNone); + return config; +} + +YGNodeRef measuredLeaf(YGConfigRef config, MeasureCalls* calls) { + YGNodeRef leaf = YGNodeNewWithConfig(config); + YGNodeSetMeasureFunc(leaf, measureWord); + YGNodeSetContext(leaf, calls); + calls->original = leaf; + return leaf; +} + +TEST(MinContentTest, testWalkClonesChildrenOfAnotherTree) { + YGConfigRef config = autoMinSizeConfig(); + MeasureCalls calls; + YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + YGNodeRef item = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(item, 30); + YGNodeStyleSetMinHeight(item, 0); + YGNodeInsertChild(root, item, 0); + // Explicit minimum heights keep every min-content walk away from the leaf. + YGNodeRef leaf = measuredLeaf(config, &calls); + YGNodeStyleSetMinHeight(leaf, 0); + YGNodeInsertChild(item, leaf, 0); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + // A second tree shares the leaf, under a copy of the item that drops its + // minimum height, so the walk first reaches the leaf in that tree. + YGNodeRef rootCopy = YGNodeClone(root); + YGNodeRef itemCopy = YGNodeClone(item); + YGNodeSwapChild(rootCopy, itemCopy, 0); + YGNodeStyleSetMinHeight(itemCopy, YGUndefined); + calls.ofOriginal = 0; + calls.ofCopies = 0; + YGNodeCalculateLayout(rootCopy, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_EQ(0, calls.ofOriginal); + EXPECT_GT(calls.ofCopies, 0); + + YGNodeFreeRecursive(rootCopy); + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(MinContentTest, testUnchangedItemIsNotMeasuredAgain) { + YGConfigRef config = autoMinSizeConfig(); + MeasureCalls calls; + YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 100); + YGNodeRef item = YGNodeNewWithConfig(config); + YGNodeInsertChild(root, item, 0); + YGNodeInsertChild(item, measuredLeaf(config, &calls), 0); + YGNodeRef sibling = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(sibling, 10); + YGNodeInsertChild(root, sibling, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeStyleSetHeight(sibling, 20); + calls.ofOriginal = 0; + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_EQ(0, calls.ofOriginal); + EXPECT_FLOAT_EQ(20.0f, YGNodeLayoutGetHeight(sibling)); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(MinContentTest, testStaticMinContentChangeReachesNextLayout) { + YGConfigRef config = autoMinSizeConfig(); + YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 20); + YGNodeStyleSetHeight(root, 50); + YGNodeRef item = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(item, 90); + YGNodeStyleSetFlexShrink(item, 1); + YGNodeInsertChild(root, item, 0); + YGNodeRef scroller = YGNodeNewWithConfig(config); + YGNodeSetMinContentWidth(scroller, 10); + YGNodeInsertChild(item, scroller, 0); + YGNodeRef spacer = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(spacer, 10); + YGNodeInsertChild(root, spacer, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(10.0f, YGNodeLayoutGetWidth(item)); + + YGNodeSetMinContentWidth(scroller, 30); + YGNodeStyleSetWidth(spacer, 5); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_FLOAT_EQ(30.0f, YGNodeLayoutGetWidth(item)); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(MinContentTest, testNewWidthDoesNotCloneItemContent) { + YGConfigRef config = autoMinSizeConfig(); + MeasureCalls calls; + YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetAlignItems(root, YGAlignFlexStart); + YGNodeStyleSetWidth(root, 300); + YGNodeStyleSetHeight(root, 100); + YGNodeRef item = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(item, 50); + YGNodeStyleSetHeight(item, 200); + YGNodeStyleSetFlexShrink(item, 1); + YGNodeInsertChild(root, item, 0); + YGNodeRef content = YGNodeNewWithConfig(config); + YGNodeInsertChild(content, measuredLeaf(config, &calls), 0); + YGNodeInsertChild(item, content, 0); + YGNodeRef sibling = YGNodeNewWithConfig(config); + YGNodeStyleSetHeight(sibling, 200); + YGNodeStyleSetFlexShrink(sibling, 1); + YGNodeInsertChild(root, sibling, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + // A narrower second tree shares the item's content. The item keeps its + // size, so its layout comes from cache, and nothing below it resolves a + // percentage against the new width. + YGNodeRef rootCopy = YGNodeClone(root); + YGNodeStyleSetWidth(rootCopy, 290); + YGNodeCalculateLayout(rootCopy, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_EQ(content, YGNodeGetChild(YGNodeGetChild(rootCopy, 0), 0)); + EXPECT_FLOAT_EQ(50.0f, YGNodeLayoutGetHeight(YGNodeGetChild(rootCopy, 0))); + + YGNodeFreeRecursive(rootCopy); + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(MinContentTest, testPercentagePaddingFollowsNewWidth) { + YGConfigRef config = autoMinSizeConfig(); + MeasureCalls calls; + YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 100); + YGNodeStyleSetHeight(root, 50); + YGNodeRef item = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(item, 100); + YGNodeStyleSetFlexShrink(item, 1); + YGNodeInsertChild(root, item, 0); + YGNodeRef content = YGNodeNewWithConfig(config); + YGNodeStyleSetPaddingPercent(content, YGEdgeLeft, 10); + YGNodeInsertChild(content, measuredLeaf(config, &calls), 0); + YGNodeInsertChild(item, content, 0); + YGNodeRef sibling = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexBasis(sibling, 300); + YGNodeStyleSetFlexShrink(sibling, 1); + YGNodeInsertChild(root, sibling, 1); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + ASSERT_FLOAT_EQ(40.0f, YGNodeLayoutGetWidth(item)); + + YGNodeStyleSetWidth(root, 150); + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + EXPECT_FLOAT_EQ(45.0f, YGNodeLayoutGetWidth(item)); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +} // namespace +} // namespace facebook::yoga diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp b/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp index 7e66845fc173..afadea4c89c9 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/YGNode.cpp @@ -320,12 +320,22 @@ bool YGNodeHasMinContentMeasureFunc(YGNodeConstRef node) { return resolveRef(node)->hasMinContentMeasureFunc(); } -void YGNodeSetMinContentWidth(YGNodeRef node, float minContentWidth) { - resolveRef(node)->setMinContentWidth(FloatOptional{minContentWidth}); +void YGNodeSetMinContentWidth(YGNodeRef nodeRef, float minContentWidth) { + const auto node = resolveRef(nodeRef); + const FloatOptional value{minContentWidth}; + if (node->getMinContentWidth() != value) { + node->setMinContentWidth(value); + node->markDirtyAndPropagate(); + } } -void YGNodeSetMinContentHeight(YGNodeRef node, float minContentHeight) { - resolveRef(node)->setMinContentHeight(FloatOptional{minContentHeight}); +void YGNodeSetMinContentHeight(YGNodeRef nodeRef, float minContentHeight) { + const auto node = resolveRef(nodeRef); + const FloatOptional value{minContentHeight}; + if (node->getMinContentHeight() != value) { + node->setMinContentHeight(value); + node->markDirtyAndPropagate(); + } } float YGNodeGetMinContentWidth(YGNodeConstRef node) { diff --git a/packages/react-native/ReactCommon/yoga/yoga/YGNode.h b/packages/react-native/ReactCommon/yoga/yoga/YGNode.h index 7a9b0e1c3f12..4f8cdf81a521 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/YGNode.h +++ b/packages/react-native/ReactCommon/yoga/yoga/YGNode.h @@ -235,7 +235,8 @@ YG_EXPORT bool YGNodeHasMeasureFunc(YGNodeConstRef node); * invoked with the same `AtMost 0` constraint — preserving today's * behavior. Setting this is a perf opt-in for Primitives whose min-content * size is cheap to compute or differs from what the regular measure would - * return. + * return. Its results are kept, as a measure function's are, until the node is + * marked dirty. */ typedef YGSize (*YGMinContentMeasureFunc)( YGNodeConstRef node, @@ -259,7 +260,8 @@ YG_EXPORT bool YGNodeHasMinContentMeasureFunc(YGNodeConstRef node); /** * Static per-axis min-content size used by the CSS Flexbox §4.5 automatic - * minimum sizing probe. Pass `YGUndefined` to clear. + * minimum sizing probe. Pass `YGUndefined` to clear. A new value marks the node + * dirty, as a style change does. * * When set, the algorithm's probe short-circuits at this node along the * matching axis — skipping both the regular `YGMeasureFunc` / `YGMinContent diff --git a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp index 192a6af77da9..5ac58810f8d1 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp +++ b/packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp @@ -825,6 +825,41 @@ static float computeFlexBasisForChildren( return totalOuterFlexBasis; } +// Whether what an earlier layout cached for `node` no longer holds: the node or +// a descendant changed and has not been visited in this layout yet, or its +// config or the direction it inherits changed. +static bool isCacheStale( + const yoga::Node* const node, + const Direction ownerDirection, + const uint32_t generationCount) { + const LayoutResults& layout = node->getLayout(); + return (node->isDirty() && layout.generationCount != generationCount) || + layout.configVersion != node->getConfig()->getVersion() || + layout.lastOwnerDirection != ownerDirection; +} + +// A min-content size memoized in this layout holds for the rest of it, and one +// memoized in an earlier layout holds for as long as the node's cached +// measurements do. +static bool hasMemoizedMinContent( + const yoga::Node* const node, + const Dimension axisDimension, + const Direction ownerDirection, + const uint32_t generationCount) { + const uint32_t memoizedGeneration = + node->getLayout().minContentGeneration[yoga::to_underlying(axisDimension)]; + return memoizedGeneration == generationCount || + (memoizedGeneration != 0 && + !isCacheStale(node, ownerDirection, generationCount)); +} + +// A min-content size, and whether a percentage padding or margin resolved +// against the owner's width went into it. +struct MinContentSize { + float size; + bool resolvesAgainstOwnerWidth; +}; + // Returns the min-content size of `node` along `requestedAxis`, used by CSS // Flexbox §4.5 automatic minimum sizing. // @@ -850,13 +885,16 @@ static float computeFlexBasisForChildren( // Container-level recursion does no layout writes (no positions, no // alignment, no flex distribution); only the descendant leaf measure // callbacks observe state changes (the same ones a normal layout pass -// would invoke). +// would invoke). Like a layout pass, it first clones the children a container +// does not own, since the measure callbacks and the memoized results below +// write to them. // -// Results are memoized per node and axis for the current layout generation -// (see `LayoutResults::minContent`): each leaf's measure callback runs at -// most once per axis per layout, and each container is walked once per -// distinct `ownerWidth`. -static float computeMinContentMainSize( +// Results are memoized per node and axis (see `LayoutResults::minContent`) for +// as long as the node's cached measurements hold, so a later layout walks only +// the subtrees that changed: a leaf's measure callback runs again only once the +// leaf is marked dirty, and a container is walked again for a new `ownerWidth` +// only when a percentage padding or margin below it resolves against it. +static MinContentSize computeMinContentMainSize( yoga::Node* const node, const FlexDirection requestedAxis, const Direction ownerDirection, @@ -873,14 +911,16 @@ static float computeMinContentMainSize( const FloatOptional staticMin = wantRow ? node->getMinContentWidth() : node->getMinContentHeight(); if (staticMin.isDefined()) { - return staticMin.unwrap(); + return {staticMin.unwrap(), node->style().hasPercentMarginOrPadding()}; } LayoutResults& layout = node->getLayout(); - const auto axisIndex = yoga::to_underlying(dimension(requestedAxis)); + const Dimension axisDimension = dimension(requestedAxis); + const auto axisIndex = yoga::to_underlying(axisDimension); if (node->hasMeasureFunc()) { - if (layout.minContentGeneration[axisIndex] != generationCount) { + if (!hasMemoizedMinContent( + node, axisDimension, ownerDirection, generationCount)) { // 2. Dynamic min-content callback if set (for Primitives whose // min-content depends on state). Otherwise fall back to the regular // measure function with `AtMost 0`, which text measurers naturally @@ -906,16 +946,23 @@ static float computeMinContentMainSize( requestedAxis, leafDirection, ownerWidth) + node->style().computeFlexEndPaddingAndBorder( requestedAxis, leafDirection, ownerWidth); - return layout.minContent[axisIndex] + paddingAndBorder; + return { + layout.minContent[axisIndex] + paddingAndBorder, + node->style().hasPercentMarginOrPadding()}; } if (node->getChildCount() == 0) { - return 0.0f; + return {0.0f, node->style().hasPercentMarginOrPadding()}; } - if (layout.minContentGeneration[axisIndex] == generationCount && - yoga::inexactEquals(layout.minContentOwnerWidth[axisIndex], ownerWidth)) { - return layout.minContent[axisIndex]; + if (hasMemoizedMinContent( + node, axisDimension, ownerDirection, generationCount) && + (!layout.minContentResolvesAgainstOwnerWidth[axisIndex] || + yoga::inexactEquals( + layout.minContentOwnerWidth[axisIndex], ownerWidth))) { + return { + layout.minContent[axisIndex], + layout.minContentResolvesAgainstOwnerWidth[axisIndex]}; } const Direction direction = node->resolveDirection(ownerDirection); @@ -926,7 +973,9 @@ static float computeMinContentMainSize( float mainTotal = 0.0f; float crossMax = 0.0f; + bool resolvesAgainstOwnerWidth = node->style().hasPercentMarginOrPadding(); + node->cloneChildrenIfNeeded(); for (size_t i = 0; i < node->getChildCount(); i++) { auto* const child = node->getChild(i); if (child->style().display() == Display::None || @@ -934,27 +983,30 @@ static float computeMinContentMainSize( continue; } - float childMain = computeMinContentMainSize( + const MinContentSize childMain = computeMinContentMainSize( child, nodeMainAxis, direction, ownerWidth, ownerHeight, generationCount); - childMain += child->style().computeMarginForAxis(nodeMainAxis, ownerWidth); - - float childCross = computeMinContentMainSize( + const MinContentSize childCross = computeMinContentMainSize( child, nodeCrossAxis, direction, ownerWidth, ownerHeight, generationCount); - childCross += - child->style().computeMarginForAxis(nodeCrossAxis, ownerWidth); - mainTotal += childMain; - crossMax = std::max(crossMax, childCross); + mainTotal += childMain.size + + child->style().computeMarginForAxis(nodeMainAxis, ownerWidth); + crossMax = std::max( + crossMax, + childCross.size + + child->style().computeMarginForAxis(nodeCrossAxis, ownerWidth)); + resolvesAgainstOwnerWidth = resolvesAgainstOwnerWidth || + childMain.resolvesAgainstOwnerWidth || + childCross.resolvesAgainstOwnerWidth; } mainTotal += node->style().computeFlexStartPaddingAndBorder( @@ -972,8 +1024,10 @@ static float computeMinContentMainSize( const float minContent = wantRow ? widthMin : heightMin; layout.minContent[axisIndex] = minContent; layout.minContentOwnerWidth[axisIndex] = ownerWidth; + layout.minContentResolvesAgainstOwnerWidth[axisIndex] = + resolvesAgainstOwnerWidth; layout.minContentGeneration[axisIndex] = generationCount; - return minContent; + return {minContent, resolvesAgainstOwnerWidth}; } // Computes the CSS Flexbox §4.5 automatic minimum main-axis size for @@ -1037,8 +1091,10 @@ static FloatOptional computeAutoMinMainSize( } // Content size suggestion: probe via min-content recursion. - const FloatOptional contentMain = FloatOptional{computeMinContentMainSize( - child, mainAxis, direction, ownerWidth, ownerHeight, generationCount)}; + const FloatOptional contentMain = FloatOptional{ + computeMinContentMainSize( + child, mainAxis, direction, ownerWidth, ownerHeight, generationCount) + .size}; // Combine per §4.5: floor = min(content, specified) when specified is // definite; otherwise floor = min(content, transferred) when transferred @@ -2740,9 +2796,7 @@ bool calculateLayoutInternal( depth++; const bool needToVisitNode = - (node->isDirty() && layout->generationCount != generationCount) || - layout->configVersion != node->getConfig()->getVersion() || - layout->lastOwnerDirection != ownerDirection; + isCacheStale(node, ownerDirection, generationCount); if (needToVisitNode) { // Invalidate the cached results. @@ -2755,6 +2809,13 @@ bool calculateLayoutInternal( layout->cachedLayout.computedHeight = -1; layout->cachedLayout.ownerWidth = -1; layout->cachedLayout.ownerHeight = -1; + // A min-content size memoized earlier in this layout already reflects the + // change. + for (auto& minContentGeneration : layout->minContentGeneration) { + if (minContentGeneration != generationCount) { + minContentGeneration = 0; + } + } } CachedMeasurement* cachedResults = nullptr; diff --git a/packages/react-native/ReactCommon/yoga/yoga/node/LayoutResults.h b/packages/react-native/ReactCommon/yoga/yoga/node/LayoutResults.h index 5a0111eb74cb..19a7e08cc467 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/node/LayoutResults.h +++ b/packages/react-native/ReactCommon/yoga/yoga/node/LayoutResults.h @@ -35,13 +35,16 @@ struct LayoutResults { // means "no auto-min applies." FloatOptional computedAutoMinMainSize = {}; - // Memoized min-content sizes for §4.5, indexed by dimension and valid only - // during layout generation `minContentGeneration`. A leaf stores its measured - // min-content without padding and border, which doesn't depend on the owner. - // A container stores its full min-content, which depends on - // `minContentOwnerWidth` through percentage padding and margins. + // Memoized min-content sizes for §4.5, indexed by dimension, with the layout + // generation each was computed in (0 for none). Like `cachedMeasurements`, + // they hold until the node, its config or its direction changes. A leaf + // stores its measured min-content without padding and border, which doesn't + // depend on the owner. A container stores its full min-content, which + // depends on `minContentOwnerWidth` when a percentage padding or margin in + // the subtree it walked resolves against it. std::array minContentGeneration = {}; std::array minContentOwnerWidth = {{YGUndefined, YGUndefined}}; + std::array minContentResolvesAgainstOwnerWidth = {}; std::array minContent = {{YGUndefined, YGUndefined}}; // Instead of recomputing the entire layout every single time, we cache some diff --git a/packages/react-native/ReactCommon/yoga/yoga/style/Style.h b/packages/react-native/ReactCommon/yoga/yoga/style/Style.h index 9ce453b7fc39..858a6f549b9c 100644 --- a/packages/react-native/ReactCommon/yoga/yoga/style/Style.h +++ b/packages/react-native/ReactCommon/yoga/yoga/style/Style.h @@ -346,7 +346,7 @@ class YG_EXPORT Style { // axes, and a percentage size or size bound against the owner's size on its // own axis. bool resolvesAgainstOwnerWidth() const { - return anyIsPercent(margin_) || anyIsPercent(padding_) || + return hasPercentMarginOrPadding() || dimensions_[yoga::to_underlying(Dimension::Width)].isPercent() || minDimensions_[yoga::to_underlying(Dimension::Width)].isPercent() || maxDimensions_[yoga::to_underlying(Dimension::Width)].isPercent(); @@ -358,6 +358,10 @@ class YG_EXPORT Style { maxDimensions_[yoga::to_underlying(Dimension::Height)].isPercent(); } + bool hasPercentMarginOrPadding() const { + return anyIsPercent(margin_) || anyIsPercent(padding_); + } + FloatOptional aspectRatio() const { return pool_.getNumber(aspectRatio_); } diff --git a/private/react-native-fantom/tester/src/stubs/StubComponentRegistryFactory.h b/private/react-native-fantom/tester/src/stubs/StubComponentRegistryFactory.h index b68623060fa9..9fc4f6048568 100644 --- a/private/react-native-fantom/tester/src/stubs/StubComponentRegistryFactory.h +++ b/private/react-native-fantom/tester/src/stubs/StubComponentRegistryFactory.h @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace facebook::react { @@ -27,6 +28,7 @@ inline ComponentRegistryFactory getDefaultComponentRegistryFactory() static auto providerRegistry = []() { auto providerRegistry = std::make_shared(); providerRegistry->add(concreteComponentDescriptorProvider()); + providerRegistry->add(concreteComponentDescriptorProvider()); providerRegistry->add(concreteComponentDescriptorProvider()); providerRegistry->add(concreteComponentDescriptorProvider()); providerRegistry->add(concreteComponentDescriptorProvider()); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 675923cec7c0..ff8f03c21353 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -6691,6 +6691,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -7217,6 +7218,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api index 0d1f0fd94a91..b2e46bc0fd5a 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidFrameworksCxx.api @@ -11150,6 +11150,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -11676,6 +11677,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 4b15d66312e8..1aac991b1e2c 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -6540,6 +6540,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -7066,6 +7067,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 4b15d66312e8..1aac991b1e2c 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -6540,6 +6540,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -7066,6 +7067,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index cb68d6c58bd7..421f56a3b107 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -8441,6 +8441,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -8967,6 +8968,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api index b05f7665f7da..f784e978c598 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleFrameworksCxx.api @@ -12926,6 +12926,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -13452,6 +13453,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index 497a3a43a3ee..69b1dddc64b7 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -8293,6 +8293,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -8819,6 +8820,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index 4f7774be13cd..fe51142d0111 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -8300,6 +8300,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -8826,6 +8827,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 507e71d72698..6b6889cb651a 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -3920,6 +3920,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -4446,6 +4447,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api index 2772d8e63b9e..b798e96f9f2f 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonFrameworksCxx.api @@ -8270,6 +8270,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -8796,6 +8797,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index 004abd9081df..f99a7be4e026 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -3913,6 +3913,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -4439,6 +4440,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth; diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index 004abd9081df..f99a7be4e026 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -3913,6 +3913,7 @@ class facebook::yoga::Node : public YGNode { class facebook::yoga::Style { public bool flexEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool flexStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; + public bool hasPercentMarginOrPadding() const; public bool horizontalInsetsDefined() const; public bool inlineEndMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; public bool inlineStartMarginIsAuto(facebook::yoga::FlexDirection axis, facebook::yoga::Direction direction) const; @@ -4439,6 +4440,7 @@ struct facebook::yoga::LayoutResults { public float position(facebook::yoga::PhysicalEdge physicalEdge) const; public float rawDimension(facebook::yoga::Dimension axis) const; public static constexpr int32_t MaxCachedMeasurements; + public std::array minContentResolvesAgainstOwnerWidth; public std::array cachedMeasurements; public std::array minContent; public std::array minContentOwnerWidth;