Bug 1396686 - Provide info which onMessage listener's response handle went out of scope
MozReview-Commit-ID: Bu71gP8Ey3
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -81,30 +81,35 @@ function injectAPI(source, dest) {
* A finalization witness helper that wraps a sendMessage response and
* guarantees to either get the promise resolved, or rejected when the
* wrapped promise goes out of scope.
*
* Holding a reference to a returned StrongPromise doesn't prevent the
* wrapped promise from being garbage collected.
*/
const StrongPromise = {
- wrap(promise, id) {
+ wrap(promise, channelId, location) {
return new Promise((resolve, reject) => {
- const witness = finalizationService.make("extensions-sendMessage-witness", id);
+ const tag = `${channelId}|${location}`;
+ const witness = finalizationService.make("extensions-sendMessage-witness", tag);
promise.then(value => {
witness.forget();
resolve(value);
}, error => {
witness.forget();
reject(error);
});
});
},
- observe(subject, topic, id) {
- MessageChannel.abortChannel(id, {message: "Response handle went out of scope"});
+ observe(subject, topic, tag) {
+ const pos = tag.indexOf("|");
+ const channel = tag.substr(0, pos);
+ const location = tag.substr(pos + 1);
+ const message = `Promised response from onMessage listener at ${location} went out of scope`;
+ MessageChannel.abortChannel(channel, {message});
},
};
Services.obs.addObserver(StrongPromise, "extensions-sendMessage-witness");
/**
* Abstraction for a Port object in the extension API.
*
* @param {BaseContext} context The context that owns this port.
@@ -375,16 +380,18 @@ class Messenger {
sendNativeMessage(messageManager, msg, recipient, responseCallback) {
msg = NativeApp.encodeMessage(this.context, msg);
return this.sendMessage(messageManager, msg, recipient, responseCallback);
}
_onMessage(name, filter) {
return new EventManager(this.context, name, fire => {
+ const [location] = new this.context.cloneScope.Error().stack.split("\n", 1);
+
let listener = {
messageFilterPermissive: this.optionalFilter,
messageFilterStrict: this.filter,
filterMessage: (sender, recipient) => {
// Ignore the message if it was sent by this Messenger.
return (sender.contextId !== this.context.contextId &&
filter(sender, recipient));
@@ -411,19 +418,19 @@ class Messenger {
sendResponse = Cu.exportFunction(sendResponse, this.context.cloneScope);
// Note: We intentionally do not use runSafe here so that any
// errors are propagated to the message sender.
let result = fire.raw(message, sender, sendResponse);
message = null;
if (result instanceof this.context.cloneScope.Promise) {
- return StrongPromise.wrap(result, channelId);
+ return StrongPromise.wrap(result, channelId, location);
} else if (result === true) {
- return StrongPromise.wrap(promise, channelId);
+ return StrongPromise.wrap(promise, channelId, location);
}
return response;
},
};
MessageChannel.addListener(this.messageManagers, "Extension:Message", listener);
return () => {
MessageChannel.removeListener(this.messageManagers, "Extension:Message", listener);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_runtime_sendMessage.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_sendMessage.js
@@ -150,20 +150,20 @@ add_task(async function sendMessageRespo
function page() {
browser.test.onMessage.addListener(msg => {
browser.runtime.sendMessage(msg)
.then(response => {
if (response) {
browser.test.log(`Got response: ${response}`);
browser.test.sendMessage(response);
}
- }, error => {
- browser.test.assertEq(error.message,
- "Response handle went out of scope",
- "The promise rejected with the correct error");
+ }, ({message}) => {
+ browser.test.assertTrue(
+ /at background@moz-extension:\/\/[\w-]+\/%7B[\w-]+%7D\.js:4:\d went out/.test(message),
+ `Promise rejected with the correct error message: ${message}`);
browser.test.sendMessage("rejected");
}
);
});
browser.test.sendMessage("ready");
}
let extension = ExtensionTestUtils.loadExtension({