Merge fx-team to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 05 May 2014 13:22:22 -0400
changeset 181558 2897fb5549906acbd5630f456c80ca8ec0c7ea11
parent 181548 e7735124a8920ef5634073fde8910c011213a177 (current diff)
parent 181557 43306fac27e187f8070df09c5b5c1d61deea7aa5 (diff)
child 181559 7dcb8e0d9668d694fcf8246cbba5f01bfb6a96ca
child 181561 0d4586d5273bb706a6bcf782b7e1ae7cb24f902a
child 181618 b26be6fe71beaff25853f71e8e9fd40f8155dcbb
child 181669 1a4940778298ecd47de8ff2795f2facb2742efce
push id26720
push userryanvm@gmail.com
push dateMon, 05 May 2014 17:22:12 +0000
treeherdermozilla-central@2897fb554990 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c.
--- a/browser/devtools/layoutview/view.css
+++ b/browser/devtools/layoutview/view.css
@@ -1,27 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-body, html {
-  height: 100%;
-  width: 100%;
-  overflow: hidden;
-}
-
 body {
-  margin: 0;
+  max-width: 320px;
+  position: relative;
+  margin: 0px auto;
   padding: 0;
 }
 
 #header {
   box-sizing: border-box;
   width: 100%;
-  padding: 6px 9px;
+  padding: 4px 13px;
   display: -moz-box;
   vertical-align: top;
 }
 
 #header:-moz-dir(rtl) {
   -moz-box-direction: reverse;
 }
 
@@ -33,32 +29,23 @@ body {
   -moz-box-flex: 1;
 }
 
 #element-size:-moz-dir(rtl) {
   -moz-box-pack: end;
 }
 
 #main {
-  margin: 0 10px 10px 10px;
+  margin: 0 14px 10px 14px;
   box-sizing: border-box;
-  width: calc(100% - 2 * 10px);
+  width: calc(100% - 2 * 14px);
   position: absolute;
   border-width: 1px;
 }
 
-@media (min-width: 320px) {
-  body {
-    position: absolute;
-    width: 320px;
-    left: -160px;
-    margin-left: 50%;
-  }
-}
-
 #content,
 #borders {
   border-width: 1px;
 }
 
 #content {
   height: 25px;
 }
@@ -189,8 +176,88 @@ body {
   pointer-events: none;
 }
 
 body.dim > #header > #element-position,
 body.dim > #main > p,
 body.dim > #main > .tooltip {
   visibility: hidden;
 }
+
+@media (max-height: 228px) {
+  #header {
+    padding-top: 0;
+    padding-bottom: 0;
+    margin-top: 10px;
+    margin-bottom: 8px;
+  }
+
+  #margins,
+  #padding {
+    border-width: 21px;
+  }
+  #borders {
+    padding: 21px;
+  }
+
+  #content {
+    height: 21px;
+  }
+
+  .padding.top {
+    top: 46px;
+  }
+
+  .padding.bottom {
+    bottom: 46px;
+  }
+
+  .border.top {
+    top: 25px;
+  }
+
+  .border.bottom {
+    bottom: 25px;
+  }
+
+  .margin.top {
+    top: 4px;
+  }
+
+  .margin.bottom {
+    bottom: 4px;
+  }
+
+  .size,
+  .margin.left,
+  .margin.right,
+  .border.left,
+  .border.right,
+  .padding.left,
+  .padding.right {
+    line-height: 106px;
+  }
+
+  .margin.right,
+  .margin.left,
+  .border.left,
+  .border.right,
+  .padding.right,
+  .padding.left {
+    width: 21px;
+  }
+
+  .padding.left {
+    left: 43px;
+  }
+
+  .padding.right {
+    right: 43px;
+  }
+
+  .border.left {
+    left: 22px;
+  }
+
+  .border.right {
+    right: 22px;
+  }
+}
--- a/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
+++ b/browser/devtools/styleinspector/test/browser_styleinspector_output-parser.js
@@ -90,25 +90,16 @@ function test() {
       name: "color",
       value: "#F06",
       test: fragment => {
         is(countColors(fragment), 1);
         is(fragment.textContent, "#F06");
       }
     },
     {
-      name: "border-top-left-color",
-      value: "rgba(14, 255, 20, .5)",
-      test: fragment => {
-        is(countAll(fragment), 1);
-        is(countColors(fragment), 1);
-        is(fragment.textContent, "rgba(14, 255, 20, .5)");
-      }
-    },
-    {
       name: "border",
       value: "80em dotted pink",
       test: fragment => {
         is(countAll(fragment), 1);
         is(countColors(fragment), 1);
         is(getColor(fragment), "pink");
       }
     },
