Browse Source

Add WebView Flow types

empyrical 5 years ago
parent
commit
133f1e36bc
No account linked to committer's email address
8 changed files with 627 additions and 57 deletions
  1. 88
    0
      .flowconfig
  2. 88
    0
      .flowconfig.android
  3. 9
    3
      js/WKWebView.ios.js
  4. 66
    15
      js/WebView.android.js
  5. 32
    35
      js/WebView.ios.js
  6. 3
    3
      js/WebViewShared.js
  7. 332
    0
      js/WebViewTypes.js
  8. 9
    1
      package.json

+ 88
- 0
.flowconfig View File

@@ -0,0 +1,88 @@
1
+[ignore]
2
+; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
3
+.*/*[.]android.js
4
+;.*/*[.]ios.js
5
+
6
+; Ignore templates for 'react-native init'
7
+.*/local-cli/templates/.*
8
+
9
+; Ignore the Dangerfile
10
+node_modules/react-native/bots/dangerfile.js
11
+
12
+; Ignore "BUCK" generated dirs
13
+node_modules/react-native/\.buckd/
14
+
15
+; Ignore unexpected extra "@providesModule"
16
+.*/node_modules/.*/node_modules/fbjs/.*
17
+
18
+; Ignore duplicate module providers
19
+; For RN Apps installed via npm, "Libraries" folder is inside
20
+; "node_modules/react-native" but in the source repo it is in the root
21
+.*/Libraries/react-native/React.js
22
+
23
+; Ignore polyfills
24
+.*/Libraries/polyfills/.*
25
+
26
+; Ignore metro
27
+.*/node_modules/metro/.*
28
+
29
+; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
30
+.*/node_modules/config-chain/test/*.
31
+
32
+; These should not be required directly
33
+; require from fbjs/lib instead: require('fbjs/lib/invariant')
34
+.*/node_modules/invariant/.*
35
+.*/node_modules/warning/.*
36
+
37
+[include]
38
+
39
+[libs]
40
+node_modules/react-native/Libraries/react-native/react-native-interface.js
41
+node_modules/react-native/flow/
42
+node_modules/react-native/flow-github/
43
+
44
+[lints]
45
+
46
+[options]
47
+emoji=true
48
+
49
+esproposal.optional_chaining=enable
50
+esproposal.nullish_coalescing=enable
51
+
52
+module.system=haste
53
+module.system.haste.use_name_reducers=true
54
+# keep the following in sync with server/haste/hasteImpl.js
55
+# get basename
56
+module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
57
+# strip .js or .js.flow suffix
58
+module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
59
+# strip platform suffix
60
+module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
61
+module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
62
+module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
63
+module.system.haste.paths.blacklist=.*/__tests__/.*
64
+module.system.haste.paths.blacklist=.*/__mocks__/.*
65
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
66
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
67
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
68
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
69
+module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
70
+; Surpress error `Duplicate module provider` until the slimmening is done
71
+module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
72
+module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
73
+
74
+munge_underscores=true
75
+
76
+module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
77
+
78
+suppress_type=$FlowIssue
79
+suppress_type=$FlowFixMe
80
+suppress_type=$FlowFixMeProps
81
+suppress_type=$FlowFixMeState
82
+
83
+suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
84
+suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
85
+suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
86
+suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
87
+
88
+[strict]

+ 88
- 0
.flowconfig.android View File

