From 133f1e36bc82354aedbd400529654d4fe03c6910 Mon Sep 17 00:00:00 2001 From: empyrical Date: Sun, 9 Sep 2018 22:40:32 -0600 Subject: [PATCH 1/5] Add WebView Flow types --- .flowconfig | 88 +++++++++++ .flowconfig.android | 88 +++++++++++ js/WKWebView.ios.js | 12 +- js/WebView.android.js | 81 +++++++++-- js/WebView.ios.js | 67 ++++----- js/WebViewShared.js | 6 +- js/WebViewTypes.js | 332 ++++++++++++++++++++++++++++++++++++++++++ package.json | 10 +- 8 files changed, 627 insertions(+), 57 deletions(-) create mode 100644 .flowconfig create mode 100644 .flowconfig.android create mode 100644 js/WebViewTypes.js diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 0000000..18ff414 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,88 @@ +[ignore] +; This flowconfig is forked by platform - the only difference between them is which suffix is ignored. +.*/*[.]android.js +;.*/*[.]ios.js + +; Ignore templates for 'react-native init' +.*/local-cli/templates/.* + +; Ignore the Dangerfile +node_modules/react-native/bots/dangerfile.js + +; Ignore "BUCK" generated dirs +node_modules/react-native/\.buckd/ + +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* + +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +.*/Libraries/react-native/React.js + +; Ignore polyfills +.*/Libraries/polyfills/.* + +; Ignore metro +.*/node_modules/metro/.* + +; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow +.*/node_modules/config-chain/test/*. + +; These should not be required directly +; require from fbjs/lib instead: require('fbjs/lib/invariant') +.*/node_modules/invariant/.* +.*/node_modules/warning/.* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow/ +node_modules/react-native/flow-github/ + +[lints] + +[options] +emoji=true + +esproposal.optional_chaining=enable +esproposal.nullish_coalescing=enable + +module.system=haste +module.system.haste.use_name_reducers=true +# keep the following in sync with server/haste/hasteImpl.js +# get basename +module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' +# strip .js or .js.flow suffix +module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' +# strip platform suffix +module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' +module.system.haste.paths.blacklist=.*/__tests__/.* +module.system.haste.paths.blacklist=.*/__mocks__/.* +module.system.haste.paths.whitelist=/js/.* +module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* +module.system.haste.paths.whitelist=/node_modules/react-native/RNTester/.* +module.system.haste.paths.whitelist=/node_modules/react-native/IntegrationTests/.* +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* +; Surpress error `Duplicate module provider` until the slimmening is done +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Components/WebView/*. +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Components/WKWebView/*. + +munge_underscores=true + +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' + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FlowFixMeProps +suppress_type=$FlowFixMeState + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +[strict] diff --git a/.flowconfig.android b/.flowconfig.android new file mode 100644 index 0000000..42f0de1 --- /dev/null +++ b/.flowconfig.android @@ -0,0 +1,88 @@ +[ignore] +; This flowconfig is forked by platform - the only difference between them is which suffix is ignored. +;.*/*[.]android.js +.*/*[.]ios.js + +; Ignore templates for 'react-native init' +.*/local-cli/templates/.* + +; Ignore the Dangerfile +node_modules/react-native/bots/dangerfile.js + +; Ignore "BUCK" generated dirs +node_modules/react-native/\.buckd/ + +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* + +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +.*/Libraries/react-native/React.js + +; Ignore polyfills +.*/Libraries/polyfills/.* + +; Ignore metro +.*/node_modules/metro/.* + +; Ignore "config-chain"'s test folder - it has a corrupt JSON file that's tripping flow +.*/node_modules/config-chain/test/*. + +; These should not be required directly +; require from fbjs/lib instead: require('fbjs/lib/invariant') +.*/node_modules/invariant/.* +.*/node_modules/warning/.* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow/ +node_modules/react-native/flow-github/ + +[lints] + +[options] +emoji=true + +esproposal.optional_chaining=enable +esproposal.nullish_coalescing=enable + +module.system=haste +module.system.haste.use_name_reducers=true +# keep the following in sync with server/haste/hasteImpl.js +# get basename +module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' +# strip .js or .js.flow suffix +module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' +# strip platform suffix +module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' +module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' +module.system.haste.paths.blacklist=.*/__tests__/.* +module.system.haste.paths.blacklist=.*/__mocks__/.* +module.system.haste.paths.whitelist=/js/.* +module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* +module.system.haste.paths.whitelist=/node_modules/react-native/RNTester/.* +module.system.haste.paths.whitelist=/node_modules/react-native/IntegrationTests/.* +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* +; Surpress error `Duplicate module provider` until the slimmening is done +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Components/WebView/*. +module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Components/WKWebView/*. + +munge_underscores=true + +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' + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FlowFixMeProps +suppress_type=$FlowFixMeState + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +[strict] diff --git a/js/WKWebView.ios.js b/js/WKWebView.ios.js index ebf0edb..9d36598 100644 --- a/js/WKWebView.ios.js +++ b/js/WKWebView.ios.js @@ -15,14 +15,20 @@ import { requireNativeComponent } from 'react-native'; const RCTWKWebView = requireNativeComponent('RCTWKWebView'); -class WKWebView extends React.Component { - componentWillReceiveProps(nextProps) { +type RCTWKWebViewProps = { + allowsInlineMediaPlayback?: boolean, + mediaPlaybackRequiresUserAction?: boolean, + dataDetectorTypes?: boolean, +}; + +class WKWebView extends React.Component { + componentWillReceiveProps(nextProps: RCTWKWebViewProps) { this.showRedboxOnPropChanges(nextProps, 'allowsInlineMediaPlayback'); this.showRedboxOnPropChanges(nextProps, 'mediaPlaybackRequiresUserAction'); this.showRedboxOnPropChanges(nextProps, 'dataDetectorTypes'); } - showRedboxOnPropChanges(nextProps, propName) { + showRedboxOnPropChanges(nextProps: RCTWKWebViewProps, propName: string) { if (this.props[propName] !== nextProps[propName]) { console.error(`Changes to property ${propName} do nothing after the initial render.`); } diff --git a/js/WebView.android.js b/js/WebView.android.js index b4f4adb..834306f 100644 --- a/js/WebView.android.js +++ b/js/WebView.android.js @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format + * @flow */ 'use strict'; @@ -21,9 +22,16 @@ import { requireNativeComponent } from 'react-native'; +import invariant from 'fbjs/lib/invariant'; import keyMirror from 'fbjs/lib/keyMirror'; import WebViewShared from './WebViewShared'; +import type { + WebViewErrorEvent, + WebViewEvent, + WebViewSharedProps, + WebViewSource, +} from './WebViewTypes'; const resolveAssetSource = Image.resolveAssetSource; @@ -41,10 +49,52 @@ const defaultRenderLoading = () => ( ); +type State = {| + viewState: WebViewState, + lastErrorEvent: ?WebViewErrorEvent, + startInLoadingState: boolean, +|}; + +type WebViewPropsAndroid = $ReadOnly<{| + ...WebViewSharedProps, + onNavigationStateChange?: (event: WebViewEvent) => any, + onContentSizeChange?: (event: WebViewEvent) => any, + + /** + * Sets whether Geolocation is enabled. The default is false. + * @platform android + */ + geolocationEnabled?: ?boolean, + + /** + * Boolean that sets whether JavaScript running in the context of a file + * scheme URL should be allowed to access content from any origin. + * Including accessing content from other file scheme URLs + * @platform android + */ + allowUniversalAccessFromFileURLs?: ?boolean, + + /** + * Used on Android only, controls whether form autocomplete data should be saved + * @platform android + */ + saveFormDataDisabled?: ?boolean, + + /* + * Used on Android only, controls whether the given list of URL prefixes should + * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a + * default activity intent for those URL instead of loading it within the webview. + * Use this to list URLs that WebView cannot handle, e.g. a PDF url. + * @platform android + */ + urlPrefixesForDefaultIntent?: $ReadOnlyArray, + +|}>; + /** * Renders a native WebView. */ -class WebView extends React.Component { +class WebView extends React.Component { static defaultProps = { javaScriptEnabled: true, thirdPartyCookiesEnabled: true, @@ -55,7 +105,7 @@ class WebView extends React.Component { state = { viewState: WebViewState.IDLE, - lastErrorEvent: null, + lastErrorEvent: (null: ?WebViewErrorEvent), startInLoadingState: true, }; @@ -72,6 +122,7 @@ class WebView extends React.Component { otherView = (this.props.renderLoading || defaultRenderLoading)(); } else if (this.state.viewState === WebViewState.ERROR) { const errorEvent = this.state.lastErrorEvent; + invariant(errorEvent != null, 'lastErrorEvent expected to be non-null'); otherView = this.props.renderError && this.props.renderError( @@ -81,7 +132,7 @@ class WebView extends React.Component { ); } else if (this.state.viewState !== WebViewState.IDLE) { console.error( - 'RCTWebView invalid state encountered: ' + this.state.loading, + 'RCTWebView invalid state encountered: ' + this.state.viewState, ); } @@ -94,11 +145,11 @@ class WebView extends React.Component { webViewStyles.push(styles.hidden); } - const source = this.props.source || {}; - if (this.props.html) { - source.html = this.props.html; - } else if (this.props.url) { - source.uri = this.props.url; + let source = this.props.source || ({}: WebViewSource); + if (!this.props.source && this.props.html) { + source = { html: this.props.html }; + } else if (!this.props.source && this.props.url) { + source = { uri: this.props.url }; } if (source.method === 'POST' && source.headers) { @@ -198,7 +249,7 @@ class WebView extends React.Component { ); }; - postMessage = data => { + postMessage = (data: string) => { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), UIManager.RCTWebView.Commands.postMessage, @@ -212,7 +263,7 @@ class WebView extends React.Component { * on pages with a Content Security Policy that disallows eval(). If you need that * functionality, look into postMessage/onMessage. */ - injectJavaScript = data => { + injectJavaScript = (data: string) => { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), UIManager.RCTWebView.Commands.injectJavaScript, @@ -224,7 +275,7 @@ class WebView extends React.Component { * We return an event with a bunch of fields including: * url, title, loading, canGoBack, canGoForward */ - updateNavigationState = event => { + updateNavigationState = (event: WebViewEvent) => { if (this.props.onNavigationStateChange) { this.props.onNavigationStateChange(event.nativeEvent); } @@ -234,13 +285,13 @@ class WebView extends React.Component { return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); }; - onLoadingStart = event => { + onLoadingStart = (event: WebViewEvent) => { const onLoadStart = this.props.onLoadStart; onLoadStart && onLoadStart(event); this.updateNavigationState(event); }; - onLoadingError = event => { + onLoadingError = (event: WebViewEvent) => { event.persist(); // persist this event because we need to store it const { onError, onLoadEnd } = this.props; onError && onError(event); @@ -253,7 +304,7 @@ class WebView extends React.Component { }); }; - onLoadingFinish = event => { + onLoadingFinish = (event: WebViewEvent) => { const { onLoad, onLoadEnd } = this.props; onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); @@ -263,7 +314,7 @@ class WebView extends React.Component { this.updateNavigationState(event); }; - onMessage = (event) => { + onMessage = (event: WebViewEvent) => { const { onMessage } = this.props; onMessage && onMessage(event); }; diff --git a/js/WebView.ios.js b/js/WebView.ios.js index abb41d7..529e93e 100644 --- a/js/WebView.ios.js +++ b/js/WebView.ios.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @noflow + * @flow */ 'use strict'; @@ -29,6 +29,12 @@ import invariant from 'fbjs/lib/invariant'; import keyMirror from 'fbjs/lib/keyMirror'; import WebViewShared from './WebViewShared'; +import type { + WebViewErrorEvent, + WebViewEvent, + WebViewSharedProps, + WebViewSource, +} from './WebViewTypes'; const resolveAssetSource = Image.resolveAssetSource; @@ -65,13 +71,11 @@ const NavigationType = keyMirror({ const JSNavigationScheme = 'react-js-navigation'; -// type ErrorEvent = { -// domain: any, -// code: any, -// description: any, -// }; - -// type Event = Object; +type State = {| + viewState: WebViewState, + lastErrorEvent: ?WebViewErrorEvent, + startInLoadingState: boolean, +|}; const DataDetectorTypes = [ 'phoneNumber', @@ -121,7 +125,7 @@ const defaultRenderError = (errorDomain, errorCode, errorDesc) => ( * You can use this component to navigate back and forth in the web view's * history and configure various properties for the web content. */ -class WebView extends React.Component { +class WebView extends React.Component { static JSNavigationScheme = JSNavigationScheme; static NavigationType = NavigationType; @@ -132,7 +136,7 @@ class WebView extends React.Component { state = { viewState: WebViewState.IDLE, - lastErrorEvent: null, + lastErrorEvent: (null: ?WebViewErrorEvent), startInLoadingState: true, }; @@ -174,7 +178,7 @@ class WebView extends React.Component { ); } else if (this.state.viewState !== WebViewState.IDLE) { console.error( - 'RCTWebView invalid state encountered: ' + this.state.loading, + 'RCTWebView invalid state encountered: ' + this.state.viewState, ); } @@ -217,6 +221,7 @@ class WebView extends React.Component { shouldStart && this.props.onShouldStartLoadWithRequest(event.nativeEvent); } + invariant(viewManager != null, 'viewManager expected to be non-null'); viewManager.startLoadWithResult( !!shouldStart, event.nativeEvent.lockIdentifier, @@ -227,11 +232,11 @@ class WebView extends React.Component { this.props.decelerationRate, ); - const source = this.props.source || {}; - if (this.props.html) { - source.html = this.props.html; - } else if (this.props.url) { - source.uri = this.props.url; + let source = this.props.source || ({}: WebViewSource); + if (!this.props.source && this.props.html) { + source = { html: this.props.html }; + } else if (!this.props.source && this.props.url) { + source = { uri: this.props.url }; } const messagingEnabled = typeof this.props.onMessage === 'function'; @@ -345,7 +350,7 @@ class WebView extends React.Component { * document.addEventListener('message', e => { document.title = e.data; }); * ``` */ - postMessage = data => { + postMessage = (data: string) => { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), this._getCommands().postMessage, @@ -359,7 +364,7 @@ class WebView extends React.Component { * on pages with a Content Security Policy that disallows eval(). If you need that * functionality, look into postMessage/onMessage. */ - injectJavaScript = data => { + injectJavaScript = (data: string) => { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), this._getCommands().injectJavaScript, @@ -371,7 +376,7 @@ class WebView extends React.Component { * We return an event with a bunch of fields including: * url, title, loading, canGoBack, canGoForward */ - _updateNavigationState = (event) => { + _updateNavigationState = (event: WebViewEvent) => { if (this.props.onNavigationStateChange) { this.props.onNavigationStateChange(event.nativeEvent); } @@ -384,13 +389,13 @@ class WebView extends React.Component { return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); }; - _onLoadingStart = (event) => { + _onLoadingStart = (event: WebViewEvent) => { const onLoadStart = this.props.onLoadStart; onLoadStart && onLoadStart(event); this._updateNavigationState(event); }; - _onLoadingError = (event) => { + _onLoadingError = (event: WebViewEvent) => { event.persist(); // persist this event because we need to store it const { onError, onLoadEnd } = this.props; onError && onError(event); @@ -403,7 +408,7 @@ class WebView extends React.Component { }); }; - _onLoadingFinish = (event) => { + _onLoadingFinish = (event: WebViewEvent) => { const { onLoad, onLoadEnd } = this.props; onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); @@ -413,12 +418,12 @@ class WebView extends React.Component { this._updateNavigationState(event); }; - _onMessage = (event) => { + _onMessage = (event: WebViewEvent) => { const { onMessage } = this.props; onMessage && onMessage(event); }; - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: WebViewSharedProps) { if (!(prevProps.useWebKit && this.props.useWebKit)) { return; } @@ -434,7 +439,7 @@ class WebView extends React.Component { } } - _showRedboxOnPropChanges(prevProps, propName) { + _showRedboxOnPropChanges(prevProps, propName: string) { if (this.props[propName] !== prevProps[propName]) { console.error( `Changes to property ${propName} do nothing after the initial render.`, @@ -443,16 +448,8 @@ class WebView extends React.Component { } } -const RCTWebView = requireNativeComponent( - 'RCTWebView', - WebView, - WebView.extraNativeComponentConfig, -); -const RCTWKWebView = requireNativeComponent( - 'RCTWKWebView', - WebView, - WebView.extraNativeComponentConfig, -); +const RCTWebView = requireNativeComponent('RCTWebView'); +const RCTWKWebView = requireNativeComponent('RCTWKWebView'); const styles = StyleSheet.create({ container: { diff --git a/js/WebViewShared.js b/js/WebViewShared.js index a40caa3..0033207 100644 --- a/js/WebViewShared.js +++ b/js/WebViewShared.js @@ -14,11 +14,11 @@ const escapeStringRegexp = require('escape-string-regexp'); const WebViewShared = { defaultOriginWhitelist: ['http://*', 'https://*'], - extractOrigin: (url) => { + extractOrigin: (url: string) => { const result = /^[A-Za-z0-9]+:(\/\/)?[^/]*/.exec(url); - return result === null ? null : result[0]; + return result === null ? '' : result[0]; }, - originWhitelistToRegex: (originWhitelist) => { + originWhitelistToRegex: (originWhitelist: string) => { return escapeStringRegexp(originWhitelist).replace(/\\\*/g, '.*'); }, }; diff --git a/js/WebViewTypes.js b/js/WebViewTypes.js new file mode 100644 index 0000000..217ae68 --- /dev/null +++ b/js/WebViewTypes.js @@ -0,0 +1,332 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import type {Node, Element} from 'react'; +import type {EdgeInsetsProp} from 'EdgeInsetsPropType'; +import type {ViewStyleProp} from 'StyleSheet'; +import type {ViewProps} from 'ViewPropTypes'; + +export type WebViewErrorEvent = { + domain: any, + code: any, + description: any, +}; + +export type WebViewEvent = Object; + +export type DataDetectorTypes = + | 'phoneNumber' + | 'link' + | 'address' + | 'calendarEvent' + | 'trackingNumber' + | 'flightNumber' + | 'lookupSuggestion' + | 'none' + | 'all'; + +export type WebViewSourceUri = {| + /** + * The URI to load in the `WebView`. Can be a local or remote file. + */ + uri?: ?string, + + /** + * The HTTP Method to use. Defaults to GET if not specified. + * NOTE: On Android, only GET and POST are supported. + */ + method?: string, + + /** + * Additional HTTP headers to send with the request. + * NOTE: On Android, this can only be used with GET requests. + */ + headers?: Object, + + /** + * The HTTP body to send with the request. This must be a valid + * UTF-8 string, and will be sent exactly as specified, with no + * additional encoding (e.g. URL-escaping or base64) applied. + * NOTE: On Android, this can only be used with POST requests. + */ + body?: string, +|}; + +export type WebViewSourceHtml = {| + /** + * A static HTML page to display in the WebView. + */ + html?: ?string, + /** + * The base URL to be used for any relative links in the HTML. + */ + baseUrl?: ?string, +|}; + +export type WebViewSource = WebViewSourceUri | WebViewSourceHtml; + +export type WebViewNativeConfig = $ReadOnly<{| + /* + * The native component used to render the WebView. + */ + component?: ?any, + /* + * Set props directly on the native component WebView. Enables custom props which the + * original WebView doesn't pass through. + */ + props?: ?Object, + /* + * Set the ViewManager to use for communication with the native side. + * @platform ios + */ + viewManager?: ?Object, +|}>; + +export type WebViewSharedProps = $ReadOnly<{| + ...ViewProps, + /** + * Deprecated. Use `source` instead. + */ + url?: ?string, + /** + * Deprecated. Use `source` instead. + */ + html?: ?string, + + /** + * Loads static html or a uri (with optional headers) in the WebView. + */ + source?: ?WebViewSource, + + /** + * If true, use WKWebView instead of UIWebView. + * @platform ios + */ + useWebKit?: ?boolean, + + /** + * Function that returns a view to show if there's an error. + */ + renderError: (errorDomain: any, errorCode: any, errorDesc: any) => Element, // view to show if there's an error + + /** + * Function that returns a loading indicator. + */ + renderLoading: () => Element, + + /** + * Function that is invoked when the `WebView` has finished loading. + */ + onLoad: (event: WebViewEvent) => any, + + /** + * Function that is invoked when the `WebView` load succeeds or fails. + */ + onLoadEnd: (event: WebViewEvent) => any, + + /** + * Function that is invoked when the `WebView` starts loading. + */ + onLoadStart: (event: WebViewEvent) => any, + + /** + * Function that is invoked when the `WebView` load fails. + */ + onError: (event: WebViewEvent) => any, + + /** + * Boolean value that determines whether the web view bounces + * when it reaches the edge of the content. The default value is `true`. + * @platform ios + */ + bounces?: ?boolean, + + /** + * A floating-point number that determines how quickly the scroll view + * decelerates after the user lifts their finger. You may also use the + * string shortcuts `"normal"` and `"fast"` which match the underlying iOS + * settings for `UIScrollViewDecelerationRateNormal` and + * `UIScrollViewDecelerationRateFast` respectively: + * + * - normal: 0.998 + * - fast: 0.99 (the default for iOS web view) + * @platform ios + */ + decelerationRate?: ?('fast' | 'normal' | number), + + /** + * Boolean value that determines whether scrolling is enabled in the + * `WebView`. The default value is `true`. + * @platform ios + */ + scrollEnabled?: ?boolean, + + /** + * Controls whether to adjust the content inset for web views that are + * placed behind a navigation bar, tab bar, or toolbar. The default value + * is `true`. + */ + automaticallyAdjustContentInsets?: ?boolean, + + /** + * The amount by which the web view content is inset from the edges of + * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}. + * @platform ios + */ + contentInset?: ?EdgeInsetsProp, + + /** + * Function that is invoked when the `WebView` loading starts or ends. + */ + onNavigationStateChange?: (event: WebViewEvent) => any, + + /** + * A function that is invoked when the webview calls `window.postMessage`. + * Setting this property will inject a `postMessage` global into your + * webview, but will still call pre-existing values of `postMessage`. + * + * `window.postMessage` accepts one argument, `data`, which will be + * available on the event object, `event.nativeEvent.data`. `data` + * must be a string. + */ + onMessage?: (event: WebViewEvent) => any, + + /** + * Boolean value that forces the `WebView` to show the loading view + * on the first load. + */ + startInLoadingState?: ?boolean, + + /** + * Determines the types of data converted to clickable URLs in the web view's content. + * By default only phone numbers are detected. + * + * You can provide one type or an array of many types. + * + * Possible values for `dataDetectorTypes` are: + * + * - `'phoneNumber'` + * - `'link'` + * - `'address'` + * - `'calendarEvent'` + * - `'none'` + * - `'all'` + * + * With the new WebKit implementation, we have three new values: + * - `'trackingNumber'`, + * - `'flightNumber'`, + * - `'lookupSuggestion'`, + * + * @platform ios + */ + dataDetectorTypes?: + | ?DataDetectorTypes + | $ReadOnlyArray, + + /** + * Boolean value to enable JavaScript in the `WebView`. Used on Android only + * as JavaScript is enabled by default on iOS. The default value is `true`. + * @platform android + */ + javaScriptEnabled?: ?boolean, + + /** + * Boolean value to enable third party cookies in the `WebView`. Used on + * Android Lollipop and above only as third party cookies are enabled by + * default on Android Kitkat and below and on iOS. The default value is `true`. + * @platform android + */ + thirdPartyCookiesEnabled?: ?boolean, + + /** + * Boolean value to control whether DOM Storage is enabled. Used only in + * Android. + * @platform android + */ + domStorageEnabled?: ?boolean, + + /** + * Set this to provide JavaScript that will be injected into the web page + * when the view loads. + */ + injectedJavaScript?: ?string, + + /** + * Sets the user-agent for the `WebView`. + * @platform android + */ + userAgent?: ?string, + + /** + * Boolean that controls whether the web content is scaled to fit + * the view and enables the user to change the scale. The default value + * is `true`. + * + * On iOS, when `useWebKit=true`, this prop will not work. + */ + scalesPageToFit?: ?boolean, + + /** + * Function that allows custom handling of any web view requests. Return + * `true` from the function to continue loading the request and `false` + * to stop loading. + * @platform ios + */ + onShouldStartLoadWithRequest?: (event: WebViewEvent) => any, + + /** + * Boolean that determines whether HTML5 videos play inline or use the + * native full-screen controller. The default value is `false`. + * + * **NOTE** : In order for video to play inline, not only does this + * property need to be set to `true`, but the video element in the HTML + * document must also include the `webkit-playsinline` attribute. + * @platform ios + */ + allowsInlineMediaPlayback?: ?boolean, + + /** + * Boolean that determines whether HTML5 audio and video requires the user + * to tap them before they start playing. The default value is `true`. + */ + mediaPlaybackRequiresUserAction?: ?boolean, + + /** + * List of origin strings to allow being navigated to. The strings allow + * wildcards and get matched against *just* the origin (not the full URL). + * If the user taps to navigate to a new page but the new page is not in + * this whitelist, we will open the URL in Safari. + * The default whitelisted origins are "http://*" and "https://*". + */ + originWhitelist?: $ReadOnlyArray, + + /** + * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin. + * + * Possible values for `mixedContentMode` are: + * + * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin. + * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. + * - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content. + * @platform android + */ + mixedContentMode?: ?('never' | 'always' | 'compatibility'), + + /** + * Override the native component used to render the WebView. Enables a custom native + * WebView which uses the same JavaScript as the original WebView. + */ + nativeConfig?: ?WebViewNativeConfig, + + style?: ViewStyleProp, + children: Node, +|}>; diff --git a/package.json b/package.json index 63936fb..b1ca5e0 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,20 @@ "author": "Jamon Holmgren ", "version": "0.1.0", "homepage": "https://github.com/react-native-community/react-native-webview#readme", + "scripts": { + "flow": "flow check", + "flow-android": "flow check --flowconfig-name .flowconfig.android" + }, "peerDependencies": { "react": "^16.0", - "react-native": "^0.56" + "react-native": "~0.57.0-rc.4" }, "dependencies": { "escape-string-regexp": "^1.0.5", "fbjs": "^0.8.17" + }, + "devDependencies": { + "flow-bin": "^0.80.0", + "react-native": "~0.57.0-rc.4" } } From 795dffb6a757965f0bedbc5a89522111fcdaa4ff Mon Sep 17 00:00:00 2001 From: empyrical Date: Mon, 10 Sep 2018 12:13:20 -0600 Subject: [PATCH 2/5] Clean up flow types --- js/WKWebView.ios.js | 14 +++++--- js/WebView.android.js | 26 ++++++++------- js/WebView.ios.js | 21 ++++++------ js/WebViewTypes.js | 75 +++++++++++++++++++++++++++++++------------ 4 files changed, 91 insertions(+), 45 deletions(-) diff --git a/js/WKWebView.ios.js b/js/WKWebView.ios.js index 9d36598..d51ccc0 100644 --- a/js/WKWebView.ios.js +++ b/js/WKWebView.ios.js @@ -13,13 +13,17 @@ import React from 'react'; import { requireNativeComponent } from 'react-native'; +import type {DataDetectorTypes} from './WebViewTypes'; + const RCTWKWebView = requireNativeComponent('RCTWKWebView'); -type RCTWKWebViewProps = { - allowsInlineMediaPlayback?: boolean, - mediaPlaybackRequiresUserAction?: boolean, - dataDetectorTypes?: boolean, -}; +type RCTWKWebViewProps = $ReadOnly<{| + allowsInlineMediaPlayback?: ?boolean, + mediaPlaybackRequiresUserAction?: ?boolean, + dataDetectorTypes?: + | ?DataDetectorTypes + | $ReadOnlyArray, +|}>; class WKWebView extends React.Component { componentWillReceiveProps(nextProps: RCTWKWebViewProps) { diff --git a/js/WebView.android.js b/js/WebView.android.js index 834306f..49f4bfc 100644 --- a/js/WebView.android.js +++ b/js/WebView.android.js @@ -27,8 +27,12 @@ import keyMirror from 'fbjs/lib/keyMirror'; import WebViewShared from './WebViewShared'; import type { - WebViewErrorEvent, WebViewEvent, + WebViewError, + WebViewErrorEvent, + WebViewMessageEvent, + WebViewNavigation, + WebViewNavigationEvent, WebViewSharedProps, WebViewSource, } from './WebViewTypes'; @@ -51,14 +55,14 @@ const defaultRenderLoading = () => ( type State = {| viewState: WebViewState, - lastErrorEvent: ?WebViewErrorEvent, + lastErrorEvent: ?WebViewError, startInLoadingState: boolean, |}; type WebViewPropsAndroid = $ReadOnly<{| ...WebViewSharedProps, - onNavigationStateChange?: (event: WebViewEvent) => any, - onContentSizeChange?: (event: WebViewEvent) => any, + onNavigationStateChange?: (event: WebViewNavigation) => mixed, + onContentSizeChange?: (event: WebViewEvent) => mixed, /** * Sets whether Geolocation is enabled. The default is false. @@ -105,7 +109,7 @@ class WebView extends React.Component { state = { viewState: WebViewState.IDLE, - lastErrorEvent: (null: ?WebViewErrorEvent), + lastErrorEvent: null, startInLoadingState: true, }; @@ -145,7 +149,7 @@ class WebView extends React.Component { webViewStyles.push(styles.hidden); } - let source = this.props.source || ({}: WebViewSource); + let source: WebViewSource = this.props.source || {}; if (!this.props.source && this.props.html) { source = { html: this.props.html }; } else if (!this.props.source && this.props.url) { @@ -275,7 +279,7 @@ class WebView extends React.Component { * We return an event with a bunch of fields including: * url, title, loading, canGoBack, canGoForward */ - updateNavigationState = (event: WebViewEvent) => { + updateNavigationState = (event: WebViewNavigationEvent) => { if (this.props.onNavigationStateChange) { this.props.onNavigationStateChange(event.nativeEvent); } @@ -285,13 +289,13 @@ class WebView extends React.Component { return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); }; - onLoadingStart = (event: WebViewEvent) => { + onLoadingStart = (event: WebViewNavigationEvent) => { const onLoadStart = this.props.onLoadStart; onLoadStart && onLoadStart(event); this.updateNavigationState(event); }; - onLoadingError = (event: WebViewEvent) => { + onLoadingError = (event: WebViewErrorEvent) => { event.persist(); // persist this event because we need to store it const { onError, onLoadEnd } = this.props; onError && onError(event); @@ -304,7 +308,7 @@ class WebView extends React.Component { }); }; - onLoadingFinish = (event: WebViewEvent) => { + onLoadingFinish = (event: WebViewNavigationEvent) => { const { onLoad, onLoadEnd } = this.props; onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); @@ -314,7 +318,7 @@ class WebView extends React.Component { this.updateNavigationState(event); }; - onMessage = (event: WebViewEvent) => { + onMessage = (event: WebViewMessageEvent) => { const { onMessage } = this.props; onMessage && onMessage(event); }; diff --git a/js/WebView.ios.js b/js/WebView.ios.js index 529e93e..1b1c23a 100644 --- a/js/WebView.ios.js +++ b/js/WebView.ios.js @@ -30,8 +30,11 @@ import keyMirror from 'fbjs/lib/keyMirror'; import WebViewShared from './WebViewShared'; import type { - WebViewErrorEvent, WebViewEvent, + WebViewError, + WebViewErrorEvent, + WebViewMessageEvent, + WebViewNavigationEvent, WebViewSharedProps, WebViewSource, } from './WebViewTypes'; @@ -73,7 +76,7 @@ const JSNavigationScheme = 'react-js-navigation'; type State = {| viewState: WebViewState, - lastErrorEvent: ?WebViewErrorEvent, + lastErrorEvent: ?WebViewError, startInLoadingState: boolean, |}; @@ -136,7 +139,7 @@ class WebView extends React.Component { state = { viewState: WebViewState.IDLE, - lastErrorEvent: (null: ?WebViewErrorEvent), + lastErrorEvent: null, startInLoadingState: true, }; @@ -232,7 +235,7 @@ class WebView extends React.Component { this.props.decelerationRate, ); - let source = this.props.source || ({}: WebViewSource); + let source: WebViewSource = this.props.source || {}; if (!this.props.source && this.props.html) { source = { html: this.props.html }; } else if (!this.props.source && this.props.url) { @@ -376,7 +379,7 @@ class WebView extends React.Component { * We return an event with a bunch of fields including: * url, title, loading, canGoBack, canGoForward */ - _updateNavigationState = (event: WebViewEvent) => { + _updateNavigationState = (event: WebViewNavigationEvent) => { if (this.props.onNavigationStateChange) { this.props.onNavigationStateChange(event.nativeEvent); } @@ -389,13 +392,13 @@ class WebView extends React.Component { return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEW_REF]); }; - _onLoadingStart = (event: WebViewEvent) => { + _onLoadingStart = (event: WebViewNavigationEvent) => { const onLoadStart = this.props.onLoadStart; onLoadStart && onLoadStart(event); this._updateNavigationState(event); }; - _onLoadingError = (event: WebViewEvent) => { + _onLoadingError = (event: WebViewErrorEvent) => { event.persist(); // persist this event because we need to store it const { onError, onLoadEnd } = this.props; onError && onError(event); @@ -408,7 +411,7 @@ class WebView extends React.Component { }); }; - _onLoadingFinish = (event: WebViewEvent) => { + _onLoadingFinish = (event: WebViewNavigationEvent) => { const { onLoad, onLoadEnd } = this.props; onLoad && onLoad(event); onLoadEnd && onLoadEnd(event); @@ -418,7 +421,7 @@ class WebView extends React.Component { this._updateNavigationState(event); }; - _onMessage = (event: WebViewEvent) => { + _onMessage = (event: WebViewMessageEvent) => { const { onMessage } = this.props; onMessage && onMessage(event); }; diff --git a/js/WebViewTypes.js b/js/WebViewTypes.js index 217ae68..87627d2 100644 --- a/js/WebViewTypes.js +++ b/js/WebViewTypes.js @@ -10,18 +10,53 @@ 'use strict'; -import type {Node, Element} from 'react'; +import type {Node, Element, ComponentType} from 'react'; +import type {SyntheticEvent} from 'CoreEventTypes'; import type {EdgeInsetsProp} from 'EdgeInsetsPropType'; import type {ViewStyleProp} from 'StyleSheet'; import type {ViewProps} from 'ViewPropTypes'; -export type WebViewErrorEvent = { - domain: any, - code: any, - description: any, -}; +export type WebViewNativeEvent = $ReadOnly<{| + url: string, + loading: boolean, + title: string, + canGoBack: boolean, + canGoForward: boolean, +|}>; -export type WebViewEvent = Object; +export type WebViewNavigation = $ReadOnly<{| + ...WebViewNativeEvent, + navigationType: + | 'click' + | 'formsubmit' + | 'backforward' + | 'reload' + | 'formresubmit' + | 'other', +|}>; + +export type WebViewMessage = $ReadOnly<{| + ...WebViewNativeEvent, + data: string, +|}>; + +export type WebViewError = $ReadOnly<{| + ...WebViewNativeEvent, + /** + * `domain` is only used on iOS + */ + domain: ?string, + code: number, + description: string, +|}>; + +export type WebViewEvent = SyntheticEvent; + +export type WebViewNavigationEvent = SyntheticEvent; + +export type WebViewMessageEvent = SyntheticEvent; + +export type WebViewErrorEvent = SyntheticEvent; export type DataDetectorTypes = | 'phoneNumber' @@ -34,7 +69,7 @@ export type DataDetectorTypes = | 'none' | 'all'; -export type WebViewSourceUri = {| +export type WebViewSourceUri = $ReadOnly<{| /** * The URI to load in the `WebView`. Can be a local or remote file. */ @@ -59,9 +94,9 @@ export type WebViewSourceUri = {| * NOTE: On Android, this can only be used with POST requests. */ body?: string, -|}; +|}>; -export type WebViewSourceHtml = {| +export type WebViewSourceHtml = $ReadOnly<{| /** * A static HTML page to display in the WebView. */ @@ -70,7 +105,7 @@ export type WebViewSourceHtml = {| * The base URL to be used for any relative links in the HTML. */ baseUrl?: ?string, -|}; +|}>; export type WebViewSource = WebViewSourceUri | WebViewSourceHtml; @@ -78,7 +113,7 @@ export type WebViewNativeConfig = $ReadOnly<{| /* * The native component used to render the WebView. */ - component?: ?any, + component?: ComponentType, /* * Set props directly on the native component WebView. Enables custom props which the * original WebView doesn't pass through. @@ -116,7 +151,7 @@ export type WebViewSharedProps = $ReadOnly<{| /** * Function that returns a view to show if there's an error. */ - renderError: (errorDomain: any, errorCode: any, errorDesc: any) => Element, // view to show if there's an error + renderError: (errorDomain: ?string, errorCode: number, errorDesc: string) => Element, // view to show if there's an error /** * Function that returns a loading indicator. @@ -126,22 +161,22 @@ export type WebViewSharedProps = $ReadOnly<{| /** * Function that is invoked when the `WebView` has finished loading. */ - onLoad: (event: WebViewEvent) => any, + onLoad: (event: WebViewNavigationEvent) => mixed, /** * Function that is invoked when the `WebView` load succeeds or fails. */ - onLoadEnd: (event: WebViewEvent) => any, + onLoadEnd: (event: WebViewNavigationEvent | WebViewErrorEvent) => mixed, /** * Function that is invoked when the `WebView` starts loading. */ - onLoadStart: (event: WebViewEvent) => any, + onLoadStart: (event: WebViewNavigationEvent) => mixed, /** * Function that is invoked when the `WebView` load fails. */ - onError: (event: WebViewEvent) => any, + onError: (event: WebViewErrorEvent) => mixed, /** * Boolean value that determines whether the web view bounces @@ -187,7 +222,7 @@ export type WebViewSharedProps = $ReadOnly<{| /** * Function that is invoked when the `WebView` loading starts or ends. */ - onNavigationStateChange?: (event: WebViewEvent) => any, + onNavigationStateChange?: (event: WebViewNavigation) => mixed, /** * A function that is invoked when the webview calls `window.postMessage`. @@ -198,7 +233,7 @@ export type WebViewSharedProps = $ReadOnly<{| * available on the event object, `event.nativeEvent.data`. `data` * must be a string. */ - onMessage?: (event: WebViewEvent) => any, + onMessage?: (event: WebViewMessageEvent) => mixed, /** * Boolean value that forces the `WebView` to show the loading view @@ -281,7 +316,7 @@ export type WebViewSharedProps = $ReadOnly<{| * to stop loading. * @platform ios */ - onShouldStartLoadWithRequest?: (event: WebViewEvent) => any, + onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed, /** * Boolean that determines whether HTML5 videos play inline or use the From 22686d0371916806bb9088141f5b753b812f7883 Mon Sep 17 00:00:00 2001 From: empyrical Date: Mon, 10 Sep 2018 18:34:46 -0600 Subject: [PATCH 3/5] Re-organize platform-specific types --- js/WebView.android.js | 38 +----- js/WebViewTypes.js | 289 ++++++++++++++++++++++++------------------ 2 files changed, 165 insertions(+), 162 deletions(-) diff --git a/js/WebView.android.js b/js/WebView.android.js index 49f4bfc..d2c8338 100644 --- a/js/WebView.android.js +++ b/js/WebView.android.js @@ -59,46 +59,10 @@ type State = {| startInLoadingState: boolean, |}; -type WebViewPropsAndroid = $ReadOnly<{| - ...WebViewSharedProps, - onNavigationStateChange?: (event: WebViewNavigation) => mixed, - onContentSizeChange?: (event: WebViewEvent) => mixed, - - /** - * Sets whether Geolocation is enabled. The default is false. - * @platform android - */ - geolocationEnabled?: ?boolean, - - /** - * Boolean that sets whether JavaScript running in the context of a file - * scheme URL should be allowed to access content from any origin. - * Including accessing content from other file scheme URLs - * @platform android - */ - allowUniversalAccessFromFileURLs?: ?boolean, - - /** - * Used on Android only, controls whether form autocomplete data should be saved - * @platform android - */ - saveFormDataDisabled?: ?boolean, - - /* - * Used on Android only, controls whether the given list of URL prefixes should - * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a - * default activity intent for those URL instead of loading it within the webview. - * Use this to list URLs that WebView cannot handle, e.g. a PDF url. - * @platform android - */ - urlPrefixesForDefaultIntent?: $ReadOnlyArray, - -|}>; - /** * Renders a native WebView. */ -class WebView extends React.Component { +class WebView extends React.Component { static defaultProps = { javaScriptEnabled: true, thirdPartyCookiesEnabled: true, diff --git a/js/WebViewTypes.js b/js/WebViewTypes.js index 87627d2..ca3e31e 100644 --- a/js/WebViewTypes.js +++ b/js/WebViewTypes.js @@ -11,6 +11,7 @@ 'use strict'; import type {Node, Element, ComponentType} from 'react'; + import type {SyntheticEvent} from 'CoreEventTypes'; import type {EdgeInsetsProp} from 'EdgeInsetsPropType'; import type {ViewStyleProp} from 'StyleSheet'; @@ -126,8 +127,171 @@ export type WebViewNativeConfig = $ReadOnly<{| viewManager?: ?Object, |}>; +export type IOSWebViewProps = $ReadOnly<{| + /** + * If true, use WKWebView instead of UIWebView. + * @platform ios + */ + useWebKit?: ?boolean, + + /** + * Boolean value that determines whether the web view bounces + * when it reaches the edge of the content. The default value is `true`. + * @platform ios + */ + bounces?: ?boolean, + + /** + * A floating-point number that determines how quickly the scroll view + * decelerates after the user lifts their finger. You may also use the + * string shortcuts `"normal"` and `"fast"` which match the underlying iOS + * settings for `UIScrollViewDecelerationRateNormal` and + * `UIScrollViewDecelerationRateFast` respectively: + * + * - normal: 0.998 + * - fast: 0.99 (the default for iOS web view) + * @platform ios + */ + decelerationRate?: ?('fast' | 'normal' | number), + + /** + * Boolean value that determines whether scrolling is enabled in the + * `WebView`. The default value is `true`. + * @platform ios + */ + scrollEnabled?: ?boolean, + + /** + * The amount by which the web view content is inset from the edges of + * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}. + * @platform ios + */ + contentInset?: ?EdgeInsetsProp, + + /** + * Determines the types of data converted to clickable URLs in the web view's content. + * By default only phone numbers are detected. + * + * You can provide one type or an array of many types. + * + * Possible values for `dataDetectorTypes` are: + * + * - `'phoneNumber'` + * - `'link'` + * - `'address'` + * - `'calendarEvent'` + * - `'none'` + * - `'all'` + * + * With the new WebKit implementation, we have three new values: + * - `'trackingNumber'`, + * - `'flightNumber'`, + * - `'lookupSuggestion'`, + * + * @platform ios + */ + dataDetectorTypes?: + | ?DataDetectorTypes + | $ReadOnlyArray, + + /** + * Function that allows custom handling of any web view requests. Return + * `true` from the function to continue loading the request and `false` + * to stop loading. + * @platform ios + */ + onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed, + + /** + * Boolean that determines whether HTML5 videos play inline or use the + * native full-screen controller. The default value is `false`. + * + * **NOTE** : In order for video to play inline, not only does this + * property need to be set to `true`, but the video element in the HTML + * document must also include the `webkit-playsinline` attribute. + * @platform ios + */ + allowsInlineMediaPlayback?: ?boolean, +|}>; + +export type AndroidWebViewProps = $ReadOnly<{| + onNavigationStateChange?: (event: WebViewNavigation) => mixed, + onContentSizeChange?: (event: WebViewEvent) => mixed, + + /** + * Sets whether Geolocation is enabled. The default is false. + * @platform android + */ + geolocationEnabled?: ?boolean, + + /** + * Boolean that sets whether JavaScript running in the context of a file + * scheme URL should be allowed to access content from any origin. + * Including accessing content from other file scheme URLs + * @platform android + */ + allowUniversalAccessFromFileURLs?: ?boolean, + + /** + * Used on Android only, controls whether form autocomplete data should be saved + * @platform android + */ + saveFormDataDisabled?: ?boolean, + + /* + * Used on Android only, controls whether the given list of URL prefixes should + * make {@link com.facebook.react.views.webview.ReactWebViewClient} to launch a + * default activity intent for those URL instead of loading it within the webview. + * Use this to list URLs that WebView cannot handle, e.g. a PDF url. + * @platform android + */ + urlPrefixesForDefaultIntent?: $ReadOnlyArray, + + /** + * Boolean value to enable JavaScript in the `WebView`. Used on Android only + * as JavaScript is enabled by default on iOS. The default value is `true`. + * @platform android + */ + javaScriptEnabled?: ?boolean, + + /** + * Boolean value to enable third party cookies in the `WebView`. Used on + * Android Lollipop and above only as third party cookies are enabled by + * default on Android Kitkat and below and on iOS. The default value is `true`. + * @platform android + */ + thirdPartyCookiesEnabled?: ?boolean, + + /** + * Boolean value to control whether DOM Storage is enabled. Used only in + * Android. + * @platform android + */ + domStorageEnabled?: ?boolean, + + /** + * Sets the user-agent for the `WebView`. + * @platform android + */ + userAgent?: ?string, + + /** + * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin. + * + * Possible values for `mixedContentMode` are: + * + * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin. + * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. + * - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content. + * @platform android + */ + mixedContentMode?: ?('never' | 'always' | 'compatibility'), +|}>; + export type WebViewSharedProps = $ReadOnly<{| ...ViewProps, + ...IOSWebViewProps, + ...AndroidWebViewProps, /** * Deprecated. Use `source` instead. */ @@ -142,12 +306,6 @@ export type WebViewSharedProps = $ReadOnly<{| */ source?: ?WebViewSource, - /** - * If true, use WKWebView instead of UIWebView. - * @platform ios - */ - useWebKit?: ?boolean, - /** * Function that returns a view to show if there's an error. */ @@ -178,33 +336,6 @@ export type WebViewSharedProps = $ReadOnly<{| */ onError: (event: WebViewErrorEvent) => mixed, - /** - * Boolean value that determines whether the web view bounces - * when it reaches the edge of the content. The default value is `true`. - * @platform ios - */ - bounces?: ?boolean, - - /** - * A floating-point number that determines how quickly the scroll view - * decelerates after the user lifts their finger. You may also use the - * string shortcuts `"normal"` and `"fast"` which match the underlying iOS - * settings for `UIScrollViewDecelerationRateNormal` and - * `UIScrollViewDecelerationRateFast` respectively: - * - * - normal: 0.998 - * - fast: 0.99 (the default for iOS web view) - * @platform ios - */ - decelerationRate?: ?('fast' | 'normal' | number), - - /** - * Boolean value that determines whether scrolling is enabled in the - * `WebView`. The default value is `true`. - * @platform ios - */ - scrollEnabled?: ?boolean, - /** * Controls whether to adjust the content inset for web views that are * placed behind a navigation bar, tab bar, or toolbar. The default value @@ -212,13 +343,6 @@ export type WebViewSharedProps = $ReadOnly<{| */ automaticallyAdjustContentInsets?: ?boolean, - /** - * The amount by which the web view content is inset from the edges of - * the scroll view. Defaults to {top: 0, left: 0, bottom: 0, right: 0}. - * @platform ios - */ - contentInset?: ?EdgeInsetsProp, - /** * Function that is invoked when the `WebView` loading starts or ends. */ @@ -241,66 +365,12 @@ export type WebViewSharedProps = $ReadOnly<{| */ startInLoadingState?: ?boolean, - /** - * Determines the types of data converted to clickable URLs in the web view's content. - * By default only phone numbers are detected. - * - * You can provide one type or an array of many types. - * - * Possible values for `dataDetectorTypes` are: - * - * - `'phoneNumber'` - * - `'link'` - * - `'address'` - * - `'calendarEvent'` - * - `'none'` - * - `'all'` - * - * With the new WebKit implementation, we have three new values: - * - `'trackingNumber'`, - * - `'flightNumber'`, - * - `'lookupSuggestion'`, - * - * @platform ios - */ - dataDetectorTypes?: - | ?DataDetectorTypes - | $ReadOnlyArray, - - /** - * Boolean value to enable JavaScript in the `WebView`. Used on Android only - * as JavaScript is enabled by default on iOS. The default value is `true`. - * @platform android - */ - javaScriptEnabled?: ?boolean, - - /** - * Boolean value to enable third party cookies in the `WebView`. Used on - * Android Lollipop and above only as third party cookies are enabled by - * default on Android Kitkat and below and on iOS. The default value is `true`. - * @platform android - */ - thirdPartyCookiesEnabled?: ?boolean, - - /** - * Boolean value to control whether DOM Storage is enabled. Used only in - * Android. - * @platform android - */ - domStorageEnabled?: ?boolean, - /** * Set this to provide JavaScript that will be injected into the web page * when the view loads. */ injectedJavaScript?: ?string, - /** - * Sets the user-agent for the `WebView`. - * @platform android - */ - userAgent?: ?string, - /** * Boolean that controls whether the web content is scaled to fit * the view and enables the user to change the scale. The default value @@ -310,25 +380,6 @@ export type WebViewSharedProps = $ReadOnly<{| */ scalesPageToFit?: ?boolean, - /** - * Function that allows custom handling of any web view requests. Return - * `true` from the function to continue loading the request and `false` - * to stop loading. - * @platform ios - */ - onShouldStartLoadWithRequest?: (event: WebViewEvent) => mixed, - - /** - * Boolean that determines whether HTML5 videos play inline or use the - * native full-screen controller. The default value is `false`. - * - * **NOTE** : In order for video to play inline, not only does this - * property need to be set to `true`, but the video element in the HTML - * document must also include the `webkit-playsinline` attribute. - * @platform ios - */ - allowsInlineMediaPlayback?: ?boolean, - /** * Boolean that determines whether HTML5 audio and video requires the user * to tap them before they start playing. The default value is `true`. @@ -344,18 +395,6 @@ export type WebViewSharedProps = $ReadOnly<{| */ originWhitelist?: $ReadOnlyArray, - /** - * Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin. - * - * Possible values for `mixedContentMode` are: - * - * - `'never'` (default) - WebView will not allow a secure origin to load content from an insecure origin. - * - `'always'` - WebView will allow a secure origin to load content from any other origin, even if that origin is insecure. - * - `'compatibility'` - WebView will attempt to be compatible with the approach of a modern web browser with regard to mixed content. - * @platform android - */ - mixedContentMode?: ?('never' | 'always' | 'compatibility'), - /** * Override the native component used to render the WebView. Enables a custom native * WebView which uses the same JavaScript as the original WebView. From 66ecb8b3241156a781ffab40b443f78be8e2edfe Mon Sep 17 00:00:00 2001 From: empyrical Date: Mon, 10 Sep 2018 18:44:25 -0600 Subject: [PATCH 4/5] Add note to README.md about flow types --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 27be3ba..b97ffda 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,13 @@ Simply install React Native WebView and then use it in place of the core WebView ### Contributor Notes -* I've removed all PropTypes for now. Instead, we'll be moving toward Flow or TypeScript at a later date +* I've removed all PropTypes for now. Instead, we'll be using Flow types. TypeScript types will be added at a later date. * UIWebView is not tested fully and you will encounter some yellow warning boxes. Since it is deprecated, we don't intend to put a lot of time into supporting it, but feel free to submit PRs if you have a special use case. Note that you will need to specify `useWebKit={false}` to use UIWebView +* After pulling this repo and installing all dependencies, you can run flow on iOS and Android-specific files using the commands: + * `yarn flow` or `npm run flow` for iOS + * `yarn flow-android` or `npm run flow-android` for Android +* If you want to add another React Native platform to this repository, you will need to create another `.flowconfig` for it. If your platform is `example`, copy the main flowconfig and rename it to `.flowconfig.example`. Then edit the config to ignore other platforms, and add `.*/*[.]example.js` to the ignore lists of the other platforms. Then add an entry to `package.json` like this: + * ` "flow-example": "flow check --flowconfig-name .flowconfig.example"` ## Maintainers From c3e2443d243fba23e760e8fc4fa308a7d2c18355 Mon Sep 17 00:00:00 2001 From: empyrical Date: Mon, 10 Sep 2018 21:59:07 -0600 Subject: [PATCH 5/5] Restore minimum react native version ^0.56 --- README.md | 1 + package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b97ffda..c116864 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Simply install React Native WebView and then use it in place of the core WebView * `yarn flow-android` or `npm run flow-android` for Android * If you want to add another React Native platform to this repository, you will need to create another `.flowconfig` for it. If your platform is `example`, copy the main flowconfig and rename it to `.flowconfig.example`. Then edit the config to ignore other platforms, and add `.*/*[.]example.js` to the ignore lists of the other platforms. Then add an entry to `package.json` like this: * ` "flow-example": "flow check --flowconfig-name .flowconfig.example"` +* Currently you need to install React Native 0.57 to be able to test these types - `flow check` will not pass aganst 0.56. ## Maintainers diff --git a/package.json b/package.json index b1ca5e0..24ba41a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "peerDependencies": { "react": "^16.0", - "react-native": "~0.57.0-rc.4" + "react-native": "^0.56" }, "dependencies": { "escape-string-regexp": "^1.0.5", @@ -19,6 +19,6 @@ }, "devDependencies": { "flow-bin": "^0.80.0", - "react-native": "~0.57.0-rc.4" + "react-native": "^0.56" } }