--- a/toolkit/devtools/output-parser.js
+++ b/toolkit/devtools/output-parser.js
@@ -289,26 +289,22 @@ OutputParser.prototype = {
    *         CSS Property name to check
    * @param  {String} value
    *         CSS Property value to check
    */
   _cssPropertySupportsValue: function(name, value) {
     let win = Services.appShell.hiddenDOMWindow;
     let doc = win.document;
 
-    name = name.replace(/-\w{1}/g, function(match) {
-      return match.charAt(1).toUpperCase();
-    });
-
     value = value.replace("!important", "");
 
     let div = doc.createElement("div");
-    div.style[name] = value;
+    div.style.setProperty(name, value);
 
-    return !!div.style[name];
+    return !!div.style.getPropertyValue(name);
   },
 
   /**
    * Tests if a given colorObject output by CssColor is valid for parsing.
    * Valid means it's really a color, not any of the CssColor SPECIAL_VALUES
    * except transparent
    */
   _isValidColor: function(colorObj) {
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -62,16 +62,18 @@ var PREF_EM_CHECK_COMPATIBILITY;
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
 const VALID_TYPES_REGEXP = /^[\w\-]+$/;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+                                  "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
                                   "resource://gre/modules/addons/AddonRepository.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
@@ -457,32 +459,32 @@ var gStartupComplete = false;
 var gCheckCompatibility = true;
 var gStrictCompatibility = true;
 var gCheckUpdateSecurityDefault = true;
 var gCheckUpdateSecurity = gCheckUpdateSecurityDefault;
 var gUpdateEnabled = true;
 var gAutoUpdateDefault = true;
 var gHotfixID = null;
 
+var gUpdateCheckInProgress = false;
 /**
  * This is the real manager, kept here rather than in AddonManager to keep its
  * contents hidden from API users.
  */
 var AddonManagerInternal = {
   managerListeners: [],
   installListeners: [],
   addonListeners: [],
   typeListeners: [],
   providers: [],
   types: {},
   startupChanges: {},
   // Store telemetry details per addon provider
   telemetryDetails: {},
 
-
   // A read-only wrapper around the types dictionary
   typesProxy: Proxy.create({
     getOwnPropertyDescriptor: function typesProxy_getOwnPropertyDescriptor(aName) {
       if (!(aName in AddonManagerInternal.types))
         return undefined;
 
       return {
         value: AddonManagerInternal.types[aName].type,
@@ -1127,130 +1129,131 @@ var AddonManagerInternal = {
 
     // escape() does not properly encode + symbols in any embedded FVF strings.
     return uri.replace(/\+/g, "%2B");
   },
 
   /**
    * Performs a background update check by starting an update for all add-ons
    * that can be updated.
+   * @return Promise{null} resolves when the background update check is complete
+   *                       (including all addon installs)
    */
   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;
-
-    Services.obs.notifyObservers(null, "addons-background-update-start", null);
-
-    // Start this from one to ensure the whole of this function completes before
-    // we can send the complete notification. Some parts can in some cases
-    // complete synchronously before later parts have a chance to increment
-    // pendingUpdates.
-    let pendingUpdates = 1;
-
-    function notifyComplete() {
-      if (--pendingUpdates == 0) {
-        Services.obs.notifyObservers(null,
-                                     "addons-background-update-complete",
-                                     null);
-      }
+    if (gUpdateCheckInProgress) {
+      throw Components.Exception("Background update check already in progress",
+                                 Cr.NS_ERROR_UNEXPECTED);
     }
-
-    if (this.updateEnabled) {
-      let scope = {};
-      Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
-      scope.LightweightThemeManager.updateCurrentTheme();
-
-      pendingUpdates++;
-      this.getAllAddons(function getAddonsCallback(aAddons) {
+    gUpdateCheckInProgress = true;
+
+    return 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;
+
+      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();
+
+        let aAddons = yield new Promise((resolve, reject) => this.getAllAddons(resolve));
+
         // If there is a known hotfix then exclude it from the list of add-ons to update.
         var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)];
 
         // Repopulate repository cache first, to ensure compatibility overrides
         // are up to date before checking for addon updates.
-        AddonRepository.backgroundUpdateCheck(
-                     ids, function BUC_backgroundUpdateCheckCallback() {
-          pendingUpdates += aAddons.length;
-          aAddons.forEach(function BUC_forEachCallback(aAddon) {
-            if (aAddon.id == hotfixID) {
-              notifyComplete();
-              return;
-            }
-
-            // Check all add-ons for updates so that any compatibility updates will
-            // be applied
+        yield new Promise((resolve, reject) => AddonRepository.backgroundUpdateCheck(ids, resolve));
+
+        // Keep track of all the async add-on updates happening in parallel
+        let updates = [];
+
+        for (let aAddon of aAddons) {
+          if (aAddon.id == hotfixID) {
+            continue;
+          }
+
+          // Check all add-ons for updates so that any compatibility updates will
+          // be applied
+          updates.push(new Promise((resolve, reject) => {
             aAddon.findUpdates({
               onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
                 // Start installing updates when the add-on can be updated and
                 // background updates should be applied.
                 if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
                     AddonManager.shouldAutoUpdate(aAddon)) {
+                  // XXX we really should resolve when this install is done,
+                  // not when update-available check completes, no?
                   aInstall.install();
                 }
               },
 
-              onUpdateFinished: notifyComplete
+              onUpdateFinished: resolve
             }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
-          });
-
-          notifyComplete();
-        });
-      });
-    }
-
-    if (checkHotfix) {
-      var hotfixVersion = "";
-      try {
-        hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
+          }));
+        }
+        yield Promise.all(updates);
       }
-      catch (e) { }
-
-      let url = null;
-      if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
-        url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
-      else
-        url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
-
-      // Build the URI from a fake add-on data.
-      url = AddonManager.escapeAddonURI({
-        id: hotfixID,
-        version: hotfixVersion,
-        userDisabled: false,
-        appDisabled: false
-      }, url);
-
-      pendingUpdates++;
-      Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
-      AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
-        onUpdateCheckComplete: function BUC_onUpdateCheckComplete(aUpdates) {
-          let update = AddonUpdateChecker.getNewestCompatibleUpdate(aUpdates);
-          if (!update) {
-            notifyComplete();
-            return;
-          }
-
-          // If the available version isn't newer than the last installed
-          // version then ignore it.
-          if (Services.vc.compare(hotfixVersion, update.version) >= 0) {
-            notifyComplete();
-            return;
-          }
-
-          logger.debug("Downloading hotfix version " + update.version);
-          AddonManager.getInstallForURL(update.updateURL,
-                                       function BUC_getInstallForURL(aInstall) {
+
+      if (checkHotfix) {
+        var hotfixVersion = "";
+        try {
+          hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
+        }
+        catch (e) { }
+
+        let url = null;
+        if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
+          url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
+        else
+          url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
+
+        // Build the URI from a fake add-on data.
+        url = AddonManager.escapeAddonURI({
+          id: hotfixID,
+          version: hotfixVersion,
+          userDisabled: false,
+          appDisabled: false
+        }, url);
+
+        Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
+        let update = null;
+        try {
+          let foundUpdates = yield new Promise((resolve, reject) => {
+            AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
+              onUpdateCheckComplete: resolve,
+              onUpdateCheckError: reject
+            });
+          });
+          update = AddonUpdateChecker.getNewestCompatibleUpdate(foundUpdates);
+        } catch (e) {
+          // AUC.checkForUpdates already logged the error
+        }
+
+        // Check that we have a hotfix update, and it's newer than the one we already
+        // have installed (if any)
+        if (update) {
+          if (Services.vc.compare(hotfixVersion, update.version) < 0) {
+            logger.debug("Downloading hotfix version " + update.version);
+            let aInstall = yield new Promise((resolve, reject) =>
+              AddonManager.getInstallForURL(update.updateURL, resolve,
+                "application/x-xpinstall", update.updateHash, null,
+                null, update.version));
+
             aInstall.addListener({
               onDownloadEnded: function BUC_onDownloadEnded(aInstall) {
                 try {
                   if (!Services.prefs.getBoolPref(PREF_EM_CERT_CHECKATTRIBUTES))
                     return;
                 }
                 catch (e) {
                   // By default don't do certificate checks.
@@ -1278,27 +1281,25 @@ var AddonManagerInternal = {
                 // Revert to the previous version if the installation was
                 // cancelled.
                 Services.prefs.setCharPref(PREF_EM_HOTFIX_LASTVERSION,
                                            hotfixVersion);
               }
             });
 
             aInstall.install();
-
-            notifyComplete();
-          }, "application/x-xpinstall", update.updateHash, null,
-             null, update.version);
-        },
-
-        onUpdateCheckError: notifyComplete
-      });
-    }
-
-    notifyComplete();
+          }
+        }
+      }
+
+      gUpdateCheckInProgress = false;
+      Services.obs.notifyObservers(null,
+                                   "addons-background-update-complete",
+                                   null);
+    }.bind(this));
   },
 
   /**
    * 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
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -3605,17 +3605,23 @@ this.XPIProvider = {
 
   /**
    * Removes an AddonInstall from the list of active installs.
    *
    * @param  install
    *         The AddonInstall to remove
    */
   removeActiveInstall: function XPI_removeActiveInstall(aInstall) {
-    this.installs = this.installs.filter(function installFilter(i) i != aInstall);
+    let where = this.installs.indexOf(aInstall);
+    if (where == -1) {
+      logger.warn("removeActiveInstall: could not find active install for "
+          + aInstall.sourceURI.spec);
+      return;
+    }
+    this.installs.splice(where, 1);
   },
 
   /**
    * Called to get an Addon with a particular ID.
    *
    * @param  aId
    *         The ID of the add-on to retrieve
    * @param  aCallback
--- a/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug557956.js
@@ -70,17 +70,17 @@ function install_test_addons(aCallback) 
     installCount: 0,
 
     onInstallEnded: function() {
       this.installCount++;
       if (this.installCount == installs.length) {
         // Switch to the test update URL
         Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf");
 
-        aCallback();
+        executeSoon(aCallback);
       }
     }
   };
 
   for (let install of installs) {
     install.addListener(listener);
     install.install();
   }
--- a/toolkit/mozapps/extensions/test/browser/browser_bug562992.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562992.js
@@ -36,17 +36,17 @@ function end_test() {
 // Create a MockInstall with a MockAddon payload and add it to the provider,
 // causing the onNewInstall event to fire, which in turn will cause a new
 // "installing" item to appear in the list of extensions.
 add_test(function () {
   let addon = new MockAddon(undefined, EXTENSION_NAME, "extension", true);
   gInstall = new MockInstall(undefined, undefined, addon);
   gInstall.addTestListener({
     onNewInstall: function () {
-      run_next_test();
+      executeSoon(run_next_test);
     }
   });
   gProvider.addInstall(gInstall);
 });
 
 // Finish the install, which will cause the "installing" item to be converted
 // to an "installed" item, which should have the correct add-on name.
 add_test(function () {
@@ -60,13 +60,13 @@ add_test(function () {
         let item = list.getItemAtIndex(i);
         if (item.getAttribute("name") === EXTENSION_NAME) {
           ok(true, "Item with correct name found");
           run_next_test();
           return;
         }
       }
       ok(false, "Item with correct name was not found");
-      run_next_test();
+      executeSoon(run_next_test);
     }
   });
   gInstall.install();
 });
--- a/toolkit/mozapps/extensions/test/browser/browser_bug572561.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug572561.js
@@ -16,27 +16,28 @@ var gInstall;
 var gExpectedCancel = false;
 var gTestInstallListener = {
   onInstallStarted: function(aInstall) {
     check_hidden(false);
   },
 
   onInstallEnded: function(aInstall) {
     check_hidden(false);
-    run_next_test();
+    executeSoon(run_next_test);
   },
 
   onInstallCancelled: function(aInstall) {
     ok(gExpectedCancel, "Should expect install cancel");
     check_hidden(false);
-    run_next_test();
+    executeSoon(run_next_test);
   },
 
   onInstallFailed: function(aInstall) {
     ok(false, "Did not expect onInstallFailed");
+    executeSoon(run_next_test);
   }
 };
 
 function test() {
   waitForExplicitFinish();
 
   gProvider = new MockProvider();
 
--- a/toolkit/mozapps/extensions/test/browser/browser_bug577990.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug577990.js
@@ -35,17 +35,17 @@ function end_test() {
   close_manager(gManagerWindow, finish);
 }
 
 function install_locale(aCallback) {
   gInstall = gProvider.createInstalls(gInstallProperties)[0];
   gInstall.addTestListener({
     onInstallEnded: function(aInstall) {
       gInstall.removeTestListener(this);
-      aCallback();
+      executeSoon(aCallback);
     }
   });
   gInstall.install();
 }
 
 function check_hidden(aExpectedHidden) {
   var hidden = !gCategoryUtilities.isTypeVisible("locale");
   is(hidden, !!aExpectedHidden, "Should have correct hidden state");
--- a/toolkit/mozapps/extensions/test/browser/browser_bug591663.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug591663.js
@@ -92,17 +92,17 @@ add_test(function() {
       // Install type unknown until download complete
       check_list(null);
     },
     onInstallStarted: function() {
       check_list(gItem);
     },
     onInstallEnded: function() {
       check_list(gItem);
-      run_next_test();
+      executeSoon(run_next_test);
     }
   });
 
   gItem.install();
 });
 
 // Test that restarting the manager does not change list
 add_test(function() {
@@ -131,17 +131,17 @@ add_test(function() {
       check_list(null);
     },
     onInstallStarted: function() {
       check_list(null);
     },
     onInstallEnded: function() {
       check_list(null);
       extension.cancel();
-      run_next_test();
+      executeSoon(run_next_test);
     }
   });
 
   extension.install();
 });
 
 // Test that onExternalInstall properly hides empty notice and adds install to list
 add_test(function() {
--- a/toolkit/mozapps/extensions/test/browser/browser_hotfix.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_hotfix.js
@@ -10,61 +10,66 @@ const PREF_EM_CERT_CHECKATTRIBUTES     =
 
 const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
 const PREF_UPDATE_REQUIREBUILTINCERTS  = "extensions.update.requireBuiltInCerts";
 
 const PREF_APP_UPDATE_ENABLED          = "app.update.enabled";
 
 const HOTFIX_ID = "hotfix@tests.mozilla.org";
 
-var gNextTest;
-
-var SuccessfulInstallListener = {
-  onDownloadCancelled: function(aInstall) {
-    ok(false, "Should not have seen the download cancelled");
-    is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
-
-    AddonManager.removeInstallListener(this);
-    gNextTest();
-  },
-
-  onInstallEnded: function(aInstall) {
-    ok(true, "Should have seen the install complete");
-    is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
-
-    AddonManager.removeInstallListener(this);
-    aInstall.addon.uninstall();
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
-    gNextTest();
-  }
+/*
+ * Register an addon install listener and return a promise that:
+ *  resolves with the AddonInstall object if the install succeeds
+ *  rejects with the AddonInstall if the install fails
+ */
+function promiseInstallListener() {
+  return new Promise((resolve, reject) => {
+    let listener = {
+      onInstallEnded: ai => {
+        AddonManager.removeInstallListener(listener);
+        resolve(ai);
+      },
+      onDownloadCancelled: ai => {
+        AddonManager.removeInstallListener(listener);
+        reject(ai);
+      }
+    };
+    AddonManager.addInstallListener(listener);
+  });
 }
 
