Backout 42bc5a310a29 (bug 782881) due to multi-orange.
authorBlair McBride <bmcbride@mozilla.com>
Wed, 15 Aug 2012 23:07:45 +1200
changeset 102224 7a847385e42bcdc4b3807fba6e333e29659f14dd
parent 102223 cdca67c9cf4ba0e469ae84bbd3585d97629881a2
child 102225 d33742ea515f9f5ebc5abef78923630e1804ec97
push id1004
push userbmcbride@mozilla.com
push dateWed, 15 Aug 2012 11:08:19 +0000
treeherderfx-team@7a847385e42b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs782881
milestone17.0a1
backs out42bc5a310a29c2708ed72b9a8ad7b237b9942326
Backout 42bc5a310a29 (bug 782881) due to multi-orange.
services/sync/tests/unit/test_prefs_store.js
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/addonManager.js
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js
toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/services/sync/tests/unit/test_prefs_store.js
+++ b/services/sync/tests/unit/test_prefs_store.js
@@ -2,19 +2,16 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-common/preferences.js");
 Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const PREFS_GUID = CommonUtils.encodeBase64URL(Services.appinfo.ID);
 
-loadAddonTestFunctions();
-startupManager();
-
 function makePersona(id) {
   return {
     id: id || Math.random().toString(),
     name: Math.random().toString(),
     headerURL: "http://localhost:1234/a"
   };
 }
 
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -362,17 +362,16 @@ function AddonType(aID, aLocaleURI, aLoc
     });
   }
   else {
     this.name = aLocaleKey;
   }
 }
 
 var gStarted = false;
-var gStartupComplete = false;
 var gCheckCompatibility = true;
 var gStrictCompatibility = true;
 var gCheckUpdateSecurityDefault = true;
 var gCheckUpdateSecurity = gCheckUpdateSecurityDefault;
 var gUpdateEnabled = true;
 var gAutoUpdateDefault = true;
 var gHotfixID = null;
 
@@ -531,30 +530,26 @@ var AddonManagerInternal = {
         Components.utils.import(url, {});
       }
       catch (e) {
         ERROR("Exception loading provider " + entry + " from category \"" +
               url + "\"", e);
       }
     }
 
-    // Once we start calling providers we must allow all normal methods to work.
-    gStarted = true;
-
     this.callProviders("startup", 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];
     }
 
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-    gStartupComplete = true;
+    gStarted = true;
   },
 
   /**
    * Registers a new AddonProvider.
    *
    * @param  aProvider
    *         The provider to register
    * @param  aTypes
@@ -667,50 +662,40 @@ var AddonManagerInternal = {
     }
   },
 
   /**
    * Shuts down the addon manager and all registered providers, this must clean
    * up everything in order for automated tests to fake restarts.
    */
   shutdown: function AMI_shutdown() {
-    Services.obs.removeObserver(this, "xpcom-shutdown");
     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
     Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
     Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
     Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
 
-    // Always clean up listeners, but only shutdown providers if they've been 
-    // started.
-    if (gStarted)
-      this.callProviders("shutdown");
+    this.callProviders("shutdown");
 
     this.managerListeners.splice(0, this.managerListeners.length);
     this.installListeners.splice(0, this.installListeners.length);
     this.addonListeners.splice(0, this.addonListeners.length);
     this.typeListeners.splice(0, this.typeListeners.length);
     for (let type in this.startupChanges)
       delete this.startupChanges[type];
     gStarted = false;
-    gStartupComplete = false;
   },
 
   /**
    * Notified when a preference we're interested in has changed.
    *
    * @see nsIObserver
    */
   observe: function AMI_observe(aSubject, aTopic, aData) {
-    if (aSubject == "xpcom-shutdown") {
-      this.shutdown();
-      return;
-    }
-
     switch (aData) {
       case PREF_EM_CHECK_COMPATIBILITY: {
         let oldValue = gCheckCompatibility;
         try {
           gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
         } catch(e) {
           gCheckCompatibility = true;
         }
@@ -862,20 +847,16 @@ var AddonManagerInternal = {
     return uri.replace(/\+/g, "%2B");
   },
 
   /**
    * Performs a background update check by starting an update for all add-ons
    * that can be updated.
    */
   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     let hotfixID = this.hotfixID;
 
     let checkHotfix = hotfixID &&
                       Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
                       Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
 
     if (!this.updateEnabled && !checkHotfix)
       return;
@@ -1047,17 +1028,17 @@ var AddonManagerInternal = {
     if (!aType || typeof aType != "string")
       throw Components.Exception("aType must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!aID || typeof aID != "string")
       throw Components.Exception("aID must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    if (gStartupComplete)
+    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] = [];
@@ -1076,37 +1057,33 @@ var AddonManagerInternal = {
     if (!aType || typeof aType != "string")
       throw Components.Exception("aType must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!aID || typeof aID != "string")
       throw Components.Exception("aID must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    if (gStartupComplete)
+    if (gStarted)
       return;
 
     if (!(aType in this.startupChanges))
       return;
 
     this.startupChanges[aType] = this.startupChanges[aType].filter(function(aItem) aItem != aID);
   },
 
   /**
    * Calls all registered AddonManagerListeners with an event. Any parameters
    * after the method parameter are passed to the listener.
    *
    * @param  aMethod
    *         The method on the listeners to call
    */
   callManagerListeners: function AMI_callManagerListeners(aMethod, ...aArgs) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aMethod || typeof aMethod != "string")
       throw Components.Exception("aMethod must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     let managerListeners = this.managerListeners.slice(0);
     for (let listener of managerListeners) {
       try {
         if (aMethod in listener)
@@ -1124,20 +1101,16 @@ var AddonManagerInternal = {
    *
    * @param  aMethod
    *         The method on the listeners to call
    * @param  aExtraListeners
    *         An optional array of extra InstallListeners to also call
    * @return false if any of the listeners returned false, true otherwise
    */
   callInstallListeners: function AMI_callInstallListeners(aMethod, aExtraListeners, ...aArgs) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aMethod || typeof aMethod != "string")
       throw Components.Exception("aMethod must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aExtraListeners && !Array.isArray(aExtraListeners))
       throw Components.Exception("aExtraListeners must be an array or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1165,20 +1138,16 @@ var AddonManagerInternal = {
   /**
    * Calls all registered AddonListeners with an event. Any parameters after
    * the method parameter are passed to the listener.
    *
    * @param  aMethod
    *         The method on the listeners to call
    */
   callAddonListeners: function AMI_callAddonListeners(aMethod, ...aArgs) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aMethod || typeof aMethod != "string")
       throw Components.Exception("aMethod must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     let addonListeners = this.addonListeners.slice(0);
     for (let listener of addonListeners) {
       try {
         if (aMethod in listener)
@@ -1199,20 +1168,16 @@ var AddonManagerInternal = {
    *         The ID of the enabled add-on
    * @param  aType
    *         The type of the enabled add-on
    * @param  aPendingRestart
    *         A boolean indicating if the change will only take place the next
    *         time the application is restarted
    */
   notifyAddonChanged: function AMI_notifyAddonChanged(aID, aType, aPendingRestart) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (aID && typeof aID != "string")
       throw Components.Exception("aID must be a string or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!aType || typeof aType != "string")
       throw Components.Exception("aType must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1220,35 +1185,27 @@ var AddonManagerInternal = {
   },
 
   /**
    * Notifies all providers they need to update the appDisabled property for
    * their add-ons in response to an application change such as a blocklist
    * update.
    */
   updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     this.callProviders("updateAddonAppDisabledStates");
   },
-
+  
   /**
    * Notifies all providers that the repository has updated its data for
    * installed add-ons.
    *
    * @param  aCallback
    *         Function to call when operation is complete.
    */
   updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     new AsyncObjectCaller(this.providers, "updateAddonRepositoryData", {
       nextObject: function(aCaller, aProvider) {
         callProvider(aProvider,
                      "updateAddonRepositoryData",
@@ -1280,20 +1237,16 @@ var AddonManagerInternal = {
    *         An optional placeholder version while the add-on is being downloaded
    * @param  aLoadGroup
    *         An optional nsILoadGroup to associate any network requests with
    * @throws if the aUrl, aCallback or aMimetype arguments are not specified
    */
   getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype,
                                                   aHash, aName, aIconURL,
                                                   aVersion, aLoadGroup) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aUrl || typeof aUrl != "string")
       throw Components.Exception("aURL must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1342,20 +1295,16 @@ var AddonManagerInternal = {
    *         The nsIFile where the add-on is located
    * @param  aCallback
    *         A callback to pass the AddonInstall to
    * @param  aMimetype
    *         An optional mimetype hint for the add-on
    * @throws if the aFile or aCallback arguments are not specified
    */
   getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!(aFile instanceof Ci.nsIFile))
       throw Components.Exception("aFile must be a nsIFile",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1386,20 +1335,16 @@ var AddonManagerInternal = {
    *
    * @param  aTypes
    *         An optional array of types to retrieve. Each type is a string name
    * @param  aCallback
    *         A callback which will be passed an array of AddonInstalls
    * @throws If the aCallback argument is not specified
    */
   getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (aTypes && !Array.isArray(aTypes))
       throw Components.Exception("aTypes must be an array or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1422,35 +1367,27 @@ var AddonManagerInternal = {
 
   /**
    * Asynchronously gets all current AddonInstalls.
    *
    * @param  aCallback
    *         A callback which will be passed an array of AddonInstalls
    */
   getAllInstalls: function AMI_getAllInstalls(aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     this.getInstallsByTypes(null, aCallback);
   },
 
   /**
    * Checks whether installation is enabled for a particular mimetype.
    *
    * @param  aMimetype
    *         The mimetype to check
    * @return true if installation is enabled for the mimetype
    */
   isInstallEnabled: function AMI_isInstallEnabled(aMimetype) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     let providers = this.providers.slice(0);
     for (let provider of providers) {
       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
           callProvider(provider, "isInstallEnabled"))
@@ -1465,20 +1402,16 @@ var AddonManagerInternal = {
    *
    * @param  aMimetype
    *         The mimetype of the add-on
    * @param  aURI
    *         The optional nsIURI of the source
    * @return true if the source is allowed to install this mimetype
    */
   isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aURI) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aURI && !(aURI instanceof Ci.nsIURI))
       throw Components.Exception("aURI must be a nsIURI or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1503,20 +1436,16 @@ var AddonManagerInternal = {
    *         The optional nsIURI that started the installs
    * @param  aInstalls
    *         The array of AddonInstalls to be installed
    */
   installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype,
                                                                   aSource,
                                                                   aURI,
                                                                   aInstalls) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aSource && !(aSource instanceof Ci.nsIDOMWindow))
       throw Components.Exception("aSource must be a nsIDOMWindow or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1610,20 +1539,16 @@ var AddonManagerInternal = {
    *
    * @param  aID
    *         The ID of the add-on to retrieve
    * @param  aCallback
    *         The callback to pass the retrieved add-on to
    * @throws if the aID or aCallback arguments are not specified
    */
   getAddonByID: function AMI_getAddonByID(aID, aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aID || typeof aID != "string")
       throw Components.Exception("aID must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1648,20 +1573,16 @@ var AddonManagerInternal = {
    *
    * @param  aGUID
    *         String GUID of add-on to retrieve
    * @param  aCallback
    *         The callback to pass the retrieved add-on to.
    * @throws if the aGUID or aCallback arguments are not specified
    */
   getAddonBySyncGUID: function AMI_getAddonBySyncGUID(aGUID, aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!aGUID || typeof aGUID != "string")
       throw Components.Exception("aGUID must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1687,20 +1608,16 @@ var AddonManagerInternal = {
    *
    * @param  aIDs
    *         The array of IDs to retrieve
    * @param  aCallback
    *         The callback to pass an array of Addons to
    * @throws if the aID or aCallback arguments are not specified
    */
   getAddonsByIDs: function AMI_getAddonsByIDs(aIDs, aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (!Array.isArray(aIDs))
       throw Components.Exception("aIDs must be an array",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1725,20 +1642,16 @@ var AddonManagerInternal = {
    *
    * @param  aTypes
    *         An optional array of types to retrieve. Each type is a string name
    * @param  aCallback
    *         The callback to pass an array of Addons to.
    * @throws if the aCallback argument is not specified
    */
   getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (aTypes && !Array.isArray(aTypes))
       throw Components.Exception("aTypes must be an array or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1761,43 +1674,31 @@ var AddonManagerInternal = {
 
   /**
    * Asynchronously gets all installed add-ons.
    *
    * @param  aCallback
    *         A callback which will be passed an array of Addons
    */
   getAllAddons: function AMI_getAllAddons(aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
-    if (typeof aCallback != "function")
-      throw Components.Exception("aCallback must be a function",
-                                 Cr.NS_ERROR_INVALID_ARG);
-
     this.getAddonsByTypes(null, aCallback);
   },
 
   /**
    * Asynchronously gets add-ons that have operations waiting for an application
    * restart to complete.
    *
    * @param  aTypes
    *         An optional array of types to retrieve. Each type is a string name
    * @param  aCallback
    *         The callback to pass the array of Addons to
    * @throws if the aCallback argument is not specified
    */
   getAddonsWithOperationsByTypes:
   function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) {
-    if (!gStarted)
-      throw Components.Exception("AddonManager is not initialized",
-                                 Cr.NS_ERROR_NOT_INITIALIZED);
-
     if (aTypes && !Array.isArray(aTypes))
       throw Components.Exception("aTypes must be an array or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
@@ -1874,16 +1775,17 @@ var AddonManagerInternal = {
    * @param  aListener
    *         The AddonListener to remove
    */
   removeAddonListener: function AMI_removeAddonListener(aListener) {
     if (!aListener || typeof aListener != "object")
       throw Components.Exception("aListener must be an AddonListener object",
                                  Cr.NS_ERROR_INVALID_ARG);
 
+
     let pos = 0;
     while (pos < this.addonListeners.length) {
       if (this.addonListeners[pos] == aListener)
         this.addonListeners.splice(pos, 1);
       else
         pos++;
     }
   },
@@ -2013,16 +1915,20 @@ var AddonManagerPrivate = {
   registerProvider: function AMP_registerProvider(aProvider, aTypes) {
     AddonManagerInternal.registerProvider(aProvider, aTypes);
   },
 
   unregisterProvider: function AMP_unregisterProvider(aProvider) {
     AddonManagerInternal.unregisterProvider(aProvider);
   },
 
+  shutdown: function AMP_shutdown() {
+    AddonManagerInternal.shutdown();
+  },
+
   backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
     AddonManagerInternal.backgroundUpdateCheck();
   },
 
   addStartupChange: function AMP_addStartupChange(aType, aID) {
     AddonManagerInternal.addStartupChange(aType, aID);
   },
 
--- a/toolkit/mozapps/extensions/addonManager.js
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -49,18 +49,23 @@ function amManager() {
 
 amManager.prototype = {
   observe: function AMC_observe(aSubject, aTopic, aData) {
     let os = Cc["@mozilla.org/observer-service;1"].
              getService(Ci.nsIObserverService);
 
     switch (aTopic) {
     case "addons-startup":
+      os.addObserver(this, "xpcom-shutdown", false);
       AddonManagerPrivate.startup();
       break;
+    case "xpcom-shutdown":
+      os.removeObserver(this, "xpcom-shutdown");
+      AddonManagerPrivate.shutdown();
+      break;
     }
   },
 
   /**
    * @see amIWebInstaller.idl
    */
   isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) {
     return AddonManager.isInstallEnabled(aMimetype);
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -388,18 +388,17 @@ function shutdownManager() {
   obs.addObserver({
     observe: function(aSubject, aTopic, aData) {
       repositoryShutdown = true;
       obs.removeObserver(this, "addon-repository-shutdown");
     }
   }, "addon-repository-shutdown", false);
 
   obs.notifyObservers(null, "quit-application-granted", null);
-  let scope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
-  scope.AddonManagerInternal.shutdown();
+  gInternalManager.observe(null, "xpcom-shutdown", null);
   gInternalManager = null;
 
   AddonRepository.shutdown();
 
   // Load the add-ons list as it was after application shutdown
   loadAddonsList();
 
   // Clear any crash report annotations
@@ -412,17 +411,17 @@ function shutdownManager() {
   // Wait until we observe the shutdown notifications
   while (!repositoryShutdown || !xpiShutdown) {
     if (thr.hasPendingEvents())
       thr.processNextEvent(false);
   }
 
   // Force the XPIProvider provider to reload to better
   // simulate real-world usage.
-  scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
+  let scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
   AddonManagerPrivate.unregisterProvider(scope.XPIProvider);
   Components.utils.unload("resource://gre/modules/XPIProvider.jsm");
 }
 
 function loadAddonsList() {
   function readDirectories(aSection) {
     var dirs = [];
     var keys = parser.getKeys(aSection);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug514327_3.js
@@ -115,17 +115,16 @@ function do_update_blocklist(aDatafile, 
 
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
 
   gTestserver = new HttpServer();
   gTestserver.registerDirectory("/data/", do_get_file("data"));
   gTestserver.start(4444);
 
-  startupManager();
 
   // initialize the blocklist with no entries
   var blocklistFile = gProfD.clone();
   blocklistFile.append("blocklist.xml");
   if (blocklistFile.exists())
     blocklistFile.remove(false);
   var source = do_get_file("data/test_bug514327_3_empty.xml");
   source.copyTo(gProfD, "blocklist.xml");
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug616841.js
@@ -8,17 +8,16 @@ function test_string_compare() {
   do_check_true("C".localeCompare("D") < 0);
   do_check_true("D".localeCompare("C") > 0);
   do_check_true("\u010C".localeCompare("D") < 0);
   do_check_true("D".localeCompare("\u010C") > 0);
 }
 
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-  startupManager();
 
   do_test_pending();
 
   test_string_compare();
 
   AddonManager.getAddonByID("foo", function(aAddon) {
     test_string_compare();
     do_test_finished();
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// Verify that API functions fail if the Add-ons Manager isn't initialised.
-
-const IGNORE = ["escapeAddonURI", "shouldAutoUpdate", "getStartupChanges",
-                "addTypeListener", "removeTypeListener",
-                "addAddonListener", "removeAddonListener",
-                "addInstallListener", "removeInstallListener",
-                "addManagerListener", "removeManagerListener"];
-
-const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
-                        "AddonScreenshot", "AddonType", "startup", "shutdown",
-                        "registerProvider", "unregisterProvider",
-                        "addStartupChange", "removeStartupChange"];
-
-function test_functions() {
-  for (let prop in AddonManager) {
-    if (typeof AddonManager[prop] != "function")
-      continue;
-    if (IGNORE.indexOf(prop) != -1)
-      continue;
-
-    try {
-      do_print("AddonManager." + prop);
-      AddonManager[prop]();
-      do_throw(prop + " did not throw an exception");
-    }
-    catch (e) {
-      if (e.result != Components.results.NS_ERROR_NOT_INITIALIZED)
-        do_throw(prop + " threw an unexpected exception: " + e);
-    }
-  }
-
-  for (let prop in AddonManagerPrivate) {
-    if (typeof AddonManagerPrivate[prop] != "function")
-      continue;
-    if (IGNORE_PRIVATE.indexOf(prop) != -1)
-      continue;
-
-    try {
-      do_print("AddonManagerPrivate." + prop);
-      AddonManagerPrivate[prop]();
-      do_throw(prop + " did not throw an exception");
-    }
-    catch (e) {
-      if (e.result != Components.results.NS_ERROR_NOT_INITIALIZED)
-        do_throw(prop + " threw an unexpected exception: " + e);
-    }
-  }
-}
-
-function run_test() {
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-  test_functions();
-  startupManager();
-  shutdownManager();
-  test_functions();
-}
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -188,17 +188,16 @@ skip-if = os == "android"
 [test_permissions.js]
 [test_plugins.js]
 [test_pluginBlocklistCtp.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
 [test_pref_properties.js]
 [test_registry.js]
 [test_safemode.js]
-[test_shutdown.js]
 [test_startup.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
 [test_syncGUID.js]
 [test_strictcompatibility.js]
 [test_targetPlatforms.js]
 [test_theme.js]
 # Bug 676992: test consistently fails on Android