Skip to content

Commit 1c52eec

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Ensure that ShadowNode measure functions respect constraints (#51180)
Summary: Pull Request resolved: #51180 Android's TextLayoutManager may return widths greather than the max measure constraint. Yoga will clamp these, but this sort of issue points to a logic bug, and creates issues when we are looking at caching text measurements based on constraint reuse. Let's debug assert that we don't do that, and fix a case of rounding up at a pixel boundary, to ensure that it doesn't go above max width. This should theoretically be safe, since Yoga is already doing this clamping, which is what dictates final size of the TextView. Changelog: [Internal] Reviewed By: rshest Differential Revision: D74291373 fbshipit-source-id: 44166f2e47323384cb00f3cf4c32f398e298a63e
1 parent 6554402 commit 1c52eec

4 files changed

Lines changed: 26 additions & 4 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ private static float calculateWidth(
825825
// where the container is measured smaller than text. Math.ceil prevents it
826826
// See T136756103 for investigation
827827
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
828-
calculatedWidth = (float) Math.ceil(calculatedWidth);
828+
calculatedWidth = Math.min((float) Math.ceil(calculatedWidth), width);
829829
}
830830
return calculatedWidth;
831831
}

packages/react-native/ReactCommon/react/renderer/components/view/YogaLayoutableShadowNode.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
#include <react/renderer/components/view/ViewProps.h>
1616
#include <react/renderer/components/view/ViewShadowNode.h>
1717
#include <react/renderer/components/view/conversions.h>
18+
#include <react/renderer/core/ComponentDescriptor.h>
1819
#include <react/renderer/core/LayoutConstraints.h>
1920
#include <react/renderer/core/LayoutContext.h>
2021
#include <react/renderer/debug/DebugStringConvertibleItem.h>
22+
#include <react/utils/FloatComparison.h>
2123
#include <yoga/Yoga.h>
2224
#include <algorithm>
2325
#include <limits>
@@ -837,6 +839,21 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector(
837839
auto size = shadowNode.measureContent(
838840
threadLocalLayoutContext, {minimumSize, maximumSize});
839841

842+
#ifdef REACT_NATIVE_DEBUG
843+
bool widthInBounds = size.width + kDefaultEpsilon >= minimumSize.width &&
844+
size.width - kDefaultEpsilon <= maximumSize.width;
845+
bool heightInBounds = size.height + kDefaultEpsilon >= minimumSize.height &&
846+
size.height - kDefaultEpsilon <= maximumSize.height;
847+
848+
if (!widthInBounds || !heightInBounds) {
849+
LOG(FATAL) << shadowNode.getComponentDescriptor().getComponentName()
850+
<< " returned in invalid measurement. Min: ["
851+
<< minimumSize.width << "," << minimumSize.height << "] Max: ["
852+
<< maximumSize.width << "," << maximumSize.height
853+
<< "] Actual: [" << size.width << "," << size.height << "]";
854+
}
855+
#endif
856+
840857
return YGSize{
841858
yogaFloatFromFloat(size.width), yogaFloatFromFloat(size.height)};
842859
}

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/cxx/react/renderer/textlayoutmanager/TextLayoutManager.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ TextMeasurement TextLayoutManager::measure(
1717
const AttributedStringBox& attributedStringBox,
1818
const ParagraphAttributes& /*paragraphAttributes*/,
1919
const TextLayoutContext& /*layoutContext*/,
20-
const LayoutConstraints& /*layoutConstraints*/) const {
20+
const LayoutConstraints& layoutConstraints) const {
2121
TextMeasurement::Attachments attachments;
2222
for (const auto& fragment : attributedStringBox.getValue().getFragments()) {
2323
if (fragment.isAttachment()) {
2424
attachments.push_back(
2525
TextMeasurement::Attachment{{{0, 0}, {0, 0}}, false});
2626
}
2727
}
28-
return TextMeasurement{{0, 0}, attachments};
28+
return TextMeasurement{
29+
{layoutConstraints.minimumSize.width,
30+
layoutConstraints.minimumSize.height},
31+
attachments};
2932
}
3033

3134
} // namespace facebook::react

packages/react-native/ReactCommon/react/utils/FloatComparison.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
namespace facebook::react {
1111

12-
inline bool floatEquality(float a, float b, float epsilon = 0.005f) {
12+
constexpr float kDefaultEpsilon = 0.005f;
13+
14+
inline bool floatEquality(float a, float b, float epsilon = kDefaultEpsilon) {
1315
return (std::isnan(a) && std::isnan(b)) ||
1416
(!std::isnan(a) && !std::isnan(b) && fabs(a - b) < epsilon);
1517
}

0 commit comments

Comments
 (0)