2017-08-19 164 views
2

我想在使用react-native的內置WebView組件創建的Android WebView中禁用文件訪問。我怎樣才能setAllowFileAccess爲反應原生的android webview?

Android WebView docs說「文件訪問是默認啓用」,這是我的組織的安全問題。

react-native 0.31 docs提到了一個可用於訪問底層WebView節點的getWebViewHandle方法;如果這個工作,然後我可以(大概)寫:

import { WebView, Platform } from 'react-native'; 
//... 
var reactWebview = <Webview [props here] /> 
if (Platform.OS === 'android') { 
    var webview = reactWebview.getWebViewHandle(); 
    webview.setAllowFileAccess(false); 
} 

然而,後來react-native docs的版本不提getWebViewHandle,當我像這樣運行的代碼做出反應,原生0.44的Android設備上,我得到錯誤webview.getWebViewHandle is not a function

我的問題是:

  1. 默認情況下對通過反應本土創造了Android網頁視圖啓用文件訪問?

  2. 如果是這樣,我們該如何禁用此文件訪問?我們可以通過擴展WebView類來完成這個任務嗎?還是我們需要fork和修改react-native?

謝謝你的時間!

回答

2

問題1:從ReactWebViewManager.java的源代碼可見,RN不會調用WebView.setAllowFileAccess,因此文件訪問是由Android WebView而不是RN啓用的。

問題2:您可以創建自定義的WebView做你需要什麼,或從Native Module只是讓你的WebView的參考,那麼你就可以訪問Android的WebView中的所有的API,如setAllowFileAccessNative 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

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); 

整個代碼可以發現here

0

任何地方,所以我想我會分享我找不到此解決方案,我希望它可以幫助...

要啓用瀏覽按鈕工作,隨後讓文件的訪問,你可以替換上面的這個反應本地文件與這些修改後的版本:

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(); 
    } 
} 

第二個文件是上述

0

node_modules /反應天然/ ReactAndroid/SRC /主/ JAVA/COM/faceboo列出K /反應/視圖/ 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); 
    } 
} 
+0

這是大量的代碼來回答一些幾乎'是'/'否'的問題。我甚至不知道'如何'被埋在那裏,因爲有這麼多! – UKMonkey