Browse Source

Add WebView Flow types

empyrical 6 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

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

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
 
15
 
16
 const RCTWKWebView = requireNativeComponent('RCTWKWebView');
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
     this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
26
     this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback');
21
     this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
27
     this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction');
22
     this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
28
     this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes');
23
   }
29
   }
24
 
30
 
25
-  showRedboxOnPropChanges(nextProps, propName) {
31
+  showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) {
26
     if (this.props[propName] !== nextProps[propName]) {
32
     if (this.props[propName] !== nextProps[propName]) {
27
       console.error(`Changes to property ${propName} do nothing after the initial render.`);
33
       console.error(`Changes to property ${propName} do nothing after the initial render.`);
28
     }
34
     }

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

5
  * LICENSE file in the root directory of this source tree.
5
  * LICENSE file in the root directory of this source tree.
6
  *
6
  *
7
  * @format
7
  * @format
8
+ * @flow
8
  */
9
  */
9
 
10
 
10
 'use strict';
11
 'use strict';
21
   requireNativeComponent
22
   requireNativeComponent
22
 } from 'react-native';
23
 } from 'react-native';
23
 
24
 
25
+import invariant from 'fbjs/lib/invariant';
24
 import keyMirror from 'fbjs/lib/keyMirror';
26
 import keyMirror from 'fbjs/lib/keyMirror';
25
 
27
 
26
 import WebViewShared from './WebViewShared';
28
 import WebViewShared from './WebViewShared';
29
+import type {
30
+  WebViewErrorEvent,
31
+  WebViewEvent,
32
+  WebViewSharedProps,
33
+  WebViewSource,
34
+} from './WebViewTypes';
27
 
35
 
28
 const resolveAssetSource = Image.resolveAssetSource;
36
 const resolveAssetSource = Image.resolveAssetSource;
29
 
37
 
41
   </View>
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
  * Renders a native WebView.
95
  * Renders a native WebView.
46
  */
96
  */
