Bug 1357486: Part 0e - Support legacy extensions in OOP mode. r?aswan
MozReview-Commit-ID: 4JHxX78HNRV
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -172,53 +172,92 @@ ProxyMessenger = {
let messageManagers = [Services.mm, Services.ppmm];
MessageChannel.addListener(messageManagers, "Extension:Connect", this);
MessageChannel.addListener(messageManagers, "Extension:Message", this);
MessageChannel.addListener(messageManagers, "Extension:Port:Disconnect", this);
MessageChannel.addListener(messageManagers, "Extension:Port:PostMessage", this);
},
- receiveMessage({target, messageName, channelId, sender, recipient, data, responseType}) {
+ async receiveMessage({target, messageName, channelId, sender, recipient, data, responseType}) {
if (recipient.toNativeApp) {
let {childId, toNativeApp} = recipient;
if (messageName == "Extension:Message") {
let context = ParentAPIManager.getContextById(childId);
return new NativeApp(context, toNativeApp).sendMessage(data);
}
if (messageName == "Extension:Connect") {
let context = ParentAPIManager.getContextById(childId);
NativeApp.onConnectNative(context, target.messageManager, data.portId, sender, toNativeApp);
return true;
}
// "Extension:Port:Disconnect" and "Extension:Port:PostMessage" for
// native messages are handled by NativeApp.
return;
}
+ const noHandlerError = {
+ result: MessageChannel.RESULT_NO_HANDLER,
+ message: "No matching message handler for the given recipient.",
+ };
+
let extension = GlobalManager.extensionMap.get(sender.extensionId);
let receiverMM = this.getMessageManagerForRecipient(recipient);
if (!extension || !receiverMM) {
- return Promise.reject({
- result: MessageChannel.RESULT_NO_HANDLER,
- message: "No matching message handler for the given recipient.",
- });
+ return Promise.reject(noHandlerError);
}
if ((messageName == "Extension:Message" ||
messageName == "Extension:Connect") &&
apiManager.global.tabGetSender) {
// From ext-tabs.js, undefined on Android.
apiManager.global.tabGetSender(extension, target, sender);
}
- return MessageChannel.sendMessage(receiverMM, messageName, data, {
+
+ let promise1 = MessageChannel.sendMessage(receiverMM, messageName, data, {
sender,
recipient,
responseType,
});
+
+ // If we have a remote, embedded extension, the legacy side is
+ // running in a different process than the WebExtension side.
+ // As a result, we need to dispatch the message to both the parent
+ // and extension processes, and manually merge the results.
+ if (extension.isEmbedded && extension.remote) {
+ let promise2 = MessageChannel.sendMessage(Services.ppmm.getChildAt(0), messageName, data, {
+ sender,
+ recipient,
+ responseType,
+ });
+
+ let result = undefined;
+ let failures = 0;
+ let tryPromise = async promise => {
+ try {
+ let res = await promise;
+ if (result === undefined) {
+ result = res;
+ }
+ } catch (e) {
+ if (e.result != MessageChannel.RESULT_NO_HANDLER) {
+ throw e;
+ }
+ failures++;
+ }
+ };
+
+ await Promise.all([tryPromise(promise1), tryPromise(promise2)]);
+ if (failures == 2) {
+ return Promise.reject(noHandlerError);
+ }
+ return result;
+ }
+
+ return promise1;
},
/**
* @param {object} recipient An object that was passed to
* `MessageChannel.sendMessage`.
* @param {Extension} extension
* @returns {object|null} The message manager matching the recipient if found.
*/
--- a/toolkit/components/extensions/LegacyExtensionsUtils.jsm
+++ b/toolkit/components/extensions/LegacyExtensionsUtils.jsm
@@ -140,16 +140,18 @@ class EmbeddedExtension {
// This is the instance of the WebExtension embedded in the hybrid add-on.
this.extension = new Extension({
id: this.addonId,
resourceURI: embeddedExtensionURI,
version: this.version,
});
+ this.extension.isEmbedded = true;
+
// This callback is register to the "startup" event, emitted by the Extension instance
// after the extension manifest.json has been loaded without any errors, but before
// starting any of the defined contexts (which give the legacy part a chance to subscribe
// runtime.onMessage/onConnect listener before the background page has been loaded).
const onBeforeStarted = () => {
this.extension.off("startup", onBeforeStarted);
// Resolve the startup promise and reset the startupError.
--- a/toolkit/components/extensions/test/xpcshell/test_ext_legacy_extension_context.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_legacy_extension_context.js
@@ -52,16 +52,17 @@ add_task(async function test_legacy_exte
});
}
let extensionData = {
background,
};
let extension = Extension.generate(extensionData);
+ extension.isEmbedded = true;
let waitForExtensionInfo = new Promise((resolve, reject) => {
extension.on("test-message", function testMessageListener(kind, msg, ...args) {
if (msg != "webextension-ready") {
reject(new Error(`Got an unexpected test-message: ${msg}`));
} else {
extension.off("test-message", testMessageListener);
resolve(args[0]);
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -24,16 +24,18 @@ skip-if = os == "android" || (os=='linux
[test_ext_downloads_search.js]
skip-if = os == "android"
[test_ext_experiments.js]
[test_ext_extension.js]
[test_ext_extensionPreferencesManager.js]
[test_ext_extensionSettingsStore.js]
[test_ext_extension_startup_telemetry.js]
[test_ext_idle.js]
+[test_ext_legacy_extension_context.js]
+[test_ext_legacy_extension_embedding.js]
[test_ext_localStorage.js]
[test_ext_management.js]
[test_ext_management_uninstall_self.js]
[test_ext_onmessage_removelistener.js]
skip-if = true # This test no longer tests what it is meant to test.
[test_ext_privacy.js]
[test_ext_privacy_disable.js]
[test_ext_privacy_update.js]
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -46,14 +46,12 @@ tags = webextensions in-process-webexten
[test_ext_unknown_permissions.js]
[test_locale_converter.js]
[test_locale_data.js]
[test_ext_permissions.js]
skip-if = os == "android" # Bug 1350559
[test_ext_runtime_sendMessage_args.js]
-[test_ext_legacy_extension_context.js]
-[test_ext_legacy_extension_embedding.js]
[test_proxy_scripts.js]
[include:xpcshell-common.ini]
[include:xpcshell-content.ini]