From b078d65a33c4c4b3cbd2b035e3568bf56b6bde26 Mon Sep 17 00:00:00 2001 From: Jacob Parker Date: Tue, 15 Nov 2016 09:40:40 +0000 Subject: [PATCH 1/4] Remove errors for detecting native postMessage --- React/Views/RCTWebView.m | 11 ----------- .../react/views/webview/ReactWebViewManager.java | 13 ------------- 2 files changed, 24 deletions(-) diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index 23fe37b84dca..7760208e73ac 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -275,17 +275,6 @@ - (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) {" diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 1a80722fe00b..e0efdbd16f04 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -273,19 +273,6 @@ 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() { - @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.originalPostMessage = window.postMessage," + "window.postMessage = function(data) {" + From a855b02c90c420d98f35926f9e65c1521b6e6772 Mon Sep 17 00:00:00 2001 From: Jacob Parker Date: Tue, 15 Nov 2016 09:42:49 +0000 Subject: [PATCH 2/4] Update docs --- Libraries/Components/WebView/WebView.ios.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 7961d777f7fc..c9111b5a7ff1 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -236,9 +236,10 @@ class WebView extends React.Component { */ onNavigationStateChange: PropTypes.func, /** - * A function that is invoked when the webview calls `window.postMessage`. + * A function that will be 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`. + * webview. If you need the old postMessage, it's under + * `window.originalPostMessage`. * * `window.postMessage` accepts one argument, `data`, which will be * available on the event object, `event.nativeEvent.data`. `data` From 8b00a35429d0b6ab23d6acd9028b6ec656e872bb Mon Sep 17 00:00:00 2001 From: Jacob Parker Date: Fri, 2 Dec 2016 10:58:23 +0000 Subject: [PATCH 3/4] Allow postMessage shims --- Examples/UIExplorer/js/messagingtest.html | 3 ++- Libraries/Components/WebView/WebView.ios.js | 22 +++++++++++++------ React/Views/RCTWebView.m | 9 ++++++-- .../views/webview/ReactWebViewManager.java | 9 ++++++-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Examples/UIExplorer/js/messagingtest.html b/Examples/UIExplorer/js/messagingtest.html index c9258dbf5104..61300ec6f789 100644 --- a/Examples/UIExplorer/js/messagingtest.html +++ b/Examples/UIExplorer/js/messagingtest.html @@ -15,6 +15,7 @@ diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index c9111b5a7ff1..0d6ae00c3de3 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -237,13 +237,16 @@ class WebView extends React.Component { onNavigationStateChange: PropTypes.func, /** * A function that will be invoked when the webview calls `window.postMessage`. - * Setting this property will inject a `postMessage` global into your - * webview. If you need the old postMessage, it's under - * `window.originalPostMessage`. + * 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, /** @@ -476,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) => { diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index 7760208e73ac..91a2b4d672c5 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -87,6 +87,7 @@ - (void)stopLoading - (void)postMessage:(NSString *)message { NSDictionary *eventInitDict = @{ + @"origin": @"react-native", @"data": message, }; NSString *source = [NSString @@ -277,8 +278,12 @@ - (void)webViewDidFinishLoad:(UIWebView *)webView if (_messagingEnabled) { NSString *source = [NSString stringWithFormat: @"window.originalPostMessage = window.postMessage;" - "window.postMessage = function(data) {" - "window.location = '%@://%@?' + encodeURIComponent(String(data));" + "window.postMessage = function(data, targetOrigin) {" + "if (targetOrigin === 'react-native') {" + "window.location = '%@://%@?' + encodeURIComponent(String(data));" + "} else {" + "window.originalPostMessage.apply(arguments);" + "}" "};", RCTJSNavigationScheme, RCTJSPostMessageHost ]; [webView stringByEvaluatingJavaScriptFromString:source]; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index e0efdbd16f04..312c2d4f20e6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -275,8 +275,12 @@ public void linkBridge() { if (messagingEnabled) { loadUrl("javascript:(" + "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);" + + "}" + "}" + ")"); } @@ -480,6 +484,7 @@ 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() + ")))"); } catch (JSONException e) { From 0173884583ae6e675d36b53df97e45ad326f18bd Mon Sep 17 00:00:00 2001 From: Jacob Parker Date: Sat, 3 Dec 2016 13:42:43 +0000 Subject: [PATCH 4/4] Expose web-worker-like global for postMessage --- React/Views/RCTWebView.m | 14 ++++++++++++-- .../react/views/webview/ReactWebViewManager.java | 12 +++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index 91a2b4d672c5..64d8d12424c7 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -91,7 +91,11 @@ - (void)postMessage:(NSString *)message @"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]; @@ -277,7 +281,13 @@ - (void)webViewDidFinishLoad:(UIWebView *)webView { if (_messagingEnabled) { NSString *source = [NSString stringWithFormat: - @"window.originalPostMessage = window.postMessage;" + @"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));" diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java index 312c2d4f20e6..b33e84ea5581 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java @@ -274,6 +274,12 @@ public void callInjectedJavaScript() { public void linkBridge() { if (messagingEnabled) { loadUrl("javascript:(" + + "window.native = {" + + "postMessage: function(data) {" + + BRIDGE_NAME + ".postMessage(String(data));" + + "}," + + "onmessage: null" + + "}," + "window.originalPostMessage = window.postMessage," + "window.postMessage = function(data, targetOrigin) {" + "if (targetOrigin === 'react-native') {" + @@ -486,7 +492,11 @@ public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray 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); }