From f4690ef8dec02a12ca85c7c4529e89a4c4a43ae4 Mon Sep 17 00:00:00 2001 From: Denis Koroskin Date: Mon, 14 Mar 2016 02:27:20 -0700 Subject: [PATCH] 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 --- .../react/flat/FlatUIImplementation.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java index 9e05a3f6256..1ef816ec13e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/flat/FlatUIImplementation.java @@ -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.