Fix bug where we insert nodes at wrong index in view hierarchy.
Summary: We currently have a bug in `UIView+CSSLayout.m` that you can repro in a scenario like this: ``` UIView *container = [[UIView alloc] initWithFrame:CGRectZero]; [container css_setUsesFlexbox:YES]; [container css_setFlexDirection:CSSFlexDirectionRow]; UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero]; [subview1 css_setUsesFlexbox:YES]; [subview1 css_setIncludeInLayout:NO]; [container addSubview:subview1]; UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero]; [subview2 css_setUsesFlexbox:YES]; [subview2 css_setIncludeInLayout:NO]; [container addSubview:subview2]; UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero]; [subview3 css_setUsesFlexbox:YES]; [subview3 css_setIncludeInLayout:YES]; [container addSubview:subview3]; [container css_applyLayout]; ``` `subview3` (which is the only view whose is going to be included in layout calculation) is inserted at child index 2, instead of 0. This eventually can cause crash in CSSLayout.c because it will attempt access a child in the list which is null. Reviewed By: emilsjolander Differential Revision: D4215659 fbshipit-source-id: a615f50e51f85b15d3bdb437e55958865898b183
This commit is contained in:
committed by
Facebook Github Bot
parent
2d58a36619
commit
49a21e657b
@@ -164,6 +164,35 @@
|
|||||||
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview3.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview3.bounds.size));
|
XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(100, 50), subview3.bounds.size), @"Actual size is %@", NSStringFromCGSize(subview3.bounds.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testThatNumberOfChildrenIsCorrectWhenWeIgnoreSubviews
|
||||||
|
{
|
||||||
|
UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
|
[container css_setUsesFlexbox:YES];
|
||||||
|
[container css_setFlexDirection:CSSFlexDirectionRow];
|
||||||
|
|
||||||
|
UIView *subview1 = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
|
[subview1 css_setUsesFlexbox:YES];
|
||||||
|
[subview1 css_setIncludeInLayout:NO];
|
||||||
|
[container addSubview:subview1];
|
||||||
|
|
||||||
|
UIView *subview2 = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
|
[subview2 css_setUsesFlexbox:YES];
|
||||||
|
[subview2 css_setIncludeInLayout:NO];
|
||||||
|
[container addSubview:subview2];
|
||||||
|
|
||||||
|
UIView *subview3 = [[UIView alloc] initWithFrame:CGRectZero];
|
||||||
|
[subview3 css_setUsesFlexbox:YES];
|
||||||
|
[subview3 css_setIncludeInLayout:YES];
|
||||||
|
[container addSubview:subview3];
|
||||||
|
|
||||||
|
[container css_applyLayout];
|
||||||
|
XCTAssertEqual(1, [container css_numberOfChildren]);
|
||||||
|
|
||||||
|
[subview2 css_setIncludeInLayout:YES];
|
||||||
|
[container css_applyLayout];
|
||||||
|
XCTAssertEqual(2, [container css_numberOfChildren]);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond
|
- (void)testThatViewNotIncludedInFirstLayoutPassAreIncludedInSecond
|
||||||
{
|
{
|
||||||
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
|
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 50)];
|
||||||
|
@@ -64,4 +64,9 @@
|
|||||||
*/
|
*/
|
||||||
- (CGSize)css_intrinsicSize;
|
- (CGSize)css_intrinsicSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the number of children that are using Flexbox.
|
||||||
|
*/
|
||||||
|
- (NSUInteger)css_numberOfChildren;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -45,6 +45,11 @@
|
|||||||
return (includeInLayout != nil) ? [includeInLayout boolValue] : YES;
|
return (includeInLayout != nil) ? [includeInLayout boolValue] : YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSUInteger)css_numberOfChildren
|
||||||
|
{
|
||||||
|
return CSSNodeChildCount([self cssNode]);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Setters
|
#pragma mark - Setters
|
||||||
|
|
||||||
- (void)css_setIncludeInLayout:(BOOL)includeInLayout
|
- (void)css_setIncludeInLayout:(BOOL)includeInLayout
|
||||||
@@ -281,24 +286,24 @@ static void CSSAttachNodesFromViewHierachy(UIView *view) {
|
|||||||
} else {
|
} else {
|
||||||
CSSNodeSetMeasureFunc(node, NULL);
|
CSSNodeSetMeasureFunc(node, NULL);
|
||||||
|
|
||||||
NSUInteger numSubviewsInLayout = 0;
|
NSUInteger subviewIndex = 0;
|
||||||
// Add any children which were added since the last call to css_applyLayout
|
// Add any children which were added since the last call to css_applyLayout
|
||||||
for (NSUInteger i = 0; i < view.subviews.count; i++) {
|
for (UIView *subview in view.subviews) {
|
||||||
UIView *const subview = view.subviews[i];
|
|
||||||
if (![subview css_includeInLayout]) {
|
if (![subview css_includeInLayout]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
numSubviewsInLayout++;
|
|
||||||
|
|
||||||
CSSNodeRef childNode = [subview cssNode];
|
CSSNodeRef childNode = [subview cssNode];
|
||||||
if (CSSNodeChildCount(node) < i + 1 || CSSNodeGetChild(node, i) != childNode) {
|
if (CSSNodeGetChild(node, subviewIndex) != childNode) {
|
||||||
CSSNodeInsertChild(node, childNode, i);
|
CSSNodeInsertChild(node, childNode, subviewIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSAttachNodesFromViewHierachy(subview);
|
CSSAttachNodesFromViewHierachy(subview);
|
||||||
|
subviewIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any children which were removed since the last call to css_applyLayout
|
// Remove any children which were removed since the last call to css_applyLayout
|
||||||
while (numSubviewsInLayout < CSSNodeChildCount(node)) {
|
while (subviewIndex < CSSNodeChildCount(node)) {
|
||||||
CSSNodeRemoveChild(node, CSSNodeGetChild(node, CSSNodeChildCount(node) - 1));
|
CSSNodeRemoveChild(node, CSSNodeGetChild(node, CSSNodeChildCount(node) - 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user