Bug 1353194 Streamline the startup extension compatibility check r=kmag
authorAndrew Swan <aswan@mozilla.com>
Sun, 10 Sep 2017 12:23:45 -0700
changeset 662266 005cddf9e02038e5fd6c5e1590f6795df6648fe9
parent 662265 2a70f67c7c655b66c63655c7c7c68b57a1d93abb
child 662267 5b3b0c446caa3cb7f23c072487d6353561c25d2c
push id79014
push userbmo:bpostelnicu@mozilla.com
push dateMon, 11 Sep 2017 09:58:20 +0000
reviewerskmag
bugs1353194
milestone57.0a1
Bug 1353194 Streamline the startup extension compatibility check r=kmag Also extend activeAddons records with a started flag to avoid double-starting extensions that are upgraded during the startup check. MozReview-Commit-ID: FPX71Q3lSrw
toolkit/locales/en-US/chrome/mozapps/extensions/update.dtd
toolkit/locales/en-US/chrome/mozapps/extensions/update.properties
toolkit/locales/jar.mn
toolkit/mozapps/extensions/content/update.css
toolkit/mozapps/extensions/content/update.html
toolkit/mozapps/extensions/content/update.js
toolkit/mozapps/extensions/content/update.xul
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/jar.mn
toolkit/themes/osx/mozapps/extensions/update.css
toolkit/themes/osx/mozapps/jar.mn
toolkit/themes/shared/non-mac.jar.inc.mn
toolkit/themes/windows/mozapps/extensions/update.css
deleted file mode 100644
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/update.dtd
+++ /dev/null
@@ -1,65 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!ENTITY  updateWizard.title              "&brandShortName; Update">
-
-<!ENTITY  offline.title                   "&brandShortName; is working offline">
-<!ENTITY  offline.description             "&brandShortName; needs to go online in order to see if updates
-                                           are available for your add-ons to make them compatible with this
-                                           version.">
-<!ENTITY  offline.toggleOffline.label     "Go online now.">
-<!ENTITY  offline.toggleOffline.accesskey "G">
-
-<!ENTITY  mismatch.win.title              "Incompatible Add-ons">
-<!ENTITY  mismatch.top.label              "The following add-ons are not compatible with this version of
-                                           &brandShortName; and have been disabled:">
-<!ENTITY  mismatch.bottom.label           "&brandShortName; can check if there are compatible versions
-                                           of these add-ons available.">
-
-<!ENTITY  checking.wizard.title           "Checking for Compatible Add-ons">
-<!ENTITY  checking.top.label              "Checking your incompatible add-ons for updates…">
-<!ENTITY  checking.status                 "This may take a few minutes…">
-
-<!ENTITY  found.wizard.title               "Found Compatible Add-ons">
-<!ENTITY  found.top.label                 "Select the add-ons you would like to install:">
-<!ENTITY  found.disabledXPinstall.label   "These updates can’t be installed because software installation is currently
-                                           disabled. You can change this setting below.">
-<!ENTITY  found.enableXPInstall.label     "Allow websites to install software">
-<!ENTITY  found.enableXPInstall.accesskey "A">
-
-<!ENTITY  installing.wizard.title         "Installing Compatible Add-ons">
-<!ENTITY  installing.top.label            "Downloading and installing updates to your add-ons…">
-
-<!ENTITY  noupdates.wizard.title          "No Compatible Add-ons Found">
-<!ENTITY  noupdates.intro.desc            "&brandShortName; was unable to find updates to your
-                                           incompatible add-ons.">
-<!ENTITY  noupdates.error.desc            "Some problems were encountered when trying to find updates.">
-<!ENTITY  noupdates.checkEnabled.desc     "&brandShortName; will check periodically and inform you
-                                           when compatible updates for these add-ons are found.">
-
-<!ENTITY  finished.wizard.title           "Compatible Add-ons Installed">
-<!ENTITY  finished.top.label              "&brandShortName; has installed the updates to your add-ons.">
-<!ENTITY  finished.checkDisabled.desc     "&brandShortName; can check periodically and inform you
-                                           when updates for add-ons are found.">
-<!ENTITY  finished.checkEnabled.desc      "&brandShortName; will check periodically and inform you
-                                           when updates for add-ons are found.">
-
-<!ENTITY  adminDisabled.wizard.title      "Unable to Check for Updates">
-<!ENTITY  adminDisabled.warning.label     "It is not possible to check for updates to incompatible add-ons
-                                           because software installation for &brandShortName; has been disabled.
-                                           Please contact your System Administrator for assistance.">
-
-<!ENTITY  versioninfo.wizard.title        "Checking Compatibility of Add-ons">
-<!ENTITY  versioninfo.top.label           "Checking your add-ons for compatibility with this
-                                           version of &brandShortName;.">
-<!ENTITY  versioninfo.waiting             "This may take a few minutes…">
-
-<!ENTITY  installerrors.wizard.title      "Problems Installing Updates">
-<!ENTITY  installerrors.intro.label       "&brandShortName; encountered problems when updating
-                                           some of your add-ons.">
-
-<!-- general strings used by several of the finish pages -->
-<!ENTITY  clickFinish.label               "Click Finish to continue starting &brandShortName;.">
-<!ENTITY  clickFinish.labelMac            "Click Done to continue starting &brandShortName;.">
-<!ENTITY  enableChecking.label            "Allow &brandShortName; to check for updates.">
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/update.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/update.properties
@@ -1,21 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-mismatchCheckNow=Check Now
-mismatchCheckNowAccesskey=C
-mismatchDontCheck=Don’t Check
-mismatchDontCheckAccesskey=D
-installButtonText=Install Now
-installButtonTextAccesskey=I
-nextButtonText=Next >
-nextButtonTextAccesskey=N
-cancelButtonText=Cancel
-cancelButtonTextAccesskey=C
-statusPrefix=Finished checking %S
-downloadingPrefix=Downloading: %S
-installingPrefix=Installing: %S
-closeButton=Close
-installErrors=%S was unable to install updates for the following add-ons:
-checkingErrors=%S was unable to check for updates for the following add-ons:
-installErrorItemFormat=%S (%S)
+# LOCALIZATION NOTE (addonUpdateHeader)
+# %S will be replace with the localized name of the application
+addonUpdateTitle=%S Update
+
+# LOCALIZATION NOTE (addonUpdateMessage)
+# %S will be replace with the localized name of the application
+addonUpdateMessage=%S is updating your extensions…
+
+addonUpdateCancelMessage=Still updating. Want to wait?
+
+# LOCALIZATION NOTE (addonUpdateCancelButton)
+# %S will be replace with the localized name of the application
+addonUpdateCancelButton=Stop update and launch %S
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -105,17 +105,16 @@
   locale/@AB_CD@/mozapps/downloads/unknownContentType.dtd         (%chrome/mozapps/downloads/unknownContentType.dtd)
   locale/@AB_CD@/mozapps/downloads/settingsChange.dtd             (%chrome/mozapps/downloads/settingsChange.dtd)
   locale/@AB_CD@/mozapps/downloads/downloads.properties           (%chrome/mozapps/downloads/downloads.properties)
   locale/@AB_CD@/mozapps/extensions/extensions.dtd                (%chrome/mozapps/extensions/extensions.dtd)
 #ifndef MOZ_FENNEC
   locale/@AB_CD@/mozapps/extensions/extensions.properties         (%chrome/mozapps/extensions/extensions.properties)
   locale/@AB_CD@/mozapps/extensions/blocklist.dtd                 (%chrome/mozapps/extensions/blocklist.dtd)
   locale/@AB_CD@/mozapps/extensions/about.dtd                     (%chrome/mozapps/extensions/about.dtd)
