Ver código fonte

feat(Android Webview): Support onShouldStartLoadWithRequest on Android (#107)

This PR adds support for `onShouldStartLoadWithRequest` on android.

The initial PR was #59

The issue for this PR is: #106

fixes #106
Thibault Malbranche 6 anos atrás
pai
commit
b1b662628e

+ 1
- 0
android/build.gradle Ver arquivo

@@ -48,6 +48,7 @@ android {
48 48
 
49 49
 repositories {
50 50
     mavenCentral()
51
+    jcenter()
51 52
     maven {
52 53
         url 'https://maven.google.com/'
53 54
         name 'Google'

+ 33
- 63
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java Ver arquivo

@@ -30,6 +30,7 @@ import android.webkit.GeolocationPermissions;
30 30
 import android.webkit.JavascriptInterface;
31 31
 import android.webkit.ValueCallback;
32 32
 import android.webkit.WebChromeClient;
33
+import android.webkit.WebResourceRequest;
33 34
 import android.webkit.WebSettings;
34 35
 import android.webkit.WebView;
35 36
 import android.webkit.WebViewClient;
@@ -51,11 +52,19 @@ import com.facebook.react.uimanager.annotations.ReactProp;
51 52
 import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
52 53
 import com.facebook.react.uimanager.events.Event;
53 54
 import com.facebook.react.uimanager.events.EventDispatcher;
55
+import com.facebook.react.uimanager.events.RCTEventEmitter;
54 56
 import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
55 57
 import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
56 58
 import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
57 59
 import com.reactnativecommunity.webview.events.TopMessageEvent;
58 60
 import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
61
+import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
62
+import java.io.UnsupportedEncodingException;
63
+import java.util.ArrayList;
64
+import java.util.HashMap;
65
+import java.util.Locale;
66
+import java.util.Map;
67
+import javax.annotation.Nullable;
59 68
 import org.json.JSONException;
60 69
 import org.json.JSONObject;
61 70
 
@@ -66,12 +75,14 @@ import org.json.JSONObject;
66 75
  *  - GO_BACK
67 76
  *  - GO_FORWARD
68 77
  *  - RELOAD
78
+ *  - LOAD_URL
69 79
  *
70 80
  * {@link WebView} instances could emit following direct events:
71 81
  *  - topLoadingFinish
72 82
  *  - topLoadingStart
73 83
  *  - topLoadingStart
74 84
  *  - topLoadingProgress
85
+ *  - topShouldStartLoadWithRequest
75 86
  *
76 87
  * Each event will carry the following properties:
77 88
  *  - target - view's react tag
@@ -99,6 +110,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
99 110
   public static final int COMMAND_STOP_LOADING = 4;
100 111
   public static final int COMMAND_POST_MESSAGE = 5;
101 112
   public static final int COMMAND_INJECT_JAVASCRIPT = 6;
113
+  public static final int COMMAND_LOAD_URL = 7;
102 114
 
103 115
   // Use `webView.loadUrl("about:blank")` to reliably reset the view
104 116
   // state and release page resources (including any running JavaScript).
@@ -111,7 +123,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
111 123
 
112 124
     protected boolean mLastLoadFailed = false;
113 125
     protected @Nullable ReadableArray mUrlPrefixesForDefaultIntent;
114
-    protected @Nullable List<Pattern> mOriginWhitelist;
115 126
 
116 127
     @Override
117 128
     public void onPageFinished(WebView webView, String url) {
@@ -139,50 +150,16 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
139 150
 
140 151
     @Override
141 152
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
142
-      if (url.equals(BLANK_URL)) return false;
143
-
144
-      // url blacklisting
145
-      if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) {
146
-        ArrayList<Object> urlPrefixesForDefaultIntent =
147
-            mUrlPrefixesForDefaultIntent.toArrayList();
148
-        for (Object urlPrefix : urlPrefixesForDefaultIntent) {
149
-          if (url.startsWith((String) urlPrefix)) {
150
-            launchIntent(view.getContext(), url);
151
-            return true;
152
-          }
153
-        }
154
-      }
155
-
156
-      if (mOriginWhitelist != null && shouldHandleURL(mOriginWhitelist, url)) {
157
-        return false;
158
-      }
159
-
160
-      launchIntent(view.getContext(), url);
153
+      dispatchEvent(view, new TopShouldStartLoadWithRequestEvent(view.getId(), url));
161 154
       return true;
162 155
     }
163 156
 
164
-    private void launchIntent(Context context, String url) {
165
-      try {
166
-        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
167
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
168
-        intent.addCategory(Intent.CATEGORY_BROWSABLE);
169
-        context.startActivity(intent);
170
-      } catch (ActivityNotFoundException e) {
171
-        FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e);
172
-      }
173
-    }
174 157
 
175
-    private boolean shouldHandleURL(List<Pattern> originWhitelist, String url) {
176
-      Uri uri = Uri.parse(url);
177
-      String scheme = uri.getScheme() != null ? uri.getScheme() : "";
178
-      String authority = uri.getAuthority() != null ? uri.getAuthority() : "";
179
-      String urlToCheck = scheme + "://" + authority;
180
-      for (Pattern pattern : originWhitelist) {
181
-        if (pattern.matcher(urlToCheck).matches()) {
182
-          return true;
183
-        }
184
-      }
185
-      return false;
158
+    @TargetApi(Build.VERSION_CODES.N)
159
+    @Override
160
+    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
161
+      dispatchEvent(view, new TopShouldStartLoadWithRequestEvent(view.getId(), request.getUrl().toString()));
162
+      return true;
186 163
     }
187 164
 
188 165
     @Override
@@ -231,10 +208,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
231 208
     public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
232 209
       mUrlPrefixesForDefaultIntent = specialUrls;
233 210
     }
234
-
235
-    public void setOriginWhitelist(List<Pattern> originWhitelist) {
236
-      mOriginWhitelist = originWhitelist;
237
-    }
238 211
   }
