Bug 1317101 - Part 6: Remove or refactor code that prevents extensions from running in the child process. r?aswan
MozReview-Commit-ID: 7v5eIBOKA2v
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -17,16 +17,18 @@ this.EXPORTED_SYMBOLS = ["ExtensionChild
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
+ "resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
"resource://gre/modules/MessageChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NativeApp",
"resource://gre/modules/NativeMessaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
@@ -727,22 +729,16 @@ class ExtensionPageContextChild extends
* @param {string} params.viewType One of "background", "popup" or "tab".
* "background" and "tab" are used by `browser.extension.getViews`.
* "popup" is only used internally to identify page action and browser
* action popups and options_ui pages.
* @param {number} [params.tabId] This tab's ID, used if viewType is "tab".
*/
constructor(extension, params) {
super("addon_child", extension);
- if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
- // This check is temporary. It should be removed once the proxy creation
- // is asynchronous.
- throw new Error("ExtensionPageContextChild cannot be created in child processes");
- }
-
let {viewType, uri, contentWindow, tabId} = params;
this.viewType = viewType;
this.uri = uri || extension.baseURI;
this.setContentWindow(contentWindow);
// This is the MessageSender property passed to extension.
// It can be augmented by the "page-open" hook.
@@ -918,65 +914,73 @@ class ContentGlobal {
// Do not confuse it with the innerWindowID of DOMWindows!
this.windowId = windowId;
}
this.initialized = true;
}
}
ExtensionChild = {
+ ChildAPIManager,
+ Messenger,
+ Port,
+
// Map<nsIContentFrameMessageManager, ContentGlobal>
contentGlobals: new Map(),
// Map<innerWindowId, ExtensionPageContextChild>
extensionContexts: new Map(),
initOnce() {
// This initializes the default message handler for messages targeted at
// an addon process, in case the addon process receives a message before
// its Messenger has been instantiated. For example, if a content script
// sends a message while there is no background page.
MessageChannel.setupMessageManagers([Services.cpmm]);
},
init(global) {
+ if (!ExtensionManagement.isExtensionProcess) {
+ throw new Error("Cannot init extension page global in current process");
+ }
+
this.contentGlobals.set(global, new ContentGlobal(global));
},
uninit(global) {
this.contentGlobals.get(global).uninit();
this.contentGlobals.delete(global);
},
/**
* Create a privileged context at document-element-inserted.
*
* @param {BrowserExtensionContent} extension
* The extension for which the context should be created.
* @param {nsIDOMWindow} contentWindow The global of the page.
*/
createExtensionContext(extension, contentWindow) {
+ if (!ExtensionManagement.isExtensionProcess) {
+ throw new Error("Cannot create an extension page context in current process");
+ }
+
let windowId = getInnerWindowID(contentWindow);
let context = this.extensionContexts.get(windowId);
if (context) {
if (context.extension !== extension) {
- // Oops. This should never happen.
- Cu.reportError("A different extension context already exists in this frame!");
- } else {
- // This should not happen either.
- Cu.reportError("The extension context was already initialized in this frame.");
+ throw new Error("A different extension context already exists for this frame");
}
- return;
+ throw new Error("An extension context was already initialized for this frame");
}
- let mm = contentWindow
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDocShell)
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIContentFrameMessageManager);
+ let mm = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell)
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+
let {viewType, tabId} = this.contentGlobals.get(mm).ensureInitialized();
let uri = contentWindow.document.documentURIObject;
context = new ExtensionPageContextChild(extension, {viewType, contentWindow, uri, tabId});
this.extensionContexts.set(windowId, context);
},
@@ -997,25 +1001,8 @@ ExtensionChild = {
for (let [windowId, context] of this.extensionContexts) {
if (context.extension.id == extensionId) {
context.shutdown();
this.extensionContexts.delete(windowId);
}
}
},
};
-
-// TODO(robwu): Change this condition when addons move to a separate process.
-if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
- Object.keys(ExtensionChild).forEach(function(key) {
- if (typeof ExtensionChild[key] == "function") {
- // :/
- ExtensionChild[key] = () => {};
- }
- });
-}
-
-Object.assign(ExtensionChild, {
- ChildAPIManager,
- Messenger,
- Port,
-});
-
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -1013,21 +1013,25 @@ class ExtensionGlobal {
}
}
this.ExtensionContent = {
globals: new Map(),
init(global) {
this.globals.set(global, new ExtensionGlobal(global));
- ExtensionChild.init(global);
+ if (ExtensionManagement.isExtensionProcess) {
+ ExtensionChild.init(global);
+ }
},
uninit(global) {
- ExtensionChild.uninit(global);
+ if (ExtensionManagement.isExtensionProcess) {
+ ExtensionChild.uninit(global);
+ }
this.globals.get(global).uninit();
this.globals.delete(global);
},
// This helper is exported to be integrated in the devtools RDP actors,
// that can use it to retrieve the existent WebExtensions ContentScripts
// of a target window and be able to show the ContentScripts source in the
// DevTools Debugger panel.
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -20,16 +20,18 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
let {UUIDMap} = Cu.import("resource://gre/modules/Extension.jsm", {});
return UUIDMap;
});
+var ExtensionManagement;
+
/*
* This file should be kept short and simple since it's loaded even
* when no extensions are running.
*/
// Keep track of frame IDs for content windows. Mostly we can just use
// the outer window ID as the frame ID. However, the API specifies
// that top-level windows have a frame ID of 0. So we need to keep
@@ -203,27 +205,30 @@ var Service = {
// Checks whether a given extension can load this URI (typically via
// an XML HTTP request). The manifest.json |permissions| directive
// determines this.
checkAddonMayLoad(extension, uri) {
return extension.whiteListedHosts.matchesIgnoringPath(uri);
},
generateBackgroundPageUrl(extension) {
- let background_scripts = extension.manifest.background &&
- extension.manifest.background.scripts;
+ let background_scripts = (extension.manifest.background &&
+ extension.manifest.background.scripts);
+
if (!background_scripts) {
return;
}
- let html = "<!DOCTYPE html>\n<body>\n";
+
+ let html = "<!DOCTYPE html>\n<html>\n<body>\n";
for (let script of background_scripts) {
script = script.replace(/"/g, """);
html += `<script src="${script}"></script>\n`;
}
html += "</body>\n</html>\n";
+
return "data:text/html;charset=utf-8," + encodeURIComponent(html);
},
// Finds the add-on ID associated with a given moz-extension:// URI.
// This is used to set the addonId on the originAttributes for the
// nsIPrincipal attached to the URI.
extensionURIToAddonID(uri) {
let uuid = uri.host;
@@ -254,19 +259,17 @@ function getAPILevelForWindow(window, ad
const {NO_PRIVILEGES, CONTENTSCRIPT_PRIVILEGES, FULL_PRIVILEGES} = API_LEVELS;
// Non WebExtension URLs and WebExtension URLs from a different extension
// has no access to APIs.
if (!addonId || getAddonIdForWindow(window) != addonId) {
return NO_PRIVILEGES;
}
- // Extension pages running in the content process always defaults to
- // "content script API level privileges".
- if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ if (!ExtensionManagement.isExtensionProcess) {
return CONTENTSCRIPT_PRIVILEGES;
}
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell);
// Handling of ExtensionPages running inside sub-frames.
if (docShell.sameTypeParent) {
@@ -295,17 +298,21 @@ function getAPILevelForWindow(window, ad
// (see Bug 1214658 for rationale)
return CONTENTSCRIPT_PRIVILEGES;
}
// WebExtension URLs loaded into top frames UI could have full API level privileges.
return FULL_PRIVILEGES;
}
-this.ExtensionManagement = {
+ExtensionManagement = {
+ get isExtensionProcess() {
+ return 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),
getFrameId: Frames.getId.bind(Frames),
getParentFrameId: Frames.getParentId.bind(Frames),
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -434,22 +434,16 @@ ParentAPIManager = {
let extension = GlobalManager.getExtension(extensionId);
if (!extension) {
throw new Error(`No WebExtension found with ID ${extensionId}`);
}
let context;
if (envType == "addon_parent") {
- // Privileged addon contexts can only be loaded in documents whose main
- // frame is also the same addon.
- if (principal.URI.prePath !== extension.baseURI.prePath ||
- !target.contentPrincipal.subsumes(principal)) {
- throw new Error(`Refused to create privileged WebExtension context for ${principal.URI.spec}`);
- }
context = new ExtensionPageContextParent(envType, extension, data, target);
} else if (envType == "content_parent") {
context = new ContentScriptContextParent(envType, extension, data, target, principal);
} else {
throw new Error(`Invalid WebExtension context envType: ${envType}`);
}
this.proxyContexts.set(childId, context);
},
@@ -539,19 +533,17 @@ ParentAPIManager = {
let context = this.getContextById(data.childId);
let listener = context.listenerProxies.get(data.path);
findPathInObject(context.apiObj, data.path).removeListener(listener);
},
getContextById(childId) {
let context = this.proxyContexts.get(childId);
if (!context) {
- let error = new Error("WebExtension context not found!");
- Cu.reportError(error);
- throw error;
+ throw new Error("WebExtension context not found!");
}
return context;
},
};
ParentAPIManager.init();