-  locale/@AB_CD@/mozapps/extensions/update.dtd                    (%chrome/mozapps/extensions/update.dtd)
   locale/@AB_CD@/mozapps/extensions/update.properties             (%chrome/mozapps/extensions/update.properties)
   locale/@AB_CD@/mozapps/extensions/newaddon.dtd                  (%chrome/mozapps/extensions/newaddon.dtd)
   locale/@AB_CD@/mozapps/extensions/newaddon.properties           (%chrome/mozapps/extensions/newaddon.properties)
 #endif
   locale/@AB_CD@/mozapps/handling/handling.dtd                    (%chrome/mozapps/handling/handling.dtd)
   locale/@AB_CD@/mozapps/handling/handling.properties             (%chrome/mozapps/handling/handling.properties)
   locale/@AB_CD@/mozapps/preferences/changemp.dtd                 (%chrome/mozapps/preferences/changemp.dtd)
   locale/@AB_CD@/mozapps/preferences/removemp.dtd                 (%chrome/mozapps/preferences/removemp.dtd)
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/update.css
@@ -0,0 +1,26 @@
+body {
+  font: message-box;
+  min-width: 480px;
+}
+
+#message {
+  font-size: 14px;
+}
+
+#message, #cancel-section {
+  margin: 10px 5px;
+}
+
+#progress {
+  width: calc(100% - 10px);
+  margin: 0 5px;
+}
+
+#cancel-section {
+  display: flex;
+  justify-content: space-between;
+}
+
+#cancel-message {
+  vertical-align: middle;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/update.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <script src="update.js"></script>
+    <link rel="stylesheet"  href="chrome://mozapps/content/extensions/update.css">
+  </head>
+  <body>
+    <div>
+      <div id="message"></div>
+
+      <progress id="progress" val="0" max="1"></progress>
+
+      <div id="cancel-section">
+        <span id="cancel-message"></span>
+        <button id="cancel-btn"></button>
+      </div>
+    </div>
+  </body>
+</html>
--- a/toolkit/mozapps/extensions/content/update.js
+++ b/toolkit/mozapps/extensions/content/update.js
@@ -1,633 +1,24 @@
-// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
-
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// This UI is only opened from the Extension Manager when the app is upgraded.
-
 "use strict";
 
-/* exported gAdminDisabledPage, gFinishedPage, gFoundPage, gInstallErrorsPage,
- *          gNoUpdatesPage, gOfflinePage, gUpdatePage */
-
-const PREF_UPDATE_EXTENSIONS_ENABLED            = "extensions.update.enabled";
-const PREF_XPINSTALL_ENABLED                    = "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, "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.
-  addons: [],
-  // Contains a Set of IDs for add-on that were disabled by the application update.
-  affectedAddonIDs: null,
-  // The add-ons that we found updates available for
-  addonsToUpdate: [],
-  shouldSuggestAutoChecking: false,
-  shouldAutoCheck: false,
-  xpinstallEnabled: true,
-  xpinstallLocked: false,
-  // cached AddonInstall entries for add-ons we might want to update,
-  // keyed by add-on ID
-  addonInstalls: new Map(),
-  shuttingDown: false,
-  // Count the add-ons disabled by this update, enabled/disabled by
-  // metadata checks, and upgraded.
-  disabled: 0,
-  metadataEnabled: 0,
-  metadataDisabled: 0,
-  upgraded: 0,
-  upgradeFailed: 0,
-  upgradeDeclined: 0,
-
-  init() {
-    logger = Log.repository.getLogger("addons.update-dialog");
-    // XXX could we pass the addons themselves rather than the IDs?
-    this.affectedAddonIDs = new Set(window.arguments[0]);
-
-    try {
-      this.shouldSuggestAutoChecking =
-        !Services.prefs.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
-    } catch (e) {
-    }
-
-    try {
-      this.xpinstallEnabled = Services.prefs.getBoolPref(PREF_XPINSTALL_ENABLED);
-      this.xpinstallLocked = Services.prefs.prefIsLocked(PREF_XPINSTALL_ENABLED);
-    } catch (e) {
-    }
-
-    if (Services.io.offline)
-      document.documentElement.currentPage = document.getElementById("offline");
-    else
-      document.documentElement.currentPage = document.getElementById("versioninfo");
-  },
-
-  onWizardFinish: function gUpdateWizard_onWizardFinish() {
-    if (this.shouldSuggestAutoChecking)
-      Services.prefs.setBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED, this.shouldAutoCheck);
-  },
-
-  _setUpButton(aButtonID, aButtonKey, aDisabled) {
-    var strings = document.getElementById("updateStrings");
-    var button = document.documentElement.getButton(aButtonID);
-    if (aButtonKey) {
-      button.label = strings.getString(aButtonKey);
-      try {
-        button.setAttribute("accesskey", strings.getString(aButtonKey + "Accesskey"));
-      } catch (e) {
-      }
-    }
-    button.disabled = aDisabled;
-  },
-
-  setButtonLabels(aBackButton, aBackButtonIsDisabled,
-                             aNextButton, aNextButtonIsDisabled,
-                             aCancelButton, aCancelButtonIsDisabled) {
-    this._setUpButton("back", aBackButton, aBackButtonIsDisabled);
-    this._setUpButton("next", aNextButton, aNextButtonIsDisabled);
-    this._setUpButton("cancel", aCancelButton, aCancelButtonIsDisabled);
-  },
-
-  // Update Errors
-  errorItems: [],
-
-  checkForErrors(aElementIDToShow) {
-    if (this.errorItems.length > 0)
-      document.getElementById(aElementIDToShow).hidden = false;
-  },
-
-  onWizardClose(aEvent) {
-    return this.onWizardCancel();
-  },
-
-  onWizardCancel() {
-    gUpdateWizard.shuttingDown = true;
-    // Allow add-ons to continue downloading and installing
-    // in the background, though some may require a later restart
-    // Pages that are waiting for user input go into the background
-    // on cancel
-    if (gMismatchPage.waiting) {
-      logger.info("Dialog closed in mismatch page");
-      if (gUpdateWizard.addonInstalls.size > 0) {
-        gInstallingPage.startInstalls(
-          Array.from(gUpdateWizard.addonInstalls.values()));
-      }
-      return true;
-    }
-
-    // Pages that do asynchronous things will just keep running and check
-    // gUpdateWizard.shuttingDown to trigger background behaviour
-    if (!gInstallingPage.installing) {
-      logger.info("Dialog closed while waiting for updated compatibility information");
-    } else {
-      logger.info("Dialog closed while downloading and installing updates");
-    }
-    return true;
-  }
-};
-
-var gOfflinePage = {
-  onPageAdvanced() {
-    Services.io.offline = false;
-    return true;
-  },
-
-  toggleOffline() {
-    var nextbtn = document.documentElement.getButton("next");
-    nextbtn.disabled = !nextbtn.disabled;
-  }
-}
-
-// Addon listener to count addons enabled/disabled by metadata checks
-var listener = {
-  onDisabled(aAddon) {
-    gUpdateWizard.affectedAddonIDs.add(aAddon.id);
-    gUpdateWizard.metadataDisabled++;
-  },
-  onEnabled(aAddon) {
-    gUpdateWizard.affectedAddonIDs.delete(aAddon.id);
-    gUpdateWizard.metadataEnabled++;
-  }
-};
+Components.utils.import("resource://gre/modules/Services.jsm");
 