@@ -0,0 +1,88 @@
1
+[ignore]
2
+; This flowconfig is forked by platform - the only difference between them is which suffix is ignored.
3
+;.*/*[.]android.js
4
+.*/*[.]ios.js
5
+
6
+; Ignore templates for 'react-native init'
7
+.*/local-cli/templates/.*
8
+
9
+; Ignore the Dangerfile
10
+node_modules/react-native/bots/dangerfile.js
11
+
12
+; Ignore "BUCK" generated dirs
13
+node_modules/react-native/\.buckd/
14
+
15
+; Ignore unexpected extra "@providesModule"
16
+.*/node_modules/.*/node_modules/fbjs/.*
17
+
18
+; Ignore duplicate module providers
19
+; For RN Apps installed via npm, "Libraries" folder is inside
20
+; "node_modules/react-native" but in the source repo it is in the root
21
+.*/Libraries/react-native/React.js
22
+
23
+; Ignore polyfills
24
+.*/Libraries/polyfills/.*
25
+
26
+; Ignore metro
27
+.*/node_modules/metro/.*
28
+
29
+; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow
30
+.*/node_modules/config-chain/test/*.
31
+
32
+; These should not be required directly
33
+; require from fbjs/lib instead: require('fbjs/lib/invariant')
34
+.*/node_modules/invariant/.*
35
+.*/node_modules/warning/.*
36
+
37
+[include]
38
+
39
+[libs]
40
+node_modules/react-native/Libraries/react-native/react-native-interface.js
41
+node_modules/react-native/flow/
42
+node_modules/react-native/flow-github/
43
+
44
+[lints]
45
+
46
+[options]
47
+emoji=true
48
+
49
+esproposal.optional_chaining=enable
50
+esproposal.nullish_coalescing=enable
51
+
52
+module.system=haste
53
+module.system.haste.use_name_reducers=true
54
+# keep the following in sync with server/haste/hasteImpl.js
55
+# get basename
56
+module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1'
57
+# strip .js or .js.flow suffix
58
+module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1'
59
+# strip platform suffix
60
+module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1'
61
+module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1'
62
+module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1'
63
+module.system.haste.paths.blacklist=.*/__tests__/.*
64
+module.system.haste.paths.blacklist=.*/__mocks__/.*
65
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/js/.*
66
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
67
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
68
+module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
69
+module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
70
+; Surpress error `Duplicate module provider` until the slimmening is done
71
+module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
72
+module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WKWebView/*.
73
+
74
+munge_underscores=true
75
+
76
+module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
77
+
78
+suppress_type=$FlowIssue
79
+suppress_type=$FlowFixMe
80
+suppress_type=$FlowFixMeProps
81
+suppress_type=$FlowFixMeState
82
+
83
+suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
84
+suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
85
+suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
86
+suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
87
+
88
+[strict]

+ 9
- 3
js/WKWebView.ios.js View File

@@ -15,14 +15,20 @@ import { requireNativeComponent } from 'react-native';
15 15
 
16 16
 const RCTWKWebView = requireNativeComponent('RCTWKWebView');
17 17
 
18
-class WKWebView extends React.Component {
19
-  componentWillReceiveProps(nextProps) {
18
+type RCTWKWebViewProps = {
19
+  allowsInlineMediaPlayback?: boolean,
20
+  mediaPlaybackRequiresUserAction?: boolean,
21
+  dataDetectorTypes?: boolean,
22
+};
23
+
24
+class WKWebView extends React.Component<RCTWKWebViewProps> {
25
+  componentWillReceiveProps(nextProps: RCTWKWebViewProps) {
20 26
     this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
21 27
     this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
22 28
     this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
23 29
   }
24 30
 
25
-  showRedboxOnPropChanges(nextProps, propName) {
31
+  showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) {
26 32
     if (this.props[propName] !== nextProps[propName]) {
27 33
       console.error(`Changes to property ${propName} do nothing after the initial render.`);
28 34
     }

+ 66
- 15
js/WebView.android.js View File

@@ -5,6 +5,7 @@
5 5
  * LICENSE file in the root directory of this source tree.
6 6
  *
7 7
  * @format
8
+ * @flow
8 9
  */
9 10
 
10 11
 'use strict';
@@ -21,9 +22,16 @@ import {
21 22
   requireNativeComponent
22 23
 } from 'react-native';
23 24
 
25
+import invariant from 'fbjs/lib/invariant';
24 26
 import keyMirror from 'fbjs/lib/keyMirror';
25 27
 
26 28
 import WebViewShared from './WebViewShared';
29
+import type {
30
+  WebViewErrorEvent,
31
+  WebViewEvent,
32
+  WebViewSharedProps,
33
+  WebViewSource,
34
+} from './WebViewTypes';
27 35
 
28 36
 const resolveAssetSource = Image.resolveAssetSource;
29 37
 
@@ -41,10 +49,52 @@ const defaultRenderLoading = () => (
41 49
   </View>
42 50
 );
43 51
 
