Make sure UIImplementation methods that touch native View has the Views created

Summary:
UIImplementation has a few methods that in the end touch native Views, such as dispatchViewManagerCommand, addAnimation, sendAccessibilityEvent etc. There are 2 cases where it is possible to have those methods called on shadow nodes that don't have backing Views created:
- backing view is scheduled to be created but not commited yet (StateBuilder accumulates createView commands into a queue and flushes it in the very end)
- shadow node doesn't mount to a View so there is no backing View

Touching View in UI thread in these 2 cases will either lead to silent error (e.g. failure callback will execute), or a native crash.

This diff is overriding all UIImplementation methods that touch Views in UI thread and makes sure that backing View is created before we do so.

Reviewed By: ahmedre

Differential Revision: D3046392
This commit is contained in:
Denis Koroskin 2016-03-14 02:27:20 -07:00 committed by Ahmed El-Helw
parent 68580fcab6
commit f4690ef8de
1 changed files with 44 additions and 0 deletions

View File

@ -169,6 +169,7 @@ public class FlatUIImplementation extends UIImplementation {
public void measure(int reactTag, Callback callback) {
FlatShadowNode node = (FlatShadowNode) resolveShadowNode(reactTag);
if (node.mountsToView()) {
mStateBuilder.ensureBackingViewIsCreated(node);
super.measure(reactTag, callback);
return;
}
@ -182,6 +183,7 @@ public class FlatUIImplementation extends UIImplementation {
while (true) {
node = Assertions.assumeNotNull((FlatShadowNode) node.getParent());
if (node.mountsToView()) {
mStateBuilder.ensureBackingViewIsCreated(node);
break;
}
@ -202,6 +204,48 @@ public class FlatUIImplementation extends UIImplementation {
callback);
}
private void ensureMountsToViewAndBackingViewIsCreated(int reactTag) {
FlatShadowNode node = (FlatShadowNode) resolveShadowNode(reactTag);
node.forceMountToView();
mStateBuilder.ensureBackingViewIsCreated(node);
}
@Override
public void findSubviewIn(int reactTag, float targetX, float targetY, Callback callback) {
ensureMountsToViewAndBackingViewIsCreated(reactTag);
super.findSubviewIn(reactTag, targetX, targetY, callback);
}
@Override
public void measureInWindow(int reactTag, Callback callback) {
ensureMountsToViewAndBackingViewIsCreated(reactTag);
super.measureInWindow(reactTag, callback);
}
@Override
public void addAnimation(int reactTag, int animationID, Callback onSuccess) {
ensureMountsToViewAndBackingViewIsCreated(reactTag);
super.addAnimation(reactTag, animationID, onSuccess);
}
@Override
public void dispatchViewManagerCommand(int reactTag, int commandId, ReadableArray commandArgs) {
ensureMountsToViewAndBackingViewIsCreated(reactTag);
super.dispatchViewManagerCommand(reactTag, commandId, commandArgs);
}
@Override
public void showPopupMenu(int reactTag, ReadableArray items, Callback error, Callback success) {
ensureMountsToViewAndBackingViewIsCreated(reactTag);
super.showPopupMenu(reactTag, items, error, success);
}
@Override
public void sendAccessibilityEvent(int reactTag, int eventType) {
ensureMountsToViewAndBackingViewIsCreated(reactTag);
super.sendAccessibilityEvent(reactTag, eventType);
}
/**
* Removes all children defined by moveFrom and removeFrom from a given parent,
* preparing elements in moveFrom to be re-added at proper index.