Bug 1365005: Cleanup async code after Task.jsm migration. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Mon, 15 May 2017 12:07:06 -0700
changeset 577997 f6e5056fc2de0b13b4b77e27fe88c2d4063b1445
parent 577996 1e803c515c7a7f8c90b0f2bc0e997bb9865052a6
child 628652 a2d28bc3d03e41097bc98631a0f70fe80ac179bd
push id58855
push usermaglione.k@gmail.com
push dateMon, 15 May 2017 19:07:29 +0000
reviewersaswan
bugs1365005
milestone55.0a1
Bug 1365005: Cleanup async code after Task.jsm migration. r?aswan MozReview-Commit-ID: FNKSsBqq6Kf
browser/components/extensions/ExtensionPopups.jsm
browser/components/extensions/ext-bookmarks.js
browser/components/extensions/ext-devtools.js
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionStorageSync.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -441,100 +441,98 @@ class ViewPopup extends BasePopup {
    *
    * @param {Element} viewNode
    *        The node to attach the browser to.
    * @returns {Promise<boolean>}
    *        Resolves when the browser is ready. Resolves to `false` if the
    *        browser was destroyed before it was fully loaded, and the popup
    *        should be closed, or `true` otherwise.
    */
-  attach(viewNode) {
-    return (async () => {
-      this.viewNode = viewNode;
-      this.viewNode.addEventListener(this.DESTROY_EVENT, this);
+  async attach(viewNode) {
+    this.viewNode = viewNode;
+    this.viewNode.addEventListener(this.DESTROY_EVENT, this);
 
-      // Wait until the browser element is fully initialized, and give it at least
-      // a short grace period to finish loading its initial content, if necessary.
-      //
-      // In practice, the browser that was created by the mousdown handler should
-      // nearly always be ready by this point.
-      await Promise.all([
-        this.browserReady,
-        Promise.race([
-          // This promise may be rejected if the popup calls window.close()
-          // before it has fully loaded.
-          this.browserLoaded.catch(() => {}),
-          new Promise(resolve => setTimeout(resolve, POPUP_LOAD_TIMEOUT_MS)),
-        ]),
-      ]);
+    // Wait until the browser element is fully initialized, and give it at least
+    // a short grace period to finish loading its initial content, if necessary.
+    //
+    // In practice, the browser that was created by the mousdown handler should
+    // nearly always be ready by this point.
+    await Promise.all([
+      this.browserReady,
+      Promise.race([
+        // This promise may be rejected if the popup calls window.close()
+        // before it has fully loaded.
+        this.browserLoaded.catch(() => {}),
+        new Promise(resolve => setTimeout(resolve, POPUP_LOAD_TIMEOUT_MS)),
+      ]),
+    ]);
 
-      if (!this.destroyed && !this.panel) {
-        this.destroy();
-      }
+    if (!this.destroyed && !this.panel) {
+      this.destroy();
+    }
 
-      if (this.destroyed) {
-        CustomizableUI.hidePanelForNode(viewNode);
-        return false;
-      }
+    if (this.destroyed) {
+      CustomizableUI.hidePanelForNode(viewNode);
+      return false;
+    }
 
-      this.attached = true;
+    this.attached = true;
 
 
-      // Store the initial height of the view, so that we never resize menu panel
-      // sub-views smaller than the initial height of the menu.
-      this.viewHeight = this.viewNode.boxObject.height;
+    // Store the initial height of the view, so that we never resize menu panel
+    // sub-views smaller than the initial height of the menu.
+    this.viewHeight = this.viewNode.boxObject.height;
 
-      // Calculate the extra height available on the screen above and below the
-      // menu panel. Use that to calculate the how much the sub-view may grow.
-      let popupRect = this.panel.getBoundingClientRect();
+    // Calculate the extra height available on the screen above and below the
+    // menu panel. Use that to calculate the how much the sub-view may grow.
+    let popupRect = this.panel.getBoundingClientRect();
 
-      this.setBackground(this.background);
+    this.setBackground(this.background);
 
-      let win = this.window;
-      let popupBottom = win.mozInnerScreenY + popupRect.bottom;
-      let popupTop = win.mozInnerScreenY + popupRect.top;
+    let win = this.window;
+    let popupBottom = win.mozInnerScreenY + popupRect.bottom;
+    let popupTop = win.mozInnerScreenY + popupRect.top;
 
-      let screenBottom = win.screen.availTop + win.screen.availHeight;
-      this.extraHeight = {
-        bottom: Math.max(0, screenBottom - popupBottom),
-        top:  Math.max(0, popupTop - win.screen.availTop),
-      };
+    let screenBottom = win.screen.availTop + win.screen.availHeight;
+    this.extraHeight = {
+      bottom: Math.max(0, screenBottom - popupBottom),
+      top:  Math.max(0, popupTop - win.screen.availTop),
+    };
 
-      // Create a new browser in the real popup.
-      let browser = this.browser;
-      await this.createBrowser(this.viewNode);
+    // Create a new browser in the real popup.
+    let browser = this.browser;
+    await this.createBrowser(this.viewNode);
 
-      this.ignoreResizes = false;
+    this.ignoreResizes = false;
 
-      this.browser.swapDocShells(browser);
-      this.destroyBrowser(browser);
+    this.browser.swapDocShells(browser);
+    this.destroyBrowser(browser);
 
-      if (this.dimensions && !this.fixedWidth) {
-        this.resizeBrowser(this.dimensions);
-      }
+    if (this.dimensions && !this.fixedWidth) {
+      this.resizeBrowser(this.dimensions);
+    }
 
-      this.tempPanel.remove();
-      this.tempPanel = null;
+    this.tempPanel.remove();
+    this.tempPanel = null;
 
-      this.shown = true;
+    this.shown = true;
 
-      if (this.destroyed) {
-        this.closePopup();
-        this.destroy();
-        return false;
-      }
+    if (this.destroyed) {
+      this.closePopup();
+      this.destroy();
+      return false;
+    }
 
-      let event = new this.window.CustomEvent("WebExtPopupLoaded", {
-        bubbles: true,
-        detail: {extension: this.extension},
-      });
-      this.browser.dispatchEvent(event);
+    let event = new this.window.CustomEvent("WebExtPopupLoaded", {
+      bubbles: true,
+      detail: {extension: this.extension},
+    });
+    this.browser.dispatchEvent(event);
 
-      return true;
-    })();
+    return true;
   }
 
   destroy() {
     return super.destroy().then(() => {
       if (this.tempPanel) {
         this.tempPanel.remove();
         this.tempPanel = null;
       }
--- a/browser/components/extensions/ext-bookmarks.js
+++ b/browser/components/extensions/ext-bookmarks.js
@@ -171,30 +171,32 @@ function incrementListeners() {
     PlacesUtils.bookmarks.addObserver(observer);
   }
 }
 
 this.bookmarks = class extends ExtensionAPI {
   getAPI(context) {
     return {
       bookmarks: {
-        get: function(idOrIdList) {
+        async get(idOrIdList) {
           let list = Array.isArray(idOrIdList) ? idOrIdList : [idOrIdList];
 
-          return (async function() {
+          try {
             let bookmarks = [];
             for (let id of list) {
               let bookmark = await PlacesUtils.bookmarks.fetch({guid: id});
               if (!bookmark) {
                 throw new Error("Bookmark not found");
               }
               bookmarks.push(convert(bookmark));
             }
             return bookmarks;
-          })().catch(error => Promise.reject({message: error.message}));
+          } catch (error) {
+            return Promise.reject({message: error.message});
+          }
         },
 
         getChildren: function(id) {
           // TODO: We should optimize this.
           return getTree(id, true);
         },
 
         getTree: function() {
--- a/browser/components/extensions/ext-devtools.js
+++ b/browser/components/extensions/ext-devtools.js
@@ -30,38 +30,36 @@ let initDevTools;
  * the first time that it is accessed).
  *
  * @param {DevToolsExtensionPageContextParent} context
  *   A devtools extension proxy context.
  *
  * @returns {Promise<TabTarget>}
  *   The cloned devtools target associated to the context.
  */
-global.getDevToolsTargetForContext = (context) => {
-  return (async function asyncGetTabTarget() {
-    if (context.devToolsTarget) {
-      await context.devToolsTarget.makeRemote();
-      return context.devToolsTarget;
-    }
+global.getDevToolsTargetForContext = async (context) => {
+  if (context.devToolsTarget) {
+    await context.devToolsTarget.makeRemote();
+    return context.devToolsTarget;
+  }
 
-    if (!context.devToolsToolbox || !context.devToolsToolbox.target) {
-      throw new Error("Unable to get a TabTarget for a context not associated to any toolbox");
-    }
+  if (!context.devToolsToolbox || !context.devToolsToolbox.target) {
+    throw new Error("Unable to get a TabTarget for a context not associated to any toolbox");
+  }
 
-    if (!context.devToolsToolbox.target.isLocalTab) {
-      throw new Error("Unexpected target type: only local tabs are currently supported.");
-    }
-
-    const {TabTarget} = require("devtools/client/framework/target");
+  if (!context.devToolsToolbox.target.isLocalTab) {
+    throw new Error("Unexpected target type: only local tabs are currently supported.");
+  }
 
-    context.devToolsTarget = new TabTarget(context.devToolsToolbox.target.tab);
-    await context.devToolsTarget.makeRemote();
+  const {TabTarget} = require("devtools/client/framework/target");
 
-    return context.devToolsTarget;
-  })();
+  context.devToolsTarget = new TabTarget(context.devToolsToolbox.target.tab);
+  await context.devToolsTarget.makeRemote();
+
+  return context.devToolsTarget;
 };
 
 /**
  * Retrieve the devtools target for the devtools extension proxy context
  * (lazily cloned from the target of the toolbox associated to the context
  * the first time that it is accessed).
  *
  * @param {Toolbox} toolbox
@@ -112,47 +110,45 @@ class DevToolsPage extends HiddenExtensi
 
     this.unwatchExtensionProxyContextLoad = null;
 
     this.waitForTopLevelContext = new Promise(resolve => {
       this.resolveTopLevelContext = resolve;
     });
   }
 
-  build() {
-    return (async () => {
-      await this.createBrowserElement();
+  async build() {
+    await this.createBrowserElement();
 
-      // Listening to new proxy contexts.
-      this.unwatchExtensionProxyContextLoad = watchExtensionProxyContextLoad(this, context => {
-        // Keep track of the toolbox and target associated to the context, which is
-        // needed by the API methods implementation.
-        context.devToolsToolbox = this.toolbox;
+    // Listening to new proxy contexts.
+    this.unwatchExtensionProxyContextLoad = watchExtensionProxyContextLoad(this, context => {
+      // Keep track of the toolbox and target associated to the context, which is
+      // needed by the API methods implementation.
+      context.devToolsToolbox = this.toolbox;
 
-        if (!this.topLevelContext) {
-          this.topLevelContext = context;
+      if (!this.topLevelContext) {
+        this.topLevelContext = context;
 
-          // Ensure this devtools page is destroyed, when the top level context proxy is
-          // closed.
-          this.topLevelContext.callOnClose(this);
+        // Ensure this devtools page is destroyed, when the top level context proxy is
+        // closed.
+        this.topLevelContext.callOnClose(this);
 
-          this.resolveTopLevelContext(context);
-        }
-      });
+        this.resolveTopLevelContext(context);
+      }
+    });
 
-      extensions.emit("extension-browser-inserted", this.browser, {
-        devtoolsToolboxInfo: {
-          inspectedWindowTabId: getTargetTabIdForToolbox(this.toolbox),
-        },
-      });
+    extensions.emit("extension-browser-inserted", this.browser, {
+      devtoolsToolboxInfo: {
+        inspectedWindowTabId: getTargetTabIdForToolbox(this.toolbox),
+      },
+    });
 
-      this.browser.loadURI(this.url);
+    this.browser.loadURI(this.url);
 
-      await this.waitForTopLevelContext;
-    })();
+    await this.waitForTopLevelContext;
   }
 
   close() {
     if (this.closed) {
       throw new Error("Unable to shutdown a closed DevToolsPage instance");
     }
 
     this.closed = true;
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -311,79 +311,77 @@ this.ExtensionData = class {
       throw new Error("getURL may not be called before an `id` or `uuid` has been set");
     }
     if (!this.uuid) {
       this.uuid = UUIDMap.get(this.id);
     }
     return `moz-extension://${this.uuid}/${path}`;
   }
 
-  readDirectory(path) {
-    return (async () => {
-      if (this.rootURI instanceof Ci.nsIFileURL) {
-        let uri = NetUtil.newURI(this.rootURI.resolve("./" + path));
-        let fullPath = uri.QueryInterface(Ci.nsIFileURL).file.path;
+  async readDirectory(path) {
+    if (this.rootURI instanceof Ci.nsIFileURL) {
+      let uri = NetUtil.newURI(this.rootURI.resolve("./" + path));
+      let fullPath = uri.QueryInterface(Ci.nsIFileURL).file.path;
+
+      let iter = new OS.File.DirectoryIterator(fullPath);
+      let results = [];
 
-        let iter = new OS.File.DirectoryIterator(fullPath);
-        let results = [];
+      try {
+        await iter.forEach(entry => {
+          results.push(entry);
+        });
+      } catch (e) {
+        // Always return a list, even if the directory does not exist (or is
+        // not a directory) for symmetry with the ZipReader behavior.
+      }
+      iter.close();
+
+      return results;
+    }
+
+    // FIXME: We need a way to do this without main thread IO.
+
+    let uri = this.rootURI.QueryInterface(Ci.nsIJARURI);
 
-        try {
-          await iter.forEach(entry => {
-            results.push(entry);
+    let file = uri.JARFile.QueryInterface(Ci.nsIFileURL).file;
+    let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
+    zipReader.open(file);
+    try {
+      let results = [];
+
+      // Normalize the directory path.
+      path = `${uri.JAREntry}/${path}`;
+      path = path.replace(/\/\/+/g, "/").replace(/^\/|\/$/g, "") + "/";
+
+      // Escape pattern metacharacters.
+      let pattern = path.replace(/[[\]()?*~|$\\]/g, "\\$&");
+
+      let enumerator = zipReader.findEntries(pattern + "*");
+      while (enumerator.hasMore()) {
+        let name = enumerator.getNext();
+        if (!name.startsWith(path)) {
+          throw new Error("Unexpected ZipReader entry");
+        }
+
+        // The enumerator returns the full path of all entries.
+        // Trim off the leading path, and filter out entries from
+        // subdirectories.
+        name = name.slice(path.length);
+        if (name && !/\/./.test(name)) {
+          results.push({
+            name: name.replace("/", ""),
+            isDir: name.endsWith("/"),
           });
-        } catch (e) {
-          // Always return a list, even if the directory does not exist (or is
-          // not a directory) for symmetry with the ZipReader behavior.
         }
-        iter.close();
-
-        return results;
       }
 
-      // FIXME: We need a way to do this without main thread IO.
-
-      let uri = this.rootURI.QueryInterface(Ci.nsIJARURI);
-
-      let file = uri.JARFile.QueryInterface(Ci.nsIFileURL).file;
-      let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
-      zipReader.open(file);
-      try {
-        let results = [];
-
-        // Normalize the directory path.
-        path = `${uri.JAREntry}/${path}`;
-        path = path.replace(/\/\/+/g, "/").replace(/^\/|\/$/g, "") + "/";
-
-        // Escape pattern metacharacters.
-        let pattern = path.replace(/[[\]()?*~|$\\]/g, "\\$&");
-
-        let enumerator = zipReader.findEntries(pattern + "*");
-        while (enumerator.hasMore()) {
-          let name = enumerator.getNext();
-          if (!name.startsWith(path)) {
-            throw new Error("Unexpected ZipReader entry");
-          }
-
-          // The enumerator returns the full path of all entries.
-          // Trim off the leading path, and filter out entries from
-          // subdirectories.
-          name = name.slice(path.length);
-          if (name && !/\/./.test(name)) {
-            results.push({
-              name: name.replace("/", ""),
-              isDir: name.endsWith("/"),
-            });
-          }
-        }
-
-        return results;
-      } finally {
-        zipReader.close();
-      }
-    })();
+      return results;
+    } finally {
+      zipReader.close();
+    }
   }
 
   readJSON(path) {
     return new Promise((resolve, reject) => {
       let uri = this.rootURI.resolve(`./${path}`);
 
       NetUtil.asyncFetch({uri, loadUsingSystemPrincipal: true}, (inputStream, status) => {
         if (!Components.isSuccessCode(status)) {
@@ -564,30 +562,28 @@ this.ExtensionData = class {
   // Gecko-compatible variant. Currently, this means simply
   // replacing underscores with hyphens.
   normalizeLocaleCode(locale) {
     return locale.replace(/_/g, "-");
   }
 
   // Reads the locale file for the given Gecko-compatible locale code, and
   // stores its parsed contents in |this.localeMessages.get(locale)|.
-  readLocaleFile(locale) {
-    return (async () => {
-      let locales = await this.promiseLocales();
-      let dir = locales.get(locale) || locale;
-      let file = `_locales/${dir}/messages.json`;
+  async readLocaleFile(locale) {
+    let locales = await this.promiseLocales();
+    let dir = locales.get(locale) || locale;
+    let file = `_locales/${dir}/messages.json`;
 
-      try {
-        let messages = await this.readJSON(file);
-        return this.localeData.addLocale(locale, messages, this);
-      } catch (e) {
-        this.packagingError(`Loading locale file ${file}: ${e}`);
-        return new Map();
-      }
-    })();
+    try {
+      let messages = await this.readJSON(file);
+      return this.localeData.addLocale(locale, messages, this);
+    } catch (e) {
+      this.packagingError(`Loading locale file ${file}: ${e}`);
+      return new Map();
+    }
   }
 
   // Reads the list of locales available in the extension, and returns a
   // Promise which resolves to a Map upon completion.
   // Each map key is a Gecko-compatible locale code, and each value is the
   // "_locales" subdirectory containing that locale:
   //
   // Map(gecko-locale-code -> locale-directory-name)
@@ -616,65 +612,61 @@ this.ExtensionData = class {
 
     return this._promiseLocales;
   }
 
   // Reads the locale messages for all locales, and returns a promise which
   // resolves to a Map of locale messages upon completion. Each key in the map
   // is a Gecko-compatible locale code, and each value is a locale data object
   // as returned by |readLocaleFile|.
-  initAllLocales() {
-    return (async () => {
-      let locales = await this.promiseLocales();
+  async initAllLocales() {
+    let locales = await this.promiseLocales();
 
-      await Promise.all(Array.from(locales.keys(),
-                                   locale => this.readLocaleFile(locale)));
+    await Promise.all(Array.from(locales.keys(),
+                                 locale => this.readLocaleFile(locale)));
 
-      let defaultLocale = this.defaultLocale;
-      if (defaultLocale) {
-        if (!locales.has(defaultLocale)) {
-          this.manifestError('Value for "default_locale" property must correspond to ' +
-                             'a directory in "_locales/". Not found: ' +
-                             JSON.stringify(`_locales/${this.manifest.default_locale}/`));
-        }
-      } else if (locales.size) {
-        this.manifestError('The "default_locale" property is required when a ' +
-                           '"_locales/" directory is present.');
+    let defaultLocale = this.defaultLocale;
+    if (defaultLocale) {
+      if (!locales.has(defaultLocale)) {
+        this.manifestError('Value for "default_locale" property must correspond to ' +
+                           'a directory in "_locales/". Not found: ' +
+                           JSON.stringify(`_locales/${this.manifest.default_locale}/`));
       }
+    } else if (locales.size) {
+      this.manifestError('The "default_locale" property is required when a ' +
+                         '"_locales/" directory is present.');
+    }
 
-      return this.localeData.messages;
-    })();
+    return this.localeData.messages;
   }
 
   // Reads the locale file for the given Gecko-compatible locale code, or the
   // default locale if no locale code is given, and sets it as the currently
   // selected locale on success.
   //
   // Pre-loads the default locale for fallback message processing, regardless
   // of the locale specified.
   //
   // If no locales are unavailable, resolves to |null|.
-  initLocale(locale = this.defaultLocale) {
-    return (async () => {
-      if (locale == null) {
-        return null;
-      }
+  async initLocale(locale = this.defaultLocale) {
+    if (locale == null) {
+      return null;
+    }
 
-      let promises = [this.readLocaleFile(locale)];
+    let promises = [this.readLocaleFile(locale)];
 
-      let {defaultLocale} = this;
-      if (locale != defaultLocale && !this.localeData.has(defaultLocale)) {
-        promises.push(this.readLocaleFile(defaultLocale));
-      }
+    let {defaultLocale} = this;
+    if (locale != defaultLocale && !this.localeData.has(defaultLocale)) {
+      promises.push(this.readLocaleFile(defaultLocale));
+    }
 
-      let results = await Promise.all(promises);
+    let results = await Promise.all(promises);
 
-      this.localeData.selectedLocale = locale;
-      return results[0];
-    })();
+    this.localeData.selectedLocale = locale;
+    return results[0];
   }
 };
 
 const PROXIED_EVENTS = new Set(["test-harness-message", "add-permissions", "remove-permissions"]);
 
 // We create one instance of this class per extension. |addonData|
 // comes directly from bootstrap.js when initializing.
 this.Extension = class extends ExtensionData {
--- a/toolkit/components/extensions/ExtensionStorageSync.jsm
+++ b/toolkit/components/extensions/ExtensionStorageSync.jsm
@@ -169,86 +169,80 @@ const getKBHash = async function(fxaServ
 /**
  * A "remote transformer" that the Kinto library will use to
  * encrypt/decrypt records when syncing.
  *
  * This is an "abstract base class". Subclass this and override
  * getKeys() to use it.
  */
 class EncryptionRemoteTransformer {
-  encode(record) {
-    const self = this;
-    return (async function() {
-      const keyBundle = await self.getKeys();
-      if (record.ciphertext) {
-        throw new Error("Attempt to reencrypt??");
-      }
-      let id = await self.getEncodedRecordId(record);
-      if (!id) {
-        throw new Error("Record ID is missing or invalid");
-      }
+  async encode(record) {
+    const keyBundle = await this.getKeys();
+    if (record.ciphertext) {
+      throw new Error("Attempt to reencrypt??");
+    }
+    let id = await this.getEncodedRecordId(record);
+    if (!id) {
+      throw new Error("Record ID is missing or invalid");
+    }
 
-      let IV = Svc.Crypto.generateRandomIV();
-      let ciphertext = Svc.Crypto.encrypt(JSON.stringify(record),
-                                          keyBundle.encryptionKeyB64, IV);
-      let hmac = ciphertextHMAC(keyBundle, id, IV, ciphertext);
-      const encryptedResult = {ciphertext, IV, hmac, id};
+    let IV = Svc.Crypto.generateRandomIV();
+    let ciphertext = Svc.Crypto.encrypt(JSON.stringify(record),
+                                        keyBundle.encryptionKeyB64, IV);
+    let hmac = ciphertextHMAC(keyBundle, id, IV, ciphertext);
+    const encryptedResult = {ciphertext, IV, hmac, id};
 
-      // Copy over the _status field, so that we handle concurrency
-      // headers (If-Match, If-None-Match) correctly.
-      // DON'T copy over "deleted" status, because then we'd leak
-      // plaintext deletes.
-      encryptedResult._status = record._status == "deleted" ? "updated" : record._status;
-      if (record.hasOwnProperty("last_modified")) {
-        encryptedResult.last_modified = record.last_modified;
-      }
+    // Copy over the _status field, so that we handle concurrency
+    // headers (If-Match, If-None-Match) correctly.
+    // DON'T copy over "deleted" status, because then we'd leak
+    // plaintext deletes.
+    encryptedResult._status = record._status == "deleted" ? "updated" : record._status;
+    if (record.hasOwnProperty("last_modified")) {
+      encryptedResult.last_modified = record.last_modified;
+    }
 
-      return encryptedResult;
-    })();
+    return encryptedResult;
   }
 
-  decode(record) {
-    const self = this;
-    return (async function() {
-      if (!record.ciphertext) {
-        // This can happen for tombstones if a record is deleted.
-        if (record.deleted) {
-          return record;
-        }
-        throw new Error("No ciphertext: nothing to decrypt?");
+  async decode(record) {
+    if (!record.ciphertext) {
+      // This can happen for tombstones if a record is deleted.
+      if (record.deleted) {
+        return record;
       }
-      const keyBundle = await self.getKeys();
-      // Authenticate the encrypted blob with the expected HMAC
-      let computedHMAC = ciphertextHMAC(keyBundle, record.id, record.IV, record.ciphertext);
+      throw new Error("No ciphertext: nothing to decrypt?");
+    }
+    const keyBundle = await this.getKeys();
+    // Authenticate the encrypted blob with the expected HMAC
+    let computedHMAC = ciphertextHMAC(keyBundle, record.id, record.IV, record.ciphertext);
 
-      if (computedHMAC != record.hmac) {
-        Utils.throwHMACMismatch(record.hmac, computedHMAC);
-      }
+    if (computedHMAC != record.hmac) {
+      Utils.throwHMACMismatch(record.hmac, computedHMAC);
+    }
 
-      // Handle invalid data here. Elsewhere we assume that cleartext is an object.
-      let cleartext = Svc.Crypto.decrypt(record.ciphertext,
-                                         keyBundle.encryptionKeyB64, record.IV);
-      let jsonResult = JSON.parse(cleartext);
-      if (!jsonResult || typeof jsonResult !== "object") {
-        throw new Error("Decryption failed: result is <" + jsonResult + ">, not an object.");
-      }
+    // Handle invalid data here. Elsewhere we assume that cleartext is an object.
+    let cleartext = Svc.Crypto.decrypt(record.ciphertext,
+                                       keyBundle.encryptionKeyB64, record.IV);
+    let jsonResult = JSON.parse(cleartext);
+    if (!jsonResult || typeof jsonResult !== "object") {
+      throw new Error("Decryption failed: result is <" + jsonResult + ">, not an object.");
+    }
 
-      if (record.hasOwnProperty("last_modified")) {
-        jsonResult.last_modified = record.last_modified;
-      }
+    if (record.hasOwnProperty("last_modified")) {
+      jsonResult.last_modified = record.last_modified;
+    }
 
-      // _status: deleted records were deleted on a client, but
-      // uploaded as an encrypted blob so we don't leak deletions.
-      // If we get such a record, flag it as deleted.
-      if (jsonResult._status == "deleted") {
-        jsonResult.deleted = true;
-      }
+    // _status: deleted records were deleted on a client, but
+    // uploaded as an encrypted blob so we don't leak deletions.
+    // If we get such a record, flag it as deleted.
+    if (jsonResult._status == "deleted") {
+      jsonResult.deleted = true;
+    }
 
-      return jsonResult;
-    })();
+    return jsonResult;
   }
 
   /**
    * Retrieve keys to use during encryption.
    *
    * Returns a Promise<KeyBundle>.
    */
   getKeys() {
@@ -304,23 +298,20 @@ class KeyRingEncryptionRemoteTransformer
       bundle.keyPair = [keyMaterial.slice(0, 32), keyMaterial.slice(32, 64)];
       return bundle;
     })();
   }
   // Pass through the kbHash field from the unencrypted record. If
   // encryption fails, we can use this to try to detect whether we are
   // being compromised or if the record here was encoded with a
   // different kB.
-  encode(record) {
-    const encodePromise = super.encode(record);
-    return (async function() {
-      const encoded = await encodePromise;
-      encoded.kbHash = record.kbHash;
-      return encoded;
-    })();
+  async encode(record) {
+    const encoded = await super.encode(record);
+    encoded.kbHash = record.kbHash;
+    return encoded;
   }
 
   async decode(record) {
     if (record === null) {
       // XXX: This is a hack that detects a situation that should
       // never happen by using a technique that shouldn't actually
       // work. See
       // https://bugzilla.mozilla.org/show_bug.cgi?id=1359879 for
@@ -675,28 +666,25 @@ this.CryptoCollection = CryptoCollection
    */
 let CollectionKeyEncryptionRemoteTransformer = class extends EncryptionRemoteTransformer {
   constructor(cryptoCollection, extensionId) {
     super();
     this.cryptoCollection = cryptoCollection;
     this.extensionId = extensionId;
   }
 
-  getKeys() {
-    const self = this;
-    return (async function() {
-      // FIXME: cache the crypto record for the duration of a sync cycle?
-      const collectionKeys = await self.cryptoCollection.getKeyRing();
-      if (!collectionKeys.hasKeysFor([self.extensionId])) {
-        // This should never happen. Keys should be created (and
-        // synced) at the beginning of the sync cycle.
-        throw new Error(`tried to encrypt records for ${this.extensionId}, but key is not present`);
-      }
-      return collectionKeys.keyForCollection(self.extensionId);
-    })();
+  async getKeys() {
+    // FIXME: cache the crypto record for the duration of a sync cycle?
+    const collectionKeys = await this.cryptoCollection.getKeyRing();
+    if (!collectionKeys.hasKeysFor([this.extensionId])) {
+      // This should never happen. Keys should be created (and
+      // synced) at the beginning of the sync cycle.
+      throw new Error(`tried to encrypt records for ${this.extensionId}, but key is not present`);
+    }
+    return collectionKeys.keyForCollection(this.extensionId);
   }
 
   getEncodedRecordId(record) {
     // It isn't really clear whether kinto.js record IDs are
     // bytestrings or strings that happen to only contain ASCII
     // characters, so encode them to be sure.
     const id = CommonUtils.encodeUTF8(record.id);
     // Like extensionIdToCollectionId, the rules about Kinto record
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2082,31 +2082,29 @@ 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 (async function() {
-    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
-    }
-  })();
+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
+  }
 }
 
 /**
  * Recursively removes a directory or file fixing permissions when necessary.
  *
  * @param  aFile
  *         The nsIFile to remove
  */
@@ -5940,120 +5938,118 @@ class AddonInstall {
    * 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(file) {
-    return (async () => {
-      let zipreader = Cc["@mozilla.org/libjar/zip-reader;1"].
-          createInstance(Ci.nsIZipReader);
-      try {
-        zipreader.open(file);
-      } catch (e) {
+  async loadManifest(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 = await loadManifestFromZipReader(zipreader, this.installLocation);
+    } catch (e) {
+      zipreader.close();
+      return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
+    }
+
+    if (!this.addon.id) {
+      let err = new Error(`Cannot find id for addon ${file.path}`);
+      return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, err]);
+    }
+
+    if (this.existingAddon) {
+      // Check various conditions related to upgrades
+      if (this.addon.id != this.existingAddon.id) {
         zipreader.close();
-        return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
-      }
-
-      try {
-        // loadManifestFromZipReader performs the certificate verification for us
-        this.addon = await loadManifestFromZipReader(zipreader, this.installLocation);
-      } catch (e) {
+        return Promise.reject([AddonManager.ERROR_INCORRECT_ID,
+                               `Refusing to upgrade addon ${this.existingAddon.id} to different ID ${this.addon.id}`]);
+      }
+
+      if (isWebExtension(this.existingAddon.type) && !isWebExtension(this.addon.type)) {
         zipreader.close();
-        return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, e]);
-      }
-
-      if (!this.addon.id) {
-        let err = new Error(`Cannot find id for addon ${file.path}`);
-        return Promise.reject([AddonManager.ERROR_CORRUPT_FILE, err]);
-      }
-
-      if (this.existingAddon) {
-        // Check various conditions related to upgrades
-        if (this.addon.id != this.existingAddon.id) {
-          zipreader.close();
-          return Promise.reject([AddonManager.ERROR_INCORRECT_ID,
-                                 `Refusing to upgrade addon ${this.existingAddon.id} to different ID ${this.addon.id}`]);
-        }
-
-        if (isWebExtension(this.existingAddon.type) && !isWebExtension(this.addon.type)) {
+        return Promise.reject([AddonManager.ERROR_UNEXPECTED_ADDON_TYPE,
+                               "WebExtensions may not be upated to other extension types"]);
+      }
+    }
+
+    if (mustSign(this.addon.type)) {
+      if (this.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
+        // This add-on isn't properly signed by a signature that chains to the
+        // trusted root.
+        let state = this.addon.signedState;
+        this.addon = null;
+        zipreader.close();
+
+        if (state == AddonManager.SIGNEDSTATE_MISSING)
+          return Promise.reject([AddonManager.ERROR_SIGNEDSTATE_REQUIRED,
+                                 "signature is required but missing"])
+
+        return Promise.reject([AddonManager.ERROR_CORRUPT_FILE,
+                               "signature verification failed"])
+      }
+    } else if (this.addon.signedState == AddonManager.SIGNEDSTATE_UNKNOWN ||
+             this.addon.signedState == AddonManager.SIGNEDSTATE_NOT_REQUIRED) {
+      // Check object signing certificate, if any
+      let x509 = zipreader.getSigningCert(null);
+      if (x509) {
+        logger.debug("Verifying XPI signature");
+        if (verifyZipSigning(zipreader, x509)) {
+          this.certificate = x509;
+          if (this.certificate.commonName.length > 0) {
+            this.certName = this.certificate.commonName;
+          } else {
+            this.certName = this.certificate.organization;
+          }
+        } else {
           zipreader.close();
-          return Promise.reject([AddonManager.ERROR_UNEXPECTED_ADDON_TYPE,
-                                 "WebExtensions may not be upated to other extension types"]);
-        }
-      }
-
-      if (mustSign(this.addon.type)) {
-        if (this.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
-          // This add-on isn't properly signed by a signature that chains to the
-          // trusted root.
-          let state = this.addon.signedState;
-          this.addon = null;
-          zipreader.close();
-
-          if (state == AddonManager.SIGNEDSTATE_MISSING)
-            return Promise.reject([AddonManager.ERROR_SIGNEDSTATE_REQUIRED,
-                                   "signature is required but missing"])
-
           return Promise.reject([AddonManager.ERROR_CORRUPT_FILE,
-                                 "signature verification failed"])
-        }
-      } else if (this.addon.signedState == AddonManager.SIGNEDSTATE_UNKNOWN ||
-               this.addon.signedState == AddonManager.SIGNEDSTATE_NOT_REQUIRED) {
-        // Check object signing certificate, if any
-        let x509 = zipreader.getSigningCert(null);
-        if (x509) {
-          logger.debug("Verifying XPI signature");
-          if (verifyZipSigning(zipreader, x509)) {
-            this.certificate = x509;
-            if (this.certificate.commonName.length > 0) {
-              this.certName = this.certificate.commonName;
-            } else {
-              this.certName = this.certificate.organization;
-            }
-          } else {
-            zipreader.close();
-            return Promise.reject([AddonManager.ERROR_CORRUPT_FILE,
-                                   "XPI is incorrectly signed"]);
-          }
+                                 "XPI is incorrectly signed"]);
         }
       }
-
-      zipreader.close();
-
-      this.updateAddonURIs();
-
-      this.addon._install = this;
-      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 = await new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
-
-      // It wasn't there so try to re-download it
-      if (!repoAddon) {
-        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;
-    })();
+    }
+
+    zipreader.close();
+
+    this.updateAddonURIs();
+
+    this.addon._install = this;
+    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 = await new Promise(resolve => AddonRepository.getCachedAddonByID(this.addon.id, resolve));
+
+    // It wasn't there so try to re-download it
+    if (!repoAddon) {
+      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;
   }
 
   getIcon(desiredSize = 64) {
     if (!this.addon.icons || !this.file) {
       return null;
     }
 
     let {icon} = IconDetails.getPreferredIcon(this.addon.icons, null, desiredSize);
@@ -6290,54 +6286,52 @@ class AddonInstall {
       this.removeTemporaryFile();
       return this.installLocation.releaseStagingDir();
     });
   }
 
   /**
    * Stages an upgrade for next application restart.
    */
-  stageInstall(restartRequired, stagedAddon, isUpgrade) {
-    return (async () => {
-      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;
-        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";
-
-        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));
-
-        logger.debug("Staged install of " + this.addon.id + " from " + this.sourceURI.spec + " ready; waiting for restart.");
-        if (isUpgrade) {
-          delete this.existingAddon.pendingUpgrade;
-          this.existingAddon.pendingUpgrade = this.addon;
-        }
-      }
-
-      return installedUnpacked;
-    })();
+  async stageInstall(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;
+      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";
+
+      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));
+
+      logger.debug("Staged install of " + this.addon.id + " from " + this.sourceURI.spec + " ready; waiting for restart.");
+      if (isUpgrade) {
+        delete this.existingAddon.pendingUpgrade;
+        this.existingAddon.pendingUpgrade = this.addon;
+      }
+    }
+
+    return installedUnpacked;
   }
 
   /**
    * Removes any previously staged upgrade.
    */
   async unstageInstall(stagedAddon) {
     let stagedJSON = getFile(`${this.addon.id}.json`, stagedAddon);
     if (stagedJSON.exists()) {
@@ -6399,105 +6393,103 @@ class AddonInstall {
 
 class LocalAddonInstall extends AddonInstall {
   /**
    * Initialises this install to be an install from a local file.
    *
    * @returns Promise
    *          A Promise that resolves when the object is ready to use.
    */
-  init() {
-    return (async () => {
-      this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file;
-
-      if (!this.file.exists()) {
-        logger.warn("XPI file " + this.file.path + " does not exist");
+  async init() {
+    this.file = this.sourceURI.QueryInterface(Ci.nsIFileURL).file;
+
+    if (!this.file.exists()) {
+      logger.warn("XPI file " + this.file.path + " does not exist");
+      this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+      this.error = AddonManager.ERROR_NETWORK_FAILURE;
+      XPIProvider.removeActiveInstall(this);
+      return;
+    }
+
+    this.state = AddonManager.STATE_DOWNLOADED;
+    this.progress = this.file.fileSize;
+    this.maxProgress = this.file.fileSize;
+
+    if (this.hash) {
+      let crypto = Cc["@mozilla.org/security/hash;1"].
+          createInstance(Ci.nsICryptoHash);
+      try {
+        crypto.initWithString(this.hash.algorithm);
+      } catch (e) {
+        logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
         this.state = AddonManager.STATE_DOWNLOAD_FAILED;
-        this.error = AddonManager.ERROR_NETWORK_FAILURE;
+        this.error = AddonManager.ERROR_INCORRECT_HASH;
+        XPIProvider.removeActiveInstall(this);
+        return;
+      }
+
+      let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+          createInstance(Ci.nsIFileInputStream);
+      fis.init(this.file, -1, -1, false);
+      crypto.updateFromStream(fis, this.file.fileSize);
+      let calculatedHash = getHashStringForCrypto(crypto);
+      if (calculatedHash != this.hash.data) {
+        logger.warn("File hash (" + calculatedHash + ") did not match provided hash (" +
+                    this.hash.data + ")");
+        this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+        this.error = AddonManager.ERROR_INCORRECT_HASH;
         XPIProvider.removeActiveInstall(this);
         return;
       }
-
-      this.state = AddonManager.STATE_DOWNLOADED;
-      this.progress = this.file.fileSize;
-      this.maxProgress = this.file.fileSize;
-
-      if (this.hash) {
-        let crypto = Cc["@mozilla.org/security/hash;1"].
-            createInstance(Ci.nsICryptoHash);
-        try {
-          crypto.initWithString(this.hash.algorithm);
-        } catch (e) {
-          logger.warn("Unknown hash algorithm '" + this.hash.algorithm + "' for addon " + this.sourceURI.spec, e);
-          this.state = AddonManager.STATE_DOWNLOAD_FAILED;
-          this.error = AddonManager.ERROR_INCORRECT_HASH;
-          XPIProvider.removeActiveInstall(this);
-          return;
-        }
-
-        let fis = Cc["@mozilla.org/network/file-input-stream;1"].
-            createInstance(Ci.nsIFileInputStream);
-        fis.init(this.file, -1, -1, false);
-        crypto.updateFromStream(fis, this.file.fileSize);
-        let calculatedHash = getHashStringForCrypto(crypto);
-        if (calculatedHash != this.hash.data) {
-          logger.warn("File hash (" + calculatedHash + ") did not match provided hash (" +
-                      this.hash.data + ")");
-          this.state = AddonManager.STATE_DOWNLOAD_FAILED;
-          this.error = AddonManager.ERROR_INCORRECT_HASH;
-          XPIProvider.removeActiveInstall(this);
-          return;
-        }
-      }
-
-      try {
-        await this.loadManifest(this.file);
-      } catch ([error, message]) {
-        logger.warn("Invalid XPI", message);
-        this.state = AddonManager.STATE_DOWNLOAD_FAILED;
-        this.error = error;
-        XPIProvider.removeActiveInstall(this);
-        AddonManagerPrivate.callInstallListeners("onNewInstall",
-                                                 this.listeners,
-                                                 this.wrapper);
-        flushJarCache(this.file);
-        return;
-      }
-
-      let addon = await new Promise(resolve => {
-        XPIDatabase.getVisibleAddonForID(this.addon.id, resolve);
+    }
+
+    try {
+      await this.loadManifest(this.file);
+    } catch ([error, message]) {
+      logger.warn("Invalid XPI", message);
+      this.state = AddonManager.STATE_DOWNLOAD_FAILED;
+      this.error = error;
+      XPIProvider.removeActiveInstall(this);
+      AddonManagerPrivate.callInstallListeners("onNewInstall",
+                                               this.listeners,
+                                               this.wrapper);
+      flushJarCache(this.file);
+      return;
+    }
+
+    let addon = await new Promise(resolve => {
+      XPIDatabase.getVisibleAddonForID(this.addon.id, resolve);
+    });
+
+    this.existingAddon = addon;
+    if (addon)
+      applyBlocklistChanges(addon, this.addon);
+    this.addon.updateDate = Date.now();
+    this.addon.installDate = addon ? addon.installDate : this.addon.updateDate;
+
+    if (!this.addon.isCompatible) {
+      this.state = AddonManager.STATE_CHECKING;
+
+      await new Promise(resolve => {
+        new UpdateChecker(this.addon, {
+          onUpdateFinished: aAddon => {
+            this.state = AddonManager.STATE_DOWNLOADED;
+            AddonManagerPrivate.callInstallListeners("onNewInstall",
+                                                     this.listeners,
+                                                     this.wrapper);
+            resolve();
+          }
+        }, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
       });
-
-      this.existingAddon = addon;
-      if (addon)
-        applyBlocklistChanges(addon, this.addon);
-      this.addon.updateDate = Date.now();
-      this.addon.installDate = addon ? addon.installDate : this.addon.updateDate;
-
-      if (!this.addon.isCompatible) {
-        this.state = AddonManager.STATE_CHECKING;
-
-        await new Promise(resolve => {
-          new UpdateChecker(this.addon, {
-            onUpdateFinished: aAddon => {
-              this.state = AddonManager.STATE_DOWNLOADED;
-              AddonManagerPrivate.callInstallListeners("onNewInstall",
-                                                       this.listeners,
-                                                       this.wrapper);
-              resolve();
-            }
-          }, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
-        });
-      } else {
-        AddonManagerPrivate.callInstallListeners("onNewInstall",
-                                                 this.listeners,
-                                                 this.wrapper);
-
-      }
-    })();
+    } else {
+      AddonManagerPrivate.callInstallListeners("onNewInstall",
+                                               this.listeners,
+                                               this.wrapper);
+
+    }
   }
 
   install() {
     if (this.state == AddonManager.STATE_DOWNLOAD_FAILED) {
       // For a local install, this state means that verification of the
       // file failed (e.g., the hash or signature or manifest contents
       // were invalid).  It doesn't make sense to retry anything in this
       // case but we have callers who don't know if their AddonInstall