--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -237,39 +237,42 @@ const TYPES = {
};
if (!AppConstants.RELEASE_OR_BETA)
TYPES.apiextension = 256;
// Some add-on types that we track internally are presented as other types
// externally
const TYPE_ALIASES = {
+ "apiextension": "extension",
"webextension": "extension",
- "apiextension": "extension",
+ "webextension-theme": "theme",
};
const CHROME_TYPES = new Set([
"extension",
"locale",
"experiment",
]);
const RESTARTLESS_TYPES = new Set([
- "webextension",
+ "apiextension",
"dictionary",
"experiment",
"locale",
- "apiextension",
+ "webextension",
+ "webextension-theme",
]);
const SIGNED_TYPES = new Set([
- "webextension",
+ "apiextension",
"extension",
"experiment",
- "apiextension",
+ "webextension",
+ "webextension-theme",
]);
// This is a random number array that can be used as "salt" when generating
// an automatic ID based on the directory path of an add-on. It will prevent
// someone from creating an ID for a permanent add-on that could be replaced
// by a temporary add-on (because that would be confusing, I guess).
const TEMP_INSTALL_ID_GEN_SESSION =
new Uint8Array(Float64Array.of(Math.random()).buffer);
@@ -396,16 +399,27 @@ function findMatchingStaticBlocklistItem
/**
* Converts an iterable of addon objects into a map with the add-on's ID as key.
*/
function addonMap(addons) {
return new Map(addons.map(a => [a.id, a]));
}
/**
+ * Helper function that determines whether an addon of a certain type is a
+ * WebExtension.
+ *
+ * @param {String} type
+ * @return {Boolean}
+ */
+function isWebExtension(type) {
+ return type == "webextension" || type == "webextension-theme";
+}
+
+/**
* Sets permissions on a file
*
* @param aFile
* The file or directory to operate on.
* @param aPermissions
* The permisions to set
*/
function setFilePermissions(aFile, aPermissions) {
@@ -928,16 +942,17 @@ function getRDFProperty(aDs, aResource,
var loadManifestFromWebManifest = Task.async(function*(aUri) {
// We're passed the URI for the manifest file. Get the URI for its
// parent directory.
let uri = NetUtil.newURI("./", null, aUri);
let extension = new ExtensionData(uri);
let manifest = yield extension.readManifest();
+ let theme = !!manifest.theme;
// Read the list of available locales, and pre-load messages for
// all locales.
let locales = yield extension.initAllLocales();
// If there were any errors loading the extension, bail out now.
if (extension.errors.length)
throw new Error("Extension is invalid");
@@ -951,17 +966,17 @@ var loadManifestFromWebManifest = Task.a
// A * is illegal in strict_min_version
if (bss.strict_min_version && bss.strict_min_version.split(".").some(part => part == "*")) {
throw new Error("The use of '*' in strict_min_version is invalid");
}
let addon = new AddonInternal();
addon.id = bss.id;
addon.version = manifest.version;
- addon.type = "webextension";
+ addon.type = "webextension" + (theme ? "-theme" : "");
addon.unpack = false;
addon.strictCompatibility = true;
addon.bootstrap = true;
addon.hasBinaryComponents = false;
addon.multiprocessCompatible = true;
addon.internalName = null;
addon.updateURL = bss.update_url;
addon.updateKey = null;
@@ -4435,18 +4450,19 @@ this.XPIProvider = {
* Determine if an add-on should be blocking e10s if enabled.
*
* @param aAddon
* The add-on to test
* @return true if enabling the add-on should block e10s
*/
isBlockingE10s(aAddon) {
if (aAddon.type != "extension" &&
+ aAddon.type != "theme" &&
aAddon.type != "webextension" &&
- aAddon.type != "theme")
+ aAddon.type != "webextension-theme")
return false;
// The hotfix is exempt
let hotfixID = Preferences.get(PREF_EM_HOTFIX_ID, undefined);
if (hotfixID && hotfixID == aAddon.id)
return false;
// The default theme is exempt
@@ -4723,17 +4739,17 @@ this.XPIProvider = {
metadata: { addonID: aId } });
logger.error("Attempted to load bootstrap scope from missing directory " + aFile.path);
return;
}
let uri = getURIForResourceInFile(aFile, "bootstrap.js").spec;
if (aType == "dictionary")
uri = "resource://gre/modules/addons/SpellCheckDictionaryBootstrap.js"
- else if (aType == "webextension")
+ else if (isWebExtension(aType))
uri = "resource://gre/modules/addons/WebExtensionBootstrap.js"
else if (aType == "apiextension")
uri = "resource://gre/modules/addons/APIExtensionBootstrap.js"
activeAddon.bootstrapScope =
new Cu.Sandbox(principal, { sandboxName: uri,
wantGlobalProperties: ["indexedDB"],
addonId: aId,
@@ -5523,17 +5539,17 @@ class AddonInstall {
if (this.existingAddon) {
// Check various conditions related to upgrades
if (this.addon.id != this.existingAddon.id) {
zipreader.close();
return Promise.reject([AddonManager.ERROR_INCORRECT_ID,
`Refusing to upgrade addon ${this.existingAddon.id} to different ID ${this.addon.id}`]);
}
- if (this.existingAddon.type == "webextension" && this.addon.type != "webextension") {
+ if (isWebExtension(this.existingAddon.type) && !isWebExtension(this.addon.type)) {
zipreader.close();
return Promise.reject([AddonManager.ERROR_UNEXPECTED_ADDON_TYPE,
"WebExtensions may not be upated to other extension types"]);
}
}
if (mustSign(this.addon.type)) {
if (this.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
@@ -7206,17 +7222,17 @@ AddonWrapper.prototype = {
XPIDatabase.saveChanges();
},
get type() {
return getExternalType(addonFor(this).type);
},
get isWebExtension() {
- return addonFor(this).type == "webextension";
+ return isWebExtension(addonFor(this).type);
},
get temporarilyInstalled() {
return addonFor(this)._installLocation == TemporaryInstallLocation;
},
get aboutURL() {
return this.isActive ? addonFor(this)["aboutURL"] : null;