-var FailedInstallListener = {
-  onDownloadCancelled: function(aInstall) {
-    ok(true, "Should have seen the download cancelled");
-    is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
-
-    AddonManager.removeInstallListener(this);
-    gNextTest();
-  },
-
-  onInstallEnded: function(aInstall) {
-    ok(false, "Should not have seen the install complete");
-    is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
-
-    AddonManager.removeInstallListener(this);
-    aInstall.addon.uninstall();
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
-    gNextTest();
-  }
+function promiseSuccessfulInstall() {
+  return promiseInstallListener().then(
+    aInstall => {
+      ok(true, "Should have seen the install complete");
+      is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
+      aInstall.addon.uninstall();
+      Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
+    },
+    aInstall => {
+      ok(false, "Should not have seen the download cancelled");
+      is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
+    });
 }
 
-function test() {
-  waitForExplicitFinish();
+function promiseFailedInstall() {
+  return promiseInstallListener().then(
+    aInstall => {
+      ok(false, "Should not have seen the install complete");
+      is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
+      aInstall.addon.uninstall();
+      Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
+    },
+    aInstall => {
+      ok(true, "Should have seen the download cancelled");
+      is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
+    });
+}
 
+add_task(function setup() {
   Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
   Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
   Services.prefs.setBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, false);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_ID, HOTFIX_ID);
   var oldURL = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, TESTROOT + "signed_hotfix.rdf");
 
   registerCleanupFunction(function() {
@@ -73,114 +78,90 @@ function test() {
     Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, oldURL);
     Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
     Services.prefs.clearUserPref(PREF_UPDATE_REQUIREBUILTINCERTS);
 
     Services.prefs.clearUserPref(PREF_EM_CERT_CHECKATTRIBUTES);
     var prefs = Services.prefs.getChildList(PREF_EM_HOTFIX_CERTS);
     prefs.forEach(Services.prefs.clearUserPref);
   });
-
-  run_next_test();
-}
-
-function end_test() {
-  finish();
-}
-
-add_test(function check_no_cert_checks() {
-  Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
-  AddonManager.addInstallListener(SuccessfulInstallListener);
-
-  gNextTest = run_next_test;
-
-  AddonManagerPrivate.backgroundUpdateCheck();
 });
 
