Bug 1314176 - convert AddonManager from Task.{async,spawn} to async/await r?kmag draft
authorRobert Helmer <rhelmer@mozilla.com>
Mon, 31 Oct 2016 21:03:56 -0700
changeset 432346 742d8e0bbd59a5f724fcfeccb9f3c0aa0b9db707
parent 432301 4cde141bbedfd7355bdf3e9ac41740fb8fa2f06d
child 535615 241f351ecfa92c8143783ec876de242999d13080
push id34273
push userrhelmer@mozilla.com
push dateTue, 01 Nov 2016 18:38:55 +0000
reviewerskmag
bugs1314176
milestone52.0a1
Bug 1314176 - convert AddonManager from Task.{async,spawn} to async/await r?kmag MozReview-Commit-ID: 38Vhk8JIhp5
toolkit/mozapps/extensions/.eslintrc.js
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/amWebAPI.js
toolkit/mozapps/extensions/content/update.js
toolkit/mozapps/extensions/internal/AddonRepository.jsm
toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
toolkit/mozapps/extensions/internal/GMPProvider.jsm
toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/internal/XPIProviderUtils.js
toolkit/mozapps/extensions/nsBlocklistService.js
--- a/toolkit/mozapps/extensions/.eslintrc.js
+++ b/toolkit/mozapps/extensions/.eslintrc.js
@@ -1,8 +1,11 @@
 "use strict";
 
 module.exports = { // eslint-disable-line no-undef
+  "parserOptions": {
+    "ecmaVersion": 8,
+  },
   "rules": {
     // No using undeclared variables
     "no-undef": 2,
   }
 };
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -74,18 +74,16 @@ const WEBAPI_TEST_INSTALL_HOSTS = [
   "testpilot.stage.mozaws.net", "testpilot.dev.mozaws.net",
   "example.com",
 ];
 
 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, "Extension",
                                   "resource://gre/modules/Extension.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
@@ -1177,17 +1175,17 @@ var AddonManagerInternal = {
   },
 
   /**
    * Shuts down the addon manager and all registered providers, this must clean
    * up everything in order for automated tests to fake restarts.
    * @return Promise{null} that resolves when all providers and dependent modules
    *                       have finished shutting down
    */
