From 4f7fd8d1de46c9596394a4ba73ed99dff902e04b Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Wed, 15 Feb 2023 13:06:36 -0800 Subject: [PATCH] Reduce use of Java exceptions in prop parsing (#36160) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/36160 Changelog: [Android][Fixed] - Invalid prop values no longer trigger Java exceptions in the legacy renderer ## Context We are changing React Native to behave more like a browser in the sense that **bad style values are not runtime errors**. (See e.g. D43159284 (https://github.com/facebook/react-native/commit/d6e9891577c81503407adaa85db8f5bf97557db0), D43184380.) The recommended way for developers to ensure they are passing correct style values is to use a typechecker (TypeScript or Flow) in conjunction with E2E tests and manual spot checks. ## This diff This change is similar to D43184380, but here we target the legacy renderer on Android by (1) replacing exceptions with logging calls, (2) adding fallback values (equivalent to resetting the respective props to `null`) where needed, and (3) replacing `null` checks in `ReactProp` converters with `instanceof` checks. Leaving the logging call sites in place (as opposed to deleting them) will be helpful if we decide that we want to repurpose these checks for a new, more visible diagnostic (though we would likely only build such a diagnostic in Fabric at this point). Reviewed By: javache Differential Revision: D43274525 fbshipit-source-id: 9d1e7ca3b6299dd827e8667e3d542433ec896c0e --- .../react/bridge/ColorPropConverter.java | 11 ++++ .../processing/ReactPropertyProcessor.java | 36 ++++++++----- .../react/uimanager/LayoutShadowNode.java | 54 ++++++++++++------- .../react/uimanager/TouchTargetHelper.java | 12 ++--- .../react/uimanager/TransformHelper.java | 6 +-- .../drawer/ReactDrawerLayoutManager.java | 16 ++++-- .../java/com/facebook/react/views/image/BUCK | 1 + .../react/views/image/ImageResizeMode.java | 21 ++++---- .../react/views/image/ReactImageManager.java | 7 +-- .../com/facebook/react/views/progressbar/BUCK | 1 + .../ReactProgressBarViewManager.java | 10 ++-- .../views/scroll/ReactScrollViewHelper.java | 8 +-- .../views/text/ReactBaseTextShadowNode.java | 13 +++-- .../text/ReactTextAnchorViewManager.java | 13 ++--- .../react/views/text/TextAttributeProps.java | 13 +++-- .../react/views/text/TextAttributes.java | 8 +-- .../react/views/text/frescosupport/BUCK | 1 + ...coBasedReactTextInlineImageShadowNode.java | 11 ++-- .../textinput/ReactTextInputManager.java | 12 +++-- .../textinput/ReactTextInputShadowNode.java | 7 +-- .../react/views/view/ReactViewManager.java | 12 +++-- .../views/image/ReactImagePropertyTest.java | 8 --- 22 files changed, 168 insertions(+), 113 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java index af4cfd3b1844cc..afc690dedaabbf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/ColorPropConverter.java @@ -12,6 +12,8 @@ import android.util.TypedValue; import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; +import com.facebook.common.logging.FLog; +import com.facebook.react.common.ReactConstants; public class ColorPropConverter { private static final String JSON_KEY = "resource_paths"; @@ -61,6 +63,15 @@ public static Integer getColor(Object value, Context context) { "ColorValue: the value must be a number or Object."); } + public static Integer getColor(Object value, Context context, int defaultInt) { + try { + return getColor(value, context); + } catch (JSApplicationCausedNativeException e) { + FLog.w(ReactConstants.TAG, e, "Error converting ColorValue"); + return defaultInt; + } + } + public static Integer resolveResourcePath(Context context, @Nullable String resourcePath) { if (resourcePath == null || resourcePath.isEmpty()) { return null; diff --git a/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java b/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java index 2b5b4ed60953cf..359f854fe5114f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java +++ b/ReactAndroid/src/main/java/com/facebook/react/processing/ReactPropertyProcessor.java @@ -363,11 +363,15 @@ private static CodeBlock.Builder getPropertyExtractor( ClassInfo classInfo, PropertyInfo info, CodeBlock.Builder builder) { TypeName propertyType = info.propertyType; if (propertyType.equals(STRING_TYPE)) { - return builder.add("($L)value", STRING_TYPE); + return builder.add("value instanceof $L ? ($L)value : null", STRING_TYPE, STRING_TYPE); } else if (propertyType.equals(READABLE_ARRAY_TYPE)) { - return builder.add("($L)value", READABLE_ARRAY_TYPE); // TODO: use real type but needs import + return builder.add( + "value instanceof $L ? ($L)value : null", + READABLE_ARRAY_TYPE, + READABLE_ARRAY_TYPE); // TODO: use real type but needs import } else if (propertyType.equals(READABLE_MAP_TYPE)) { - return builder.add("($L)value", READABLE_MAP_TYPE); + return builder.add( + "value instanceof $L ? ($L)value : null", READABLE_MAP_TYPE, READABLE_MAP_TYPE); } else if (propertyType.equals(DYNAMIC_TYPE)) { return builder.add("new $L(value)", DYNAMIC_FROM_OBJECT_TYPE); } else if (propertyType.equals(YOGA_VALUE_TYPE)) { @@ -380,40 +384,46 @@ private static CodeBlock.Builder getPropertyExtractor( } if (propertyType.equals(TypeName.BOOLEAN)) { - return builder.add("value == null ? $L : (boolean) value", info.mProperty.defaultBoolean()); + return builder.add( + "!(value instanceof Boolean) ? $L : (boolean)value", info.mProperty.defaultBoolean()); } if (propertyType.equals(TypeName.DOUBLE)) { double defaultDouble = info.mProperty.defaultDouble(); if (Double.isNaN(defaultDouble)) { - return builder.add("value == null ? $T.NaN : (double) value", Double.class); + return builder.add("!(value instanceof Double) ? $T.NaN : (double)value", Double.class); } else { - return builder.add("value == null ? $Lf : (double) value", defaultDouble); + return builder.add("!(value instanceof Double) ? $Lf : (double)value", defaultDouble); } } if (propertyType.equals(TypeName.FLOAT)) { float defaultFloat = info.mProperty.defaultFloat(); if (Float.isNaN(defaultFloat)) { - return builder.add("value == null ? $T.NaN : ((Double)value).floatValue()", Float.class); + return builder.add( + "!(value instanceof Double) ? $T.NaN : ((Double)value).floatValue()", Float.class); } else { - return builder.add("value == null ? $Lf : ((Double)value).floatValue()", defaultFloat); + return builder.add( + "!(value instanceof Double) ? $Lf : ((Double)value).floatValue()", defaultFloat); } } if ("Color".equals(info.mProperty.customType())) { switch (classInfo.getType()) { case VIEW_MANAGER: return builder.add( - "value == null ? $L : $T.getColor(value, view.getContext())", + "value == null ? $L : $T.getColor(value, view.getContext(), $L)", info.mProperty.defaultInt(), - com.facebook.react.bridge.ColorPropConverter.class); + com.facebook.react.bridge.ColorPropConverter.class, + info.mProperty.defaultInt()); case SHADOW_NODE: return builder.add( - "value == null ? $L : $T.getColor(value, node.getThemedContext())", + "value == null ? $L : $T.getColor(value, node.getThemedContext(), $L)", info.mProperty.defaultInt(), - com.facebook.react.bridge.ColorPropConverter.class); + com.facebook.react.bridge.ColorPropConverter.class, + info.mProperty.defaultInt()); } } else if (propertyType.equals(TypeName.INT)) { return builder.add( - "value == null ? $L : ((Double)value).intValue()", info.mProperty.defaultInt()); + "!(value instanceof Double) ? $L : ((Double)value).intValue()", + info.mProperty.defaultInt()); } throw new IllegalArgumentException(); diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java index bfcf2be62e5366..07cd486b5477f4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/LayoutShadowNode.java @@ -8,9 +8,10 @@ package com.facebook.react.uimanager; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableType; +import com.facebook.react.common.ReactConstants; import com.facebook.react.modules.i18nmanager.I18nUtil; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactPropGroup; @@ -60,11 +61,14 @@ void setFromDynamic(Dynamic dynamic) { unit = YogaUnit.PERCENT; value = Float.parseFloat(s.substring(0, s.length() - 1)); } else { - throw new IllegalArgumentException("Unknown value: " + s); + FLog.w(ReactConstants.TAG, "Unknown value: " + s); } - } else { + } else if (dynamic.getType() == ReadableType.Number) { unit = YogaUnit.POINT; value = PixelUtil.toPixelFromDIP(dynamic.asDouble()); + } else { + unit = YogaUnit.UNDEFINED; + value = YogaConstants.UNDEFINED; } } } @@ -318,8 +322,9 @@ public void setFlexDirection(@Nullable String flexDirection) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for flexDirection: " + flexDirection); + FLog.w(ReactConstants.TAG, "invalid value for flexDirection: " + flexDirection); + setFlexDirection(YogaFlexDirection.COLUMN); + break; } } } @@ -353,8 +358,9 @@ public void setFlexWrap(@Nullable String flexWrap) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for flexWrap: " + flexWrap); + FLog.w(ReactConstants.TAG, "invalid value for flexWrap: " + flexWrap); + setFlexWrap(YogaWrap.NO_WRAP); + break; } } } @@ -413,8 +419,9 @@ public void setAlignSelf(@Nullable String alignSelf) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for alignSelf: " + alignSelf); + FLog.w(ReactConstants.TAG, "invalid value for alignSelf: " + alignSelf); + setAlignSelf(YogaAlign.AUTO); + return; } } } @@ -473,8 +480,9 @@ public void setAlignItems(@Nullable String alignItems) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for alignItems: " + alignItems); + FLog.w(ReactConstants.TAG, "invalid value for alignItems: " + alignItems); + setAlignItems(YogaAlign.STRETCH); + return; } } } @@ -533,8 +541,9 @@ public void setAlignContent(@Nullable String alignContent) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for alignContent: " + alignContent); + FLog.w(ReactConstants.TAG, "invalid value for alignContent: " + alignContent); + setAlignContent(YogaAlign.FLEX_START); + return; } } } @@ -583,8 +592,9 @@ public void setJustifyContent(@Nullable String justifyContent) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for justifyContent: " + justifyContent); + FLog.w(ReactConstants.TAG, "invalid value for justifyContent: " + justifyContent); + setJustifyContent(YogaJustify.FLEX_START); + break; } } } @@ -617,8 +627,9 @@ public void setOverflow(@Nullable String overflow) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for overflow: " + overflow); + FLog.w(ReactConstants.TAG, "invalid value for overflow: " + overflow); + setOverflow(YogaOverflow.VISIBLE); + break; } } } @@ -647,7 +658,9 @@ public void setDisplay(@Nullable String display) { } default: { - throw new JSApplicationIllegalArgumentException("invalid value for display: " + display); + FLog.w(ReactConstants.TAG, "invalid value for display: " + display); + setDisplay(YogaDisplay.FLEX); + break; } } } @@ -820,8 +833,9 @@ public void setPosition(@Nullable String position) { } default: { - throw new JSApplicationIllegalArgumentException( - "invalid value for position: " + position); + FLog.w(ReactConstants.TAG, "invalid value for position: " + position); + setPositionType(YogaPositionType.RELATIVE); + break; } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java index cff46d4dcbd27e..f3ad5d201f550a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TouchTargetHelper.java @@ -17,8 +17,9 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.UiThreadUtil; +import com.facebook.react.common.ReactConstants; import com.facebook.react.touch.ReactHitSlopView; import com.facebook.react.uimanager.common.ViewUtil; import java.util.ArrayList; @@ -366,7 +367,10 @@ && isTouchPointInView(eventCoords[0], eventCoords[1], view)) { return null; - } else if (pointerEvents == PointerEvents.AUTO) { + } else { + if (pointerEvents != PointerEvents.AUTO) { + FLog.w(ReactConstants.TAG, "Unknown pointer event type: " + pointerEvents.toString()); + } // Either this view or one of its children is the target if (view instanceof ReactCompoundViewGroup && isTouchPointInView(eventCoords[0], eventCoords[1], view) @@ -387,10 +391,6 @@ && isTouchPointInView(eventCoords[0], eventCoords[1], view) pathAccumulator.add(new ViewTarget(view.getId(), view)); } return result; - - } else { - throw new JSApplicationIllegalArgumentException( - "Unknown pointer event type: " + pointerEvents.toString()); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java index da72da4b8d577e..49d6af12396a01 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java @@ -7,10 +7,11 @@ package com.facebook.react.uimanager; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableType; +import com.facebook.react.common.ReactConstants; /** * Class providing helper methods for converting transformation list (as accepted by 'transform' @@ -100,8 +101,7 @@ public static void processTransform(ReadableArray transforms, double[] result) { } else if ("skewY".equals(transformType)) { MatrixMathHelper.applySkewY(helperMatrix, convertToRadians(transform, transformType)); } else { - throw new JSApplicationIllegalArgumentException( - "Unsupported transform type: " + transformType); + FLog.w(ReactConstants.TAG, "Unsupported transform type: " + transformType); } MatrixMathHelper.multiplyInto(result, result, helperMatrix); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java index f3b50120c4bf0d..68a941f16ed717 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayoutManager.java @@ -12,11 +12,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.drawerlayout.widget.DrawerLayout; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableType; import com.facebook.react.common.MapBuilder; +import com.facebook.react.common.ReactConstants; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ThemedReactContext; @@ -90,13 +92,14 @@ public void setDrawerPosition(ReactDrawerLayout view, Dynamic drawerPosition) { if (Gravity.START == drawerPositionNum || Gravity.END == drawerPositionNum) { view.setDrawerPosition(drawerPositionNum); } else { - throw new JSApplicationIllegalArgumentException( - "Unknown drawerPosition " + drawerPositionNum); + FLog.w(ReactConstants.TAG, "Unknown drawerPosition " + drawerPositionNum); + view.setDrawerPosition(Gravity.START); } } else if (drawerPosition.getType() == ReadableType.String) { setDrawerPositionInternal(view, drawerPosition.asString()); } else { - throw new JSApplicationIllegalArgumentException("drawerPosition must be a string or int"); + FLog.w(ReactConstants.TAG, "drawerPosition must be a string or int"); + view.setDrawerPosition(Gravity.START); } } @@ -106,8 +109,10 @@ private void setDrawerPositionInternal(ReactDrawerLayout view, String drawerPosi } else if (drawerPosition.equals("right")) { view.setDrawerPosition(Gravity.END); } else { - throw new JSApplicationIllegalArgumentException( + FLog.w( + ReactConstants.TAG, "drawerPosition must be 'left' or 'right', received" + drawerPosition); + view.setDrawerPosition(Gravity.START); } } @@ -139,7 +144,8 @@ public void setDrawerLockMode(ReactDrawerLayout view, @Nullable String drawerLoc } else if ("locked-open".equals(drawerLockMode)) { view.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); } else { - throw new JSApplicationIllegalArgumentException("Unknown drawerLockMode " + drawerLockMode); + FLog.w(ReactConstants.TAG, "Unknown drawerLockMode " + drawerLockMode); + view.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK index f6f86f8b9a7388..eecd39d1a30a49 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/BUCK @@ -41,6 +41,7 @@ rn_android_library( ], deps = [ YOGA_TARGET, + react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("libraries/fresco/fresco-react-native:fbcore"), react_native_dep("libraries/fresco/fresco-react-native:fresco-drawee"), react_native_dep("libraries/fresco/fresco-react-native:fresco-react-native"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMode.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMode.java index 54df4db60d3e17..8af1fce425cce1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMode.java @@ -9,8 +9,9 @@ import android.graphics.Shader; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.drawee.drawable.ScalingUtils; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.react.common.ReactConstants; /** Converts JS resize modes into Android-specific scale type. */ public class ImageResizeMode { @@ -41,12 +42,11 @@ public static ScalingUtils.ScaleType toScaleType(@Nullable String resizeModeValu // Handled via a combination of ScaleType and TileMode return ScaleTypeStartInside.INSTANCE; } - if (resizeModeValue == null) { - // Use the default. Never use null. - return defaultValue(); + if (resizeModeValue != null) { + FLog.w(ReactConstants.TAG, "Invalid resize mode: '" + resizeModeValue + "'"); } - throw new JSApplicationIllegalArgumentException( - "Invalid resize mode: '" + resizeModeValue + "'"); + // Use the default. Never use null. + return defaultValue(); } /** Converts JS resize modes into {@code Shader.TileMode}. See {@code ImageResizeMode.js}. */ @@ -61,12 +61,11 @@ public static Shader.TileMode toTileMode(@Nullable String resizeModeValue) { // Handled via a combination of ScaleType and TileMode return Shader.TileMode.REPEAT; } - if (resizeModeValue == null) { - // Use the default. Never use null. - return defaultTileMode(); + if (resizeModeValue != null) { + FLog.w(ReactConstants.TAG, "Invalid resize mode: '" + resizeModeValue + "'"); } - throw new JSApplicationIllegalArgumentException( - "Invalid resize mode: '" + resizeModeValue + "'"); + // Use the default. Never use null. + return defaultTileMode(); } /** This is the default as per web and iOS. We want to be consistent across platforms. */ diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java index 187958c6ee4db0..67345805224b87 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java @@ -10,12 +10,13 @@ import android.graphics.Color; import android.graphics.PorterDuff.Mode; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; +import com.facebook.react.common.ReactConstants; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.SimpleViewManager; @@ -210,8 +211,8 @@ public void setResizeMethod(ReactImageView view, @Nullable String resizeMethod) } else if ("scale".equals(resizeMethod)) { view.setResizeMethod(ImageResizeMethod.SCALE); } else { - throw new JSApplicationIllegalArgumentException( - "Invalid resize method: '" + resizeMethod + "'"); + view.setResizeMethod(ImageResizeMethod.AUTO); + FLog.w(ReactConstants.TAG, "Invalid resize method: '" + resizeMethod + "'"); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK index 52d4b639706aed..ba2f92b743d10d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/BUCK @@ -13,6 +13,7 @@ rn_android_library( ], deps = [ YOGA_TARGET, + react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/java/infer-annotations:infer-annotations"), react_native_dep("third-party/java/jsr-305:jsr-305"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java index 3cdb730a391378..eb4cbfd779f858 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/progressbar/ReactProgressBarViewManager.java @@ -13,8 +13,9 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import androidx.annotation.Nullable; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.ReactConstants; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.PixelUtil; @@ -146,8 +147,8 @@ protected ViewManagerDelegate getDelegate() { /* package */ static int getStyleFromString(@Nullable String styleStr) { if (styleStr == null) { - throw new JSApplicationIllegalArgumentException( - "ProgressBar needs to have a style, null received"); + FLog.w(ReactConstants.TAG, "ProgressBar needs to have a style, null received"); + return android.R.attr.progressBarStyle; } else if (styleStr.equals("Horizontal")) { return android.R.attr.progressBarStyleHorizontal; } else if (styleStr.equals("Small")) { @@ -163,7 +164,8 @@ protected ViewManagerDelegate getDelegate() { } else if (styleStr.equals("Normal")) { return android.R.attr.progressBarStyle; } else { - throw new JSApplicationIllegalArgumentException("Unknown ProgressBar style: " + styleStr); + FLog.w(ReactConstants.TAG, "Unknown ProgressBar style: " + styleStr); + return android.R.attr.progressBarStyle; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java index 81d2382b212f23..e16dd1cad29358 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewHelper.java @@ -17,10 +17,10 @@ import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.common.ReactConstants; import com.facebook.react.common.build.ReactBuildConfig; import com.facebook.react.uimanager.FabricViewStateManager; import com.facebook.react.uimanager.PixelUtil; @@ -153,7 +153,8 @@ public static int parseOverScrollMode(String jsOverScrollMode) { } else if (jsOverScrollMode.equals(OVER_SCROLL_NEVER)) { return View.OVER_SCROLL_NEVER; } else { - throw new JSApplicationIllegalArgumentException("wrong overScrollMode: " + jsOverScrollMode); + FLog.w(ReactConstants.TAG, "wrong overScrollMode: " + jsOverScrollMode); + return View.OVER_SCROLL_IF_CONTENT_SCROLLS; } } @@ -167,7 +168,8 @@ public static int parseSnapToAlignment(@Nullable String alignment) { } else if ("end".equals(alignment)) { return SNAP_ALIGNMENT_END; } else { - throw new JSApplicationIllegalArgumentException("wrong snap alignment value: " + alignment); + FLog.w(ReactConstants.TAG, "wrong snap alignment value: " + alignment); + return SNAP_ALIGNMENT_DISABLED; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java index cbf2967d9755ab..12c1c5e05728a9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactBaseTextShadowNode.java @@ -17,10 +17,11 @@ import android.text.TextUtils; import android.view.Gravity; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.IllegalViewOperationException; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.NativeViewHierarchyOptimizer; @@ -460,7 +461,8 @@ public void setTextAlign(@Nullable String textAlign) { } else if ("center".equals(textAlign)) { mTextAlign = Gravity.CENTER_HORIZONTAL; } else { - throw new JSApplicationIllegalArgumentException("Invalid textAlign: " + textAlign); + FLog.w(ReactConstants.TAG, "Invalid textAlign: " + textAlign); + mTextAlign = Gravity.NO_GRAVITY; } } markUpdated(); @@ -572,8 +574,8 @@ public void setTextBreakStrategy(@Nullable String textBreakStrategy) { } else if ("balanced".equals(textBreakStrategy)) { mTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED; } else { - throw new JSApplicationIllegalArgumentException( - "Invalid textBreakStrategy: " + textBreakStrategy); + FLog.w(ReactConstants.TAG, "Invalid textBreakStrategy: " + textBreakStrategy); + mTextBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY; } markUpdated(); @@ -629,7 +631,8 @@ public void setTextTransform(@Nullable String textTransform) { } else if ("capitalize".equals(textTransform)) { mTextAttributes.setTextTransform(TextTransform.CAPITALIZE); } else { - throw new JSApplicationIllegalArgumentException("Invalid textTransform: " + textTransform); + FLog.w(ReactConstants.TAG, "Invalid textTransform: " + textTransform); + mTextAttributes.setTextTransform(TextTransform.UNSET); } markUpdated(); } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java index dc874023c58425..7b5d0c1dd32eaf 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextAnchorViewManager.java @@ -16,7 +16,7 @@ import android.view.View; import androidx.annotation.Nullable; import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.BaseViewManager; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.Spacing; @@ -65,7 +65,8 @@ public void setEllipsizeMode(ReactTextView view, @Nullable String ellipsizeMode) } else if (ellipsizeMode.equals("clip")) { view.setEllipsizeLocation(null); } else { - throw new JSApplicationIllegalArgumentException("Invalid ellipsizeMode: " + ellipsizeMode); + FLog.w(ReactConstants.TAG, "Invalid ellipsizeMode: " + ellipsizeMode); + view.setEllipsizeLocation(TextUtils.TruncateAt.END); } } @@ -85,8 +86,8 @@ public void setTextAlignVertical(ReactTextView view, @Nullable String textAlignV } else if ("center".equals(textAlignVertical)) { view.setGravityVertical(Gravity.CENTER_VERTICAL); } else { - throw new JSApplicationIllegalArgumentException( - "Invalid textAlignVertical: " + textAlignVertical); + FLog.w(ReactConstants.TAG, "Invalid textAlignVertical: " + textAlignVertical); + view.setGravityVertical(Gravity.NO_GRAVITY); } } @@ -118,8 +119,8 @@ public void setAndroidHyphenationFrequency(ReactTextView view, @Nullable String } else if (frequency.equals("normal")) { view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); } else { - throw new JSApplicationIllegalArgumentException( - "Invalid android_hyphenationFrequency: " + frequency); + FLog.w(ReactConstants.TAG, "Invalid android_hyphenationFrequency: " + frequency); + view.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java index 787ab9bb14db4a..822969ad8d8fc9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java @@ -13,9 +13,10 @@ import android.util.LayoutDirection; import android.view.Gravity; import androidx.annotation.Nullable; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.ReactConstants; import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactAccessibilityDelegate; @@ -267,7 +268,8 @@ public static int getTextAlignment(ReactStylesDiffMap props, boolean isRTL) { } else if ("center".equals(textAlignPropValue)) { textAlignment = Gravity.CENTER_HORIZONTAL; } else { - throw new JSApplicationIllegalArgumentException("Invalid textAlign: " + textAlignPropValue); + FLog.w(ReactConstants.TAG, "Invalid textAlign: " + textAlignPropValue); + textAlignment = Gravity.NO_GRAVITY; } } return textAlignment; @@ -563,8 +565,8 @@ public static int getLayoutDirection(@Nullable String layoutDirection) { } else if ("ltr".equals(layoutDirection)) { androidLayoutDirection = LayoutDirection.LTR; } else { - throw new JSApplicationIllegalArgumentException( - "Invalid layoutDirection: " + layoutDirection); + FLog.w(ReactConstants.TAG, "Invalid layoutDirection: " + layoutDirection); + androidLayoutDirection = UNSET; } return androidLayoutDirection; } @@ -595,7 +597,8 @@ private void setTextTransform(@Nullable String textTransform) { } else if ("capitalize".equals(textTransform)) { mTextTransform = TextTransform.CAPITALIZE; } else { - throw new JSApplicationIllegalArgumentException("Invalid textTransform: " + textTransform); + FLog.w(ReactConstants.TAG, "Invalid textTransform: " + textTransform); + mTextTransform = TextTransform.NONE; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributes.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributes.java index f4f78871e8b3c8..8d54a23bbdf6ac 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributes.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributes.java @@ -7,7 +7,8 @@ package com.facebook.react.views.text; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; +import com.facebook.common.logging.FLog; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ViewDefaults; @@ -98,8 +99,9 @@ public float getMaxFontSizeMultiplier() { public void setMaxFontSizeMultiplier(float maxFontSizeMultiplier) { if (maxFontSizeMultiplier != 0 && maxFontSizeMultiplier < 1) { - throw new JSApplicationIllegalArgumentException( - "maxFontSizeMultiplier must be NaN, 0, or >= 1"); + FLog.w(ReactConstants.TAG, "maxFontSizeMultiplier must be NaN, 0, or >= 1"); + mMaxFontSizeMultiplier = Float.NaN; + return; } mMaxFontSizeMultiplier = maxFontSizeMultiplier; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK index e48762cb597ef9..7f8afcb93b4f78 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/BUCK @@ -14,6 +14,7 @@ rn_android_library( deps = [ YOGA_TARGET, react_native_dep("third-party/android/androidx:annotation"), + react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("libraries/fresco/fresco-react-native:fbcore"), react_native_dep("libraries/fresco/fresco-react-native:fresco-drawee"), react_native_dep("libraries/fresco/fresco-react-native:fresco-react-native"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java index 65d83c2a536cf6..845ca7f1d19858 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/frescosupport/FrescoBasedReactTextInlineImageShadowNode.java @@ -11,13 +11,14 @@ import android.content.res.Resources; import android.net.Uri; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.common.util.UriUtil; import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableType; +import com.facebook.react.common.ReactConstants; import com.facebook.react.uimanager.ViewProps; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.views.text.ReactTextInlineImageShadowNode; @@ -84,8 +85,8 @@ public void setWidth(Dynamic width) { if (width.getType() == ReadableType.Number) { mWidth = (float) width.asDouble(); } else { - throw new JSApplicationIllegalArgumentException( - "Inline images must not have percentage based width"); + FLog.w(ReactConstants.TAG, "Inline images must not have percentage based width"); + mWidth = YogaConstants.UNDEFINED; } } @@ -94,8 +95,8 @@ public void setHeight(Dynamic height) { if (height.getType() == ReadableType.Number) { mHeight = (float) height.asDouble(); } else { - throw new JSApplicationIllegalArgumentException( - "Inline images must not have percentage based height"); + FLog.w(ReactConstants.TAG, "Inline images must not have percentage based height"); + mHeight = YogaConstants.UNDEFINED; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java index f4733dfcd2bc7e..ddbc072e20d38b 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java @@ -35,7 +35,6 @@ import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.react.bridge.Dynamic; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.ReadableArray; @@ -45,6 +44,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableNativeMap; import com.facebook.react.common.MapBuilder; +import com.facebook.react.common.ReactConstants; import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.BaseViewManager; @@ -691,7 +691,8 @@ public void setTextAlign(ReactEditText view, @Nullable String textAlign) { } else if ("center".equals(textAlign)) { view.setGravityHorizontal(Gravity.CENTER_HORIZONTAL); } else { - throw new JSApplicationIllegalArgumentException("Invalid textAlign: " + textAlign); + FLog.w(ReactConstants.TAG, "Invalid textAlign: " + textAlign); + view.setGravityHorizontal(Gravity.NO_GRAVITY); } } } @@ -707,8 +708,8 @@ public void setTextAlignVertical(ReactEditText view, @Nullable String textAlignV } else if ("center".equals(textAlignVertical)) { view.setGravityVertical(Gravity.CENTER_VERTICAL); } else { - throw new JSApplicationIllegalArgumentException( - "Invalid textAlignVertical: " + textAlignVertical); + FLog.w(ReactConstants.TAG, "Invalid textAlignVertical: " + textAlignVertical); + view.setGravityVertical(Gravity.NO_GRAVITY); } } @@ -784,7 +785,8 @@ public void setTextContentType(ReactEditText view, @Nullable String autoComplete } else if (REACT_PROPS_AUTOFILL_HINTS_MAP.containsKey(autoComplete)) { setAutofillHints(view, REACT_PROPS_AUTOFILL_HINTS_MAP.get(autoComplete)); } else { - throw new JSApplicationIllegalArgumentException("Invalid autoComplete: " + autoComplete); + FLog.w(ReactConstants.TAG, "Invalid autoComplete: " + autoComplete); + setImportantForAutofill(view, View.IMPORTANT_FOR_AUTOFILL_NO); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java index e621e1b585f9be..d53a7f9083aa60 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputShadowNode.java @@ -15,9 +15,10 @@ import android.widget.EditText; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; +import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.uimanager.Spacing; import com.facebook.react.uimanager.ThemedReactContext; @@ -218,8 +219,8 @@ public void setTextBreakStrategy(@Nullable String textBreakStrategy) { } else if ("balanced".equals(textBreakStrategy)) { mTextBreakStrategy = Layout.BREAK_STRATEGY_BALANCED; } else { - throw new JSApplicationIllegalArgumentException( - "Invalid textBreakStrategy: " + textBreakStrategy); + FLog.w(ReactConstants.TAG, "Invalid textBreakStrategy: " + textBreakStrategy); + mTextBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE; } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java index 4bd81d72153b0b..4b3d624907d0d5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java @@ -13,12 +13,14 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; +import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.PixelUtil; @@ -152,9 +154,6 @@ public void setBorderStyle(ReactViewGroup view, @Nullable String borderStyle) { @ReactProp(name = "hitSlop") public void setHitSlop(final ReactViewGroup view, Dynamic hitSlop) { switch (hitSlop.getType()) { - case Null: - view.setHitSlopRect(null); - break; case Map: ReadableMap hitSlopMap = hitSlop.asMap(); view.setHitSlopRect( @@ -177,8 +176,11 @@ public void setHitSlop(final ReactViewGroup view, Dynamic hitSlop) { view.setHitSlopRect(new Rect(hitSlopValue, hitSlopValue, hitSlopValue, hitSlopValue)); break; default: - throw new JSApplicationIllegalArgumentException( - "Invalid type for 'hitSlop' value " + hitSlop.getType()); + FLog.w(ReactConstants.TAG, "Invalid type for 'hitSlop' value " + hitSlop.getType()); + /* falls through */ + case Null: + view.setHitSlopRect(null); + break; } } diff --git a/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java b/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java index a2e255f1e4db5c..4240993a2a698c 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/views/image/ReactImagePropertyTest.java @@ -16,7 +16,6 @@ import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.CatalystInstance; -import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.JavaOnlyArray; import com.facebook.react.bridge.JavaOnlyMap; import com.facebook.react.bridge.ReactApplicationContext; @@ -101,13 +100,6 @@ public ReactStylesDiffMap buildStyles(Object... keysAndValues) { return new ReactStylesDiffMap(JavaOnlyMap.of(keysAndValues)); } - @Test(expected = JSApplicationIllegalArgumentException.class) - public void testImageInvalidResizeMode() { - ReactImageManager viewManager = new ReactImageManager(); - ReactImageView view = viewManager.createViewInstance(mThemeContext); - viewManager.updateProperties(view, buildStyles("resizeMode", "pancakes")); - } - @Test public void testBorderColor() { ReactImageManager viewManager = new ReactImageManager();