From cd1dbc3f0f668b2835e5db62e16ae33f4add1fc3 Mon Sep 17 00:00:00 2001 From: Georgiy Kassabli Date: Tue, 23 May 2017 11:11:37 -0700 Subject: [PATCH] Fixing potential measure call with negative size value Summary: There is a case when measure() function will be called with negative width/height because of margin/padding. This diff fixes that scenario Reviewed By: emilsjolander Differential Revision: D5111534 fbshipit-source-id: 99f09b85f0c6a0e5dec89a26baba8f9d560100da --- tests/YGMeasureTest.cpp | 51 +++++++++++++++++++++++++++++++++++++++++ yoga/Yoga.c | 9 ++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/tests/YGMeasureTest.cpp b/tests/YGMeasureTest.cpp index b1214493..6dba7955 100644 --- a/tests/YGMeasureTest.cpp +++ b/tests/YGMeasureTest.cpp @@ -39,6 +39,19 @@ static YGSize _simulate_wrapping_text(YGNodeRef node, }; } +static YGSize _measure_assert_negative(YGNodeRef node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) { + EXPECT_GE(width, 0); + EXPECT_GE(height, 0); + + return YGSize{ + .width = 0, .height = 0, + }; +} + TEST(YogaTest, dont_measure_single_grow_shrink_child) { const YGNodeRef root = YGNodeNew(); YGNodeStyleSetWidth(root, 100); @@ -563,3 +576,41 @@ TEST(YogaTest, can_nullify_measure_func_on_any_node) { ASSERT_TRUE(YGNodeGetMeasureFunc(root) == NULL); YGNodeFreeRecursive(root); } + +TEST(YogaTest, cant_call_negative_measure) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn); + YGNodeStyleSetWidth(root, 50); + YGNodeStyleSetHeight(root, 10); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeSetMeasureFunc(root_child0, _measure_assert_negative); + YGNodeStyleSetMargin(root_child0, YGEdgeTop, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} + +TEST(YogaTest, cant_call_negative_measure_horizontal) { + const YGConfigRef config = YGConfigNew(); + + const YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow); + YGNodeStyleSetWidth(root, 10); + YGNodeStyleSetHeight(root, 20); + + const YGNodeRef root_child0 = YGNodeNewWithConfig(config); + YGNodeSetMeasureFunc(root_child0, _measure_assert_negative); + YGNodeStyleSetMargin(root_child0, YGEdgeStart, 20); + YGNodeInsertChild(root, root_child0, 0); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + YGNodeFreeRecursive(root); + YGConfigFree(config); +} diff --git a/yoga/Yoga.c b/yoga/Yoga.c index 9aaa6333..e77a9ca6 100644 --- a/yoga/Yoga.c +++ b/yoga/Yoga.c @@ -1736,8 +1736,13 @@ static void YGNodeWithMeasureFuncSetMeasuredDimensions(const YGNodeRef node, const float marginAxisRow = YGNodeMarginForAxis(node, YGFlexDirectionRow, availableWidth); const float marginAxisColumn = YGNodeMarginForAxis(node, YGFlexDirectionColumn, availableWidth); - const float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; - const float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + // We want to make sure we don't call measure with negative size + const float innerWidth = YGFloatIsUndefined(availableWidth) + ? availableWidth + : fmaxf(0, availableWidth - marginAxisRow - paddingAndBorderAxisRow); + const float innerHeight = YGFloatIsUndefined(availableHeight) + ? availableHeight + : fmaxf(0, availableHeight - marginAxisColumn - paddingAndBorderAxisColumn); if (widthMeasureMode == YGMeasureModeExactly && heightMeasureMode == YGMeasureModeExactly) { // Don't bother sizing the text if both dimensions are already defined.