-add_test(function check_wrong_cert_fingerprint() {
+add_task(function* check_no_cert_checks() {
+  Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
+  yield Promise.all([
+    promiseSuccessfulInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
+});
+
+add_task(function* check_wrong_cert_fingerprint() {
   Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo");
 
-  AddonManager.addInstallListener(FailedInstallListener);
-
-  gNextTest = function() {
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
-
-    run_next_test();
-  };
-
-  AddonManagerPrivate.backgroundUpdateCheck();
+  yield Promise.all([
+    promiseFailedInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
 });
 
-add_test(function check_right_cert_fingerprint() {
+add_task(function* check_right_cert_fingerprint() {
   Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
 
-  AddonManager.addInstallListener(SuccessfulInstallListener);
-
-  gNextTest = function() {
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
+  yield Promise.all([
+    promiseSuccessfulInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
 
-    run_next_test();
-  };
-
-  AddonManagerPrivate.backgroundUpdateCheck();
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
 });
 
-add_test(function check_multi_cert_fingerprint_1() {
+add_task(function* check_multi_cert_fingerprint_1() {
   Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "foo");
 
-  AddonManager.addInstallListener(SuccessfulInstallListener);
+  yield Promise.all([
+    promiseSuccessfulInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
 
-  gNextTest = function() {
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
-
-    run_next_test();
-  };
-
-  AddonManagerPrivate.backgroundUpdateCheck();
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
 });
 
-add_test(function check_multi_cert_fingerprint_2() {
+add_task(function* check_multi_cert_fingerprint_2() {
   Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo");
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
 
-  AddonManager.addInstallListener(SuccessfulInstallListener);
+  yield Promise.all([
+    promiseSuccessfulInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
 
-  gNextTest = function() {
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
-
-    run_next_test();
-  };
-
-  AddonManagerPrivate.backgroundUpdateCheck();
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
 });
 
-add_test(function check_no_cert_no_checks() {
+add_task(function* check_no_cert_no_checks() {
   Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, TESTROOT + "unsigned_hotfix.rdf");
 
-  AddonManager.addInstallListener(SuccessfulInstallListener);
-
-  gNextTest = run_next_test;
-
-  AddonManagerPrivate.backgroundUpdateCheck();
+  yield Promise.all([
+    promiseSuccessfulInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
 });
 
-add_test(function check_no_cert_cert_fingerprint_check() {
+add_task(function* check_no_cert_cert_fingerprint_check() {
   Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
   Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
 
-  AddonManager.addInstallListener(FailedInstallListener);
-
-  gNextTest = function() {
-    Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
+  yield Promise.all([
+    promiseFailedInstall(),
+    AddonManagerPrivate.backgroundUpdateCheck()
+  ]);
 
-    run_next_test();
-  };
-
-  AddonManagerPrivate.backgroundUpdateCheck();
+  Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
 });
--- a/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_manualupdates.js
@@ -179,17 +179,17 @@ add_test(function() {
       is_element_visible(item._installStatus, "Install progress widget should be visible");
     },
     onInstallEnded: function() {
       install.removeTestListener(this);
       info("Install ended");
       is_element_hidden(item._installStatus, "Install progress widget should be hidden");
 
       if (badgeUpdated)
-        run_next_test();
+        executeSoon(run_next_test);
       else
         installCompleted = true;
     }
   };
   install.addTestListener(listener);
   EventUtils.synthesizeMouseAtCenter(updateBtn, { }, gManagerWindow);
 });
 
--- a/toolkit/mozapps/extensions/test/browser/browser_searching.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_searching.js
@@ -561,17 +561,17 @@ add_test(function() {
       // Don't immediately consider the installed add-on as local because
       // if the user was filtering out local add-ons, the installed add-on
       // would vanish. Only consider add-on as local on new searches.
 
       aInstall.removeListener(this);
 
       is(installBtn.hidden, true, "Install button should be hidden after install ended");
       check_filtered_results(QUERY, "relevancescore", false);
-      run_next_test();
+      executeSoon(run_next_test);
     }
   }
 
   search(QUERY, false, function() {
     var list = gManagerWindow.document.getElementById("search-list");
     var remoteItem = get_addon_item(REMOTE_TO_INSTALL);
     list.ensureElementIsVisible(remoteItem);
 
--- a/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_select_compatoverrides.js
@@ -25,17 +25,17 @@ function waitForView(aView, aCallback) {
 }
 
 function install_test_addon(aCallback) {
   AddonManager.getInstallForURL(TESTROOT + "addons/browser_select_compatoverrides_1.xpi", function(aInstall) {
     var listener = {
       onInstallEnded: function() {
         AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
           gTestAddon = addon;
-          aCallback();
+          executeSoon(aCallback);
         });
       }
     };
     aInstall.addListener(listener);
     aInstall.install();
   }, "application/x-xpinstall");
 }