47
-class WebView extends React.Component {
97
+class WebView extends React.Component<WebViewPropsAndroid, State> {
48
   static defaultProps = {
98
   static defaultProps = {
49
     javaScriptEnabled: true,
99
     javaScriptEnabled: true,
50
     thirdPartyCookiesEnabled: true,
100
     thirdPartyCookiesEnabled: true,
55
 
105
 
56
   state = {
106
   state = {
57
     viewState: WebViewState.IDLE,
107
     viewState: WebViewState.IDLE,
58
-    lastErrorEvent: null,
108
+    lastErrorEvent: (null: ?WebViewErrorEvent),
59
     startInLoadingState: true,
109
     startInLoadingState: true,
60
   };
110
   };
61
 
111
 
72
       otherView = (this.props.renderLoading || defaultRenderLoading)();
122
       otherView = (this.props.renderLoading || defaultRenderLoading)();
73
     } else if (this.state.viewState === WebViewState.ERROR) {
123
     } else if (this.state.viewState === WebViewState.ERROR) {
74
       const errorEvent = this.state.lastErrorEvent;
124
       const errorEvent = this.state.lastErrorEvent;
125
+      invariant(errorEvent != null, 'lastErrorEvent expected to be non-null');
75
       otherView =
126
       otherView =
76
         this.props.renderError &&
127
         this.props.renderError &&
77
         this.props.renderError(
128
         this.props.renderError(
81
         );
132
         );
82
     } else if (this.state.viewState !== WebViewState.IDLE) {
133
     } else if (this.state.viewState !== WebViewState.IDLE) {
83
       console.error(
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
       webViewStyles.push(styles.hidden);
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
     if (source.method === 'POST' && source.headers) {
155
     if (source.method === 'POST' && source.headers) {
198
     );
249
     );
199
   };
250
   };
200
 
251
 
201
-  postMessage = data => {
252
+  postMessage = (data: string) => {
202
     UIManager.dispatchViewManagerCommand(
253
     UIManager.dispatchViewManagerCommand(
203
       this.getWebViewHandle(),
254
       this.getWebViewHandle(),
204
       UIManager.RCTWebView.Commands.postMessage,
255
       UIManager.RCTWebView.Commands.postMessage,
212
    * on pages with a Content Security Policy that disallows eval(). If you need that
263
    * on pages with a Content Security Policy that disallows eval(). If you need that
213
    * functionality, look into postMessage/onMessage.
264
    * functionality, look into postMessage/onMessage.
214
    */
265
    */
215
-  injectJavaScript = data => {
266
+  injectJavaScript = (data: string) => {
216
     UIManager.dispatchViewManagerCommand(
267
     UIManager.dispatchViewManagerCommand(
217
       this.getWebViewHandle(),
268
       this.getWebViewHandle(),
218
       UIManager.RCTWebView.Commands.injectJavaScript,
269
       UIManager.RCTWebView.Commands.injectJavaScript,
224
    * We return an event with a bunch of fields including:
275
    * We return an event with a bunch of fields including:
225
    *  url, title, loading, canGoBack, canGoForward
276
    *  url, title, loading, canGoBack, canGoForward
226
    */
277
    */
227
-  updateNavigationState = event => {
278
+  updateNavigationState = (event: WebViewEvent) => {
228
     if (this.props.onNavigationStateChange) {
279
     if (this.props.onNavigationStateChange) {
229
       this.props.onNavigationStateChange(event.nativeEvent);
280
       this.props.onNavigationStateChange(event.nativeEvent);
230
     }
281
     }
234
     return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
285
     return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]);
235
   };
286
   };
236
 
287
 
237
-  onLoadingStart = event => {
288
+  onLoadingStart = (event: WebViewEvent) => {
238
     const onLoadStart = this.props.onLoadStart;
289
     const onLoadStart = this.props.onLoadStart;
239
     onLoadStart && onLoadStart(event);
290
     onLoadStart && onLoadStart(event);
240
     this.updateNavigationState(event);
291
     this.updateNavigationState(event);
241
   };
292
   };
242
 
293
 
243
-  onLoadingError = event => {
294
+  onLoadingError = (event: WebViewEvent) => {
244
     event.persist(); // persist this event because we need to store it
295
     event.persist(); // persist this event because we need to store it
245
     const { onError, onLoadEnd } = this.props;
296
     const { onError, onLoadEnd } = this.props;
246
     onError && onError(event);
297
     onError && onError(event);
253
     });
304
     });
254
   };
305
   };
255
 
306
 
256
-  onLoadingFinish = event => {
307
+  onLoadingFinish = (event: WebViewEvent) => {
257
     const { onLoad, onLoadEnd } = this.props;
308
     const { onLoad, onLoadEnd } = this.props;
258
     onLoad && onLoad(event);
309
     onLoad && onLoad(event);
259
     onLoadEnd && onLoadEnd(event);
310
     onLoadEnd && onLoadEnd(event);
263
     this.updateNavigationState(event);
314
     this.updateNavigationState(event);
264
   };
315
   };
265
 
316
 
266
-  onMessage = (event) => {
317
+  onMessage = (event: WebViewEvent) => {
267
     const { onMessage } = this.props;
318
     const { onMessage } = this.props;
268
     onMessage && onMessage(event);
319
     onMessage && onMessage(event);
269
   };
320
   };

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

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

+ 3
- 3
js/WebViewShared.js View File

14
 
14
 
15
 const WebViewShared = {
15
 const WebViewShared = {
16
   defaultOriginWhitelist: ['http://*', 'https://*'],
16
   defaultOriginWhitelist: ['http://*', 'https://*'],
17
-  extractOrigin: (url) => {
17
+  extractOrigin: (url: string) => {
18
     const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url);
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
     return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
22
     return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*');
23
   },
23
   },
24
 };
24
 };

+ 332
- 0
js/WebViewTypes.js View File

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
   "author": "Jamon Holmgren <jamon@infinite.red>",
5
   "author": "Jamon Holmgren <jamon@infinite.red>",
6
   "version": "0.1.0",
6
   "version": "0.1.0",
7
   "homepage": "https://github.com/react-native-community/react-native-webview#readme",
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
   "peerDependencies": {
12
   "peerDependencies": {
9
     "react": "^16.0",
13
     "react": "^16.0",
10
-    "react-native": "^0.56"
14
+    "react-native": "~0.57.0-rc.4"
11
   },
15
   },
12
   "dependencies": {
16
   "dependencies": {
13
     "escape-string-regexp": "^1.0.5",
17
     "escape-string-regexp": "^1.0.5",
14
     "fbjs": "^0.8.17"
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
 }