--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1651,25 +1651,32 @@ this.XPIStates = {
*/
getAddon(aLocation, aId) {
let location = this.db.get(aLocation);
return location && location.get(aId);
},
/**
* Find the highest priority location of an add-on by ID and return the
- * location and the XPIState.
- * @param aId The add-on ID
- * @param aLocations If specified, the locations to search
+ * XPIState.
+ * @param {string} aId
+ * The add-on IDa
+ * @param {function} aFilter
+ * An optional filter to apply to install locations. If provided,
+ * addons in locations that do not match the filter are not considered.
+ *
* @return {XPIState?}
*/
- findAddon(aId, aLocations = this.db.values()) {
+ findAddon(aId, aFilter = location => true) {
// Fortunately the Map iterator returns in order of insertion, which is
// also our highest -> lowest priority order.
- for (let location of aLocations) {
+ for (let location of this.db.values()) {
+ if (!aFilter(location)) {
+ continue;
+ }
if (location.has(aId)) {
return location.get(aId);
}
}
return undefined;
},
/**
@@ -2248,19 +2255,18 @@ this.XPIProvider = {
// If the add-on was pending disable then shut it down and remove it
// from the persisted data.
let reason = BOOTSTRAP_REASONS.APP_SHUTDOWN;
if (addon.disable) {
reason = BOOTSTRAP_REASONS.ADDON_DISABLE;
} else if (addon.location.name == KEY_APP_TEMPORARY) {
reason = BOOTSTRAP_REASONS.ADDON_UNINSTALL;
- let locations = Array.from(XPIStates.db.values())
- .filter(loc => loc.name != TemporaryInstallLocation.name);
- let existing = XPIStates.findAddon(addon.id, locations);
+ let existing = XPIStates.findAddon(addon.id, loc =>
+ loc.name != TemporaryInstallLocation.name);
if (existing) {
reason = newVersionReason(addon.version, existing.version);
}
}
XPIProvider.callBootstrapMethod(addon, addon.file,
"shutdown", reason);
}
Services.obs.removeObserver(this, "quit-application-granted");
@@ -2344,19 +2350,17 @@ this.XPIProvider = {
// Uninstall any temporary add-ons.
let tempLocation = XPIStates.getLocation(TemporaryInstallLocation.name);
if (tempLocation) {
for (let [id, addon] of tempLocation.entries()) {
tempLocation.delete(id);
let reason = BOOTSTRAP_REASONS.ADDON_UNINSTALL;
- let locations = Array.from(XPIStates.db.values())
- .filter(loc => loc != tempLocation);
- let existing = XPIStates.findAddon(id, locations);
+ let existing = XPIStates.findAddon(id, loc => loc != tempLocation);
if (existing) {
reason = newVersionReason(addon.version, existing.version);
}
this.callBootstrapMethod(addon, addon.file, "uninstall", reason);
this.unloadBootstrapScope(id);
TemporaryInstallLocation.uninstallAddon(id);
@@ -4637,73 +4641,71 @@ this.XPIProvider = {
// Passing makePending as the requiresRestart parameter is a little
// strange as in some cases this operation can complete without a restart
// so really this is now saying that the uninstall isn't going to happen
// immediately but will happen later.
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper,
makePending);
}
- // Reveal the highest priority add-on with the same ID
- function revealAddon(aAddon) {
- XPIDatabase.makeAddonVisible(aAddon);
-
- let wrappedAddon = aAddon.wrapper;
- AddonManagerPrivate.callAddonListeners("onInstalling", wrappedAddon, false);
-
- if (!aAddon.disabled && !XPIProvider.enableRequiresRestart(aAddon)) {
- XPIDatabase.updateAddonActive(aAddon, true);
- }
-
- if (aAddon.bootstrap) {
- XPIProvider.callBootstrapMethod(aAddon, aAddon._sourceBundle,
- "install", BOOTSTRAP_REASONS.ADDON_INSTALL);
-
- if (aAddon.active) {
- XPIProvider.callBootstrapMethod(aAddon, aAddon._sourceBundle,
- "startup", BOOTSTRAP_REASONS.ADDON_INSTALL);
- } else {
- XPIProvider.unloadBootstrapScope(aAddon.id);
- }
- }
-
- // We always send onInstalled even if a restart is required to enable
- // the revealed add-on
- AddonManagerPrivate.callAddonListeners("onInstalled", wrappedAddon);
- }
-
- function findAddonAndReveal(aId) {
- let state = XPIStates.findAddon(aId);
- if (state) {
- XPIDatabase.getAddonInLocation(aId, state.location.name, revealAddon);
- }
+ let reason = BOOTSTRAP_REASONS.ADDON_UNINSTALL;
+ let existingAddon = XPIStates.findAddon(aAddon.id, loc =>
+ loc.name != aAddon._installLocation.name);
+ if (existingAddon) {
+ reason = newVersionReason(aAddon.version, existingAddon.version);
}
if (!makePending) {
if (aAddon.bootstrap) {
if (aAddon.active) {
this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown",
- BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ reason);
}
this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "uninstall",
- BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ reason);
XPIStates.disableAddon(aAddon.id);
this.unloadBootstrapScope(aAddon.id);
flushChromeCaches();
}
aAddon._installLocation.uninstallAddon(aAddon.id);
XPIDatabase.removeAddonMetadata(aAddon);
XPIStates.removeAddon(aAddon.location, aAddon.id);
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
- findAddonAndReveal(aAddon.id);
+ if (existingAddon) {
+ XPIDatabase.getAddonInLocation(aAddon.id, existingAddon.location.name, existing => {
+ XPIDatabase.makeAddonVisible(existing);
+
+ let wrappedAddon = existing.wrapper;
+ AddonManagerPrivate.callAddonListeners("onInstalling", wrappedAddon, false);
+
+ if (!existing.disabled && !XPIProvider.enableRequiresRestart(existing)) {
+ XPIDatabase.updateAddonActive(existing, true);
+ }
+
+ if (aAddon.bootstrap) {
+ XPIProvider.callBootstrapMethod(existing, existing._sourceBundle,
+ "install", reason);
+
+ if (existing.active) {
+ XPIProvider.callBootstrapMethod(existing, existing._sourceBundle,
+ "startup", reason);
+ } else {
+ XPIProvider.unloadBootstrapScope(existing.id);
+ }
+ }
+
+ // We always send onInstalled even if a restart is required to enable
+ // the revealed add-on
+ AddonManagerPrivate.callAddonListeners("onInstalled", wrappedAddon);
+ });
+ }
} else if (aAddon.bootstrap && aAddon.active && !this.disableRequiresRestart(aAddon)) {
- this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown",
- BOOTSTRAP_REASONS.ADDON_UNINSTALL);
+ this.callBootstrapMethod(aAddon, aAddon._sourceBundle, "shutdown", reason);
XPIStates.disableAddon(aAddon.id);
this.unloadBootstrapScope(aAddon.id);
XPIDatabase.updateAddonActive(aAddon, false);
}
// Notify any other providers that a new theme has been enabled
if (isTheme(aAddon.type) && aAddon.active)
AddonManagerPrivate.notifyAddonChanged(null, aAddon.type, requiresRestart);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -951,21 +951,20 @@ function check_test_19() {
// Should have reverted to the older version
BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
BootstrapMonitor.checkAddonStarted(ID1, "1.0");
do_check_neq(b1, null);
do_check_eq(b1.version, "1.0");
do_check_true(b1.isActive);
do_check_false(b1.isSystem);
- // TODO these reasons really should be ADDON_DOWNGRADE (bug 607818)
- do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
- do_check_eq(getUninstallReason(), ADDON_UNINSTALL);
- do_check_eq(getInstallReason(), ADDON_INSTALL);
- do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getShutdownReason(), ADDON_DOWNGRADE);
+ do_check_eq(getUninstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getStartupReason(), ADDON_DOWNGRADE);
do_check_eq(getShutdownNewVersion(), undefined);
do_check_eq(getUninstallNewVersion(), undefined);
do_check_eq(getInstallOldVersion(), undefined);
do_check_eq(getStartupOldVersion(), undefined);
do_check_bootstrappedPref(run_test_20);
});