[ASLayout] Add additional validation step to intercept incorrect values in production and safely zero them out.

These conditions already had assertions, but at runtime, an insufficiently tested and incorrect ASLayoutSpec could
generate values that cause UIKit to enter an infinite loop (e.g. inside of UICollectionView layout validation).
This commit is contained in:
Scott Goodson 2016-04-10 16:42:22 -07:00
parent c94490f7bb
commit 678c201dbc
4 changed files with 24 additions and 2 deletions

View File

@ -293,9 +293,18 @@
CGSize size = self.preferredFrameSize; CGSize size = self.preferredFrameSize;
if (CGSizeEqualToSize(size, CGSizeZero)) { if (CGSizeEqualToSize(size, CGSizeZero)) {
size = constrainedSize; size = constrainedSize;
// FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc)
// These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value.
if (!isValidForLayout(size.width)) {
size.width = 100.0;
}
if (!isValidForLayout(size.height)) {
size.height = 100.0;
}
} }
[self setSnapshotSizeWithReloadIfNeeded:size]; [self setSnapshotSizeWithReloadIfNeeded:size];
return constrainedSize; return size;
} }
// -layout isn't usually needed over -layoutSpecThatFits, but this way we can avoid a needless node wrapper for MKMapView. // -layout isn't usually needed over -layoutSpecThatFits, but this way we can avoid a needless node wrapper for MKMapView.

View File

@ -8,6 +8,7 @@
* *
*/ */
#pragma once
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
@ -34,6 +35,8 @@ typedef struct {
extern ASRelativeDimension const ASRelativeDimensionUnconstrained; extern ASRelativeDimension const ASRelativeDimensionUnconstrained;
#define isValidForLayout(x) ((isnormal(x) || x == 0.0) && x >= 0.0 && x < (CGFLOAT_MAX / 2.0))
ASDISPLAYNODE_EXTERN_C_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN

View File

@ -8,6 +8,8 @@
* *
*/ */
#pragma once
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASAssert.h> #import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASLayoutable.h> #import <AsyncDisplayKit/ASLayoutable.h>

View File

@ -39,7 +39,15 @@ extern BOOL CGPointIsNull(CGPoint point)
ASLayout *l = [super new]; ASLayout *l = [super new];
if (l) { if (l) {
l->_layoutableObject = layoutableObject; l->_layoutableObject = layoutableObject;
l->_size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
if (!isValidForLayout(size.width) || !isValidForLayout(size.height)) {
ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Production will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutableObject);
size = CGSizeZero;
} else {
size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
}
l->_size = size;
if (CGPointIsNull(position) == NO) { if (CGPointIsNull(position) == NO) {
l->_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y)); l->_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y));
} else { } else {