-var gVersionInfoPage = {
-  _completeCount: 0,
-  _totalCount: 0,
-  _versionInfoDone: false,
-  async onPageShow() {
-    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.
-      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 = await AddonManager.getAddonsByIDs(idlist);
-    // 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() {
-    AddonManager.removeAddonListener(listener);
-    AddonManagerPrivate.recordSimpleMeasure("appUpdate_disabled",
-        gUpdateWizard.disabled);
-    AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_enabled",
-        gUpdateWizard.metadataEnabled);
-    AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_disabled",
-        gUpdateWizard.metadataDisabled);
-    // Record 0 for these here in case we exit early; values will be replaced
-    // later if we actually upgrade any.
-    AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded", 0);
-    AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed", 0);
-    AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined", 0);
-    // Filter out any add-ons that are now enabled.
-    let addonList = gUpdateWizard.addons.map(a => a.id + ":" + a.appDisabled);
-    logger.debug("VersionInfo updates finished: found " + addonList.toSource());
-    let filteredAddons = [];
-    for (let a of gUpdateWizard.addons) {
-      if (a.appDisabled) {
-        logger.debug("Continuing with add-on " + a.id);
-        filteredAddons.push(a);
-      } else if (gUpdateWizard.addonInstalls.has(a.id)) {
-        gUpdateWizard.addonInstalls.get(a.id).cancel();
-        gUpdateWizard.addonInstalls.delete(a.id);
-      }
-    }
-    gUpdateWizard.addons = filteredAddons;
+let BRAND_PROPS = "chrome://branding/locale/brand.properties";
+let UPDATE_PROPS = "chrome://mozapps/locale/extensions/update.properties";
 
