Last Comment Bug 666431 - Add an API to get the list of add-on changes that happened during startup
: Add an API to get the list of add-on changes that happened during startup
Status: VERIFIED FIXED
: dev-doc-complete
Product: Toolkit
Classification: Components
Component: Add-ons Manager (show other bugs)
: Trunk
: All All
: -- enhancement (vote)
: mozilla7
Assigned To: Dave Townsend [:mossop]
:
Mentors:
Depends on:
Blocks: 476430
  Show dependency treegraph
 
Reported: 2011-06-22 16:23 PDT by Dave Townsend [:mossop]
Modified: 2011-08-11 08:36 PDT (History)
8 users (show)
dtownsend: in‑testsuite+
dtownsend: in‑litmus-
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---


Attachments
patch rev 1 (51.06 KB, patch)
2011-06-22 16:33 PDT, Dave Townsend [:mossop]
robert.strong.bugs: review+
Details | Diff | Review
patch rev 2 (55.37 KB, patch)
2011-06-28 14:38 PDT, Dave Townsend [:mossop]
robert.strong.bugs: review+
Details | Diff | Review

Description Dave Townsend [:mossop] 2011-06-22 16:23:25 PDT
During startup it is possible for various changes to happen to add-ons. New things can be installed, uninstalled, enabled, disabled etc. Since this all happens before its possible for apps or extensions to register AddonListeners there is currently no way to find out what happened during startup. To fix bug 476430 we need an API that does this
Comment 1 Dave Townsend [:mossop] 2011-06-22 16:33:45 PDT
Created attachment 541222 [details] [diff] [review]
patch rev 1

This adds an API for add-on providers to register a change during startup:

AddonManagerPrivate.addStartupChange(aType, aId)

aType is a freeform string, aId is the add-on ID. A single ID can only be registered for one change, any previous changes will be dropped.

There is also a matching AddonManagerPrivate.removeStartupChange(aType, aId).

There is also the API for apps/extensions to use:

AddonManager.getStartupChanges(aType) which returns an array of IDs for that type of change.

XPIProvider is made to register changes for "installed", "uninstalled", "updated", "enabled", "disabled".

Mobile is fixed to use this new API instead of the old preference that we set to track almost the same thing on app upgrade. I spoke with mark and he is ok with the slight behaviour change here, now we will only warn about add-ons that used to be enabled and became disabled, before we were warning about any installed incompatible add-ons.
Comment 2 Robert Strong [:rstrong] (use needinfo to contact me) 2011-06-23 11:30:43 PDT
Comment on attachment 541222 [details] [diff] [review]
patch rev 1

>diff --git a/mobile/chrome/content/browser-ui.js b/mobile/chrome/content/browser-ui.js
>--- a/mobile/chrome/content/browser-ui.js
>+++ b/mobile/chrome/content/browser-ui.js
>...
>@@ -550,27 +551,24 @@ var BrowserUI = {
>       FormHelperUI.init();
>       FindHelperUI.init();
>       PageActions.init();
>       FullScreenVideo.init();
>       NewTabPopup.init();
>       CharsetMenu.init();
> 
>       // If some add-ons were disabled during during an application update, alert user
>-      if (Services.prefs.prefHasUserValue("extensions.disabledAddons")) {
>-        let addons = Services.prefs.getCharPref("extensions.disabledAddons").split(",");
>-        if (addons.length > 0) {
>-          let disabledStrings = Strings.browser.GetStringFromName("alertAddonsDisabled");
>-          let label = PluralForm.get(addons.length, disabledStrings).replace("#1", addons.length);
>-          let image = "chrome://browser/skin/images/alert-addons-30.png";
>+      let addons = AddonManager.getStartupChanges("disabled");
should be something like disabled-on-install instead of just disabled since this implies add-ons that are manually disabled are also included.

>+      if (addons.length > 0) {

>diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm
>--- a/toolkit/mozapps/extensions/AddonManager.jsm
>+++ b/toolkit/mozapps/extensions/AddonManager.jsm
>@@ -534,16 +544,60 @@ var AddonManagerInternal = {
>         }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
>       });
> 
>       notifyComplete();
>     });
>   },
> 
>   /**
>+   * Notifies the AddonManager that a change has been made to an add-on during
>+   * startup. One add-on can only be changed in one way during startup. If
>+   * addStartupChange is called multiple times for the same add-on in the same
>+   * startup then only the most recent call will have effect.
This function doesn't perform notifications to the AddonManager.
The there can only be one comment could be written better or omitted since it is covered by the comment following it.

>+   *
>+   * @param  aType
>+   *         The type of change as a string, providers can make up their own
>+   *         types of changes if necessary however the standard types are
>+   *         "installed", "updated", "uninstalled", "enabled", "disabled"
Think you will need disabled-on-install and possibly others. Perhaps an additional field for install-type which can contain update, upgrade, install (might need to take into account install on top of)?
Comment 3 Blair McBride [:Unfocused] (mostly unavailable, needinfo open, reviews not) 2011-06-23 18:08:27 PDT
(In reply to comment #1)
> AddonManagerPrivate.addStartupChange(aType, aId)
> 
> aType is a freeform string

Could this be a const instead? (Even if the const is just a string.)
Comment 4 Robert Strong [:rstrong] (use needinfo to contact me) 2011-06-27 15:29:21 PDT
Comment on attachment 541222 [details] [diff] [review]
patch rev 1

Please ignore the previous review comment... I have a better idea now about what you're doing here.

>diff --git a/mobile/chrome/content/browser-ui.js b/mobile/chrome/content/browser-ui.js
>--- a/mobile/chrome/content/browser-ui.js
>+++ b/mobile/chrome/content/browser-ui.js
>...
>@@ -550,27 +551,24 @@ var BrowserUI = {
>       FormHelperUI.init();
>       FindHelperUI.init();
>       PageActions.init();
>       FullScreenVideo.init();
>       NewTabPopup.init();
>       CharsetMenu.init();
> 
>       // If some add-ons were disabled during during an application update, alert user
>-      if (Services.prefs.prefHasUserValue("extensions.disabledAddons")) {
>-        let addons = Services.prefs.getCharPref("extensions.disabledAddons").split(",");
>-        if (addons.length > 0) {
>-          let disabledStrings = Strings.browser.GetStringFromName("alertAddonsDisabled");
>-          let label = PluralForm.get(addons.length, disabledStrings).replace("#1", addons.length);
>-          let image = "chrome://browser/skin/images/alert-addons-30.png";
>+      let addons = AddonManager.getStartupChanges("disabled");
nit: might be clearer if this were named addonIDs instead of addons... since it was that way before I leave that up to you.

>+      if (addons.length > 0) {
>+        let disabledStrings = Strings.browser.GetStringFromName("alertAddonsDisabled");
>+        let label = PluralForm.get(addons.length, disabledStrings).replace("#1", addons.length);
>+        let image = "chrome://browser/skin/images/alert-addons-30.png";
> 
>-          let alerts = Cc["@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService);
>-          alerts.showAlertNotification(image, Strings.browser.GetStringFromName("alertAddons"), label, false, "", null);
>-        }
>-        Services.prefs.clearUserPref("extensions.disabledAddons");
>+        let alerts = Cc["@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService);
>+        alerts.showAlertNotification(image, Strings.browser.GetStringFromName("alertAddons"), label, false, "", null);

>diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm
>--- a/toolkit/mozapps/extensions/AddonManager.jsm
>+++ b/toolkit/mozapps/extensions/AddonManager.jsm
>...
>@@ -534,16 +544,60 @@ var AddonManagerInternal = {
>         }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
>       });
> 
>       notifyComplete();
>     });
>   },
> 
>   /**
>+   * Notifies the AddonManager that a change has been made to an add-on during
>+   * startup. One add-on can only be changed in one way during startup. If
>+   * addStartupChange is called multiple times for the same add-on in the same
>+   * startup then only the most recent call will have effect.
This function doesn't perform notifications to the AddonManager.
The there can only be one comment could be written better or omitted since it is covered by the comment following it.

Perhaps change "the most recent call will have effect." to (could be written better)
the most recent call will be will be returned when calling getStartupChanges.


>+   *
>+   * @param  aType
>+   *         The type of change as a string, providers can make up their own
>+   *         types of changes if necessary however the standard types are
>+   *         "installed", "updated", "uninstalled", "enabled", "disabled"
I think this could be better written than the following though I think this would be an improvement
The type of change as a string. Providers can define their own types of changes however the standard types are "installed", "updated", "uninstalled", "enabled", and "disabled".

>+   * @param  aID
>+   *         The ID of the add-on changed
nit: "The ID of the changed add-on" or just "The ID of the add-on" as you used elsewhere.

>+   */
>+  addStartupChange: function AMI_addStartupChange(aType, aID) {
>+    if (gStarted)
>+      return;
>+
>+    // Ensure that an ID is only listed in one type of change
>+    for (let type in this.startupChanges)
>+      this.removeStartupChange(type, aID);
>+
>+    if (!(aType in this.startupChanges))
>+      this.startupChanges[aType] = [];
>+    this.startupChanges[aType].push(aID);
>+  },
>+
>...
>@@ -1226,16 +1288,22 @@ var AddonManager = {
>     AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
>                                           aName, aIconURL, aVersion, aLoadGroup);
>   },
> 
>   getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
>     AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype);
>   },
> 
>+  getStartupChanges: function AM_getStartupChanges(aType) {
>+    if (!(aType in AddonManagerInternal.startupChanges))
>+      return [];
>+    return AddonManagerInternal.startupChanges[aType].slice(0);
>+  },
I'm concerned that since this doesn't really return all startup changes consumers will try to use it and then find out it isn't suitable for their needs. Perhaps add a comment for this function? Would be helpful if the comment included the unusual cases as well such as an add-on being uninstalled unhiding the same add-on in a different install location will have a startup change of "updated" for the newly unhidden add-on.

