Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Examples/UIExplorer/js/messagingtest.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
<script>
var messagesReceivedFromReactNative = 0;
document.addEventListener('message', function(e) {
if (e.origin !== 'react-native') return;
messagesReceivedFromReactNative += 1;
document.getElementsByTagName('p')[0].innerHTML =
'Messages recieved from React Native: ' + messagesReceivedFromReactNative;
document.getElementsByTagName('p')[1].innerHTML = e.data;
});

document.getElementsByTagName('button')[0].addEventListener('click', function() {
window.postMessage('"Hello" from the web view');
window.postMessage('"Hello" from the web view', 'react-native');
});
</script>
</html>
23 changes: 16 additions & 7 deletions Libraries/Components/WebView/WebView.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,17 @@ class WebView extends React.Component {
*/
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`.
* A function that will be invoked when the webview calls `window.postMessage`.
* Setting this property will alter `postMessage` to allow posting to React
* Native. `postMessage accepts the arguments `data`, `targetOrigin`, and
* `transfer`.
*
* `window.postMessage` accepts one argument, `data`, which will be
* available on the event object, `event.nativeEvent.data`. `data`
* must be a string.
* Calling `postMessage` with a `targetOrigin` of `react-native` will post
* to React Native. All other values for `targetOrigin` will use the default
* HTML5 behaviour.
*
* Note that when posting to React Native, `data` must be a string. The data
* can be retrieved from this handler with `event.nativeEvent.data`.
*/
onMessage: PropTypes.func,
/**
Expand Down Expand Up @@ -475,10 +479,15 @@ 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.
*
* The message event will have the properties `data` and `origin`, where
* `data` is the string provided to `postMessage` and `origin` is `react-native`
*
* In your webview, you'll need to something like the following.
*
* ```js
* document.addEventListener('message', e => { document.title = e.data; });
* document.addEventListener('message', e => {
* if (e.origin === 'react-native') doSomethingWith(e.data);
* });
* ```
*/
postMessage = (data) => {
Expand Down
34 changes: 19 additions & 15 deletions React/Views/RCTWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,15 @@ - (void)stopLoading
- (void)postMessage:(NSString *)message
{
NSDictionary *eventInitDict = @{
@"origin": @"react-native",
@"data": message,
};
NSString *source = [NSString
stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));",
stringWithFormat:
@"document.dispatchEvent(new MessageEvent('message', %@));"
"if (window.native && typeof window.native.onmessage === 'function') {"
"window.native.onmessage(new MessageEvent('message', %@))"
"}",
RCTJSONStringify(eventInitDict, NULL)
];
[_webView stringByEvaluatingJavaScriptFromString:source];
Expand Down Expand Up @@ -275,21 +280,20 @@ - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)er
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (_messagingEnabled) {
#if RCT_DEV
// See isNative in lodash
NSString *testPostMessageNative = @"String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
BOOL postMessageIsNative = [
[webView stringByEvaluatingJavaScriptFromString:testPostMessageNative]
isEqualToString:@"true"
];
if (!postMessageIsNative) {
RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
}
#endif
NSString *source = [NSString stringWithFormat:
@"window.originalPostMessage = window.postMessage;"
"window.postMessage = function(data) {"
"window.location = '%@://%@?' + encodeURIComponent(String(data));"
@"window.native = {"
"postMessage: function(data) {"
"window.location = '%@://%@?' + encodeURIComponent(String(data));"
"},"
"onmessage: null"
"};"
"window.originalPostMessage = window.postMessage;"
"window.postMessage = function(data, targetOrigin) {"
"if (targetOrigin === 'react-native') {"
"window.location = '%@://%@?' + encodeURIComponent(String(data));"
"} else {"
"window.originalPostMessage.apply(arguments);"
"}"
"};", RCTJSNavigationScheme, RCTJSPostMessageHost
];
[webView stringByEvaluatingJavaScriptFromString:source];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,23 +273,20 @@ public void callInjectedJavaScript() {

public void linkBridge() {
if (messagingEnabled) {
if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// See isNative in lodash
String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
evaluateJavascript(testPostMessageNative, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
if (value.equals("true")) {
FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
}
}
});
}

loadUrl("javascript:(" +
"window.native = {" +
"postMessage: function(data) {" +
BRIDGE_NAME + ".postMessage(String(data));" +
"}," +
"onmessage: null" +
"}," +
"window.originalPostMessage = window.postMessage," +
"window.postMessage = function(data) {" +
BRIDGE_NAME + ".postMessage(String(data));" +
"window.postMessage = function(data, targetOrigin) {" +
"if (targetOrigin === 'react-native') {" +
BRIDGE_NAME + ".postMessage(String(data));" +
"} else {" +
"window.originalPostMessage.apply(arguments);" +
"}" +
"}" +
")");
}
Expand Down Expand Up @@ -493,8 +490,13 @@ public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray
case COMMAND_POST_MESSAGE:
try {
JSONObject eventInitDict = new JSONObject();
eventInitDict.put("origin", "react-native");
eventInitDict.put("data", args.getString(0));
root.loadUrl("javascript:(document.dispatchEvent(new MessageEvent('message', " + eventInitDict.toString() + ")))");
root.loadUrl("javascript:(" +
"document.dispatchEvent(new MessageEvent('message', " + eventInitDict.toString() + "))," +
"window.native && typeof window.native.onmessage === 'function' &&" +
"window.native.onmessage(new MessageEvent('message', " + eventInitDict.toString() + "))" +
")");
} catch (JSONException e) {
throw new RuntimeException(e);
}
Expand Down