-    if (gUpdateWizard.shuttingDown) {
-      // jump directly to updating auto-update add-ons in the background
-      if (gUpdateWizard.addonInstalls.size > 0) {
-        let installs = Array.from(gUpdateWizard.addonInstalls.values());
-        gInstallingPage.startInstalls(installs);
-      }
-      return;
-    }
-
-    if (filteredAddons.length > 0) {
-      if (!gUpdateWizard.xpinstallEnabled && gUpdateWizard.xpinstallLocked) {
-        document.documentElement.currentPage = document.getElementById("adminDisabled");
-        return;
-      }
-      document.documentElement.currentPage = document.getElementById("mismatch");
-    } else {
-      logger.info("VersionInfo: No updates require further action");
-      // VersionInfo compatibility updates resolved all compatibility problems,
-      // close this window and continue starting the application...
-      // XXX Bug 314754 - We need to use setTimeout to close the window due to
-      // the EM using xmlHttpRequest when checking for updates.
-      setTimeout(close, 0);
-    }
-  },
-
-  // UpdateListener
-  onUpdateFinished(aAddon, status) {
-    ++this._completeCount;
-
-    if (status != AddonManager.UPDATE_STATUS_NO_ERROR) {
-      logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
-           " failed for " + aAddon.id + ": " + status);
-      gUpdateWizard.errorItems.push(aAddon);
-    } else {
-      logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
-           " finished for " + aAddon.id);
-    }
-
-    // If we're not in the background, just make a list of add-ons that have
-    // updates available
-    if (!gUpdateWizard.shuttingDown) {
-      // If we're still in the update check window and the add-on is now active
-      // then it won't have been disabled by startup
-      if (aAddon.active) {
-        AddonManagerPrivate.removeStartupChange(AddonManager.STARTUP_CHANGE_DISABLED, aAddon.id);
-        gUpdateWizard.metadataEnabled++;
-      }
-
-      // Update the status text and progress bar
-      var updateStrings = document.getElementById("updateStrings");
-      var statusElt = document.getElementById("versioninfo.status");
-      var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
-      statusElt.setAttribute("value", statusString);
-
-      // Update the status text and progress bar
-      var progress = document.getElementById("versioninfo.progress");
-      progress.mode = "normal";
-      progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
-    }
-
-    if (this._completeCount == this._totalCount)
-      this.onAllUpdatesFinished();
-  },
-
-  onUpdateAvailable(aAddon, aInstall) {
-    logger.debug("VersionInfo got an install for " + aAddon.id + ": " + aAddon.version);
-    gUpdateWizard.addonInstalls.set(aAddon.id, aInstall);
-  },
-};
-
-var gMismatchPage = {
-  waiting: false,
-
-  onPageShow() {
-    gMismatchPage.waiting = true;
-    gUpdateWizard.setButtonLabels(null, true,
-                                  "mismatchCheckNow", false,
-                                  "mismatchDontCheck", false);
-    document.documentElement.getButton("next").focus();
+let appName = Services.strings.createBundle(BRAND_PROPS)
+                      .GetStringFromName("brandShortName");
+let bundle = Services.strings.createBundle(UPDATE_PROPS);
 
-    var incompatible = document.getElementById("mismatch.incompatible");
-    for (let addon of gUpdateWizard.addons) {
-      var listitem = document.createElement("listitem");
-      listitem.setAttribute("label", addon.name + " " + addon.version);
-      incompatible.appendChild(listitem);
-    }
-  }
-};
-
-var gUpdatePage = {
-  _totalCount: 0,
-  _completeCount: 0,
-  onPageShow() {
-    gMismatchPage.waiting = false;
-    gUpdateWizard.setButtonLabels(null, true,
-                                  "nextButtonText", true,
-                                  "cancelButtonText", false);
-    document.documentElement.getButton("next").focus();
-
-    gUpdateWizard.errorItems = [];
-
-    this._totalCount = gUpdateWizard.addons.length;
-    for (let addon of gUpdateWizard.addons) {
-      logger.debug("UpdatePage requesting update for " + addon.id);
-      // Redundant call to find updates again here when we already got them
-      // in the VersionInfo page: https://bugzilla.mozilla.org/show_bug.cgi?id=960597
-      addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
-    }
-  },
-
-  onAllUpdatesFinished() {
-    if (gUpdateWizard.shuttingDown)
-      return;
-
-    var nextPage = document.getElementById("noupdates");
-    if (gUpdateWizard.addonsToUpdate.length > 0)
-      nextPage = document.getElementById("found");
-    document.documentElement.currentPage = nextPage;
-  },
-
-  // UpdateListener
-  onUpdateAvailable(aAddon, aInstall) {
-    logger.debug("UpdatePage got an update for " + aAddon.id + ": " + aAddon.version);
-    gUpdateWizard.addonsToUpdate.push(aInstall);
-  },
-
-  onUpdateFinished(aAddon, status) {
-    if (status != AddonManager.UPDATE_STATUS_NO_ERROR)
-      gUpdateWizard.errorItems.push(aAddon);
-
-    ++this._completeCount;
-
-    if (!gUpdateWizard.shuttingDown) {
-      // Update the status text and progress bar
-      var updateStrings = document.getElementById("updateStrings");
-      var statusElt = document.getElementById("checking.status");
-      var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
-      statusElt.setAttribute("value", statusString);
-
-      var progress = document.getElementById("checking.progress");
-      progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
-    }
-
-    if (this._completeCount == this._totalCount)
-      this.onAllUpdatesFinished()
-  },
-};
-
-var gFoundPage = {
-  onPageShow() {
-    gUpdateWizard.setButtonLabels(null, true,
-                                  "installButtonText", false,
-                                  null, false);
+let titleText = bundle.formatStringFromName("addonUpdateTitle", [appName], 1);
+let messageText = bundle.formatStringFromName("addonUpdateMessage", [appName], 1);
+let cancelText = bundle.GetStringFromName("addonUpdateCancelMessage");
+let cancelButtonText = bundle.formatStringFromName("addonUpdateCancelButton", [appName], 1);
 
-    var foundUpdates = document.getElementById("found.updates");
-    for (let install of gUpdateWizard.addonsToUpdate) {
-      let listItem = foundUpdates.appendItem(install.name + " " + install.version);
-      listItem.setAttribute("type", "checkbox");
-      listItem.setAttribute("checked", "true");
-      listItem.install = install;
-    }
-
-    if (!gUpdateWizard.xpinstallEnabled) {
-      document.getElementById("xpinstallDisabledAlert").hidden = false;
-      document.getElementById("enableXPInstall").focus();
-      document.documentElement.getButton("next").disabled = true;
-    } else {
-      document.documentElement.getButton("next").focus();
-      document.documentElement.getButton("next").disabled = false;
-    }
-  },
-
-  toggleXPInstallEnable(aEvent) {
-    var enabled = aEvent.target.checked;
-    gUpdateWizard.xpinstallEnabled = enabled;
-    var pref = Components.classes["@mozilla.org/preferences-service;1"]
-                         .getService(Components.interfaces.nsIPrefBranch);
-    pref.setBoolPref(PREF_XPINSTALL_ENABLED, enabled);
-    this.updateNextButton();
-  },
-
-  updateNextButton() {
-    if (!gUpdateWizard.xpinstallEnabled) {
-      document.documentElement.getButton("next").disabled = true;
-      return;
-    }
-
-    var oneChecked = false;
-    var foundUpdates = document.getElementById("found.updates");
-    var updates = foundUpdates.getElementsByTagName("listitem");
-    for (let update of updates) {
-      if (!update.checked)
-        continue;
-      oneChecked = true;
-      break;
-    }
-
-    gUpdateWizard.setButtonLabels(null, true,
-                                  "installButtonText", true,
-                                  null, false);
-    document.getElementById("found").setAttribute("next", "installing");
-    document.documentElement.getButton("next").disabled = !oneChecked;
-  }
-};
-
-var gInstallingPage = {
-  _installs: [],
-  _errors: [],
-  _strings: null,
-  _currentInstall: -1,
-  _installing: false,
-
-  // Initialize fields we need for installing and tracking progress,
-  // and start iterating through the installations
-  startInstalls(aInstallList) {
-    if (!gUpdateWizard.xpinstallEnabled) {
-      return;
-    }
-
-    let installs = Array.from(aInstallList).map(a => a.existingAddon.id);
-    logger.debug("Start installs for " + installs.toSource());
-    this._errors = [];
-    this._installs = aInstallList;
-    this._installing = true;
-    this.startNextInstall();
-  },
-
-  onPageShow() {
-    gUpdateWizard.setButtonLabels(null, true,
-                                  "nextButtonText", true,
-                                  null, true);
+document.title = titleText;
 
-    var foundUpdates = document.getElementById("found.updates");
-    var updates = foundUpdates.getElementsByTagName("listitem");
-    let toInstall = [];
-    for (let update of updates) {
-      if (!update.checked) {
-        logger.info("User chose to cancel update of " + update.label);
-        gUpdateWizard.upgradeDeclined++;
-        update.install.cancel();
-        continue;
-      }
-      toInstall.push(update.install);
-    }
-    this._strings = document.getElementById("updateStrings");
-
-    this.startInstalls(toInstall);
-  },
-
-  startNextInstall() {
-    if (this._currentInstall >= 0) {
-      this._installs[this._currentInstall].removeListener(this);
-    }
-
-    this._currentInstall++;
-
-    if (this._installs.length == this._currentInstall) {
-      Services.obs.notifyObservers(null, "TEST:all-updates-done");
-      AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded",
-          gUpdateWizard.upgraded);
-      AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed",
-          gUpdateWizard.upgradeFailed);
-      AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined",
-          gUpdateWizard.upgradeDeclined);
-      this._installing = false;
-      if (gUpdateWizard.shuttingDown) {
-        return;
-      }
-      var nextPage = this._errors.length > 0 ? "installerrors" : "finished";
-      document.getElementById("installing").setAttribute("next", nextPage);
-      document.documentElement.advance();
-      return;
-    }
-
-    let install = this._installs[this._currentInstall];
-
-    if (gUpdateWizard.shuttingDown && !AddonManager.shouldAutoUpdate(install.existingAddon)) {
-      logger.debug("Don't update " + install.existingAddon.id + " in background");
-      gUpdateWizard.upgradeDeclined++;
-      install.cancel();
-      this.startNextInstall();
-      return;
-    }
-    install.addListener(this);
-    install.install();
-  },
-
-  // InstallListener
-  onDownloadStarted(aInstall) {
-    if (gUpdateWizard.shuttingDown) {
-      return;
-    }
-    var strings = document.getElementById("updateStrings");
-    var label = strings.getFormattedString("downloadingPrefix", [aInstall.name]);
-    var actionItem = document.getElementById("actionItem");
-    actionItem.value = label;
-  },
-
-  onDownloadProgress(aInstall) {
-    if (gUpdateWizard.shuttingDown) {
-      return;
-    }
-    var downloadProgress = document.getElementById("downloadProgress");
-    downloadProgress.value = Math.ceil(100 * aInstall.progress / aInstall.maxProgress);
-  },
-
-  onDownloadEnded(aInstall) {
-  },
-
-  onDownloadFailed(aInstall) {
-    this._errors.push(aInstall);
-
-    gUpdateWizard.upgradeFailed++;
-    this.startNextInstall();
-  },
-
-  onInstallStarted(aInstall) {
-    if (gUpdateWizard.shuttingDown) {
-      return;
-    }
-    var strings = document.getElementById("updateStrings");
-    var label = strings.getFormattedString("installingPrefix", [aInstall.name]);
-    var actionItem = document.getElementById("actionItem");
-    actionItem.value = label;
-  },
-
-  onInstallEnded(aInstall, aAddon) {
-    if (!gUpdateWizard.shuttingDown) {
-      // Remember that this add-on was updated during startup
-      AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
-                                           aAddon.id);
-    }
-
-    gUpdateWizard.upgraded++;
-    this.startNextInstall();
-  },
-
-  onInstallFailed(aInstall) {
-    this._errors.push(aInstall);
-
-    gUpdateWizard.upgradeFailed++;
-    this.startNextInstall();
-  }
-};
-
-var gInstallErrorsPage = {
-  onPageShow() {
-    gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
-    document.documentElement.getButton("finish").focus();
-  },
-};
-
-// Displayed when there are incompatible add-ons and the xpinstall.enabled
-// pref is false and locked.
-var gAdminDisabledPage = {
-  onPageShow() {
-    gUpdateWizard.setButtonLabels(null, true, null, true,
-                                  "cancelButtonText", true);
-    document.documentElement.getButton("finish").focus();
-  }
-};
-
-// Displayed when selected add-on updates have been installed without error.
-// There can still be add-ons that are not compatible and don't have an update.
-var gFinishedPage = {
-  onPageShow() {
-    gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
-    document.documentElement.getButton("finish").focus();
-
-    if (gUpdateWizard.shouldSuggestAutoChecking) {
-      document.getElementById("finishedCheckDisabled").hidden = false;
-      gUpdateWizard.shouldAutoCheck = true;
-    } else
-      document.getElementById("finishedCheckEnabled").hidden = false;
-
-    document.documentElement.getButton("finish").focus();
-  }
-};
-
-// Displayed when there are incompatible add-ons and there are no available
-// updates.
-var gNoUpdatesPage = {
-  onPageShow(aEvent) {
-    gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
-    if (gUpdateWizard.shouldSuggestAutoChecking) {
-      document.getElementById("noupdatesCheckDisabled").hidden = false;
-      gUpdateWizard.shouldAutoCheck = true;
-    } else
-      document.getElementById("noupdatesCheckEnabled").hidden = false;
-
-    gUpdateWizard.checkForErrors("updateCheckErrorNotFound");
-    document.documentElement.getButton("finish").focus();
-  }
-};
+window.addEventListener("load", e => {
+  document.getElementById("message").textContent = messageText;
+  document.getElementById("cancel-message").textContent = cancelText;
+  document.getElementById("cancel-btn").textContent = cancelButtonText;
+  window.sizeToContent();
+});
deleted file mode 100644
--- a/toolkit/mozapps/extensions/content/update.xul
+++ /dev/null
@@ -1,194 +0,0 @@
-<?xml version="1.0"?>
-
-# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
-<?xml-stylesheet href="chrome://mozapps/skin/extensions/update.css" type="text/css"?> 
-
-<!DOCTYPE wizard [
-<!ENTITY % updateDTD SYSTEM "chrome://mozapps/locale/extensions/update.dtd">
-<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
-%updateDTD;
-%brandDTD;
-]>
-
-<wizard id="updateWizard"
-        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="&updateWizard.title;"
-        windowtype="Addons:Compatibility"
-        branded="true"
-        onload="gUpdateWizard.init();"
-        onwizardfinish="gUpdateWizard.onWizardFinish();"
-        onwizardcancel="return gUpdateWizard.onWizardCancel();"
-        onclose="return gUpdateWizard.onWizardClose(event);"
-        buttons="accept,cancel">
-
-  <script type="application/javascript" src="chrome://mozapps/content/extensions/update.js"/>
-  
-  <stringbundleset id="updateSet">
-    <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
-    <stringbundle id="updateStrings" src="chrome://mozapps/locale/extensions/update.properties"/>
-  </stringbundleset>
-  
-  <wizardpage id="dummy" pageid="dummy"/>
-  
-  <wizardpage id="offline" pageid="offline" next="versioninfo"
-              label="&offline.title;"
-              onpageadvanced="return gOfflinePage.onPageAdvanced();">
-    <description>&offline.description;</description>
-    <checkbox id="toggleOffline"
-              checked="true"
-              label="&offline.toggleOffline.label;"
-              accesskey="&offline.toggleOffline.accesskey;"
-              oncommand="gOfflinePage.toggleOffline();"/>
-  </wizardpage>
-  
-  <wizardpage id="versioninfo" pageid="versioninfo" next="mismatch"
-              label="&versioninfo.wizard.title;"
-              onpageshow="gVersionInfoPage.onPageShow();">
-    <label>&versioninfo.top.label;</label>
-    <separator class="thin"/>
-    <progressmeter id="versioninfo.progress" mode="undetermined"/>
-    <hbox align="center">
-      <image id="versioninfo.throbber" class="throbber"/>
-      <label flex="1" id="versioninfo.status" crop="right">&versioninfo.waiting;</label>
-    </hbox>
-    <separator/>
-  </wizardpage>
-
-  <wizardpage id="mismatch" pageid="mismatch" next="checking"
-              label="&mismatch.win.title;"
-              onpageshow="gMismatchPage.onPageShow();">
-    <label>&mismatch.top.label;</label>
-    <separator class="thin"/>
-    <listbox id="mismatch.incompatible" flex="1"/>
-    <separator class="thin"/>
-    <label>&mismatch.bottom.label;</label>
-  </wizardpage>
-  
-  <wizardpage id="checking" pageid="checking" next="noupdates"
-              label="&checking.wizard.title;"
-              onpageshow="gUpdatePage.onPageShow();">
-    <label>&checking.top.label;</label>
-    <separator class="thin"/>
-    <progressmeter id="checking.progress"/>
-    <hbox align="center">
-      <image id="checking.throbber" class="throbber"/>
-      <label id="checking.status" flex="1" crop="right">&checking.status;</label>
-    </hbox>
-  </wizardpage>
-    
-  <wizardpage id="noupdates" pageid="noupdates"
-              label="&noupdates.wizard.title;"
-              onpageshow="gNoUpdatesPage.onPageShow();">
-    <description>&noupdates.intro.desc;</description>
-    <separator class="thin"/>
-    <hbox id="updateCheckErrorNotFound" class="alertBox" hidden="true" align="top">
-      <description flex="1">&noupdates.error.desc;</description>
-    </hbox>
-    <separator class="thin"/>
-    <description id="noupdatesCheckEnabled" hidden="true">
-      &noupdates.checkEnabled.desc;
-    </description>
-    <vbox id="noupdatesCheckDisabled" hidden="true">
-      <description>&finished.checkDisabled.desc;</description>
-      <checkbox label="&enableChecking.label;" checked="true"
-                oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
-    </vbox>
-    <separator flex="1"/>
-#ifndef XP_MACOSX
-    <label>&clickFinish.label;</label>
-#else
-    <label>&clickFinish.labelMac;</label>
-#endif
-    <separator class="thin"/>
-  </wizardpage>
-
-  <wizardpage id="found" pageid="found" next="installing"
-              label="&found.wizard.title;"
-              onpageshow="gFoundPage.onPageShow();">
-    <label>&found.top.label;</label>
-    <separator class="thin"/>
-    <listbox id="found.updates" flex="1" seltype="multiple"
-             onclick="gFoundPage.updateNextButton();"/>
-    <separator class="thin"/>
-    <vbox align="left" id="xpinstallDisabledAlert" hidden="true">
-      <description>&found.disabledXPinstall.label;</description>
-      <checkbox label="&found.enableXPInstall.label;"
-                id="enableXPInstall"
-                accesskey="&found.enableXPInstall.accesskey;"
-                oncommand="gFoundPage.toggleXPInstallEnable(event);"/>
-    </vbox>
-  </wizardpage>
-
-  <wizardpage id="installing" pageid="installing" next="finished"
-              label="&installing.wizard.title;"
-              onpageshow="gInstallingPage.onPageShow();">
-    <label>&installing.top.label;</label>
-    <progressmeter id="downloadProgress"/>
-    <hbox align="center">
-      <image id="installing.throbber" class="throbber"/>
-      <label id="actionItem" flex="1" crop="right"/>
-    </hbox>
-    <separator/>
-  </wizardpage>
-  
-  <wizardpage id="installerrors" pageid="installerrors"
-              label="&installerrors.wizard.title;"
-              onpageshow="gInstallErrorsPage.onPageShow();">
-    <hbox align="top" class="alertBox">
-      <description flex="1">&installerrors.intro.label;</description>
-    </hbox>
-    <separator flex="1"/>
-#ifndef XP_MACOSX
-    <label>&clickFinish.label;</label>
-#else
-    <label>&clickFinish.labelMac;</label>
-#endif
-    <separator class="thin"/>
-  </wizardpage>
-  
-  <wizardpage id="adminDisabled" pageid="adminDisabled"
-              label="&adminDisabled.wizard.title;"
-              onpageshow="gAdminDisabledPage.onPageShow();">
-    <separator/>
-    <hbox class="alertBox" align="top">
-      <description flex="1">&adminDisabled.warning.label;</description>
-    </hbox>
-    <separator flex="1"/>
-#ifndef XP_MACOSX
-    <label>&clickFinish.label;</label>
-#else
-    <label>&clickFinish.labelMac;</label>
-#endif
-    <separator class="thin"/>
-  </wizardpage>
-
-  <wizardpage id="finished" pageid="finished"
-              label="&finished.wizard.title;"
-              onpageshow="gFinishedPage.onPageShow();">
-
-    <label>&finished.top.label;</label>
-    <separator/>
-    <description id="finishedCheckEnabled" hidden="true">
-      &finished.checkEnabled.desc;
-    </description>
-    <vbox id="finishedCheckDisabled" hidden="true">
-      <description>&finished.checkDisabled.desc;</description>
-      <checkbox label="&enableChecking.label;" checked="true"
-                oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
-    </vbox>
-    <separator flex="1"/>
-#ifndef XP_MACOSX
-    <label>&clickFinish.label;</label>
-#else
-    <label>&clickFinish.labelMac;</label>
-#endif
-    <separator class="thin"/>
-  </wizardpage>
-  
-</wizard>
-
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -31,16 +31,18 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PermissionsUtils: "resource://gre/modules/PermissionsUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   ConsoleAPI: "resource://gre/modules/Console.jsm",
   ProductAddonChecker: "resource://gre/modules/addons/ProductAddonChecker.jsm",
   UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
   isAddonPartOfE10SRollout: "resource://gre/modules/addons/E10SAddonsRollout.jsm",
   JSONFile: "resource://gre/modules/JSONFile.jsm",
   LegacyExtensionsUtils: "resource://gre/modules/LegacyExtensionsUtils.jsm",