239 212
 
240 213
   /**
@@ -656,20 +629,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
656 629
     view.getSettings().setGeolocationEnabled(isGeolocationEnabled != null && isGeolocationEnabled);
657 630
   }
658 631
 
659
-  @ReactProp(name = "originWhitelist")
660
-  public void setOriginWhitelist(
661
-    WebView view,
662
-    @Nullable ReadableArray originWhitelist) {
663
-    RNCWebViewClient client = ((RNCWebView) view).getRNCWebViewClient();
664
-    if (client != null && originWhitelist != null) {
665
-      List<Pattern> whiteList = new LinkedList<>();
666
-      for (int i = 0 ; i < originWhitelist.size() ; i++) {
667
-        whiteList.add(Pattern.compile(originWhitelist.getString(i)));
668
-      }
669
-      client.setOriginWhitelist(whiteList);
670
-    }
671
-  }
672
-
673 632
   @Override
674 633
   protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
675 634
     // Do not register default touch emitter and let WebView implementation handle touches
@@ -678,9 +637,13 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
678 637
 
679 638
   @Override
680 639
   public Map getExportedCustomDirectEventTypeConstants() {
681
-    MapBuilder.Builder builder = MapBuilder.builder();
682
-    builder.put("topLoadingProgress", MapBuilder.of("registrationName", "onLoadingProgress"));
683
-    return builder.build();
640
+    Map export = super.getExportedCustomDirectEventTypeConstants();
641
+    if (export == null) {
642
+      export = MapBuilder.newHashMap();
643
+    }
644
+    export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
645
+    export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
646
+    return export;
684 647
   }
685 648
 
686 649
   @Override
@@ -691,7 +654,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
691 654
         "reload", COMMAND_RELOAD,
692 655
         "stopLoading", COMMAND_STOP_LOADING,
693 656
         "postMessage", COMMAND_POST_MESSAGE,
694
-        "injectJavaScript", COMMAND_INJECT_JAVASCRIPT
657
+        "injectJavaScript", COMMAND_INJECT_JAVASCRIPT,
658
+        "loadUrl", COMMAND_LOAD_URL
695 659
       );
696 660
   }
697 661
 
@@ -734,6 +698,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
734 698
         RNCWebView reactWebView = (RNCWebView) root;
735 699
         reactWebView.evaluateJavascriptWithFallback(args.getString(0));
736 700
         break;
701
+      case COMMAND_LOAD_URL:
702
+        if (args == null) {
703
+          throw new RuntimeException("Arguments for loading an url are null!");
704
+        }
705
+        root.loadUrl(args.getString(0));
706
+        break;
737 707
     }
738 708
   }
739 709
 

+ 40
- 0
android/src/main/java/com/reactnativecommunity/webview/events/TopShouldStartLoadWithRequestEvent.java Ver arquivo

@@ -0,0 +1,40 @@
1
+package com.reactnativecommunity.webview.events;
2
+
3
+import com.facebook.react.bridge.Arguments;
4
+import com.facebook.react.bridge.WritableMap;
5
+import com.facebook.react.uimanager.events.Event;
6
+import com.facebook.react.uimanager.events.RCTEventEmitter;
7
+
8
+public class TopShouldStartLoadWithRequestEvent extends Event<TopMessageEvent> {
9
+    public static final String EVENT_NAME = "topShouldStartLoadWithRequest";
10
+    private final String mUrl;
11
+
12
+    public TopShouldStartLoadWithRequestEvent(int viewId, String url) {
13
+        super(viewId);
14
+        mUrl = url;
15
+    }
16
+
17
+    @Override
18
+    public String getEventName() {
19
+        return EVENT_NAME;
20
+    }
21
+
22
+    @Override
23
+    public boolean canCoalesce() {
24
+        return false;
25
+    }
26
+
27
+    @Override
28
+    public short getCoalescingKey() {
29
+        // All events for a given view can be coalesced.
30
+        return 0;
31
+    }
32
+
33
+    @Override
34
+    public void dispatch(RCTEventEmitter rctEventEmitter) {
35
+        WritableMap data = Arguments.createMap();
36
+        data.putString("url", mUrl);
37
+        data.putString("navigationType", "other");
38
+        rctEventEmitter.receiveEvent(getViewTag(), EVENT_NAME, data);
39
+    }
40
+}

+ 35
- 20
js/WebView.android.js Ver arquivo

@@ -8,35 +8,33 @@
8 8
  * @flow
9 9
  */