52
+type State = {|
53
+  viewState: WebViewState,
54
+  lastErrorEvent: ?WebViewErrorEvent,
55
+  startInLoadingState: boolean,
56
+|};
57
+
58
+type WebViewPropsAndroid = $ReadOnly<{|
59
+  ...WebViewSharedProps,
60
+  onNavigationStateChange?: (event: WebViewEvent) => any,
61
+  onContentSizeChange?: (event: WebViewEvent) => any,
62
+
63
+  /**
64
+   * Sets whether Geolocation is enabled. The default is false.
65
+   * @platform android
66
+   */
67
+  geolocationEnabled?: ?boolean,
68
+
69
+  /**
70
+   * Boolean that sets whether JavaScript running in the context of a file
71
+   * scheme URL should be allowed to access content from any origin.
72
+   * Including accessing content from other file scheme URLs
73
+   * @platform android
74
+   */
75
+  allowUniversalAccessFromFileURLs?: ?boolean,
76
+
77
+  /**
78
+   * Used on Android only, controls whether form autocomplete data should be saved
79
+   * @platform android
80
+   */
81
+  saveFormDataDisabled?: ?boolean,
82
+
83
+  /*
84
+   * Used on Android only, controls whether the given list of URL prefixes should
85
+   * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a
86
+   * default activity intent for those URL instead of loading it within the webview.
87
+   * Use this to list URLs that WebView cannot handle, e.g. a PDF url.
88
+   * @platform android
89
+   */
90
+  urlPrefixesForDefaultIntent?: $ReadOnlyArray<string>,
91
+
92
+|}>;
93
+
44 94
 /**
45 95
  * Renders a native WebView.
46 96
  */
