--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -339,280 +339,234 @@ function webAPIForAddon(addon) {
result.canUninstall = Boolean(addon.permissions & AddonManager.PERM_CAN_UNINSTALL);
return result;
}
/**
* A helper class to repeatedly call a listener with each object in an array
* optionally checking whether the object has a method in it.
- *
- * @param aObjects
- * The array of objects to iterate through
- * @param aMethod
- * An optional method name, if not null any objects without this method
- * will not be passed to the listener
- * @param aListener
- * A listener implementing nextObject and noMoreObjects methods. The
- * former will be called with the AsyncObjectCaller as the first
- * parameter and the object as the second. noMoreObjects will be passed
- * just the AsyncObjectCaller
*/
-function AsyncObjectCaller(aObjects, aMethod, aListener) {
- this.objects = [...aObjects];
- this.method = aMethod;
- this.listener = aListener;
-
- this.callNext();
-}
-
-AsyncObjectCaller.prototype = {
- objects: null,
- method: null,
- listener: null,
+ class AsyncObjectCaller {
+ /**
+ * @param aObjects
+ * The array of objects to iterate through
+ * @param aMethod
+ * An optional method name, if not null any objects without this method
+ * will not be passed to the listener
+ * @param aListener
+ * A listener implementing nextObject and noMoreObjects methods. The
+ * former will be called with the AsyncObjectCaller as the first
+ * parameter and the object as the second. noMoreObjects will be passed
+ * just the AsyncObjectCaller
+ */
+ constructor(aObjects, aMethod, aListener) {
+ this.objects = [...aObjects];
+ this.method = aMethod;
+ this.listener = aListener;
+
+ this.callNext();
+ }
/**
* Passes the next object to the listener or calls noMoreObjects if there
* are none left.
*/
- callNext: function() {
+ callNext() {
if (this.objects.length == 0) {
this.listener.noMoreObjects(this);
return;
}
let object = this.objects.shift();
if (!this.method || this.method in object)
this.listener.nextObject(this, object);
else
this.callNext();
}
-};
+}
/**
* Listens for a browser changing origin and cancels the installs that were
* started by it.
*/
-function BrowserListener(aBrowser, aInstallingPrincipal, aInstalls) {
- this.browser = aBrowser;
- this.principal = aInstallingPrincipal;
- this.installs = aInstalls;
- this.installCount = aInstalls.length;
-
- aBrowser.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
- Services.obs.addObserver(this, "message-manager-close", true);
-
- for (let install of this.installs)
- install.addListener(this);
-
- this.registered = true;
-}
-
-BrowserListener.prototype = {
- browser: null,
- installs: null,
- installCount: null,
- registered: false,
-
- unregister: function() {
+ class BrowserListener {
+ constructor(aBrowser, aInstallingPrincipal, aInstalls) {
+ this.browser = aBrowser;
+ this.principal = aInstallingPrincipal;
+ this.installs = aInstalls;
+ this.installCount = aInstalls.length;
+
+ aBrowser.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
+ Services.obs.addObserver(this, "message-manager-close", true);
+
+ for (let install of this.installs)
+ install.addListener(this);
+
+ this.registered = true;
+ }
+
+ unregister() {
if (!this.registered)
return;
this.registered = false;
Services.obs.removeObserver(this, "message-manager-close");
// The browser may have already been detached
if (this.browser.removeProgressListener)
this.browser.removeProgressListener(this);
for (let install of this.installs)
install.removeListener(this);
this.installs = null;
- },
-
- cancelInstalls: function() {
+ }
+
+ cancelInstalls() {
for (let install of this.installs) {
try {
install.cancel();
}
catch (e) {
// Some installs may have already failed or been cancelled, ignore these
}
}
- },
-
- observe: function(subject, topic, data) {
+ }
+
+ observe(subject, topic, data) {
if (subject != this.browser.messageManager)
return;
// The browser's message manager has closed and so the browser is
// going away, cancel all installs
this.cancelInstalls();
- },
-
- onLocationChange: function(webProgress, request, location) {
+ }
+
+ onLocationChange(webProgress, request, location) {
if (this.browser.contentPrincipal && this.principal.subsumes(this.browser.contentPrincipal))
return;
// The browser has navigated to a new origin so cancel all installs
this.cancelInstalls();
- },
-
- onDownloadCancelled: function(install) {
+ }
+
+ onDownloadCancelled(install) {
// Don't need to hear more events from this install
install.removeListener(this);
// Once all installs have ended unregister everything
if (--this.installCount == 0)
this.unregister();
- },
-
- onDownloadFailed: function(install) {
+ }
+
+ onDownloadFailed(install) {
this.onDownloadCancelled(install);
- },
-
- onInstallFailed: function(install) {
+ }
+
+ onInstallFailed(install) {
this.onDownloadCancelled(install);
- },
-
- onInstallEnded: function(install) {
+ }
+
+ onInstallEnded(install) {
this.onDownloadCancelled(install);
- },
-
- QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
- Ci.nsIWebProgressListener,
- Ci.nsIObserver])
-};
+ }
+
+ get QueryInterface() {
+ XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
+ Ci.nsIWebProgressListener,
+ Ci.nsIObserver])
+ }
+}
/**
* This represents an author of an add-on (e.g. creator or developer)
- *
- * @param aName
- * The name of the author
- * @param aURL
- * The URL of the author's profile page
*/
-function AddonAuthor(aName, aURL) {
- this.name = aName;
- this.url = aURL;
-}
-
-AddonAuthor.prototype = {
- name: null,
- url: null,
+ class AddonAuthor {
+ /**
+ * @param aName
+ * The name of the author
+ * @param aURL
+ * The URL of the author's profile page
+ */
+ constructor(aName, aURL) {
+ this.name = aName;
+ this.url = aURL;
+ }
// Returns the author's name, defaulting to the empty string
- toString: function() {
+ toString() {
return this.name || "";
}
}
/**
* This represents an screenshot for an add-on
- *
- * @param aURL
- * The URL to the full version of the screenshot
- * @param aWidth
- * The width in pixels of the screenshot
- * @param aHeight
- * The height in pixels of the screenshot
- * @param aThumbnailURL
- * The URL to the thumbnail version of the screenshot
- * @param aThumbnailWidth
- * The width in pixels of the thumbnail version of the screenshot
- * @param aThumbnailHeight
- * The height in pixels of the thumbnail version of the screenshot
- * @param aCaption
- * The caption of the screenshot
*/
-function AddonScreenshot(aURL, aWidth, aHeight, aThumbnailURL,
- aThumbnailWidth, aThumbnailHeight, aCaption) {
- this.url = aURL;
- if (aWidth) this.width = aWidth;
- if (aHeight) this.height = aHeight;
- if (aThumbnailURL) this.thumbnailURL = aThumbnailURL;
- if (aThumbnailWidth) this.thumbnailWidth = aThumbnailWidth;
- if (aThumbnailHeight) this.thumbnailHeight = aThumbnailHeight;
- if (aCaption) this.caption = aCaption;
-}
-
-AddonScreenshot.prototype = {
- url: null,
- width: null,
- height: null,
- thumbnailURL: null,
- thumbnailWidth: null,
- thumbnailHeight: null,
- caption: null,
+class AddonScreenshot {
+ /**
+ * @param aURL
+ * The URL to the full version of the screenshot
+ * @param aWidth
+ * The width in pixels of the screenshot
+ * @param aHeight
+ * The height in pixels of the screenshot
+ * @param aThumbnailURL
+ * The URL to the thumbnail version of the screenshot
+ * @param aThumbnailWidth
+ * The width in pixels of the thumbnail version of the screenshot
+ * @param aThumbnailHeight
+ * The height in pixels of the thumbnail version of the screenshot
+ * @param aCaption
+ * The caption of the screenshot
+ */
+ constructor(aURL, aWidth, aHeight, aThumbnailURL,
+ aThumbnailWidth, aThumbnailHeight, aCaption) {
+ this.url = aURL;
+ if (aWidth) this.width = aWidth;
+ if (aHeight) this.height = aHeight;
+ if (aThumbnailURL) this.thumbnailURL = aThumbnailURL;
+ if (aThumbnailWidth) this.thumbnailWidth = aThumbnailWidth;
+ if (aThumbnailHeight) this.thumbnailHeight = aThumbnailHeight;
+ if (aCaption) this.caption = aCaption;
+ }
// Returns the screenshot URL, defaulting to the empty string
- toString: function() {
+ toString() {
return this.url || "";
}
}
/**
* This represents a compatibility override for an addon.
- *
- * @param aType
- * Overrride type - "compatible" or "incompatible"
- * @param aMinVersion
- * Minimum version of the addon to match
- * @param aMaxVersion
- * Maximum version of the addon to match
- * @param aAppID
- * Application ID used to match appMinVersion and appMaxVersion
- * @param aAppMinVersion
- * Minimum version of the application to match
- * @param aAppMaxVersion
- * Maximum version of the application to match
*/
-function AddonCompatibilityOverride(aType, aMinVersion, aMaxVersion, aAppID,
- aAppMinVersion, aAppMaxVersion) {
- this.type = aType;
- this.minVersion = aMinVersion;
- this.maxVersion = aMaxVersion;
- this.appID = aAppID;
- this.appMinVersion = aAppMinVersion;
- this.appMaxVersion = aAppMaxVersion;
-}
-
-AddonCompatibilityOverride.prototype = {
+class AddonCompatibilityOverride {
/**
- * Type of override - "incompatible" or "compatible".
- * Only "incompatible" is supported for now.
+ * @param aType
+ * Overrride type - "compatible" or "incompatible"
+ * @param aMinVersion
+ * Minimum version of the addon to match
+ * @param aMaxVersion
+ * Maximum version of the addon to match
+ * @param aAppID
+ * Application ID used to match appMinVersion and appMaxVersion
+ * @param aAppMinVersion
+ * Minimum version of the application to match
+ * @param aAppMaxVersion
+ * Maximum version of the application to match
*/
- type: null,
-
- /**
- * Min version of the addon to match.
- */
- minVersion: null,
-
- /**
- * Max version of the addon to match.
- */
- maxVersion: null,
-
- /**
- * Application ID to match.
- */
- appID: null,
-
- /**
- * Min version of the application to match.
- */
- appMinVersion: null,
-
- /**
- * Max version of the application to match.
- */
- appMaxVersion: null
-};
-
+ constructor(aType, aMinVersion, aMaxVersion, aAppID,
+ aAppMinVersion, aAppMaxVersion) {
+ this.type = aType;
+ this.minVersion = aMinVersion;
+ this.maxVersion = aMaxVersion;
+ this.appID = aAppID;
+ this.appMinVersion = aAppMinVersion;
+ this.appMaxVersion = aAppMaxVersion;
+ }
+}
/**
* A type of add-on, used by the UI to determine how to display different types
* of add-ons.
*
* @param aID
* The add-on type ID
* @param aLocaleURI
--- a/toolkit/mozapps/extensions/DeferredSave.jsm
+++ b/toolkit/mozapps/extensions/DeferredSave.jsm
@@ -83,125 +83,125 @@ var PrefObserver = {
PrefObserver.init();
/**
* A module to manage deferred, asynchronous writing of data files
* to disk. Writing is deferred by waiting for a specified delay after
* a request to save the data, before beginning to write. If more than
* one save request is received during the delay, all requests are
* fulfilled by a single write.
- *
- * @constructor
- * @param aPath
- * String representing the full path of the file where the data
- * is to be written.
- * @param aDataProvider
- * Callback function that takes no argument and returns the data to
- * be written. If aDataProvider returns an ArrayBufferView, the
- * bytes it contains are written to the file as is.
- * If aDataProvider returns a String the data are UTF-8 encoded
- * and then written to the file.
- * @param [optional] aDelay
- * The delay in milliseconds between the first saveChanges() call
- * that marks the data as needing to be saved, and when the DeferredSave
- * begins writing the data to disk. Default 50 milliseconds.
*/
-this.DeferredSave = function(aPath, aDataProvider, aDelay) {
- // Create a new logger (child of 'DeferredSave' logger)
- // for use by this particular instance of DeferredSave object
- let leafName = OS.Path.basename(aPath);
- let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName;
- this.logger = Log.repository.getLogger(logger_id);
+class DeferredSave {
+ /**
+ * @param aPath
+ * String representing the full path of the file where the data
+ * is to be written.
+ * @param aDataProvider
+ * Callback function that takes no argument and returns the data to
+ * be written. If aDataProvider returns an ArrayBufferView, the
+ * bytes it contains are written to the file as is.
+ * If aDataProvider returns a String the data are UTF-8 encoded
+ * and then written to the file.
+ * @param [optional] aDelay
+ * The delay in milliseconds between the first saveChanges() call
+ * that marks the data as needing to be saved, and when the DeferredSave
+ * begins writing the data to disk. Default 50 milliseconds.
+ */
+ constructor(aPath, aDataProvider, aDelay) {
+ // Create a new logger (child of 'DeferredSave' logger)
+ // for use by this particular instance of DeferredSave object
+ let leafName = OS.Path.basename(aPath);
+ let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName;
+ this.logger = Log.repository.getLogger(logger_id);
- // @type {Deferred|null}, null when no data needs to be written
- // @resolves with the result of OS.File.writeAtomic when all writes complete
- // @rejects with the error from OS.File.writeAtomic if the write fails,
- // or with the error from aDataProvider() if that throws.
- this._pending = null;
+ // @type {Deferred|null}, null when no data needs to be written
+ // @resolves with the result of OS.File.writeAtomic when all writes complete
+ // @rejects with the error from OS.File.writeAtomic if the write fails,
+ // or with the error from aDataProvider() if that throws.
+ this._pending = null;
- // @type {Promise}, completes when the in-progress write (if any) completes,
- // kept as a resolved promise at other times to simplify logic.
- // Because _deferredSave() always uses _writing.then() to execute
- // its next action, we don't need a special case for whether a write
- // is in progress - if the previous write is complete (and the _writing
- // promise is already resolved/rejected), _writing.then() starts
- // the next action immediately.
- //
- // @resolves with the result of OS.File.writeAtomic
- // @rejects with the error from OS.File.writeAtomic
- this._writing = Promise.resolve(0);
+ // @type {Promise}, completes when the in-progress write (if any) completes,
+ // kept as a resolved promise at other times to simplify logic.
+ // Because _deferredSave() always uses _writing.then() to execute
+ // its next action, we don't need a special case for whether a write
+ // is in progress - if the previous write is complete (and the _writing
+ // promise is already resolved/rejected), _writing.then() starts
+ // the next action immediately.
+ //
+ // @resolves with the result of OS.File.writeAtomic
+ // @rejects with the error from OS.File.writeAtomic
+ this._writing = Promise.resolve(0);
- // Are we currently waiting for a write to complete
- this.writeInProgress = false;
+ // Are we currently waiting for a write to complete
+ this.writeInProgress = false;
- this._path = aPath;
- this._dataProvider = aDataProvider;
+ this._path = aPath;
+ this._dataProvider = aDataProvider;
- this._timer = null;
+ this._timer = null;
- // Some counters for telemetry
- // The total number of times the file was written
- this.totalSaves = 0;
+ // Some counters for telemetry
+ // The total number of times the file was written
+ this.totalSaves = 0;
- // The number of times the data became dirty while
- // another save was in progress
- this.overlappedSaves = 0;
+ // The number of times the data became dirty while
+ // another save was in progress
+ this.overlappedSaves = 0;
- // Error returned by the most recent write (if any)
- this._lastError = null;
+ // Error returned by the most recent write (if any)
+ this._lastError = null;
- if (aDelay && (aDelay > 0))
- this._delay = aDelay;
- else
- this._delay = DEFAULT_SAVE_DELAY_MS;
-}
+ if (aDelay && (aDelay > 0))
+ this._delay = aDelay;
+ else
+ this._delay = DEFAULT_SAVE_DELAY_MS;
+ }
-this.DeferredSave.prototype = {
get dirty() {
return this._pending || this.writeInProgress;
- },
+ }
get lastError() {
return this._lastError;
- },
+ }
// Start the pending timer if data is dirty
- _startTimer: function() {
+ _startTimer() {
if (!this._pending) {
return;
}
this.logger.debug("Starting timer");
if (!this._timer)
this._timer = MakeTimer();
this._timer.initWithCallback(() => this._deferredSave(),
this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
- },
+ }
/**
* Mark the current stored data dirty, and schedule a flush to disk
* @return A Promise<integer> that will be resolved after the data is written to disk;
* the promise is resolved with the number of bytes written.
*/
- saveChanges: function() {
+ saveChanges() {
this.logger.debug("Save changes");
if (!this._pending) {
if (this.writeInProgress) {
this.logger.debug("Data changed while write in progress");
this.overlappedSaves++;
}
this._pending = Promise.defer();
// Wait until the most recent write completes or fails (if it hasn't already)
// and then restart our timer
this._writing.then(count => this._startTimer(), error => this._startTimer());
}
return this._pending.promise;
- },
+ }
- _deferredSave: function() {
+ _deferredSave() {
let pending = this._pending;
this._pending = null;
let writing = this._writing;
this._writing = pending.promise;
// In either the success or the exception handling case, we don't need to handle
// the error from _writing here; it's already being handled in another then()
let toSave = null;
@@ -233,17 +233,17 @@ this.DeferredSave.prototype = {
},
error => {
this._lastError = error;
this.writeInProgress = false;
this.logger.warn("Write failed", error);
pending.reject(error);
});
});
- },
+ }
/**
* Immediately save the dirty data to disk, skipping
* the delay of normal operation. Note that the write
* still happens asynchronously in the worker
* thread from OS.File.
*
* There are four possible situations:
@@ -252,24 +252,24 @@ this.DeferredSave.prototype = {
* 3) Data is currently being written, in-memory copy is clean
* 4) Data is being written and in-memory copy is dirty
*
* @return Promise<integer> that will resolve when all in-memory data
* has finished being flushed, returning the number of bytes
* written. If all in-memory data is clean, completes with the
* result of the most recent write.
*/
- flush: function() {
+ flush() {
// If we have pending changes, cancel our timer and set up the write
// immediately (_deferredSave queues the write for after the most
// recent write completes, if it hasn't already)
if (this._pending) {
this.logger.debug("Flush called while data is dirty");
if (this._timer) {
this._timer.cancel();
this._timer = null;
}
this._deferredSave();
}
return this._writing;
}
-};
+}
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -459,175 +459,175 @@ this.LightweightThemeManager = {
const wrapperMap = new WeakMap();
let themeFor = wrapper => wrapperMap.get(wrapper);
/**
* The AddonWrapper wraps lightweight theme to provide the data visible to
* consumers of the AddonManager API.
*/
-function AddonWrapper(aTheme) {
- wrapperMap.set(this, aTheme);
-}
+class AddonWrapper {
+ constructor(aTheme) {
+ wrapperMap.set(this, aTheme);
+ }
-AddonWrapper.prototype = {
get id() {
return themeFor(this).id + ID_SUFFIX;
- },
+ }
get type() {
return ADDON_TYPE;
- },
+ }
get isActive() {
let current = LightweightThemeManager.currentTheme;
if (current)
return themeFor(this).id == current.id;
return false;
- },
+ }
get name() {
return themeFor(this).name;
- },
+ }
get version() {
let theme = themeFor(this);
return "version" in theme ? theme.version : "";
- },
+ }
get creator() {
let theme = themeFor(this);
return "author" in theme ? new AddonManagerPrivate.AddonAuthor(theme.author) : null;
- },
+ }
get screenshots() {
let url = themeFor(this).previewURL;
return [new AddonManagerPrivate.AddonScreenshot(url)];
- },
+ }
get pendingOperations() {
let pending = AddonManager.PENDING_NONE;
if (this.isActive == this.userDisabled)
pending |= this.isActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE;
return pending;
- },
+ }
get operationsRequiringRestart() {
// If a non-default theme is in use then a restart will be required to
// enable lightweight themes unless dynamic theme switching is enabled
if (Services.prefs.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN)) {
try {
if (Services.prefs.getBoolPref(PREF_EM_DSS_ENABLED))
return AddonManager.OP_NEEDS_RESTART_NONE;
}
catch (e) {
}
return AddonManager.OP_NEEDS_RESTART_ENABLE;
}
return AddonManager.OP_NEEDS_RESTART_NONE;
- },
+ }
get size() {
// The size changes depending on whether the theme is in use or not, this is
// probably not worth exposing.
return null;
- },
+ }
get permissions() {
let permissions = 0;
// Do not allow uninstall of builtIn themes.
if (!LightweightThemeManager._builtInThemes.has(themeFor(this).id))
permissions = AddonManager.PERM_CAN_UNINSTALL;
if (this.userDisabled)
permissions |= AddonManager.PERM_CAN_ENABLE;
else
permissions |= AddonManager.PERM_CAN_DISABLE;
return permissions;
- },
+ }
get userDisabled() {
let id = themeFor(this).id;
if (_themeIDBeingEnabled == id)
return false;
if (_themeIDBeingDisabled == id)
return true;
try {
let toSelect = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
return id != toSelect;
}
catch (e) {
let current = LightweightThemeManager.currentTheme;
return !current || current.id != id;
}
- },
+ }
set userDisabled(val) {
if (val == this.userDisabled)
return val;
if (val)
LightweightThemeManager.currentTheme = null;
else
LightweightThemeManager.currentTheme = themeFor(this);
return val;
- },
+ }
// Lightweight themes are never disabled by the application
get appDisabled() {
return false;
- },
+ }
// Lightweight themes are always compatible
get isCompatible() {
return true;
- },
+ }
get isPlatformCompatible() {
return true;
- },
+ }
get scope() {
return AddonManager.SCOPE_PROFILE;
- },
+ }
get foreignInstall() {
return false;
- },
+ }
- uninstall: function() {
+ uninstall() {
LightweightThemeManager.forgetUsedTheme(themeFor(this).id);
- },
+ }
- cancelUninstall: function() {
+ cancelUninstall() {
throw new Error("Theme is not marked to be uninstalled");
- },
+ }
- findUpdates: function(listener, reason, appVersion, platformVersion) {
+ findUpdates(listener, reason, appVersion, platformVersion) {
AddonManagerPrivate.callNoUpdateListeners(this, listener, reason, appVersion, platformVersion);
- },
+ }
// Lightweight themes are always compatible
- isCompatibleWith: function(appVersion, platformVersion) {
+ isCompatibleWith(appVersion, platformVersion) {
return true;
- },
+ }
// Lightweight themes are always securely updated
get providesUpdatesSecurely() {
return true;
- },
+ }
// Lightweight themes are never blocklisted
get blocklistState() {
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
}
-};
+}
["description", "homepageURL", "iconURL"].forEach(function(prop) {
Object.defineProperty(AddonWrapper.prototype, prop, {
get: function() {
let theme = themeFor(this);
return prop in theme ? theme[prop] : null;
},
enumarable: true,
--- a/toolkit/mozapps/extensions/internal/AddonLogging.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonLogging.jsm
@@ -69,24 +69,22 @@ function getStackDetails(aException) {
catch (e) {
return {
sourceName: null,
lineNumber: 0
};
}
}
-function AddonLogger(aName) {
- this.name = aName;
-}
+class AddonLogger {
+ constructor(aName) {
+ this.name = aName;
+ }
-AddonLogger.prototype = {
- name: null,
-
- error: function(aStr, aException) {
+ error(aStr, aException) {
let message = formatLogMessage("error", this.name, aStr, aException);
let stack = getStackDetails(aException);
let consoleMessage = Cc["@mozilla.org/scripterror;1"].
createInstance(Ci.nsIScriptError);
consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0,
Ci.nsIScriptError.errorFlag, "component javascript");
@@ -117,41 +115,41 @@ AddonLogger.prototype = {
createInstance(Ci.nsIConverterOutputStream);
writer.init(stream, "UTF-8", 0, 0x0000);
writer.writeString(formatTimestamp(tstamp) + " " +
message + " at " + stack.sourceName + ":" +
stack.lineNumber + "\n");
writer.close();
}
catch (e) { }
- },
+ }
- warn: function(aStr, aException) {
+ warn(aStr, aException) {
let message = formatLogMessage("warn", this.name, aStr, aException);
let stack = getStackDetails(aException);
let consoleMessage = Cc["@mozilla.org/scripterror;1"].
createInstance(Ci.nsIScriptError);
consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0,
Ci.nsIScriptError.warningFlag, "component javascript");
Services.console.logMessage(consoleMessage);
if (gDebugLogEnabled)
dump("*** " + message + "\n");
- },
+ }
- log: function(aStr, aException) {
+ log(aStr, aException) {
if (gDebugLogEnabled) {
let message = formatLogMessage("log", this.name, aStr, aException);
dump("*** " + message + "\n");
Services.console.logStringMessage(message);
}
}
-};
+}
this.LogManager = {
getLogger: function(aName, aTarget) {
let logger = new AddonLogger(aName);
if (aTarget) {
["error", "warn", "log"].forEach(function(name) {
let fname = name.toUpperCase();
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -467,48 +467,51 @@ var AddonTestUtils = {
Services.obs.notifyObservers(file, "flush-cache-entry", "cert-override");
}
}
return [callback, result, cert];
}).bind(this);
- function FakeCertDB() {
- for (let property of Object.keys(realCertDB)) {
- if (property in this)
- continue;
+ class FakeCertDB {
+ constructor() {
+ for (let property of Object.keys(realCertDB)) {
+ if (property in this)
+ continue;
- if (typeof realCertDB[property] == "function")
- this[property] = realCertDB[property].bind(realCertDB);
+ if (typeof realCertDB[property] == "function")
+ this[property] = realCertDB[property].bind(realCertDB);
+ }
}
- }
- FakeCertDB.prototype = {
+
openSignedAppFileAsync(root, file, callback) {
// First try calling the real cert DB
realCertDB.openSignedAppFileAsync(root, file, (result, zipReader, cert) => {
verifyCert(file.clone(), result, cert, callback)
.then(([callback, result, cert]) => {
callback.openSignedAppFileFinished(result, zipReader, cert);
});
});
- },
+ }
verifySignedDirectoryAsync(root, dir, callback) {
// First try calling the real cert DB
realCertDB.verifySignedDirectoryAsync(root, dir, (result, cert) => {
verifyCert(dir.clone(), result, cert, callback)
.then(([callback, result, cert]) => {
callback.verifySignedDirectoryFinished(result, cert);
});
});
- },
+ }
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIX509CertDB]),
- };
+ get QueryInterface() {
+ return XPCOMUtils.generateQI([Ci.nsIX509CertDB]);
+ }
+ }
let certDBFactory = XPCOMUtils.generateSingletonFactory(FakeCertDB);
registrar.registerFactory(CERTDB_CID, "CertDB",
CERTDB_CONTRACTID, certDBFactory);
},
/**
* Starts up the add-on manager as if it was started by the application.
--- a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.jsm
@@ -56,79 +56,78 @@ var logger = Log.repository.getLogger(LO
/**
* A serialisation method for RDF data that produces an identical string
* for matching RDF graphs.
* The serialisation is not complete, only assertions stemming from a given
* resource are included, multiple references to the same resource are not
* permitted, and the RDF prolog and epilog are not included.
* RDF Blob and Date literals are not supported.
*/
-function RDFSerializer() {
- this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"].
- getService(Ci.nsIRDFContainerUtils);
- this.resources = [];
-}
-
-RDFSerializer.prototype = {
- INDENT: " ", // The indent used for pretty-printing
- resources: null, // Array of the resources that have been found
+class RDFSerializer {
+ constructor() {
+ this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"].
+ getService(Ci.nsIRDFContainerUtils);
+ this.resources = [];
+ this.INDENT = " "; // The indent used for pretty-printing
+ this.resources = null; // Array of the resources that have been found
+ }
/**
* Escapes characters from a string that should not appear in XML.
*
* @param aString
* The string to be escaped
* @return a string with all characters invalid in XML character data
* converted to entity references.
*/
- escapeEntities: function(aString) {
+ escapeEntities(aString) {
aString = aString.replace(/&/g, "&");
aString = aString.replace(/</g, "<");
aString = aString.replace(/>/g, ">");
return aString.replace(/"/g, """);
- },
+ }
/**
* Serializes all the elements of an RDF container.
*
* @param aDs
* The RDF datasource
* @param aContainer
* The RDF container to output the child elements of
* @param aIndent
* The current level of indent for pretty-printing
* @return a string containing the serialized elements.
*/
- serializeContainerItems: function(aDs, aContainer, aIndent) {
+ serializeContainerItems(aDs, aContainer, aIndent) {
var result = "";
var items = aContainer.GetElements();
while (items.hasMoreElements()) {
var item = items.getNext().QueryInterface(Ci.nsIRDFResource);
result += aIndent + "<RDF:li>\n"
result += this.serializeResource(aDs, item, aIndent + this.INDENT);
result += aIndent + "</RDF:li>\n"
}
return result;
- },
+ }
/**
* Serializes all em:* (see EM_NS) properties of an RDF resource except for
* the em:signature property. As this serialization is to be compared against
* the manifest signature it cannot contain the em:signature property itself.
*
* @param aDs
* The RDF datasource
* @param aResource
* The RDF resource that contains the properties to serialize
* @param aIndent
* The current level of indent for pretty-printing
* @return a string containing the serialized properties.
* @throws if the resource contains a property that cannot be serialized
*/
- serializeResourceProperties: function(aDs, aResource, aIndent) {
+ serializeResourceProperties(aDs, aResource, aIndent) {
var result = "";
var items = [];
var arcs = aDs.ArcLabelsOut(aResource);
while (arcs.hasMoreElements()) {
var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource);
if (arc.ValueUTF8.substring(0, PREFIX_NS_EM.length) != PREFIX_NS_EM)
continue;
var prop = arc.ValueUTF8.substring(PREFIX_NS_EM.length);
@@ -155,34 +154,34 @@ RDFSerializer.prototype = {
else {
throw Components.Exception("Cannot serialize unknown literal type");
}
}
}
items.sort();
result += items.join("");
return result;
- },
+ }
/**
* Recursively serializes an RDF resource and all resources it links to.
* This will only output EM_NS properties and will ignore any em:signature
* property.
*
* @param aDs
* The RDF datasource
* @param aResource
* The RDF resource to serialize
* @param aIndent
* The current level of indent for pretty-printing. If undefined no
* indent will be added
* @return a string containing the serialized resource.
* @throws if the RDF data contains multiple references to the same resource.
*/
- serializeResource: function(aDs, aResource, aIndent) {
+ serializeResource(aDs, aResource, aIndent) {
if (this.resources.indexOf(aResource) != -1 ) {
// We cannot output multiple references to the same resource.
throw Components.Exception("Cannot serialize multiple references to " + aResource.Value);
}
if (aIndent === undefined)
aIndent = "";
this.resources.push(aResource);
@@ -548,72 +547,68 @@ function parseJSONManifest(aId, aUpdateK
results.push(result);
}
return results;
}
/**
* Starts downloading an update manifest and then passes it to an appropriate
* parser to convert to an array of update objects
- *
- * @param aId
- * The ID of the add-on being checked for updates
- * @param aUpdateKey
- * An optional update key for the add-on
- * @param aUrl
- * The URL of the update manifest
- * @param aObserver
- * An observer to pass results to
*/
-function UpdateParser(aId, aUpdateKey, aUrl, aObserver) {
- this.id = aId;
- this.updateKey = aUpdateKey;
- this.observer = aObserver;
- this.url = aUrl;
-
- let requireBuiltIn = true;
- try {
- requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
- }
- catch (e) {
- }
+class UpdateParser {
+ /**
+ *
+ * @param aId
+ * The ID of the add-on being checked for updates
+ * @param aUpdateKey
+ * An optional update key for the add-on
+ * @param aUrl
+ * The URL of the update manifest
+ * @param aObserver
+ * An observer to pass results to
+ */
+ constructor(aId, aUpdateKey, aUrl, aObserver) {
+ this.id = aId;
+ this.updateKey = aUpdateKey;
+ this.observer = aObserver;
+ this.url = aUrl;
- logger.debug("Requesting " + aUrl);
- try {
- this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
- createInstance(Ci.nsIXMLHttpRequest);
- this.request.open("GET", this.url, true);
- this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
- this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
- // Prevent the request from writing to cache.
- this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
- this.request.overrideMimeType("text/plain");
- this.request.setRequestHeader("Moz-XPI-Update", "1", true);
- this.request.timeout = TIMEOUT;
- this.request.addEventListener("load", () => this.onLoad(), false);
- this.request.addEventListener("error", () => this.onError(), false);
- this.request.addEventListener("timeout", () => this.onTimeout(), false);
- this.request.send(null);
+ let requireBuiltIn = true;
+ try {
+ requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
+ }
+ catch (e) {
+ }
+
+ logger.debug("Requesting " + aUrl);
+ try {
+ this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+ createInstance(Ci.nsIXMLHttpRequest);
+ this.request.open("GET", this.url, true);
+ this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
+ this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+ // Prevent the request from writing to cache.
+ this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+ this.request.overrideMimeType("text/plain");
+ this.request.setRequestHeader("Moz-XPI-Update", "1", true);
+ this.request.timeout = TIMEOUT;
+ this.request.addEventListener("load", () => this.onLoad(), false);
+ this.request.addEventListener("error", () => this.onError(), false);
+ this.request.addEventListener("timeout", () => this.onTimeout(), false);
+ this.request.send(null);
+ }
+ catch (e) {
+ logger.error("Failed to request update manifest", e);
+ }
}
- catch (e) {
- logger.error("Failed to request update manifest", e);
- }
-}
-
-UpdateParser.prototype = {
- id: null,
- updateKey: null,
- observer: null,
- request: null,
- url: null,
/**
* Called when the manifest has been successfully loaded.
*/
- onLoad: function() {
+ onLoad() {
let request = this.request;
this.request = null;
this._doneAt = new Error("place holder");
let requireBuiltIn = true;
try {
requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
}
@@ -684,32 +679,32 @@ UpdateParser.prototype = {
}
catch (e) {
logger.warn("onUpdateCheckComplete notification failed", e);
}
}
else {
logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
}
- },
+ }
/**
* Called when the request times out
*/
- onTimeout: function() {
+ onTimeout() {
this.request = null;
this._doneAt = new Error("Timed out");
logger.warn("Request for " + this.url + " timed out");
this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT);
- },
+ }
/**
* Called when the manifest failed to load.
*/
- onError: function() {
+ onError() {
if (!Components.isSuccessCode(this.request.status)) {
logger.warn("Request failed: " + this.url + " - " + this.request.status);
}
else if (this.request.channel instanceof Ci.nsIHttpChannel) {
try {
if (this.request.channel.requestSucceeded) {
logger.warn("Request failed: " + this.url + " - " +
this.request.channel.responseStatus + ": " +
@@ -723,46 +718,46 @@ UpdateParser.prototype = {
else {
logger.warn("Request failed for an unknown reason");
}
this.request = null;
this._doneAt = new Error("UP_onError");
this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
- },
+ }
/**
* Helper method to notify the observer that an error occured.
*/
- notifyError: function(aStatus) {
+ notifyError(aStatus) {
if ("onUpdateCheckError" in this.observer) {
try {
this.observer.onUpdateCheckError(aStatus);
}
catch (e) {
logger.warn("onUpdateCheckError notification failed", e);
}
}
- },
+ }
/**
* Called to cancel an in-progress update check.
*/
- cancel: function() {
+ cancel() {
if (!this.request) {
logger.error("Trying to cancel already-complete request", this._doneAt);
return;
}
this.request.abort();
this.request = null;
this._doneAt = new Error("UP_cancel");
this.notifyError(AddonUpdateChecker.ERROR_CANCELLED);
}
-};
+}
/**
* Tests if an update matches a version of the application or platform
*
* @param aUpdate
* The available update
* @param aAppVersion
* The application version to use
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -113,154 +113,155 @@ function configureLogging() {
}
/**
* The GMPWrapper provides the info for the various GMP plugins to public
* callers through the API.
*/
-function GMPWrapper(aPluginInfo) {
- this._plugin = aPluginInfo;
- this._log =
- Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
- "GMPWrapper(" +
- this._plugin.id + ") ");
- Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
- this._plugin.id),
- this.onPrefEnabledChanged, this);
- Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
- this._plugin.id),
- this.onPrefVersionChanged, this);
- if (this._plugin.isEME) {
- Preferences.observe(GMPPrefs.KEY_EME_ENABLED,
- this.onPrefEMEGlobalEnabledChanged, this);
- messageManager.addMessageListener("EMEVideo:ContentMediaKeysRequest", this);
+ class GMPWrapper {
+ constructor(aPluginInfo) {
+ // An active task that checks for plugin updates and installs them.
+ this._updateTask = null;
+ this._gmpPath = null;
+ this._isUpdateCheckPending = false;
+
+ this.optionsType = AddonManager.OPTIONS_TYPE_INLINE;
+
+ this._plugin = aPluginInfo;
+ this._log =
+ Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
+ "GMPWrapper(" +
+ this._plugin.id + ") ");
+ Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
+ this._plugin.id),
+ this.onPrefEnabledChanged, this);
+ Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
+ this._plugin.id),
+ this.onPrefVersionChanged, this);
+ if (this._plugin.isEME) {
+ Preferences.observe(GMPPrefs.KEY_EME_ENABLED,
+ this.onPrefEMEGlobalEnabledChanged, this);
+ messageManager.addMessageListener("EMEVideo:ContentMediaKeysRequest", this);
+ }
}
-}
-GMPWrapper.prototype = {
- // An active task that checks for plugin updates and installs them.
- _updateTask: null,
- _gmpPath: null,
- _isUpdateCheckPending: false,
+ get optionsURL() { return this._plugin.optionsURL; }
- optionsType: AddonManager.OPTIONS_TYPE_INLINE,
- get optionsURL() { return this._plugin.optionsURL; },
-
- set gmpPath(aPath) { this._gmpPath = aPath; },
+ set gmpPath(aPath) { this._gmpPath = aPath; }
get gmpPath() {
if (!this._gmpPath && this.isInstalled) {
this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
this._plugin.id,
GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION,
null, this._plugin.id));
}
return this._gmpPath;
- },
+ }
- get id() { return this._plugin.id; },
- get type() { return "plugin"; },
- get isGMPlugin() { return true; },
- get name() { return this._plugin.name; },
- get creator() { return null; },
- get homepageURL() { return this._plugin.homepageURL; },
+ get id() { return this._plugin.id; }
+ get type() { return "plugin"; }
+ get isGMPlugin() { return true; }
+ get name() { return this._plugin.name; }
+ get creator() { return null; }
+ get homepageURL() { return this._plugin.homepageURL; }
- get description() { return this._plugin.description; },
- get fullDescription() { return this._plugin.fullDescription; },
+ get description() { return this._plugin.description; }
+ get fullDescription() { return this._plugin.fullDescription; }
get version() { return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, null,
- this._plugin.id); },
+ this._plugin.id); }
get isActive() {
return !this.appDisabled &&
!this.userDisabled &&
!GMPUtils.isPluginHidden(this._plugin);
- },
+ }
get appDisabled() {
if (this._plugin.isEME && !GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true)) {
// If "media.eme.enabled" is false, all EME plugins are disabled.
return true;
}
return false;
- },
+ }
get userDisabled() {
return !GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ENABLED, true, this._plugin.id);
- },
+ }
set userDisabled(aVal) { GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ENABLED,
aVal === false,
- this._plugin.id); },
+ this._plugin.id); }
- get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; },
- get size() { return 0; },
- get scope() { return AddonManager.SCOPE_APPLICATION; },
- get pendingOperations() { return AddonManager.PENDING_NONE; },
+ get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; }
+ get size() { return 0; }
+ get scope() { return AddonManager.SCOPE_APPLICATION; }
+ get pendingOperations() { return AddonManager.PENDING_NONE; }
- get operationsRequiringRestart() { return AddonManager.OP_NEEDS_RESTART_NONE },
+ get operationsRequiringRestart() { return AddonManager.OP_NEEDS_RESTART_NONE }
get permissions() {
let permissions = 0;
if (!this.appDisabled) {
permissions |= AddonManager.PERM_CAN_UPGRADE;
permissions |= this.userDisabled ? AddonManager.PERM_CAN_ENABLE :
AddonManager.PERM_CAN_DISABLE;
}
return permissions;
- },
+ }
get updateDate() {
let time = Number(GMPPrefs.get(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, null,
this._plugin.id));
if (!isNaN(time) && this.isInstalled) {
return new Date(time * 1000)
}
return null;
- },
+ }
get isCompatible() {
return true;
- },
+ }
get isPlatformCompatible() {
return true;
- },
+ }
get providesUpdatesSecurely() {
return true;
- },
+ }
get foreignInstall() {
return false;
- },
+ }
- isCompatibleWith: function(aAppVersion, aPlatformVersion) {
+ isCompatibleWith(aAppVersion, aPlatformVersion) {
return true;
- },
+ }
get applyBackgroundUpdates() {
if (!GMPPrefs.isSet(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id)) {
return AddonManager.AUTOUPDATE_DEFAULT;
}
return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id) ?
AddonManager.AUTOUPDATE_ENABLE : AddonManager.AUTOUPDATE_DISABLE;
- },
+ }
set applyBackgroundUpdates(aVal) {
if (aVal == AddonManager.AUTOUPDATE_DEFAULT) {
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id);
} else if (aVal == AddonManager.AUTOUPDATE_ENABLE) {
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id);
} else if (aVal == AddonManager.AUTOUPDATE_DISABLE) {
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, this._plugin.id);
}
- },
+ }
- findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
this._log.trace("findUpdates() - " + this._plugin.id + " - reason=" +
aReason);
AddonManagerPrivate.callNoUpdateListeners(this, aListener);
if (aReason === AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
if (!AddonManager.shouldAutoUpdate(this)) {
this._log.trace("findUpdates() - " + this._plugin.id +
@@ -308,41 +309,41 @@ GMPWrapper.prototype = {
throw e;
} finally {
this._updateTask = null;
return true;
}
}.bind(this));
return this._updateTask;
- },
+ }
- get pluginMimeTypes() { return []; },
+ get pluginMimeTypes() { return []; }
get pluginLibraries() {
if (this.isInstalled) {
let path = this.version;
return [path];
}
return [];
- },
+ }
get pluginFullpath() {
if (this.isInstalled) {
let path = OS.Path.join(OS.Constants.Path.profileDir,
this._plugin.id,
this.version);
return [path];
}
return [];
- },
+ }
get isInstalled() {
return this.version && this.version.length > 0;
- },
+ }
- _handleEnabledChanged: function() {
+ _handleEnabledChanged() {
this._log.info("_handleEnabledChanged() id=" +
this._plugin.id + " isActive=" + this.isActive);
AddonManagerPrivate.callAddonListeners(this.isActive ?
"onEnabling" : "onDisabling",
this, false);
if (this._gmpPath) {
if (this.isActive) {
@@ -353,19 +354,19 @@ GMPWrapper.prototype = {
this._log.info("onPrefEnabledChanged() - removing gmp directory " +
this._gmpPath);
gmpService.removePluginDirectory(this._gmpPath);
}
}
AddonManagerPrivate.callAddonListeners(this.isActive ?
"onEnabled" : "onDisabled",
this);
- },
+ }
- onPrefEMEGlobalEnabledChanged: function() {
+ onPrefEMEGlobalEnabledChanged() {
this._log.info("onPrefEMEGlobalEnabledChanged() id=" + this._plugin.id +
" appDisabled=" + this.appDisabled + " isActive=" + this.isActive +
" hidden=" + GMPUtils.isPluginHidden(this._plugin));
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this,
["appDisabled"]);
// If EME or the GMP itself are disabled, uninstall the GMP.
// Otherwise, check for updates, so we download and install the GMP.
@@ -376,59 +377,59 @@ GMPWrapper.prototype = {
null, false);
AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
AddonManagerPrivate.callAddonListeners("onInstalled", this);
this.checkForUpdates(GMP_CHECK_DELAY);
}
if (!this.userDisabled) {
this._handleEnabledChanged();
}
- },
+ }
- checkForUpdates: function(delay) {
+ checkForUpdates(delay) {
if (this._isUpdateCheckPending) {
return;
}
this._isUpdateCheckPending = true;
GMPPrefs.reset(GMPPrefs.KEY_UPDATE_LAST_CHECK, null);
// Delay this in case the user changes his mind and doesn't want to
// enable EME after all.
setTimeout(() => {
if (!this.appDisabled) {
let gmpInstallManager = new GMPInstallManager();
// We don't really care about the results, if someone is interested
// they can check the log.
gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
}
this._isUpdateCheckPending = false;
}, delay);
- },
+ }
- receiveMessage: function({target: browser, data: data}) {
+ receiveMessage({target: browser, data: data}) {
this._log.trace("receiveMessage() data=" + data);
let parsedData;
try {
parsedData = JSON.parse(data);
} catch (ex) {
this._log.error("Malformed EME video message with data: " + data);
return;
}
let {status: status, keySystem: keySystem} = parsedData;
if (status == "cdm-not-installed") {
this.checkForUpdates(0);
}
- },
+ }
- onPrefEnabledChanged: function() {
+ onPrefEnabledChanged() {
if (!this._plugin.isEME || !this.appDisabled) {
this._handleEnabledChanged();
}
- },
+ }
- onPrefVersionChanged: function() {
+ onPrefVersionChanged() {
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
if (this._gmpPath) {
this._log.info("onPrefVersionChanged() - unregistering gmp directory " +
this._gmpPath);
gmpService.removeAndDeletePluginDirectory(this._gmpPath, true /* can defer */);
}
AddonManagerPrivate.callAddonListeners("onUninstalled", this);
@@ -443,47 +444,47 @@ GMPWrapper.prototype = {
null, this._plugin.id));
}
if (this._gmpPath && this.isActive) {
this._log.info("onPrefVersionChanged() - registering gmp directory " +
this._gmpPath);
gmpService.addPluginDirectory(this._gmpPath);
}
AddonManagerPrivate.callAddonListeners("onInstalled", this);
- },
+ }
- uninstallPlugin: function() {
+ uninstallPlugin() {
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
if (this.gmpPath) {
this._log.info("uninstallPlugin() - unregistering gmp directory " +
this.gmpPath);
gmpService.removeAndDeletePluginDirectory(this.gmpPath);
}
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_VERSION, this.id);
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_ABI, this.id);
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, this.id);
AddonManagerPrivate.callAddonListeners("onUninstalled", this);
- },
+ }
- shutdown: function() {
+ shutdown() {
Preferences.ignore(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
this._plugin.id),
this.onPrefEnabledChanged, this);
Preferences.ignore(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
this._plugin.id),
this.onPrefVersionChanged, this);
if (this._plugin.isEME) {
Preferences.ignore(GMPPrefs.KEY_EME_ENABLED,
this.onPrefEMEGlobalEnabledChanged, this);
messageManager.removeMessageListener("EMEVideo:ContentMediaKeysRequest", this);
}
return this._updateTask;
- },
+ }
- _arePluginFilesOnDisk: function() {
+ _arePluginFilesOnDisk() {
let fileExists = function(aGmpPath, aFileName) {
let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
let path = OS.Path.join(aGmpPath, aFileName);
f.initWithPath(path);
return f.exists();
};
let id = this._plugin.id.substring(4);
@@ -493,19 +494,19 @@ GMPWrapper.prototype = {
infoName = "manifest.json";
} else {
infoName = id + ".info";
}
return fileExists(this.gmpPath, libName) &&
fileExists(this.gmpPath, infoName) &&
(this._plugin.id != EME_ADOBE_ID || fileExists(this.gmpPath, id + ".voucher"));
- },
+ }
- validate: function() {
+ validate() {
if (!this.isInstalled) {
// Not installed -> Valid.
return {
installed: false,
valid: true
};
}
@@ -521,18 +522,18 @@ GMPWrapper.prototype = {
}
// Installed -> Check if files are missing.
let filesOnDisk = this._arePluginFilesOnDisk();
return {
installed: true,
valid: filesOnDisk
};
- },
-};
+ }
+}
var GMPProvider = {
get name() { return "GMPProvider"; },
_plugins: null,
startup: function() {
configureLogging();
--- a/toolkit/mozapps/extensions/internal/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/PluginProvider.jsm
@@ -295,75 +295,75 @@ function canDisableFlashProtectedMode(aP
const wrapperMap = new WeakMap();
let pluginFor = wrapper => wrapperMap.get(wrapper);
/**
* The PluginWrapper wraps a set of nsIPluginTags to provide the data visible to
* public callers through the API.
*/
-function PluginWrapper(id, name, description, tags) {
- wrapperMap.set(this, { id, name, description, tags });
-}
+class PluginWrapper {
+ constructor(id, name, description, tags) {
+ wrapperMap.set(this, { id, name, description, tags });
+ }
-PluginWrapper.prototype = {
get id() {
return pluginFor(this).id;
- },
+ }
get type() {
return "plugin";
- },
+ }
get name() {
return pluginFor(this).name;
- },
+ }
get creator() {
return null;
- },
+ }
get description() {
return pluginFor(this).description.replace(/<\/?[a-z][^>]*>/gi, " ");
- },
+ }
get version() {
let { tags: [tag] } = pluginFor(this);
return tag.version;
- },
+ }
get homepageURL() {
let { description } = pluginFor(this);
if (/<A\s+HREF=[^>]*>/i.test(description))
return /<A\s+HREF=["']?([^>"'\s]*)/i.exec(description)[1];
return null;
- },
+ }
get isActive() {
let { tags: [tag] } = pluginFor(this);
return !tag.blocklisted && !tag.disabled;
- },
+ }
get appDisabled() {
let { tags: [tag] } = pluginFor(this);
return tag.blocklisted;
- },
+ }
get userDisabled() {
let { tags: [tag] } = pluginFor(this);
if (tag.disabled)
return true;
if ((Services.prefs.getBoolPref("plugins.click_to_play") && tag.clicktoplay) ||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE ||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE)
return AddonManager.STATE_ASK_TO_ACTIVATE;
return false;
- },
+ }
set userDisabled(val) {
let previousVal = this.userDisabled;
if (val === previousVal)
return val;
let { tags } = pluginFor(this);
@@ -393,31 +393,31 @@ PluginWrapper.prototype = {
// If the 'userDisabled' value involved AddonManager.STATE_ASK_TO_ACTIVATE,
// call the onPropertyChanged listeners.
if (previousVal == AddonManager.STATE_ASK_TO_ACTIVATE ||
val == AddonManager.STATE_ASK_TO_ACTIVATE) {
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["userDisabled"]);
}
return val;
- },
+ }
get blocklistState() {
let { tags: [tag] } = pluginFor(this);
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
getService(Ci.nsIBlocklistService);
return bs.getPluginBlocklistState(tag);
- },
+ }
get blocklistURL() {
let { tags: [tag] } = pluginFor(this);
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
getService(Ci.nsIBlocklistService);
return bs.getPluginBlocklistURL(tag);
- },
+ }
get size() {
function getDirectorySize(aFile) {
let size = 0;
let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
let entry;
while ((entry = entries.nextFile)) {
if (entry.isSymlink() || !entry.isDirectory())
@@ -434,31 +434,31 @@ PluginWrapper.prototype = {
for (let tag of pluginFor(this).tags) {
file.initWithPath(tag.fullpath);
if (file.isDirectory())
size += getDirectorySize(file);
else
size += file.fileSize;
}
return size;
- },
+ }
get pluginLibraries() {
let libs = [];
for (let tag of pluginFor(this).tags)
libs.push(tag.filename);
return libs;
- },
+ }
get pluginFullpath() {
let paths = [];
for (let tag of pluginFor(this).tags)
paths.push(tag.fullpath);
return paths;
- },
+ }
get pluginMimeTypes() {
let types = [];
for (let tag of pluginFor(this).tags) {
let mimeTypes = tag.getMimeTypes({});
let mimeDescriptions = tag.getMimeDescriptions({});
let extensions = tag.getExtensions({});
for (let i = 0; i < mimeTypes.length; i++) {
@@ -466,25 +466,25 @@ PluginWrapper.prototype = {
type.type = mimeTypes[i];
type.description = mimeDescriptions[i];
type.suffixes = extensions[i];
types.push(type);
}
}
return types;
- },
+ }
get installDate() {
let date = 0;
for (let tag of pluginFor(this).tags) {
date = Math.max(date, tag.lastModifiedTime);
}
return new Date(date);
- },
+ }
get scope() {
let { tags: [tag] } = pluginFor(this);
let path = tag.fullpath;
// Plugins inside the application directory are in the application scope
let dir = Services.dirsvc.get("APlugns", Ci.nsIFile);
if (path.startsWith(dir.path))
return AddonManager.SCOPE_APPLICATION;
@@ -503,25 +503,25 @@ PluginWrapper.prototype = {
} catch (e) {
if (!e.result || e.result != Components.results.NS_ERROR_FAILURE)
throw e;
// Do nothing: missing "Home".
}
// Any other locations are system scope
return AddonManager.SCOPE_SYSTEM;
- },
+ }
get pendingOperations() {
return AddonManager.PENDING_NONE;
- },
+ }
get operationsRequiringRestart() {
return AddonManager.OP_NEEDS_RESTART_NONE;
- },
+ }
get permissions() {
let { tags: [tag] } = pluginFor(this);
let permissions = 0;
if (tag.isEnabledStateLocked) {
return permissions;
}
if (!this.appDisabled) {
@@ -540,61 +540,61 @@ PluginWrapper.prototype = {
permissions |= AddonManager.PERM_CAN_ASK_TO_ACTIVATE;
}
if (this.userDisabled !== false && !isCTPBlocklisted) {
permissions |= AddonManager.PERM_CAN_ENABLE;
}
}
return permissions;
- },
+ }
get optionsType() {
if (canDisableFlashProtectedMode(this)) {
return AddonManager.OPTIONS_TYPE_INLINE;
}
return AddonManager.OPTIONS_TYPE_INLINE_INFO;
- },
+ }
get optionsURL() {
return "chrome://mozapps/content/extensions/pluginPrefs.xul";
- },
+ }
get updateDate() {
return this.installDate;
- },
+ }
get isCompatible() {
return true;
- },
+ }
get isPlatformCompatible() {
return true;
- },
+ }
get providesUpdatesSecurely() {
return true;
- },
+ }
get foreignInstall() {
return true;
- },
+ }
- isCompatibleWith: function(aAppVersion, aPlatformVersion) {
+ isCompatibleWith(aAppVersion, aPlatformVersion) {
return true;
- },
+ }
- findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
if ("onNoCompatibilityUpdateAvailable" in aListener)
aListener.onNoCompatibilityUpdateAvailable(this);
if ("onNoUpdateAvailable" in aListener)
aListener.onNoUpdateAvailable(this);
if ("onUpdateFinished" in aListener)
aListener.onUpdateFinished(this);
}
-};
+}
AddonManagerPrivate.registerProvider(PluginProvider, [
new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 6000,
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE)
]);
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1,9 +1,9 @@
- /* This Source Code Form is subject to the terms of the Mozilla Public
+/* 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";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
@@ -443,26 +443,23 @@ function writeStringToFile(file, string)
* directory. The file or directory is moved or copied recursively and if
* anything fails an attempt is made to rollback the entire operation. The
* operation may also be rolled back to its original state after it has
* completed by calling the rollback method.
*
* Operations can be chained. Calling move or copy multiple times will remember
* the whole set and if one fails all of the operations will be rolled back.
*/
-function SafeInstallOperation() {
- this._installedFiles = [];
- this._createdDirs = [];
-}
-
-SafeInstallOperation.prototype = {
- _installedFiles: null,
- _createdDirs: null,
-
- _installFile: function(aFile, aTargetDirectory, aCopy) {
+class SafeInstallOperation {
+ constructor() {
+ this._installedFiles = [];
+ this._createdDirs = [];
+ }
+
+ _installFile(aFile, aTargetDirectory, aCopy) {
let oldFile = aCopy ? null : aFile.clone();
let newFile = aFile.clone();
try {
if (aCopy) {
newFile.copyTo(aTargetDirectory, null);
// copyTo does not update the nsIFile with the new.
newFile = aTargetDirectory.clone();
newFile.append(aFile.leafName);
@@ -475,19 +472,19 @@ SafeInstallOperation.prototype = {
}
}
catch (e) {
logger.error("Failed to " + (aCopy ? "copy" : "move") + " file " + aFile.path +
" to " + aTargetDirectory.path, e);
throw e;
}
this._installedFiles.push({ oldFile: oldFile, newFile: newFile });
- },
-
- _installDirectory: function(aDirectory, aTargetDirectory, aCopy) {
+ }
+
+ _installDirectory(aDirectory, aTargetDirectory, aCopy) {
if (aDirectory.contains(aTargetDirectory)) {
let err = new Error(`Not installing ${aDirectory} into its own descendent ${aTargetDirectory}`);
logger.error(err);
throw err;
}
let newDir = aTargetDirectory.clone();
newDir.append(aDirectory.leafName);
@@ -529,19 +526,19 @@ SafeInstallOperation.prototype = {
catch (e) {
logger.error("Failed to remove directory " + aDirectory.path, e);
throw e;
}
// Note we put the directory move in after all the file moves so the
// directory is recreated before all the files are moved back
this._installedFiles.push({ oldFile: aDirectory, newFile: newDir });
- },
-
- _installDirEntry: function(aDirEntry, aTargetDirectory, aCopy) {
+ }
+
+ _installDirEntry(aDirEntry, aTargetDirectory, aCopy) {
let isDir = null;
try {
isDir = aDirEntry.isDirectory() && !aDirEntry.isSymlink();
}
catch (e) {
// If the file has already gone away then don't worry about it, this can
// happen on OSX where the resource fork is automatically moved with the
@@ -560,85 +557,85 @@ SafeInstallOperation.prototype = {
else
this._installFile(aDirEntry, aTargetDirectory, aCopy);
}
catch (e) {
logger.error("Failure " + (aCopy ? "copying" : "moving") + " " + aDirEntry.path +
" to " + aTargetDirectory.path);
throw e;
}
- },
+ }
/**
* Moves a file or directory into a new directory. If an error occurs then all
* files that have been moved will be moved back to their original location.
*
* @param aFile
* The file or directory to be moved.
* @param aTargetDirectory
* The directory to move into, this is expected to be an empty
* directory.
*/
- moveUnder: function(aFile, aTargetDirectory) {
+ moveUnder(aFile, aTargetDirectory) {
try {
this._installDirEntry(aFile, aTargetDirectory, false);
}
catch (e) {
this.rollback();
throw e;
}
- },
+ }
/**
* Renames a file to a new location. If an error occurs then all
* files that have been moved will be moved back to their original location.
*
* @param aOldLocation
* The old location of the file.
* @param aNewLocation
* The new location of the file.
*/
- moveTo: function(aOldLocation, aNewLocation) {
+ moveTo(aOldLocation, aNewLocation) {
try {
let oldFile = aOldLocation.clone(), newFile = aNewLocation.clone();
oldFile.moveTo(newFile.parent, newFile.leafName);
this._installedFiles.push({ oldFile: oldFile, newFile: newFile, isMoveTo: true});
}
catch (e) {
this.rollback();
throw e;
}
- },
+ }
/**
* Copies a file or directory into a new directory. If an error occurs then
* all new files that have been created will be removed.
*
* @param aFile
* The file or directory to be copied.
* @param aTargetDirectory
* The directory to copy into, this is expected to be an empty
* directory.
*/
- copy: function(aFile, aTargetDirectory) {
+ copy(aFile, aTargetDirectory) {
try {
this._installDirEntry(aFile, aTargetDirectory, true);
}
catch (e) {
this.rollback();
throw e;
}
- },
+ }
/**
* Rolls back all the moves that this operation performed. If an exception
* occurs here then both old and new directories are left in an indeterminate
* state
*/
- rollback: function() {
+ rollback() {
while (this._installedFiles.length > 0) {
let move = this._installedFiles.pop();
if (move.isMoveTo) {
move.newFile.moveTo(move.oldDir.parent, move.oldDir.leafName);
}
else if (move.newFile.isDirectory() && !move.newFile.isSymlink()) {
let oldDir = move.oldFile.parent.clone();
oldDir.append(move.oldFile.leafName);
@@ -651,17 +648,17 @@ SafeInstallOperation.prototype = {
else {
move.newFile.moveTo(move.oldFile.parent, null);
}
}
while (this._createdDirs.length > 0)
recursiveRemove(this._createdDirs.pop());
}
-};
+}
/**
* Sets the userDisabled and softDisabled properties of an add-on based on what
* values those properties had for a previous instance of the add-on. The
* previous instance may be a previous install or in the case of an application
* version change the same add-on.
*
* NOTE: this may modify aNewAddon in place; callers should save the database if
@@ -2104,49 +2101,52 @@ function recordAddonTelemetry(aAddon) {
XPIProvider.setTelemetry(aAddon.id, "creator", locale.creator);
}
}
/**
* The on-disk state of an individual XPI, created from an Object
* as stored in the 'extensions.xpiState' pref.
*/
-function XPIState(saved) {
- for (let [short, long] of XPIState.prototype.fields) {
- if (short in saved) {
- this[long] = saved[short];
- }
- }
-}
-
-XPIState.prototype = {
- fields: [['d', 'descriptor'],
- ['e', 'enabled'],
- ['v', 'version'],
- ['st', 'scanTime'],
- ['mt', 'manifestTime']],
+class XPIState {
+ constructor(saved) {
+ for (let [short, long] of XPIState.fields) {
+ if (short in saved) {
+ this[long] = saved[short];
+ }
+ }
+ }
+
+ static get fields() {
+ return [['d', 'descriptor'],
+ ['e', 'enabled'],
+ ['v', 'version'],
+ ['st', 'scanTime'],
+ ['mt', 'manifestTime']];
+ }
+
/**
* Return the last modified time, based on enabled/disabled
*/
get mtime() {
if (!this.enabled && ('manifestTime' in this) && this.manifestTime > this.scanTime) {
return this.manifestTime;
}
return this.scanTime;
- },
+ }
toJSON() {
let json = {};
- for (let [short, long] of XPIState.prototype.fields) {
+ for (let [short, long] of XPIState.fields) {
if (long in this) {
json[short] = this[long];
}
}
return json;
- },
+ }
/**
* Update the last modified time for an add-on on disk.
* @param aFile: nsIFile path of the add-on.
* @param aId: The add-on ID.
* @return True if the time stamp has changed.
*/
getModTime(aFile, aId) {
@@ -2191,17 +2191,17 @@ XPIState.prototype = {
changed = true;
this.scanTime = 0;
}
}
// Record duration of file-modified check
XPIProvider.setTelemetry(aId, "scan_MS", Math.round(Cu.now() - scanStarted));
return changed;
- },
+ }
/**
* Update the XPIState to match an XPIDatabase entry; if 'enabled' is changed to true,
* update the last-modified time. This should probably be made async, but for now we
* don't want to maintain parallel sync and async versions of the scan.
* Caller is responsible for doing XPIStates.save() if necessary.
* @param aDBAddon The DBAddonInternal for this add-on.
* @param aUpdated The add-on was updated, so we must record new modified time.
@@ -2218,18 +2218,18 @@ XPIState.prototype = {
// XXX Eventually also copy bootstrap, etc.
if (aUpdated || mustGetMod) {
this.getModTime(new nsIFile(this.descriptor), aDBAddon.id);
if (this.scanTime != aDBAddon.updateDate) {
aDBAddon.updateDate = this.scanTime;
XPIDatabase.saveChanges();
}
}
- },
-};
+ }
+}
// Constructor for an ES6 Map that knows how to convert itself into a
// regular object for toJSON().
function SerializableMap() {
let m = new Map();
m.toJSON = function() {
let out = {}
for (let [key, val] of m) {
@@ -6866,98 +6866,93 @@ AddonInstallWrapper.prototype = {
return installFor(this)[aProp];
},
enumerable: true,
});
});
/**
* Creates a new update checker.
- *
- * @param aAddon
- * The add-on to check for updates
- * @param aListener
- * An UpdateListener to notify of updates
- * @param aReason
- * The reason for the update check
- * @param aAppVersion
- * An optional application version to check for updates for
- * @param aPlatformVersion
- * An optional platform version to check for updates for
- * @throws if the aListener or aReason arguments are not valid
*/
-function UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion) {
- if (!aListener || !aReason)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
-
- this.addon = aAddon;
- aAddon._updateCheck = this;
- XPIProvider.doing(this);
- this.listener = aListener;
- this.appVersion = aAppVersion;
- this.platformVersion = aPlatformVersion;
- this.syncCompatibility = (aReason == AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
-
- let updateURL = aAddon.updateURL;
- if (!updateURL) {
- if (aReason == AddonManager.UPDATE_WHEN_PERIODIC_UPDATE &&
- Services.prefs.getPrefType(PREF_EM_UPDATE_BACKGROUND_URL) == Services.prefs.PREF_STRING) {
- updateURL = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
- } else {
- updateURL = Services.prefs.getCharPref(PREF_EM_UPDATE_URL);
- }
- }
-
- const UPDATE_TYPE_COMPATIBILITY = 32;
- const UPDATE_TYPE_NEWVERSION = 64;
-
- aReason |= UPDATE_TYPE_COMPATIBILITY;
- if ("onUpdateAvailable" in this.listener)
- aReason |= UPDATE_TYPE_NEWVERSION;
-
- let url = escapeAddonURI(aAddon, updateURL, aReason, aAppVersion);
- this._parser = AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey,
- url, this);
-}
-
-UpdateChecker.prototype = {
- addon: null,
- listener: null,
- appVersion: null,
- platformVersion: null,
- syncCompatibility: null,
+class UpdateChecker {
+ /**
+ * @param aAddon
+ * The add-on to check for updates
+ * @param aListener
+ * An UpdateListener to notify of updates
+ * @param aReason
+ * The reason for the update check
+ * @param aAppVersion
+ * An optional application version to check for updates for
+ * @param aPlatformVersion
+ * An optional platform version to check for updates for
+ * @throws if the aListener or aReason arguments are not valid
+ */
+ constructor(aAddon, aListener, aReason, aAppVersion, aPlatformVersion) {
+ if (!aListener || !aReason)
+ throw Cr.NS_ERROR_INVALID_ARG;
+
+ Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
+
+ this.addon = aAddon;
+ aAddon._updateCheck = this;
+ XPIProvider.doing(this);
+ this.listener = aListener;
+ this.appVersion = aAppVersion;
+ this.platformVersion = aPlatformVersion;
+ this.syncCompatibility = (aReason == AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+
+ let updateURL = aAddon.updateURL;
+ if (!updateURL) {
+ if (aReason == AddonManager.UPDATE_WHEN_PERIODIC_UPDATE &&
+ Services.prefs.getPrefType(PREF_EM_UPDATE_BACKGROUND_URL) == Services.prefs.PREF_STRING) {
+ updateURL = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
+ } else {
+ updateURL = Services.prefs.getCharPref(PREF_EM_UPDATE_URL);
+ }
+ }
+
+ const UPDATE_TYPE_COMPATIBILITY = 32;
+ const UPDATE_TYPE_NEWVERSION = 64;
+
+ aReason |= UPDATE_TYPE_COMPATIBILITY;
+ if ("onUpdateAvailable" in this.listener)
+ aReason |= UPDATE_TYPE_NEWVERSION;
+
+ let url = escapeAddonURI(aAddon, updateURL, aReason, aAppVersion);
+ this._parser = AddonUpdateChecker.checkForUpdates(aAddon.id, aAddon.updateKey,
+ url, this);
+ }
/**
* Calls a method on the listener passing any number of arguments and
* consuming any exceptions.
*
* @param aMethod
* The method to call on the listener
*/
- callListener: function(aMethod, ...aArgs) {
+ callListener(aMethod, ...aArgs) {
if (!(aMethod in this.listener))
return;
try {
this.listener[aMethod].apply(this.listener, aArgs);
}
catch (e) {
logger.warn("Exception calling UpdateListener method " + aMethod, e);
}
- },
+ }
/**
* Called when AddonUpdateChecker completes the update check
*
* @param updates
* The list of update details for the add-on
*/
- onUpdateCheckComplete: function(aUpdates) {
+ onUpdateCheckComplete(aUpdates) {
XPIProvider.done(this.addon._updateCheck);
this.addon._updateCheck = null;
let AUC = AddonUpdateChecker;
let ignoreMaxVersion = false;
let ignoreStrictCompat = false;
if (!AddonManager.checkCompatibility) {
ignoreMaxVersion = true;
@@ -7043,93 +7038,96 @@ UpdateChecker.prototype = {
createUpdate(aInstall => {
sendUpdateAvailableMessages(this, aInstall);
}, this.addon, update);
}
else {
sendUpdateAvailableMessages(this, null);
}
- },
+ }
/**
* Called when AddonUpdateChecker fails the update check
*
* @param aError
* An error status
*/
- onUpdateCheckError: function(aError) {
+ onUpdateCheckError(aError) {
XPIProvider.done(this.addon._updateCheck);
this.addon._updateCheck = null;
this.callListener("onNoCompatibilityUpdateAvailable", this.addon.wrapper);
this.callListener("onNoUpdateAvailable", this.addon.wrapper);
this.callListener("onUpdateFinished", this.addon.wrapper, aError);
- },
+ }
/**
* Called to cancel an in-progress update check
*/
- cancel: function() {
+ cancel() {
let parser = this._parser;
if (parser) {
this._parser = null;
// This will call back to onUpdateCheckError with a CANCELLED error
parser.cancel();
}
}
-};
+}
/**
* The AddonInternal is an internal only representation of add-ons. It may
* have come from the database (see DBAddonInternal in XPIProviderUtils.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,
- sourceURI: null,
- releaseNotesURI: null,
- foreignInstall: false,
- seen: true,
- skinnable: false,
-
+class AddonInternal {
+ constructor() {
+ this._hasResourceCache = new Map();
+
+ XPCOMUtils.defineLazyGetter(this, "wrapper", () => {
+ return new AddonWrapper(this);
+ });
+
+ this._selectedLocale = null;
+ this.active = false;
+ this.visible = false;
+ this.userDisabled = false;
+ this.appDisabled = false;
+ this.softDisabled = false;
+ this.sourceURI = null;
+ this.releaseNotesURI = null;
+ this.foreignInstall = false;
+ this.seen = true;
+ this.skinnable = false;
+ }
/**
* @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,
+ static get dependencies() {
+ return Object.freeze([]);
+ }
+
+ static get hasEmbeddedWebExtension() {
+ return false;
+ }
get selectedLocale() {
if (this._selectedLocale)
return this._selectedLocale;
let locale = Locale.findClosestLocale(this.locales);
this._selectedLocale = locale ? locale : this.defaultLocale;
return this._selectedLocale;
- },
+ }
get providesUpdatesSecurely() {
return !!(this.updateKey || !this.updateURL ||
this.updateURL.substring(0, 6) == "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:
@@ -7145,25 +7143,25 @@ 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 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
@@ -7196,19 +7194,19 @@ 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: function(aAppVersion, aPlatformVersion) {
+ }
+
+ isCompatibleWith(aAppVersion, aPlatformVersion) {
let app = this.matchingTargetApplication;
if (!app)
return false;
// set reasonable defaults for minVersion and maxVersion
let minVersion = app.minVersion || "0";
let maxVersion = app.maxVersion || "*";
@@ -7251,92 +7249,92 @@ 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;
- },
+ }
get blocklistState() {
let staticItem = findMatchingStaticBlocklistItem(this);
if (staticItem)
return staticItem.level;
return Blocklist.getAddonBlocklistState(this.wrapper);
- },
+ }
get blocklistURL() {
let staticItem = findMatchingStaticBlocklistItem(this);
if (staticItem) {
let url = Services.urlFormatter.formatURLPref("extensions.blocklist.itemURL");
return url.replace(/%blockID%/g, staticItem.blockID);
}
return Blocklist.getAddonBlocklistURL(this.wrapper);
- },
-
- applyCompatibilityUpdate: function(aUpdate, aSyncCompatibility) {
+ }
+
+ 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;
}
}
}
if (aUpdate.multiprocessCompatible !== undefined)
this.multiprocessCompatible = aUpdate.multiprocessCompatible;
this.appDisabled = !isUsableAddon(this);
- },
+ }
/**
* getDataDirectory tries to execute the callback with two arguments:
* 1) the path of the data directory within the profile,
* 2) any exception generated from trying to build it.
*/
- getDataDirectory: function(callback) {
+ getDataDirectory(callback) {
let parentPath = OS.Path.join(OS.Constants.Path.profileDir, "extension-data");
let dirPath = OS.Path.join(parentPath, this.id);
Task.spawn(function*() {
yield OS.File.makeDir(parentPath, {ignoreExisting: true});
yield OS.File.makeDir(dirPath, {ignoreExisting: true});
}).then(() => callback(dirPath, null),
e => callback(dirPath, e));
- },
+ }
/**
* 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.
*
* @param aKey
* The key that this object is being serialized as in the JSON.
* Unused here since this is always the main object serialized
*
* @return an object containing copies of the properties of this object
* ignoring private properties, functions, getters and setters
*/
- toJSON: function(aKey) {
+ toJSON(aKey) {
let obj = {};
for (let prop in this) {
// Ignore the wrapper property
if (prop == "wrapper")
continue;
// Ignore private properties
if (prop.substring(0, 1) == "_")
@@ -7353,39 +7351,39 @@ 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 aObj
* A JS object containing the cached metadata
*/
- importMetadata: function(aObj) {
+ importMetadata(aObj) {
for (let prop of PENDING_INSTALL_METADATA) {
if (!(prop in aObj))
continue;
this[prop] = aObj[prop];
}
// Compatibility info may have changed so update appDisabled
this.appDisabled = !isUsableAddon(this);
- },
-
- permissions: function() {
+ }
+
+ permissions() {
let permissions = 0;
// Add-ons that aren't installed cannot be modified in any way
if (!(this.inDatabase))
return permissions;
if (!this.appDisabled) {
if (this.userDisabled || this.softDisabled) {
@@ -7408,60 +7406,60 @@ AddonInternal.prototype = {
!this._installLocation.isLinkedAddon(this.id) && !isSystem) {
permissions |= AddonManager.PERM_CAN_UPGRADE;
}
permissions |= AddonManager.PERM_CAN_UNINSTALL;
}
return permissions;
- },
-};
+ }
+}
/**
* The AddonWrapper wraps an Addon to provide the data visible to consumers of
* the public API.
*/
-function AddonWrapper(aAddon) {
- wrapperMap.set(this, aAddon);
-}
-
-AddonWrapper.prototype = {
+class AddonWrapper {
+ 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: function() {
+ }
+
+ markAsSeen() {
addonFor(this).seen = true;
XPIDatabase.saveChanges();
- },
+ }
get type() {
return getExternalType(addonFor(this).type);
- },
+ }
get isWebExtension() {
return addonFor(this).type == "webextension";
- },
+ }
get temporarilyInstalled() {
return addonFor(this)._installLocation == TemporaryInstallLocation;
- },
+ }
get aboutURL() {
return this.isActive ? addonFor(this)["aboutURL"] : null;
- },
+ }
get optionsURL() {
if (!this.isActive) {
return null;
}
let addon = addonFor(this);
if (addon.optionsURL) {
@@ -7478,17 +7476,17 @@ AddonWrapper.prototype = {
}
return addon.optionsURL;
}
if (this.hasResource("options.xul"))
return this.getResourceURI("options.xul").spec;
return null;
- },
+ }
get optionsType() {
if (!this.isActive)
return null;
let addon = addonFor(this);
let hasOptionsXUL = this.hasResource("options.xul");
let hasOptionsURL = !!this.optionsURL;
@@ -7508,25 +7506,25 @@ AddonWrapper.prototype = {
if (hasOptionsXUL)
return AddonManager.OPTIONS_TYPE_INLINE;
if (hasOptionsURL)
return AddonManager.OPTIONS_TYPE_DIALOG;
return null;
- },
+ }
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];
@@ -7553,38 +7551,39 @@ AddonWrapper.prototype = {
}
if (this.isActive && 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 (addon.type == "theme" && 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 (this.type == "experiment") {
logger.warn("Setting applyBackgroundUpdates on an experiment is not supported.");
return addon.applyBackgroundUpdates;
}
if (val != AddonManager.AUTOUPDATE_DEFAULT &&
@@ -7598,50 +7597,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
@@ -7660,54 +7659,55 @@ 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() {
let addon = addonFor(this);
let ops = 0;
if (XPIProvider.installRequiresRestart(addon))
ops |= AddonManager.OP_NEEDS_RESTART_INSTALL;
if (XPIProvider.uninstallRequiresRestart(addon))
ops |= AddonManager.OP_NEEDS_RESTART_UNINSTALL;
if (XPIProvider.enableRequiresRestart(addon))
ops |= AddonManager.OP_NEEDS_RESTART_ENABLE;
if (XPIProvider.disableRequiresRestart(addon))
ops |= AddonManager.OP_NEEDS_RESTART_DISABLE;
return ops;
- },
+ }
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 && canRunInSafeMode(addon);
- },
+ }
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) {
if (addon.type == "theme" && val) {
@@ -7727,17 +7727,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
@@ -7751,81 +7751,81 @@ AddonWrapper.prototype = {
}
}
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.name == KEY_APP_SYSTEM_DEFAULTS ||
addon._installLocation.name == KEY_APP_SYSTEM_ADDONS);
- },
+ }
get isSystem() {
let addon = addonFor(this);
return (addon._installLocation.name == KEY_APP_SYSTEM_DEFAULTS ||
addon._installLocation.name == KEY_APP_SYSTEM_ADDONS);
- },
+ }
// Returns true if Firefox Sync should sync this addon. Only non-hotfixes
// directly in the profile are considered syncable.
get isSyncable() {
let addon = addonFor(this);
let hotfixID = Preferences.get(PREF_EM_HOTFIX_ID, undefined);
if (hotfixID && hotfixID == addon.id) {
return false;
}
return (addon._installLocation.name == KEY_APP_PROFILE);
- },
-
- isCompatibleWith: function(aAppVersion, aPlatformVersion) {
+ }
+
+ isCompatibleWith(aAppVersion, aPlatformVersion) {
return addonFor(this).isCompatibleWith(aAppVersion, aPlatformVersion);
- },
-
- uninstall: function(alwaysAllowUndo) {
+ }
+
+ uninstall(alwaysAllowUndo) {
let addon = addonFor(this);
XPIProvider.uninstallAddon(addon, alwaysAllowUndo);
- },
-
- cancelUninstall: function() {
+ }
+
+ cancelUninstall() {
let addon = addonFor(this);
XPIProvider.cancelUninstallAddon(addon);
- },
-
- findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
+ }
+
+ findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
// Short-circuit updates for experiments because updates are handled
// through the Experiments Manager.
if (this.type == "experiment") {
AddonManagerPrivate.callNoUpdateListeners(this, aListener, aReason,
aAppVersion, aPlatformVersion);
return;
}
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: function() {
+ cancelUpdate() {
let addon = addonFor(this);
if (addon._updateCheck) {
addon._updateCheck.cancel();
return true;
}
return false;
- },
-
- hasResource: function(aPath) {
+ }
+
+ hasResource(aPath) {
let addon = addonFor(this);
if (addon._hasResourceCache.has(aPath))
return addon._hasResourceCache.get(aPath);
let bundle = addon._sourceBundle.clone();
// Bundle may not exist any more if the addon has just been uninstalled,
// but explicitly first checking .exists() results in unneeded file I/O.
@@ -7854,95 +7854,94 @@ AddonWrapper.prototype = {
}
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.
*
* @return Promise
*/
- reload: function() {
+ reload() {
return new Promise((resolve) => {
const addon = addonFor(this);
logger.debug(`reloading add-on ${addon.id}`);
if (!this.temporarilyInstalled) {
let addonFile = addon.getResourceURI;
XPIProvider.updateAddonDisabledState(addon, true);
Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
XPIProvider.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 aPath
* The path in the add-on to get the URI for or null to get a URI to
* the file or directory the add-on is installed as.
* @return an nsIURI
*/
- getResourceURI: function(aPath) {
+ getResourceURI(aPath) {
let addon = addonFor(this);
if (!aPath)
return NetUtil.newURI(addon._sourceBundle);
return getURIForResourceInFile(addon._sourceBundle, aPath);
}
-};
+}
/**
* The PrivateWrapper is used to expose certain functionality only when being
* called with the add-on instanceID, disallowing other add-ons to access it.
*/
-function PrivateWrapper(aAddon) {
- AddonWrapper.call(this, aAddon);
-}
-
-PrivateWrapper.prototype = Object.create(AddonWrapper.prototype);
-Object.assign(PrivateWrapper.prototype, {
+class PrivateWrapper {
+ constructor(aAddon) {
+ AddonWrapper.call(this, aAddon);
+ }
+
addonId() {
return this.id;
- },
+ }
/**
* Retrieves the preferred global context to be used from the
* add-on debugging window.
*
* @returns global
* The object set as global context. Must be a window object.
*/
getDebugGlobal(global) {
let activeAddon = XPIProvider.activeAddons.get(this.id);
if (activeAddon) {
return activeAddon.debugGlobal;
}
return null;
- },
+ }
/**
* Defines a global context to be used in the console
* of the add-on debugging window.
*
* @param global
* The object to set as global context. Must be a window object.
*/
@@ -7964,17 +7963,17 @@ Object.assign(PrivateWrapper.prototype,
if (globalChanged) {
AddonManagerPrivate.callAddonListeners("onPropertyChanged",
addonFor(this),
["debugGlobal"]);
}
}
}
}
-});
+}
function chooseValue(aAddon, aObj, aProp) {
let repositoryAddon = aAddon._repositoryAddon;
let objValue = aObj[aProp];
if (repositoryAddon && (aProp in repositoryAddon) &&
(objValue === undefined || objValue === null)) {
return [repositoryAddon[aProp], true];
@@ -8109,58 +8108,56 @@ PROP_LOCALE_MULTI.forEach(function(aProp
return results;
});
});
/**
* An object which identifies a directory install location for add-ons. The
* location consists of a directory which contains the add-ons installed in the
* location.
- *
- * Each add-on installed in the location is either a directory containing the
- * add-on's files or a text file containing an absolute path to the directory
- * containing the add-ons files. The directory or text file must have the same
- * name as the add-on's ID.
- *
- * @param aName
- * The string identifier for the install location
- * @param aDirectory
- * The nsIFile directory for the install location
- * @param aScope
- * The scope of add-ons installed in this location
*/
-function DirectoryInstallLocation(aName, aDirectory, aScope) {
- this._name = aName;
- this.locked = true;
- this._directory = aDirectory;
- this._scope = aScope
- this._IDToFileMap = {};
- this._linkedAddons = [];
-
- if (!aDirectory || !aDirectory.exists())
- return;
- if (!aDirectory.isDirectory())
- throw new Error("Location must be a directory.");
-
- this._readAddons();
-}
-
-DirectoryInstallLocation.prototype = {
- _name : "",
- _directory : null,
- _IDToFileMap : null, // mapping from add-on ID to nsIFile
+class DirectoryInstallLocation {
+
+ /**
+ * Each add-on installed in the location is either a directory containing the
+ * add-on's files or a text file containing an absolute path to the directory
+ * containing the add-ons files. The directory or text file must have the same
+ * name as the add-on's ID.
+ *
+ * @param aName
+ * The string identifier for the install location
+ * @param aDirectory
+ * The nsIFile directory for the install location
+ * @param aScope
+ * The scope of add-ons installed in this location
+ */
+ constructor(aName, aDirectory, aScope) {
+ this._name = aName;
+ this.locked = true;
+ this._directory = aDirectory;
+ this._scope = aScope
+ this._IDToFileMap = {}; // mapping from add-on ID to nsIFile
+ this._linkedAddons = [];
+
+ if (!aDirectory || !aDirectory.exists())
+ return;
+ if (!aDirectory.isDirectory())
+ throw new Error("Location must be a directory.");
+
+ this._readAddons();
+ }
/**
* Reads a directory linked to in a file.
*
* @param file
* The file containing the directory path
* @return An nsIFile object representing the linked directory.
*/
- _readDirectoryFromFile: function(aFile) {
+ _readDirectoryFromFile(aFile) {
let linkedDirectory;
if (aFile.isSymlink()) {
linkedDirectory = aFile.clone();
try {
linkedDirectory.normalize();
} catch (e) {
logger.warn("Symbolic link " + aFile.path + " points to a path" +
" which does not exist");
@@ -8201,22 +8198,22 @@ DirectoryInstallLocation.prototype = {
return null;
}
return linkedDirectory;
}
logger.warn("File pointer " + aFile.path + " does not contain a path");
return null;
- },
+ }
/**
* Finds all the add-ons installed in this location.
*/
- _readAddons: function() {
+ _readAddons() {
// Use a snapshot of the directory contents to avoid possible issues with
// iterating over a directory while removing files from it (the YAFFS2
// embedded filesystem has this issue, see bug 772238).
let entries = getDirectoryEntries(this._directory);
for (let entry of entries) {
let id = entry.leafName;
if (id == DIR_STAGE || id == DIR_TRASH)
@@ -8251,136 +8248,137 @@ DirectoryInstallLocation.prototype = {
entry = newEntry;
this._linkedAddons.push(id);
}
this._IDToFileMap[id] = entry;
XPIProvider._addURIMapping(id, entry);
}
- },
+ }
/**
* Gets the name of this install location.
*/
get name() {
return this._name;
- },
+ }
/**
* Gets the scope of this install location.
*/
get scope() {
return this._scope;
- },
+ }
/**
* Gets an array of nsIFiles for add-ons installed in this location.
*/
- getAddonLocations: function() {
+ getAddonLocations() {
let locations = new Map();
for (let id in this._IDToFileMap) {
locations.set(id, this._IDToFileMap[id].clone());
}
return locations;
- },
+ }
/**
* Gets the directory that the add-on with the given ID is installed in.
*
* @param aId
* The ID of the add-on
* @return The nsIFile
* @throws if the ID does not match any of the add-ons installed
*/
- getLocationForID: function(aId) {
+ getLocationForID(aId) {
if (aId in this._IDToFileMap)
return this._IDToFileMap[aId].clone();
throw new Error("Unknown add-on ID " + aId);
- },
+ }
/**
* Returns true if the given addon was installed in this location by a text
* file pointing to its real path.
*
* @param aId
* The ID of the addon
*/
- isLinkedAddon: function(aId) {
+ isLinkedAddon(aId) {
return this._linkedAddons.indexOf(aId) != -1;
}
-};
+}
/**
* An extension of DirectoryInstallLocation which adds methods to installing
* and removing add-ons from the directory at runtime.
- *
- * @param aName
- * The string identifier for the install location
- * @param aDirectory
- * The nsIFile directory for the install location
- * @param aScope
- * The scope of add-ons installed in this location
*/
-function MutableDirectoryInstallLocation(aName, aDirectory, aScope) {
- DirectoryInstallLocation.call(this, aName, aDirectory, aScope);
- this.locked = false;
- this._stagingDirLock = 0;
-}
-
-MutableDirectoryInstallLocation.prototype = Object.create(DirectoryInstallLocation.prototype);
-Object.assign(MutableDirectoryInstallLocation.prototype, {
+
+ class MutableDirectoryInstallLocation extends DirectoryInstallLocation {
+ /**
+ * @param aName
+ * The string identifier for the install location
+ * @param aDirectory
+ * The nsIFile directory for the install location
+ * @param aScope
+ * The scope of add-ons installed in this location
+ */
+ constructor(aName, aDirectory, aScope) {
+ super(aName, aDirectory, aScope);
+ this.locked = false;
+ this._stagingDirLock = 0;
+ }
+
/**
* Gets the staging directory to put add-ons that are pending install and
* uninstall into.
*
* @return an nsIFile
*/
- getStagingDir: function() {
+ getStagingDir() {
let dir = this._directory.clone();
dir.append(DIR_STAGE);
return dir;
- },
-
- requestStagingDir: function() {
+ }
+
+ requestStagingDir() {
this._stagingDirLock++;
if (this._stagingDirPromise)
return this._stagingDirPromise;
OS.File.makeDir(this._directory.path);
let stagepath = OS.Path.join(this._directory.path, DIR_STAGE);
return this._stagingDirPromise = OS.File.makeDir(stagepath).then(null, (e) => {
if (e instanceof OS.File.Error && e.becauseExists)
return;
logger.error("Failed to create staging directory", e);
throw e;
});
- },
-
- releaseStagingDir: function() {
+ }
+
+ releaseStagingDir() {
this._stagingDirLock--;
if (this._stagingDirLock == 0) {
this._stagingDirPromise = null;
this.cleanStagingDir();
}
return Promise.resolve();
- },
+ }
/**
* Removes the specified files or directories in the staging directory and
* then if the staging directory is empty attempts to remove it.
*
* @param aLeafNames
* An array of file or directory to remove from the directory, the
* array may be empty
*/
- cleanStagingDir: function(aLeafNames = []) {
+ cleanStagingDir(aLeafNames = []) {
let dir = this.getStagingDir();
for (let name of aLeafNames) {
let file = dir.clone();
file.append(name);
recursiveRemove(file);
}
@@ -8399,42 +8397,42 @@ Object.assign(MutableDirectoryInstallLoc
try {
setFilePermissions(dir, FileUtils.PERMS_DIRECTORY);
dir.remove(false);
}
catch (e) {
logger.warn("Failed to remove staging dir", e);
// Failing to remove the staging directory is ignorable
}
- },
+ }
/**
* Returns a directory that is normally on the same filesystem as the rest of
* the install location and can be used for temporarily storing files during
* safe move operations. Calling this method will delete the existing trash
* directory and its contents.
*
* @return an nsIFile
*/
- getTrashDir: function() {
+ getTrashDir() {
let trashDir = this._directory.clone();
trashDir.append(DIR_TRASH);
let trashDirExists = trashDir.exists();
try {
if (trashDirExists)
recursiveRemove(trashDir);
trashDirExists = false;
} catch (e) {
logger.warn("Failed to remove trash directory", e);
}
if (!trashDirExists)
trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
return trashDir;
- },
+ }
/**
* Installs an add-on into the install location.
*
* @param id
* The ID of the add-on to install
* @param source
* The source nsIFile to install from
@@ -8446,17 +8444,21 @@ Object.assign(MutableDirectoryInstallLoc
* Default action, the source files will be moved to the new
* location,
* "copy"
* The source files will be copied,
* "proxy"
* A "proxy file" is going to refer to the source file path
* @return an nsIFile indicating where the add-on was installed to
*/
- installAddon: function({ id, source, existingAddonID, action = "move" }) {
+ installAddon({id, source, existingAddonID, action}) {
+ if (!action) {
+ action = "move";
+ }
+
let trashDir = this.getTrashDir();
let transaction = new SafeInstallOperation();
let moveOldAddon = aId => {
let file = this._directory.clone();
file.append(aId);
@@ -8546,26 +8548,26 @@ Object.assign(MutableDirectoryInstallLoc
XPIProvider._addURIMapping(id, newFile);
if (existingAddonID && existingAddonID != id &&
existingAddonID in this._IDToFileMap) {
delete this._IDToFileMap[existingAddonID];
}
return newFile;
- },
+ }
/**
* Uninstalls an add-on from this location.
*
* @param aId
* The ID of the add-on to uninstall
* @throws if the ID does not match any of the add-ons installed
*/
- uninstallAddon: function(aId) {
+ uninstallAddon(aId) {
let file = this._IDToFileMap[aId];
if (!file) {
logger.warn("Attempted to remove " + aId + " from " +
this._name + " but it was already gone");
return;
}
file = this._directory.clone();
@@ -8600,70 +8602,71 @@ Object.assign(MutableDirectoryInstallLoc
recursiveRemove(trashDir);
}
catch (e) {
logger.warn("Failed to remove trash directory when uninstalling " + aId, e);
}
}
delete this._IDToFileMap[aId];
- },
-});
+ }
+}
/**
* An object which identifies a directory install location for system add-ons
* upgrades.
*
* The location consists of a directory which contains the add-ons installed.
- *
- * @param aName
- * The string identifier for the install location
- * @param aDirectory
- * The nsIFile directory for the install location
- * @param aScope
- * The scope of add-ons installed in this location
- * @param aResetSet
- * True to throw away the current add-on set
*/
-function SystemAddonInstallLocation(aName, aDirectory, aScope, aResetSet) {
- this._baseDir = aDirectory;
- this._nextDir = null;
-
- this._stagingDirLock = 0;
-
- if (aResetSet)
- this.resetAddonSet();
-
- this._addonSet = this._loadAddonSet();
-
- this._directory = null;
- if (this._addonSet.directory) {
- this._directory = aDirectory.clone();
- this._directory.append(this._addonSet.directory);
- logger.info("SystemAddonInstallLocation scanning directory " + this._directory.path);
- }
- else {
- logger.info("SystemAddonInstallLocation directory is missing");
- }
-
- DirectoryInstallLocation.call(this, aName, this._directory, aScope);
- this.locked = false;
-}
-
-SystemAddonInstallLocation.prototype = Object.create(DirectoryInstallLocation.prototype);
-Object.assign(SystemAddonInstallLocation.prototype, {
+ class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
+ /**
+ * @param aName
+ * The string identifier for the install location
+ * @param aDirectory
+ * The nsIFile directory for the install location
+ * @param aScope
+ * The scope of add-ons installed in this location
+ * @param aResetSet
+ * True to throw away the current add-on set
+ */
+ constructor(aName, aDirectory, aScope, aResetSet) {
+ super(aName, aDirectory, aScope);
+
+ this._baseDir = aDirectory;
+ this._nextDir = null;
+
+ this._stagingDirLock = 0;
+
+ if (aResetSet)
+ this.resetAddonSet();
+
+ this._addonSet = this._loadAddonSet();
+
+ this._directory = null;
+ if (this._addonSet.directory) {
+ this._directory = aDirectory.clone();
+ this._directory.append(this._addonSet.directory);
+ logger.info("SystemAddonInstallLocation scanning directory " + this._directory.path);
+ }
+ else {
+ logger.info("SystemAddonInstallLocation directory is missing");
+ }
+
+ this.locked = false;
+ }
+
/**
* Removes the specified files or directories in the staging directory and
* then if the staging directory is empty attempts to remove it.
*
* @param aLeafNames
* An array of file or directory to remove from the directory, the
* array may be empty
*/
- cleanStagingDir: function(aLeafNames = []) {
+ cleanStagingDir(aLeafNames = []) {
let dir = this.getStagingDir();
for (let name of aLeafNames) {
let file = dir.clone();
file.append(name);
recursiveRemove(file);
}
@@ -8682,41 +8685,41 @@ Object.assign(SystemAddonInstallLocation
try {
setFilePermissions(dir, FileUtils.PERMS_DIRECTORY);
dir.remove(false);
}
catch (e) {
logger.warn("Failed to remove staging dir", e);
// Failing to remove the staging directory is ignorable
}
- },
+ }
/**
* Gets the staging directory to put add-ons that are pending install and
* uninstall into.
*
* @return {nsIFile} - staging directory for system add-on upgrades.
*/
- getStagingDir: function() {
+ getStagingDir() {
this._addonSet = this._loadAddonSet();
let dir = null;
if (this._addonSet.directory) {
this._directory = this._baseDir.clone();
this._directory.append(this._addonSet.directory);
dir = this._directory.clone();
dir.append(DIR_STAGE);
}
else {
logger.info("SystemAddonInstallLocation directory is missing");
}
return dir;
- },
-
- requestStagingDir: function() {
+ }
+
+ requestStagingDir() {
this._stagingDirLock++;
if (this._stagingDirPromise)
return this._stagingDirPromise;
this._addonSet = this._loadAddonSet();
if (this._addonSet.directory) {
this._directory = this._baseDir.clone();
this._directory.append(this._addonSet.directory);
@@ -8725,82 +8728,82 @@ Object.assign(SystemAddonInstallLocation
OS.File.makeDir(this._directory.path);
let stagepath = OS.Path.join(this._directory.path, DIR_STAGE);
return this._stagingDirPromise = OS.File.makeDir(stagepath).then(null, (e) => {
if (e instanceof OS.File.Error && e.becauseExists)
return;
logger.error("Failed to create staging directory", e);
throw e;
});
- },
-
- releaseStagingDir: function() {
+ }
+
+ releaseStagingDir() {
this._stagingDirLock--;
if (this._stagingDirLock == 0) {
this._stagingDirPromise = null;
this.cleanStagingDir();
}
return Promise.resolve();
- },
+ }
/**
* Reads the current set of system add-ons
*/
- _loadAddonSet: function() {
+ _loadAddonSet() {
try {
let setStr = Preferences.get(PREF_SYSTEM_ADDON_SET, null);
if (setStr) {
let addonSet = JSON.parse(setStr);
if ((typeof addonSet == "object") && addonSet.schema == 1)
return addonSet;
}
}
catch (e) {
logger.error("Malformed system add-on set, resetting.");
}
return { schema: 1, addons: {} };
- },
+ }
/**
* Saves the current set of system add-ons
*
* @param {Object} aAddonSet - object containing schema, directory and set
* of system add-on IDs and versions.
*/
- _saveAddonSet: function(aAddonSet) {
+ _saveAddonSet(aAddonSet) {
Preferences.set(PREF_SYSTEM_ADDON_SET, JSON.stringify(aAddonSet));
- },
-
- getAddonLocations: function() {
+ }
+
+ getAddonLocations() {
// Updated system add-ons are ignored in safe mode
if (Services.appinfo.inSafeMode)
return new Map();
let addons = DirectoryInstallLocation.prototype.getAddonLocations.call(this);
// Strip out any unexpected add-ons from the list
for (let id of addons.keys()) {
if (!(id in this._addonSet.addons))
addons.delete(id);
}
return addons;
- },
+ }
/**
* Tests whether updated system add-ons are expected.
*/
- isActive: function() {
+ isActive() {
return this._directory != null;
- },
-
- isValidAddon: function(aAddon) {
+ }
+
+ isValidAddon(aAddon) {
if (aAddon.appDisabled) {
logger.warn(`System add-on ${aAddon.id} isn't compatible with the application.`);
return false;
}
if (aAddon.unpack) {
logger.warn(`System add-on ${aAddon.id} isn't a packed add-on.`);
return false;
@@ -8812,22 +8815,22 @@ Object.assign(SystemAddonInstallLocation
}
if (!aAddon.multiprocessCompatible) {
logger.warn(`System add-on ${aAddon.id} isn't multiprocess compatible.`);
return false;
}
return true;
- },
+ }
/**
* Tests whether the loaded add-on information matches what is expected.
*/
- isValid: function(aAddons) {
+ isValid(aAddons) {
for (let id of Object.keys(this._addonSet.addons)) {
if (!aAddons.has(id)) {
logger.warn(`Expected add-on ${id} is missing from the system add-on location.`);
return false;
}
let addon = aAddons.get(id);
if (addon.version != this._addonSet.addons[id].version) {
@@ -8835,22 +8838,22 @@ Object.assign(SystemAddonInstallLocation
return false;
}
if (!this.isValidAddon(addon))
return false;
}
return true;
- },
+ }
/**
* Resets the add-on set so on the next startup the default set will be used.
*/
- resetAddonSet: function() {
+ resetAddonSet() {
if (this._addonSet) {
logger.info("Removing all system add-on upgrades.");
// remove everything from the pref first, if uninstall
// fails then at least they will not be re-activated on
// next restart.
this._saveAddonSet({ schema: 1, addons: {} });
@@ -8858,246 +8861,252 @@ Object.assign(SystemAddonInstallLocation
for (let id of Object.keys(this._addonSet.addons)) {
AddonManager.getAddonByID(id, addon => {
if (addon) {
addon.uninstall();
}
});
}
}
- },
+ }
/**
* Removes any directories not currently in use or pending use after a
* restart. Any errors that happen here don't really matter as we'll attempt
* to cleanup again next time.
*/
- cleanDirectories: Task.async(function*() {
-
- // System add-ons directory does not exist
- if (!(yield OS.File.exists(this._baseDir.path))) {
- return;
- }
-
- let iterator;
- try {
- iterator = new OS.File.DirectoryIterator(this._baseDir.path);
- }
- catch (e) {
- logger.error("Failed to clean updated system add-ons directories.", e);
- return;
- }
-
- try {
- let entries = [];
-
- yield iterator.forEach(entry => {
- // Skip the directory currently in use
- if (this._directory && this._directory.path == entry.path)
- return;
-
- // Skip the next directory
- if (this._nextDir && this._nextDir.path == entry.path)
- return;
-
- entries.push(entry);
- });
-
- for (let entry of entries) {
- if (entry.isDir) {
- yield OS.File.removeDir(entry.path, {
- ignoreAbsent: true,
- ignorePermissions: true,
- });
- }
- else {
- yield OS.File.remove(entry.path, {
- ignoreAbsent: true,
- });
- }
- }
- }
- catch (e) {
- logger.error("Failed to clean updated system add-ons directories.", e);
- }
- finally {
- iterator.close();
- }
- }),
+ cleanDirectories() {
+ return Task.spawn(function*() {
+
+ // System add-ons directory does not exist
+ if (!(yield OS.File.exists(this._baseDir.path))) {
+ return;
+ }
+
+ let iterator;
+ try {
+ iterator = new OS.File.DirectoryIterator(this._baseDir.path);
+ }
+ catch (e) {
+ logger.error("Failed to clean updated system add-ons directories.", e);
+ return;
+ }
+
+ try {
+ let entries = [];
+
+ yield iterator.forEach(entry => {
+ // Skip the directory currently in use
+ if (this._directory && this._directory.path == entry.path)
+ return;
+
+ // Skip the next directory
+ if (this._nextDir && this._nextDir.path == entry.path)
+ return;
+
+ entries.push(entry);
+ });
+
+ for (let entry of entries) {
+ if (entry.isDir) {
+ yield OS.File.removeDir(entry.path, {
+ ignoreAbsent: true,
+ ignorePermissions: true,
+ });
+ }
+ else {
+ yield OS.File.remove(entry.path, {
+ ignoreAbsent: true,
+ });
+ }
+ }
+ }
+ catch (e) {
+ logger.error("Failed to clean updated system add-ons directories.", e);
+ }
+ finally {
+ iterator.close();
+ }
+ }).bind(this);
+ }
/**
* Installs a new set of system add-ons into the location and updates the
* add-on set in prefs.
*
* @param {Array} aAddons - An array of addons to install.
*/
- installAddonSet: Task.async(function*(aAddons) {
- // Make sure the base dir exists
- yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
-
- let addonSet = this._loadAddonSet();
-
- // Remove any add-ons that are no longer part of the set.
- for (let addonID of Object.keys(addonSet.addons)) {
- if (!aAddons.includes(addonID)) {
- AddonManager.getAddonByID(addonID, a => a.uninstall());
- }
- }
-
- let newDir = this._baseDir.clone();
-
- let uuidGen = Cc["@mozilla.org/uuid-generator;1"].
- getService(Ci.nsIUUIDGenerator);
- newDir.append("blank");
-
- while (true) {
- newDir.leafName = uuidGen.generateUUID().toString();
+ installAddonSet() {
+ return Task.spawn(function*(aAddons) {
+ // Make sure the base dir exists
+ yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
+
+ let addonSet = this._loadAddonSet();
+
+ // Remove any add-ons that are no longer part of the set.
+ for (let addonID of Object.keys(addonSet.addons)) {
+ if (!aAddons.includes(addonID)) {
+ AddonManager.getAddonByID(addonID, a => a.uninstall());
+ }
+ }
+
+ let newDir = this._baseDir.clone();
+
+ let uuidGen = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ newDir.append("blank");
+
+ while (true) {
+ newDir.leafName = uuidGen.generateUUID().toString();
+
+ try {
+ yield OS.File.makeDir(newDir.path, { ignoreExisting: false });
+ break;
+ }
+ catch (e) {
+ logger.debug("Could not create new system add-on updates dir, retrying", e);
+ }
+ }
+
+ // Record the new upgrade directory.
+ let state = { schema: 1, directory: newDir.leafName, addons: {} };
+ this._saveAddonSet(state);
+
+ this._nextDir = newDir;
+ let location = this;
+
+ let installs = [];
+ for (let addon of aAddons) {
+ let install = yield createLocalInstall(addon._sourceBundle, location);
+ installs.push(install);
+ }
+
+ let installAddon = Task.async(function*(install) {
+ // Make the new install own its temporary file.
+ install.ownsTempFile = true;
+ install.install();
+ });
+
+ let postponeAddon = Task.async(function*(install) {
+ let resumeFn;
+ if (AddonManagerPrivate.hasUpgradeListener(install.addon.id)) {
+ logger.info(`system add-on ${install.addon.id} has an upgrade listener, postponing upgrade set until restart`);
+ resumeFn = () => {
+ logger.info(`${install.addon.id} has resumed a previously postponed addon set`);
+ install.installLocation.resumeAddonSet(installs);
+ }
+ }
+ yield install.postpone(resumeFn);
+ });
+
+ let previousState;
try {
- yield OS.File.makeDir(newDir.path, { ignoreExisting: false });
- break;
+ // All add-ons in position, create the new state and store it in prefs
+ state = { schema: 1, directory: newDir.leafName, addons: {} };
+ for (let addon of aAddons) {
+ state.addons[addon.id] = {
+ version: addon.version
+ }
+ }
+
+ previousState = this._loadAddonSet();
+ this._saveAddonSet(state);
+
+ let blockers = aAddons.filter(
+ addon => AddonManagerPrivate.hasUpgradeListener(addon.id)
+ );
+
+ if (blockers.length > 0) {
+ yield waitForAllPromises(installs.map(postponeAddon));
+ } else {
+ yield waitForAllPromises(installs.map(installAddon));
+ }
}
catch (e) {
- logger.debug("Could not create new system add-on updates dir, retrying", e);
- }
- }
-
- // Record the new upgrade directory.
- let state = { schema: 1, directory: newDir.leafName, addons: {} };
- this._saveAddonSet(state);
-
- this._nextDir = newDir;
- let location = this;
-
- let installs = [];
- for (let addon of aAddons) {
- let install = yield createLocalInstall(addon._sourceBundle, location);
- installs.push(install);
- }
-
- let installAddon = Task.async(function*(install) {
- // Make the new install own its temporary file.
- install.ownsTempFile = true;
- install.install();
- });
-
- let postponeAddon = Task.async(function*(install) {
- let resumeFn;
- if (AddonManagerPrivate.hasUpgradeListener(install.addon.id)) {
- logger.info(`system add-on ${install.addon.id} has an upgrade listener, postponing upgrade set until restart`);
- resumeFn = () => {
- logger.info(`${install.addon.id} has resumed a previously postponed addon set`);
- install.installLocation.resumeAddonSet(installs);
- }
- }
- yield install.postpone(resumeFn);
- });
-
- let previousState;
-
- try {
- // All add-ons in position, create the new state and store it in prefs
- state = { schema: 1, directory: newDir.leafName, addons: {} };
- for (let addon of aAddons) {
- state.addons[addon.id] = {
- version: addon.version
- }
- }
-
- previousState = this._loadAddonSet();
- this._saveAddonSet(state);
-
- let blockers = aAddons.filter(
- addon => AddonManagerPrivate.hasUpgradeListener(addon.id)
- );
-
- if (blockers.length > 0) {
- yield waitForAllPromises(installs.map(postponeAddon));
- } else {
- yield waitForAllPromises(installs.map(installAddon));
- }
- }
- catch (e) {
- // Roll back to previous upgrade set (if present) on restart.
- if (previousState) {
- this._saveAddonSet(previousState);
- }
- // Otherwise, roll back to built-in set on restart.
- // TODO try to do these restartlessly
- this.resetAddonSet();
-
- try {
- yield OS.File.removeDir(newDir.path, { ignorePermissions: true });
- }
- catch (e) {
- logger.warn(`Failed to remove failed system add-on directory ${newDir.path}.`, e);
- }
- throw e;
- }
- }),
+ // Roll back to previous upgrade set (if present) on restart.
+ if (previousState) {
+ this._saveAddonSet(previousState);
+ }
+ // Otherwise, roll back to built-in set on restart.
+ // TODO try to do these restartlessly
+ this.resetAddonSet();
+
+ try {
+ yield OS.File.removeDir(newDir.path, { ignorePermissions: true });
+ }
+ catch (e) {
+ logger.warn(`Failed to remove failed system add-on directory ${newDir.path}.`, e);
+ }
+ throw e;
+ }
+ }).bind(this);
+ }
/**
* Resumes upgrade of a previously-delayed add-on set.
*/
- resumeAddonSet: Task.async(function*(installs) {
- let resumeAddon = Task.async(function*(install) {
- install.state = AddonManager.STATE_DOWNLOADED;
- install.installLocation.releaseStagingDir();
- install.install();
- });
-
- let addonSet = this._loadAddonSet();
- let addonIDs = Object.keys(addonSet.addons);
-
- let blockers = installs.filter(
- install => AddonManagerPrivate.hasUpgradeListener(install.addon.id)
- );
-
- if (blockers.length > 1) {
- logger.warn("Attempted to resume system add-on install but upgrade blockers are still present");
- } else {
- yield waitForAllPromises(installs.map(resumeAddon));
- }
- }),
+ resumeAddonSet() {
+ return Task.spawn(function*(installs) {
+ let resumeAddon = Task.async(function*(install) {
+ install.state = AddonManager.STATE_DOWNLOADED;
+ install.installLocation.releaseStagingDir();
+ install.install();
+ });
+
+ let addonSet = this._loadAddonSet();
+ let addonIDs = Object.keys(addonSet.addons);
+
+ let blockers = installs.filter(
+ install => AddonManagerPrivate.hasUpgradeListener(install.addon.id)
+ );
+
+ if (blockers.length > 1) {
+ logger.warn("Attempted to resume system add-on install but upgrade blockers are still present");
+ } else {
+ yield waitForAllPromises(installs.map(resumeAddon));
+ }
+ }).bind(this);
+ }
/**
* Returns a directory that is normally on the same filesystem as the rest of
* the install location and can be used for temporarily storing files during
* safe move operations. Calling this method will delete the existing trash
* directory and its contents.
*
* @return an nsIFile
*/
- getTrashDir: function() {
+ getTrashDir() {
let trashDir = this._directory.clone();
trashDir.append(DIR_TRASH);
let trashDirExists = trashDir.exists();
try {
if (trashDirExists)
recursiveRemove(trashDir);
trashDirExists = false;
} catch (e) {
logger.warn("Failed to remove trash directory", e);
}
if (!trashDirExists)
trashDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
return trashDir;
- },
+ }
/**
* Installs an add-on into the install location.
*
* @param id
* The ID of the add-on to install
* @param source
* The source nsIFile to install from
* @return an nsIFile indicating where the add-on was installed to
*/
- installAddon: function({id, source}) {
+ installAddon({id, source}) {
let trashDir = this.getTrashDir();
let transaction = new SafeInstallOperation();
// If any of these operations fails the finally block will clean up the
// temporary directory
try {
if (source.isFile()) {
flushJarCache(source);
@@ -9123,21 +9132,21 @@ Object.assign(SystemAddonInstallLocation
newFile.lastModifiedTime = Date.now();
} catch (e) {
logger.warn("failed to set lastModifiedTime on " + newFile.path, e);
}
this._IDToFileMap[id] = newFile;
XPIProvider._addURIMapping(id, newFile);
return newFile;
- },
+ }
// old system add-on upgrade dirs get automatically removed
- uninstallAddon: (aAddon) => {},
-});
+ uninstallAddon(aAddon) {}
+}
/**
* An object which identifies an install location for temporary add-ons.
*/
const TemporaryInstallLocation = {
locked: false,
name: KEY_APP_TEMPORARY,
scope: AddonManager.SCOPE_TEMPORARY,
@@ -9147,128 +9156,123 @@ const TemporaryInstallLocation = {
uninstallAddon: (aAddon) => {},
getStagingDir: () => {},
}
/**
* An object that identifies a registry install location for add-ons. The location
* consists of a registry key which contains string values mapping ID to the
* path where an add-on is installed
- *
- * @param aName
- * The string identifier of this Install Location.
- * @param aRootKey
- * The root key (one of the ROOT_KEY_ values from nsIWindowsRegKey).
- * @param scope
- * The scope of add-ons installed in this location
*/
-function WinRegInstallLocation(aName, aRootKey, aScope) {
- this.locked = true;
- this._name = aName;
- this._rootKey = aRootKey;
- this._scope = aScope;
- this._IDToFileMap = {};
-
- let path = this._appKeyPath + "\\Extensions";
- let key = Cc["@mozilla.org/windows-registry-key;1"].
- createInstance(Ci.nsIWindowsRegKey);
-
- // Reading the registry may throw an exception, and that's ok. In error
- // cases, we just leave ourselves in the empty state.
- try {
- key.open(this._rootKey, path, Ci.nsIWindowsRegKey.ACCESS_READ);
- }
- catch (e) {
- return;
- }
-
- this._readAddons(key);
- key.close();
-}
-
-WinRegInstallLocation.prototype = {
- _name : "",
- _rootKey : null,
- _scope : null,
- _IDToFileMap : null, // mapping from ID to nsIFile
-
+class WinRegInstallLocation {
+ /**
+ * @param aName
+ * The string identifier of this Install Location.
+ * @param aRootKey
+ * The root key (one of the ROOT_KEY_ values from nsIWindowsRegKey).
+ * @param scope
+ * The scope of add-ons installed in this location
+ */
+ constructor(aName, aRootKey, aScope) {
+ this.locked = true;
+ this._name = aName;
+ this._rootKey = aRootKey;
+ this._scope = aScope;
+ this._IDToFileMap = {}; // mapping from ID to nsIFile
+
+ let path = this._appKeyPath + "\\Extensions";
+ let key = Cc["@mozilla.org/windows-registry-key;1"].
+ createInstance(Ci.nsIWindowsRegKey);
+
+ // Reading the registry may throw an exception, and that's ok. In error
+ // cases, we just leave ourselves in the empty state.
+ try {
+ key.open(this._rootKey, path, Ci.nsIWindowsRegKey.ACCESS_READ);
+ }
+ catch (e) {
+ return;
+ }
+
+ this._readAddons(key);
+ key.close();
+ }
/**
* Retrieves the path of this Application's data key in the registry.
*/
get _appKeyPath() {
let appVendor = Services.appinfo.vendor;
let appName = Services.appinfo.name;
// XXX Thunderbird doesn't specify a vendor string
if (AppConstants.MOZ_APP_NAME == "thunderbird" && appVendor == "")
appVendor = "Mozilla";
// XULRunner-based apps may intentionally not specify a vendor
if (appVendor != "")
appVendor += "\\";
return "SOFTWARE\\" + appVendor + appName;
- },
+ }
/**
* Read the registry and build a mapping between ID and path for each
* installed add-on.
*
* @param key
* The key that contains the ID to path mapping
*/
- _readAddons: function(aKey) {
+ _readAddons(aKey) {
let count = aKey.valueCount;
for (let i = 0; i < count; ++i) {
let id = aKey.getValueName(i);
let file = new nsIFile(aKey.readStringValue(id));
if (!file.exists()) {
logger.warn("Ignoring missing add-on in " + file.path);
continue;
}
this._IDToFileMap[id] = file;
XPIProvider._addURIMapping(id, file);
}
- },
+ }
/**
* Gets the name of this install location.
*/
get name() {
return this._name;
- },
+ }
/**
* Gets the scope of this install location.
*/
get scope() {
return this._scope;
- },
+ }
/**
* Gets an array of nsIFiles for add-ons installed in this location.
*/
- getAddonLocations: function() {
+ getAddonLocations() {
let locations = new Map();
for (let id in this._IDToFileMap) {
locations.set(id, this._IDToFileMap[id].clone());
}
return locations;
- },
+ }
/**
* @see DirectoryInstallLocation
*/
- isLinkedAddon: function(aId) {
+ isLinkedAddon(aId) {
return true;
}
-};
+}
var addonTypes = [
new AddonManagerPrivate.AddonType("extension", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
AddonManager.VIEW_TYPE_LIST, 4000,
AddonManager.TYPE_SUPPORTS_UNDO_RESTARTLESS_UNINSTALL),
new AddonManagerPrivate.AddonType("theme", URI_EXTENSION_STRINGS,
STRING_TYPE_NAME,
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -317,62 +317,62 @@ function copyRowProperties(aRow, aProper
return aTarget;
}
/**
* 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 aLoaded
- * Addon data fields loaded from JSON or the addon manifest.
*/
-function DBAddonInternal(aLoaded) {
- AddonInternal.call(this);
+ class DBAddonInternal extends AddonInternal {
+ /**
+ * @param aLoaded
+ * Addon data fields loaded from JSON or the addon manifest.
+ */
+ constructor(aLoaded) {
+ super();
- copyProperties(aLoaded, PROP_JSON_FIELDS, this);
+ copyProperties(aLoaded, PROP_JSON_FIELDS, this);
+
+ if (!this.dependencies)
+ this.dependencies = [];
+ Object.freeze(this.dependencies);
- 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 descriptor");
+ }
- if (aLoaded._installLocation) {
- this._installLocation = aLoaded._installLocation;
- this.location = aLoaded._installLocation.name;
- }
- else if (aLoaded.location) {
- this._installLocation = XPIProvider.installLocationsByName[this.location];
+ this._sourceBundle = aLoaded._sourceBundle;
+
+ XPCOMUtils.defineLazyGetter(this, "pendingUpgrade", function() {
+ for (let install of XPIProvider.installs) {
+ if (install.state == AddonManager.STATE_INSTALLED &&
+ !(install.addon.inDatabase) &&
+ install.addon.id == this.id &&
+ install.installLocation == this._installLocation) {
+ delete this.pendingUpgrade;
+ return this.pendingUpgrade = install.addon;
+ }
+ }
+ return null;
+ });
}
- this._key = this.location + ":" + this.id;
-
- if (!aLoaded._sourceBundle) {
- throw new Error("Expected passed argument to contain a descriptor");
- }
-
- this._sourceBundle = aLoaded._sourceBundle;
-
- XPCOMUtils.defineLazyGetter(this, "pendingUpgrade", function() {
- for (let install of XPIProvider.installs) {
- if (install.state == AddonManager.STATE_INSTALLED &&
- !(install.addon.inDatabase) &&
- install.addon.id == this.id &&
- install.installLocation == this._installLocation) {
- delete this.pendingUpgrade;
- return this.pendingUpgrade = install.addon;
- }
- }
- return null;
- });
-}
-
-DBAddonInternal.prototype = Object.create(AddonInternal.prototype);
-Object.assign(DBAddonInternal.prototype, {
- applyCompatibilityUpdate: function(aUpdate, aSyncCompatibility) {
+ 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;
@@ -383,35 +383,35 @@ Object.assign(DBAddonInternal.prototype,
if (aUpdate.multiprocessCompatible !== undefined &&
aUpdate.multiprocessCompatible != this.multiprocessCompatible) {
this.multiprocessCompatible = aUpdate.multiprocessCompatible;
XPIDatabase.saveChanges();
}
if (wasCompatible != this.isCompatible)
XPIProvider.updateAddonDisabledState(this);
- },
+ }
- toJSON: function() {
+ toJSON() {
let jsonData = copyProperties(this, PROP_JSON_FIELDS);
// Experiments are serialized as disabled so they aren't run on the next
// startup.
if (this.type == "experiment") {
jsonData.userDisabled = true;
jsonData.active = false;
}
return jsonData;
- },
+ }
get inDatabase() {
return true;
}
-});
+}
/**
* Internal interface: find an addon from an already loaded addonDB
*/
function _findAddon(addonDB, aFilter) {
for (let addon of addonDB.values()) {
if (aFilter(addon)) {
return addon;