Improved window.postMessage implementation

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 https://github.com/facebook/react-native/pull/11304

Differential Revision: D5481385

Pulled By: hramos

fbshipit-source-id: 9b6af195eeff8f20c820e2fcdac997c90763e840
This commit is contained in:
Tom Clarkson 2017-07-28 11:32:00 -07:00 committed by Facebook Github Bot
parent bca825ee50
commit 42f7b9e717
4 changed files with 91 additions and 4 deletions

View File

@ -33,6 +33,7 @@ var TESTS = [
require('./ImageCachePolicyTest'),
require('./ImageSnapshotTest'),
require('./PromiseTest'),
require('./WebViewTest'),
require('./SyncMethodTest'),
require('./WebSocketTest'),
require('./AccessibilityManagerTest'),

View File

@ -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;

View File

@ -76,5 +76,9 @@ RCT_TEST(PromiseTest)
RCT_TEST_ONLY_WITH_PACKAGER(WebSocketTest)
RCT_TEST(AccessibilityManagerTest)
#if !TARGET_OS_TV // tvOS does not fully support WebView
RCT_TEST(WebViewTest)
#endif
@end

View File

@ -250,6 +250,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
[event addEntriesFromDictionary: @{
@"data": data,
}];
NSString *source = @"document.dispatchEvent(new MessageEvent('message:received'));";
[_webView stringByEvaluatingJavaScriptFromString:source];
_onMessage(event);
}
@ -301,10 +306,28 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
}
#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];
}