+  setTimeout: "resource://gre/modules/Timer.jsm",
+  clearTimeout: "resource://gre/modules/Timer.jsm",
 
   DownloadAddonInstall: "resource://gre/modules/addons/XPIInstall.jsm",
   LocalAddonInstall: "resource://gre/modules/addons/XPIInstall.jsm",
   StagedAddonInstall: "resource://gre/modules/addons/XPIInstall.jsm",
   UpdateChecker: "resource://gre/modules/addons/XPIInstall.jsm",
   loadManifestFromFile: "resource://gre/modules/addons/XPIInstall.jsm",
   verifyBundleSignedState: "resource://gre/modules/addons/XPIInstall.jsm",
 });
@@ -102,17 +104,16 @@ const PREF_EM_LAST_APP_BUILD_ID       = 
 
 const OBSOLETE_PREFERENCES = [
   "extensions.bootstrappedAddons",
   "extensions.enabledAddons",
   "extensions.xpiState",
   "extensions.installCache",
 ];
 
-const URI_EXTENSION_UPDATE_DIALOG     = "chrome://mozapps/content/extensions/update.xul";
 const URI_EXTENSION_STRINGS           = "chrome://mozapps/locale/extensions/extensions.properties";
 
 const DIR_EXTENSIONS                  = "extensions";
 const DIR_SYSTEM_ADDONS               = "features";
 const DIR_STAGE                       = "staged";
 const DIR_TRASH                       = "trash";
 
 const FILE_XPI_STATES                 = "addonStartup.json.lz4";