47
-class WebView extends React.Component {
97
+class WebView extends React.Component<WebViewPropsAndroid, State> {
48 98
   static defaultProps = {
49 99
     javaScriptEnabled: true,
50 100
     thirdPartyCookiesEnabled: true,
@@ -55,7 +105,7 @@ class WebView extends React.Component {
55 105
 
56 106
   state = {
57 107
     viewState: WebViewState.IDLE,
58
-    lastErrorEvent: null,
108
+    lastErrorEvent: (null: ?WebViewErrorEvent),
59 109
     startInLoadingState: true,
60 110
   };
61 111
 
@@ -72,6 +122,7 @@ class WebView extends React.Component {
72 122
       otherView = (this.props.renderLoading || defaultRenderLoading)();
73 123
     } else if (this.state.viewState === WebViewState.ERROR) {
74 124
       const errorEvent = this.state.lastErrorEvent;
125
+      invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
75 126
       otherView =
76 127
         this.props.renderError &&
77 128
         this.props.renderError(
@@ -81,7 +132,7 @@ class WebView extends React.Component {
81 132
         );
82 133
     } else if (this.state.viewState !== WebViewState.IDLE) {
83 134
       console.error(
84
-        'RCTWebView invalid state encountered: ' + this.state.loading,
135
+        'RCTWebView invalid state encountered: ' + this.state.viewState,
85 136
       );
86 137
     }
87 138
 
@@ -94,11 +145,11 @@ class WebView extends React.Component {
94 145
       webViewStyles.push(styles.hidden);
95 146
     }
96 147
 
97
-    const source = this.props.source || {};
98
-    if (this.props.html) {
99
-      source.html = this.props.html;
100
-    } else if (this.props.url) {
101
-      source.uri = this.props.url;
148
+    let source = this.props.source || ({}: WebViewSource);
149
+    if (!this.props.source && this.props.html) {
150
+      source = { html: this.props.html };
151
+    } else if (!this.props.source && this.props.url) {
152
+      source = { uri: this.props.url };
102 153
     }
103 154
 
104 155
     if (source.method === 'POST' && source.headers) {
@@ -198,7 +249,7 @@ class WebView extends React.Component {
198 249
     );
199 250
   };
200 251
 
201
-  postMessage = data => {
252
+  postMessage = (data: string) => {
202 253
     UIManager.dispatchViewManagerCommand(
203 254
       this.getWebViewHandle(),
204 255
       UIManager.RCTWebView.Commands.postMessage,
@@ -212,7 +263,7 @@ class WebView extends React.Component {
212 263
    * on pages with a Content Security Policy that disallows eval(). If you need that
213 264
    * functionality, look into postMessage/onMessage.
214 265
    */
215
-  injectJavaScript = data => {
266
+  injectJavaScript = (data: string) => {
216 267
     UIManager.dispatchViewManagerCommand(
217 268
       this.getWebViewHandle(),
218 269
       UIManager.RCTWebView.Commands.injectJavaScript,
@@ -224,7 +275,7 @@ class WebView extends React.Component {
224 275
    * We return an event with a bunch of fields including:
225 276
    *  url, title, loading, canGoBack, canGoForward
226 277
    */
227
-  updateNavigationState = event => {
278
+  updateNavigationState = (event: WebViewEvent) => {
228 279
     if (this.props.onNavigationStateChange) {
229 280
       this.props.onNavigationStateChange(event.nativeEvent);
230 281
     }
@@ -234,13 +285,13 @@ class WebView extends React.Component {
234 285
     return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
235 286
   };
236 287
 
237
-  onLoadingStart = event => {
288
+  onLoadingStart = (event: WebViewEvent) => {
238 289
     const onLoadStart = this.props.onLoadStart;
239 290
     onLoadStart && onLoadStart(event);
240 291
     this.updateNavigationState(event);
241 292
   };
242 293
 
243
-  onLoadingError = event => {
294
+  onLoadingError = (event: WebViewEvent) => {
244 295
     event.persist(); // persist this event because we need to store it
245 296
     const { onError, onLoadEnd } = this.props;
246 297
     onError && onError(event);
@@ -253,7 +304,7 @@ class WebView extends React.Component {
253 304
     });
254 305
   };
255 306
 
256
-  onLoadingFinish = event => {
307
+  onLoadingFinish = (event: WebViewEvent) => {
257 308
     const { onLoad, onLoadEnd } = this.props;
258 309
     onLoad && onLoad(event);
259 310
     onLoadEnd && onLoadEnd(event);
@@ -263,7 +314,7 @@ class WebView extends React.Component {
263 314
     this.updateNavigationState(event);
264 315
   };
265 316
 
266
-  onMessage = (event) => {
317
+  onMessage = (event: WebViewEvent) => {
267 318
     const { onMessage } = this.props;
268 319
     onMessage && onMessage(event);
269 320
   };

+ 32
- 35
js/WebView.ios.js View File

@@ -5,7 +5,7 @@
5 5
  * LICENSE file in the root directory of this source tree.
6 6
  *
7 7
  * @format
8
- * @noflow
8
+ * @flow
9 9
  */
10 10
 
11 11
 'use strict';
@@ -29,6 +29,12 @@ import invariant from 'fbjs/lib/invariant';
29 29
 import keyMirror from 'fbjs/lib/keyMirror';
30 30
 
31 31
 import WebViewShared from './WebViewShared';
32
+import type {
33
+  WebViewErrorEvent,
34
+  WebViewEvent,
35
+  WebViewSharedProps,
36
+  WebViewSource,
37
+} from './WebViewTypes';
32 38
 
33 39
 const resolveAssetSource = Image.resolveAssetSource;
34 40
 
@@ -65,13 +71,11 @@ const NavigationType = keyMirror({
65 71
 
66 72
 const JSNavigationScheme = 'react-js-navigation';
67 73
 
68
-// type ErrorEvent = {
69
-//   domain: any,
70
-//   code: any,
71
-//   description: any,
72
-// };
73
-
74
-// type Event = Object;
74
+type State = {|
75
+  viewState: WebViewState,
76
+  lastErrorEvent: ?WebViewErrorEvent,
77
+  startInLoadingState: boolean,
78
+|};
75 79
 
76 80
 const DataDetectorTypes = [
77 81
   'phoneNumber',
@@ -121,7 +125,7 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => (
121 125
  * You can use this component to navigate back and forth in the web view's
122 126
  * history and configure various properties for the web content.
123 127
  */
124
-class WebView extends React.Component {
128
+class WebView extends React.Component<WebViewSharedProps, State> {
125 129
   static JSNavigationScheme = JSNavigationScheme;
126 130
   static NavigationType = NavigationType;
127 131
 
@@ -132,7 +136,7 @@ class WebView extends React.Component {
132 136
 
133 137
   state = {
134 138
     viewState: WebViewState.IDLE,
135
-    lastErrorEvent: null,
139
+    lastErrorEvent: (null: ?WebViewErrorEvent),
136 140
     startInLoadingState: true,
137 141
   };
138 142
 
@@ -174,7 +178,7 @@ class WebView extends React.Component {
174 178
       );
175 179
     } else if (this.state.viewState !== WebViewState.IDLE) {
176 180
       console.error(
177
-        'RCTWebView invalid state encountered: ' + this.state.loading,
181
+        'RCTWebView invalid state encountered: ' + this.state.viewState,
178 182
       );
179 183
     }
180 184
 
@@ -217,6 +221,7 @@ class WebView extends React.Component {
217 221
           shouldStart &&
218 222
           this.props.onShouldStartLoadWithRequest(event.nativeEvent);
219 223
       }
224
+      invariant(viewManager != null, 'viewManager expected to be non-null');
220 225
       viewManager.startLoadWithResult(
221 226
         !!shouldStart,
222 227
         event.nativeEvent.lockIdentifier,
@@ -227,11 +232,11 @@ class WebView extends React.Component {
227 232
       this.props.decelerationRate,
228 233
     );
229 234
 
230
-    const source = this.props.source || {};
231
-    if (this.props.html) {
232
-      source.html = this.props.html;
233
-    } else if (this.props.url) {
234
-      source.uri = this.props.url;
235
+    let source = this.props.source || ({}: WebViewSource);
236
+    if (!this.props.source && this.props.html) {
237
+      source = { html: this.props.html };
238
+    } else if (!this.props.source && this.props.url) {
239
+      source = { uri: this.props.url };
235 240
     }
236 241
 
237 242
     const messagingEnabled = typeof this.props.onMessage === 'function';
@@ -345,7 +350,7 @@ class WebView extends React.Component {
345 350
    * document.addEventListener('message', e => { document.title = e.data; });
346 351
    * ```
347 352
    */
348
-  postMessage = data => {
353
+  postMessage = (data: string) => {
349 354
     UIManager.dispatchViewManagerCommand(
350 355
       this.getWebViewHandle(),
351 356
       this._getCommands().postMessage,
@@ -359,7 +364,7 @@ class WebView extends React.Component {
359 364
    * on pages with a Content Security Policy that disallows eval(). If you need that
360 365
    * functionality, look into postMessage/onMessage.
361 366
    */
362
-  injectJavaScript = data => {
367
+  injectJavaScript = (data: string) => {
363 368
     UIManager.dispatchViewManagerCommand(
364 369
       this.getWebViewHandle(),
365 370
       this._getCommands().injectJavaScript,
@@ -371,7 +376,7 @@ class WebView extends React.Component {
371 376
    * We return an event with a bunch of fields including:
372 377
    *  url, title, loading, canGoBack, canGoForward
373 378
    */
374
-  _updateNavigationState = (event) => {
379
+  _updateNavigationState = (event: WebViewEvent) => {
375 380
     if (this.props.onNavigationStateChange) {
376 381
       this.props.onNavigationStateChange(event.nativeEvent);
377 382
     }
@@ -384,13 +389,13 @@ class WebView extends React.Component {
384 389
     return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
385 390
   };
386 391
 
387
-  _onLoadingStart = (event) => {
392
+  _onLoadingStart = (event: WebViewEvent) => {
388 393
     const onLoadStart = this.props.onLoadStart;
389 394
     onLoadStart && onLoadStart(event);
390 395
     this._updateNavigationState(event);
391 396
   };
392 397
 
393
-  _onLoadingError = (event) => {
398
+  _onLoadingError = (event: WebViewEvent) => {
394 399
     event.persist(); // persist this event because we need to store it
395 400
     const { onError, onLoadEnd } = this.props;
396 401
     onError && onError(event);
@@ -403,7 +408,7 @@ class WebView extends React.Component {
403 408
     });
404 409
   };
405 410
 
406
-  _onLoadingFinish = (event) => {
411
+  _onLoadingFinish = (event: WebViewEvent) => {
407 412
     const { onLoad, onLoadEnd } = this.props;
408 413
     onLoad && onLoad(event);
409 414
     onLoadEnd && onLoadEnd(event);
@@ -413,12 +418,12 @@ class WebView extends React.Component {
413 418
     this._updateNavigationState(event);
414 419
   };
415 420
 
416
-  _onMessage = (event) => {
421
+  _onMessage = (event: WebViewEvent) => {
417 422
     const { onMessage } = this.props;
418 423
     onMessage && onMessage(event);
419 424
   };
420 425
 
421
-  componentDidUpdate(prevProps) {
426
+  componentDidUpdate(prevProps: WebViewSharedProps) {
422 427
     if (!(prevProps.useWebKit && this.props.useWebKit)) {
423 428
       return;
424 429
     }
@@ -434,7 +439,7 @@ class WebView extends React.Component {
434 439
     }
435 440
   }
436 441
 
437
-  _showRedboxOnPropChanges(prevProps, propName) {
442
+  _showRedboxOnPropChanges(prevProps, propName: string) {
438 443
     if (this.props[propName] !== prevProps[propName]) {
439 444
       console.error(
440 445
         `Changes to property ${propName} do nothing after the initial render.`,
@@ -443,16 +448,8 @@ class WebView extends React.Component {
443 448
   }
444 449
 }
445 450
 
446
-const RCTWebView = requireNativeComponent(
447
-  'RCTWebView',
448
-  WebView,
449
-  WebView.extraNativeComponentConfig,
450
-);
451
-const RCTWKWebView = requireNativeComponent(
452
-  'RCTWKWebView',
453
-  WebView,
454
-  WebView.extraNativeComponentConfig,
455
-);
451
+const RCTWebView = requireNativeComponent('RCTWebView');
452
+const RCTWKWebView = requireNativeComponent('RCTWKWebView');
456 453
 
457 454
 const styles = StyleSheet.create({
458 455
   container: {

+ 3
- 3
js/WebViewShared.js View File

@@ -14,11 +14,11 @@ const escapeStringRegexp = require('escape-string-regexp');
14 14
 
15 15
 const WebViewShared = {
16 16
   defaultOriginWhitelist: ['http://*', 'https://*'],
17
-  extractOrigin: (url) => {
17
+  extractOrigin: (url: string) => {
18 18
     const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
19
-    return result === null ? null : result[0];
19
+    return result === null ? '' : result[0];
20 20
   },
21
-  originWhitelistToRegex: (originWhitelist) => {
21
+  originWhitelistToRegex: (originWhitelist: string) => {
22 22
     return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
23 23
   },
24 24
 };

+ 332
- 0
js/WebViewTypes.js View File

@@ -0,0 +1,332 @@
1
+/**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @flow
9
+ */
10
+
11
+'use strict';
12
+
13
+import type {Node, Element} from 'react';
14
+import type {EdgeInsetsProp} from 'EdgeInsetsPropType';
15
+import type {ViewStyleProp} from 'StyleSheet';
16
+import type {ViewProps} from 'ViewPropTypes';
17
+
18
+export type WebViewErrorEvent = {
19
+  domain: any,
20
+  code: any,
21
+  description: any,
22
+};
23
+
24
+export type WebViewEvent = Object;
25
+
26
+export type DataDetectorTypes =
27
+  | 'phoneNumber'
28
+  | 'link'
29
+  | 'address'
30
+  | 'calendarEvent'
31
+  | 'trackingNumber'
32
+  | 'flightNumber'
33
+  | 'lookupSuggestion'
34
+  | 'none'
35
+  | 'all';
36
+
37
+export type WebViewSourceUri = {|
38
+  /**
39
+   * The URI to load in the `WebView`. Can be a local or remote file.
40
+   */
41
+  uri?: ?string,
42
+
43
+  /**
44
+   * The HTTP Method to use. Defaults to GET if not specified.
45
+   * NOTE: On Android, only GET and POST are supported.
46
+   */
47
+  method?: string,
48
+
49
+  /**
50
+   * Additional HTTP headers to send with the request.
51
+   * NOTE: On Android, this can only be used with GET requests.
52
+   */
53
+  headers?: Object,
54
+
55
+  /**
56
+   * The HTTP body to send with the request. This must be a valid
57
+   * UTF-8 string, and will be sent exactly as specified, with no
58
+   * additional encoding (e.g. URL-escaping or base64) applied.
59
+   * NOTE: On Android, this can only be used with POST requests.
60
+   */
61
+  body?: string,
62
+|};
63
+
64
+export type WebViewSourceHtml = {|
65
+  /**
66
+   * A static HTML page to display in the WebView.
67
+   */
68
+  html?: ?string,
69
+  /**
70
+   * The base URL to be used for any relative links in the HTML.
71
+   */
72
+  baseUrl?: ?string,
73
+|};
74
+
75
+export type WebViewSource = WebViewSourceUri | WebViewSourceHtml;
76
+
77
+export type WebViewNativeConfig = $ReadOnly<{|
78
+  /*
79
+   * The native component used to render the WebView.
80
+   */
81
+  component?: ?any,
82
+  /*
83
+   * Set props directly on the native component WebView. Enables custom props which the
84
+   * original WebView doesn't pass through.
85
+   */
86
+  props?: ?Object,
87
+  /*
88
+   * Set the ViewManager to use for communication with the native side.
89
+   * @platform ios
90
+   */
91
+  viewManager?: ?Object,
92
+|}>;
93
+
94
+export type WebViewSharedProps =  $ReadOnly<{|
95
+  ...ViewProps,
96
+  /**
97
+   * Deprecated. Use `source` instead.
98
+   */
99
+  url?: ?string,
100
+  /**
101
+   * Deprecated. Use `source` instead.
102
+   */
103
+  html?: ?string,
104
+
105
+  /**
106
+   * Loads static html or a uri (with optional headers) in the WebView.
107
+   */
108
+  source?: ?WebViewSource,
109
+
110
+  /**
111
+   * If true, use WKWebView instead of UIWebView.
112
+   * @platform ios
113
+   */
114
+  useWebKit?: ?boolean,
115
+
116
+  /**
117
+   * Function that returns a view to show if there's an error.
118
+   */
119
+  renderError: (errorDomain: any, errorCode: any, errorDesc: any) => Element<any>, // view to show if there's an error
120
+
121
+  /**
122
+   * Function that returns a loading indicator.
123
+   */
124
+  renderLoading: () => Element<any>,
125
+
126
+  /**
127
+   * Function that is invoked when the `WebView` has finished loading.
128
+   */
129
+  onLoad: (event: WebViewEvent) => any,
130
+
131
+  /**
132
+   * Function that is invoked when the `WebView` load succeeds or fails.
133
+   */
134
+  onLoadEnd: (event: WebViewEvent) => any,
135
+
136
+  /**
137
+   * Function that is invoked when the `WebView` starts loading.
138
+   */
139
+  onLoadStart: (event: WebViewEvent) => any,
140
+
141
+  /**
142
+   * Function that is invoked when the `WebView` load fails.
143
+   */
144
+  onError: (event: WebViewEvent) => any,
145
+
146
+  /**
147
+   * Boolean value that determines whether the web view bounces
148
+   * when it reaches the edge of the content. The default value is `true`.
149
+   * @platform ios
150
+   */
151
+  bounces?: ?boolean,
152
+
153
+  /**
154
+   * A floating-point number that determines how quickly the scroll view
155
+   * decelerates after the user lifts their finger. You may also use the
156
+   * string shortcuts `"normal"` and `"fast"` which match the underlying iOS
157
+   * settings for `UIScrollViewDecelerationRateNormal` and
158
+   * `UIScrollViewDecelerationRateFast` respectively:
159
+   *
160
+   *   - normal: 0.998
161
+   *   - fast: 0.99 (the default for iOS web view)
162
+   * @platform ios
163
+   */
164
+  decelerationRate?: ?('fast' | 'normal' | number),
165
+
166
+  /**
167
+   * Boolean value that determines whether scrolling is enabled in the
168
+   * `WebView`. The default value is `true`.
169
+   * @platform ios
170
+   */
171
+  scrollEnabled?: ?boolean,
172
+
173
+  /**
174
+   * Controls whether to adjust the content inset for web views that are
175
+   * placed behind a navigation bar, tab bar, or toolbar. The default value
176
+   * is `true`.
177
+   */
178
+  automaticallyAdjustContentInsets?: ?boolean,
179
+
180
+  /**
181
+   * The amount by which the web view content is inset from the edges of
182
+   * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}.
183
+   * @platform ios
184
+   */
185
+  contentInset?: ?EdgeInsetsProp,
186
+
187
+  /**
188
+   * Function that is invoked when the `WebView` loading starts or ends.
189
+   */
190
+  onNavigationStateChange?: (event: WebViewEvent) => any,
191
+
192
+  /**
193
+   * A function that is invoked when the webview calls `window.postMessage`.
194
+   * Setting this property will inject a `postMessage` global into your
195
+   * webview, but will still call pre-existing values of `postMessage`.
196
+   *
197
+   * `window.postMessage` accepts one argument, `data`, which will be
198
+   * available on the event object, `event.nativeEvent.data`. `data`
199
+   * must be a string.
200
+   */
201
+  onMessage?: (event: WebViewEvent) => any,
202
+
203
+  /**
204
+   * Boolean value that forces the `WebView` to show the loading view
205
+   * on the first load.
206
+   */
207
+  startInLoadingState?: ?boolean,
208
+
209
+  /**
210
+   * Determines the types of data converted to clickable URLs in the web view's content.
211
+   * By default only phone numbers are detected.
212
+   *
213
+   * You can provide one type or an array of many types.
214
+   *
215
+   * Possible values for `dataDetectorTypes` are:
216
+   *
217
+   * - `'phoneNumber'`
218
+   * - `'link'`
219
+   * - `'address'`
220
+   * - `'calendarEvent'`
221
+   * - `'none'`
222
+   * - `'all'`
223
+   *
224
+   * With the new WebKit implementation, we have three new values:
225
+   * - `'trackingNumber'`,
226
+   * - `'flightNumber'`,
227
+   * - `'lookupSuggestion'`,
228
+   *
229
+   * @platform ios
230
+   */
231
+  dataDetectorTypes?:
232
+    | ?DataDetectorTypes
233
+    | $ReadOnlyArray<DataDetectorTypes>,
234
+
235
+  /**
236
+   * Boolean value to enable JavaScript in the `WebView`. Used on Android only
237
+   * as JavaScript is enabled by default on iOS. The default value is `true`.
238
+   * @platform android
239
+   */
240
+  javaScriptEnabled?: ?boolean,
241
+
242
+  /**
243
+   * Boolean value to enable third party cookies in the `WebView`. Used on
244
+   * Android Lollipop and above only as third party cookies are enabled by
245
+   * default on Android Kitkat and below and on iOS. The default value is `true`.
246
+   * @platform android
247
+   */
248
+  thirdPartyCookiesEnabled?: ?boolean,
249
+
250
+  /**
251
+   * Boolean value to control whether DOM Storage is enabled. Used only in
252
+   * Android.
253
+   * @platform android
254
+   */
255
+  domStorageEnabled?: ?boolean,
256
+
257
+  /**
258
+   * Set this to provide JavaScript that will be injected into the web page
259
+   * when the view loads.
260
+   */
261
+  injectedJavaScript?: ?string,
262
+
263
+  /**
264
+   * Sets the user-agent for the `WebView`.
265
+   * @platform android
266
+   */
267
+  userAgent?: ?string,
268
+
269
+  /**
270
+   * Boolean that controls whether the web content is scaled to fit
271
+   * the view and enables the user to change the scale. The default value
272
+   * is `true`.
273
+   *
274
+   * On iOS, when `useWebKit=true`, this prop will not work.
275
+   */
276
+  scalesPageToFit?: ?boolean,
277
+
278
+  /**
279
+   * Function that allows custom handling of any web view requests. Return
280
+   * `true` from the function to continue loading the request and `false`
281
+   * to stop loading.
282
+   * @platform ios
283
+   */
284
+  onShouldStartLoadWithRequest?: (event: WebViewEvent) => any,
285
+
286
+  /**
287
+   * Boolean that determines whether HTML5 videos play inline or use the
288
+   * native full-screen controller. The default value is `false`.
289
+   *
290
+   * **NOTE** : In order for video to play inline, not only does this
291
+   * property need to be set to `true`, but the video element in the HTML
292
+   * document must also include the `webkit-playsinline` attribute.
293
+   * @platform ios
294
+   */
295
+  allowsInlineMediaPlayback?: ?boolean,
296
+
297
+  /**
298
+   * Boolean that determines whether HTML5 audio and video requires the user
299
+   * to tap them before they start playing. The default value is `true`.
300
+   */
301
+  mediaPlaybackRequiresUserAction?: ?boolean,
302
+
303
+  /**
304
+   * List of origin strings to allow being navigated to. The strings allow
305
+   * wildcards and get matched against *just* the origin (not the full URL).
306
+   * If the user taps to navigate to a new page but the new page is not in
307
+   * this whitelist, we will open the URL in Safari.
308
+   * The default whitelisted origins are "http://*" and "https://*".
309
+   */
310
+  originWhitelist?: $ReadOnlyArray<string>,
311
+
312
+  /**
313
+   * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
314
+   *
315
+   * Possible values for `mixedContentMode` are:
316
+   *
317
+   * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin.
318
+   * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure.
319
+   * - `'compatibility'` -  WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content.
320
+   * @platform android
321
+   */
322
+  mixedContentMode?: ?('never' | 'always' | 'compatibility'),
323
+
324
+  /**
325
+   * Override the native component used to render the WebView. Enables a custom native
326
+   * WebView which uses the same JavaScript as the original WebView.
327
+   */
328
+  nativeConfig?: ?WebViewNativeConfig,
329
+
330
+  style?: ViewStyleProp,
331
+  children: Node,
332
+|}>;

+ 9
- 1
package.json View File

@@ -5,12 +5,20 @@
5 5
   "author": "Jamon Holmgren <jamon@infinite.red>",
6 6
   "version": "0.1.0",
7 7
   "homepage": "https://github.com/react-native-community/react-native-webview#readme",
8
+  "scripts": {
9
+    "flow": "flow check",
10
+    "flow-android": "flow check --flowconfig-name .flowconfig.android"
11
+  },
8 12
   "peerDependencies": {
9 13
     "react": "^16.0",
10
-    "react-native": "^0.56"
14
+    "react-native": "~0.57.0-rc.4"
11 15
   },
12 16
   "dependencies": {
13 17
     "escape-string-regexp": "^1.0.5",
14 18
     "fbjs": "^0.8.17"
19
+  },
20
+  "devDependencies": {
21
+    "flow-bin": "^0.80.0",
22
+    "react-native": "~0.57.0-rc.4"
15 23
   }
16 24
 }