Also, I just realized that the internal functions are commented and the ones we want people to use are not. :/
Comment 5 Robert Strong [:rstrong] (use needinfo to contact me) 2011-06-27 15:30:57 PDT
Comment on attachment 541222 [details] [diff] [review]
patch rev 1

>diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm
>--- a/toolkit/mozapps/extensions/XPIProvider.jsm
>+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
>@@ -1573,34 +1564,25 @@ var XPIProvider = {
>     Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false);
> 
>     let flushCaches = this.checkForChanges(aAppChanged, aOldAppVersion,
>                                            aOldPlatformVersion);
> 
>     // Changes to installed extensions may have changed which theme is selected
>     this.applyThemeChange();
> 
>-    if (Services.prefs.prefHasUserValue(PREF_EM_DISABLED_ADDONS_LIST))
>-      Services.prefs.clearUserPref(PREF_EM_DISABLED_ADDONS_LIST);
>-
>     // If the application has been upgraded and there are add-ons outside the
>     // application directory then we may need to synchronize compatibility
>     // information
>     if (aAppChanged && !this.allAppGlobal) {
>       // Should we show a UI or just pass the list via a pref?
>       if (Prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true)) {
>         this.showMismatchWindow();
>         flushCaches = true;
>       }
Might as well move the second if statement into the first

