[英]How can I setAllowFileAccess for a react-native android WebView?
I want to disable file access within an Android WebView I'm creating using react-native's built-in WebView component. 我想在我使用react-native的内置WebView组件创建的Android WebView中禁用文件访问。
The Android WebView docs say "File access is enabled by default.", and this is a security concern for my organization. Android WebView文档说“默认情况下启用文件访问。”,这是我组织的安全问题。
The react-native 0.31 docs mention a getWebViewHandle
method that can be used to access the underlying WebView node; 本机0.31文档提到了一个
getWebViewHandle
方法,该方法可用于访问基础WebView节点。 if this worked, then I could (presumably) write: 如果这行得通,那么我可以(大概)写:
import { WebView, Platform } from 'react-native';
//...
var reactWebview = <Webview [props here] />
if (Platform.OS === 'android') {
var webview = reactWebview.getWebViewHandle();
webview.setAllowFileAccess(false);
}
However, later versions of the react-native docs don't mention getWebViewHandle
, and when I run code like this in react-native 0.44 on an Android device, I get the error webview.getWebViewHandle is not a function
. 但是, react-native文档的更高版本没有提及
getWebViewHandle
,并且当我在Android设备上的react-native 0.44中运行这样的代码时,出现错误webview.getWebViewHandle is not a function
。
My questions are: 我的问题是:
Is file access enabled by default for the Android WebViews created by react-native? 默认情况下是否为react-native创建的Android WebView启用文件访问?
If so, how can we disable this file access? 如果是这样,我们如何禁用此文件访问? Could we accomplish this by extending the WebView class, or would we need to fork and modify react-native?
我们可以通过扩展WebView类来完成此操作,还是需要派生和修改react-native?
Thanks for your time! 谢谢你的时间!
Questions 1: As see from the source code of ReactWebViewManager.java , RN does not call WebView.setAllowFileAccess
, so file access is enabled by Android WebView, not by RN. 问题1:从ReactWebViewManager.java的源代码可以看出 ,RN不会调用
WebView.setAllowFileAccess
,因此文件访问是由Android WebView而不是RN启用的。
Questions 2: You can create a custom WebView to do what you need, or just get the reference of your WebView from a Native Module , then you can access all the apis of Android WebView like setAllowFileAccess
in that Native Module . 问题2:您可以创建自定义WebView来执行所需的操作,或者仅从本机模块获取WebView的引用,然后可以访问该本地模块中的Android WebView的所有
setAllowFileAccess
(例如setAllowFileAccess
。
Native Module 本机模块
public class WebViewSettingModule extends ReactContextBaseJavaModule {
public WebViewSettingModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "WebViewSetting";
}
@ReactMethod
public void setWebView() {
Activity activity = getCurrentActivity();
//the id for the ReactRootView is always be 1
@IdRes int id = 1;
View view = activity.findViewById(id);
if (view instanceof ReactRootView) {
ReactRootView reactRootView = (ReactRootView) view;
//make sure the WebView is directly child of ReactRootView
reactRootView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, final View child) {
if (child instanceof WebView) {
Log.e("onChildViewAdded: ", ((WebView) child).getUrl());
//get the reference to the WebView and setAllowFileAccess
((WebView) child).getSettings().setAllowFileAccess(false);
}
}
@Override
public void onChildViewRemoved(View parent, View child) {
}
});
}
}
}
index.android.js index.android.js
import React, {Component} from "react";
import {AppRegistry, View, WebView} from "react-native";
//the native module
import MyWebView from "./src/MyWebView";
export default class WebViewSetting extends Component {
componentDidMount() {
//notify native code to modify WebView setting
MyWebView.setWebView();
}
render() {
return (
<View style={{flex: 1}}>
<WebView
source={{uri: 'https://github.com/'}}
style={{marginTop: 20}}/>
</View>
);
}
}
AppRegistry.registerComponent('WebViewSetting', () => WebViewSetting);
I could not find this solution anywhere so I thought I would share, I hope it helps... 我在任何地方都找不到此解决方案,所以我想分享一下,希望对您有所帮助...
To enable the browse button to work and subsequently allow file access you can replace the above and this react-native files with these modified versions: 要启用浏览按钮并随后允许文件访问,您可以使用以下修改后的版本替换上述文件和该本机文件:
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java node_modules /反应天然/ ReactAndroid / SRC /主/ JAVA / COM / Facebook的/反应/ UIManager的/ ThemedReactContext.java
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.uimanager;
import javax.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.LifecycleEventListener;
//
/**
* Wraps {@link ReactContext} with the base {@link Context} passed into the constructor.
* It provides also a way to start activities using the viewContext to which RN native views belong.
* It delegates lifecycle listener registration to the original instance of {@link ReactContext}
* which is supposed to receive the lifecycle events. At the same time we disallow receiving
* lifecycle events for this wrapper instances.
* TODO: T7538544 Rename ThemedReactContext to be in alignment with name of ReactApplicationContext
*/
public class ThemedReactContext extends ReactContext {
private final ReactApplicationContext mReactApplicationContext;
public ThemedReactContext(ReactApplicationContext reactApplicationContext, Context base) {
super(base);
initializeWithInstance(reactApplicationContext.getCatalystInstance());
mReactApplicationContext = reactApplicationContext;
}
@Override
public void addLifecycleEventListener(LifecycleEventListener listener) {
mReactApplicationContext.addLifecycleEventListener(listener);
}
@Override
public void removeLifecycleEventListener(LifecycleEventListener listener) {
mReactApplicationContext.removeLifecycleEventListener(listener);
}
@Override
public void addActivityEventListener(ActivityEventListener listener) {
mReactApplicationContext.addActivityEventListener(listener);
}
@Override
public void removeActivityEventListener(ActivityEventListener listener) {
mReactApplicationContext.removeActivityEventListener(listener);
}
@Override
public boolean hasCurrentActivity() {
return mReactApplicationContext.hasCurrentActivity();
}
@Override
public
@Nullable
Activity getCurrentActivity() {
return mReactApplicationContext.getCurrentActivity();
}
}
The second file is listed above 上面列出了第二个文件
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/ReactWebViewManager.java node_modules /反应天然/ ReactAndroid / SRC /主/ JAVA / COM / Facebook的/反应/视图/ ReactWebViewManager.java
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.webview;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.webkit.ConsoleMessage;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.common.ReactConstants;
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.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 org.json.JSONObject;
import org.json.JSONException;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
public class ReactWebViewManager extends SimpleViewManager<WebView> {
protected static final String REACT_CLASS = "RCTWebView";
private static final String HTML_ENCODING = "UTF-8";
private static final String HTML_MIME_TYPE = "text/html; charset=utf-8";
private static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE";
private 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;
private static final String BLANK_URL = "about:blank";
public static final int INPUT_FILE_REQUEST_GALLERY_IMAGE = 1001;
public static final int REQUEST_SELECT_FILE_LEGACY = 1012;
private WebViewConfig mWebViewConfig;
private
@Nullable
WebView.PictureListener mPictureListener;
private ValueCallback<Uri[]> mFilePathCallbackArr;
private ValueCallback<Uri> mFilePathCallback; // Legacy (Android 4.1+)
private String mCameraPhotoPath;
protected static class ReactWebViewClient extends WebViewClient {
private boolean mLastLoadFailed = false;
@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.startsWith("http://") || url.startsWith("https://") ||
url.startsWith("file://")) {
return false;
} else {
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(intent);
} catch (ActivityNotFoundException e) {
FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e);
}
return true;
}
}
@Override
public void onReceivedError(
WebView webView,
int errorCode,
String description,
String failingUrl) {
super.onReceivedError(webView, errorCode, description, failingUrl);
mLastLoadFailed = true;
emitFinishEvent(webView, failingUrl);
WritableMap eventData = createWebViewEvent(webView, failingUrl);
eventData.putDouble("code", errorCode);
eventData.putString("description", description);
dispatchEvent(
webView,
new TopLoadingErrorEvent(webView.getId(), eventData));
}
@Override
public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {
super.doUpdateVisitedHistory(webView, url, isReload);
dispatchEvent(
webView,
new TopLoadingStartEvent(
webView.getId(),
createWebViewEvent(webView, url)));
}
private void emitFinishEvent(WebView webView, String url) {
dispatchEvent(
webView,
new TopLoadingFinishEvent(
webView.getId(),
createWebViewEvent(webView, url)));
}
private WritableMap createWebViewEvent(WebView webView, String url) {
WritableMap event = Arguments.createMap();
event.putDouble("target", webView.getId());
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;
}
}
protected static class ReactWebView extends WebView implements LifecycleEventListener {
private
@Nullable
String injectedJS;
private boolean messagingEnabled = false;
private class ReactWebViewBridge {
ReactWebView mContext;
ReactWebViewBridge(ReactWebView c) {
mContext = c;
}
@JavascriptInterface
public void postMessage(String message) {
mContext.onMessage(message);
}
}
public ReactWebView(ThemedReactContext reactContext) {
super(reactContext);
}
@Override
public void onHostResume() {
// do nothing
}
@Override
public void onHostPause() {
// do nothing
}
@Override
public void onHostDestroy() {
cleanupCallbacksAndDestroy();
}
public void setInjectedJavaScript(@Nullable String js) {
injectedJS = js;
}
public void setMessagingEnabled(boolean enabled) {
if (messagingEnabled == enabled) {
return;
}
messagingEnabled = enabled;
if (enabled) {
addJavascriptInterface(new ReactWebViewBridge(this), BRIDGE_NAME);
linkBridge();
} else {
removeJavascriptInterface(BRIDGE_NAME);
}
}
public void callInjectedJavaScript() {
if (getSettings().getJavaScriptEnabled() &&
injectedJS != null &&
!TextUtils.isEmpty(injectedJS)) {
loadUrl("javascript:(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<String>() {
@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");
}
}
});
}
loadUrl("javascript:(" +
"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));
}
private 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;
}
@Override
protected WebView createViewInstance(final ThemedReactContext reactContext) {
ReactWebView webView = new ReactWebView(reactContext);
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onConsoleMessage(ConsoleMessage message) {
if (ReactBuildConfig.DEBUG) {
return super.onConsoleMessage(message);
}
return true;
}
@Override
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File imageFile = new File(
storageDir,
imageFileName + ".jpg"
);
return imageFile;
}
private Intent getVideoCaptureIntent() {
Intent recordVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
recordVideoIntent
.resolveActivity(
reactContext
.getCurrentActivity()
.getPackageManager()
);
recordVideoIntent.putExtra("type", "foobar");
return recordVideoIntent;
}
public boolean onShowFileChooser(
WebView webView,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams
) {
if (mFilePathCallbackArr != null) {
mFilePathCallbackArr.onReceiveValue(null);
}
mFilePathCallbackArr = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
ComponentName comp = takePictureIntent
.resolveActivity(
reactContext
.getCurrentActivity()
.getPackageManager()
);
if (comp != null) {
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
FLog.e(ReactConstants.TAG, "Unable to create Image File", ex);
}
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent videoCaptureIntent = getVideoCaptureIntent();
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
intentArray = new Intent[]{
intentArray[0],
videoCaptureIntent
};
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
reactContext.getCurrentActivity().startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_GALLERY_IMAGE);
return true;
}
});
reactContext.addLifecycleEventListener(webView);
reactContext.addActivityEventListener(new ActivityEventListener() {
// Android 5+
@Override
public void onActivityResult (Activity activity, int requestCode, int resultCode, Intent data) {
if(requestCode != INPUT_FILE_REQUEST_GALLERY_IMAGE || mFilePathCallbackArr == null) {
return;
}
Uri[] results = null;
if(resultCode == Activity.RESULT_OK) {
if(data == null || data.getData() == null) {
if(mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = data.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
if(results == null) {
mFilePathCallbackArr.onReceiveValue(new Uri[]{});
}
else {
mFilePathCallbackArr.onReceiveValue(results);
}
mFilePathCallbackArr = null;
return;
}
@Override
public void onNewIntent(Intent intent) {}
});
mWebViewConfig.configWebView(webView);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setDisplayZoomControls(false);
webView.getSettings().setDomStorageEnabled(true);
webView.setLayoutParams(
new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
return webView;
}
@ReactProp(name = "javaScriptEnabled")
public void setJavaScriptEnabled(WebView view, boolean enabled) {
view.getSettings().setJavaScriptEnabled(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")
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 = "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.equals(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<String, String> 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);
}
}
}
@Override
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
view.setWebViewClient(new ReactWebViewClient());
}
@Override
public
@Nullable
Map<String, Integer> 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 {
JSONObject eventInitDict = new JSONObject();
eventInitDict.put("data", args.getString(0));
root.loadUrl("javascript:(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:
root.loadUrl("javascript:" + args.getString(0));
break;
}
}
@Override
public void onDropViewInstance(WebView webView) {
super.onDropViewInstance(webView);
((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((ReactWebView) webView);
((ReactWebView) webView).cleanupCallbacksAndDestroy();
}
private 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;
}
private static void dispatchEvent(WebView webView, Event event) {
ReactContext reactContext = (ReactContext) webView.getContext();
EventDispatcher eventDispatcher =
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
eventDispatcher.dispatchEvent(event);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.