Bug 1368152: Part 2 - Move extension policy registration out of ExtensionManagement.jsm. r?aswan,mixedpuppy
MozReview-Commit-ID: Ls1ZvJLcjlR
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -72,24 +72,31 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "require",
"resource://devtools/shared/Loader.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
-Cu.import("resource://gre/modules/ExtensionManagement.jsm");
+XPCOMUtils.defineLazyGetter(
+ this, "processScript",
+ () => Cc["@mozilla.org/webextensions/extension-process-script;1"]
+ .getService().wrappedJSObject);
+
Cu.import("resource://gre/modules/ExtensionParent.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "uuidGen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
+XPCOMUtils.defineLazyPreferenceGetter(this, "useRemoteWebExtensions",
+ "extensions.webextensions.remote", false);
+
var {
GlobalManager,
ParentAPIManager,
apiManager: Management,
} = ExtensionParent;
const {
classifyPermission,
@@ -701,17 +708,17 @@ this.Extension = class extends Extension
this.addonData = addonData;
this.startupReason = startupReason;
if (["ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(startupReason)) {
StartupCache.clearAddonData(addonData.id);
}
- this.remote = ExtensionManagement.useRemoteWebExtensions;
+ this.remote = useRemoteWebExtensions;
if (this.remote && processCount !== 1) {
throw new Error("Out-of-process WebExtensions are not supported with multiple child processes");
}
// This is filled in the first time an extension child is created.
this.parentMessageManager = null;
this.id = addonData.id;
@@ -905,23 +912,16 @@ this.Extension = class extends Extension
Services.obs.addObserver(observer, "message-manager-close");
Services.obs.addObserver(observer, "message-manager-disconnect");
ppmm.broadcastAsyncMessage(msg, data);
});
}
runManifest(manifest) {
- let resources = [];
- if (manifest.web_accessible_resources) {
- resources = manifest.web_accessible_resources.map(path => path.replace(/^\/*/, "/"));
- }
-
- this.webAccessibleResources = resources.map(res => new MatchGlob(res));
-
let promises = [];
for (let directive in manifest) {
if (manifest[directive] !== null) {
promises.push(Management.emit(`manifest_${directive}`, directive, this, manifest));
promises.push(Management.asyncEmitManifestEntry(this, directive));
}
}
@@ -971,24 +971,19 @@ this.Extension = class extends Extension
}
startup() {
this.startupPromise = this._startup();
return this.startupPromise;
}
async _startup() {
- this.started = false;
-
try {
let [, perms] = await Promise.all([this.loadManifest(), ExtensionPermissions.get(this)]);
- ExtensionManagement.startupExtension(this.uuid, this.addonData.resourceURI, this);
- this.started = true;
-
if (!this.hasShutdown) {
await this.initLocale();
}
if (this.errors.length) {
return Promise.reject({errors: this.errors});
}
@@ -1004,34 +999,41 @@ this.Extension = class extends Extension
}
if (perms.origins.length > 0) {
let patterns = this.whiteListedHosts.patterns.map(host => host.pattern);
this.whiteListedHosts = new MatchPatternSet([...patterns, ...perms.origins],
{ignorePath: true});
}
+ let resources = (this.manifest.web_accessible_resources || [])
+ .map(path => path.replace(/^\/*/, "/"));
+
+ this.webAccessibleResources = resources.map(res => new MatchGlob(res));
+
+
+ this.policy = processScript.initExtension(this.serialize(), this);
+
// The "startup" Management event sent on the extension instance itself
// is emitted just before the Management "startup" event,
// and it is used to run code that needs to be executed before
// any of the "startup" listeners.
this.emit("startup", this);
Management.emit("startup", this);
await this.runManifest(this.manifest);
Management.emit("ready", this);
this.emit("ready");
} catch (e) {
dump(`Extension error: ${e.message} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`);
Cu.reportError(e);
- if (this.started) {
- this.started = false;
- ExtensionManagement.shutdownExtension(this);
+ if (this.policy) {
+ this.policy.active = false;
}
this.cleanupGeneratedFile();
throw e;
}
this.startupPromise = null;
@@ -1063,32 +1065,32 @@ this.Extension = class extends Extension
}
} catch (e) {
Cu.reportError(e);
}
this.shutdownReason = reason;
this.hasShutdown = true;
- if (!this.started) {
+ if (!this.policy) {
return;
}
if (this.cleanupFile ||
["ADDON_INSTALL", "ADDON_UNINSTALL", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(reason)) {
StartupCache.clearAddonData(this.id);
}
let data = Services.ppmm.initialProcessData;
data["Extension:Extensions"] = data["Extension:Extensions"].filter(e => e.id !== this.id);
Services.ppmm.removeMessageListener(this.MESSAGE_EMIT_EVENT, this);
if (!this.manifest) {
- ExtensionManagement.shutdownExtension(this);
+ this.policy.active = false;
this.cleanupGeneratedFile();
return;
}
GlobalManager.uninit(this);
for (let obj of this.onShutdown) {
@@ -1103,17 +1105,17 @@ this.Extension = class extends Extension
Management.emit("shutdown", this);
this.emit("shutdown");
Services.ppmm.broadcastAsyncMessage("Extension:Shutdown", {id: this.id});
MessageChannel.abortResponses({extensionId: this.id});
- ExtensionManagement.shutdownExtension(this);
+ this.policy.active = false;
return this.cleanupGeneratedFile();
}
observe(subject, topic, data) {
if (topic === "xpcom-shutdown") {
this.cleanupGeneratedFile();
}
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -1,105 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
+/* exported ExtensionManagement */
+
this.EXPORTED_SYMBOLS = ["ExtensionManagement"];
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, "E10SUtils",
- "resource:///modules/E10SUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
- let {UUIDMap} = Cu.import("resource://gre/modules/Extension.jsm", {});
- return UUIDMap;
-});
-
/*
* This file should be kept short and simple since it's loaded even
* when no extensions are running.
*/
-function parseScriptOptions(options) {
- return {
- allFrames: options.all_frames,
- matchAboutBlank: options.match_about_blank,
- frameID: options.frame_id,
- runAt: options.run_at,
-
- matches: new MatchPatternSet(options.matches),
- excludeMatches: new MatchPatternSet(options.exclude_matches || []),
- includeGlobs: options.include_globs && options.include_globs.map(glob => new MatchGlob(glob)),
- excludeGlobs: options.include_globs && options.exclude_globs.map(glob => new MatchGlob(glob)),
-
- jsPaths: options.js || [],
- cssPaths: options.css || [],
- };
-}
-
-function getURLForExtension(id, path = "") {
- let uuid = UUIDMap.get(id, false);
- if (!uuid) {
- Cu.reportError(`Called getURLForExtension on unmapped extension ${id}`);
- return null;
- }
- return `moz-extension://${uuid}/${path}`;
-}
-
let cacheInvalidated = 0;
function onCacheInvalidate() {
cacheInvalidated++;
}
Services.obs.addObserver(onCacheInvalidate, "startupcache-invalidate");
var ExtensionManagement = {
get cacheInvalidated() {
return cacheInvalidated;
},
get isExtensionProcess() {
return WebExtensionPolicy.isExtensionProcess;
},
- // Called when a new extension is loaded.
- startupExtension(uuid, uri, extension) {
- let policy = new WebExtensionPolicy({
- id: extension.id,
- mozExtensionHostname: uuid,
- baseURL: uri.spec,
-
- permissions: Array.from(extension.permissions),
- allowedOrigins: extension.whiteListedHosts,
- webAccessibleResources: extension.webAccessibleResources || [],
-
- contentSecurityPolicy: extension.manifest.content_security_policy,
-
- localizeCallback: extension.localize.bind(extension),
-
- backgroundScripts: (extension.manifest.background &&
- extension.manifest.background.scripts),
-
- contentScripts: (extension.manifest.content_scripts || []).map(parseScriptOptions),
- });
-
- extension.policy = policy;
- policy.active = true;
+ getURLForExtension(id, path = "") {
+ let policy = WebExtensionPolicy.getByID(id);
+ if (!policy) {
+ Cu.reportError(`Called getURLForExtension on unmapped extension ${id}`);
+ return null;
+ }
+ return policy.getURL(path);
},
-
- // Called when an extension is unloaded.
- shutdownExtension(extension) {
- extension.policy.active = false;
- },
-
- getURLForExtension,
};
-
-XPCOMUtils.defineLazyPreferenceGetter(ExtensionManagement, "useRemoteWebExtensions",
- "extensions.webextensions.remote", false);
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -289,44 +289,50 @@ ExtensionManager = {
procData["Extension:Schemas"] = new Map();
}
this.schemaJSON = procData["Extension:Schemas"];
Services.cpmm.addMessageListener("Schema:Add", this);
}
},
- initExtension(data) {
- let policy;
- if (isContentProcess) {
+ initExtensionPolicy(data, extension) {
+ let policy = WebExtensionPolicy.getByID(data.id);
+ if (!policy) {
+ let localizeCallback = (
+ extension ? extension.localize.bind(extension)
+ : str => extensions.get(policy).localize(str));
+
policy = new WebExtensionPolicy({
id: data.id,
mozExtensionHostname: data.uuid,
baseURL: data.resourceURL,
permissions: Array.from(data.permissions),
allowedOrigins: new MatchPatternSet(data.whiteListedHosts),
webAccessibleResources: data.webAccessibleResources.map(host => new MatchGlob(host)),
contentSecurityPolicy: data.manifest.content_security_policy,
- localizeCallback: str => extensions.get(policy).localize(str),
+ localizeCallback,
backgroundScripts: (data.manifest.background &&
data.manifest.background.scripts),
contentScripts: (data.manifest.content_scripts || []).map(parseScriptOptions),
});
policy.active = true;
- } else {
- policy = WebExtensionPolicy.getByID(data.id);
+ policy.initData = data;
}
+ return policy;
+ },
- policy.initData = data;
+ initExtension(data) {
+ let policy = this.initExtensionPolicy(data);
DocumentManager.initExtension(policy);
},
receiveMessage({name, data}) {
switch (name) {
case "Extension:Startup": {
this.initExtension(data);
@@ -370,16 +376,22 @@ function ExtensionProcessScript() {
}
ExtensionProcessScript.singleton = null;
ExtensionProcessScript.prototype = {
classID: Components.ID("{21f9819e-4cdf-49f9-85a0-850af91a5058}"),
QueryInterface: XPCOMUtils.generateQI([Ci.mozIExtensionProcessScript]),
+ get wrappedJSObject() { return this; },
+
+ initExtension(data) {
+ return ExtensionManager.initExtensionPolicy(data);
+ },
+
initExtensionDocument(policy, doc) {
if (DocumentManager.globals.has(getMessageManager(doc.defaultView))) {
DocumentManager.loadInto(policy, doc.defaultView);
}
},
preloadContentScript(contentScript) {
contentScripts.get(contentScript).preload();