Bug 1317101 - Part 7a: Add a `remote` flag to run an extension out-of-process based on a preference. r?billm
MozReview-Commit-ID: ChinmbLjnQA
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4708,16 +4708,17 @@ pref("xpinstall.whitelist.required", tru
pref("xpinstall.signatures.required", false);
pref("extensions.alwaysUnpack", false);
pref("extensions.minCompatiblePlatformVersion", "2.0");
pref("extensions.webExtensionsMinPlatformVersion", "42.0a1");
// Other webextensions prefs
pref("extensions.webextensions.keepStorageOnUninstall", false);
pref("extensions.webextensions.keepUuidOnUninstall", false);
+pref("extensions.webextensions.outOfProcess", false);
pref("network.buffer.cache.count", 24);
pref("network.buffer.cache.size", 32768);
// Desktop Notification
pref("notification.feature.enabled", false);
// Web Notification
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -21,16 +21,20 @@ const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
Cu.importGlobalProperties(["TextEncoder"]);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
+/* globals processCount */
+
+XPCOMUtils.defineLazyPreferenceGetter(this, "processCount", "dom.ipc.processCount");
+
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionAPIs",
"resource://gre/modules/ExtensionAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
"resource://gre/modules/ExtensionStorage.jsm");
@@ -583,16 +587,30 @@ this.Extension = class extends Extension
Services.obs.addObserver(this, "xpcom-shutdown", false);
this.cleanupFile = addonData.cleanupFile || null;
delete addonData.cleanupFile;
}
this.addonData = addonData;
this.startupReason = startupReason;
+ this.remote = ExtensionManagement.useRemoteWebExtensions;
+
+ if (this.remote && processCount !== 1) {
+ throw new Error("Out-of-process WebExtensions are not supported with multiple child processes");
+ }
+ if (this.remote) {
+ // Since we're currently extensions in the web content child
+ // process for testing, we currently only support single-process
+ // e10s, and the extension child will always run in that process.
+ this.parentMessageManager = Services.ppmm.getChildAt(1);
+ } else {
+ this.parentMessageManager = Services.ppmm.getChildAt(0);
+ }
+
this.id = addonData.id;
this.baseURI = NetUtil.newURI(this.getURL("")).QueryInterface(Ci.nsIURL);
this.principal = this.createPrincipal();
this.onStartup = null;
this.hasShutdown = false;
this.onShutdown = new Set();
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -300,17 +300,18 @@ function getAPILevelForWindow(window, ad
}
// WebExtension URLs loaded into top frames UI could have full API level privileges.
return FULL_PRIVILEGES;
}
ExtensionManagement = {
get isExtensionProcess() {
- return Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
+ return (this.useRemoteWebExtensions ||
+ Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT);
},
startupExtension: Service.startupExtension.bind(Service),
shutdownExtension: Service.shutdownExtension.bind(Service),
registerAPI: APIs.register.bind(APIs),
unregisterAPI: APIs.unregister.bind(APIs),
@@ -321,8 +322,11 @@ ExtensionManagement = {
// exported API Level Helpers
getAddonIdForWindow,
getAPILevelForWindow,
API_LEVELS,
APIs,
};
+
+XPCOMUtils.defineLazyPreferenceGetter(ExtensionManagement, "useRemoteWebExtensions",
+ "extensions.webextensions.outOfProcess", false);
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -101,34 +101,31 @@ let apiManager = new class extends Schem
}
}();
// Subscribes to messages related to the extension messaging API and forwards it
// to the relevant message manager. The "sender" field for the `onMessage` and
// `onConnect` events are updated if needed.
ProxyMessenger = {
_initialized: false,
+
init() {
if (this._initialized) {
return;
}
this._initialized = true;
- // TODO(robwu): When addons move to a separate process, we should use the
- // parent process manager(s) of the addon process(es) instead of the
- // in-process one.
- let pipmm = Services.ppmm.getChildAt(0);
// Listen on the global frame message manager because content scripts send
// and receive extension messages via their frame.
// Listen on the parent process message manager because `runtime.connect`
// and `runtime.sendMessage` requests must be delivered to all frames in an
// addon process (by the API contract).
// And legacy addons are not associated with a frame, so that is another
// reason for having a parent process manager here.
- let messageManagers = [Services.mm, pipmm];
+ 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}) {
@@ -142,18 +139,19 @@ ProxyMessenger = {
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;
}
+
let extension = GlobalManager.extensionMap.get(sender.extensionId);
- let receiverMM = this._getMessageManagerForRecipient(recipient);
+ let receiverMM = this.getMessageManagerForRecipient(recipient, extension);
if (!extension || !receiverMM) {
return Promise.reject({
result: MessageChannel.RESULT_NO_HANDLER,
message: "No matching message handler for the given recipient.",
});
}
if ((messageName == "Extension:Message" ||
@@ -167,33 +165,32 @@ ProxyMessenger = {
recipient,
responseType,
});
},
/**
* @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.
*/
- _getMessageManagerForRecipient(recipient) {
- let {extensionId, tabId} = recipient;
+ getMessageManagerForRecipient(recipient, extension) {
+ let {tabId} = recipient;
// tabs.sendMessage / tabs.connect
if (tabId) {
// `tabId` being set implies that the tabs API is supported, so we don't
// need to check whether `TabManager` exists.
let tab = apiManager.global.TabManager.getTab(tabId, null, null);
return tab && tab.linkedBrowser.messageManager;
}
// runtime.sendMessage / runtime.connect
- if (extensionId) {
- // TODO(robwu): map the extensionId to the addon parent process's message
- // manager when they run in a separate process.
- return Services.ppmm.getChildAt(0);
+ if (extension) {
+ return extension.parentMessageManager;
}
return null;
},
};
// Responsible for loading extension APIs into the right globals.
GlobalManager = {