Implement a postMessage function and an onMessage event for webviews …

Summary:
JS API very similar to web workers and node's child process.

Work has been done by somebody else for the Android implementation over at #7020, so we'd need to have these in sync before anything gets merged.

I've made a prop `messagingEnabled` to be more explicit about creating globals—it might be sufficient to just check for an onMessage handler though.

![screen shot 2016-09-06 at 10 28 23](https://cloud.githubusercontent.com/assets/7275322/18268669/b1a12348-741c-11e6-91a1-ad39d5a8bc03.png)
Closes https://github.com/facebook/react-native/pull/9762

Differential Revision: D4008260

fbshipit-source-id: 84b1afafbc0ab1edc3dfbf1a8fb870218e171a4c
This commit is contained in:
Jacob Parker
2016-10-16 06:29:14 -07:00
committed by Facebook Github Bot
parent 6ea26c01de
commit abb8ea3aea
11 changed files with 338 additions and 2 deletions

View File

@@ -58,6 +58,7 @@ class WebView extends React.Component {
automaticallyAdjustContentInsets: PropTypes.bool,
contentInset: EdgeInsetsPropType,
onNavigationStateChange: PropTypes.func,
onMessage: PropTypes.func,
onContentSizeChange: PropTypes.func,
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
style: View.propTypes.style,
@@ -218,6 +219,8 @@ class WebView extends React.Component {
userAgent={this.props.userAgent}
javaScriptEnabled={this.props.javaScriptEnabled}
domStorageEnabled={this.props.domStorageEnabled}
messagingEnabled={typeof this.props.onMessage === 'function'}
onMessage={this.onMessage}
contentInset={this.props.contentInset}
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
onContentSizeChange={this.props.onContentSizeChange}
@@ -268,6 +271,14 @@ class WebView extends React.Component {
);
};
postMessage = (data) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.postMessage,
[String(data)]
);
};
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
@@ -310,9 +321,18 @@ class WebView extends React.Component {
});
this.updateNavigationState(event);
};
onMessage = (event: Event) => {
var {onMessage} = this.props;
onMessage && onMessage(event);
}
}
var RCTWebView = requireNativeComponent('RCTWebView', WebView);
var RCTWebView = requireNativeComponent('RCTWebView', WebView, {
nativeOnly: {
messagingEnabled: PropTypes.bool,
},
});
var styles = StyleSheet.create({
container: {

View File

@@ -235,6 +235,16 @@ class WebView extends React.Component {
* Function that is invoked when the `WebView` loading starts or ends.
*/
onNavigationStateChange: PropTypes.func,
/**
* 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: PropTypes.func,
/**
* Boolean value that forces the `WebView` to show the loading view
* on the first load.
@@ -382,6 +392,8 @@ class WebView extends React.Component {
source.uri = this.props.url;
}
const messagingEnabled = typeof this.props.onMessage === 'function';
var webView =
<RCTWebView
ref={RCT_WEBVIEW_REF}
@@ -397,6 +409,8 @@ class WebView extends React.Component {
onLoadingStart={this._onLoadingStart}
onLoadingFinish={this._onLoadingFinish}
onLoadingError={this._onLoadingError}
messagingEnabled={messagingEnabled}
onMessage={this._onMessage}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
scalesPageToFit={this.props.scalesPageToFit}
allowsInlineMediaPlayback={this.props.allowsInlineMediaPlayback}
@@ -457,6 +471,24 @@ class WebView extends React.Component {
);
};
/**
* Posts a message to the web view, which will emit a `message` event.
* Accepts one argument, `data`, which must be a string.
*
* In your webview, you'll need to something like the following.
*
* ```js
* document.addEventListener('message', e => { document.title = e.data; });
* ```
*/
postMessage = (data) => {
UIManager.dispatchViewManagerCommand(
this.getWebViewHandle(),
UIManager.RCTWebView.Commands.postMessage,
[String(data)]
);
};
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
@@ -502,6 +534,11 @@ class WebView extends React.Component {
});
this._updateNavigationState(event);
};
_onMessage = (event: Event) => {
var {onMessage} = this.props;
onMessage && onMessage(event);
}
}
var RCTWebView = requireNativeComponent('RCTWebView', WebView, {
@@ -509,6 +546,8 @@ var RCTWebView = requireNativeComponent('RCTWebView', WebView, {
onLoadingStart: true,
onLoadingError: true,
onLoadingFinish: true,
onMessage: true,
messagingEnabled: PropTypes.bool,
},
});