>-      else if (this.startupChanges.appDisabled.length > 0) {
>-        // Remember the list of add-ons that were disabled this startup so
>-        // the application can notify the user however it wants to
>-        Services.prefs.setCharPref(PREF_EM_DISABLED_ADDONS_LIST,
>-                                   this.startupChanges.appDisabled.join(","));
>-      }
>     }
> 
>     if (flushCaches) {
>       flushStartupCache();
> 
>       // UI displayed early in startup (like the compatibility UI) may have
>       // caused us to cache parts of the skin or locale in memory. These must
>       // be flushed to allow extension provided skins and locales to take full
Comment 6 Robert Strong [:rstrong] (use needinfo to contact me) 2011-06-28 13:12:10 PDT
Comment on attachment 541222 [details] [diff] [review]
patch rev 1

>diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm
>--- a/toolkit/mozapps/extensions/XPIProvider.jsm
>+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
>...
>@@ -2364,16 +2345,20 @@ var XPIProvider = {
>                                               aAddonState) {
>       let changed = false;
> 
>       // This add-ons metadata has not changed but it may have become visible
>       if (!(aOldAddon.id in visibleAddons)) {
>         visibleAddons[aOldAddon.id] = aOldAddon;
> 
>         if (!aOldAddon.visible) {
>+          // Remember add-ons that were updated during startup. It is difficult
>+          // to get the old add-on version here so we use "updated" to mean any
>+          // change from one version to another
Could you list the cases where this happens? For example, a different install location becomes visible is more accurate than version change. Same for any other places if applicable.

>+          AddonManagerPrivate.addStartupChange("updated", aOldAddon.id);
>           XPIDatabase.makeAddonVisible(aOldAddon);
> 
>           if (aOldAddon.bootstrap) {
>             // The add-on is bootstrappable so call its install script
>             let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
>             file.persistentDescriptor = aAddonState.descriptor;
>             XPIProvider.callBootstrapMethod(aOldAddon.id, aOldAddon.version, file,
>                                             "install",
Comment 7 Robert Strong [:rstrong] (use needinfo to contact me) 2011-06-28 13:13:52 PDT
Comment on attachment 541222 [details] [diff] [review]
patch rev 1

If it isn't difficult to accomplish I agree with Blair regarding using const
Comment 8 Dave Townsend [:mossop] 2011-06-28 14:38:49 PDT
Created attachment 542605 [details] [diff] [review]
patch rev 2

Not sure there is much need to re-review here, but I did switch to constants and did documentation against those about what to expect. I also switched the naming from "updated" to "changed" since that is a better match to what is actually happening here.
Comment 9 Robert Strong [:rstrong] (use needinfo to contact me) 2011-06-28 15:00:51 PDT
Comment on attachment 542605 [details] [diff] [review]
patch rev 2

>diff --git a/mobile/chrome/content/browser-ui.js b/mobile/chrome/content/browser-ui.js
>--- a/mobile/chrome/content/browser-ui.js
>+++ b/mobile/chrome/content/browser-ui.js
>@@ -34,16 +34,17 @@
>  * and other provisions required by the GPL or the LGPL. If you do not delete
>  * the provisions above, a recipient may use your version of this file under
>  * the terms of any one of the MPL, the GPL or the LGPL.
>  *
>  * ***** END LICENSE BLOCK ***** */
> 
> Cu.import("resource://gre/modules/XPCOMUtils.jsm");
> Cu.import("resource://gre/modules/Services.jsm");
>+Cu.import("resource://gre/modules/AddonManager.jsm");
> 
> [
>   ["AllPagesList", "popup_autocomplete", "cmd_openLocation"],
>   ["HistoryList", "history-items", "cmd_history"],
>   ["BookmarkList", "bookmarks-items", "cmd_bookmarks"],
> #ifdef MOZ_SERVICES_SYNC
>   ["RemoteTabsList", "remotetabs-items", "cmd_remoteTabs"]
> #endif
>@@ -550,27 +551,24 @@ var BrowserUI = {
>       FormHelperUI.init();
>       FindHelperUI.init();
>       PageActions.init();
>       FullScreenVideo.init();
>       NewTabPopup.init();
>       CharsetMenu.init();
> 
>       // If some add-ons were disabled during during an application update, alert user
>-      if (Services.prefs.prefHasUserValue("extensions.disabledAddons")) {
>-        let addons = Services.prefs.getCharPref("extensions.disabledAddons").split(",");
>-        if (addons.length > 0) {
>-          let disabledStrings = Strings.browser.GetStringFromName("alertAddonsDisabled");
>-          let label = PluralForm.get(addons.length, disabledStrings).replace("#1", addons.length);
>-          let image = "chrome://browser/skin/images/alert-addons-30.png";
>+      let addonIDs = AddonManager.getStartupChanges("disabled");
>+      if (addonIDs.length > 0) {
>+        let disabledStrings = Strings.browser.GetStringFromName("alertAddonsDisabled");
>+        let label = PluralForm.get(addonIDs.length, disabledStrings).replace("#1", addonIDs.length);
>+        let image = "chrome://browser/skin/images/alert-addons-30.png";
> 
>-          let alerts = Cc["@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService);
>-          alerts.showAlertNotification(image, Strings.browser.GetStringFromName("alertAddons"), label, false, "", null);
>-        }
>-        Services.prefs.clearUserPref("extensions.disabledAddons");
>+        let alerts = Cc["@mozilla.org/toaster-alerts-service;1"].getService(Ci.nsIAlertsService);
>+        alerts.showAlertNotification(image, Strings.browser.GetStringFromName("alertAddons"), label, false, "", null);
>       }
> 
> #ifdef MOZ_UPDATER
>       // Check for updates in progress
>       let updatePrompt = Cc["@mozilla.org/updates/update-prompt;1"].createInstance(Ci.nsIUpdatePrompt);
>       updatePrompt.checkForUpdates();
> #endif
>     }, false);
>diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm
>--- a/toolkit/mozapps/extensions/AddonManager.jsm
>+++ b/toolkit/mozapps/extensions/AddonManager.jsm
>@@ -268,16 +268,17 @@ var gStarted = false;
>  * contents hidden from API users.
>  */
> var AddonManagerInternal = {
>   installListeners: [],
>   addonListeners: [],
>   typeListeners: [],
>   providers: [],
>   types: {},
>+  startupChanges: {},
> 
>   // A read-only wrapper around the types dictionary
>   typesProxy: Proxy.create({
>     getOwnPropertyDescriptor: function(aName) {
>       if (!(aName in AddonManagerInternal.types))
>         return undefined;
> 
>       return {
>@@ -380,16 +381,23 @@ var AddonManagerInternal = {
>               url + "\"", e);
>       }
>     }
> 
>     this.providers.forEach(function(provider) {
>       callProvider(provider, "startup", null, appChanged, oldAppVersion,
>                    oldPlatformVersion);
>     });
>+
>+    // If this is a new profile just pretend that there were no changes
>+    if (appChanged === undefined) {
>+      for (let type in this.startupChanges)
>+        delete this.startupChanges[type];
>+    }
>+
>     gStarted = true;
>   },
> 
>   /**
>    * Registers a new AddonProvider.
>    *
>    * @param  aProvider
>    *         The provider to register
>@@ -462,24 +470,26 @@ var AddonManagerInternal = {
>     if (gStarted)
>       callProvider(aProvider, "shutdown");
>   },
> 
>   /**
>    * Shuts down the addon manager and all registered providers, this must clean
>    * up everything in order for automated tests to fake restarts.
>    */
>-  shutdown: function AM_shutdown() {
>+  shutdown: function AMI_shutdown() {
>     this.providers.forEach(function(provider) {
>       callProvider(provider, "shutdown");
>     });
> 
>     this.installListeners.splice(0);
>     this.addonListeners.splice(0);
>     this.typeListeners.splice(0);
>+    for (let type in this.startupChanges)
>+      delete this.startupChanges[type];
>     gStarted = false;
>   },
> 
>   /**
>    * Performs a background update check by starting an update for all add-ons
>    * that can be updated.
>    */
>   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
>@@ -534,16 +544,60 @@ var AddonManagerInternal = {
>         }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
>       });
> 
>       notifyComplete();
>     });
>   },
> 
>   /**
>+   * Called to notify the AddonManager that a change has been made to an add-on
>+   * during startup. If addStartupChange is called multiple times for the same
>+   * add-on in the same startup then only the most recent change will be
>+   * remembered.
>+   *
>+   * @param  aType
>+   *         The type of change as a string. Providers can define their own
>+   *         types of changes or use the existing defined STARTUP_CHANGE_*
>+   *         constants
>+   * @param  aID
>+   *         The ID of the add-on
>+   */
>+  addStartupChange: function AMI_addStartupChange(aType, aID) {
>+    if (gStarted)
>+      return;
>+
>+    // Ensure that an ID is only listed in one type of change
>+    for (let type in this.startupChanges)
>+      this.removeStartupChange(type, aID);
>+
>+    if (!(aType in this.startupChanges))
>+      this.startupChanges[aType] = [];
>+    this.startupChanges[aType].push(aID);
>+  },
>+
>+  /**
>+   * Removes a startup change for an add-on.
>+   *
>+   * @param  aType
>+   *         The type of change
>+   * @param  aID
>+   *         The ID of the add-on
>+   */
>+  removeStartupChange: function AMI_removeStartupChange(aType, aID) {
>+    if (gStarted)
>+      return;
>+
>+    if (!(aType in this.startupChanges))
>+      return;
>+
>+    this.startupChanges[aType] = this.startupChanges[aType].filter(function(aItem) aItem != aID);
>+  },
>+
>+  /**
>    * Calls all registered InstallListeners with an event. Any parameters after
>    * the extraListeners parameter are passed to the listener.
>    *
>    * @param  aMethod
>    *         The method on the listeners to call
>    * @param  aExtraListeners
>    *         An array of extra InstallListeners to also call
>    * @return false if any of the listeners returned false, true otherwise
>@@ -1059,16 +1113,24 @@ var AddonManagerPrivate = {
>   shutdown: function AMP_shutdown() {
>     AddonManagerInternal.shutdown();
>   },
> 
>   backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
>     AddonManagerInternal.backgroundUpdateCheck();
>   },
> 
>+  addStartupChange: function AMP_addStartupChange(aType, aID) {
>+    AddonManagerInternal.addStartupChange(aType, aID);
>+  },
>+
>+  removeStartupChange: function AMP_removeStartupChange(aType, aID) {
>+    AddonManagerInternal.removeStartupChange(aType, aID);
>+  },
>+
>   notifyAddonChanged: function AMP_notifyAddonChanged(aId, aType, aPendingRestart) {
>     AddonManagerInternal.notifyAddonChanged(aId, aType, aPendingRestart);
>   },
> 
>   updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() {
>     AddonManagerInternal.updateAddonAppDisabledStates();
>   },
> 
>@@ -1215,27 +1277,60 @@ var AddonManager = {
>   // Constants for how Addon options should be shown.
>   // Options will be opened in a new window
>   OPTIONS_TYPE_DIALOG: 1,
>   // Options will be displayed within the AM detail view
>   OPTIONS_TYPE_INLINE: 2,
>   // Options will be displayed in a new tab, if possible
>   OPTIONS_TYPE_TAB: 3,
> 
>+  // Constants for getStartupChanges, addStartupChange and removeStartupChange
>+  // Add-ons that were detected as installed during startup. Doesn't include
>+  // add-ons that were pending installation the last time the application ran.
>+  STARTUP_CHANGE_INSTALLED: "installed",
>+  // Add-ons that were detected as changed during startup. This includes an
>+  // add-on moving to a different location, changing version or just having
>+  // been detected as possibly changed.
>+  STARTUP_CHANGE_CHANGED: "changed",
>+  // Add-ons that were detected as uninstalled during startup. Doesn't include
>+  // add-ons that were pending uninstallation the last time the application ran.
>+  STARTUP_CHANGE_UNINSTALLED: "uninstalled",
>+  // Add-ons that were detected as disabled during startup, normally because of
>+  // an application change making an add-on incompatible. Doesn't include
>+  // add-ons that were pending being disabled the last time the application ran.
>+  STARTUP_CHANGE_DISABLED: "disabled",
>+  // Add-ons that were detected as enabled during startup, normally because of
>+  // an application change making an add-on compatible. Doesn't include
>+  // add-ons that were pending being enabled the last time the application ran.
>+  STARTUP_CHANGE_ENABLED: "enabled",
>+
>   getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
>                                                  aHash, aName, aIconURL,
>                                                  aVersion, aLoadGroup) {
>     AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
>                                           aName, aIconURL, aVersion, aLoadGroup);
>   },
> 
>   getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
>     AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype);
>   },
> 
>+  /**
>+   * Gets an array of add-on IDs that changed during the most recent startup.
>+   *
>+   * @param  aType
>+   *         The type of startup change to get
>+   * @return An array of add-on IDs
>+   */
>+  getStartupChanges: function AM_getStartupChanges(aType) {
>+    if (!(aType in AddonManagerInternal.startupChanges))
>+      return [];
>+    return AddonManagerInternal.startupChanges[aType].slice(0);
>+  },
>+
>   getAddonByID: function AM_getAddonByID(aId, aCallback) {
>     AddonManagerInternal.getAddonByID(aId, aCallback);
>   },
> 
>   getAddonsByIDs: function AM_getAddonsByIDs(aIds, aCallback) {
>     AddonManagerInternal.getAddonsByIDs(aIds, aCallback);
>   },
> 
>diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm
>--- a/toolkit/mozapps/extensions/XPIProvider.jsm
>+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
>@@ -63,17 +63,16 @@ const PREF_GENERAL_SKINS_SELECTEDSKIN = 
> const PREF_EM_CHECK_COMPATIBILITY_BASE = "extensions.checkCompatibility";
> const PREF_EM_CHECK_UPDATE_SECURITY   = "extensions.checkUpdateSecurity";
> const PREF_EM_UPDATE_URL              = "extensions.update.url";
> const PREF_EM_ENABLED_ADDONS          = "extensions.enabledAddons";
> const PREF_EM_EXTENSION_FORMAT        = "extensions.";
> const PREF_EM_ENABLED_SCOPES          = "extensions.enabledScopes";
> const PREF_EM_AUTO_DISABLED_SCOPES    = "extensions.autoDisableScopes";
> const PREF_EM_SHOW_MISMATCH_UI        = "extensions.showMismatchUI";
>-const PREF_EM_DISABLED_ADDONS_LIST    = "extensions.disabledAddons";
> const PREF_XPI_ENABLED                = "xpinstall.enabled";
> const PREF_XPI_WHITELIST_REQUIRED     = "xpinstall.whitelist.required";
> const PREF_XPI_WHITELIST_PERMISSIONS  = "xpinstall.whitelist.add";
> const PREF_XPI_BLACKLIST_PERMISSIONS  = "xpinstall.blacklist.add";
> const PREF_XPI_UNPACK                 = "extensions.alwaysUnpack";
> const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
> const PREF_INSTALL_DISTRO_ADDONS      = "extensions.installDistroAddons";
> const PREF_BRANCH_INSTALLED_ADDON     = "extensions.installedDistroAddon.";
>@@ -1451,24 +1450,16 @@ var XPIProvider = {
> 
>   // True if all of the add-ons found during startup were installed in the
>   // application install location
>   allAppGlobal: true,
>   // A string listing the enabled add-ons for annotating crash reports
>   enabledAddons: null,
>   // An array of add-on IDs of add-ons that were inactive during startup
>   inactiveAddonIDs: [],
>-  // A cache of the add-on IDs of add-ons that had changes performed to them
>-  // during this session's startup. This is preliminary work, hopefully it will
>-  // be expanded on in the future and an API made to get at it from the
>-  // application.
>-  startupChanges: {
>-    // Add-ons that became disabled for compatibility reasons
>-    appDisabled: []
>-  },
> 
>   /**
>    * Starts the XPI provider initializes the install locations and prefs.
>    *
>    * @param  aAppChanged
>    *         A tri-state value. Undefined means the current profile was created
>    *         for this session, true means the profile already existed but was
>    *         last used with an application with a different version number,
>@@ -1581,34 +1572,23 @@ var XPIProvider = {
>     Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false);
> 
>     let flushCaches = this.checkForChanges(aAppChanged, aOldAppVersion,
>                                            aOldPlatformVersion);
> 
>     // Changes to installed extensions may have changed which theme is selected
>     this.applyThemeChange();
> 
>-    if (Services.prefs.prefHasUserValue(PREF_EM_DISABLED_ADDONS_LIST))
>-      Services.prefs.clearUserPref(PREF_EM_DISABLED_ADDONS_LIST);
>-
>     // If the application has been upgraded and there are add-ons outside the
>     // application directory then we may need to synchronize compatibility
>-    // information
>-    if (aAppChanged && !this.allAppGlobal) {
>-      // Should we show a UI or just pass the list via a pref?
>-      if (Prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true)) {
>-        this.showMismatchWindow();
>-        flushCaches = true;
>-      }
>-      else if (this.startupChanges.appDisabled.length > 0) {
>-        // Remember the list of add-ons that were disabled this startup so
>-        // the application can notify the user however it wants to
>-        Services.prefs.setCharPref(PREF_EM_DISABLED_ADDONS_LIST,
>-                                   this.startupChanges.appDisabled.join(","));
>-      }
>+    // information but only if the mismatch UI isn't disabled
>+    if (aAppChanged && !this.allAppGlobal &&
>+        Prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true)) {
>+      this.showMismatchWindow();
>+      flushCaches = true;
>     }
> 
>     if (flushCaches) {
>       flushStartupCache();
> 
>       // UI displayed early in startup (like the compatibility UI) may have
>       // caused us to cache parts of the skin or locale in memory. These must
>       // be flushed to allow extension provided skins and locales to take full
>@@ -1667,19 +1647,16 @@ var XPIProvider = {
>     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
>     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
> 
>     this.bootstrappedAddons = {};
>     this.bootstrapScopes = {};
>     this.enabledAddons = null;
>     this.allAppGlobal = true;
> 
>-    for (let type in this.startupChanges)
>-      this.startupChanges[type] = [];
>-
>     this.inactiveAddonIDs = [];
> 
>     // If there are pending operations then we must update the list of active
>     // add-ons
>     if (Prefs.getBoolPref(PREF_PENDING_OPERATIONS, false)) {
>       XPIDatabase.updateActiveAddons();
>       XPIDatabase.writeAddonsList();
>       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
>@@ -2301,16 +2278,19 @@ var XPIProvider = {
>       newAddon._installLocation = aInstallLocation;
>       newAddon.updateDate = aAddonState.mtime;
>       newAddon.visible = !(newAddon.id in visibleAddons);
> 
>       // Update the database
>       XPIDatabase.updateAddonMetadata(aOldAddon, newAddon, aAddonState.descriptor);
>       if (newAddon.visible) {
>         visibleAddons[newAddon.id] = newAddon;
>+        // Remember add-ons that were changed during startup
>+        AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
>+                                             newAddon.id);
> 
>         // If this was the active theme and it is now disabled then enable the
>         // default theme
>         if (aOldAddon.active && isAddonDisabled(newAddon))
>           XPIProvider.enableDefaultTheme();
> 
>         // If the new add-on is bootstrapped and active then call its install method
>         if (newAddon.active && newAddon.bootstrap) {
>@@ -2385,16 +2365,19 @@ var XPIProvider = {
>                                               aAddonState) {
>       let changed = false;
> 
>       // This add-ons metadata has not changed but it may have become visible
>       if (!(aOldAddon.id in visibleAddons)) {
>         visibleAddons[aOldAddon.id] = aOldAddon;
> 
>         if (!aOldAddon.visible) {
>+          // Remember add-ons that were changed during startup.
>+          AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
>+                                               aOldAddon.id);
>           XPIDatabase.makeAddonVisible(aOldAddon);
> 
>           if (aOldAddon.bootstrap) {
>             // The add-on is bootstrappable so call its install script
>             let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
>             file.persistentDescriptor = aAddonState.descriptor;
>             XPIProvider.callBootstrapMethod(aOldAddon.id, aOldAddon.version, file,
>                                             "install",
>@@ -2432,20 +2415,16 @@ var XPIProvider = {
>           newAddon.userDisabled = aOldAddon.internalName != XPIProvider.selectedSkin;
> 
>         applyBlocklistChanges(aOldAddon, newAddon, aOldAppVersion,
>                               aOldPlatformVersion);
> 
>         let wasDisabled = isAddonDisabled(aOldAddon);
>         let isDisabled = isAddonDisabled(newAddon);
> 
>-        // Remember add-ons that became appDisabled by the application change
>-        if (aOldAddon.visible && newAddon.appDisabled && !aOldAddon.appDisabled)
>-          XPIProvider.startupChanges.appDisabled.push(aOldAddon.id);
>-
>         // If either property has changed update the database.
>         if (newAddon.appDisabled != aOldAddon.appDisabled ||
>             newAddon.userDisabled != aOldAddon.userDisabled ||
>             newAddon.softDisabled != aOldAddon.softDisabled) {
>           LOG("Add-on " + aOldAddon.id + " changed appDisabled state to " +
>               newAddon.appDisabled + ", userDisabled state to " +
>               newAddon.userDisabled + " and softDisabled state to " +
>               newAddon.softDisabled);
>@@ -2454,16 +2433,21 @@ var XPIProvider = {
>             userDisabled: newAddon.userDisabled,
>             softDisabled: newAddon.softDisabled
>           });
>         }
> 
>         // If this is a visible add-on and it has changed disabled state then we
>         // may need a restart or to update the bootstrap list.
>         if (aOldAddon.visible && wasDisabled != isDisabled) {
>+          // Remember add-ons that became disabled or enabled by the application
>+          // change
>+          let change = isDisabled ? AddonManager.STARTUP_CHANGE_DISABLED
>+                                  : AddonManager.STARTUP_CHANGE_ENABLED;
>+          AddonManagerPrivate.addStartupChange(change, aOldAddon.id);
>           if (aOldAddon.bootstrap) {
>             // Update the add-ons active state
>             aOldAddon.active = !isDisabled;
>             XPIDatabase.updateAddonActive(aOldAddon);
>           }
>           else {
>             changed = true;
>           }
>@@ -2490,18 +2474,29 @@ var XPIProvider = {
>      *         ran
>      * @return a boolean indicating if flushing caches is required to complete
>      *         changing this add-on
>      */
>     function removeMetadata(aInstallLocation, aOldAddon) {
>       // This add-on has disappeared
>       LOG("Add-on " + aOldAddon.id + " removed from " + aInstallLocation.name);
>       XPIDatabase.removeAddonMetadata(aOldAddon);
>+
>+      // Remember add-ons that were uninstalled during startup
>+      if (aOldAddon.visible) {
>+        AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED,
>+                                             aOldAddon.id);
>+      }
>+      else if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
>+                           .indexOf(aOldAddon.id) != -1) {
>+        AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
>+                                             aOldAddon.id);
>+      }
>+
>       if (aOldAddon.active) {
>-
>         // Enable the default theme if the previously active theme has been
>         // removed
>         if (aOldAddon.type == "theme")
>           XPIProvider.enableDefaultTheme();
> 
>         return true;
>       }
> 
>@@ -2623,16 +2618,34 @@ var XPIProvider = {
>         // add-on will just be unavailable until we try again in a subsequent
>         // startup
>         ERROR("Failed to add add-on " + aId + " in " + aInstallLocation.name +
>               " to database", e);
>         return false;
>       }
> 
>       if (newAddon.visible) {
>+        // Remember add-ons that were installed during startup. If there was a
>+        // cached manifest or migration data then this install is already
>+        // expected
>+        if (!aMigrateData && (!(aInstallLocation.name in aManifests) ||
>+                              !(aId in aManifests[aInstallLocation.name]))) {
>+          // If a copy from a higher priority location was removed then this
>+          // add-on has changed
>+          if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_UNINSTALLED)
>+                          .indexOf(newAddon.id) != -1) {
>+            AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
>+                                                 newAddon.id);
>+          }
>+          else {
>+            AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_INSTALLED,
>+                                                 newAddon.id);
>+          }
>+        }
>+
>         // Note if any visible add-on is not in the application install location
>         if (newAddon._installLocation.name != KEY_APP_GLOBAL)
>           XPIProvider.allAppGlobal = false;
> 
>         visibleAddons[newAddon.id] = newAddon;
> 
>         let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL;
> 
>@@ -2691,16 +2704,24 @@ var XPIProvider = {
>       // Check if the database knows about any add-ons in this install location.
>       let pos = knownLocations.indexOf(installLocation.name);
>       if (pos >= 0) {
>         knownLocations.splice(pos, 1);
>         let addons = XPIDatabase.getAddonsInLocation(installLocation.name);
>         // Iterate through the add-ons installed the last time the application
>         // ran
>         addons.forEach(function(aOldAddon) {
>+          // If a version of this add-on has been installed in an higher
>+          // priority install location then count it as changed
>+          if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
>+                          .indexOf(aOldAddon.id) != -1) {
>+            AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
>+                                                 aOldAddon.id);
>+          }
>+
>           // Check if the add-on is still installed
>           if (aOldAddon.id in addonStates) {
>             let addonState = addonStates[aOldAddon.id];
>             delete addonStates[aOldAddon.id];
> 
>             // Remember add-ons that were inactive during startup
>             if (aOldAddon.visible && !aOldAddon.active)
>               XPIProvider.inactiveAddonIDs.push(aOldAddon.id);
>diff --git a/toolkit/mozapps/extensions/content/update.js b/toolkit/mozapps/extensions/content/update.js
>--- a/toolkit/mozapps/extensions/content/update.js
>+++ b/toolkit/mozapps/extensions/content/update.js
>@@ -188,16 +188,20 @@ var gVersionInfoPage = {
>       // the EM using xmlHttpRequest when checking for updates.
>       setTimeout(close, 0);
>     }
>   },
> 
>   /////////////////////////////////////////////////////////////////////////////
>   // UpdateListener
>   onUpdateFinished: function(aAddon, status) {
>+    // If the add-on is now active then it won't have been disabled by startup
>+    if (aAddon.active)
>+      AddonManagerPrivate.removeStartupChange("disabled", aAddon.id);
>+
>     if (status != AddonManager.UPDATE_STATUS_NO_ERROR)
>       gUpdateWizard.errorItems.push(aAddon);
> 
>     ++this._completeCount;
> 
>     // Update the status text and progress bar
>     var updateStrings = document.getElementById("updateStrings");
>     var status = document.getElementById("versioninfo.status");
>@@ -426,16 +430,19 @@ var gInstallingPage = {
>   onInstallStarted: function(aInstall) {
>     var strings = document.getElementById("updateStrings");
>     var label = strings.getFormattedString("installingPrefix", [aInstall.name]);
>     var actionItem = document.getElementById("actionItem");
>     actionItem.value = label;
>   },
> 
>   onInstallEnded: function(aInstall) {
>+    // Remember that this add-on was updated during startup
>+    AddonManager.addStartupChange("updated", aInstall.id);
>+
>     this.startNextInstall();
>   },
> 
>   onInstallFailed: function(aInstall) {
>     this._errors.push(aInstall);
> 
>     this.startNextInstall();
>   }
>diff --git a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
>--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
>+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
>@@ -425,16 +425,30 @@ function isItemInAddonsList(aType, aDir,
> function isThemeInAddonsList(aDir, aId) {
>   return isItemInAddonsList("themes", aDir, aId);
> }
> 
> function isExtensionInAddonsList(aDir, aId) {
>   return isItemInAddonsList("extensions", aDir, aId);
> }
> 
>+function check_startup_changes(aType, aIds) {
>+  var ids = aIds.slice(0);
>+  ids.sort();
>+  var changes = AddonManager.getStartupChanges(aType);
>+  changes.sort();
>+
>+  // Remove the default theme if it is in the list
>+  var pos = changes.indexOf("{972ce4c6-7e08-4474-a285-3208198ce6fd}");
>+  if (pos != -1)
>+    changes.splice(pos, 1);
>+
>+  do_check_eq(JSON.stringify(ids), JSON.stringify(changes));
>+}
>+
> /**
>  * Escapes any occurances of &, ", < or > with XML entities.
>  *
>  * @param   str
>  *          The string to escape
>  * @return  The escaped string
>  */
> function escapeXML(aStr) {
>diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js
>--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js
>+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug542391.js
>@@ -33,17 +33,16 @@
>  * and other provisions required by the GPL or the LGPL. If you do not delete
>  * the provisions above, a recipient may use your version of this file under
>  * the terms of any one of the MPL, the GPL or the LGPL
>  *
>  * ***** END LICENSE BLOCK *****
>  */
> 
> const URI_EXTENSION_UPDATE_DIALOG     = "chrome://mozapps/content/extensions/update.xul";
>-const PREF_EM_DISABLED_ADDONS_LIST    = "extensions.disabledAddons";
> const PREF_EM_SHOW_MISMATCH_UI        = "extensions.showMismatchUI";
> 
> // The test extension uses an insecure update url.
> Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
> 
> do_load_httpd_js();
> var testserver;
> 
>@@ -64,26 +63,29 @@ var WindowWatcher = {
>     this.expected = false;
>     this.arguments = arguments.QueryInterface(AM_Ci.nsIVariant);
> 
>     var updated = !gCheckUpdates;
>     if (gCheckUpdates) {
>       AddonManager.getAddonByID("bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org", function(a6) {
>         a6.findUpdates({
>           onUpdateFinished: function() {
>+            AddonManagerPrivate.removeStartupChange("disabled", "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org");
>             updated = true;
>           }
>         }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
>       });
>     }
> 
>     var installed = !gInstallUpdate;
>     if (gInstallUpdate) {
>       // Simulate installing an update while in the dialog
>       installAllFiles([do_get_addon("test_bug542391_3_2")], function() {
>+        AddonManagerPrivate.removeStartupChange("disabled", "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org");
>+        AddonManagerPrivate.addStartupChange("updated", "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org");
>         installed = true;
>       });
>     }
> 
>     // The dialog is meant to be opened modally and the install operation can be
>     // asynchronous, so we must spin an event loop (like the modal window does)
>     // until the install is complete
>     let thr = AM_Cc["@mozilla.org/thread-manager;1"].
>@@ -150,16 +152,55 @@ function check_state_v1([a1, a2, a3, a4,
> 
>   do_check_neq(a6, null);
>   do_check_false(a6.appDisabled);
>   do_check_false(a6.userDisabled);
>   do_check_true(a6.isActive);
>   do_check_true(isExtensionInAddonsList(profileDir, a6.id));
> }
> 
>+function check_state_v1_2([a1, a2, a3, a4, a5, a6]) {
>+  do_check_neq(a1, null);
>+  do_check_false(a1.appDisabled);
>+  do_check_false(a1.userDisabled);
>+  do_check_true(a1.isActive);
>+  do_check_true(isExtensionInAddonsList(profileDir, a1.id));
>+
>+  do_check_neq(a2, null);
>+  do_check_false(a2.appDisabled);
>+  do_check_true(a2.userDisabled);
>+  do_check_false(a2.isActive);
>+  do_check_false(isExtensionInAddonsList(profileDir, a2.id));
>+
>+  do_check_neq(a3, null);
>+  do_check_true(a3.appDisabled);
>+  do_check_false(a3.userDisabled);
>+  do_check_false(a3.isActive);
>+  do_check_false(isExtensionInAddonsList(profileDir, a3.id));
>+  do_check_eq(a3.version, "2.0");
>+
>+  do_check_neq(a4, null);
>+  do_check_false(a4.appDisabled);
>+  do_check_true(a4.userDisabled);
>+  do_check_false(a4.isActive);
>+  do_check_false(isExtensionInAddonsList(profileDir, a4.id));
>+
>+  do_check_neq(a5, null);
>+  do_check_false(a5.appDisabled);
>+  do_check_false(a5.userDisabled);
>+  do_check_true(a5.isActive);
>+  do_check_true(isExtensionInAddonsList(profileDir, a5.id));
>+
>+  do_check_neq(a6, null);
>+  do_check_false(a6.appDisabled);
>+  do_check_false(a6.userDisabled);
>+  do_check_true(a6.isActive);
>+  do_check_true(isExtensionInAddonsList(profileDir, a6.id));
>+}
>+
> function check_state_v2([a1, a2, a3, a4, a5, a6]) {
>   do_check_neq(a1, null);
>   do_check_true(a1.appDisabled);
>   do_check_false(a1.userDisabled);
>   do_check_false(a1.isActive);
>   do_check_false(isExtensionInAddonsList(profileDir, a1.id));
> 
>   do_check_neq(a2, null);
>@@ -306,35 +347,50 @@ function run_test() {
>   installAllFiles([do_get_addon("test_bug542391_1"),
>                    do_get_addon("test_bug542391_2"),
>                    do_get_addon("test_bug542391_3_1"),
>                    do_get_addon("test_bug542391_4"),
>                    do_get_addon("test_bug542391_5"),
>                    do_get_addon("test_bug542391_6")], function() {
> 
>     restartManager();
>+    check_startup_changes("installed", []);
>+    check_startup_changes("updated", []);
>+    check_startup_changes("uninstalled", ["addon1@tests.mozilla.org"]);
>+    check_startup_changes("disabled", []);
>+    check_startup_changes("enabled", []);
> 
>     AddonManager.getAddonsByIDs(["bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>                                  "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org"],
>                                  function([a2, a4]) {
>       a2.userDisabled = true;
>       a4.userDisabled = true;
>       restartManager();
>+      check_startup_changes("installed", []);
>+      check_startup_changes("updated", []);
>+      check_startup_changes("uninstalled", []);
>+      check_startup_changes("disabled", []);
>+      check_startup_changes("enabled", []);
> 
>       AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>                                    "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>                                    "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>                                    "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>                                    "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>                                    "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org"],
>                                    function(addons) {
>         check_state_v1(addons);
> 
>         WindowWatcher.expected = true;
>         restartManager("2");
>+        check_startup_changes("installed", []);
>+        check_startup_changes("updated", []);
>+        check_startup_changes("uninstalled", []);
>+        check_startup_changes("disabled", ["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org"]);
>+        check_startup_changes("enabled", []);
>         do_check_false(WindowWatcher.expected);
> 
>         AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>                                      "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>                                      "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>                                      "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>                                      "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>                                      "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org"],
>@@ -353,16 +409,21 @@ function end_test() {
> }
> 
> // Upgrade to version 3 which will appDisable two more add-ons. Check that the
> // 3 already disabled add-ons were passed to the mismatch dialog.
> function run_test_1() {
>   gCheckUpdates = true;
>   WindowWatcher.expected = true;
>   restartManager("3");
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", []);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", ["bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org"]);
>+  check_startup_changes("enabled", []);
>   do_check_false(WindowWatcher.expected);
>   gCheckUpdates = false;
> 
>   AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>                                "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>                                "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>                                "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>                                "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>@@ -379,16 +440,21 @@ function run_test_1() {
>   });
> }
> 
> // Downgrade to version 2 which will remove appDisable from two add-ons and
> // should pass all 4 previously disabled add-ons.
> function run_test_2() {
>   WindowWatcher.expected = true;
>   restartManager("2");
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", []);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", []);
>+  check_startup_changes("enabled", ["bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org"]);
>   do_check_false(WindowWatcher.expected);
> 
>   AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>                                "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>                                "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>                                "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>                                "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>                                "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org"],
>@@ -396,81 +462,33 @@ function run_test_2() {
>     check_state_v2(addons);
> 
>     do_check_eq(WindowWatcher.arguments.length, 4);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org") >= 0);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org") >= 0);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org") >= 0);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org") >= 0);
> 
>-    run_test_3();
>-  });
>-}
>-
>-// Upgrade to version 3 which will appDisable two more add-ons.
>-function run_test_3() {
>-  Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, false);
>-
>-  restartManager("3");
>-
>-  AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>-                               "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>-                               "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>-                               "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>-                               "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>-                               "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org"],
>-                               function(addons) {
>-    check_state_v3(addons);
>-
>-    var disabled = [];
>-    try {
>-      disabled = Services.prefs.getCharPref(PREF_EM_DISABLED_ADDONS_LIST).split(",");
>-    }
>-    catch (e) {}
>-    do_check_eq(disabled.length, 2);
>-    do_check_true(disabled.indexOf("bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org") >= 0);
>-    do_check_true(disabled.indexOf("bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org") >= 0);
>-    Services.prefs.clearUserPref(PREF_EM_DISABLED_ADDONS_LIST);
>-
>-    run_test_4();
>-  });
>-}
>-
>-// Downgrade to version 2 which will remove appDisable from two add-ons.
>-function run_test_4() {
>-  restartManager("2");
>-
>-  AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>-                               "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>-                               "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>-                               "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>-                               "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>-                               "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org"],
>-                               function(addons) {
>-    check_state_v2(addons);
>-
>-    var disabled = [];
>-    try {
>-      disabled = Services.prefs.getCharPref(PREF_EM_DISABLED_ADDONS_LIST).split(",");
>-    }
>-    catch (e) {}
>-    do_check_eq(disabled.length, 0);
>-
>     run_test_5();
>   });
> }
> 
> // Upgrade to version 3 which will appDisable two more add-ons. Check that when
> // the upgrade dialog updates an add-on no restart is necessary
> function run_test_5() {
>   Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
>   gInstallUpdate = true;
> 
>   WindowWatcher.expected = true;
>   restartManager("3");
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", ["bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org"]);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", []);
>+  check_startup_changes("enabled", []);
>   do_check_false(WindowWatcher.expected);
>   gInstallUpdate = false;
> 
>   AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>                                "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>                                "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>                                "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>                                "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>@@ -478,15 +496,39 @@ function run_test_5() {
>                                function(addons) {
>     check_state_v3_2(addons);
> 
>     do_check_eq(WindowWatcher.arguments.length, 3);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org") >= 0);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org") >= 0);
>     do_check_true(WindowWatcher.arguments.indexOf("bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org") >= 0);
> 
>+    run_test_6();
>+  });
>+}
>+
>+// Downgrade to version 1 which will appEnable all the add-ons
>+function run_test_6() {
>+  WindowWatcher.expected = true;
>+  restartManager("1");
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", []);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", ["bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org"]);
>+  check_startup_changes("enabled", ["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org"]);
>+  do_check_false(WindowWatcher.expected);
>+
>+  AddonManager.getAddonsByIDs(["bug542391_1@tests.mozilla.org">bug542391_1@tests.mozilla.org",
>+                               "bug542391_2@tests.mozilla.org">bug542391_2@tests.mozilla.org",
>+                               "bug542391_3@tests.mozilla.org">bug542391_3@tests.mozilla.org",
>+                               "bug542391_4@tests.mozilla.org">bug542391_4@tests.mozilla.org",
>+                               "bug542391_5@tests.mozilla.org">bug542391_5@tests.mozilla.org",
>+                               "bug542391_6@tests.mozilla.org">bug542391_6@tests.mozilla.org"],
>+                               function(addons) {
>+    check_state_v1_2(addons);
>+
>     finish_test();
>   });
> }
> 
> function finish_test() {
>   do_test_finished();
> }
>diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
>--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
>+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
>@@ -124,16 +124,21 @@ function run_test() {
>   oldCache.create(AM_Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
> 
>   // Theme state is determined by the selected theme pref
>   Services.prefs.setCharPref("general.skins.selectedSkin", "theme1/1.0");
> 
>   Services.prefs.setCharPref("extensions.lastAppVersion", "1");
> 
>   startupManager();
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", []);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", []);
>+  check_startup_changes("enabled", []);
> 
>   do_check_false(oldCache.exists());
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org",
>diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js
>--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js
>+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate2.js
>@@ -134,16 +134,22 @@ function run_test() {
>   stmt.execute();
>   stmt.finalize();
> 
>   db.schemaVersion = 100;
>   Services.prefs.setIntPref("extensions.databaseSchema", 100);
>   db.close();
> 
>   startupManager();
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", []);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", []);
>+  check_startup_changes("enabled", []);
>+
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org",
>                                "addon6@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5, a6]) {
>     // addon1 was enabled in the database
>diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js b/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js
>--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js
>+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate3.js
>@@ -138,16 +138,22 @@ function run_test() {
> 
>   let old = do_get_file("data/test_migrate.rdf");
>   old.copyTo(gProfD, "extensions.rdf");
> 
>   // Theme state is determined by the selected theme pref
>   Services.prefs.setCharPref("general.skins.selectedSkin", "theme1/1.0");
> 
>   startupManager();
>+  check_startup_changes("installed", []);
>+  check_startup_changes("updated", []);
>+  check_startup_changes("uninstalled", []);
>+  check_startup_changes("disabled", []);
>+  check_startup_changes("enabled", []);
>+
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org",
>                                "addon6@tests.mozilla.org",
>                                "addon7@tests.mozilla.org",
>                                "theme1@tests.mozilla.org",
>diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
>--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
>+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
>@@ -121,16 +121,21 @@ function run_test() {
>     getService(AM_Ci.nsIObserverService);
>   obs.addObserver({
>     observe: function(aSubject, aTopic, aData) {
>       gCachePurged = true;
>     }
>   }, "startupcache-invalidate", false);
> 
>   startupManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org",
>                                "addon6@tests.mozilla.org",
>                                "addon7@tests.mozilla.org"],
>@@ -164,16 +169,23 @@ function run_test_1() {
>   writeInstallRDFForExtension(addon3, profileDir);
>   writeInstallRDFForExtension(addon4, profileDir);
>   writeInstallRDFForExtension(addon5, profileDir);
>   writeInstallRDFForExtension(addon6, profileDir);
>   writeInstallRDFForExtension(addon7, profileDir);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon1@tests.mozilla.org",
>+                                      "addon2@tests.mozilla.org",
>+                                      "addon3@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org",
>                                "addon6@tests.mozilla.org",
>@@ -257,16 +269,21 @@ function run_test_2() {
>   addon2.version="2.3";
>   writeInstallRDFForExtension(addon2, userDir);
>   var dest = profileDir.clone();
>   dest.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
>   dest.remove(true);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon3@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -313,16 +330,22 @@ function run_test_3() {
>   dest.remove(true);
>   dest = profileDir.clone();
>   dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
>   dest.remove(true);
>   writeInstallRDFForExtension(addon3, profileDir, "addon4@tests.mozilla.org");
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org",
>+                                    "addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -366,16 +389,21 @@ function run_test_3() {
> }
> 
> // Test that disabling an install location works
> function run_test_4() {
>   Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_SYSTEM);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon1@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -400,16 +428,21 @@ function run_test_4() {
> }
> 
> // Switching disabled locations works
> function run_test_5() {
>   Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_USER);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon1@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -440,16 +473,21 @@ function run_test_5() {
> }
> 
> // Resetting the pref makes everything visible again
> function run_test_6() {
>   Services.prefs.clearUserPref("extensions.enabledScopes");
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -484,16 +522,22 @@ function run_test_7() {
>   addon1.version = "1.2";
>   writeInstallRDFForExtension(addon1, profileDir);
>   var dest = userDir.clone();
>   dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
>   dest.remove(true);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org",
>+                                    "addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -533,16 +577,21 @@ function run_test_7() {
> }
> 
> // Disabling all locations still leaves the profile working
> function run_test_8() {
>   Services.prefs.setIntPref("extensions.enabledScopes", 0);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -576,16 +625,21 @@ function run_test_9() {
>   dest = globalDir.clone();
>   dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
>   dest.remove(true);
>   addon2.version = "2.4";
>   writeInstallRDFForExtension(addon2, profileDir);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -628,16 +682,21 @@ function run_test_10() {
>   var dest = profileDir.clone();
>   dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
>   dest.remove(true);
>   addon1.version = "1.3";
>   writeInstallRDFForExtension(addon1, userDir);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
>@@ -680,16 +739,22 @@ function run_test_11() {
>   dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
>   dest.remove(true);
>   dest = profileDir.clone();
>   dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
>   dest.remove(true);
> 
>   gCachePurged = false;
>   restartManager();
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon1@tests.mozilla.org",
>+                                        "addon2@tests.mozilla.org"]);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
>+  check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
>   do_check_true(gCachePurged);
> 
>   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
>                                "addon2@tests.mozilla.org",
>                                "addon3@tests.mozilla.org",
>                                "addon4@tests.mozilla.org",
>                                "addon5@tests.mozilla.org"],
>                                function([a1, a2, a3, a4, a5]) {
Comment 10 Dave Townsend [:mossop] 2011-06-29 09:26:21 PDT
Landed: http://hg.mozilla.org/mozilla-central/rev/96c264a18099
Comment 11 Dave Townsend [:mossop] 2011-06-29 12:28:00 PDT
Backed out due to some failing tests
Comment 12 Dave Townsend [:mossop] 2011-06-30 10:32:15 PDT
Relanded with a small typo fix, this span through try overnight so should be fine now. http://hg.mozilla.org/mozilla-central/rev/26457f316426
Comment 13 Eric Shepherd [:sheppy] 2011-08-08 13:42:20 PDT
Reference documentation updated:

https://developer.mozilla.org/en/Addons/Add-on_Manager/AddonManager
https://developer.mozilla.org/en/Addons/Add-on_Manager

And mentioned on Firefox 7 for developers.
Comment 14 Henrik Skupin (:whimboo) 2011-08-11 08:36:59 PDT
Marking as verified fixed based on check-in and passing tests.

Note You need to log in before you can comment on or make changes to this bug.