Files
react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm
David Vacca 6aa297a7f3 Fabric: Fixed crash when LayoutMetrics contained NaN values
Summary:
CALayer will crash if we pass NaN or Inf values.

It's unclear how to detect this case on cross-platform manner holistically, so we have to do it on the mounting layer as well.
NaN/Inf is a kinda valid result of some math operations. Even if we can (and should) detect (and report early) incorrect (NaN and Inf) values which come from JavaScript side, we sometimes cannot backtrace the sources of a calculation that produced an incorrect/useless result.

Besides that, I will investigate why the crash is actually happening, so we might need to fix something in layout engine. But, it general, we cannot capture all errors like that, so we need to have it here anyway.

Reviewed By: JoshuaGross, mmmulani

Differential Revision: D14126058

fbshipit-source-id: 807e5a223bdef48af9a3b7210803863431e8c507
2019-02-19 13:03:15 -08:00

86 lines
2.9 KiB
Plaintext

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "UIView+ComponentViewProtocol.h"
#import <React/RCTAssert.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
#import "RCTConversions.h"
using namespace facebook::react;
@implementation UIView (ComponentViewProtocol)
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
[self insertSubview:childComponentView atIndex:index];
}
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
RCTAssert(childComponentView.superview == self, @"Attempt to unmount improperly mounted component view.");
[childComponentView removeFromSuperview];
}
- (void)updateProps:(SharedProps)props oldProps:(SharedProps)oldProps
{
// Default implementation does nothing.
}
- (void)updateEventEmitter:(SharedEventEmitter)eventEmitter
{
// Default implementation does nothing.
}
- (void)updateLocalData:(SharedLocalData)localData oldLocalData:(SharedLocalData)oldLocalData
{
// Default implementation does nothing.
}
- (void)updateLayoutMetrics:(LayoutMetrics)layoutMetrics oldLayoutMetrics:(LayoutMetrics)oldLayoutMetrics
{
if (layoutMetrics.frame != oldLayoutMetrics.frame) {
CGRect frame = RCTCGRectFromRect(layoutMetrics.frame);
if (std::isnan(frame.origin.x) || std::isnan(frame.origin.y) || std::isnan(frame.size.width) ||
std::isnan(frame.size.height) || std::isinf(frame.origin.x) || std::isinf(frame.origin.y) ||
std::isinf(frame.size.width) || std::isinf(frame.size.height)) {
// CALayer will crash if we pass NaN or Inf values.
// It's unclear how to detect this case on cross-platform manner holistically, so we have to do it on the mounting
// layer as well. NaN/Inf is a kinda valid result of some math operations. Even if we can (and should) detect (and
// report early) incorrect (NaN and Inf) values which come from JavaScript side, we sometimes cannot backtrace the
// sources of a calculation that produced an incorrect/useless result.
RCTLogWarn(
@"-[UIView(ComponentViewProtocol) updateLayoutMetrics:oldLayoutMetrics:]: Received invalid layout metrics (%@) for a view (%@).",
NSStringFromCGRect(frame),
self);
return;
}
self.frame = frame;
}
if (layoutMetrics.layoutDirection != oldLayoutMetrics.layoutDirection) {
self.semanticContentAttribute = layoutMetrics.layoutDirection == LayoutDirection::RightToLeft
? UISemanticContentAttributeForceRightToLeft
: UISemanticContentAttributeForceLeftToRight;
}
if (layoutMetrics.displayType != oldLayoutMetrics.displayType) {
self.hidden = layoutMetrics.displayType == DisplayType::None;
}
}
- (void)prepareForRecycle
{
// Default implementation does nothing.
}
@end