Bug 1202481 - [webext] Fix browser.runtime.onMessage reply handling (r=gabor)

This commit is contained in:
Bill McCloskey 2015-08-31 07:54:12 -07:00
parent 7ab0636be2
commit bd04960a06
5 changed files with 191 additions and 7 deletions

View File

@ -198,6 +198,8 @@ function ExtensionContext(extensionId, contentWindow)
this.extensionId = extensionId;
this.contentWindow = contentWindow;
this.onClose = new Set();
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindowId = utils.outerWindowID;
@ -232,8 +234,6 @@ function ExtensionContext(extensionId, contentWindow)
// reason. However, we waive here anyway in case that changes.
Cu.waiveXrays(this.sandbox).chrome = Cu.waiveXrays(this.sandbox).browser;
injectAPI(api(this), chromeObj);
this.onClose = new Set();
}
ExtensionContext.prototype = {

View File

@ -517,7 +517,7 @@ Messenger.prototype = {
},
onMessage(name) {
return new EventManager(this.context, name, fire => {
return new SingletonEventManager(this.context, name, callback => {
let listener = (type, target, message, sender, recipient) => {
message = Cu.cloneInto(message, this.context.cloneScope);
if (this.delegate) {
@ -538,13 +538,13 @@ Messenger.prototype = {
};
sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
let result = fire.withoutClone(message, sender, sendResponse);
let result = runSafeSyncWithoutClone(callback, message, sender, sendResponse);
if (result !== true) {
valid = false;
}
if (!sent) {
mm.sendAsyncMessage(replyName, {gotData: false});
}
}
};
this.broker.addListener("message", listener, this.filter);

View File

@ -24,4 +24,6 @@ support-files =
[test_ext_runtime_connect.html]
[test_ext_runtime_disconnect.html]
[test_ext_sandbox_var.html]
[test_ext_sendmessage_reply.html]
[test_ext_sendmessage_doublereply.html]
[test_ext_storage.html]

View File

@ -0,0 +1,102 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
function backgroundScript() {
// Add two listeners that both send replies. We're supposed to ignore all but one
// of them. Which one is chosen is non-deterministic.
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == "getreply") {
sendReply("reply1");
}
});
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == "getreply") {
sendReply("reply2");
}
});
function sleep(callback, n = 10) {
if (n == 0) {
callback();
} else {
setTimeout(function() { sleep(callback, n - 1); }, 0);
}
}
var done_count = 0;
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == "done") {
done_count++;
browser.test.assertEq(done_count, 1, "got exactly one reply");
// Go through the event loop a few times to make sure we don't get multiple replies.
sleep(function() {
browser.test.notifyPass("sendmessage_doublereply");
});
}
});
}
function contentScript() {
browser.runtime.sendMessage("getreply", function(resp) {
if (resp != "reply1" && resp != "reply2") {
return; // test failed
}
browser.runtime.sendMessage("done");
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitFinish("sendmessage_doublereply")]);
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

View File

@ -0,0 +1,80 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.8">
"use strict";
function backgroundScript() {
browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
browser.test.assertTrue(sender.tab.url.endsWith("file_sample.html"), "sender url correct");
if (msg == 0) {
sendReply("reply1");
} else if (msg == 1) {
window.setTimeout(function() {
sendReply("reply2");
}, 0);
return true;
} else if (msg == 2) {
browser.test.notifyPass("sendmessage_reply");
}
});
}
function contentScript() {
browser.runtime.sendMessage(0, function(resp1) {
if (resp1 != "reply1") {
return; // test failed
}
browser.runtime.sendMessage(1, function(resp2) {
if (resp2 != "reply2") {
return; // test failed
}
browser.runtime.sendMessage(2);
});
});
}
let extensionData = {
background: "(" + backgroundScript.toString() + ")()",
manifest: {
"permissions": ["tabs"],
"content_scripts": [{
"matches": ["http://mochi.test/*/file_sample.html"],
"js": ["content_script.js"],
"run_at": "document_start"
}]
},
files: {
"content_script.js": "(" + contentScript.toString() + ")()",
},
};
add_task(function* test_contentscript() {
let extension = ExtensionTestUtils.loadExtension(extensionData);
yield extension.startup();
info("extension loaded");
let win = window.open("file_sample.html");
yield Promise.all([waitForLoad(win), extension.awaitFinish("sendmessage_reply")]);
win.close();
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>