@@ -768,16 +769,30 @@ function canRunInSafeMode(aAddon) {
   // in safe mode. assuming for now that they are.
   if (aAddon._installLocation.name == KEY_APP_TEMPORARY)
     return true;
 
   return aAddon._installLocation.isSystem;
 }
 
 /**
+ * Determine if this addon should be disabled due to being legacy
+ *
+ * @param {Addon} addon The addon to check
+ *
+ * @returns {boolean} Whether the addon should be disabled for being legacy
+ */
+function isDisabledLegacy(addon) {
+  return (!AddonSettings.ALLOW_LEGACY_EXTENSIONS &&
+          LEGACY_TYPES.has(addon.type) &&
+          !addon._installLocation.isSystem &&
+          addon.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED);
+}
+
+/**
  * Calculates whether an add-on should be appDisabled or not.
  *
  * @param  aAddon
  *         The add-on to check
  * @return true if the add-on should not be appDisabled
  */
 function isUsableAddon(aAddon) {
   // Hack to ensure the default theme is always usable
@@ -823,19 +838,17 @@ function isUsableAddon(aAddon) {
       let active = XPIProvider.activeAddons.get(id);
       return active && !active.disable;
     };
 
     if (aAddon.dependencies.some(id => !isActive(id)))
       return false;
   }
 
