Capture StackOverflowExceptions triggered when drawing a ReactViewGroup or ReactRootView
Reviewed By: achen1 Differential Revision: D6653395 fbshipit-source-id: 849b1a2ed6ab9bc057414d451e97a673178c30dd
This commit is contained in:
parent
877f1cde2e
commit
1aac962378
|
@ -12,6 +12,7 @@ package com.facebook.react;
|
|||
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -40,6 +41,7 @@ import com.facebook.react.modules.appregistry.AppRegistry;
|
|||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.modules.deviceinfo.DeviceInfoModule;
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
import com.facebook.react.uimanager.IllegalViewOperationException;
|
||||
import com.facebook.react.uimanager.JSTouchDispatcher;
|
||||
import com.facebook.react.uimanager.MeasureSpecProvider;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
|
@ -201,6 +203,17 @@ public class ReactRootView extends SizeMonitoringFrameLayout
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
try {
|
||||
super.dispatchDraw(canvas);
|
||||
} catch (StackOverflowError e) {
|
||||
// Adding special exception management for StackOverflowError for logging purposes.
|
||||
// This will be removed in the future.
|
||||
handleException(new IllegalViewOperationException("StackOverflowError", e));
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchJSTouchEvent(MotionEvent event) {
|
||||
if (mReactInstanceManager == null || !mIsAttachedToInstance ||
|
||||
mReactInstanceManager.getCurrentReactContext() == null) {
|
||||
|
@ -496,6 +509,15 @@ public class ReactRootView extends SizeMonitoringFrameLayout
|
|||
mRootViewTag = rootViewTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleException(Exception e) {
|
||||
if (mReactInstanceManager != null && mReactInstanceManager.getCurrentReactContext() != null) {
|
||||
mReactInstanceManager.getCurrentReactContext().handleException(e);
|
||||
} else {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ReactInstanceManager getReactInstanceManager() {
|
||||
return mReactInstanceManager;
|
||||
|
|
|
@ -304,13 +304,13 @@ public class ReactContext extends ContextWrapper {
|
|||
* {@link com.facebook.react.bridge.NativeModuleCallExceptionHandler} if one exists, rethrowing
|
||||
* otherwise.
|
||||
*/
|
||||
public void handleException(RuntimeException e) {
|
||||
public void handleException(Exception e) {
|
||||
if (mCatalystInstance != null &&
|
||||
!mCatalystInstance.isDestroyed() &&
|
||||
mNativeModuleCallExceptionHandler != null) {
|
||||
mNativeModuleCallExceptionHandler.handleException(e);
|
||||
} else {
|
||||
throw e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,4 +19,8 @@ public class IllegalViewOperationException extends JSApplicationCausedNativeExce
|
|||
public IllegalViewOperationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public IllegalViewOperationException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,4 +21,6 @@ public interface RootView {
|
|||
* from the child's onTouchIntercepted implementation.
|
||||
*/
|
||||
void onChildStartedNativeGesture(MotionEvent androidEvent);
|
||||
|
||||
void handleException(Exception e);
|
||||
}
|
||||
|
|
|
@ -315,18 +315,27 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
|
|||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
if (getChildCount() > 0) {
|
||||
final int viewTag = getChildAt(0).getId();
|
||||
ReactContext reactContext = (ReactContext) getContext();
|
||||
ReactContext reactContext = getReactContext();
|
||||
reactContext.runOnNativeModulesQueueThread(
|
||||
new GuardedRunnable(reactContext) {
|
||||
@Override
|
||||
public void runGuarded() {
|
||||
((ReactContext) getContext()).getNativeModule(UIManagerModule.class)
|
||||
(getReactContext()).getNativeModule(UIManagerModule.class)
|
||||
.updateNodeSize(viewTag, w, h);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleException(Exception e) {
|
||||
getReactContext().handleException(e);
|
||||
}
|
||||
|
||||
private ReactContext getReactContext() {
|
||||
return (ReactContext) getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
mJSTouchDispatcher.handleTouchEvent(event, getEventDispatcher());
|
||||
|
@ -354,7 +363,7 @@ public class ReactModalHostView extends ViewGroup implements LifecycleEventListe
|
|||
}
|
||||
|
||||
private EventDispatcher getEventDispatcher() {
|
||||
ReactContext reactContext = (ReactContext) getContext();
|
||||
ReactContext reactContext = getReactContext();
|
||||
return reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,15 @@ import com.facebook.react.modules.i18nmanager.I18nUtil;
|
|||
import com.facebook.react.touch.OnInterceptTouchEventListener;
|
||||
import com.facebook.react.touch.ReactHitSlopView;
|
||||
import com.facebook.react.touch.ReactInterceptingViewGroup;
|
||||
import com.facebook.react.uimanager.IllegalViewOperationException;
|
||||
import com.facebook.react.uimanager.MeasureSpecAssertions;
|
||||
import com.facebook.react.uimanager.PointerEvents;
|
||||
import com.facebook.react.uimanager.ReactClippingViewGroup;
|
||||
import com.facebook.react.uimanager.ReactClippingViewGroupHelper;
|
||||
import com.facebook.react.uimanager.ReactPointerEventsView;
|
||||
import com.facebook.react.uimanager.ReactZIndexedViewGroup;
|
||||
import com.facebook.react.uimanager.RootView;
|
||||
import com.facebook.react.uimanager.RootViewUtil;
|
||||
import com.facebook.react.uimanager.ViewGroupDrawingOrderHelper;
|
||||
import com.facebook.yoga.YogaConstants;
|
||||
import javax.annotation.Nullable;
|
||||
|
@ -657,6 +660,24 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
try {
|
||||
dispatchOverflowDraw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
} catch (StackOverflowError e) {
|
||||
// Adding special exception management for StackOverflowError for logging purposes.
|
||||
// This will be removed in the future.
|
||||
RootView rootView = RootViewUtil.getRootView(ReactViewGroup.this);
|
||||
IllegalViewOperationException wrappedException =
|
||||
new IllegalViewOperationException("StackOverflowError", e);
|
||||
if (rootView != null) {
|
||||
rootView.handleException(wrappedException);
|
||||
} else {
|
||||
throw wrappedException;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchOverflowDraw(Canvas canvas) {
|
||||
if (mOverflow != null) {
|
||||
switch (mOverflow) {
|
||||
case "visible":
|
||||
|
@ -674,9 +695,9 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
final RectF borderWidth = mReactBackgroundDrawable.getDirectionAwareBorderInsets();
|
||||
|
||||
if (borderWidth.top > 0
|
||||
|| borderWidth.left > 0
|
||||
|| borderWidth.bottom > 0
|
||||
|| borderWidth.right > 0) {
|
||||
|| borderWidth.left > 0
|
||||
|| borderWidth.bottom > 0
|
||||
|| borderWidth.right > 0) {
|
||||
left += borderWidth.left;
|
||||
top += borderWidth.top;
|
||||
right -= borderWidth.right;
|
||||
|
@ -685,32 +706,32 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
|
||||
final float borderRadius = mReactBackgroundDrawable.getFullBorderRadius();
|
||||
float topLeftBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_LEFT);
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_LEFT);
|
||||
float topRightBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_RIGHT);
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_RIGHT);
|
||||
float bottomLeftBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_LEFT);
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_LEFT);
|
||||
float bottomRightBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_RIGHT);
|
||||
mReactBackgroundDrawable.getBorderRadiusOrDefaultTo(
|
||||
borderRadius, ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_RIGHT);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
final boolean isRTL = mLayoutDirection == View.LAYOUT_DIRECTION_RTL;
|
||||
float topStartBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_START);
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_START);
|
||||
float topEndBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_END);
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.TOP_END);
|
||||
float bottomStartBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_START);
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_START);
|
||||
float bottomEndBorderRadius =
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_END);
|
||||
mReactBackgroundDrawable.getBorderRadius(
|
||||
ReactViewBackgroundDrawable.BorderRadiusLocation.BOTTOM_END);
|
||||
|
||||
if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(getContext())) {
|
||||
if (YogaConstants.isUndefined(topStartBorderRadius)) {
|
||||
|
@ -730,13 +751,13 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
}
|
||||
|
||||
final float directionAwareTopLeftRadius =
|
||||
isRTL ? topEndBorderRadius : topStartBorderRadius;
|
||||
isRTL ? topEndBorderRadius : topStartBorderRadius;
|
||||
final float directionAwareTopRightRadius =
|
||||
isRTL ? topStartBorderRadius : topEndBorderRadius;
|
||||
isRTL ? topStartBorderRadius : topEndBorderRadius;
|
||||
final float directionAwareBottomLeftRadius =
|
||||
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
|
||||
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
|
||||
final float directionAwareBottomRightRadius =
|
||||
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
|
||||
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
|
||||
|
||||
topLeftBorderRadius = directionAwareTopLeftRadius;
|
||||
topRightBorderRadius = directionAwareTopRightRadius;
|
||||
|
@ -744,13 +765,13 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
bottomRightBorderRadius = directionAwareBottomRightRadius;
|
||||
} else {
|
||||
final float directionAwareTopLeftRadius =
|
||||
isRTL ? topEndBorderRadius : topStartBorderRadius;
|
||||
isRTL ? topEndBorderRadius : topStartBorderRadius;
|
||||
final float directionAwareTopRightRadius =
|
||||
isRTL ? topStartBorderRadius : topEndBorderRadius;
|
||||
isRTL ? topStartBorderRadius : topEndBorderRadius;
|
||||
final float directionAwareBottomLeftRadius =
|
||||
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
|
||||
isRTL ? bottomEndBorderRadius : bottomStartBorderRadius;
|
||||
final float directionAwareBottomRightRadius =
|
||||
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
|
||||
isRTL ? bottomStartBorderRadius : bottomEndBorderRadius;
|
||||
|
||||
if (!YogaConstants.isUndefined(directionAwareTopLeftRadius)) {
|
||||
topLeftBorderRadius = directionAwareTopLeftRadius;
|
||||
|
@ -771,27 +792,27 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
}
|
||||
|
||||
if (topLeftBorderRadius > 0
|
||||
|| topRightBorderRadius > 0
|
||||
|| bottomRightBorderRadius > 0
|
||||
|| bottomLeftBorderRadius > 0) {
|
||||
|| topRightBorderRadius > 0
|
||||
|| bottomRightBorderRadius > 0
|
||||
|| bottomLeftBorderRadius > 0) {
|
||||
if (mPath == null) {
|
||||
mPath = new Path();
|
||||
}
|
||||
|
||||
mPath.rewind();
|
||||
mPath.addRoundRect(
|
||||
new RectF(left, top, right, bottom),
|
||||
new float[] {
|
||||
Math.max(topLeftBorderRadius - borderWidth.left, 0),
|
||||
Math.max(topLeftBorderRadius - borderWidth.top, 0),
|
||||
Math.max(topRightBorderRadius - borderWidth.right, 0),
|
||||
Math.max(topRightBorderRadius - borderWidth.top, 0),
|
||||
Math.max(bottomRightBorderRadius - borderWidth.right, 0),
|
||||
Math.max(bottomRightBorderRadius - borderWidth.bottom, 0),
|
||||
Math.max(bottomLeftBorderRadius - borderWidth.left, 0),
|
||||
Math.max(bottomLeftBorderRadius - borderWidth.bottom, 0),
|
||||
},
|
||||
Path.Direction.CW);
|
||||
new RectF(left, top, right, bottom),
|
||||
new float[]{
|
||||
Math.max(topLeftBorderRadius - borderWidth.left, 0),
|
||||
Math.max(topLeftBorderRadius - borderWidth.top, 0),
|
||||
Math.max(topRightBorderRadius - borderWidth.right, 0),
|
||||
Math.max(topRightBorderRadius - borderWidth.top, 0),
|
||||
Math.max(bottomRightBorderRadius - borderWidth.right, 0),
|
||||
Math.max(bottomRightBorderRadius - borderWidth.bottom, 0),
|
||||
Math.max(bottomLeftBorderRadius - borderWidth.left, 0),
|
||||
Math.max(bottomLeftBorderRadius - borderWidth.bottom, 0),
|
||||
},
|
||||
Path.Direction.CW);
|
||||
canvas.clipPath(mPath);
|
||||
} else {
|
||||
canvas.clipRect(new RectF(left, top, right, bottom));
|
||||
|
@ -802,6 +823,5 @@ public class ReactViewGroup extends ViewGroup implements
|
|||
break;
|
||||
}
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue