Skip to content

Commit

Permalink
Add WebView Flow types
Browse files Browse the repository at this point in the history
  • Loading branch information
empyrical committed Sep 11, 2018
1 parent 13ab5d6 commit 133f1e3
Show file tree
Hide file tree
Showing 8 changed files with 627 additions and 57 deletions.
88 changes: 88 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -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=<PROJECT_ROOT>/js/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
; Surpress error `Duplicate module provider` until the slimmening is done
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
module.system.haste.paths.blacklist=<PROJECT_ROOT>/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\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError

[strict]
88 changes: 88 additions & 0 deletions .flowconfig.android
Original file line number Diff line number Diff line change
@@ -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=<PROJECT_ROOT>/js/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/Libraries/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/RNTester/.*
module.system.haste.paths.whitelist=<PROJECT_ROOT>/node_modules/react-native/IntegrationTests/.*
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Animated/src/polyfills/.*
; Surpress error `Duplicate module provider` until the slimmening is done
module.system.haste.paths.blacklist=<PROJECT_ROOT>/node_modules/react-native/Libraries/Components/WebView/*.
module.system.haste.paths.blacklist=<PROJECT_ROOT>/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\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*[react_native\\(_android\\)?_oss|react_native\\(_android\\)?_fb][a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError

[strict]
12 changes: 9 additions & 3 deletions js/WKWebView.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<RCTWKWebViewProps> {
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.`);
}
Expand Down
81 changes: 66 additions & 15 deletions js/WebView.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/

'use strict';
Expand All @@ -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;

Expand All @@ -41,10 +49,52 @@ const defaultRenderLoading = () => (
</View>
);

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<string>,

|}>;

/**
* Renders a native WebView.
*/
class WebView extends React.Component {
class WebView extends React.Component<WebViewPropsAndroid, State> {
static defaultProps = {
javaScriptEnabled: true,
thirdPartyCookiesEnabled: true,
Expand All @@ -55,7 +105,7 @@ class WebView extends React.Component {

state = {
viewState: WebViewState.IDLE,
lastErrorEvent: null,
lastErrorEvent: (null: ?WebViewErrorEvent),
startInLoadingState: true,
};

Expand All @@ -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(
Expand All @@ -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,
);
}

Expand All @@ -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) {
Expand Down Expand Up @@ -198,7 +249,7 @@ class WebView extends React.Component {
);
};

postMessage = data => {
postMessage = (data: string) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.postMessage,
Expand All @@ -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,
Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -263,7 +314,7 @@ class WebView extends React.Component {
this.updateNavigationState(event);
};

onMessage = (event) => {
onMessage = (event: WebViewEvent) => {
const { onMessage } = this.props;
onMessage && onMessage(event);
};
Expand Down
Loading

0 comments on commit 133f1e3

Please sign in to comment.