-  if (!AddonSettings.ALLOW_LEGACY_EXTENSIONS && LEGACY_TYPES.has(aAddon.type) &&
-      !aAddon._installLocation.isSystem &&
-      aAddon.signedState !== AddonManager.SIGNEDSTATE_PRIVILEGED) {
+  if (isDisabledLegacy(aAddon)) {
     logger.warn(`disabling legacy extension ${aAddon.id}`);
     return false;
   }
 
   if (!ALLOW_NON_MPC && aAddon.type == "extension" &&
       aAddon.multiprocessCompatible !== true) {
     logger.warn(`disabling ${aAddon.id} since it is not multiprocess compatible`);
     return false;
@@ -2155,20 +2168,21 @@ this.XPIProvider = {
                                              aOldPlatformVersion);
 
       // Changes to installed extensions may have changed which theme is selected
       this.applyThemeChange();
 
       AddonManagerPrivate.markProviderSafe(this);
 
       if (aAppChanged && !this.allAppGlobal &&
-          Services.prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true)) {
+          Services.prefs.getBoolPref(PREF_EM_SHOW_MISMATCH_UI, true) &&
+          AddonManager.updateEnabled) {
         let addonsToUpdate = this.shouldForceUpdateCheck(aAppChanged);
         if (addonsToUpdate) {
-          this.showUpgradeUI(addonsToUpdate);
+          this.noLegacyStartupCheck(addonsToUpdate);
           flushCaches = true;
         }
       }
 
       if (flushCaches) {
         Services.obs.notifyObservers(null, "startupcache-invalidate");
         // UI displayed early in startup (like the compatibility UI) may have
         // caused us to cache parts of the skin or locale in memory. These must
@@ -2190,16 +2204,22 @@ this.XPIProvider = {
         } catch (e) { }
         this.addAddonsToCrashReporter();
       }
 
       try {
         AddonManagerPrivate.recordTimestamp("XPI_bootstrap_addons_begin");
 
         for (let addon of this.sortBootstrappedAddons()) {
+          // The startup update check above may have already started some
+          // extensions, make sure not to try to start them twice.
+          let activeAddon = this.activeAddons.get(addon.id);
+          if (activeAddon && activeAddon.started) {
+            continue;
+          }
           try {
             let reason = BOOTSTRAP_REASONS.APP_STARTUP;
             // Eventually set INSTALLED reason when a bootstrap addon
             // is dropped in profile folder and automatically installed
             if (AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED)
                             .indexOf(addon.id) !== -1)
               reason = BOOTSTRAP_REASONS.ADDON_INSTALL;
             this.callBootstrapMethod(createAddonDetails(addon.id, addon),
@@ -2410,76 +2430,158 @@ this.XPIProvider = {
     Services.prefs.clearUserPref(PREF_SKIN_SWITCHPENDING);
   },
 
   /**
    * If the application has been upgraded and there are add-ons outside the
    * application directory then we may need to synchronize compatibility
    * information but only if the mismatch UI isn't disabled.
    *
-   * @returns False if no update check is needed, otherwise an array of add-on
-   *          IDs to check for updates. Array may be empty if no add-ons can be/need
-   *           to be updated, but the metadata check needs to be performed.
+   * @returns null if no update check is needed, otherwise an array of add-on
+   *          IDs to check for updates.
    */
   shouldForceUpdateCheck(aAppChanged) {
     AddonManagerPrivate.recordSimpleMeasure("XPIDB_metadata_age", AddonRepository.metadataAge());
 
     let startupChanges = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
     logger.debug("shouldForceUpdateCheck startupChanges: " + startupChanges.toSource());
     AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_disabled", startupChanges.length);
 
     let forceUpdate = [];
     if (startupChanges.length > 0) {
     let addons = XPIDatabase.getAddons();
       for (let addon of addons) {
         if ((startupChanges.indexOf(addon.id) != -1) &&
             (addon.permissions() & AddonManager.PERM_CAN_UPGRADE) &&
-            !addon.isCompatible) {
+            (!addon.isCompatible || isDisabledLegacy(addon))) {
           logger.debug("shouldForceUpdateCheck: can upgrade disabled add-on " + addon.id);
           forceUpdate.push(addon.id);
         }
       }
     }
 
-    if (AddonRepository.isMetadataStale()) {
-      logger.debug("shouldForceUpdateCheck: metadata is stale");
-      return forceUpdate;
-    }
     if (forceUpdate.length > 0) {
       return forceUpdate;
     }
 
-    return false;
+    return null;
   },
 
   /**
-   * Shows the "Compatibility Updates" UI.
+   * Perform startup check for updates of legacy extensions.
+   * This runs during startup when an app update has made some add-ons
+   * incompatible and legacy add-on support is diasabled.
+   * In this case, we just do a quiet update check.
    *
-   * @param  aAddonIDs
-   *         Array opf addon IDs that were disabled by the application update, and
-   *         should therefore be checked for updates.
+   * @param {Array<string>} ids The ids of the addons to check for updates.
+   *
+   * @returns {Set<string>} The ids of any addons that were updated.  These
+   *                        addons will have been started by the update
+   *                        process so they should not be started by the
+   *                        regular bootstrap startup code.
    */
-  showUpgradeUI(aAddonIDs) {
-    logger.debug("XPI_showUpgradeUI: " + aAddonIDs.toSource());
-    Services.telemetry.getHistogramById("ADDON_MANAGER_UPGRADE_UI_SHOWN").add(1);
-
-    // Flip a flag to indicate that we interrupted startup with an interactive prompt
-    Services.startup.interrupted = true;
-
-    var variant = Cc["@mozilla.org/variant;1"].
-                  createInstance(Ci.nsIWritableVariant);
-    variant.setFromVariant(aAddonIDs);
-
-    // This *must* be modal as it has to block startup.
-    var features = "chrome,centerscreen,dialog,titlebar,modal";
-    var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
-             getService(Ci.nsIWindowWatcher);
-    ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
-
-    Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
+  noLegacyStartupCheck(ids) {
+    let started = new Set();
+    const DIALOG = "chrome://mozapps/content/extensions/update.html";
+    const SHOW_DIALOG_DELAY = 1000;
+    const SHOW_CANCEL_DELAY = 30000;
+
+    // Keep track of a value between 0 and 1 indicating the progress
+    // for each addon.  Just combine these linearly into a single
+    // value for the progress bar in the update dialog.
+    let updateProgress = val => {};
+    let progressByID = new Map();
+    function setProgress(id, val) {
+      progressByID.set(id, val);
+      updateProgress(Array.from(progressByID.values()).reduce((a, b) => a + b) / progressByID.size);
+    }
+
+    // Do an update check for one addon and try to apply the update if
+    // there is one.  Progress for the check is arbitrarily defined as
+    // 10% done when the update check is done, between 10-90% during the
+    // download, then 100% when the update has been installed.
+    let checkOne = async (id) => {
+      logger.debug(`Checking for updates to disabled addon ${id}\n`);
+
+      setProgress(id, 0);
+
+      let addon = await AddonManager.getAddonByID(id);
+      let install = await new Promise(resolve => addon.findUpdates({
+        onUpdateFinished() { resolve(null); },
+        onUpdateAvailable(addon, install) { resolve(install); },
+      }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED));
+
+      if (!install) {
+        setProgress(id, 1);
+        return;
+      }
+
+      setProgress(id, 0.1);
+
+      let installPromise = new Promise(resolve => {
+        let finish = () => {
+          setProgress(id, 1);
+          resolve();
+        };
+        install.addListener({
+          onDownloadProgress() {
+            if (install.maxProgress != 0) {
+              setProgress(id, 0.1 + 0.8 * install.progress / install.maxProgress);
+            }
+          },
+          onDownloadEnded() {
+            setProgress(id, 0.9);
+          },
+          onDownloadFailed: finish,
+          onInstallFailed: finish,
+          onInstallEnded() {
+            started.add(id);
+            AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED, id);
+            finish();
+          },
+        });
+      });
+      install.install();
+      await installPromise;
+    };
+
+    let finished = false;
+    Promise.all(ids.map(checkOne)).then(() => { finished = true; });
+
+    let window;
+    let timer = setTimeout(() => {
+      const FEATURES = "chrome,dialog,centerscreen,scrollbars=no";
+      window = Services.ww.openWindow(null, DIALOG, "", FEATURES, null);
+
+      let cancelDiv;
+      window.addEventListener("DOMContentLoaded", e => {
+        let progress = window.document.getElementById("progress");
+        updateProgress = val => { progress.value = val; };
+
+        cancelDiv = window.document.getElementById("cancel-section");
+        cancelDiv.setAttribute("style", "display: none;");
+
+        let cancelBtn = window.document.getElementById("cancel-btn");
+        cancelBtn.addEventListener("click", e => { finished = true; });
+      });
+
+      timer = setTimeout(() => {
+        cancelDiv.removeAttribute("style");
+        window.sizeToContent();
+      }, SHOW_CANCEL_DELAY - SHOW_DIALOG_DELAY);
+    }, SHOW_DIALOG_DELAY);
+
+    Services.tm.spinEventLoopUntil(() => finished);
+
+    clearTimeout(timer);
+    if (window) {
+      window.close();
+    }
+
+    return started;
   },
 
   async updateSystemAddons() {
     let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
     if (!systemAddonLocation)
       return;
 
     // Don't do anything in safe mode
@@ -4176,16 +4278,17 @@ this.XPIProvider = {
    */
   loadBootstrapScope(aId, aFile, aVersion, aType,
                                aMultiprocessCompatible, aRunInSafeMode,
                                aDependencies, hasEmbeddedWebExtension) {
     this.activeAddons.set(aId, {
       bootstrapScope: null,
       // a Symbol passed to this add-on, which it can use to identify itself
       instanceID: Symbol(aId),
+      started: false,
     });
 
     // Mark the add-on as active for the crash reporter before loading
     this.addAddonsToCrashReporter();
 
     let activeAddon = this.activeAddons.get(aId);
 
     // Locales only contain chrome and can't have bootstrap scripts
@@ -4332,22 +4435,28 @@ this.XPIProvider = {
       let scope = activeAddon.bootstrapScope;
       try {
         method = scope[aMethod] || Cu.evalInSandbox(`${aMethod};`, scope);
       } catch (e) {
         // An exception will be caught if the expected method is not defined.
         // That will be logged below.
       }
 
-      // Extensions are automatically deinitialized in the correct order at shutdown.
-      if (aMethod == "shutdown" && aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
-        activeAddon.disable = true;
-        for (let addon of this.getDependentAddons(aAddon)) {
-          if (addon.active)
-            this.updateAddonDisabledState(addon);
+      if (aMethod == "startup") {
+        activeAddon.started = true;
+      } else if (aMethod == "shutdown") {
+        activeAddon.started = false;
+
+        // Extensions are automatically deinitialized in the correct order at shutdown.
+        if (aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
+          activeAddon.disable = true;
+          for (let addon of this.getDependentAddons(aAddon)) {
+            if (addon.active)
+              this.updateAddonDisabledState(addon);
+          }
         }
       }
 
       let params = {
         id: aAddon.id,
         version: aAddon.version,
         installPath: aFile.clone(),
         resourceURI: getURIForResourceInFile(aFile, "")
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -11,18 +11,19 @@ toolkit.jar:
 * content/mozapps/extensions/extensions.xml                     (content/extensions.xml)
   content/mozapps/extensions/updateinfo.xsl                     (content/updateinfo.xsl)
   content/mozapps/extensions/about.xul                          (content/about.xul)
   content/mozapps/extensions/about.js                           (content/about.js)
   content/mozapps/extensions/blocklist.xul                      (content/blocklist.xul)
   content/mozapps/extensions/blocklist.js                       (content/blocklist.js)
   content/mozapps/extensions/blocklist.css                      (content/blocklist.css)
   content/mozapps/extensions/blocklist.xml                      (content/blocklist.xml)
-* content/mozapps/extensions/update.xul                         (content/update.xul)
+  content/mozapps/extensions/update.html                        (content/update.html)
   content/mozapps/extensions/update.js                          (content/update.js)
+  content/mozapps/extensions/update.css                         (content/update.css)
   content/mozapps/extensions/eula.xul                           (content/eula.xul)
   content/mozapps/extensions/eula.js                            (content/eula.js)
   content/mozapps/extensions/newaddon.xul                       (content/newaddon.xul)
   content/mozapps/extensions/newaddon.js                        (content/newaddon.js)
   content/mozapps/extensions/pluginPrefs.xul                    (content/pluginPrefs.xul)
   content/mozapps/extensions/gmpPrefs.xul                       (content/gmpPrefs.xul)
   content/mozapps/extensions/OpenH264-license.txt               (content/OpenH264-license.txt)
 #endif
deleted file mode 100644
--- a/toolkit/themes/osx/mozapps/extensions/update.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.throbber {
-  list-style-image: url("chrome://global/skin/icons/loading.png");
-  width: 16px;
-  height: 16px;
-  margin-top: 5px;
-  margin-bottom: 5px;
-  margin-inline-start: 5px;
-  margin-inline-end: 2px;
-}
-
-@media (min-resolution: 2dppx) {
-  .throbber {
-    list-style-image: url("chrome://global/skin/icons/loading@2x.png");
-  }
-}
-
-.alertBox {
-  background-color: InfoBackground;
-  color: InfoText;
-  border: 1px outset InfoBackground;
-  margin-left: 3px;
-  margin-right: 3px;
-  padding: 5px;
-}
--- a/toolkit/themes/osx/mozapps/jar.mn
+++ b/toolkit/themes/osx/mozapps/jar.mn
@@ -10,17 +10,16 @@ toolkit.jar:
   skin/classic/mozapps/extensions/discover-logo.png               (extensions/discover-logo.png)
   skin/classic/mozapps/extensions/rating-won.png                  (extensions/rating-won.png)
   skin/classic/mozapps/extensions/rating-not-won.png              (extensions/rating-not-won.png)
   skin/classic/mozapps/extensions/cancel.png                      (extensions/cancel.png)
   skin/classic/mozapps/extensions/toolbarbutton-dropmarker.png    (extensions/toolbarbutton-dropmarker.png)
   skin/classic/mozapps/extensions/heart.png                       (extensions/heart.png)
   skin/classic/mozapps/extensions/about.css                       (extensions/about.css)
 * skin/classic/mozapps/extensions/extensions.css                  (extensions/extensions.css)
-  skin/classic/mozapps/extensions/update.css                      (extensions/update.css)
   skin/classic/mozapps/extensions/eula.css                        (extensions/eula.css)
   skin/classic/mozapps/extensions/blocklist.css                   (extensions/blocklist.css)
 * skin/classic/mozapps/extensions/newaddon.css                    (extensions/newaddon.css)
   skin/classic/mozapps/plugins/notifyPluginGeneric.png            (plugins/notifyPluginGeneric.png)
   skin/classic/mozapps/plugins/pluginGeneric.png                  (plugins/pluginGeneric.png)
   skin/classic/mozapps/plugins/pluginBlocked.png                  (plugins/pluginBlocked.png)
   skin/classic/mozapps/plugins/pluginBlocked-64.png               (plugins/pluginBlocked-64.png)
   skin/classic/mozapps/plugins/pluginGeneric-16.png               (plugins/pluginGeneric-16.png)
--- a/toolkit/themes/shared/non-mac.jar.inc.mn
+++ b/toolkit/themes/shared/non-mac.jar.inc.mn
@@ -56,17 +56,16 @@
   skin/classic/global/tree/sort-dsc.png                    (../../windows/global/tree/sort-dsc.png)
   skin/classic/global/tree/sort-asc-classic.png            (../../windows/global/tree/sort-asc-classic.png)
   skin/classic/global/tree/sort-dsc-classic.png            (../../windows/global/tree/sort-dsc-classic.png)
 
   skin/classic/mozapps/downloads/downloadButtons.png         (../../windows/mozapps/downloads/downloadButtons.png)
   skin/classic/mozapps/downloads/unknownContentType.css      (../../windows/mozapps/downloads/unknownContentType.css)
   skin/classic/mozapps/extensions/about.css                  (../../windows/mozapps/extensions/about.css)
   skin/classic/mozapps/extensions/blocklist.css              (../../windows/mozapps/extensions/blocklist.css)
-  skin/classic/mozapps/extensions/update.css                 (../../windows/mozapps/extensions/update.css)
   skin/classic/mozapps/extensions/discover-logo.png          (../../windows/mozapps/extensions/discover-logo.png)
   skin/classic/mozapps/extensions/rating-won.png             (../../windows/mozapps/extensions/rating-won.png)
   skin/classic/mozapps/extensions/rating-not-won.png         (../../windows/mozapps/extensions/rating-not-won.png)
   skin/classic/mozapps/extensions/cancel.png                 (../../windows/mozapps/extensions/cancel.png)
   skin/classic/mozapps/extensions/eula.css                   (../../windows/mozapps/extensions/eula.css)
   skin/classic/mozapps/handling/handling.css                 (../../windows/mozapps/handling/handling.css)
   skin/classic/mozapps/plugins/pluginBlocked-64.png          (../../windows/mozapps/plugins/pluginBlocked-64.png)
   skin/classic/mozapps/plugins/pluginHelp-16.png             (../../windows/mozapps/plugins/pluginHelp-16.png)
deleted file mode 100644
--- a/toolkit/themes/windows/mozapps/extensions/update.css
+++ /dev/null
@@ -1,28 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-.throbber {
-  list-style-image: url("chrome://global/skin/icons/loading.png");
-  width: 16px;
-  height: 16px;
-  margin-top: 5px;
-  margin-bottom: 5px;
-  margin-inline-start: 5px;
-  margin-inline-end: 2px;
-}
-
-@media (min-resolution: 1.1dppx) {
-  .throbber {
-    list-style-image: url("chrome://global/skin/icons/loading@2x.png");
-  }
-}
-
-.alertBox {
-  background-color: InfoBackground;
-  color: InfoText;
-  border: 1px outset InfoBackground;
-  margin-left: 3px;
-  margin-right: 3px;
-  padding: 5px;
-}