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" } }