--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -185,54 +185,61 @@ function copyProperties(aObject, aProper
});
return aTarget;
}
// Maps instances of AddonInternal to AddonWrapper
const wrapperMap = new WeakMap();
let addonFor = wrapper => wrapperMap.get(wrapper);
+const EMPTY_ARRAY = Object.freeze([]);
+
+let AddonWrapper;
+
/**
* The AddonInternal is an internal only representation of add-ons. It may
* have come from the database (see DBAddonInternal in XPIDatabase.jsm)
* or an install manifest.
*/
-function AddonInternal() {
- this._hasResourceCache = new Map();
-
- XPCOMUtils.defineLazyGetter(this, "wrapper", () => {
- return new AddonWrapper(this);
- });
-}
-
-AddonInternal.prototype = {
- _selectedLocale: null,
- _hasResourceCache: null,
- active: false,
- visible: false,
- userDisabled: false,
- appDisabled: false,
- softDisabled: false,
- blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
- blocklistURL: null,
- sourceURI: null,
- releaseNotesURI: null,
- foreignInstall: false,
- seen: true,
- skinnable: false,
- startupData: null,
-
- /**
- * @property {Array<string>} dependencies
- * An array of bootstrapped add-on IDs on which this add-on depends.
- * The add-on will remain appDisabled if any of the dependent
- * add-ons is not installed and enabled.
- */
- dependencies: Object.freeze([]),
- hasEmbeddedWebExtension: false,
+class AddonInternal {
+ constructor() {
+ this._hasResourceCache = new Map();
+
+ this._wrapper = null;
+ this._selectedLocale = null;
+ this.active = false;
+ this.visible = false;
+ this.userDisabled = false;
+ this.appDisabled = false;
+ this.softDisabled = false;
+ this.blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ this.blocklistURL = null;
+ this.sourceURI = null;
+ this.releaseNotesURI = null;
+ this.foreignInstall = false;
+ this.seen = true;
+ this.skinnable = false;
+ this.startupData = null;
+
+ /**
+ * @property {Array<string>} dependencies
+ * An array of bootstrapped add-on IDs on which this add-on depends.
+ * The add-on will remain appDisabled if any of the dependent
+ * add-ons is not installed and enabled.
+ */
+ this.dependencies = EMPTY_ARRAY;
+ this.hasEmbeddedWebExtension = false;
+ }
+
+ get wrapper() {
+ if (!this._wrapper) {
+ this._wrapper = new AddonWrapper(this);
+ }
+ return this._wrapper;
+ }
get selectedLocale() {
if (this._selectedLocale)
return this._selectedLocale;
/**
* this.locales is a list of objects that have property `locales`.
* It's value is an array of locale codes.
@@ -271,21 +278,21 @@ AddonInternal.prototype = {
* Otherwise, we'll go through all locale entries looking for the one
* that has the best match in it's locales list.
*/
this._selectedLocale = this.locales.find(loc =>
loc.locales.includes(bestLocale));
}
return this._selectedLocale;
- },
+ }
get providesUpdatesSecurely() {
return !this.updateURL || this.updateURL.startsWith("https:");
- },
+ }
get isCorrectlySigned() {
switch (this._installLocation.name) {
case KEY_APP_SYSTEM_ADDONS:
// System add-ons must be signed by the system key.
return this.signedState == AddonManager.SIGNEDSTATE_SYSTEM;
case KEY_APP_SYSTEM_DEFAULTS:
@@ -301,29 +308,29 @@ AddonInternal.prototype = {
if (Services.appinfo.OS != "Darwin")
return true;
break;
}
if (this.signedState === AddonManager.SIGNEDSTATE_NOT_REQUIRED)
return true;
return this.signedState > AddonManager.SIGNEDSTATE_MISSING;
- },
+ }
get unpack() {
return this.type === "dictionary";
- },
+ }
get isCompatible() {
return this.isCompatibleWith();
- },
+ }
get disabled() {
return (this.userDisabled || this.appDisabled || this.softDisabled);
- },
+ }
get isPlatformCompatible() {
if (this.targetPlatforms.length == 0)
return true;
let matchedOS = false;
// If any targetPlatform matches the OS and contains an ABI then we will
@@ -354,17 +361,17 @@ AddonInternal.prototype = {
+ JSON.stringify(this.targetPlatforms);
logger.error(message, e);
AddonManagerPrivate.recordException("XPI", message, e);
// don't trust this add-on
return false;
}
return matchedOS && !needsABI;
- },
+ }
isCompatibleWith(aAppVersion, aPlatformVersion) {
let app = this.matchingTargetApplication;
if (!app)
return false;
// set reasonable defaults for minVersion and maxVersion
let minVersion = app.minVersion || "0";
@@ -408,41 +415,41 @@ AddonInternal.prototype = {
Services.vc.compare(minCompatVersion, maxVersion) > 0)
return false;
return Services.vc.compare(version, minVersion) >= 0;
}
return (Services.vc.compare(version, minVersion) >= 0) &&
(Services.vc.compare(version, maxVersion) <= 0);
- },
+ }
get matchingTargetApplication() {
let app = null;
for (let targetApp of this.targetApplications) {
if (targetApp.id == Services.appinfo.ID)
return targetApp;
if (targetApp.id == TOOLKIT_ID)
app = targetApp;
}
return app;
- },
+ }
async findBlocklistEntry() {
let staticItem = findMatchingStaticBlocklistItem(this);
if (staticItem) {
let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
return {
state: staticItem.level,
url: url.replace(/%blockID%/g, staticItem.blockID)
};
}
return Services.blocklist.getAddonBlocklistEntry(this.wrapper);
- },
+ }
async updateBlocklistState(options = {}) {
let {applySoftBlock = true, oldAddon = null, updateDatabase = true} = options;
if (oldAddon) {
this.userDisabled = oldAddon.userDisabled;
this.softDisabled = oldAddon.softDisabled;
this.blocklistState = oldAddon.blocklistState;
@@ -478,30 +485,30 @@ AddonInternal.prototype = {
this.appDisabled = !XPIDatabase.isUsableAddon(this);
if (userDisabled !== undefined) {
this.userDisabled = userDisabled;
}
if (softDisabled !== undefined) {
this.softDisabled = softDisabled;
}
}
- },
+ }
applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
for (let targetApp of this.targetApplications) {
for (let updateTarget of aUpdate.targetApplications) {
if (targetApp.id == updateTarget.id && (aSyncCompatibility ||
Services.vc.compare(targetApp.maxVersion, updateTarget.maxVersion) < 0)) {
targetApp.minVersion = updateTarget.minVersion;
targetApp.maxVersion = updateTarget.maxVersion;
}
}
}
this.appDisabled = !XPIDatabase.isUsableAddon(this);
- },
+ }
/**
* toJSON is called by JSON.stringify in order to create a filtered version
* of this object to be serialized to a JSON file. A new object is returned
* with copies of all non-private properties. Functions, getters and setters
* are not copied.
*
* @returns {Object}
@@ -530,17 +537,17 @@ AddonInternal.prototype = {
// Ignore functions
if (typeof this[prop] == "function")
continue;
obj[prop] = this[prop];
}
return obj;
- },
+ }
/**
* When an add-on install is pending its metadata will be cached in a file.
* This method reads particular properties of that metadata that may be newer
* than that in the install manifest, like compatibility information.
*
* @param {Object} aObj
* A JS object containing the cached metadata
@@ -550,17 +557,17 @@ AddonInternal.prototype = {
if (!(prop in aObj))
continue;
this[prop] = aObj[prop];
}
// Compatibility info may have changed so update appDisabled
this.appDisabled = !XPIDatabase.isUsableAddon(this);
- },
+ }
permissions() {
let permissions = 0;
// Add-ons that aren't installed cannot be modified in any way
if (!(this.inDatabase))
return permissions;
@@ -587,63 +594,63 @@ AddonInternal.prototype = {
if (Services.policies &&
!Services.policies.isAllowed(`modify-extension:${this.id}`)) {
permissions &= ~AddonManager.PERM_CAN_UNINSTALL;
permissions &= ~AddonManager.PERM_CAN_DISABLE;
}
return permissions;
- },
-};
+ }
+}
/**
* The AddonWrapper wraps an Addon to provide the data visible to consumers of
* the public API.
*
* @param {AddonInternal} aAddon
* The add-on object to wrap.
*/
-function AddonWrapper(aAddon) {
- wrapperMap.set(this, aAddon);
-}
-
-AddonWrapper.prototype = {
+AddonWrapper = class {
+ constructor(aAddon) {
+ wrapperMap.set(this, aAddon);
+ }
+
get __AddonInternal__() {
return AppConstants.DEBUG ? addonFor(this) : undefined;
- },
+ }
get seen() {
return addonFor(this).seen;
- },
+ }
get hasEmbeddedWebExtension() {
return addonFor(this).hasEmbeddedWebExtension;
- },
+ }
markAsSeen() {
addonFor(this).seen = true;
XPIDatabase.saveChanges();
- },
+ }
get type() {
return XPIInternal.getExternalType(addonFor(this).type);
- },
+ }
get isWebExtension() {
return isWebExtension(addonFor(this).type);
- },
+ }
get temporarilyInstalled() {
return addonFor(this)._installLocation == XPIInternal.TemporaryInstallLocation;
- },
+ }
get aboutURL() {
return this.isActive ? addonFor(this).aboutURL : null;
- },
+ }
get optionsURL() {
if (!this.isActive) {
return null;
}
let addon = addonFor(this);
if (addon.optionsURL) {
@@ -658,17 +665,17 @@ AddonWrapper.prototype = {
}
let base = policy.getURL();
return new URL(addon.optionsURL, base).href;
}
return addon.optionsURL;
}
return null;
- },
+ }
get optionsType() {
if (!this.isActive)
return null;
let addon = addonFor(this);
let hasOptionsURL = !!this.optionsURL;
@@ -677,30 +684,30 @@ AddonWrapper.prototype = {
case AddonManager.OPTIONS_TYPE_TAB:
case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:
return hasOptionsURL ? addon.optionsType : null;
}
return null;
}
return null;
- },
+ }
get optionsBrowserStyle() {
let addon = addonFor(this);
return addon.optionsBrowserStyle;
- },
+ }
get iconURL() {
return AddonManager.getPreferredIconURL(this, 48);
- },
+ }
get icon64URL() {
return AddonManager.getPreferredIconURL(this, 64);
- },
+ }
get icons() {
let addon = addonFor(this);
let icons = {};
if (addon._repositoryAddon) {
for (let size in addon._repositoryAddon.icons) {
icons[size] = addon._repositoryAddon.icons[size];
@@ -728,38 +735,38 @@ AddonWrapper.prototype = {
}
if (canUseIconURLs && addon.icon64URL) {
icons[64] = addon.icon64URL;
}
Object.freeze(icons);
return icons;
- },
+ }
get screenshots() {
let addon = addonFor(this);
let repositoryAddon = addon._repositoryAddon;
if (repositoryAddon && ("screenshots" in repositoryAddon)) {
let repositoryScreenshots = repositoryAddon.screenshots;
if (repositoryScreenshots && repositoryScreenshots.length > 0)
return repositoryScreenshots;
}
if (isTheme(addon.type) && this.hasResource("preview.png")) {
let url = this.getResourceURI("preview.png").spec;
return [new AddonManagerPrivate.AddonScreenshot(url)];
}
return null;
- },
+ }
get applyBackgroundUpdates() {
return addonFor(this).applyBackgroundUpdates;
- },
+ }
set applyBackgroundUpdates(val) {
let addon = addonFor(this);
if (val != AddonManager.AUTOUPDATE_DEFAULT &&
val != AddonManager.AUTOUPDATE_DISABLE &&
val != AddonManager.AUTOUPDATE_ENABLE) {
val = val ? AddonManager.AUTOUPDATE_DEFAULT :
AddonManager.AUTOUPDATE_DISABLE;
}
@@ -768,50 +775,50 @@ AddonWrapper.prototype = {
return val;
XPIDatabase.setAddonProperties(addon, {
applyBackgroundUpdates: val
});
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["applyBackgroundUpdates"]);
return val;
- },
+ }
set syncGUID(val) {
let addon = addonFor(this);
if (addon.syncGUID == val)
return val;
if (addon.inDatabase)
XPIDatabase.setAddonSyncGUID(addon, val);
addon.syncGUID = val;
return val;
- },
+ }
get install() {
let addon = addonFor(this);
if (!("_install" in addon) || !addon._install)
return null;
return addon._install.wrapper;
- },
+ }
get pendingUpgrade() {
let addon = addonFor(this);
return addon.pendingUpgrade ? addon.pendingUpgrade.wrapper : null;
- },
+ }
get scope() {
let addon = addonFor(this);
if (addon._installLocation)
return addon._installLocation.scope;
return AddonManager.SCOPE_PROFILE;
- },
+ }
get pendingOperations() {
let addon = addonFor(this);
let pending = 0;
if (!(addon.inDatabase)) {
// Add-on is pending install if there is no associated install (shouldn't
// happen here) or if the install is in the process of or has successfully
// completed the install. If an add-on is pending install then we ignore
@@ -829,58 +836,58 @@ AddonWrapper.prototype = {
pending |= AddonManager.PENDING_DISABLE;
else if (!addon.active && !addon.disabled)
pending |= AddonManager.PENDING_ENABLE;
if (addon.pendingUpgrade)
pending |= AddonManager.PENDING_UPGRADE;
return pending;
- },
+ }
get operationsRequiringRestart() {
return 0;
- },
+ }
get isDebuggable() {
return this.isActive && addonFor(this).bootstrap;
- },
+ }
get permissions() {
return addonFor(this).permissions();
- },
+ }
get isActive() {
let addon = addonFor(this);
if (!addon.active)
return false;
if (!Services.appinfo.inSafeMode)
return true;
return addon.bootstrap && XPIInternal.canRunInSafeMode(addon);
- },
+ }
get startupPromise() {
let addon = addonFor(this);
if (!addon.bootstrap || !this.isActive)
return null;
let activeAddon = XPIProvider.activeAddons.get(addon.id);
if (activeAddon)
return activeAddon.startupPromise || null;
return null;
- },
+ }
updateBlocklistState(applySoftBlock = true) {
return addonFor(this).updateBlocklistState({applySoftBlock});
- },
+ }
get userDisabled() {
let addon = addonFor(this);
return addon.softDisabled || addon.userDisabled;
- },
+ }
set userDisabled(val) {
let addon = addonFor(this);
if (val == this.userDisabled) {
return val;
}
if (addon.inDatabase) {
// hidden and system add-ons should not be user disabled,
@@ -892,17 +899,17 @@ AddonWrapper.prototype = {
} else {
addon.userDisabled = val;
// When enabling remove the softDisabled flag
if (!val)
addon.softDisabled = false;
}
return val;
- },
+ }
set softDisabled(val) {
let addon = addonFor(this);
if (val == addon.softDisabled)
return val;
if (addon.inDatabase) {
// When softDisabling a theme just enable the active theme
@@ -913,69 +920,69 @@ AddonWrapper.prototype = {
XPIDatabase.updateAddonDisabledState(addon, undefined, val);
}
} else if (!addon.userDisabled) {
// Only set softDisabled if not already disabled
addon.softDisabled = val;
}
return val;
- },
+ }
get hidden() {
let addon = addonFor(this);
if (addon._installLocation.name == KEY_APP_TEMPORARY)
return false;
return addon._installLocation.isSystem;
- },
+ }
get isSystem() {
let addon = addonFor(this);
return addon._installLocation.isSystem;
- },
+ }
// Returns true if Firefox Sync should sync this addon. Only addons
// in the profile install location are considered syncable.
get isSyncable() {
let addon = addonFor(this);
return (addon._installLocation.name == KEY_APP_PROFILE);
- },
+ }
get userPermissions() {
return addonFor(this).userPermissions;
- },
+ }
isCompatibleWith(aAppVersion, aPlatformVersion) {
return addonFor(this).isCompatibleWith(aAppVersion, aPlatformVersion);
- },
+ }
uninstall(alwaysAllowUndo) {
let addon = addonFor(this);
XPIProvider.uninstallAddon(addon, alwaysAllowUndo);
- },
+ }
cancelUninstall() {
let addon = addonFor(this);
XPIProvider.cancelUninstallAddon(addon);
- },
+ }
findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
new UpdateChecker(addonFor(this), aListener, aReason, aAppVersion, aPlatformVersion);
- },
+ }
// Returns true if there was an update in progress, false if there was no update to cancel
cancelUpdate() {
let addon = addonFor(this);
if (addon._updateCheck) {
addon._updateCheck.cancel();
return true;
}
return false;
- },
+ }
hasResource(aPath) {
let addon = addonFor(this);
if (addon._hasResourceCache.has(aPath))
return addon._hasResourceCache.get(aPath);
let bundle = addon._sourceBundle.clone();
@@ -1004,17 +1011,17 @@ AddonWrapper.prototype = {
addon._hasResourceCache.set(aPath, result);
return result;
} catch (e) {
addon._hasResourceCache.set(aPath, false);
return false;
} finally {
zipReader.close();
}
- },
+ }
/**
* Reloads the add-on.
*
* For temporarily installed add-ons, this uninstalls and re-installs the
* add-on. Otherwise, the addon is disabled and then re-enabled, and the cache
* is flushed.
*
@@ -1032,17 +1039,17 @@ AddonWrapper.prototype = {
Services.obs.notifyObservers(addonFile, "flush-cache-entry");
XPIDatabase.updateAddonDisabledState(addon, false);
resolve();
} else {
// This function supports re-installing an existing add-on.
resolve(AddonManager.installTemporaryAddon(addon._sourceBundle));
}
});
- },
+ }
/**
* Returns a URI to the selected resource or to the add-on bundle if aPath
* is null. URIs to the bundle will always be file: URIs. URIs to resources
* will be file: URIs if the add-on is unpacked or jar: URIs if the add-on is
* still an XPI file.
*
* @param {string?} aPath
@@ -1199,76 +1206,75 @@ PROP_LOCALE_MULTI.forEach(function(aProp
* The DBAddonInternal is a special AddonInternal that has been retrieved from
* the database. The constructor will initialize the DBAddonInternal with a set
* of fields, which could come from either the JSON store or as an
* XPIProvider.AddonInternal created from an addon's manifest
* @constructor
* @param {Object} aLoaded
* Addon data fields loaded from JSON or the addon manifest.
*/
-function DBAddonInternal(aLoaded) {
- AddonInternal.call(this);
-
- if (aLoaded.descriptor) {
- if (!aLoaded.path) {
- aLoaded.path = descriptorToPath(aLoaded.descriptor);
+class DBAddonInternal extends AddonInternal {
+ constructor(aLoaded) {
+ super();
+
+ if (aLoaded.descriptor) {
+ if (!aLoaded.path) {
+ aLoaded.path = descriptorToPath(aLoaded.descriptor);
+ }
+ delete aLoaded.descriptor;
}
- delete aLoaded.descriptor;
+
+ copyProperties(aLoaded, PROP_JSON_FIELDS, this);
+
+ if (!this.dependencies)
+ this.dependencies = [];
+ Object.freeze(this.dependencies);
+
+ if (aLoaded._installLocation) {
+ this._installLocation = aLoaded._installLocation;
+ this.location = aLoaded._installLocation.name;
+ } else if (aLoaded.location) {
+ this._installLocation = XPIProvider.installLocationsByName[this.location];
+ }
+
+ this._key = this.location + ":" + this.id;
+
+ if (!aLoaded._sourceBundle) {
+ throw new Error("Expected passed argument to contain a path");
+ }
+
+ this._sourceBundle = aLoaded._sourceBundle;
}
- copyProperties(aLoaded, PROP_JSON_FIELDS, this);
-
- if (!this.dependencies)
- this.dependencies = [];
- Object.freeze(this.dependencies);
-
- if (aLoaded._installLocation) {
- this._installLocation = aLoaded._installLocation;
- this.location = aLoaded._installLocation.name;
- } else if (aLoaded.location) {
- this._installLocation = XPIProvider.installLocationsByName[this.location];
- }
-
- this._key = this.location + ":" + this.id;
-
- if (!aLoaded._sourceBundle) {
- throw new Error("Expected passed argument to contain a path");
- }
-
- this._sourceBundle = aLoaded._sourceBundle;
-}
-
-DBAddonInternal.prototype = Object.create(AddonInternal.prototype);
-Object.assign(DBAddonInternal.prototype, {
applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
let wasCompatible = this.isCompatible;
this.targetApplications.forEach(function(aTargetApp) {
aUpdate.targetApplications.forEach(function(aUpdateTarget) {
if (aTargetApp.id == aUpdateTarget.id && (aSyncCompatibility ||
Services.vc.compare(aTargetApp.maxVersion, aUpdateTarget.maxVersion) < 0)) {
aTargetApp.minVersion = aUpdateTarget.minVersion;
aTargetApp.maxVersion = aUpdateTarget.maxVersion;
XPIDatabase.saveChanges();
}
});
});
if (wasCompatible != this.isCompatible)
XPIDatabase.updateAddonDisabledState(this);
- },
+ }
toJSON() {
return copyProperties(this, PROP_JSON_FIELDS);
- },
+ }
get inDatabase() {
return true;
}
-});
+}
/**
* @typedef {Map<string, DBAddonInternal>} AddonDB
*/
/**
* Internal interface: find an addon from an already loaded addonDB.
*