-  shutdownManager: Task.async(function*() {
+  shutdownManager: async function() {
     logger.debug("shutdown");
     this.callManagerListeners("onShutdown");
 
     gRepoShutdownState = "pending";
     gShutdownInProgress = true;
     // Clean up listeners
     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
@@ -1197,29 +1195,29 @@ var AddonManagerInternal = {
     Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
     gPluginPageListener.destroy();
     gPluginPageListener = null;
 
     let savedError = null;
     // Only shut down providers if they've been started.
     if (gStarted) {
       try {
-        yield gShutdownBarrier.wait();
+        await gShutdownBarrier.wait();
       }
       catch (err) {
         savedError = err;
         logger.error("Failure during wait for shutdown barrier", err);
         AddonManagerPrivate.recordException("AMI", "Async shutdown of AddonManager providers", err);
       }
     }
 
     // Shut down AddonRepository after providers (if any).
     try {
       gRepoShutdownState = "in progress";
-      yield AddonRepository.shutdown();
+      await AddonRepository.shutdown();
       gRepoShutdownState = "done";
     }
     catch (err) {
       savedError = err;
       logger.error("Failure during AddonRepository shutdown", err);
       AddonManagerPrivate.recordException("AMI", "Async shutdown of AddonRepository", err);
     }
 
@@ -1233,17 +1231,17 @@ var AddonManagerInternal = {
       delete this.startupChanges[type];
     gStarted = false;
     gStartupComplete = false;
     gShutdownBarrier = null;
     gShutdownInProgress = false;
     if (savedError) {
       throw savedError;
     }
-  }),
+  },
 
   requestPlugins: function({ target: port }) {
     // Lists all the properties that plugins.html needs
     const NEEDED_PROPS = ["name", "pluginLibraries", "pluginFullpath", "version",
                           "isActive", "blocklistState", "description",
                           "pluginMimeTypes"];
     function filterProperties(plugin) {
       let filtered = {};
@@ -1430,37 +1428,37 @@ var AddonManagerInternal = {
    * @return Promise{null} Resolves when the background update check is complete
    *                       (the resulting addon installations may still be in progress).
    */
   backgroundUpdateCheck: function() {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
-    let buPromise = Task.spawn(function*() {
+    let buPromise = new Promise(resolve => async function() {
       let hotfixID = this.hotfixID;
 
       let appUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
                              Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
       let checkHotfix = hotfixID && appUpdateEnabled;
 
       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();
 
-        let allAddons = yield new Promise((resolve, reject) => this.getAllAddons(resolve));
+        let allAddons = await new Promise((resolve, reject) => this.getAllAddons(resolve));
 
         // Repopulate repository cache first, to ensure compatibility overrides
         // are up to date before checking for addon updates.
-        yield AddonRepository.backgroundUpdateCheck();
+        await AddonRepository.backgroundUpdateCheck();
 
         // Keep track of all the async add-on updates happening in parallel
         let updates = [];
 
         for (let addon of allAddons) {
           if (addon.id == hotfixID) {
             continue;
           }
@@ -1481,17 +1479,17 @@ var AddonManagerInternal = {
                   aInstall.install();
                 }
               },
 
               onUpdateFinished: aAddon => { logger.debug("onUpdateFinished for ${id}", aAddon); resolve(); }
             }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
           }));
         }
-        yield Promise.all(updates);
+        await Promise.all(updates);
       }
 
       if (checkHotfix) {
         var hotfixVersion = "";
         try {
           hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
         }
         catch (e) { }
@@ -1508,33 +1506,33 @@ var AddonManagerInternal = {
           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) => {
+          let foundUpdates = await 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) =>
+            let aInstall = await new Promise((resolve, reject) =>
               AddonManager.getInstallForURL(update.updateURL, resolve,
                 "application/x-xpinstall", update.updateHash, null,
                 null, update.version));
 
             aInstall.addListener({
               onDownloadEnded: function(aInstall) {
                 if (aInstall.addon.id != hotfixID) {
                   logger.warn("The downloaded hotfix add-on did not have the " +
@@ -1584,17 +1582,17 @@ var AddonManagerInternal = {
 
             aInstall.install();
           }
         }
       }
 
       if (appUpdateEnabled) {
         try {
-          yield AddonManagerInternal._getProviderByName("XPIProvider").updateSystemAddons();
+          await AddonManagerInternal._getProviderByName("XPIProvider").updateSystemAddons();
         }
         catch (e) {
           logger.warn("Failed to update system addons", e);
         }
       }
 
       logger.debug("Background update check complete");
       Services.obs.notifyObservers(null,
--- a/toolkit/mozapps/extensions/amWebAPI.js
+++ b/toolkit/mozapps/extensions/amWebAPI.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
 
 const MSG_PROMISE_REQUEST  = "WebAPIPromiseRequest";
 const MSG_PROMISE_RESULT   = "WebAPIPromiseResult";
 const MSG_INSTALL_EVENT    = "WebAPIInstallEvent";
 const MSG_INSTALL_CLEANUP  = "WebAPICleanup";
 const MSG_ADDON_EVENT_REQ  = "WebAPIAddonEventRequest";
 const MSG_ADDON_EVENT      = "WebAPIAddonEvent";
 
@@ -124,18 +123,18 @@ class APIObject {
    *                                 argument.  Used to convert the result
    *                                 into something appropriate for content.
    * @returns {Promise<any>} A Promise suitable for passing directly to content.
    */
   _apiTask(apiRequest, apiArgs, resultConverter) {
     let win = this.window;
     let broker = this.broker;
     return new win.Promise((resolve, reject) => {
-      Task.spawn(function*() {
-        let result = yield broker.sendRequest(apiRequest, ...apiArgs);
+      new Promise(async function() {
+        let result = await broker.sendRequest(apiRequest, ...apiArgs);
         if ("reject" in result) {
           let err = new win.Error(result.reject.message);
           // We don't currently put any other properties onto Errors
           // generated by mozAddonManager.  If/when we do, they will
           // need to get copied here.
           reject(err);
           return;
         }
--- a/toolkit/mozapps/extensions/content/update.js
+++ b/toolkit/mozapps/extensions/content/update.js
@@ -14,17 +14,16 @@ const PREF_XPINSTALL_ENABLED            
 // timeout (in milliseconds) to wait for response to the metadata ping
 const METADATA_TIMEOUT    = 30000;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", "resource://gre/modules/addons/AddonRepository.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
 var logger = null;
 
 var gUpdateWizard = {
   // When synchronizing app compatibility info this contains all installed
   // add-ons. When checking for compatible versions this contains only
   // incompatible add-ons.
@@ -174,59 +173,59 @@ var listener = {
     gUpdateWizard.metadataEnabled++;
   }
 };
 
 var gVersionInfoPage = {
   _completeCount: 0,
   _totalCount: 0,
   _versionInfoDone: false,
-  onPageShow: Task.async(function*() {
+  onPageShow: async function() {
     gUpdateWizard.setButtonLabels(null, true,
                                   "nextButtonText", true,
                                   "cancelButtonText", false);
 
     gUpdateWizard.disabled = gUpdateWizard.affectedAddonIDs.size;
 
     // Ensure compatibility overrides are up to date before checking for
     // individual addon updates.
     AddonManager.addAddonListener(listener);
     if (AddonRepository.isMetadataStale()) {
       // Do the metadata ping, listening for any newly enabled/disabled add-ons.
-      yield AddonRepository.repopulateCache(METADATA_TIMEOUT);
+      await AddonRepository.repopulateCache(METADATA_TIMEOUT);
       if (gUpdateWizard.shuttingDown) {
         logger.debug("repopulateCache completed after dialog closed");
       }
     }
     // Fetch the add-ons that are still affected by this update,
     // excluding the hotfix add-on.
     let idlist = Array.from(gUpdateWizard.affectedAddonIDs).filter(
       a => a.id != AddonManager.hotfixID);
     if (idlist.length < 1) {
       gVersionInfoPage.onAllUpdatesFinished();
       return;
     }
 
     logger.debug("Fetching affected addons " + idlist.toSource());
-    let fetchedAddons = yield new Promise((resolve, reject) =>
+    let fetchedAddons = await new Promise((resolve, reject) =>
       AddonManager.getAddonsByIDs(idlist, resolve));
     // We shouldn't get nulls here, but let's be paranoid...
     gUpdateWizard.addons = fetchedAddons.filter(a => a);
     if (gUpdateWizard.addons.length < 1) {
       gVersionInfoPage.onAllUpdatesFinished();
       return;
     }
 
     gVersionInfoPage._totalCount = gUpdateWizard.addons.length;
 
     for (let addon of gUpdateWizard.addons) {
       logger.debug("VersionInfo Finding updates for ${id}", addon);
       addon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
     }
-  }),
+  },
 
   onAllUpdatesFinished: function() {
     AddonManager.removeAddonListener(listener);
     AddonManagerPrivate.recordSimpleMeasure("appUpdate_disabled",
         gUpdateWizard.disabled);
     AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_enabled",
         gUpdateWizard.metadataEnabled);
     AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_disabled",
--- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -19,18 +19,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
                                   "resource://gre/modules/DeferredSave.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository_SQLiteMigrator",
                                   "resource://gre/modules/addons/AddonRepository_SQLiteMigrator.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
-                                  "resource://gre/modules/Task.jsm");
 
 
 this.EXPORTED_SYMBOLS = [ "AddonRepository" ];
 
 const PREF_GETADDONS_CACHE_ENABLED       = "extensions.getAddons.cache.enabled";
 const PREF_GETADDONS_CACHE_TYPES         = "extensions.getAddons.cache.types";
 const PREF_GETADDONS_CACHE_ID_ENABLED    = "extensions.%ID%.getAddons.cache.enabled"
 const PREF_GETADDONS_BROWSEADDONS        = "extensions.getAddons.browseAddons";
@@ -561,17 +559,17 @@ this.AddonRepository = {
    * add-on is not found) is passed to the specified callback. If caching is
    * disabled, null is passed to the specified callback.
    *
    * @param  aId
    *         The id of the add-on to get
    * @param  aCallback
    *         The callback to pass the result back to
    */
-  getCachedAddonByID: Task.async(function*(aId, aCallback) {
+  getCachedAddonByID: async function(aId, aCallback) {
     if (!aId || !this.cacheEnabled) {
       aCallback(null);
       return;
     }
 
     function getAddon(aAddons) {
       aCallback(aAddons.get(aId) || null);
     }
@@ -581,17 +579,17 @@ this.AddonRepository = {
         this._addons = aAddons;
         getAddon(aAddons);
       });
 
       return;
     }
 
     getAddon(this._addons);
-  }),
+  },
 
   /**
    * Asynchronously repopulate cache so it only contains the add-ons
    * corresponding to the specified ids. If caching is disabled,
    * the cache is completely removed.
    *
    * @param  aTimeout
    *         (Optional) timeout in milliseconds to abandon the XHR request
@@ -610,62 +608,62 @@ this.AddonRepository = {
   _clearCache: function() {
     this._addons = null;
     return AddonDatabase.delete().then(() =>
       new Promise((resolve, reject) =>
         AddonManagerPrivate.updateAddonRepositoryData(resolve))
     );
   },
 
-  _repopulateCacheInternal: Task.async(function*(aSendPerformance, aTimeout) {
-    let allAddons = yield new Promise((resolve, reject) =>
+  _repopulateCacheInternal: async function(aSendPerformance, aTimeout) {
+    let allAddons = await new Promise((resolve, reject) =>
       AddonManager.getAllAddons(resolve));
 
     // Filter the hotfix out of our list of add-ons
     allAddons = allAddons.filter(a => a.id != AddonManager.hotfixID);
 
     // Completely remove cache if caching is not enabled
     if (!this.cacheEnabled) {
       logger.debug("Clearing cache because it is disabled");
-      yield this._clearCache();
+      await this._clearCache();
       return;
     }
 
     let ids = allAddons.map(a => a.id);
     logger.debug("Repopulate add-on cache with " + ids.toSource());
 
-    let addonsToCache = yield new Promise((resolve, reject) =>
+    let addonsToCache = await new Promise((resolve, reject) =>
       getAddonsToCache(ids, resolve));
 
     // Completely remove cache if there are no add-ons to cache
     if (addonsToCache.length == 0) {
       logger.debug("Clearing cache because 0 add-ons were requested");
-      yield this._clearCache();
+      await this._clearCache();
       return;
     }
 
-    yield new Promise((resolve, reject) =>
+    await new Promise((resolve, reject) =>
       this._beginGetAddons(addonsToCache, {
         searchSucceeded: aAddons => {
           this._addons = new Map();
           for (let addon of aAddons) {
             this._addons.set(addon.id, addon);
           }
           AddonDatabase.repopulate(aAddons, resolve);
         },
         searchFailed: () => {
           logger.warn("Search failed when repopulating cache");
           resolve();
         }
       }, aSendPerformance, aTimeout));
 
     // Always call AddonManager updateAddonRepositoryData after we refill the cache
-    yield new Promise((resolve, reject) =>
+    await new Promise((resolve, reject) =>
       AddonManagerPrivate.updateAddonRepositoryData(resolve));
-  }),
+  },
 
   /**
    * Asynchronously add add-ons to the cache corresponding to the specified
    * ids. If caching is disabled, the cache is unchanged and the callback is
    * immediately called if it is defined.
    *
    * @param  aIds
    *         The array of add-on ids to add to the cache
@@ -1567,23 +1565,23 @@ var AddonDatabase = {
 
   /**
    * Asynchronously opens a new connection to the database file.
    *
    * @return {Promise} a promise that resolves to the database.
    */
   openConnection: function() {
     if (!this.connectionPromise) {
-     this.connectionPromise = Task.spawn(function*() {
+     this.connectionPromise = new Promise(async function() {
        this.DB = BLANK_DB();
 
        let inputDB, schema;
 
        try {
-         let data = yield OS.File.read(this.jsonFile, { encoding: "utf-8"})
+         let data = await OS.File.read(this.jsonFile, { encoding: "utf-8"})
          inputDB = JSON.parse(data);
 
          if (!inputDB.hasOwnProperty("addons") ||
              !Array.isArray(inputDB.addons)) {
            throw new Error("No addons array.");
          }
 
          if (!inputDB.hasOwnProperty("schema")) {
@@ -1607,22 +1605,22 @@ var AddonDatabase = {
          this._saveDBToDisk();
 
          let dbSchema = 0;
          try {
            dbSchema = Services.prefs.getIntPref(PREF_GETADDONS_DB_SCHEMA);
          } catch (e) {}
 
          if (dbSchema < DB_MIN_JSON_SCHEMA) {
-           let results = yield new Promise((resolve, reject) => {
+           let results = await new Promise((resolve, reject) => {
              AddonRepository_SQLiteMigrator.migrate(resolve);
            });
 
            if (results.length) {
-             yield this._insertAddons(results);
+             await this._insertAddons(results);
            }
 
          }
 
          Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
          return this.DB;
        }
 
@@ -1767,29 +1765,29 @@ var AddonDatabase = {
   /**
    * Asynchronously inserts an array of add-ons into the database
    *
    * @param  aAddons
    *         The array of add-ons to insert
    * @param  aCallback
    *         An optional callback to call once complete
    */
-  insertAddons: Task.async(function*(aAddons, aCallback) {
-    yield this.openConnection();
-    yield this._insertAddons(aAddons, aCallback);
-  }),
+  insertAddons: async function(aAddons, aCallback) {
+    await this.openConnection();
+    await this._insertAddons(aAddons, aCallback);
+  },
 
-  _insertAddons: Task.async(function*(aAddons, aCallback) {
+  _insertAddons: async function(aAddons, aCallback) {
     for (let addon of aAddons) {
       this._insertAddon(addon);
     }
 
-    yield this._saveDBToDisk();
+    await this._saveDBToDisk();
     aCallback && aCallback();
-  }),
+  },
 
   /**
    * Inserts an individual add-on into the database. If the add-on already
    * exists in the database (by id), then the specified add-on will not be
    * inserted.
    *
    * @param  aAddon
    *         The add-on to insert into the database
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -16,17 +16,16 @@ const CERTDB_CID = Components.ID("{fb0bb
 
 
 Cu.importGlobalProperties(["fetch", "TextEncoder"]);
 
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
 const {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this, "Extension",
                                   "resource://gre/modules/Extension.jsm");
 
@@ -383,77 +382,77 @@ var AddonTestUtils = {
       }
 
       throw new Error("No manifest file present");
     } finally {
       zip.close();
     }
   },
 
-  getIDFromManifest: Task.async(function*(manifestURI) {
-    let body = yield fetch(manifestURI.spec);
+  getIDFromManifest: async function(manifestURI) {
+    let body = await fetch(manifestURI.spec);
 
     if (manifestURI.spec.endsWith(".rdf")) {
-      let data = yield body.text();
+      let data = await body.text();
 
       let ds = new RDFDataSource();
       new RDFXMLParser(ds, manifestURI, data);
 
       let rdfID = ds.GetTarget(rdfService.GetResource("urn:mozilla:install-manifest"),
                                rdfService.GetResource("http://www.mozilla.org/2004/em-rdf#id"),
                                true);
       return rdfID.QueryInterface(Ci.nsIRDFLiteral).Value;
     }
 
-    let manifest = yield body.json();
+    let manifest = await body.json();
     try {
       return manifest.applications.gecko.id;
     } catch (e) {
       // IDs for WebExtensions are extracted from the certificate when
       // not present in the manifest, so just generate a random one.
       return uuidGen.generateUUID().number;
     }
-  }),
+  },
 
   overrideCertDB() {
     // Unregister the real database. This only works because the add-ons manager
     // hasn't started up and grabbed the certificate database yet.
     let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
     let factory = registrar.getClassObject(CERTDB_CID, Ci.nsIFactory);
     registrar.unregisterFactory(CERTDB_CID, factory);
 
     // Get the real DB
     let realCertDB = factory.createInstance(null, Ci.nsIX509CertDB);
 
 
-    let verifyCert = Task.async(function*(file, result, cert, callback) {
+    let verifyCert = async function(file, result, cert, callback) {
       if (result == Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED &&
           !this.useRealCertChecks && callback.wrappedJSObject) {
         // Bypassing XPConnect allows us to create a fake x509 certificate from JS
         callback = callback.wrappedJSObject;
 
         try {
           let manifestURI = this.getManifestURI(file);
 
-          let id = yield this.getIDFromManifest(manifestURI);
+          let id = await this.getIDFromManifest(manifestURI);
 
           let fakeCert = {commonName: id};
 
           return [callback, Cr.NS_OK, fakeCert];
         } catch (e) {
           // If there is any error then just pass along the original results
         } finally {
           // Make sure to close the open zip file or it will be locked.
           if (file.isFile())
             Services.obs.notifyObservers(file, "flush-cache-entry", "cert-override");
         }
       }
 
       return [callback, result, cert];
-    }).bind(this);
+    };
 
 
     function FakeCertDB() {
       for (let property of Object.keys(realCertDB)) {
         if (property in this)
           continue;
 
         if (typeof realCertDB[property] == "function")
@@ -712,46 +711,46 @@ var AddonTestUtils = {
       // Note these files are being created in the XPI archive with date "0" which is 1970-01-01.
       zipW.addEntryStream(path, 0, Ci.nsIZipWriter.COMPRESSION_NONE,
                           stream, false);
     }
 
     zipW.close();
   },
 
-  promiseWriteFilesToZip: Task.async(function*(zip, files, flags) {
-    yield this.recursiveMakeDir(OS.Path.dirname(zip));
+  promiseWriteFilesToZip: async function(zip, files, flags) {
+    await this.recursiveMakeDir(OS.Path.dirname(zip));
 
     this.writeFilesToZip(zip, files, flags);
 
     return Promise.resolve(nsFile(zip));
-  }),
+  },
 
-  promiseWriteFilesToDir: Task.async(function*(dir, files) {
-    yield this.recursiveMakeDir(dir);
+  promiseWriteFilesToDir: async function(dir, files) {
+    await this.recursiveMakeDir(dir);
 
     for (let [path, data] of Object.entries(files)) {
       path = path.split("/");
       let leafName = path.pop();
 
       // Create parent directories, if necessary.
       let dirPath = dir;
       for (let subDir of path) {
         dirPath = OS.Path.join(dirPath, subDir);
-        yield OS.Path.makeDir(dirPath, {ignoreExisting: true});
+        await OS.Path.makeDir(dirPath, {ignoreExisting: true});
       }
 
       if (typeof data == "string")
         data = new TextEncoder("utf-8").encode(data);
 
-      yield OS.File.writeAtomic(OS.Path.join(dirPath, leafName), data);
+      await OS.File.writeAtomic(OS.Path.join(dirPath, leafName), data);
     }
 
     return nsFile(dir);
-  }),
+  },
 
   promiseWriteFilesToExtension(dir, id, files, unpacked = this.testUnpacked) {
     if (typeof files["install.rdf"] === "object")
       files["install.rdf"] = this.createInstallRDF(files["install.rdf"]);
 
     if (unpacked) {
       let path = OS.Path.join(dir, id);
 
@@ -928,32 +927,32 @@ var AddonTestUtils = {
       let entries = ext.directoryEntries
                        .QueryInterface(Ci.nsIDirectoryEnumerator);
       while (entries.hasMoreElements())
         this.setExtensionModifiedTime(entries.nextFile, time);
       entries.close();
     }
   },
 
-  promiseSetExtensionModifiedTime: Task.async(function*(path, time) {
-    yield OS.File.setDates(path, time, time);
+  promiseSetExtensionModifiedTime: async function(path, time) {
+    await OS.File.setDates(path, time, time);
 
     let iterator = new OS.File.DirectoryIterator(path);
     try {
-      yield iterator.forEach(entry => {
+      await iterator.forEach(entry => {
         return this.promiseSetExtensionModifiedTime(entry.path, time);
       });
     } catch (ex) {
       if (ex instanceof OS.File.Error)
         return;
       throw ex;
     } finally {
       iterator.close().catch(() => {});
     }
-  }),
+  },
 
   registerDirectory(key, dir) {
     var dirProvider = {
       getFile(prop, persistent) {
         persistent.value = false;
         if (prop == key)
           return dir.clone();
         return null;
@@ -1169,43 +1168,43 @@ var AddonTestUtils = {
    *        either a generator function, per Task.jsm, or an ordinary
    *        function which returns promose.
    * @return {Promise<[Array<nsIConsoleMessage>, *]>}
    *        Resolves to an object containing a `messages` property, with
    *        the array of console messages emitted during the execution
    *        of the task, and a `result` property, containing the task's
    *        return value.
    */
-  promiseConsoleOutput: Task.async(function*(task) {
+  promiseConsoleOutput: async function(task) {
     const DONE = "=== xpcshell test console listener done ===";
 
     let listener, messages = [];
     let awaitListener = new Promise(resolve => {
       listener = msg => {
         if (msg == DONE) {
           resolve();
         } else {
           msg instanceof Ci.nsIScriptError;
           messages.push(msg);
         }
       };
     });
 
     Services.console.registerListener(listener);
     try {
-      let result = yield task();
+      let result = await task();
 
       Services.console.logStringMessage(DONE);
-      yield awaitListener;
+      await awaitListener;
 
       return {messages, result};
     } finally {
       Services.console.unregisterListener(listener);
     }
-  }),
+  },
 };
 
 for (let [key, val] of Object.entries(AddonTestUtils)) {
   if (typeof val == "function")
     AddonTestUtils[key] = val.bind(AddonTestUtils);
 }
 
 EventEmitter.decorate(AddonTestUtils);
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -13,17 +13,16 @@ this.EXPORTED_SYMBOLS = [];
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AddonManager.jsm");
 /* globals AddonManagerPrivate*/
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 /* globals OS*/
 Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/GMPUtils.jsm");
 /* globals EME_ADOBE_ID, GMP_PLUGIN_IDS, GMPPrefs, GMPUtils, OPEN_H264_ID, WIDEVINE_ID */
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(
@@ -282,40 +281,40 @@ GMPWrapper.prototype = {
     }
 
     if (this._updateTask !== null) {
       this._log.trace("findUpdates() - " + this._plugin.id +
                       " - update task already running");
       return this._updateTask;
     }
 
-    this._updateTask = Task.spawn(function*() {
+    this._updateTask = async function() {
       this._log.trace("findUpdates() - updateTask");
       try {
         let installManager = new GMPInstallManager();
-        let res = yield installManager.checkForAddons();
+        let res = await installManager.checkForAddons();
         let update = res.gmpAddons.find(addon => addon.id === this._plugin.id);
         if (update && update.isValid && !update.isInstalled) {
           this._log.trace("findUpdates() - found update for " +
                           this._plugin.id + ", installing");
-          yield installManager.installAddon(update);
+          await installManager.installAddon(update);
         } else {
           this._log.trace("findUpdates() - no updates for " + this._plugin.id);
         }
         this._log.info("findUpdates() - updateTask succeeded for " +
                        this._plugin.id);
       } catch (e) {
         this._log.error("findUpdates() - updateTask for " + this._plugin.id +
                         " threw", e);
         throw e;
       } finally {
         this._updateTask = null;
         return true;
       }
-    }.bind(this));
+    }
 
     return this._updateTask;
   },
 
   get pluginMimeTypes() { return []; },
   get pluginLibraries() {
     if (this.isInstalled) {
       let path = this.version;
@@ -589,34 +588,34 @@ var GMPProvider = {
       this._log.warn("startup - adding clearkey CDM failed", e);
     }
   },
 
   shutdown: function() {
     this._log.trace("shutdown");
     Preferences.ignore(GMPPrefs.KEY_LOG_BASE, configureLogging);
 
-    let shutdownTask = Task.spawn(function*() {
+    let shutdownTask = async function() {
       this._log.trace("shutdown - shutdownTask");
       let shutdownSucceeded = true;
 
       for (let plugin of this._plugins.values()) {
         try {
-          yield plugin.wrapper.shutdown();
+          await plugin.wrapper.shutdown();
         } catch (e) {
           shutdownSucceeded = false;
         }
       }
 
       this._plugins = null;
 
       if (!shutdownSucceeded) {
         throw new Error("Shutdown failed");
       }
-    }.bind(this));
+    }
 
     return shutdownTask;
   },
 
   getAddonByID: function(aId, aCallback) {
     if (!this.isEnabled) {
       aCallback(null);
       return;
--- a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
+++ b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
@@ -18,17 +18,16 @@ const LOCAL_EME_SOURCES = [{
 }];
 
 this.EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
 
 Cu.importGlobalProperties(["XMLHttpRequest"]);
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/CertUtils.jsm");
 /* globals checkCert, BadCertHandler*/
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 
 /* globals GMPPrefs */
@@ -295,22 +294,22 @@ function downloadFile(url) {
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                   createInstance(Ci.nsISupports);
     xhr.onload = function(response) {
       logger.info("downloadXHR File download. status=" + xhr.status);
       if (xhr.status != 200 && xhr.status != 206) {
         reject(Components.Exception("File download failed", xhr.status));
         return;
       }
-      Task.spawn(function* () {
-        let f = yield OS.File.openUnique(OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon"));
+      new Promise(async function() {
+        let f = await OS.File.openUnique(OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon"));
         let path = f.path;
         logger.info(`Downloaded file will be saved to ${path}`);
-        yield f.file.close();
-        yield OS.File.writeAtomic(path, new Uint8Array(xhr.response));
+        await f.file.close();
+        await OS.File.writeAtomic(path, new Uint8Array(xhr.response));
         return path;
       }).then(resolve, reject);
     };
 
     let fail = (event) => {
       let request = event.target;
       let status = getRequestStatus(request);
       let message = "Failed downloading via XHR, status: " + status +  ", reason: " + event.type;
@@ -352,63 +351,63 @@ function binaryToHex(input) {
  *
  * @param  hashFunction
  *         The type of hash function to use, must be supported by nsICryptoHash.
  * @param  path
  *         The path of the file to hash.
  * @return a promise that resolves to hash of the file or rejects with a JS
  *         exception in case of error.
  */
-var computeHash = Task.async(function*(hashFunction, path) {
-  let file = yield OS.File.open(path, { existing: true, read: true });
+var computeHash = async function(hashFunction, path) {
+  let file = await OS.File.open(path, { existing: true, read: true });
   try {
     let hasher = Cc["@mozilla.org/security/hash;1"].
                  createInstance(Ci.nsICryptoHash);
     hasher.initWithString(hashFunction);
 
     let bytes;
     do {
-      bytes = yield file.read(HASH_CHUNK_SIZE);
+      bytes = await file.read(HASH_CHUNK_SIZE);
       hasher.update(bytes, bytes.length);
     } while (bytes.length == HASH_CHUNK_SIZE);
 
     return binaryToHex(hasher.finish(false));
   }
   finally {
-    yield file.close();
+    await file.close();
   }
-});
+};
 
 /**
  * Verifies that a downloaded file matches what was expected.
  *
  * @param  properties
  *         The properties to check, `size` and `hashFunction` with `hashValue`
  *         are supported. Any properties missing won't be checked.
  * @param  path
  *         The path of the file to check.
  * @return a promise that resolves if the file matched or rejects with a JS
  *         exception in case of error.
  */
-var verifyFile = Task.async(function*(properties, path) {
+var verifyFile = async function(properties, path) {
   if (properties.size !== undefined) {
-    let stat = yield OS.File.stat(path);
+    let stat = await OS.File.stat(path);
     if (stat.size != properties.size) {
       throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
     }
   }
 
   if (properties.hashFunction !== undefined) {
     let expectedDigest = properties.hashValue.toLowerCase();
-    let digest = yield computeHash(properties.hashFunction, path);
+    let digest = await computeHash(properties.hashFunction, path);
     if (digest != expectedDigest) {
       throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest +  "`.");
     }
   }
-});
+};
 
 const ProductAddonChecker = {
   /**
    * Downloads a list of add-ons from a URL optionally testing the SSL
    * certificate for certain attributes.
    *
    * @param  url
    *         The url to download from.
@@ -431,20 +430,20 @@ const ProductAddonChecker = {
    * Downloads an add-on to a local file and checks that it matches the expected
    * file. The caller is responsible for deleting the temporary file returned.
    *
    * @param  addon
    *         The addon to download.
    * @return a promise that resolves to the temporary file downloaded or rejects
    *         with a JS exception in case of error.
    */
-  downloadAddon: Task.async(function*(addon) {
-    let path = yield downloadFile(addon.URL);
+  downloadAddon: async function(addon) {
+    let path = await downloadFile(addon.URL);
     try {
-      yield verifyFile(addon, path);
+      await verifyFile(addon, path);
       return path;
     }
     catch (e) {
-      yield OS.File.remove(path);
+      await OS.File.remove(path);
       throw e;
     }
-  })
+  }
 }
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -38,18 +38,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils",
                                   "resource://gre/modules/ZipUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
                                   "resource://gre/modules/PermissionsUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
-                                  "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
                                   "resource://devtools/client/framework/ToolboxProcess.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
                                   "resource://gre/modules/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ProductAddonChecker",
                                   "resource://gre/modules/addons/ProductAddonChecker.jsm");
@@ -931,28 +929,28 @@ function getRDFProperty(aDs, aResource, 
  * Reads an AddonInternal object from a manifest stream.
  *
  * @param  aUri
  *         A |file:| or |jar:| URL for the manifest
  * @return an AddonInternal object
  * @throws if the install manifest in the stream is corrupt or could not
  *         be read
  */
-var loadManifestFromWebManifest = Task.async(function*(aUri) {
+async function loadManifestFromWebManifest(aUri) {
   // We're passed the URI for the manifest file. Get the URI for its
   // parent directory.
   let uri = NetUtil.newURI("./", null, aUri);
 
   let extension = new ExtensionData(uri);
 
-  let manifest = yield extension.readManifest();
+  let manifest = await extension.readManifest();
 
   // Read the list of available locales, and pre-load messages for
   // all locales.
-  let locales = yield extension.initAllLocales();
+  let locales = await extension.initAllLocales();
 
   // If there were any errors loading the extension, bail out now.
   if (extension.errors.length)
     throw new Error("Extension is invalid");
 
   let bss = (manifest.browser_specific_settings && manifest.browser_specific_settings.gecko)
       || (manifest.applications && manifest.applications.gecko) || {};
   if (manifest.browser_specific_settings && manifest.applications) {
@@ -1041,30 +1039,30 @@ var loadManifestFromWebManifest = Task.a
     maxVersion: bss.strict_max_version,
   }];
 
   addon.targetPlatforms = [];
   addon.userDisabled = false;
   addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;
 
   return addon;
-});
+}
 
 /**
  * Reads an AddonInternal object from an RDF stream.
  *
  * @param  aUri
  *         The URI that the manifest is being read from
  * @param  aStream
  *         An open stream to read the RDF from
  * @return an AddonInternal object
  * @throws if the install manifest in the RDF stream is corrupt or could not
  *         be read
  */
-let loadManifestFromRDF = Task.async(function*(aUri, aStream) {
+async function loadManifestFromRDF(aUri, aStream) {
   function getPropertyArray(aDs, aSource, aProperty) {
     let values = [];
     let targets = aDs.GetTargets(aSource, EM_R(aProperty), true);
     while (targets.hasMoreElements())
       values.push(getRDFValue(targets.getNext()));
 
     return values;
   }
@@ -1202,17 +1200,17 @@ let loadManifestFromRDF = Task.async(fun
         addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE &&
         addon.optionsType != AddonManager.OPTIONS_TYPE_TAB &&
         addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_INFO) {
       throw new Error("Install manifest specifies unknown type: " + addon.optionsType);
     }
 
     if (addon.hasEmbeddedWebExtension) {
       let uri = NetUtil.newURI("webextension/manifest.json", null, aUri);
-      let embeddedAddon = yield loadManifestFromWebManifest(uri);
+      let embeddedAddon = await loadManifestFromWebManifest(uri);
       if (embeddedAddon.optionsURL) {
         if (addon.optionsType || addon.optionsURL)
           logger.warn(`Addon ${addon.id} specifies optionsType or optionsURL ` +
                       `in both install.rdf and manifest.json`);
 
         addon.optionsURL = embeddedAddon.optionsURL;
         addon.optionsType = embeddedAddon.optionsType;
       }
@@ -1329,17 +1327,17 @@ let loadManifestFromRDF = Task.async(fun
     addon.updateURL = null;
     addon.updateKey = null;
   }
 
   // icons will be filled by the calling function
   addon.icons = {};
 
   return addon;
-});
+}
 
 function defineSyncGUID(aAddon) {
   // Define .syncGUID as a lazy property which is also settable
   Object.defineProperty(aAddon, "syncGUID", {
     get: () => {
       // Generate random GUID used for Sync.
       let guid = Cc["@mozilla.org/uuid-generator;1"]
           .getService(Ci.nsIUUIDGenerator)
@@ -1376,42 +1374,42 @@ function generateTemporaryInstallID(aFil
 /**
  * Loads an AddonInternal object from an add-on extracted in a directory.
  *
  * @param  aDir
  *         The nsIFile directory holding the add-on
  * @return an AddonInternal object
  * @throws if the directory does not contain a valid install manifest
  */
-var loadManifestFromDir = Task.async(function*(aDir, aInstallLocation) {
+async function loadManifestFromDir(aDir, aInstallLocation) {
   function getFileSize(aFile) {
     if (aFile.isSymlink())
       return 0;
 
     if (!aFile.isDirectory())
       return aFile.fileSize;
 
     let size = 0;
     let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
     let entry;
     while ((entry = entries.nextFile))
       size += getFileSize(entry);
     entries.close();
     return size;
   }
 
-  function* loadFromRDF(aUri) {
+  async function loadFromRDF(aUri) {
     let fis = Cc["@mozilla.org/network/file-input-stream;1"].
               createInstance(Ci.nsIFileInputStream);
     fis.init(aUri.file, -1, -1, false);
     let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
               createInstance(Ci.nsIBufferedInputStream);
     bis.init(fis, 4096);
     try {
-      var addon = yield loadManifestFromRDF(aUri, bis);
+      var addon = await loadManifestFromRDF(aUri, bis);
     } finally {
       bis.close();
       fis.close();
     }
 
     let iconFile = aDir.clone();
     iconFile.append("icon.png");
 
@@ -1440,56 +1438,56 @@ var loadManifestFromDir = Task.async(fun
     throw new Error("Directory " + aDir.path + " does not contain a valid " +
                     "install manifest");
   }
 
   let uri = Services.io.newFileURI(file).QueryInterface(Ci.nsIFileURL);
 
   let addon;
   if (file.leafName == FILE_WEB_MANIFEST) {
-    addon = yield loadManifestFromWebManifest(uri);
+    addon = await loadManifestFromWebManifest(uri);
     if (!addon.id) {
       if (aInstallLocation == TemporaryInstallLocation) {
         addon.id = generateTemporaryInstallID(aDir);
       } else {
         addon.id = aDir.leafName;
       }
     }
   } else {
-    addon = yield loadFromRDF(uri);
+    addon = await loadFromRDF(uri);
   }
 
   addon._sourceBundle = aDir.clone();
   addon._installLocation = aInstallLocation;
   addon.size = getFileSize(aDir);
-  addon.signedState = yield verifyDirSignedState(aDir, addon)
+  addon.signedState = await verifyDirSignedState(aDir, addon)
     .then(({signedState}) => signedState);
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
-});
+}
 
 /**
  * Loads an AddonInternal object from an nsIZipReader for an add-on.
  *
  * @param  aZipReader
  *         An open nsIZipReader for the add-on's files
  * @return an AddonInternal object
  * @throws if the XPI file does not contain a valid install manifest
  */
-var loadManifestFromZipReader = Task.async(function*(aZipReader, aInstallLocation) {
-  function* loadFromRDF(aUri) {
+async function loadManifestFromZipReader(aZipReader, aInstallLocation) {
+  async function loadFromRDF(aUri) {
     let zis = aZipReader.getInputStream(entry);
     let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
               createInstance(Ci.nsIBufferedInputStream);
     bis.init(zis, 4096);
     try {
-      var addon = yield loadManifestFromRDF(aUri, bis);
+      var addon = await loadManifestFromRDF(aUri, bis);
     } finally {
       bis.close();
       zis.close();
     }
 
     if (aZipReader.hasEntry("icon.png")) {
       addon.icons[32] = "icon.png";
       addon.icons[48] = "icon.png";
@@ -1518,28 +1516,28 @@ var loadManifestFromZipReader = Task.asy
                     "install manifest");
   }
 
   let uri = buildJarURI(aZipReader.file, entry);
 
   let isWebExtension = (entry == FILE_WEB_MANIFEST);
 
   let addon = isWebExtension ?
-              yield loadManifestFromWebManifest(uri) :
-              yield loadFromRDF(uri);
+              await loadManifestFromWebManifest(uri) :
+              await loadFromRDF(uri);
 
   addon._sourceBundle = aZipReader.file;
   addon._installLocation = aInstallLocation;
 
   addon.size = 0;
   let entries = aZipReader.findEntries(null);
   while (entries.hasMore())
     addon.size += aZipReader.getEntry(entries.getNext()).realSize;
 
-  let {signedState, cert} = yield verifyZipSignedState(aZipReader.file, addon);
+  let {signedState, cert} = await verifyZipSignedState(aZipReader.file, addon);
   addon.signedState = signedState;
   if (isWebExtension && !addon.id) {
     if (cert) {
       addon.id = cert.commonName;
       if (!gIDTest.test(addon.id)) {
         throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
       }
     }
@@ -1547,42 +1545,42 @@ var loadManifestFromZipReader = Task.asy
       addon.id = generateTemporaryInstallID(aZipReader.file);
     }
   }
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
-});
+}
 
 /**
  * Loads an AddonInternal object from an add-on in an XPI file.
  *
  * @param  aXPIFile
  *         An nsIFile pointing to the add-on's XPI file
  * @return an AddonInternal object
  * @throws if the XPI file does not contain a valid install manifest
  */
-var loadManifestFromZipFile = Task.async(function*(aXPIFile, aInstallLocation) {
+async function loadManifestFromZipFile(aXPIFile, aInstallLocation) {
   let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
                   createInstance(Ci.nsIZipReader);
   try {
     zipReader.open(aXPIFile);
 
     // Can't return this promise because that will make us close the zip reader
     // before it has finished loading the manifest. Wait for the result and then
     // return.
-    let manifest = yield loadManifestFromZipReader(zipReader, aInstallLocation);
+    let manifest = await loadManifestFromZipReader(zipReader, aInstallLocation);
     return manifest;
   }
   finally {
     zipReader.close();
   }
-});
+}
 
 function loadManifestFromFile(aFile, aInstallLocation) {
   if (aFile.isFile())
     return loadManifestFromZipFile(aFile, aInstallLocation);
   return loadManifestFromDir(aFile, aInstallLocation);
 }
 
 /**
@@ -1940,33 +1938,31 @@ function escapeAddonURI(aAddon, aUri, aU
     compatMode = "ignore";
   else if (AddonManager.strictCompatibility)
     compatMode = "strict";
   uri = uri.replace(/%COMPATIBILITY_MODE%/g, compatMode);
 
   return uri;
 }
 
-function removeAsync(aFile) {
-  return Task.spawn(function*() {
-    let info = null;
-    try {
-      info = yield OS.File.stat(aFile.path);
-      if (info.isDir)
-        yield OS.File.removeDir(aFile.path);
-      else
-        yield OS.File.remove(aFile.path);
-    }
-    catch (e) {
-      if (!(e instanceof OS.File.Error) || ! e.becauseNoSuchFile)
-        throw e;
-      // The file has already gone away
-      return;
-    }
-  });
+async function removeAsync(aFile) {
+  let info = null;
+  try {
+    info = await OS.File.stat(aFile.path);
+    if (info.isDir)
+      await OS.File.removeDir(aFile.path);
+    else
+      await OS.File.remove(aFile.path);
+  }
+  catch (e) {
+    if (!(e instanceof OS.File.Error) || ! e.becauseNoSuchFile)
+      throw e;
+    // The file has already gone away
+    return;
+  }
 }
 
 /**
  * Recursively removes a directory or file fixing permissions when necessary.
  *
  * @param  aFile
  *         The nsIFile to remove
  */
@@ -3113,41 +3109,41 @@ this.XPIProvider = {
              getService(Ci.nsIWindowWatcher);
     ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
 
     // Ensure any changes to the add-ons list are flushed to disk
     Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
                                !XPIDatabase.writeAddonsList());
   },
 
-  updateSystemAddons: Task.async(function*() {
+  updateSystemAddons: async function() {
     let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
     if (!systemAddonLocation)
       return;
 
     // Don't do anything in safe mode
     if (Services.appinfo.inSafeMode)
       return;
 
     // Download the list of system add-ons
     let url = Preferences.get(PREF_SYSTEM_ADDON_UPDATE_URL, null);
     if (!url) {
-      yield systemAddonLocation.cleanDirectories();
+      await systemAddonLocation.cleanDirectories();
       return;
     }
 
     url = UpdateUtils.formatUpdateURL(url);
 
     logger.info(`Starting system add-on update check from ${url}.`);
-    let res = yield ProductAddonChecker.getProductAddonList(url);
+    let res = await ProductAddonChecker.getProductAddonList(url);
 
     // If there was no list then do nothing.
     if (!res || !res.gmpAddons) {
       logger.info("No system add-ons list was returned.");
-      yield systemAddonLocation.cleanDirectories();
+      await systemAddonLocation.cleanDirectories();
       return;
     }
 
     let addonList = new Map(
       res.gmpAddons.map(spec => [spec.id, { spec, path: null, addon: null }]));
 
     let getAddonsInLocation = (location) => {
       return new Promise(resolve => {
@@ -3167,66 +3163,66 @@ this.XPIProvider = {
         if (wantedInfo.spec.version != addon.version)
           return false;
       }
 
       return true;
     };
 
     // If this matches the current set in the profile location then do nothing.
-    let updatedAddons = addonMap(yield getAddonsInLocation(KEY_APP_SYSTEM_ADDONS));
+    let updatedAddons = addonMap(await getAddonsInLocation(KEY_APP_SYSTEM_ADDONS));
     if (setMatches(addonList, updatedAddons)) {
       logger.info("Retaining existing updated system add-ons.");
-      yield systemAddonLocation.cleanDirectories();
+      await systemAddonLocation.cleanDirectories();
       return;
     }
 
     // If this matches the current set in the default location then reset the
     // updated set.
-    let defaultAddons = addonMap(yield getAddonsInLocation(KEY_APP_SYSTEM_DEFAULTS));
+    let defaultAddons = addonMap(await getAddonsInLocation(KEY_APP_SYSTEM_DEFAULTS));
     if (setMatches(addonList, defaultAddons)) {
       logger.info("Resetting system add-ons.");
       systemAddonLocation.resetAddonSet();
-      yield systemAddonLocation.cleanDirectories();
+      await systemAddonLocation.cleanDirectories();
       return;
     }
 
     // Download all the add-ons
-    let downloadAddon = Task.async(function*(item) {
+    async function downloadAddon(item) {
       try {
         let sourceAddon = updatedAddons.get(item.spec.id);
         if (sourceAddon && sourceAddon.version == item.spec.version) {
           // Copying the file to a temporary location has some benefits. If the
           // file is locked and cannot be read then we'll fall back to
           // downloading a fresh copy. It also means we don't have to remember
           // whether to delete the temporary copy later.
           try {
             let path = OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon");
-            let unique = yield OS.File.openUnique(path);
+            let unique = await OS.File.openUnique(path);
             unique.file.close();
-            yield OS.File.copy(sourceAddon._sourceBundle.path, unique.path);
+            await OS.File.copy(sourceAddon._sourceBundle.path, unique.path);
             // Make sure to update file modification times so this is detected
             // as a new add-on.
-            yield OS.File.setDates(unique.path);
+            await OS.File.setDates(unique.path);
             item.path = unique.path;
           }
           catch (e) {
             logger.warn(`Failed make temporary copy of ${sourceAddon._sourceBundle.path}.`, e);
           }
         }
         if (!item.path) {
-          item.path = yield ProductAddonChecker.downloadAddon(item.spec);
+          item.path = await ProductAddonChecker.downloadAddon(item.spec);
         }
-        item.addon = yield loadManifestFromFile(nsIFile(item.path), systemAddonLocation);
+        item.addon = await loadManifestFromFile(nsIFile(item.path), systemAddonLocation);
       }
       catch (e) {
         logger.error(`Failed to download system add-on ${item.spec.id}`, e);
       }
-    });
-    yield Promise.all(Array.from(addonList.values()).map(downloadAddon));
+    }
+    await Promise.all(Array.from(addonList.values()).map(downloadAddon));
 
     // The download promises all resolve regardless, now check if they all
     // succeeded
     let validateAddon = (item) => {
       if (item.spec.id != item.addon.id) {
         logger.warn(`Downloaded system add-on expected to be ${item.spec.id} but was ${item.addon.id}.`);
         return false;
       }
@@ -3245,77 +3241,75 @@ this.XPIProvider = {
     try {
       if (!Array.from(addonList.values()).every(item => item.path && item.addon && validateAddon(item))) {
         throw new Error("Rejecting updated system add-on set that either could not " +
                         "be downloaded or contained unusable add-ons.");
       }
 
       // Install into the install location
       logger.info("Installing new system add-on set");
-      yield systemAddonLocation.installAddonSet(Array.from(addonList.values())
+      await systemAddonLocation.installAddonSet(Array.from(addonList.values())
         .map(a => a.addon));
 
       // Bug 1204156: Switch to the new system add-ons without requiring a restart
     }
     finally {
       // Delete the temporary files
       logger.info("Deleting temporary files");
       for (let item of addonList.values()) {
         // If this item downloaded delete the temporary file.
         if (item.path) {
           try {
-            yield OS.File.remove(item.path);
+            await OS.File.remove(item.path);
           }
           catch (e) {
             logger.warn(`Failed to remove temporary file ${item.path}.`, e);
           }
         }
       }
 
-      yield systemAddonLocation.cleanDirectories();
-    }
-  }),
+      await systemAddonLocation.cleanDirectories();
+    }
+  },
 
   /**
    * Verifies that all installed add-ons are still correctly signed.
    */
-  verifySignatures: function() {
-    XPIDatabase.getAddonList(a => true, (addons) => {
-      Task.spawn(function*() {
-        let changes = {
-          enabled: [],
-          disabled: []
-        };
-
-        for (let addon of addons) {
-          // The add-on might have vanished, we'll catch that on the next startup
-          if (!addon._sourceBundle.exists())
-            continue;
-
-          let signedState = yield verifyBundleSignedState(addon._sourceBundle, addon);
-
-          if (signedState != addon.signedState) {
-            addon.signedState = signedState;
-            AddonManagerPrivate.callAddonListeners("onPropertyChanged",
-                                                   addon.wrapper,
-                                                   ["signedState"]);
-          }
-
-          let disabled = XPIProvider.updateAddonDisabledState(addon);
-          if (disabled !== undefined)
-            changes[disabled ? "disabled" : "enabled"].push(addon.id);
-        }
-
-        XPIDatabase.saveChanges();
-
-        Services.obs.notifyObservers(null, "xpi-signature-changed", JSON.stringify(changes));
-      }).then(null, err => {
-        logger.error("XPI_verifySignature: " + err);
-      })
-    });
+  verifySignatures: async function() {
+    let addons = await new Promise(
+      resolve => XPIDatabase.getAddonList(a => true, resolve),
+    ).catch(err => logger.error("XPI_verifySignature: " + err));
+
+    let changes = {
+      enabled: [],
+      disabled: []
+    };
+
+    for (let addon of addons) {
+      // The add-on might have vanished, we'll catch that on the next startup
+      if (!addon._sourceBundle.exists())
+        continue;
+
+      let signedState = await verifyBundleSignedState(addon._sourceBundle, addon);
+
+      if (signedState != addon.signedState) {
+        addon.signedState = signedState;
+        AddonManagerPrivate.callAddonListeners("onPropertyChanged",
+                                               addon.wrapper,
+                                               ["signedState"]);
+      }
+
+      let disabled = XPIProvider.updateAddonDisabledState(addon);
+      if (disabled !== undefined)
+        changes[disabled ? "disabled" : "enabled"].push(addon.id);
+    }
+
+    XPIDatabase.saveChanges();
+
+    Services.obs.notifyObservers(null, "xpi-signature-changed", JSON.stringify(changes));
   },
 
   /**
    * Persists changes to XPIProvider.bootstrappedAddons to its store (a pref).
    */
   persistBootstrappedAddons: function() {
     // Experiments are disabled upon app load, so don't persist references.
     let filtered = {};
@@ -4067,41 +4061,41 @@ this.XPIProvider = {
    * Permanently installs add-on from a local XPI file or directory.
    * The signature is checked but the add-on persist on application restart.
    *
    * @param aFile
    *        An nsIFile for the unpacked add-on directory or XPI file.
    *
    * @return See installAddonFromLocation return value.
    */
-  installAddonFromSources: Task.async(function*(aFile) {
+  installAddonFromSources: async function(aFile) {
     let location = XPIProvider.installLocationsByName[KEY_APP_PROFILE];
     return this.installAddonFromLocation(aFile, location, "proxy");
-  }),
+  },
 
   /**
    * Installs add-on from a local XPI file or directory.
    *
    * @param aFile
    *        An nsIFile for the unpacked add-on directory or XPI file.
    * @param aInstallLocation
    *        Define a custom install location object to use for the install.
    * @param aInstallAction
    *        Optional action mode to use when installing the addon
    *        (see MutableDirectoryInstallLocation.installAddon)
    *
    * @return a Promise that resolves to an Addon object on success, or rejects
    *         if the add-on is not a valid restartless add-on or if the
    *         same ID is already installed.
    */
-  installAddonFromLocation: Task.async(function*(aFile, aInstallLocation, aInstallAction) {
+  installAddonFromLocation: async function(aFile, aInstallLocation, aInstallAction) {
     if (aFile.exists() && aFile.isFile()) {
       flushJarCache(aFile);
     }
-    let addon = yield loadManifestFromFile(aFile, aInstallLocation);
+    let addon = await loadManifestFromFile(aFile, aInstallLocation);
 
     aInstallLocation.installAddon({ id: addon.id, source: aFile, action: aInstallAction });
 
     if (addon.appDisabled) {
       let message = `Add-on ${addon.id} is not compatible with application version.`;
 
       let app = addon.matchingTargetApplication;
       if (app) {
@@ -4115,17 +4109,17 @@ this.XPIProvider = {
       throw new Error(message);
     }
 
     if (!addon.bootstrap) {
       throw new Error("Only restartless (bootstrap) add-ons"
                     + " can be installed from sources:", addon.id);
     }
     let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL;
-    let oldAddon = yield new Promise(
+    let oldAddon = await new Promise(
                    resolve => XPIDatabase.getVisibleAddonForID(addon.id, resolve));
     if (oldAddon) {
       if (!oldAddon.bootstrap) {
         logger.warn("Non-restartless Add-on is already installed", addon.id);
         throw new Error("Non-restartless add-on with ID "
                         + oldAddon.id + " is already installed");
       }
       else {
@@ -4180,17 +4174,17 @@ this.XPIProvider = {
                                     BOOTSTRAP_REASONS.ADDON_ENABLE);
     AddonManagerPrivate.callInstallListeners("onExternalInstall",
                                              null, addon.wrapper,
                                              oldAddon ? oldAddon.wrapper : null,
                                              false);
     AddonManagerPrivate.callAddonListeners("onInstalled", addon.wrapper);
 
     return addon.wrapper;
-  }),
+  },
 
   /**
    * Returns an Addon corresponding to an instance ID.
    * @param aInstanceID
    *        An Addon Instance ID
    * @return {Promise}
    * @resolves The found Addon or null if no such add-on exists.
    * @rejects  Never
@@ -5773,27 +5767,27 @@ AddonInstall.prototype = {
   /**
    * Fills out linkedInstalls with AddonInstall instances for the other files
    * in a multi-package XPI.
    *
    * @param  aFiles
    *         An array of { entryName, file } for each remaining file from the
    *         multi-package XPI.
    */
-  _createLinkedInstalls: Task.async(function*(aFiles) {
+  _createLinkedInstalls: async function(aFiles) {
     if (aFiles.length == 0)
       return;
 
     // Create new AddonInstall instances for every remaining file
     if (!this.linkedInstalls)
       this.linkedInstalls = [];
 
     for (let { entryName, file } of aFiles) {
       logger.debug("Creating linked install from " + entryName);
-      let install = yield new Promise(resolve => AddonInstall.createInstall(resolve, file));
+      let install = await new Promise(resolve => AddonInstall.createInstall(resolve, file));
 
       // Make the new install own its temporary file
       install.ownsTempFile = true;
 
       this.linkedInstalls.push(install);
 
       // If one of the internal XPIs was multipackage then move its linked
       // installs to the outer install
@@ -5802,30 +5796,30 @@ AddonInstall.prototype = {
         install.linkedInstalls = null;
       }
 
       install.sourceURI = this.sourceURI;
       install.releaseNotesURI = this.releaseNotesURI;
       if (install.state != AddonManager.STATE_DOWNLOAD_FAILED)
         install.updateAddonURIs();
     }
-  }),
+  },
 
   /**
    * Loads add-on manifests from a multi-package XPI file. Each of the
    * XPI and JAR files contained in the XPI will be extracted. Any that
    * do not contain valid add-ons will be ignored. The first valid add-on will
    * be installed by this AddonInstall instance, the rest will have new
    * AddonInstall instances created for them.
    *
    * @param  aZipReader
    *         An open nsIZipReader for the multi-package XPI's files. This will
    *         be closed before this method returns.
    */
-  _loadMultipackageManifests: Task.async(function*(aZipReader) {
+  _loadMultipackageManifests: async function(aZipReader) {
     let files = [];
     let entries = aZipReader.findEntries("(*.[Xx][Pp][Ii]|*.[Jj][Aa][Rr])");
     while (entries.hasMore()) {
       let entryName = entries.getNext();
       let file = getTemporaryFile();
       try {
         aZipReader.extract(entryName, file);
         files.push({ entryName, file });
@@ -5846,22 +5840,22 @@ AddonInstall.prototype = {
 
     let addon = null;
 
     // Find the first file that is a valid install and use it for
     // the add-on that this AddonInstall instance will install.
     for (let { entryName, file } of files) {
       this.removeTemporaryFile();
       try {
-        yield this.loadManifest(file);
+        await this.loadManifest(file);
         logger.debug("Base multi-package XPI install came from " + entryName);
         this.file = file;
         this.ownsTempFile = true;
 
-        yield this._createLinkedInstalls(files.filter(f => f.file != file));
+        await this._createLinkedInstalls(files.filter(f => f.file != file));
         return undefined;
       }
       catch (e) {
         // _createLinkedInstalls will log errors when it tries to process this
         // file
       }
     }
 
@@ -5871,41 +5865,41 @@ AddonInstall.prototype = {
         file.remove(true);
       } catch (e) {
         this.logger.warn("Could not remove temp file " + file.path);
       }
     }
 
     return Promise.reject([AddonManager.ERROR_CORRUPT_FILE,
                            "Multi-package XPI does not contain any valid packages to install"]);
-  }),
+  },
 
   /**
    * Called after the add-on is a local file and the signature and install
    * manifest can be read.
    *
    * @param  aCallback
    *         A function to call when the manifest has been loaded
    * @throws if the add-on does not contain a valid install manifest or the
    *         XPI is incorrectly signed
    */
-  loadManifest: Task.async(function*(file) {
+  loadManifest: async function(file) {
     let zipreader = Cc["@mozilla.org/libjar/zip-reader;1"].
                     createInstance(Ci.nsIZipReader);
     try {
       zipreader.open(file);
     }
     catch (e) {
       zipreader.close();
       return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
     }
 
     try {
       // loadManifestFromZipReader performs the certificate verification for us
-      this.addon = yield loadManifestFromZipReader(zipreader, this.installLocation);
+      this.addon = await loadManifestFromZipReader(zipreader, this.installLocation);
     }
     catch (e) {
       zipreader.close();
       return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
     }
 
     // A multi-package XPI is a container, the add-ons it holds each
     // have their own id.  Everything else had better have an id here.
@@ -5983,32 +5977,32 @@ AddonInstall.prototype = {
     this.name = this.addon.selectedLocale.name;
     this.type = this.addon.type;
     this.version = this.addon.version;
 
     // Setting the iconURL to something inside the XPI locks the XPI and
     // makes it impossible to delete on Windows.
 
     // Try to load from the existing cache first
-    let repoAddon = yield new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
+    let repoAddon = await new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
 
     // It wasn't there so try to re-download it
     if (!repoAddon) {
-      yield new Promise(resolve => AddonRepository.cacheAddons([this.addon.id], resolve));
-      repoAddon = yield new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
+      await new Promise(resolve => AddonRepository.cacheAddons([this.addon.id], resolve));
+      repoAddon = await new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
     }
 
     this.addon._repositoryAddon = repoAddon;
     this.name = this.name || this.addon._repositoryAddon.name;
     this.addon.compatibilityOverrides = repoAddon ?
                                     repoAddon.compatibilityOverrides :
                                     null;
     this.addon.appDisabled = !isUsableAddon(this.addon);
     return undefined;
-  }),
+  },
 
   observe: function(aSubject, aTopic, aData) {
     // Network is going offline
     this.cancel();
   },
 
   /**
    * Starts downloading the add-on's XPI file.
@@ -6290,107 +6284,107 @@ AddonInstall.prototype = {
     else
       logger.debug("downloadFailed: listener changed AddonInstall state for " +
           this.sourceURI.spec + " to " + this.state);
   },
 
   /**
    * Notify listeners that the download completed.
    */
-  downloadCompleted: function() {
-    XPIDatabase.getVisibleAddonForID(this.addon.id, aAddon => {
-      if (aAddon)
-        this.existingAddon = aAddon;
-
-      this.state = AddonManager.STATE_DOWNLOADED;
-      this.addon.updateDate = Date.now();
-
-      if (this.existingAddon) {
-        this.addon.existingAddonID = this.existingAddon.id;
-        this.addon.installDate = this.existingAddon.installDate;
-        applyBlocklistChanges(this.existingAddon, this.addon);
-      }
-      else {
-        this.addon.installDate = this.addon.updateDate;
-      }
-
-      if (AddonManagerPrivate.callInstallListeners("onDownloadEnded",
-                                                   this.listeners,
-                                                   this.wrapper)) {
-        // If a listener changed our state then do not proceed with the install
-        if (this.state != AddonManager.STATE_DOWNLOADED)
-          return;
-
-        // If an upgrade listener is registered for this add-on, pass control
-        // over the upgrade to the add-on.
-        if (AddonManagerPrivate.hasUpgradeListener(this.addon.id)) {
-          logger.info(`${this.addon.id} has an upgrade listener, postponing until restart`);
-          this.state = AddonManager.STATE_POSTPONED;
-
-          let stagingDir = this.installLocation.getStagingDir();
-          let stagedAddon = stagingDir.clone();
-
-          Task.spawn((function*() {
-            yield this.installLocation.requestStagingDir();
-
-            yield this.unstageInstall(stagedAddon);
-
-            stagedAddon.append(this.addon.id);
-            stagedAddon.leafName = this.addon.id + ".xpi";
-
-            yield this.stageInstall(true, stagedAddon, true);
-
-            AddonManagerPrivate.callInstallListeners("onInstallPostponed",
-                                                     this.listeners, this.wrapper)
-
-            // upgrade has been staged for restart, notify the add-on and give
-            // it a way to resume.
-            let callback = AddonManagerPrivate.getUpgradeListener(this.addon.id);
-            callback({
-              version: this.version,
-              install: () => {
-                switch (this.state) {
-                  case AddonManager.STATE_INSTALLED:
-                    // this addon has already been installed, nothing to do
-                    logger.warn(`${this.addon.id} tried to resume postponed upgrade, but it's already installed`);
-                    break;
-                  case AddonManager.STATE_POSTPONED:
-                    logger.info(`${this.addon.id} has resumed a previously postponed upgrade`);
-                    this.state = AddonManager.STATE_DOWNLOADED;
-                    this.installLocation.releaseStagingDir();
-                    this.install();
-                    break;
-                  default:
-                    logger.warn(`${this.addon.id} cannot resume postponed upgrade from state (${this.state})`);
-                    break;
-                }
-              },
-            });
-          }).bind(this));
-        } else {
-          // no upgrade listener present, so proceed with normal install
-          this.install();
-          if (this.linkedInstalls) {
-            for (let install of this.linkedInstalls) {
-              if (install.state == AddonManager.STATE_DOWNLOADED)
-                install.install();
+  downloadCompleted: async function() {
+    let aAddon = await new Promise(
+      resolve => XPIDatabase.getVisibleAddonForID(this.addon.id, resolve)
+    );
+    if (aAddon) {
+      this.existingAddon = aAddon;
+    }
+
+    this.state = AddonManager.STATE_DOWNLOADED;
+    this.addon.updateDate = Date.now();
+
+    if (this.existingAddon) {
+      this.addon.existingAddonID = this.existingAddon.id;
+      this.addon.installDate = this.existingAddon.installDate;
+      applyBlocklistChanges(this.existingAddon, this.addon);
+    }
+    else {
+      this.addon.installDate = this.addon.updateDate;
+    }
+
+    if (AddonManagerPrivate.callInstallListeners("onDownloadEnded",
+                                                 this.listeners,
+                                                 this.wrapper)) {
+      // If a listener changed our state then do not proceed with the install
+      if (this.state != AddonManager.STATE_DOWNLOADED)
+        return;
+
+      // If an upgrade listener is registered for this add-on, pass control
+      // over the upgrade to the add-on.
+      if (AddonManagerPrivate.hasUpgradeListener(this.addon.id)) {
+        logger.info(`${this.addon.id} has an upgrade listener, postponing until restart`);
+        this.state = AddonManager.STATE_POSTPONED;
+
+        let stagingDir = this.installLocation.getStagingDir();
+        let stagedAddon = stagingDir.clone();
+
+        await this.installLocation.requestStagingDir();
+
+        await this.unstageInstall(stagedAddon);
+
+        stagedAddon.append(this.addon.id);
+        stagedAddon.leafName = this.addon.id + ".xpi";
+
+        await this.stageInstall(true, stagedAddon, true);
+
+        AddonManagerPrivate.callInstallListeners("onInstallPostponed",
+                                                 this.listeners, this.wrapper)
+
+        // upgrade has been staged for restart, notify the add-on and give
+        // it a way to resume.
+        let callback = AddonManagerPrivate.getUpgradeListener(this.addon.id);
+        callback({
+          version: this.version,
+          install: () => {
+            switch (this.state) {
+              case AddonManager.STATE_INSTALLED:
+                // this addon has already been installed, nothing to do
+                logger.warn(`${this.addon.id} tried to resume postponed upgrade, but it's already installed`);
+                break;
+              case AddonManager.STATE_POSTPONED:
+                logger.info(`${this.addon.id} has resumed a previously postponed upgrade`);
+                this.state = AddonManager.STATE_DOWNLOADED;
+                this.installLocation.releaseStagingDir();
+                this.install();
+                break;
+              default:
+                logger.warn(`${this.addon.id} cannot resume postponed upgrade from state (${this.state})`);
+                break;
             }
+          },
+        });
+      } else {
+        // no upgrade listener present, so proceed with normal install
+        this.install();
+        if (this.linkedInstalls) {
+          for (let install of this.linkedInstalls) {
+            if (install.state == AddonManager.STATE_DOWNLOADED)
+              install.install();
           }
         }
       }
-    });
+    }
   },
 
   // TODO This relies on the assumption that we are always installing into the
   // highest priority install location so the resulting add-on will be visible
   // overriding any existing copy in another install location (bug 557710).
   /**
    * Installs the add-on into the install location.
    */
-  startInstall: function() {
+  startInstall: async function() {
     this.state = AddonManager.STATE_INSTALLING;
     if (!AddonManagerPrivate.callInstallListeners("onInstallStarted",
                                                   this.listeners, this.wrapper)) {
       this.state = AddonManager.STATE_DOWNLOADED;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callInstallListeners("onInstallCancelled",
                                                this.listeners, this.wrapper)
       return;
@@ -6414,28 +6408,28 @@ AddonInstall.prototype = {
     logger.debug("Starting install of " + this.addon.id + " from " + this.sourceURI.spec);
     AddonManagerPrivate.callAddonListeners("onInstalling",
                                            this.addon.wrapper,
                                            requiresRestart);
 
     let stagingDir = this.installLocation.getStagingDir();
     let stagedAddon = stagingDir.clone();
 
-    Task.spawn((function*() {
+    let install = new Promise(async function() {
       let installedUnpacked = 0;
 
-      yield this.installLocation.requestStagingDir();
+      await this.installLocation.requestStagingDir();
 
       // remove any previously staged files
-      yield this.unstageInstall(stagedAddon);
+      await this.unstageInstall(stagedAddon);
 
       stagedAddon.append(this.addon.id);
       stagedAddon.leafName = this.addon.id + ".xpi";
 
-      installedUnpacked = yield this.stageInstall(requiresRestart, stagedAddon, isUpgrade);
+      installedUnpacked = await this.stageInstall(requiresRestart, stagedAddon, isUpgrade);
 
       if (requiresRestart) {
         this.state = AddonManager.STATE_INSTALLED;
         AddonManagerPrivate.callInstallListeners("onInstallEnded",
                                                  this.listeners, this.wrapper,
                                                  this.addon.wrapper);
       }
       else {
@@ -6533,57 +6527,59 @@ AddonInstall.prototype = {
             // XXX this makes it dangerous to do some things in onInstallEnded
             // listeners because important cleanup hasn't been done yet
             XPIProvider.unloadBootstrapScope(this.addon.id);
           }
         }
         XPIProvider.setTelemetry(this.addon.id, "unpacked", installedUnpacked);
         recordAddonTelemetry(this.addon);
       }
-    }).bind(this)).then(null, (e) => {
+    }).then(null, (e) => {
       logger.warn("Failed to install " + this.file.path + " from " + this.sourceURI.spec + " to " + stagedAddon.path, e);
       if (stagedAddon.exists())
         recursiveRemove(stagedAddon);
       this.state = AddonManager.STATE_INSTALL_FAILED;
       this.error = AddonManager.ERROR_FILE_ACCESS;
       XPIProvider.removeActiveInstall(this);
       AddonManagerPrivate.callAddonListeners("onOperationCancelled",
                                              this.addon.wrapper);
       AddonManagerPrivate.callInstallListeners("onInstallFailed",
                                                this.listeners,
                                                this.wrapper);
     }).then(() => {
       this.removeTemporaryFile();
       return this.installLocation.releaseStagingDir();
     });
+
+    await install();
   },
 
   /**
    * Stages an upgrade for next application restart.
    */
-  stageInstall: function*(restartRequired, stagedAddon, isUpgrade) {
+  stageInstall: async function(restartRequired, stagedAddon, isUpgrade) {
     let stagedJSON = stagedAddon.clone();
     stagedJSON.leafName = this.addon.id + ".json";
 
     let installedUnpacked = 0;
 
     // First stage the file regardless of whether restarting is necessary
     if (this.addon.unpack || Preferences.get(PREF_XPI_UNPACK, false)) {
       logger.debug("Addon " + this.addon.id + " will be installed as " +
           "an unpacked directory");
       stagedAddon.leafName = this.addon.id;
-      yield OS.File.makeDir(stagedAddon.path);
-      yield ZipUtils.extractFilesAsync(this.file, stagedAddon);
+      await OS.File.makeDir(stagedAddon.path);
+      await ZipUtils.extractFilesAsync(this.file, stagedAddon);
       installedUnpacked = 1;
     }
     else {
       logger.debug("Addon " + this.addon.id + " will be installed as " +
           "a packed xpi");
       stagedAddon.leafName = this.addon.id + ".xpi";
-      yield OS.File.copy(this.file.path, stagedAddon.path);
+      await OS.File.copy(this.file.path, stagedAddon.path);
     }
 
     if (restartRequired) {
       // Point the add-on to its extracted files as the xpi may get deleted
       this.addon._sourceBundle = stagedAddon;
 
       // Cache the AddonInternal as it may have updated compatibility info
       writeStringToFile(stagedJSON, JSON.stringify(this.addon));
@@ -6596,30 +6592,30 @@ AddonInstall.prototype = {
     }
 
     return installedUnpacked;
   },
 
   /**
    * Removes any previously staged upgrade.
    */
-  unstageInstall: function*(stagedAddon) {
+  unstageInstall: async function(stagedAddon) {
     let stagedJSON = stagedAddon.clone();
     let removedAddon = stagedAddon.clone();
 
     stagedJSON.append(this.addon.id + ".json");
 
     if (stagedJSON.exists()) {
       stagedJSON.remove(true);
     }
 
     removedAddon.append(this.addon.id);
-    yield removeAsync(removedAddon);
+    await removeAsync(removedAddon);
     removedAddon.leafName = this.addon.id + ".xpi";
-    yield removeAsync(removedAddon);
+    await removeAsync(removedAddon);
   },
 
   getInterface: function(iid) {
     if (iid.equals(Ci.nsIAuthPrompt2)) {
       let win = this.window;
       if (!win && this.browser)
         win = this.browser.ownerDocument.defaultView;
 
@@ -7250,25 +7246,22 @@ AddonInternal.prototype = {
     this.appDisabled = !isUsableAddon(this);
   },
 
   /**
    * getDataDirectory tries to execute the callback with two arguments:
    * 1) the path of the data directory within the profile,
    * 2) any exception generated from trying to build it.
    */
-  getDataDirectory: function(callback) {
+  getDataDirectory: async function(callback) {
     let parentPath = OS.Path.join(OS.Constants.Path.profileDir, "extension-data");
     let dirPath = OS.Path.join(parentPath, this.id);
 
-    Task.spawn(function*() {
-      yield OS.File.makeDir(parentPath, {ignoreExisting: true});
-      yield OS.File.makeDir(dirPath, {ignoreExisting: true});
-    }).then(() => callback(dirPath, null),
-            e => callback(dirPath, e));
+    await OS.File.makeDir(parentPath, {ignoreExisting: true});
+    await OS.File.makeDir(dirPath, {ignoreExisting: true});
   },
 
   /**
    * toJSON is called by JSON.stringify in order to create a filtered version
    * of this object to be serialized to a JSON file. A new object is returned
    * with copies of all non-private properties. Functions, getters and setters
    * are not copied.
    *
@@ -8694,114 +8687,114 @@ Object.assign(SystemAddonInstallLocation
     this._saveAddonSet({ schema: 1, addons: {} });
   },
 
   /**
    * Removes any directories not currently in use or pending use after a
    * restart. Any errors that happen here don't really matter as we'll attempt
    * to cleanup again next time.
    */
-  cleanDirectories: Task.async(function*() {
+  cleanDirectories: async function() {
 
     // System add-ons directory does not exist
-    if (!(yield OS.File.exists(this._baseDir.path))) {
+    if (!(await OS.File.exists(this._baseDir.path))) {
       return;
     }
 
     let iterator;
     try {
       iterator = new OS.File.DirectoryIterator(this._baseDir.path);
     }
     catch (e) {
       logger.error("Failed to clean updated system add-ons directories.", e);
       return;
     }
 
     try {
       let entries = [];
 
-      yield iterator.forEach(entry => {
+      await iterator.forEach(entry => {
         // Skip the directory currently in use
         if (this._directory && this._directory.path == entry.path)
           return;
 
         // Skip the next directory
         if (this._nextDir && this._nextDir.path == entry.path)
           return;
 
         entries.push(entry);
       });
 
       for (let entry of entries) {
         if (entry.isDir) {
-          yield OS.File.removeDir(entry.path, {
+          await OS.File.removeDir(entry.path, {
             ignoreAbsent: true,
             ignorePermissions: true,
           });
         }
         else {
-          yield OS.File.remove(entry.path, {
+          await OS.File.remove(entry.path, {
             ignoreAbsent: true,
           });
         }
       }
     }
     catch (e) {
       logger.error("Failed to clean updated system add-ons directories.", e);
     }
     finally {
       iterator.close();
     }
-  }),
+  },
 
   /**
    * Installs a new set of system add-ons into the location and updates the
    * add-on set in prefs. We wait to switch state until a restart.
    */
-  installAddonSet: Task.async(function*(aAddons) {
+  installAddonSet: async function(aAddons) {
     // Make sure the base dir exists
-    yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
+    await OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
 
     let newDir = this._baseDir.clone();
 
     let uuidGen = Cc["@mozilla.org/uuid-generator;1"].
                   getService(Ci.nsIUUIDGenerator);
     newDir.append("blank");
 
     while (true) {
       newDir.leafName = uuidGen.generateUUID().toString();
 
       try {
-        yield OS.File.makeDir(newDir.path, { ignoreExisting: false });
+        await OS.File.makeDir(newDir.path, { ignoreExisting: false });
         break;
       }
       catch (e) {
         // Directory already exists, pick another
       }
     }
 
-    let copyAddon = Task.async(function*(addon) {
+    let copyAddon = async function(addon) {
       let target = OS.Path.join(newDir.path, addon.id + ".xpi");
       logger.info(`Copying ${addon.id} from ${addon._sourceBundle.path} to ${target}.`);
       try {
-        yield OS.File.copy(addon._sourceBundle.path, target);
+        await OS.File.copy(addon._sourceBundle.path, target);
       }
       catch (e) {
         logger.error(`Failed to copy ${addon.id} from ${addon._sourceBundle.path} to ${target}.`, e);
         throw e;
       }
       addon._sourceBundle = new nsIFile(target);
-    });
+    };
 
     try {
-      yield waitForAllPromises(aAddons.map(copyAddon));
+      await waitForAllPromises(aAddons.map(copyAddon));
     }
     catch (e) {
       try {
-        yield OS.File.removeDir(newDir.path, { ignorePermissions: true });
+        await OS.File.removeDir(newDir.path, { ignorePermissions: true });
       }
       catch (e) {
         logger.warn(`Failed to remove new system add-on directory ${newDir.path}.`, e);
       }
       throw e;
     }
 
     // All add-ons in position, create the new state and store it in prefs
@@ -8809,17 +8802,17 @@ Object.assign(SystemAddonInstallLocation
     for (let addon of aAddons) {
       state.addons[addon.id] = {
         version: addon.version
       }
     }
 
     this._saveAddonSet(state);
     this._nextDir = newDir;
-  }),
+  },
 });
 
 /**
  * An object which identifies an install location for temporary add-ons.
  */
 const TemporaryInstallLocation = {
   locked: false,
   name: KEY_APP_TEMPORARY,
--- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js
@@ -219,20 +219,20 @@ function asyncMap(aObjects, aMethod, aCa
 }
 
 /**
  * A generator to synchronously return result rows from an mozIStorageStatement.
  *
  * @param  aStatement
  *         The statement to execute
  */
-function* resultRows(aStatement) {
+async function resultRows(aStatement) {
   try {
     while (stepStatement(aStatement))
-      yield aStatement.row;
+      await aStatement.row;
   }
   finally {
     aStatement.reset();
   }
 }
 
 /**
  * A helper function to log an SQL error.
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -24,18 +24,16 @@ try {
 }
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
-                                  "resource://gre/modules/Task.jsm");
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
 const FILE_BLOCKLIST                  = "blocklist.xml";
 const PREF_BLOCKLIST_LASTUPDATETIME   = "app.update.lastUpdateTime.blocklist-background-update-timer";
 const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
 const PREF_BLOCKLIST_ITEM_URL         = "extensions.blocklist.itemURL";
@@ -634,17 +632,17 @@ Blocklist.prototype = {
                                 {});
       updater.checkVersions().catch(() => {
         // Before we enable this in release, we want to collect telemetry on
         // failed kinto updates - see bug 1254099
       });
     }
   },
 
-  onXMLLoad: Task.async(function*(aEvent) {
+  onXMLLoad: async function(aEvent) {
     let request = aEvent.target;
     try {
       gCertUtils.checkCert(request.channel);
     }
     catch (e) {
       LOG("Blocklist::onXMLLoad: " + e);
       return;
     }
@@ -663,21 +661,21 @@ Blocklist.prototype = {
 
     this._loadBlocklistFromString(request.responseText);
     // We don't inform the users when the graphics blocklist changed at runtime.
     // However addons and plugins blocking status is refreshed.
     this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
 
     try {
       let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
-      yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
+      await OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
     } catch (e) {
       LOG("Blocklist::onXMLLoad: " + e);
     }
-  }),
+  },
 
   onXMLError: function(aEvent) {
     try {
       var request = aEvent.target;
       // the following may throw (e.g. a local file or timeout)
       var status = request.status;
     }
     catch (e) {
@@ -849,54 +847,54 @@ Blocklist.prototype = {
   /* Used for testing */
   _clear: function() {
     this._addonEntries = null;
     this._gfxEntries = null;
     this._pluginEntries = null;
     this._preloadedBlocklistContent = null;
   },
 
-  _preloadBlocklist: Task.async(function*() {
+  _preloadBlocklist: async function() {
     let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
     try {
-      yield this._preloadBlocklistFile(profPath);
+      await this._preloadBlocklistFile(profPath);
       return;
     } catch (e) {
       LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e)
     }
 
     var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
     try {
-      yield this._preloadBlocklistFile(appFile.path);
+      await this._preloadBlocklistFile(appFile.path);
       return;
     } catch (e) {
       LOG("Blocklist::_preloadBlocklist: Failed to load XML file " + e)
     }
 
     LOG("Blocklist::_preloadBlocklist: no XML File found");
-  }),
+  },
 
-  _preloadBlocklistFile: Task.async(function*(path) {
+  _preloadBlocklistFile: async function(path) {
     if (this._addonEntries) {
       // The file has been already loaded.
       return;
     }
 
     if (!gBlocklistEnabled) {
       LOG("Blocklist::_preloadBlocklistFile: blocklist is disabled");
       return;
     }
 
-    let text = yield OS.File.read(path, { encoding: "utf-8" });
+    let text = await OS.File.read(path, { encoding: "utf-8" });
 
     if (!this._addonEntries) {
       // Store the content only if a sync load has not been performed in the meantime.
       this._preloadedBlocklistContent = text;
     }
-  }),
+  },
 
   _loadBlocklistFromString : function(text) {
     try {
       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
                    createInstance(Ci.nsIDOMParser);
       var doc = parser.parseFromString(text, "text/xml");
       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +