Bug 995108 - Disable blocklist and update timers during Addon Manager mochitests. r=mossop, a=lmandel
authorIrving Reid <irving@mozilla.com>
Tue, 02 Sep 2014 11:47:11 -0400
changeset 224821 fbeb249588a89cd926ef0e9968289b89fa4f56ed
parent 224820 314a3069165d1f485a07200ba52086ddf5bc8547
child 224822 10ce60dfd2237fdf026555da950363839a72076e
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmossop, lmandel
bugs995108
milestone34.0a2
Bug 995108 - Disable blocklist and update timers during Addon Manager mochitests. r=mossop, a=lmandel
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/addonManager.js
toolkit/mozapps/extensions/test/browser/browser_about.js
toolkit/mozapps/extensions/test/browser/browser_bug586574.js
toolkit/mozapps/extensions/test/browser/head.js
toolkit/mozapps/extensions/test/xpcshell/head_addons.js
toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
toolkit/mozapps/extensions/test/xpcshell/test_hotfix.js
toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
toolkit/mozapps/extensions/test/xpcshell/test_update.js
toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -1142,27 +1142,24 @@ var AddonManagerInternal = {
    * @return Promise{null} Resolves when the background update check is complete
    *                       (the resulting addon installations may still be in progress).
    */
   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
-    logger.debug("Background update check beginning");
-
-    return Task.spawn(function* backgroundUpdateTask() {
+    let buPromise = Task.spawn(function* backgroundUpdateTask() {
       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;
+      logger.debug("Background update check beginning");
 
       Services.obs.notifyObservers(null, "addons-background-update-start", null);
 
       if (this.updateEnabled) {
         let scope = {};
         Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
         scope.LightweightThemeManager.updateCurrentTheme();
 
@@ -1290,16 +1287,19 @@ var AddonManagerInternal = {
         }
       }
 
       logger.debug("Background update check complete");
       Services.obs.notifyObservers(null,
                                    "addons-background-update-complete",
                                    null);
     }.bind(this));
+    // Fork the promise chain so we can log the error and let our caller see it too.
+    buPromise.then(null, e => logger.warn("Error in background update", e));
+    return buPromise;
   },
 
   /**
    * Adds a add-on to the list of detected changes for this 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
@@ -2331,16 +2331,30 @@ this.AddonManagerPrivate = {
   unregisterProvider: function AMP_unregisterProvider(aProvider) {
     AddonManagerInternal.unregisterProvider(aProvider);
   },
 
   backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
     return AddonManagerInternal.backgroundUpdateCheck();
   },
 
+  backgroundUpdateTimerHandler() {
+    // Don't call through to the real update check if no checks are enabled.
+    let checkHotfix = this.hotfixID &&
+                      Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
+                      Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
+
+    if (!this.updateEnabled && !checkHotfix) {
+      logger.info("Skipping background update check");
+      return;
+    }
+    // Don't return the promise here, since the caller doesn't care.
+    AddonManagerInternal.backgroundUpdateCheck();
+  },
+
   addStartupChange: function AMP_addStartupChange(aType, aID) {
     AddonManagerInternal.addStartupChange(aType, aID);
   },
 
   removeStartupChange: function AMP_removeStartupChange(aType, aID) {
     AddonManagerInternal.removeStartupChange(aType, aID);
   },
 
--- a/toolkit/mozapps/extensions/addonManager.js
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -147,17 +147,17 @@ amManager.prototype = {
       }, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, loadGroup);
     }
     buildNextInstall();
 
     return retval;
   },
 
   notify: function AMC_notify(aTimer) {
-    AddonManagerPrivate.backgroundUpdateCheck();
+    AddonManagerPrivate.backgroundUpdateTimerHandler();
   },
 
   /**
    * messageManager callback function.
    *
    * Listens to requests from child processes for InstallTrigger
    * activity, and sends back callbacks.
    */
--- a/toolkit/mozapps/extensions/test/browser/browser_about.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_about.js
@@ -53,32 +53,32 @@ function test_about_window(aAddonItemNam
 
   info("Waiting for about dialog");
   Services.ww.registerNotification(function TEST_ww_observer(aSubject, aTopic,
                                                              aData) {
     if (aTopic == "domwindowclosed") {
       Services.ww.unregisterNotification(TEST_ww_observer);
 
       info("About dialog closed, waiting for focus on browser window");
-      waitForFocus(aCallback);
+      waitForFocus(() => executeSoon(aCallback));
     } else if (aTopic == "domwindowopened") {
       info("About dialog opened, waiting for focus");
 
       let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
       waitForFocus(function() {
         info("Saw about dialog");
 
         is(win.location,
            aExpectedAboutUri,
            "The correct add-on about window should have opened");
 
         is(win.arguments && win.arguments[0] && win.arguments[0].name,
            aAddonItemName,
            "window.arguments[0] should refer to the add-on object");
 
-        win.close();
+        executeSoon(() => win.close());
       }, win);
     }
   });
 
   gManagerWindow.gViewController.doCommand("cmd_showItemAbout",
                                            addonItem.mAddon);
 }
--- a/toolkit/mozapps/extensions/test/browser/browser_bug586574.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug586574.js
@@ -16,16 +16,28 @@ var gProvider;
 
 var gUtilsBtn;
 var gUtilsMenu;
 var gDropdownMenu;
 var gSetDefault;
 var gResetToAutomatic;
 var gResetToManual;
 
+// Remember and restore preferences for this test, and make sure
+// we don't accidentally start a background update while the prefs
+// are enabled.
+disableBackgroundUpdateTimer();
+var updatesEnabled = Services.prefs.getBoolPref(PREF_UPDATE_ENABLED);
+var autoDefault = Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT);
+registerCleanupFunction(() => {
+  Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, updatesEnabled);
+  Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, autoDefault);
+  enableBackgroundUpdateTimer();
+});
+
 function test() {
   waitForExplicitFinish();
 
   gProvider = new MockProvider();
 
   gProvider.createAddons([{
     id: "addon1@tests.mozilla.org",
     name: "addon 1",
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -103,16 +103,51 @@ function checkOpenWindows(aWindowID) {
       found = true;
       win.close();
     }
   }
   if (found)
     ok(false, "Found unexpected " + aWindowID + " window still open");
 }
 
+// Tools to disable and re-enable the background update and blocklist timers
+// so that tests can protect themselves from unwanted timer events.
+let gCatMan = Components.classes["@mozilla.org/categorymanager;1"]
+                           .getService(Components.interfaces.nsICategoryManager);
+// Default values from toolkit/mozapps/extensions/extensions.manifest, but disable*UpdateTimer()
+// records the actual value so we can put it back in enable*UpdateTimer()
+let backgroundUpdateConfig = "@mozilla.org/addons/integration;1,getService,addon-background-update-timer,extensions.update.interval,86400";
+let blocklistUpdateConfig = "@mozilla.org/extensions/blocklist;1,getService,blocklist-background-update-timer,extensions.blocklist.interval,86400";
+
+let UTIMER = "update-timer";
+let AMANAGER = "addonManager";
+let BLOCKLIST = "nsBlocklistService";
+
+function disableBackgroundUpdateTimer() {
+  info("Disabling " + UTIMER + " " + AMANAGER);
+  backgroundUpdateConfig = gCatMan.getCategoryEntry(UTIMER, AMANAGER);
+  gCatMan.deleteCategoryEntry(UTIMER, AMANAGER, true);
+}
+
+function enableBackgroundUpdateTimer() {
+  info("Enabling " + UTIMER + " " + AMANAGER);
+  gCatMan.addCategoryEntry(UTIMER, AMANAGER, backgroundUpdateConfig, false, true);
+}
+
+function disableBlocklistUpdateTimer() {
+  info("Disabling " + UTIMER + " " + BLOCKLIST);
+  blocklistUpdateConfig = gCatMan.getCategoryEntry(UTIMER, BLOCKLIST);
+  gCatMan.deleteCategoryEntry(UTIMER, BLOCKLIST, true);
+}
+
+function enableBlocklistUpdateTimer() {
+  info("Enabling " + UTIMER + " " + BLOCKLIST);
+  gCatMan.addCategoryEntry(UTIMER, BLOCKLIST, blocklistUpdateConfig, false, true);
+}
+
 registerCleanupFunction(function() {
   // Restore prefs
   for (let pref of gRestorePrefs) {
     if (pref.type == "clear")
       Services.prefs.clearUserPref(pref.name);
     else if (pref.type == Services.prefs.PREF_BOOL)
       Services.prefs.setBoolPref(pref.name, pref.value);
     else if (pref.type == Services.prefs.PREF_INT)
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -1570,12 +1570,21 @@ function callback_soon(aFunction) {
  * A promise-based variant of AddonManager.getAddonsByIDs.
  *
  * @param {array} list As the first argument of AddonManager.getAddonsByIDs
  * @return {promise}
  * @resolve {array} The list of add-ons sent by AddonManaget.getAddonsByIDs to
  * its callback.
  */
 function promiseAddonsByIDs(list) {
-  let deferred = Promise.defer();
-  AddonManager.getAddonsByIDs(list, deferred.resolve);
-  return deferred.promise;
+  return new Promise((resolve, reject) => AddonManager.getAddonsByIDs(list, resolve));
 }
+
+/**
+ * A promise-based variant of AddonManager.getAddonByID.
+ *
+ * @param {string} aId The ID of the add-on.
+ * @return {promise}
+ * @resolve {AddonWrapper} The corresponding add-on, or null.
+ */
+function promiseAddonByID(aId) {
+  return new Promise((resolve, reject) => AddonManager.getAddonByID(aId, resolve));
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_cache.js
@@ -396,37 +396,16 @@ const WITH_EXTENSION_CACHE = [{
   },
   get icons () {
     return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
   },
   screenshots:            [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }],
   sourceURI:              NetUtil.newURI(ADDON_FILES[2]).spec
 }];
 
-
-/*
- * Trigger an AddonManager background update check
- *
- * @return Promise{null}
- *         Resolves when the background update notification is received
- */
-function trigger_background_update() {
-  return new Promise((resolve, reject) => {
-    Services.obs.addObserver({
-      observe: function(aSubject, aTopic, aData) {
-        do_print("Observed " + aTopic);
-        Services.obs.removeObserver(this, "addons-background-update-complete");
-        resolve();
-      }
-    }, "addons-background-update-complete", false);
-
-    gInternalManager.notify(null);
-  });
-}
-
 let gDBFile = gProfD.clone();
 gDBFile.append(FILE_DATABASE);
 
 /*
  * Check the actual add-on results against the expected add-on results
  *
  * @param  aActualAddons
  *         The array of actual add-ons to check
@@ -670,43 +649,43 @@ add_task(function* run_test_12() {
 });
 
 // Tests that a background update with caching disabled deletes the add-ons
 // database, and that XPI add-ons still do not use any of repository properties
 add_task(function* run_test_13() {
   do_check_true(gDBFile.exists());
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_EMPTY);
 
-  yield trigger_background_update();
+  yield AddonManagerInternal.backgroundUpdateCheck();
   // Database should have been deleted
   do_check_false(gDBFile.exists());
 
   let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
   check_results(aAddons, WITHOUT_CACHE);
 });
 
 // Tests that the XPI add-ons have the correct properties if caching is
 // enabled but has no information
 add_task(function* run_test_14() {
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
 
-  yield trigger_background_update();
+  yield AddonManagerInternal.backgroundUpdateCheck();
   yield AddonRepository.flush();
   do_check_true(gDBFile.exists());
 
   let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
   check_results(aAddons, WITHOUT_CACHE);
 });
 
 // Tests that the XPI add-ons correctly use the repository properties when
 // caching is enabled and the repository information is available
 add_task(function* run_test_15() {
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_RESULTS);
 
-  yield trigger_background_update();
+  yield AddonManagerInternal.backgroundUpdateCheck();
   let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
   check_results(aAddons, WITH_CACHE);
 });
 
 // Tests that restarting the manager does not change the checked properties
 // on the XPI add-ons (repository properties still exist and are still properly
 // used)
 add_task(function* run_test_16() {
@@ -715,16 +694,16 @@ add_task(function* run_test_16() {
   let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
   check_results(aAddons, WITH_CACHE);
 });
 
 // Tests that setting a list of types to cache works
 add_task(function* run_test_17() {
   Services.prefs.setCharPref(PREF_GETADDONS_CACHE_TYPES, "foo,bar,extension,baz");
 
-  yield trigger_background_update();
+  yield AddonManagerInternal.backgroundUpdateCheck();
   let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
   check_results(aAddons, WITH_EXTENSION_CACHE);
 });
 
 add_task(function* end_test() {
   yield new Promise((resolve, reject) => gServer.stop(resolve));
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_compatoverrides.js
@@ -145,34 +145,16 @@ var addon10 = {
     maxVersion: "1"
   }]
 };
 
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
-
-/*
- * Trigger an AddonManager background update check
- *
- * @param  aCallback
- *         Callback to call once the background update is complete
- */
-function trigger_background_update(aCallback) {
-  Services.obs.addObserver({
-    observe: function(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(this, "addons-background-update-complete");
-      do_execute_soon(aCallback);
-    }
-  }, "addons-background-update-complete", false);
-
-  gInternalManager.notify(null);
-}
-
 function run_test() {
   do_test_pending();
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
 
   writeInstallRDFForExtension(addon1, profileDir);
   writeInstallRDFForExtension(addon2, profileDir);
   writeInstallRDFForExtension(addon3, profileDir);
   writeInstallRDFForExtension(addon4, profileDir);
@@ -180,17 +162,17 @@ function run_test() {
   writeInstallRDFForExtension(addon6, profileDir);
   writeInstallRDFForExtension(addon7, profileDir);
   writeInstallRDFForExtension(addon8, profileDir);
   writeInstallRDFForExtension(addon9, profileDir);
   writeInstallRDFForExtension(addon10, profileDir);
 
   startupManager();
 
-  trigger_background_update(run_test_1);
+  AddonManagerInternal.backgroundUpdateCheck().then(run_test_1);
 }
 
 function end_test() {
   gServer.stop(do_test_finished);
 }
 
 function check_compat_status(aCallback) {
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_hotfix.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_hotfix.js
@@ -47,18 +47,18 @@ function run_test_1() {
   }, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallEnded",
   ], callback_soon(check_test_1));
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  // We don't need to wait on the promise, just waiting for the install to finish is enough.
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function check_test_1() {
   restartManager();
 
   AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
     do_check_neq(aAddon, null);
     do_check_eq(aAddon.version, "1.0");
@@ -73,25 +73,18 @@ function run_test_2() {
   restartManager();
 
   AddonManager.addInstallListener({
     onNewInstall: function() {
       do_throw("Should not have seen a new install created");
     }
   });
 
-  function observer() {
-    Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
-    do_execute_soon(run_test_3);
-  }
-
-  Services.obs.addObserver(observer, "addons-background-update-complete", false);
-
-  // Fake a timer event
-  gInternalManager.notify(null);
+  // Run the background update
+  AddonManagerInternal.backgroundUpdateCheck().then(run_test_3);
 }
 
 // Install a newer hotfix
 function run_test_3() {
   restartManager();
   Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
                              gPort + "/data/test_hotfix_2.rdf");
 
@@ -102,18 +95,17 @@ function run_test_3() {
   }, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallEnded",
   ], callback_soon(check_test_3));
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function check_test_3() {
   restartManager();
 
   AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
     do_check_neq(aAddon, null);
     do_check_eq(aAddon.version, "2.0");
@@ -131,49 +123,33 @@ function run_test_4() {
                              gPort + "/data/test_hotfix_3.rdf");
 
   AddonManager.addInstallListener({
     onNewInstall: function() {
       do_throw("Should not have seen a new install created");
     }
   });
 
-  function observer() {
-    Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
-    do_execute_soon(run_test_5);
-  }
-
-  Services.obs.addObserver(observer, "addons-background-update-complete", false);
-
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck().then(run_test_5);
 }
 
 // Don't install an older hotfix
 function run_test_5() {
   restartManager();
 
   Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
                              gPort + "/data/test_hotfix_1.rdf");
 
   AddonManager.addInstallListener({
     onNewInstall: function() {
       do_throw("Should not have seen a new install created");
     }
   });
 
-  function observer() {
-    Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
-    do_execute_soon(run_test_6);
-  }
-
-  Services.obs.addObserver(observer, "addons-background-update-complete", false);
-
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck().then(run_test_6);
 }
 
 // Don't re-download an already pending install
 function run_test_6() {
   restartManager();
 
   Services.prefs.setCharPref("extensions.hotfix.lastVersion", "0");
   Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
@@ -186,41 +162,33 @@ function run_test_6() {
   }, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallEnded",
   ], callback_soon(check_test_6));
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function check_test_6() {
   AddonManager.addInstallListener({
     onNewInstall: function() {
       do_throw("Should not have seen a new install created");
     }
   });
 
-  function observer() {
-    Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
-    restartManager();
-
-    AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
+  AddonManagerInternal.backgroundUpdateCheck()
+    .then(promiseRestartManager)
+    .then(() => promiseAddonByID("hotfix@tests.mozilla.org"))
+    .then(aAddon => {
       aAddon.uninstall();
-      do_execute_soon(run_test_7);
+      run_test_7();
     });
-  }
-
-  Services.obs.addObserver(observer, "addons-background-update-complete", false);
-
-  // Fake a timer event
-  gInternalManager.notify(null);
 }
 
 // Start downloading again if something cancels the install
 function run_test_7() {
   restartManager();
 
   Services.prefs.setCharPref("extensions.hotfix.lastVersion", "0");
 
@@ -231,18 +199,17 @@ function run_test_7() {
   }, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallEnded",
   ], check_test_7);
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function check_test_7(aInstall) {
   prepare_test({
     "hotfix@tests.mozilla.org": [
       "onOperationCancelled"
     ]
   }, [
@@ -258,18 +225,17 @@ function check_test_7(aInstall) {
   }, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallEnded",
   ], callback_soon(finish_test_7));
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function finish_test_7() {
   restartManager();
 
   AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
     do_check_neq(aAddon, null);
     do_check_eq(aAddon.version, "1.0");
@@ -294,18 +260,17 @@ function run_test_8() {
   }, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallEnded",
   ], check_test_8);
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function check_test_8() {
   Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
                              gPort + "/data/test_hotfix_2.rdf");
 
   prepare_test({
     "hotfix@tests.mozilla.org": [
@@ -316,18 +281,17 @@ function check_test_8() {
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded",
     "onInstallStarted",
     "onInstallCancelled",
     "onInstallEnded",
   ], finish_test_8);
 
-  // Fake a timer event
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function finish_test_8() {
   AddonManager.getAllInstalls(callback_soon(function(aInstalls) {
     do_check_eq(aInstalls.length, 1);
     do_check_eq(aInstalls[0].version, "2.0");
 
     restartManager();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_shutdown.js
@@ -13,17 +13,17 @@ const IGNORE = ["escapeAddonURI", "shoul
 
 const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
                         "AddonScreenshot", "AddonType", "startup", "shutdown",
                         "registerProvider", "unregisterProvider",
                         "addStartupChange", "removeStartupChange",
                         "recordTimestamp", "recordSimpleMeasure",
                         "recordException", "getSimpleMeasures", "simpleTimer",
                         "setTelemetryDetails", "getTelemetryDetails",
-                        "callNoUpdateListeners"];
+                        "callNoUpdateListeners", "backgroundUpdateTimerHandler"];
 
 function test_functions() {
   for (let prop in AddonManager) {
     if (IGNORE.indexOf(prop) != -1)
       continue;
     if (typeof AddonManager[prop] != "function")
       continue;
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -379,19 +379,17 @@ function run_test_6() {
   restartManager();
 
   prepare_test({}, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded"
   ], continue_test_6);
 
-  // Fake a timer event to cause a background update and wait for the magic to
-  // happen
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function continue_test_6(install) {
   do_check_neq(install.existingAddon, null);
   do_check_eq(install.existingAddon.id, "addon1@tests.mozilla.org");
 
   prepare_test({
     "addon1@tests.mozilla.org": [
@@ -479,19 +477,17 @@ function run_test_7() {
       "1@personas.mozilla.org": [
         ["onInstalling", false],
         "onInstalled"
       ]
     }, [
       "onExternalInstall"
     ], check_test_7);
 
-    // Fake a timer event to cause a background update and wait for the magic to
-    // happen
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_7() {
   AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
     do_check_neq(p1, null);
     do_check_eq(p1.version, "2");
     do_check_eq(p1.name, "Updated Theme");
@@ -547,19 +543,17 @@ function run_test_7_cache() {
       "1@personas.mozilla.org": [
         ["onInstalling", false],
         "onInstalled"
       ]
     }, [
       "onExternalInstall"
     ], check_test_7_cache);
 
-    // Fake a timer event to cause a background update and wait for the magic to
-    // happen
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_7_cache() {
   AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
     let currentTheme = LightweightThemeManager.currentTheme;
     do_check_neq(p1, null);
     do_check_eq(p1.version, "3");
@@ -979,18 +973,17 @@ function run_test_14() {
         do_throw("Should not have seen onInstallFailed event");
       },
 
       onInstallCancelled: function(aInstall) {
         do_throw("Should not have seen onInstallCancelled event");
       },
     });
 
-    // Fake a timer event
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_14() {
   restartManager();
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon8@tests.mozilla.org"], function([a1, a8]) {
     do_check_neq(a1, null);
@@ -1078,18 +1071,17 @@ function run_test_15() {
         do_throw("Should not have seen onInstallFailed event");
       },
 
       onInstallCancelled: function(aInstall) {
         do_throw("Should not have seen onInstallCancelled event");
       },
     });
 
-    // Fake a timer event
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_15() {
   restartManager();
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon8@tests.mozilla.org"], function([a1, a8]) {
     do_check_neq(a1, null);
@@ -1177,18 +1169,18 @@ function run_test_17() {
     }
   });
 
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
                              "http://localhost:" + gPort + "/data/test_update.xml");
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
                              "http://localhost:" + gPort + "/data/test_update.xml");
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
-  // Fake a timer event
-  gInternalManager.notify(null);
+
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 // Tests that compatibility updates are applied to addons when the updated
 // compatibility data wouldn't match with strict compatibility enabled.
 function run_test_18() {
   restartManager();
   writeInstallRDFForExtension({
     id: "addon10@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_ignorecompat.js
@@ -53,18 +53,18 @@ function run_test_1() {
     onDownloadFailed: function(aInstall) {
       do_execute_soon(run_test_2);
     }
   });
 
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
                              "http://localhost:" + gPort + "/data/test_update.xml");
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
-  // Fake a timer event
-  gInternalManager.notify(null);
+
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 // Test that the update check correctly observes when an addon opts-in to
 // strict compatibility checking.
 function run_test_2() {
   writeInstallRDFForExtension({
     id: "addon11@tests.mozilla.org",
     version: "1.0",
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update_strictcompat.js
@@ -373,19 +373,17 @@ function run_test_6() {
   restartManager();
 
   prepare_test({}, [
     "onNewInstall",
     "onDownloadStarted",
     "onDownloadEnded"
   ], continue_test_6);
 
-  // Fake a timer event to cause a background update and wait for the magic to
-  // happen
-  gInternalManager.notify(null);
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 function continue_test_6(install) {
   do_check_neq(install.existingAddon, null);
   do_check_eq(install.existingAddon.id, "addon1@tests.mozilla.org");
 
   prepare_test({
     "addon1@tests.mozilla.org": [
@@ -469,19 +467,17 @@ function run_test_7() {
       "1@personas.mozilla.org": [
         ["onInstalling", false],
         "onInstalled"
       ]
     }, [
       "onExternalInstall"
     ], check_test_7);
 
-    // Fake a timer event to cause a background update and wait for the magic to
-    // happen
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_7() {
   AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
     do_check_neq(p1, null);
     do_check_eq(p1.version, "2");
     do_check_eq(p1.name, "Updated Theme");
@@ -895,18 +891,17 @@ function run_test_14() {
         do_throw("Should not have seen onInstallFailed event");
       },
 
       onInstallCancelled: function(aInstall) {
         do_throw("Should not have seen onInstallCancelled event");
       },
     });
 
-    // Fake a timer event
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_14() {
   restartManager();
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon8@tests.mozilla.org"], function([a1, a8]) {
     do_check_neq(a1, null);
@@ -994,18 +989,17 @@ function run_test_15() {
         do_throw("Should not have seen onInstallFailed event");
       },
 
       onInstallCancelled: function(aInstall) {
         do_throw("Should not have seen onInstallCancelled event");
       },
     });
 
-    // Fake a timer event
-    gInternalManager.notify(null);
+    AddonManagerInternal.backgroundUpdateCheck();
   });
 }
 
 function check_test_15() {
   restartManager();
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon8@tests.mozilla.org"], function([a1, a8]) {
     do_check_neq(a1, null);
@@ -1045,18 +1039,18 @@ function run_test_16() {
     onDownloadFailed: function(aInstall) {
       do_execute_soon(run_test_17);
     }
   });
 
   Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
                              "http://localhost:" + gPort + "/data/test_update.xml");
   Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
-  // Fake a timer event
-  gInternalManager.notify(null);
+
+  AddonManagerInternal.backgroundUpdateCheck();
 }
 
 // Test that the update check correctly observes when an addon opts-in to
 // strict compatibility checking.
 function run_test_17() {
 
   writeInstallRDFForExtension({
     id: "addon11@tests.mozilla.org",
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -221,17 +221,16 @@ skip-if = os == "android"
 skip-if = buildapp == "mulet"
 [test_pluginchange.js]
 [test_pluginBlocklistCtp.js]
 # Bug 676992: test consistently fails on Android
 fail-if = buildapp == "mulet" || 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
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -11,9 +11,10 @@ support-files =
 [include:xpcshell-shared.ini]
 
 [test_addon_path_service.js]
 [test_asyncBlocklistLoad.js]
 [test_DeferredSave.js]
 [test_metadata_update.js]
 [test_openh264.js]
 run-if = appname == "firefox"
+[test_shutdown.js]
 [test_XPIcancel.js]