From 1ca9a9553763a89c977f756b45486f8b9cedab80 Mon Sep 17 00:00:00 2001 From: Christoph Nakazawa Date: Tue, 2 Apr 2019 11:13:01 -0700 Subject: [PATCH] Move WebView Android files to FB internal Summary: This moves the Java files to FB internal and updates all the buck files Reviewed By: TheSavior Differential Revision: D14622521 fbshipit-source-id: a8d293e9f9e08868cca3ed2986a08d0db16dec15 --- .../main/java/com/facebook/react/shell/BUCK | 1 - .../react/shell/MainReactPackage.java | 2 - .../facebook/react/uimanager/ViewManager.java | 14 +- .../com/facebook/react/views/webview/BUCK | 18 - .../views/webview/ReactWebViewManager.java | 757 ------------------ .../react/views/webview/WebViewConfig.java | 19 - .../webview/events/TopLoadingErrorEvent.java | 47 -- .../webview/events/TopLoadingFinishEvent.java | 47 -- .../webview/events/TopLoadingStartEvent.java | 47 -- .../views/webview/events/TopMessageEvent.java | 50 -- 10 files changed, 1 insertion(+), 1001 deletions(-) delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/BUCK delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/WebViewConfig.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingErrorEvent.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingFinishEvent.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingStartEvent.java delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopMessageEvent.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK index d564204541e910..6e16ebfe191d6d 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK @@ -62,7 +62,6 @@ rn_android_library( react_native_target("java/com/facebook/react/views/toolbar:toolbar"), react_native_target("java/com/facebook/react/views/view:view"), react_native_target("java/com/facebook/react/views/viewpager:viewpager"), - react_native_target("java/com/facebook/react/views/webview:webview"), react_native_target("java/com/facebook/react/module/annotations:annotations"), react_native_target("res:shell"), ], diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index 1e0b35ea29a33c..22348ac1dc63c0 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -62,7 +62,6 @@ import com.facebook.react.views.toolbar.ReactToolbarManager; import com.facebook.react.views.view.ReactViewManager; import com.facebook.react.views.viewpager.ReactViewPagerManager; -import com.facebook.react.views.webview.ReactWebViewManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -337,7 +336,6 @@ public List createViewManagers(ReactApplicationContext reactContext viewManagers.add(new ReactSliderManager()); viewManagers.add(new ReactSwitchManager()); viewManagers.add(new ReactToolbarManager()); - viewManagers.add(new ReactWebViewManager()); viewManagers.add(new SwipeRefreshLayoutManager()); // Native equivalents diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java index cf852a64500bef..086c5b1021a7dc 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java @@ -129,7 +129,7 @@ protected void onAfterUpdateTransaction(@Nonnull T view) { /** * Subclasses may use this method to receive events/commands directly from JS through the * {@link UIManager}. Good example of such a command would be {@code scrollTo} request with - * coordinates for a {@link ScrollView} or {@code goBack} request for a {@link WebView} instance. + * coordinates for a {@link ScrollView} instance. * * @param root View instance that should receive the command * @param commandId code of the command @@ -144,18 +144,6 @@ public void receiveCommand(@Nonnull T root, int commandId, @Nullable ReadableArr * map between names of the commands and IDs that are then used in {@link #receiveCommand} method * whenever the command is dispatched for this particular {@link ViewManager}. * - * As an example we may consider {@link ReactWebViewManager} that expose the following commands: - * goBack, goForward, reload. In this case the map returned from {@link #getCommandsMap} from - * {@link ReactWebViewManager} will look as follows: - * { - * "goBack": 1, - * "goForward": 2, - * "reload": 3, - * } - * - * Now assuming that "reload" command is dispatched through {@link UIManagerModule} we trigger - * {@link ReactWebViewManager#receiveCommand} passing "3" as {@code commandId} argument. - * * @return map of string to int mapping of the expected commands */ public @Nullable Map getCommandsMap() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/webview/BUCK deleted file mode 100644 index 5e8a0cc8333e64..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/BUCK +++ /dev/null @@ -1,18 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "webview", - srcs = glob(["**/*.java"]), - visibility = [ - "PUBLIC", - ], - deps = [ - react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), - react_native_dep("third-party/java/jsr-305:jsr-305"), - react_native_target("java/com/facebook/react/bridge:bridge"), - react_native_target("java/com/facebook/react/common:common"), - react_native_target("java/com/facebook/react/module/annotations:annotations"), - react_native_target("java/com/facebook/react/uimanager:uimanager"), - react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), - ], -) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java deleted file mode 100644 index 3e3fd79d230862..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ /dev/null @@ -1,757 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - *

This source code is licensed under the MIT license found in the LICENSE file in the root - * directory of this source tree. - */ -package com.facebook.react.views.webview; - -import android.annotation.TargetApi; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.Bitmap; -import android.graphics.Picture; -import android.net.Uri; -import android.os.Build; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.webkit.ClientCertRequest; -import android.webkit.ConsoleMessage; -import android.webkit.CookieManager; -import android.webkit.GeolocationPermissions; -import android.webkit.JavascriptInterface; -import android.webkit.ValueCallback; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.ReadableMapKeySetIterator; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.common.build.ReactBuildConfig; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.uimanager.SimpleViewManager; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.events.ContentSizeChangeEvent; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.facebook.react.views.webview.events.TopLoadingErrorEvent; -import com.facebook.react.views.webview.events.TopLoadingFinishEvent; -import com.facebook.react.views.webview.events.TopLoadingStartEvent; -import com.facebook.react.views.webview.events.TopMessageEvent; -import java.io.UnsupportedEncodingException; -import java.net.URISyntaxException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Pattern; -import javax.annotation.Nullable; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Manages instances of {@link WebView} - * - *

Can accept following commands: - GO_BACK - GO_FORWARD - RELOAD - * - *

{@link WebView} instances could emit following direct events: - topLoadingFinish - - * topLoadingStart - topLoadingError - * - *

Each event will carry the following properties: - target - view's react tag - url - url set - * for the webview - loading - whether webview is in a loading state - title - title of the current - * page - canGoBack - boolean, whether there is anything on a history stack to go back - - * canGoForward - boolean, whether it is possible to request GO_FORWARD command - */ -@ReactModule(name = ReactWebViewManager.REACT_CLASS) -public class ReactWebViewManager extends SimpleViewManager { - - public static final String REACT_CLASS = "RCTWebView"; - - protected static final String HTML_ENCODING = "UTF-8"; - protected static final String HTML_MIME_TYPE = "text/html"; - protected static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE"; - - protected static final String HTTP_METHOD_POST = "POST"; - - public static final int COMMAND_GO_BACK = 1; - public static final int COMMAND_GO_FORWARD = 2; - public static final int COMMAND_RELOAD = 3; - public static final int COMMAND_STOP_LOADING = 4; - public static final int COMMAND_POST_MESSAGE = 5; - public static final int COMMAND_INJECT_JAVASCRIPT = 6; - - // Use `webView.loadUrl("about:blank")` to reliably reset the view - // state and release page resources (including any running JavaScript). - protected static final String BLANK_URL = "about:blank"; - - // Intent urls are a type of deeplinks which start with: intent:// - private static final String INTENT_URL_PREFIX = "intent://"; - - private static CustomClientCertRequestHandler customClientCertRequestHandler = null; - - protected WebViewConfig mWebViewConfig; - protected @Nullable WebView.PictureListener mPictureListener; - - protected static class ReactWebViewClient extends WebViewClient { - - protected boolean mLastLoadFailed = false; - protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent; - protected @Nullable List mOriginWhitelist; - - @Override - public void onPageFinished(WebView webView, String url) { - super.onPageFinished(webView, url); - - if (!mLastLoadFailed) { - ReactWebView reactWebView = (ReactWebView) webView; - reactWebView.callInjectedJavaScript(); - reactWebView.linkBridge(); - emitFinishEvent(webView, url); - } - } - - @Override - public void onPageStarted(WebView webView, String url, Bitmap favicon) { - super.onPageStarted(webView, url, favicon); - mLastLoadFailed = false; - - dispatchEvent( - webView, new TopLoadingStartEvent(webView.getId(), createWebViewEvent(webView, url))); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - if (url.equals(BLANK_URL)) return false; - - // url blacklisting - if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) { - ArrayList urlPrefixesForDefaultIntent = mUrlPrefixesForDefaultIntent.toArrayList(); - for (Object urlPrefix : urlPrefixesForDefaultIntent) { - if (url.startsWith((String) urlPrefix)) { - launchIntent(view.getContext(), url); - return true; - } - } - } - - if (mOriginWhitelist != null && shouldHandleURL(mOriginWhitelist, url)) { - return false; - } - - launchIntent(view.getContext(), url); - return true; - } - - private void launchIntent(Context context, String url) { - Intent intent = null; - - // URLs starting with 'intent://' require special handling. - if (url.startsWith(INTENT_URL_PREFIX)) { - try { - intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); - } catch (URISyntaxException e) { - FLog.e(ReactConstants.TAG, "Can't parse intent:// URI", e); - } - } - - if (intent != null) { - // This is needed to prevent security issue where non-exported activities from the same process can be started with intent:// URIs. - // See: T10607927/S136245 - intent.addCategory(Intent.CATEGORY_BROWSABLE); - intent.setComponent(null); - intent.setSelector(null); - - PackageManager packageManager = context.getPackageManager(); - ResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - if (info != null) { - // App is installed. - context.startActivity(intent); - } else { - String fallbackUrl = intent.getStringExtra("browser_fallback_url"); - intent = new Intent(Intent.ACTION_VIEW, Uri.parse(fallbackUrl)); - } - } else { - intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - } - - try { - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addCategory(Intent.CATEGORY_BROWSABLE); - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e); - } - } - - private boolean shouldHandleURL(List originWhitelist, String url) { - Uri uri = Uri.parse(url); - String scheme = uri.getScheme() != null ? uri.getScheme() : ""; - String authority = uri.getAuthority() != null ? uri.getAuthority() : ""; - String urlToCheck = scheme + "://" + authority; - for (Pattern pattern : originWhitelist) { - if (pattern.matcher(urlToCheck).matches()) { - return true; - } - } - return false; - } - - @Override - public void onReceivedClientCertRequest(WebView webview, ClientCertRequest request) { - if (ReactWebViewManager.customClientCertRequestHandler != null) { - ReactWebViewManager.customClientCertRequestHandler.handle(webview, request); - } else { - super.onReceivedClientCertRequest(webview, request); - } - } - - @Override - public void onReceivedError( - WebView webView, int errorCode, String description, String failingUrl) { - super.onReceivedError(webView, errorCode, description, failingUrl); - mLastLoadFailed = true; - - // In case of an error JS side expect to get a finish event first, and then get an error event - // Android WebView does it in the opposite way, so we need to simulate that behavior - emitFinishEvent(webView, failingUrl); - - WritableMap eventData = createWebViewEvent(webView, failingUrl); - eventData.putDouble("code", errorCode); - eventData.putString("description", description); - - dispatchEvent(webView, new TopLoadingErrorEvent(webView.getId(), eventData)); - } - - protected void emitFinishEvent(WebView webView, String url) { - dispatchEvent( - webView, new TopLoadingFinishEvent(webView.getId(), createWebViewEvent(webView, url))); - } - - protected WritableMap createWebViewEvent(WebView webView, String url) { - WritableMap event = Arguments.createMap(); - event.putDouble("target", webView.getId()); - // Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks - // like onPageFinished - event.putString("url", url); - event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100); - event.putString("title", webView.getTitle()); - event.putBoolean("canGoBack", webView.canGoBack()); - event.putBoolean("canGoForward", webView.canGoForward()); - return event; - } - - public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) { - mUrlPrefixesForDefaultIntent = specialUrls; - } - - public void setOriginWhitelist(List originWhitelist) { - mOriginWhitelist = originWhitelist; - } - } - - /** - * Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order - * to call {@link WebView#destroy} on activity destroy event and also to clear the client - */ - protected static class ReactWebView extends WebView implements LifecycleEventListener { - protected @Nullable String injectedJS; - protected boolean messagingEnabled = false; - protected @Nullable ReactWebViewClient mReactWebViewClient; - - protected class ReactWebViewBridge { - ReactWebView mContext; - - ReactWebViewBridge(ReactWebView c) { - mContext = c; - } - - @JavascriptInterface - public void postMessage(String message) { - mContext.onMessage(message); - } - } - - /** - * WebView must be created with an context of the current activity - * - *

Activity Context is required for creation of dialogs internally by WebView Reactive Native - * needed for access to ReactNative internal system functionality - */ - public ReactWebView(ThemedReactContext reactContext) { - super(reactContext); - } - - @Override - public void onHostResume() { - // do nothing - } - - @Override - public void onHostPause() { - // do nothing - } - - @Override - public void onHostDestroy() { - cleanupCallbacksAndDestroy(); - } - - @Override - public void setWebViewClient(WebViewClient client) { - super.setWebViewClient(client); - mReactWebViewClient = (ReactWebViewClient) client; - } - - public @Nullable ReactWebViewClient getReactWebViewClient() { - return mReactWebViewClient; - } - - public void setInjectedJavaScript(@Nullable String js) { - injectedJS = js; - } - - protected ReactWebViewBridge createReactWebViewBridge(ReactWebView webView) { - return new ReactWebViewBridge(webView); - } - - public void setMessagingEnabled(boolean enabled) { - if (messagingEnabled == enabled) { - return; - } - - messagingEnabled = enabled; - if (enabled) { - addJavascriptInterface(createReactWebViewBridge(this), BRIDGE_NAME); - linkBridge(); - } else { - removeJavascriptInterface(BRIDGE_NAME); - } - } - - protected void evaluateJavascriptWithFallback(String script) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - evaluateJavascript(script, null); - return; - } - - try { - loadUrl("javascript:" + URLEncoder.encode(script, "UTF-8")); - } catch (UnsupportedEncodingException e) { - // UTF-8 should always be supported - throw new RuntimeException(e); - } - } - - public void callInjectedJavaScript() { - if (getSettings().getJavaScriptEnabled() - && injectedJS != null - && !TextUtils.isEmpty(injectedJS)) { - evaluateJavascriptWithFallback("(function() {\n" + injectedJS + ";\n})();"); - } - } - - public void linkBridge() { - if (messagingEnabled) { - if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - // See isNative in lodash - String testPostMessageNative = - "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')"; - evaluateJavascript( - testPostMessageNative, - new ValueCallback() { - @Override - public void onReceiveValue(String value) { - if (value.equals("true")) { - FLog.w( - ReactConstants.TAG, - "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined"); - } - } - }); - } - - evaluateJavascriptWithFallback( - "(" - + "window.originalPostMessage = window.postMessage," - + "window.postMessage = function(data) {" - + BRIDGE_NAME - + ".postMessage(String(data));" - + "}" - + ")"); - } - } - - public void onMessage(String message) { - dispatchEvent(this, new TopMessageEvent(this.getId(), message)); - } - - protected void cleanupCallbacksAndDestroy() { - setWebViewClient(null); - destroy(); - } - } - - public ReactWebViewManager() { - mWebViewConfig = - new WebViewConfig() { - public void configWebView(WebView webView) {} - }; - } - - public ReactWebViewManager(WebViewConfig webViewConfig) { - mWebViewConfig = webViewConfig; - } - - @Override - public String getName() { - return REACT_CLASS; - } - - protected ReactWebView createReactWebViewInstance(ThemedReactContext reactContext) { - return new ReactWebView(reactContext); - } - - @Override - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - protected WebView createViewInstance(ThemedReactContext reactContext) { - ReactWebView webView = createReactWebViewInstance(reactContext); - webView.setWebChromeClient( - new WebChromeClient() { - @Override - public boolean onConsoleMessage(ConsoleMessage message) { - if (ReactBuildConfig.DEBUG) { - return super.onConsoleMessage(message); - } - // Ignore console logs in non debug builds. - return true; - } - - @Override - public void onGeolocationPermissionsShowPrompt( - String origin, GeolocationPermissions.Callback callback) { - callback.invoke(origin, true, false); - } - }); - reactContext.addLifecycleEventListener(webView); - mWebViewConfig.configWebView(webView); - WebSettings settings = webView.getSettings(); - settings.setBuiltInZoomControls(true); - settings.setDisplayZoomControls(false); - settings.setDomStorageEnabled(true); - - settings.setAllowFileAccess(false); - settings.setAllowContentAccess(false); - settings.setAllowFileAccessFromFileURLs(false); - setAllowUniversalAccessFromFileURLs(webView, false); - setMixedContentMode(webView, "never"); - - // Fixes broken full-screen modals/galleries due to body height being 0. - webView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - - setGeolocationEnabled(webView, false); - if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - WebView.setWebContentsDebuggingEnabled(true); - } - return webView; - } - - @ReactProp(name = "hardwareAccelerationEnabledExperimental", defaultBoolean = true) - public void sethardwareAccelerationEnabledExperimental(WebView view, boolean enabled) { - // Hardware acceleration can not be enabled at view level but it can be disabled - // see: https://developer.android.com/guide/topics/graphics/hardware-accel - // - // Disabling hardware acceleration is sometimes required to workaround chromium bugs: - // https://bugs.chromium.org/p/chromium/issues/detail?id=501901 - // - if (!enabled) { - view.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } - } - - @ReactProp(name = "javaScriptEnabled") - public void setJavaScriptEnabled(WebView view, boolean enabled) { - view.getSettings().setJavaScriptEnabled(enabled); - } - - @ReactProp(name = "thirdPartyCookiesEnabled") - public void setThirdPartyCookiesEnabled(WebView view, boolean enabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - CookieManager.getInstance().setAcceptThirdPartyCookies(view, enabled); - } - } - - @ReactProp(name = "scalesPageToFit") - public void setScalesPageToFit(WebView view, boolean enabled) { - view.getSettings().setUseWideViewPort(!enabled); - } - - @ReactProp(name = "domStorageEnabled") - public void setDomStorageEnabled(WebView view, boolean enabled) { - view.getSettings().setDomStorageEnabled(enabled); - } - - @ReactProp(name = "userAgent") - public void setUserAgent(WebView view, @Nullable String userAgent) { - if (userAgent != null) { - // TODO(8496850): Fix incorrect behavior when property is unset (uA == null) - view.getSettings().setUserAgentString(userAgent); - } - } - - @ReactProp(name = "mediaPlaybackRequiresUserAction") - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) { - view.getSettings().setMediaPlaybackRequiresUserGesture(requires); - } - - @ReactProp(name = "allowUniversalAccessFromFileURLs") - public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) { - view.getSettings().setAllowUniversalAccessFromFileURLs(allow); - } - - @ReactProp(name = "saveFormDataDisabled") - public void setSaveFormDataDisabled(WebView view, boolean disable) { - view.getSettings().setSaveFormData(!disable); - } - - @ReactProp(name = "injectedJavaScript") - public void setInjectedJavaScript(WebView view, @Nullable String injectedJavaScript) { - ((ReactWebView) view).setInjectedJavaScript(injectedJavaScript); - } - - @ReactProp(name = "messagingEnabled") - public void setMessagingEnabled(WebView view, boolean enabled) { - ((ReactWebView) view).setMessagingEnabled(enabled); - } - - @ReactProp(name = "source") - public void setSource(WebView view, @Nullable ReadableMap source) { - if (source != null) { - if (source.hasKey("html")) { - String html = source.getString("html"); - if (source.hasKey("baseUrl")) { - view.loadDataWithBaseURL( - source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null); - } else { - view.loadData(html, HTML_MIME_TYPE, HTML_ENCODING); - } - return; - } - if (source.hasKey("uri")) { - String url = source.getString("uri"); - String previousUrl = view.getUrl(); - if (previousUrl != null && previousUrl.equals(url)) { - return; - } - if (source.hasKey("method")) { - String method = source.getString("method"); - if (method.equalsIgnoreCase(HTTP_METHOD_POST)) { - byte[] postData = null; - if (source.hasKey("body")) { - String body = source.getString("body"); - try { - postData = body.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - postData = body.getBytes(); - } - } - if (postData == null) { - postData = new byte[0]; - } - view.postUrl(url, postData); - return; - } - } - HashMap headerMap = new HashMap<>(); - if (source.hasKey("headers")) { - ReadableMap headers = source.getMap("headers"); - ReadableMapKeySetIterator iter = headers.keySetIterator(); - while (iter.hasNextKey()) { - String key = iter.nextKey(); - if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) { - if (view.getSettings() != null) { - view.getSettings().setUserAgentString(headers.getString(key)); - } - } else { - headerMap.put(key, headers.getString(key)); - } - } - } - view.loadUrl(url, headerMap); - return; - } - } - view.loadUrl(BLANK_URL); - } - - @ReactProp(name = "onContentSizeChange") - public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) { - if (sendContentSizeChangeEvents) { - view.setPictureListener(getPictureListener()); - } else { - view.setPictureListener(null); - } - } - - @ReactProp(name = "mixedContentMode") - public void setMixedContentMode(WebView view, @Nullable String mixedContentMode) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (mixedContentMode == null || "never".equals(mixedContentMode)) { - view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); - } else if ("always".equals(mixedContentMode)) { - view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } else if ("compatibility".equals(mixedContentMode)) { - view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); - } - } - } - - @ReactProp(name = "urlPrefixesForDefaultIntent") - public void setUrlPrefixesForDefaultIntent( - WebView view, @Nullable ReadableArray urlPrefixesForDefaultIntent) { - ReactWebViewClient client = ((ReactWebView) view).getReactWebViewClient(); - if (client != null && urlPrefixesForDefaultIntent != null) { - client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent); - } - } - - @ReactProp(name = "allowFileAccess") - public void setAllowFileAccess(WebView view, @Nullable Boolean allowFileAccess) { - view.getSettings().setAllowFileAccess(allowFileAccess != null && allowFileAccess); - } - - @ReactProp(name = "geolocationEnabled") - public void setGeolocationEnabled(WebView view, @Nullable Boolean isGeolocationEnabled) { - view.getSettings().setGeolocationEnabled(isGeolocationEnabled != null && isGeolocationEnabled); - } - - @ReactProp(name = "originWhitelist") - public void setOriginWhitelist(WebView view, @Nullable ReadableArray originWhitelist) { - ReactWebViewClient client = ((ReactWebView) view).getReactWebViewClient(); - if (client != null && originWhitelist != null) { - List whiteList = new LinkedList<>(); - for (int i = 0; i < originWhitelist.size(); i++) { - whiteList.add(Pattern.compile(originWhitelist.getString(i))); - } - client.setOriginWhitelist(whiteList); - } - } - - @Override - protected void addEventEmitters(ThemedReactContext reactContext, WebView view) { - // Do not register default touch emitter and let WebView implementation handle touches - view.setWebViewClient(new ReactWebViewClient()); - } - - @Override - public @Nullable Map getCommandsMap() { - return MapBuilder.of( - "goBack", COMMAND_GO_BACK, - "goForward", COMMAND_GO_FORWARD, - "reload", COMMAND_RELOAD, - "stopLoading", COMMAND_STOP_LOADING, - "postMessage", COMMAND_POST_MESSAGE, - "injectJavaScript", COMMAND_INJECT_JAVASCRIPT); - } - - @Override - public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) { - switch (commandId) { - case COMMAND_GO_BACK: - root.goBack(); - break; - case COMMAND_GO_FORWARD: - root.goForward(); - break; - case COMMAND_RELOAD: - root.reload(); - break; - case COMMAND_STOP_LOADING: - root.stopLoading(); - break; - case COMMAND_POST_MESSAGE: - try { - ReactWebView reactWebView = (ReactWebView) root; - JSONObject eventInitDict = new JSONObject(); - eventInitDict.put("data", args.getString(0)); - reactWebView.evaluateJavascriptWithFallback( - "(function () {" - + "var event;" - + "var data = " - + eventInitDict.toString() - + ";" - + "try {" - + "event = new MessageEvent('message', data);" - + "} catch (e) {" - + "event = document.createEvent('MessageEvent');" - + "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" - + "}" - + "document.dispatchEvent(event);" - + "})();"); - } catch (JSONException e) { - throw new RuntimeException(e); - } - break; - case COMMAND_INJECT_JAVASCRIPT: - ReactWebView reactWebView = (ReactWebView) root; - reactWebView.evaluateJavascriptWithFallback(args.getString(0)); - break; - } - } - - @Override - public void onDropViewInstance(WebView webView) { - super.onDropViewInstance(webView); - ((ThemedReactContext) webView.getContext()) - .removeLifecycleEventListener((ReactWebView) webView); - ((ReactWebView) webView).cleanupCallbacksAndDestroy(); - } - - protected WebView.PictureListener getPictureListener() { - if (mPictureListener == null) { - mPictureListener = - new WebView.PictureListener() { - @Override - public void onNewPicture(WebView webView, Picture picture) { - dispatchEvent( - webView, - new ContentSizeChangeEvent( - webView.getId(), webView.getWidth(), webView.getContentHeight())); - } - }; - } - return mPictureListener; - } - - protected static void dispatchEvent(WebView webView, Event event) { - ReactContext reactContext = (ReactContext) webView.getContext(); - EventDispatcher eventDispatcher = - reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); - eventDispatcher.dispatchEvent(event); - } - - public static interface CustomClientCertRequestHandler { - public void handle(WebView webview, ClientCertRequest request); - } - - public static void setCustomClientCertRequestHandler(CustomClientCertRequestHandler handler) { - ReactWebViewManager.customClientCertRequestHandler = handler; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/WebViewConfig.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/WebViewConfig.java deleted file mode 100644 index 198fad85fb8938..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/WebViewConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.webview; - -import android.webkit.WebView; - -/** - * Implement this interface in order to config your {@link WebView}. An instance of that - * implementation will have to be given as a constructor argument to {@link ReactWebViewManager}. - */ -public interface WebViewConfig { - - void configWebView(WebView webView); -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingErrorEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingErrorEvent.java deleted file mode 100644 index 86e1aa058935a1..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingErrorEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.webview.events; - -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted when there is an error in loading. - */ -public class TopLoadingErrorEvent extends Event { - - public static final String EVENT_NAME = "topLoadingError"; - private WritableMap mEventData; - - public TopLoadingErrorEvent(int viewId, WritableMap eventData) { - super(viewId); - mEventData = eventData; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public boolean canCoalesce() { - return false; - } - - @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingFinishEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingFinishEvent.java deleted file mode 100644 index dd12aa877b8096..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingFinishEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.webview.events; - -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted when loading is completed. - */ -public class TopLoadingFinishEvent extends Event { - - public static final String EVENT_NAME = "topLoadingFinish"; - private WritableMap mEventData; - - public TopLoadingFinishEvent(int viewId, WritableMap eventData) { - super(viewId); - mEventData = eventData; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public boolean canCoalesce() { - return false; - } - - @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingStartEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingStartEvent.java deleted file mode 100644 index 28aae57e5989fd..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopLoadingStartEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.webview.events; - -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted when loading has started - */ -public class TopLoadingStartEvent extends Event { - - public static final String EVENT_NAME = "topLoadingStart"; - private WritableMap mEventData; - - public TopLoadingStartEvent(int viewId, WritableMap eventData) { - super(viewId); - mEventData = eventData; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public boolean canCoalesce() { - return false; - } - - @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mEventData); - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopMessageEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopMessageEvent.java deleted file mode 100644 index c2f43673b4e01e..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/events/TopMessageEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.views.webview.events; - -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; - -/** - * Event emitted when there is an error in loading. - */ -public class TopMessageEvent extends Event { - - public static final String EVENT_NAME = "topMessage"; - private final String mData; - - public TopMessageEvent(int viewId, String data) { - super(viewId); - mData = data; - } - - @Override - public String getEventName() { - return EVENT_NAME; - } - - @Override - public boolean canCoalesce() { - return false; - } - - @Override - public short getCoalescingKey() { - // All events for a given view can be coalesced. - return 0; - } - - @Override - public void dispatch(RCTEventEmitter rctEventEmitter) { - WritableMap data = Arguments.createMap(); - data.putString("data", mData); - rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data); - } -}