10 10
 
11
-'use strict';
12
-
13 11
 import React from 'react';
14 12
 
15
-import ReactNative from 'react-native';
16
-import {
13
+import ReactNative, {
17 14
   ActivityIndicator,
15
+  Image,
16
+  requireNativeComponent,
18 17
   StyleSheet,
19 18
   UIManager,
20 19
   View,
21
-  Image,
22
-  requireNativeComponent,
23 20
   NativeModules
24 21
 } from 'react-native';
25 22
 
26 23
 import invariant from 'fbjs/lib/invariant';
27 24
 import keyMirror from 'fbjs/lib/keyMirror';
28 25
 
29
-import WebViewShared from './WebViewShared';
26
+import {
27
+  defaultOriginWhitelist,
28
+  createOnShouldStartLoadWithRequest,
29
+} from './WebViewShared';
30 30
 import type {
31
-  WebViewEvent,
32 31
   WebViewError,
33 32
   WebViewErrorEvent,
34 33
   WebViewMessageEvent,
35
-  WebViewNavigation,
36 34
   WebViewNavigationEvent,
35
+  WebViewProgressEvent,
37 36
   WebViewSharedProps,
38 37
   WebViewSource,
39
-  WebViewProgressEvent,
40 38
 } from './WebViewTypes';
41 39
 
42 40
 const resolveAssetSource = Image.resolveAssetSource;
@@ -69,7 +67,7 @@ class WebView extends React.Component<WebViewSharedProps, State> {
69 67
     scalesPageToFit: true,
70 68
     allowFileAccess: false,
71 69
     saveFormDataDisabled: false,
72
-    originWhitelist: WebViewShared.defaultOriginWhitelist,
70
+    originWhitelist: defaultOriginWhitelist,
73 71
   };
74 72
 
75 73
   static isFileUploadSupported = async () => {
@@ -78,7 +76,9 @@ class WebView extends React.Component<WebViewSharedProps, State> {
78 76
   }
79 77
 
80 78
   state = {
81
-    viewState: this.props.startInLoadingState ? WebViewState.LOADING : WebViewState.IDLE,
79
+    viewState: this.props.startInLoadingState
80
+      ? WebViewState.LOADING
81
+      : WebViewState.IDLE,
82 82
     lastErrorEvent: null,
83 83
   };
84 84
 
@@ -131,12 +131,14 @@ class WebView extends React.Component<WebViewSharedProps, State> {
131 131
 
132 132
     const nativeConfig = this.props.nativeConfig || {};
133 133
 
134
-    const originWhitelist = (this.props.originWhitelist || []).map(
135
-      WebViewShared.originWhitelistToRegex,
136
-    );
137
-
138 134
     let NativeWebView = nativeConfig.component || RNCWebView;
139 135
 
136
+    const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
137
+      this.onShouldStartLoadWithRequestCallback,
138
+      this.props.originWhitelist,
139
+      this.props.onShouldStartLoadWithRequest,
140
+    );
141
+
140 142
     const webView = (
141 143
       <NativeWebView
142 144
         ref={this.webViewRef}
@@ -157,6 +159,7 @@ class WebView extends React.Component<WebViewSharedProps, State> {
157 159
         automaticallyAdjustContentInsets={
158 160
           this.props.automaticallyAdjustContentInsets
159 161
         }
162
+        onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
160 163
         onContentSizeChange={this.props.onContentSizeChange}
161 164
         onLoadingStart={this.onLoadingStart}
162 165
         onLoadingFinish={this.onLoadingFinish}
@@ -170,7 +173,6 @@ class WebView extends React.Component<WebViewSharedProps, State> {
170 173
         allowUniversalAccessFromFileURLs={
171 174
           this.props.allowUniversalAccessFromFileURLs
172 175
         }
173
-        originWhitelist={originWhitelist}
174 176
         mixedContentMode={this.props.mixedContentMode}
175 177
         saveFormDataDisabled={this.props.saveFormDataDisabled}
176 178
         urlPrefixesForDefaultIntent={this.props.urlPrefixesForDefaultIntent}
@@ -290,11 +292,24 @@ class WebView extends React.Component<WebViewSharedProps, State> {
290 292
     const { onMessage } = this.props;
291 293
     onMessage && onMessage(event);
292 294
   };
293
-  
295
+
294 296
   onLoadingProgress = (event: WebViewProgressEvent) => {
295
-    const { onLoadProgress} = this.props;
297
+    const { onLoadProgress } = this.props;
296 298
     onLoadProgress && onLoadProgress(event);
297
-  }
299
+  };
300
+
301
+  onShouldStartLoadWithRequestCallback = (
302
+    shouldStart: boolean,
303
+    url: string,
304
+  ) => {
305
+    if (shouldStart) {
306
+      UIManager.dispatchViewManagerCommand(
307
+        this.getWebViewHandle(),
308
+        UIManager.RNCWebView.Commands.loadUrl,
309
+        [String(url)],
310
+      );
311
+    }
312
+  };
298 313
 }
299 314
 
300 315
 const RNCWebView = requireNativeComponent('RNCWebView');

+ 28
- 38
js/WebView.ios.js Ver arquivo

@@ -25,7 +25,10 @@ import {
25 25
 import invariant from 'fbjs/lib/invariant';
26 26
 import keyMirror from 'fbjs/lib/keyMirror';
27 27
 
28
-import WebViewShared from './WebViewShared';
28
+import {
29
+  defaultOriginWhitelist,
30
+  createOnShouldStartLoadWithRequest,
31
+} from './WebViewShared';
29 32
 import type {
30 33
   WebViewEvent,
31 34
   WebViewError,
@@ -130,7 +133,7 @@ class WebView extends React.Component<WebViewSharedProps, State> {
130 133
 
131 134
   static defaultProps = {
132 135
     useWebKit: true,
133
-    originWhitelist: WebViewShared.defaultOriginWhitelist,
136
+    originWhitelist: defaultOriginWhitelist,
134 137
   };
135 138
 
136 139
   static isFileUploadSupported = async () => {
@@ -204,40 +207,11 @@ class WebView extends React.Component<WebViewSharedProps, State> {
204 207
 
205 208
     const nativeConfig = this.props.nativeConfig || {};
206 209
 
207
-    let viewManager = nativeConfig.viewManager;
208
-
209
-    if (this.props.useWebKit) {
210
-      viewManager = viewManager || RNCWKWebViewManager;
211
-    } else {
212
-      viewManager = viewManager || RNCUIWebViewManager;
213
-    }
214
-
215
-    const compiledWhitelist = [
216
-      'about:blank',
217
-      ...(this.props.originWhitelist || []),
218
-    ].map(WebViewShared.originWhitelistToRegex);
219
-    const onShouldStartLoadWithRequest = event => {
220
-      let shouldStart = true;
221
-      const { url } = event.nativeEvent;
222
-      const origin = WebViewShared.extractOrigin(url);
223
-      const passesWhitelist = compiledWhitelist.some(x =>
224
-        new RegExp(x).test(origin),
225
-      );
226
-      shouldStart = shouldStart && passesWhitelist;
227
-      if (!passesWhitelist) {
228
-        Linking.openURL(url);
229
-      }
230
-      if (this.props.onShouldStartLoadWithRequest) {
231
-        shouldStart =
232
-          shouldStart &&
233
-          this.props.onShouldStartLoadWithRequest(event.nativeEvent);
234
-      }
235
-      invariant(viewManager != null, 'viewManager expected to be non-null');
236
-      viewManager.startLoadWithResult(
237
-        !!shouldStart,
238
-        event.nativeEvent.lockIdentifier,
239
-      );
240
-    };
210
+    const onShouldStartLoadWithRequest = createOnShouldStartLoadWithRequest(
211
+      this.onShouldStartLoadWithRequestCallback,
212
+      this.props.originWhitelist,
213
+      this.props.onShouldStartLoadWithRequest,
214
+    );
241 215
 
242 216
     const decelerationRate = processDecelerationRate(
243 217
       this.props.decelerationRate,
@@ -441,9 +415,25 @@ class WebView extends React.Component<WebViewSharedProps, State> {
441 415
   };
442 416
 
443 417
   _onLoadingProgress = (event: WebViewProgressEvent) => {
444
-    const {onLoadProgress} = this.props;
418
+    const { onLoadProgress } = this.props;
445 419
     onLoadProgress && onLoadProgress(event);
446
-  }
420
+  };
421
+
422
+  onShouldStartLoadWithRequestCallback = (
423
+    shouldStart: boolean,
424
+    url: string,
425
+    lockIdentifier: number,
426
+  ) => {
427
+    let viewManager = (this.props.nativeConfig || {}).viewManager;
428
+
429
+    if (this.props.useWebKit) {
430
+      viewManager = viewManager || RNCWKWebViewManager;
431
+    } else {
432
+      viewManager = viewManager || RNCUIWebViewManager;
433
+    }
434
+    invariant(viewManager != null, 'viewManager expected to be non-null');
435
+    viewManager.startLoadWithResult(!!shouldStart, lockIdentifier);
436
+  };
447 437
 
448 438
   componentDidUpdate(prevProps: WebViewSharedProps) {
449 439
     if (!(prevProps.useWebKit && this.props.useWebKit)) {

+ 53
- 14
js/WebViewShared.js Ver arquivo

@@ -8,19 +8,58 @@
8 8
  * @flow
9 9
  */
10 10
 
11
-'use strict';
12
-
13
-const escapeStringRegexp = require('escape-string-regexp');
14
-
15
-const WebViewShared = {
16
-  defaultOriginWhitelist: ['http://*', 'https://*'],
17
-  extractOrigin: (url: string): string => {
18
-    const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
19
-    return result === null ? '' : result[0];
20
-  },
21
-  originWhitelistToRegex: (originWhitelist: string): string => {
22
-    return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
23
-  },
11
+import escapeStringRegexp from 'escape-string-regexp';
12
+import { Linking } from 'react-native';
13
+import type {
14
+  WebViewNavigationEvent,
15
+  WebViewNavigation,
16
+  OnShouldStartLoadWithRequest,
17
+} from './WebViewTypes';
18
+
19
+const defaultOriginWhitelist = ['http://*', 'https://*'];
20
+
21
+const extractOrigin = (url: string): string => {
22
+  const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
23
+  return result === null ? '' : result[0];
24
+};
25
+
26
+const originWhitelistToRegex = (originWhitelist: string): string =>
27
+  escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
28
+
29
+const passesWhitelist = (compiledWhitelist: Array<string>, url: string) => {
30
+  const origin = extractOrigin(url);
31
+  return compiledWhitelist.some(x => new RegExp(x).test(origin));
32
+};
33
+
34
+const compileWhitelist = (
35
+  originWhitelist: ?$ReadOnlyArray<string>,
36
+): Array<string> =>
37
+  ['about:blank', ...(originWhitelist || [])].map(originWhitelistToRegex);
38
+
39
+const createOnShouldStartLoadWithRequest = (
40
+  loadRequest: (
41
+    shouldStart: boolean,
42
+    url: string,
43
+    lockIdentifier: number,
44
+  ) => void,
45
+  originWhitelist: ?$ReadOnlyArray<string>,
46
+  onShouldStartLoadWithRequest: ?OnShouldStartLoadWithRequest,
47
+) => {
48
+  return ({ nativeEvent }: WebViewNavigationEvent) => {
49
+    let shouldStart = true;
50
+    const { url, lockIdentifier } = nativeEvent;
51
+
52
+    if (!passesWhitelist(compileWhitelist(originWhitelist), url)) {
53
+      Linking.openURL(url);
54
+      shouldStart = false
55
+    }
56
+
57
+    if (onShouldStartLoadWithRequest) {
58
+      shouldStart = onShouldStartLoadWithRequest(nativeEvent);
59
+    }
60
+
61
+    loadRequest(shouldStart, url, lockIdentifier);
62
+  };
24 63
 };
25 64
 
26
-module.exports = WebViewShared;
65
+export { defaultOriginWhitelist, createOnShouldStartLoadWithRequest };

+ 31
- 25
js/WebViewTypes.js Ver arquivo

@@ -10,12 +10,12 @@
10 10
 
11 11
 'use strict';
12 12
 
13
-import type {Node, Element, ComponentType} from 'react';
13
+import type { Node, Element, ComponentType } from 'react';
14 14
 
15
-import type {SyntheticEvent} from 'CoreEventTypes';
16
-import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
17
-import type {ViewStyleProp} from 'StyleSheet';
18
-import type {ViewProps} from 'ViewPropTypes';
15
+import type { SyntheticEvent } from 'CoreEventTypes';
16
+import type { EdgeInsetsProp } from 'EdgeInsetsPropType';
17
+import type { ViewStyleProp } from 'StyleSheet';
18
+import type { ViewProps } from 'ViewPropTypes';
19 19
 
20 20
 export type WebViewNativeEvent = $ReadOnly<{|
21 21
   url: string,
@@ -23,12 +23,13 @@ export type WebViewNativeEvent = $ReadOnly<{|
23 23
   title: string,
24 24
   canGoBack: boolean,
25 25
   canGoForward: boolean,
26
+  lockIdentifier: number,
26 27
 |}>;
27 28
 
28 29
 export type WebViewProgressEvent = $ReadOnly<{|
29
-    ...WebViewNativeEvent,
30
-    progress: number,
31
-|}>
30
+  ...WebViewNativeEvent,
31
+  progress: number,
32
+|}>;
32 33
 
33 34
 export type WebViewNavigation = $ReadOnly<{|
34 35
   ...WebViewNativeEvent,
@@ -118,22 +119,26 @@ export type WebViewSourceHtml = $ReadOnly<{|
118 119
 export type WebViewSource = WebViewSourceUri | WebViewSourceHtml;
119 120
 
120 121
 export type WebViewNativeConfig = $ReadOnly<{|
121
-  /*
122
+  /**
122 123
    * The native component used to render the WebView.
123 124
    */
124 125
   component?: ComponentType<WebViewSharedProps>,
125
-  /*
126
+  /**
126 127
    * Set props directly on the native component WebView. Enables custom props which the
127 128
    * original WebView doesn't pass through.
128 129
    */
129 130
   props?: ?Object,
130
-  /*
131
+  /**
131 132
    * Set the ViewManager to use for communication with the native side.
132 133
    * @platform ios
133 134
    */
134 135
   viewManager?: ?Object,
135 136
 |}>;
136 137
 
138
+export type OnShouldStartLoadWithRequest = (
139
+  event: WebViewNavigation,
140
+) => boolean;
141
+
137 142
 export type IOSWebViewProps = $ReadOnly<{|
138 143
   /**
139 144
    * If true, use WKWebView instead of UIWebView.
@@ -205,17 +210,7 @@ export type IOSWebViewProps = $ReadOnly<{|
205 210
    *
206 211
    * @platform ios
207 212
    */
208
-  dataDetectorTypes?:
209
-    | ?DataDetectorTypes
210
-    | $ReadOnlyArray<DataDetectorTypes>,
211
-
212
-  /**
213
-   * Function that allows custom handling of any web view requests. Return
214
-   * `true` from the function to continue loading the request and `false`
215
-   * to stop loading.
216
-   * @platform ios
217
-   */
218
-  onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed,
213
+  dataDetectorTypes?: ?DataDetectorTypes | $ReadOnlyArray<DataDetectorTypes>,
219 214
 
220 215
   /**
221 216
    * Boolean that determines whether HTML5 videos play inline or use the
@@ -295,7 +290,7 @@ export type AndroidWebViewProps = $ReadOnly<{|
295 290
    */
296 291
   saveFormDataDisabled?: ?boolean,
297 292
 
298
-  /*
293
+  /**
299 294
    * Used on Android only, controls whether the given list of URL prefixes should
300 295
    * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
301 296
    * default activity intent for those URL instead of loading it within the webview.
@@ -345,7 +340,7 @@ export type AndroidWebViewProps = $ReadOnly<{|
345 340
   mixedContentMode?: ?('never' | 'always' | 'compatibility'),
346 341
 |}>;
347 342
 
348
-export type WebViewSharedProps =  $ReadOnly<{|
343
+export type WebViewSharedProps = $ReadOnly<{|
349 344
   ...ViewProps,
350 345
   ...IOSWebViewProps,
351 346
   ...AndroidWebViewProps,
@@ -366,7 +361,11 @@ export type WebViewSharedProps =  $ReadOnly<{|
366 361
   /**
367 362
    * Function that returns a view to show if there's an error.
368 363
    */
369
-  renderError: (errorDomain: ?string, errorCode: number, errorDesc: string) => Element<any>, // view to show if there's an error
364
+  renderError: (
365
+    errorDomain: ?string,
366
+    errorCode: number,
367
+    errorDesc: string,
368
+  ) => Element<any>, // view to show if there's an error
370 369
 
371 370
   /**
372 371
    * Function that returns a loading indicator.
@@ -457,6 +456,13 @@ export type WebViewSharedProps =  $ReadOnly<{|
457 456
    */
458 457
   originWhitelist?: $ReadOnlyArray<string>,
459 458
 
459
+  /**
460
+   * Function that allows custom handling of any web view requests. Return
461
+   * `true` from the function to continue loading the request and `false`
462
+   * to stop loading. The `navigationType` is always `other` on android.
463
+   */
464
+  onShouldStartLoadWithRequest?: OnShouldStartLoadWithRequest,
465
+
460 466
   /**
461 467
    * Override the native component used to render the WebView. Enables a custom native
462 468
    * WebView which uses the same JavaScript as the original WebView.