Skip to content

Commit

Permalink
Improved window.postMessage implementation
Browse files Browse the repository at this point in the history
Summary:
Adds a queue to postMessage so that messages sent close together are not lost.

Setting location="a";location="b" results in only "b" reaching shouldStartLoadWithRequest. Making the second update asynchronous with setTimeout does not fix the issue unless a delay is added.

With this update, postMessage queues "b" until it gets a "message:received" event that confirms "a" has already been processed.

The included test sends two messages from a webview and checks that both are received. It fails against the preexisting code with the first message being dropped.
Closes #11304

Differential Revision: D5481385

Pulled By: hramos

fbshipit-source-id: 9b6af195eeff8f20c820e2fcdac997c90763e840
  • Loading branch information
tqc authored and facebook-github-bot committed Jul 28, 2017
1 parent bca825e commit 42f7b9e
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 4 deletions.
1 change: 1 addition & 0 deletions IntegrationTests/IntegrationTestsApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var TESTS = [
require('./ImageCachePolicyTest'),
require('./ImageSnapshotTest'),
require('./PromiseTest'),
require('./WebViewTest'),
require('./SyncMethodTest'),
require('./WebSocketTest'),
require('./AccessibilityManagerTest'),
Expand Down
59 changes: 59 additions & 0 deletions IntegrationTests/WebViewTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';

var React = require('react');
var ReactNative = require('react-native');
var {
WebView,
} = ReactNative;

var { TestModule } = ReactNative.NativeModules;

class WebViewTest extends React.Component {

render() {
var firstMessageReceived = false;
var secondMessageReceived = false;
function processMessage(e) {
var message = e.nativeEvent.data;
if (message === 'First') {firstMessageReceived = true;}
if (message === 'Second') {secondMessageReceived = true;}

// got both messages
if (firstMessageReceived && secondMessageReceived) {TestModule.markTestPassed(true);}
// wait for next message
else if (firstMessageReceived && !secondMessageReceived) {return;}
// first message got lost
else if (!firstMessageReceived && secondMessageReceived) {throw new Error('First message got lost');}
}
var html = 'Hello world'
+ '<script>'
+ "window.setTimeout(function(){window.postMessage('First'); window.postMessage('Second')}, 0)"
+ '</script>';

// fail if messages didn't get through;
window.setTimeout(function() { throw new Error(firstMessageReceived ? 'Both messages got lost' : 'Second message got lost');}, 10000);

var source = {
html: html,
};

return (
<WebView
source={source}
onMessage = {processMessage}
/>
);
}
}

WebViewTest.displayName = 'WebViewTest';

module.exports = WebViewTest;
4 changes: 4 additions & 0 deletions RNTester/RNTesterIntegrationTests/RNTesterIntegrationTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,9 @@ - (void)testTheTester_ExpectError
RCT_TEST_ONLY_WITH_PACKAGER(WebSocketTest)
RCT_TEST(AccessibilityManagerTest)

#if !TARGET_OS_TV // tvOS does not fully support WebView
RCT_TEST(WebViewTest)
#endif


@end
31 changes: 27 additions & 4 deletions React/Views/RCTWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLR
[event addEntriesFromDictionary: @{
@"data": data,
}];

NSString *source = @"document.dispatchEvent(new MessageEvent('message:received'));";

[_webView stringByEvaluatingJavaScriptFromString:source];

_onMessage(event);
}

Expand Down Expand Up @@ -301,10 +306,28 @@ - (void)webViewDidFinishLoad:(UIWebView *)webView
}
#endif
NSString *source = [NSString stringWithFormat:
@"window.originalPostMessage = window.postMessage;"
"window.postMessage = function(data) {"
"window.location = '%@://%@?' + encodeURIComponent(String(data));"
"};", RCTJSNavigationScheme, kPostMessageHost
@"(function() {"
"window.originalPostMessage = window.postMessage;"

"var messageQueue = [];"
"var messagePending = false;"

"function processQueue() {"
"if (!messageQueue.length || messagePending) return;"
"messagePending = true;"
"window.location = '%@://%@?' + encodeURIComponent(messageQueue.shift());"
"}"

"window.postMessage = function(data) {"
"messageQueue.push(String(data));"
"processQueue();"
"};"

"document.addEventListener('message:received', function(e) {"
"messagePending = false;"
"processQueue();"
"});"
"})();", RCTJSNavigationScheme, kPostMessageHost
];
[webView stringByEvaluatingJavaScriptFromString:source];
}
Expand Down

0 comments on commit 42f7b9e

Please sign in to comment.