Bug 1614237 - Remove legacy extensions API. r=mkmelin
authorGeoff Lankow <geoff@darktrojan.net>
Mon, 10 Feb 2020 10:54:05 +1300
changeset 37273 3f3fc2c0d80474dff5953969f765e16937dde150
parent 37272 9f1fae8a80cd64a52f57b33a475df7bc2d4e306b
child 37274 5c29bf6eb78be3eda24e484bc4bf8ed5f7cfe3aa
push id2552
push userclokep@gmail.com
push dateMon, 10 Feb 2020 21:24:16 +0000
treeherdercomm-beta@f95a6f4408a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1614237
Bug 1614237 - Remove legacy extensions API. r=mkmelin
common/moz.build
common/src/ExtensionSupport.jsm
common/src/Overlays.jsm
common/src/moz.build
common/test/xpcshell/data/BootstrapMonitor.jsm
common/test/xpcshell/head_addons.js
common/test/xpcshell/test_bootstrap.js
common/test/xpcshell/test_bootstrap_const.js
common/test/xpcshell/test_bootstrap_globals.js
common/test/xpcshell/test_bootstrapped_chrome_manifest.js
common/test/xpcshell/xpcshell.ini
mail/base/content/aboutAddonsExtra.js
mail/base/content/mailWindowOverlay.js
mail/base/content/messenger.xhtml
mail/base/modules/ExtensionsUI.jsm
mail/components/extensions/ext-mail.json
mail/components/extensions/jar.mn
mail/components/extensions/parent/ext-legacy.js
mail/components/extensions/schemas/legacy.json
mail/locales/en-US/chrome/messenger/addons.properties
mail/locales/en-US/chrome/messenger/extensionsOverlay.properties
mail/locales/jar.mn
--- a/common/moz.build
+++ b/common/moz.build
@@ -3,12 +3,8 @@
 # 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/.
 
 DIRS += [
     'public',
     'src',
     'saxparser'
 ]
-
-XPCSHELL_TESTS_MANIFESTS += [
-    'test/xpcshell/xpcshell.ini',
-]
--- a/common/src/ExtensionSupport.jsm
+++ b/common/src/ExtensionSupport.jsm
@@ -4,205 +4,23 @@
 
 /**
  * Helper functions for use by extensions that should ease them plug
  * into the application.
  */
 
 this.EXPORTED_SYMBOLS = ["ExtensionSupport"];
 
-const { AddonManager } = ChromeUtils.import(
-  "resource://gre/modules/AddonManager.jsm"
-);
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-// ChromeUtils.import("resource://gre/modules/Deprecated.jsm") - needed for warning.
-const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-
-var { fixIterator } = ChromeUtils.import(
-  "resource:///modules/iteratorUtils.jsm"
-);
-const { IOUtils } = ChromeUtils.import("resource:///modules/IOUtils.jsm");
 
 var extensionHooks = new Map();
-var legacyExtensions = new Map();
-var bootstrapExtensions = new Set();
 var openWindowList;
 
 var ExtensionSupport = {
   /**
-   * A Map-like object which tracks legacy extension status. The "has" method
-   * returns only active extensions for compatibility with existing code.
-   */
-  loadedLegacyExtensions: {
-    set(id, state) {
-      legacyExtensions.set(id, state);
-    },
-    get(id) {
-      return legacyExtensions.get(id);
-    },
-    has(id) {
-      if (!legacyExtensions.has(id)) {
-        return false;
-      }
-
-      let state = legacyExtensions.get(id);
-      return !["install", "enable"].includes(state.pendingOperation);
-    },
-    hasAnyState(id) {
-      return legacyExtensions.has(id);
-    },
-    _maybeDelete(id, newPendingOperation) {
-      if (!legacyExtensions.has(id)) {
-        return;
-      }
-
-      let state = legacyExtensions.get(id);
-      if (
-        state.pendingOperation == "enable" &&
-        newPendingOperation == "disable"
-      ) {
-        legacyExtensions.delete(id);
-        this.notifyObservers(state);
-      } else if (
-        state.pendingOperation == "install" &&
-        newPendingOperation == "uninstall"
-      ) {
-        legacyExtensions.delete(id);
-        this.notifyObservers(state);
-      }
-    },
-    notifyObservers(state) {
-      let wrappedState = { wrappedJSObject: state };
-      Services.obs.notifyObservers(wrappedState, "legacy-addon-status-changed");
-    },
-    // AddonListener
-    onDisabled(ev) {
-      this._maybeDelete(ev.id, "disable");
-    },
-    onUninstalled(ev) {
-      this._maybeDelete(ev.id, "uninstall");
-    },
-    async listRemoved() {
-      let running = [...legacyExtensions.keys()];
-      let installed = await AddonManager.getAddonsByIDs(running);
-      let runningButNotInstalled = [];
-      for (let i = 0; i < running.length; i++) {
-        if (installed[i] === null) {
-          runningButNotInstalled.push(legacyExtensions.get(running[i]));
-        }
-      }
-      return runningButNotInstalled;
-    },
-  },
-
-  loadedBootstrapExtensions: bootstrapExtensions,
-
-  loadAddonPrefs(addonFile) {
-    function setPref(preferDefault, name, value) {
-      let branch = preferDefault
-        ? Services.prefs.getDefaultBranch("")
-        : Services.prefs.getBranch("");
-
-      if (typeof value == "boolean") {
-        branch.setBoolPref(name, value);
-      } else if (typeof value == "string") {
-        if (value.startsWith("chrome://") && value.endsWith(".properties")) {
-          let valueLocal = Cc[
-            "@mozilla.org/pref-localizedstring;1"
-          ].createInstance(Ci.nsIPrefLocalizedString);
-          valueLocal.data = value;
-          branch.setComplexValue(name, Ci.nsIPrefLocalizedString, valueLocal);
-        } else {
-          branch.setStringPref(name, value);
-        }
-      } else if (typeof value == "number" && Number.isInteger(value)) {
-        branch.setIntPref(name, value);
-      } else if (typeof value == "number" && Number.isFloat(value)) {
-        // Floats are set as char prefs, then retrieved using getFloatPref
-        branch.setCharPref(name, value);
-      }
-    }
-
-    function walkExtensionPrefs(extensionRoot) {
-      let prefFile = extensionRoot.clone();
-      let foundPrefStrings = [];
-      if (!prefFile.exists()) {
-        return [];
-      }
-
-      if (prefFile.isDirectory()) {
-        prefFile.append("defaults");
-        prefFile.append("preferences");
-        if (!prefFile.exists() || !prefFile.isDirectory()) {
-          return [];
-        }
-
-        let unsortedFiles = [];
-        for (let file of fixIterator(prefFile.directoryEntries, Ci.nsIFile)) {
-          if (file.isFile() && file.leafName.toLowerCase().endsWith(".js")) {
-            unsortedFiles.push(file);
-          }
-        }
-
-        for (let file of unsortedFiles.sort((a, b) =>
-          a.path < b.path ? 1 : -1
-        )) {
-          foundPrefStrings.push(IOUtils.loadFileToString(file));
-        }
-      } else if (prefFile.isFile() && prefFile.leafName.endsWith("xpi")) {
-        let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(
-          Ci.nsIZipReader
-        );
-        zipReader.open(prefFile);
-        let entries = zipReader.findEntries("defaults/preferences/*.js");
-
-        for (let entryName of [...entries].sort().reverse()) {
-          let stream = zipReader.getInputStream(entryName);
-          let entrySize = zipReader.getEntry(entryName).realSize;
-          if (entrySize > 0) {
-            let content = NetUtil.readInputStreamToString(stream, entrySize, {
-              charset: "utf-8",
-              replacement: "?",
-            });
-            foundPrefStrings.push(content);
-          }
-        }
-      }
-
-      return foundPrefStrings;
-    }
-
-    let sandbox = new Cu.Sandbox(null);
-    sandbox.pref = setPref.bind(undefined, true);
-    sandbox.user_pref = setPref.bind(undefined, false);
-
-    let prefDataStrings = walkExtensionPrefs(addonFile);
-    for (let prefDataString of prefDataStrings) {
-      try {
-        Cu.evalInSandbox(prefDataString, sandbox);
-      } catch (e) {
-        Cu.reportError(
-          "Error reading default prefs of addon " +
-            addonFile.leafName +
-            ": " +
-            e
-        );
-      }
-    }
-
-    /*
-    TODO: decide whether we need to warn the user/make addon authors to migrate away from these pref files.
-    if (prefDataStrings.length > 0) {
-      Deprecated.warning(addon.defaultLocale.name + " uses defaults/preferences/*.js files to load prefs",
-                         "https://bugzilla.mozilla.org/show_bug.cgi?id=1414398");
-    }
-    */
-  },
-
-  /**
    * Register listening for windows getting opened that will run the specified callback function
    * when a matching window is loaded.
    *
    * @param aID {String}  Some identification of the caller, usually the extension ID.
    * @param aExtensionHook {Object}  The object describing the hook the caller wants to register.
    *        Members of the object can be (all optional, but one callback must be supplied):
    *        chromeURLs {Array}         An array of strings of document URLs on which
    *                                   the given callback should run. If not specified,
@@ -410,10 +228,8 @@ var ExtensionSupport = {
       }
     }
   },
 
   get registeredWindowListenerCount() {
     return extensionHooks.size;
   },
 };
-
-AddonManager.addAddonListener(ExtensionSupport.loadedLegacyExtensions);
deleted file mode 100644
--- a/common/src/Overlays.jsm
+++ /dev/null
@@ -1,603 +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/. */
-
-/**
- * Load overlays in a similar way as XUL did for legacy XUL add-ons.
- */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["Overlays"];
-
-const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
-ChromeUtils.defineModuleGetter(
-  this,
-  "Services",
-  "resource://gre/modules/Services.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "setTimeout",
-  "resource://gre/modules/Timer.jsm"
-);
-
-let oconsole = new ConsoleAPI({
-  prefix: "Overlays.jsm",
-  consoleID: "overlays-jsm",
-  maxLogLevelPref: "extensions.overlayloader.loglevel",
-});
-
-/**
- * The overlays class, providing support for loading overlays like they used to work. This class
- * should likely be called through its static method Overlays.load()
- */
-class Overlays {
-  /**
-   * Load overlays for the given window using the overlay provider, which can for example be a
-   * ChromeManifest object.
-   *
-   * @param {ChromeManifest} overlayProvider        The overlay provider that contains information
-   *                                                  about styles and overlays.
-   * @param {DOMWindow} window                      The window to load into
-   */
-  static load(overlayProvider, window) {
-    let instance = new Overlays(overlayProvider, window);
-
-    let urls = overlayProvider.overlay.get(instance.location, false);
-    instance.load(urls);
-  }
-
-  /**
-   * Constructs the overlays instance. This class should be called via Overlays.load() instead.
-   *
-   * @param {ChromeManifest} overlayProvider        The overlay provider that contains information
-   *                                                  about styles and overlays.
-   * @param {DOMWindow} window                      The window to load into
-   */
-  constructor(overlayProvider, window) {
-    this.overlayProvider = overlayProvider;
-    this.window = window;
-    if (window.location.protocol == "about:") {
-      this.location = window.location.protocol + window.location.pathname;
-    } else {
-      this.location = window.location.origin + window.location.pathname;
-    }
-  }
-
-  /**
-   * A shorthand to this.window.document
-   */
-  get document() {
-    return this.window.document;
-  }
-
-  /**
-   * Loads the given urls into the window, recursively loading further overlays as provided by the
-   * overlayProvider.
-   *
-   * @param {String[]} urls                         The urls to load
-   */
-  load(urls) {
-    let unloadedOverlays = this._collectOverlays(this.document).concat(urls);
-    let forwardReferences = [];
-    let unloadedScripts = [];
-    let unloadedSheets = [];
-    this._toolbarsToResolve = [];
-    let xulStore = Services.xulStore;
-    this.persistedIDs = new Set();
-
-    // Load css styles from the registry
-    for (let sheet of this.overlayProvider.style.get(this.location, false)) {
-      unloadedSheets.push(sheet);
-    }
-
-    if (!unloadedOverlays.length && !unloadedSheets.length) {
-      return;
-    }
-
-    while (unloadedOverlays.length) {
-      let url = unloadedOverlays.shift();
-      let xhr = this.fetchOverlay(url);
-      let doc = xhr.responseXML;
-
-      oconsole.debug(`Applying ${url} to ${this.location}`);
-
-      // clean the document a bit
-      let emptyNodes = doc.evaluate(
-        "//text()[normalize-space(.) = '']",
-        doc,
-        null,
-        7,
-        null
-      );
-      for (let i = 0, len = emptyNodes.snapshotLength; i < len; ++i) {
-        let node = emptyNodes.snapshotItem(i);
-        node.remove();
-      }
-
-      let commentNodes = doc.evaluate("//comment()", doc, null, 7, null);
-      for (let i = 0, len = commentNodes.snapshotLength; i < len; ++i) {
-        let node = commentNodes.snapshotItem(i);
-        node.remove();
-      }
-
-      // Force a re-evaluation of inline styles to work around an issue
-      // causing inline styles to be initially ignored.
-      let styledNodes = doc.evaluate("//*[@style]", doc, null, 7, null);
-      for (let i = 0, len = styledNodes.snapshotLength; i < len; ++i) {
-        let node = styledNodes.snapshotItem(i);
-        node.style.display = node.style.display; // eslint-disable-line no-self-assign
-      }
-
-      // Load css styles from the registry
-      for (let sheet of this.overlayProvider.style.get(url, false)) {
-        unloadedSheets.push(sheet);
-      }
-
-      // Load css processing instructions from the overlay
-      let stylesheets = doc.evaluate(
-        "/processing-instruction('xml-stylesheet')",
-        doc,
-        null,
-        7,
-        null
-      );
-      for (let i = 0, len = stylesheets.snapshotLength; i < len; ++i) {
-        let node = stylesheets.snapshotItem(i);
-        let match = node.nodeValue.match(/href=["']([^"']*)["']/);
-        if (match) {
-          unloadedSheets.push(new URL(match[1], node.baseURI).href);
-        }
-      }
-
-      // Prepare loading further nested xul overlays from the overlay
-      unloadedOverlays.push(...this._collectOverlays(doc));
-
-      // Prepare loading further nested xul overlays from the registry
-      for (let overlayUrl of this.overlayProvider.overlay.get(url, false)) {
-        unloadedOverlays.push(overlayUrl);
-      }
-
-      // Run through all overlay nodes on the first level (hookup nodes). Scripts will be deferred
-      // until later for simplicity (c++ code seems to process them earlier?).
-      for (let node of doc.documentElement.children) {
-        if (node.localName == "script") {
-          unloadedScripts.push(node);
-        } else {
-          forwardReferences.push(node);
-        }
-      }
-    }
-
-    let ids = xulStore.getIDsEnumerator(this.location);
-    while (ids.hasMore()) {
-      this.persistedIDs.add(ids.getNext());
-    }
-
-    // At this point, all (recursive) overlays are loaded. Unloaded scripts and sheets are ready and
-    // in order, and forward references are good to process.
-    let previous = 0;
-    while (forwardReferences.length && forwardReferences.length != previous) {
-      previous = forwardReferences.length;
-      let unresolved = [];
-
-      for (let ref of forwardReferences) {
-        if (!this._resolveForwardReference(ref)) {
-          unresolved.push(ref);
-        }
-      }
-
-      forwardReferences = unresolved;
-    }
-
-    if (forwardReferences.length) {
-      oconsole.warn(
-        `Could not resolve ${forwardReferences.length} references`,
-        forwardReferences
-      );
-    }
-
-    // Loading the sheets now to avoid race conditions with xbl bindings
-    for (let sheet of unloadedSheets) {
-      this.loadCSS(sheet);
-    }
-
-    this._decksToResolve = new Map();
-    for (let id of this.persistedIDs.values()) {
-      let element = this.document.getElementById(id);
-      if (element) {
-        let attrNames = xulStore.getAttributeEnumerator(this.location, id);
-        while (attrNames.hasMore()) {
-          let attrName = attrNames.getNext();
-          let attrValue = xulStore.getValue(this.location, id, attrName);
-          if (attrName == "selectedIndex" && element.localName == "deck") {
-            this._decksToResolve.set(element, attrValue);
-          } else if (
-            element != this.document.documentElement ||
-            !["height", "screenX", "screenY", "sizemode", "width"].includes(
-              attrName
-            )
-          ) {
-            element.setAttribute(attrName, attrValue);
-          }
-        }
-      }
-    }
-
-    // We've resolved all the forward references we can, we can now go ahead and load the scripts
-    let deferredLoad = [];
-    for (let script of unloadedScripts) {
-      deferredLoad.push(...this.loadScript(script));
-    }
-
-    if (this.document.readyState == "complete") {
-      setTimeout(() => {
-        this._finish();
-
-        // Now execute load handlers since we are done loading scripts
-        let bubbles = [];
-        for (let { listener, useCapture } of deferredLoad) {
-          if (useCapture) {
-            this._fireEventListener(listener);
-          } else {
-            bubbles.push(listener);
-          }
-        }
-
-        for (let listener of bubbles) {
-          this._fireEventListener(listener);
-        }
-      });
-    } else {
-      this.document.defaultView.addEventListener(
-        "load",
-        this._finish.bind(this),
-        { once: true }
-      );
-    }
-  }
-
-  _finish() {
-    for (let [deck, selectedIndex] of this._decksToResolve.entries()) {
-      deck.setAttribute("selectedIndex", selectedIndex);
-    }
-
-    for (let bar of this._toolbarsToResolve) {
-      let currentset = Services.xulStore.getValue(
-        this.location,
-        bar.id,
-        "currentset"
-      );
-      if (currentset) {
-        bar.currentSet = currentset;
-      } else if (bar.getAttribute("defaultset")) {
-        bar.currentSet = bar.getAttribute("defaultset");
-      }
-    }
-  }
-
-  /**
-   * Gets the overlays referenced by processing instruction on a document.
-   *
-   * @param {DOMDocument} document  The document to read instuctions from
-   * @return {String[]}             URLs of the overlays from the document
-   */
-  _collectOverlays(doc) {
-    let urls = [];
-    let instructions = doc.evaluate(
-      "/processing-instruction('xul-overlay')",
-      doc,
-      null,
-      7,
-      null
-    );
-    for (let i = 0, len = instructions.snapshotLength; i < len; ++i) {
-      let node = instructions.snapshotItem(i);
-      let match = node.nodeValue.match(/href=["']([^"']*)["']/);
-      if (match) {
-        urls.push(match[1]);
-      }
-    }
-    return urls;
-  }
-
-  /**
-   * Fires a "load" event for the given listener, using the current window
-   *
-   * @param {EventListener|Function} listener       The event listener to call
-   */
-  _fireEventListener(listener) {
-    let fakeEvent = new this.window.UIEvent("load", { view: this.window });
-    if (typeof listener == "function") {
-      listener(fakeEvent);
-    } else if (listener && typeof listener == "object") {
-      listener.handleEvent(fakeEvent);
-    } else {
-      oconsole.error("Unknown listener type", listener);
-    }
-  }
-
-  /**
-   * Resolves forward references for the given node. If the node exists in the target document, it
-   * is merged in with the target node. If the node has no id it is inserted at documentElement
-   * level.
-   *
-   * @param {Element} node          The DOM Element to resolve in the target document.
-   * @return {Boolean}              True, if the node was merged/inserted, false otherwise
-   */
-  _resolveForwardReference(node) {
-    if (node.id) {
-      let target = this.document.getElementById(node.id);
-      if (node.localName == "toolbarpalette") {
-        let box;
-        if (target) {
-          box = target.closest("toolbox");
-        } else {
-          // These vanish from the document but still exist via the palette property
-          let boxes = [...this.document.getElementsByTagName("toolbox")];
-          box = boxes.find(box => box.palette && box.palette.id == node.id);
-          let palette = box ? box.palette : null;
-
-          if (!palette) {
-            oconsole.debug(
-              `The palette for ${
-                node.id
-              } could not be found, deferring to later`
-            );
-            return false;
-          }
-
-          target = palette;
-        }
-
-        this._toolbarsToResolve.push(...box.querySelectorAll("toolbar"));
-        this._toolbarsToResolve.push(
-          ...this.document.querySelectorAll(`toolbar[toolboxid="${box.id}"]`)
-        );
-      } else if (!target) {
-        oconsole.debug(
-          `The node ${node.id} could not be found, deferring to later`
-        );
-        return false;
-      }
-
-      this._mergeElement(target, node);
-    } else {
-      this._insertElement(this.document.documentElement, node);
-    }
-    return true;
-  }
-
-  /**
-   * Insert the node in the given parent, observing the insertbefore/insertafter/position attributes
-   *
-   * @param {Element} parent        The parent element to insert the node into.
-   * @param {Element} node          The node to insert.
-   */
-  _insertElement(parent, node) {
-    // These elements need their values set before they are added to
-    // the document, or bad things happen.
-    for (let element of node.querySelectorAll("menulist")) {
-      if (element.id && this.persistedIDs.has(element.id)) {
-        element.setAttribute(
-          "value",
-          Services.xulStore.getValue(this.location, element.id, "value")
-        );
-      }
-    }
-
-    if (node.localName == "toolbar") {
-      this._toolbarsToResolve.push(node);
-    } else {
-      this._toolbarsToResolve.push(...node.querySelectorAll("toolbar"));
-    }
-
-    let wasInserted = false;
-    let pos = node.getAttribute("insertafter");
-    let after = true;
-
-    if (!pos) {
-      pos = node.getAttribute("insertbefore");
-      after = false;
-    }
-
-    if (pos) {
-      for (let id of pos.split(",")) {
-        let targetchild = this.document.getElementById(id);
-        if (targetchild && targetchild.parentNode == parent) {
-          parent.insertBefore(
-            node,
-            after ? targetchild.nextElementSibling : targetchild
-          );
-          wasInserted = true;
-          break;
-        }
-      }
-    }
-
-    if (!wasInserted) {
-      // position is 1-based
-      let position = parseInt(node.getAttribute("position"), 10);
-      if (position > 0 && position - 1 <= parent.children.length) {
-        parent.insertBefore(node, parent.children[position - 1]);
-        wasInserted = true;
-      }
-    }
-
-    if (!wasInserted) {
-      parent.appendChild(node);
-    }
-  }
-
-  /**
-   * Merge the node into the target, adhering to the removeelement attribute, merging further
-   * attributes into the target node, and merging children as appropriate for xul nodes. If a child
-   * has an id, it will be searched in the target document and recursively merged.
-   *
-   * @param {Element} target        The node to merge into
-   * @param {Element} node          The node that is being merged
-   */
-  _mergeElement(target, node) {
-    for (let attribute of node.attributes) {
-      if (attribute.name == "id") {
-        continue;
-      }
-
-      if (attribute.name == "removeelement" && attribute.value == "true") {
-        target.remove();
-        return;
-      }
-
-      target.setAttributeNS(
-        attribute.namespaceURI,
-        attribute.name,
-        attribute.value
-      );
-    }
-
-    for (let i = 0, len = node.childElementCount; i < len; i++) {
-      let child = node.firstElementChild;
-      child.remove();
-
-      let elementInDocument = child.id
-        ? this.document.getElementById(child.id)
-        : null;
-      let parentId = elementInDocument ? elementInDocument.parentNode.id : null;
-
-      if (parentId && parentId == target.id) {
-        this._mergeElement(elementInDocument, child);
-      } else {
-        this._insertElement(target, child);
-      }
-    }
-  }
-
-  /**
-   * Fetches the overlay from the given chrome:// or resource:// URL. This happen synchronously so
-   * we have a chance to complete before the load event.
-   *
-   * @param {String} srcUrl                         The URL to load
-   * @return {XMLHttpRequest}                       The completed XHR.
-   */
-  fetchOverlay(srcUrl) {
-    if (!srcUrl.startsWith("chrome://") && !srcUrl.startsWith("resource://")) {
-      throw new Error(
-        "May only load overlays from chrome:// or resource:// uris"
-      );
-    }
-
-    let xhr = new this.window.XMLHttpRequest();
-    xhr.overrideMimeType("application/xml");
-    xhr.open("GET", srcUrl, false);
-
-    // Elevate the request, so DTDs will work. Should not be a security issue since we
-    // only load chrome, resource and file URLs, and that is our privileged chrome package.
-    try {
-      xhr.channel.owner = Services.scriptSecurityManager.getSystemPrincipal();
-    } catch (ex) {
-      oconsole.error(
-        "Failed to set system principal while fetching overlay " + srcUrl
-      );
-      xhr.close();
-      throw new Error("Failed to set system principal");
-    }
-
-    xhr.send(null);
-    return xhr;
-  }
-
-  /**
-   * Loads scripts described by the given script node. The node can either have a src attribute, or
-   * be an inline script with textContent.
-   *
-   * @param {Element} node                          The <script> element to load the script from
-   * @return {Object[]}                             An object with listener and useCapture,
-   *                                                  describing load handlers the script creates
-   *                                                  when first run.
-   */
-  loadScript(node) {
-    let deferredLoad = [];
-
-    let oldAddEventListener = this.window.addEventListener;
-    if (this.document.readyState == "complete") {
-      this.window.addEventListener = function(
-        type,
-        listener,
-        useCapture,
-        ...args
-      ) {
-        if (type == "load") {
-          if (typeof useCapture == "object") {
-            useCapture = useCapture.capture;
-          }
-
-          if (typeof useCapture == "undefined") {
-            useCapture = true;
-          }
-          deferredLoad.push({ listener, useCapture });
-          return null;
-        }
-        return oldAddEventListener.call(
-          this,
-          type,
-          listener,
-          useCapture,
-          ...args
-        );
-      };
-    }
-
-    if (node.hasAttribute("src")) {
-      let url = new URL(node.getAttribute("src"), node.baseURI).href;
-      oconsole.debug(`Loading script ${url} into ${this.window.location}`);
-      try {
-        Services.scriptloader.loadSubScript(url, this.window);
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-    } else if (node.textContent) {
-      oconsole.debug(`Loading eval'd script into ${this.window.location}`);
-      try {
-        let dataURL =
-          "data:application/javascript," + encodeURIComponent(node.textContent);
-        // It would be great if we could have script errors show the right url, but for now
-        // loadSubScript will have to do.
-        Services.scriptloader.loadSubScript(dataURL, this.window);
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-    }
-
-    if (this.document.readyState == "complete") {
-      this.window.addEventListener = oldAddEventListener;
-    }
-
-    // This works because we only care about immediately executed addEventListener calls and
-    // loadSubScript is synchronous. Everyone else should be checking readyState anyway.
-    return deferredLoad;
-  }
-
-  /**
-   * Load the CSS stylesheet from the given url
-   *
-   * @param {String} url        The url to load from
-   * @return {Element}          An HTML link element for this stylesheet
-   */
-  loadCSS(url) {
-    oconsole.debug(`Loading ${url} into ${this.window.location}`);
-
-    // domWindowUtils.loadSheetUsingURIString doesn't record the sheet in document.styleSheets,
-    // adding a html link element seems to do so.
-    let link = this.document.createElementNS(
-      "http://www.w3.org/1999/xhtml",
-      "link"
-    );
-    link.setAttribute("rel", "stylesheet");
-    link.setAttribute("type", "text/css");
-    link.setAttribute("href", url);
-
-    this.document.documentElement.appendChild(link);
-    return link;
-  }
-}
--- a/common/src/moz.build
+++ b/common/src/moz.build
@@ -1,17 +1,16 @@
 # vim: set filetype=python:
 # 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/.
 
 EXTRA_JS_MODULES += [
     'ChromeManifest.jsm',
     'ExtensionSupport.jsm',
-    'Overlays.jsm',
 ]
 
 SOURCES += [
     'nsCommonModule.cpp',
     'nsComponentManagerExtra.cpp',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
deleted file mode 100644
--- a/common/test/xpcshell/data/BootstrapMonitor.jsm
+++ /dev/null
@@ -1,41 +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/. */
-
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-var EXPORTED_SYMBOLS = ["monitor"];
-
-function notify(event, originalMethod, data, reason) {
-  let info = {
-    event,
-    data: Object.assign({}, data, {
-      resourceURI: data.resourceURI.spec,
-    }),
-    reason,
-  };
-
-  let subject = { wrappedJSObject: { data } };
-
-  Services.obs.notifyObservers(
-    subject,
-    "bootstrapmonitor-event",
-    JSON.stringify(info)
-  );
-
-  // If the bootstrap scope already declares a method call it
-  if (originalMethod) {
-    originalMethod(data, reason);
-  }
-}
-
-// Allows a simple one-line bootstrap script:
-// Components.utils.import("resource://xpcshelldata/bootstrapmonitor.jsm").monitor(this);
-var monitor = function(
-  scope,
-  methods = ["install", "startup", "shutdown", "uninstall"]
-) {
-  for (let event of methods) {
-    scope[event] = notify.bind(null, event, scope[event]);
-  }
-};
deleted file mode 100644
--- a/common/test/xpcshell/head_addons.js
+++ /dev/null
@@ -1,1534 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
-const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
-const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
-const PREF_COMPAT_OVERRIDES = "extensions.getAddons.compatOverides.url";
-const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
-
-const PREF_DISABLE_SECURITY =
-  "security.turn_off_all_security_so_that_" +
-  "viruses_can_take_over_this_computer";
-
-// Maximum error in file modification times. Some file systems don't store
-// modification times exactly. As long as we are closer than this then it
-// still passes.
-const MAX_TIME_DIFFERENCE = 3000;
-
-// Time to reset file modified time relative to Date.now() so we can test that
-// times are modified (10 hours old).
-const MAKE_FILE_OLD_DIFFERENCE = 10 * 3600 * 1000;
-
-var { AppConstants } = ChromeUtils.import(
-  "resource://gre/modules/AppConstants.jsm"
-);
-var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
-var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var { XPCOMUtils } = ChromeUtils.import(
-  "resource://gre/modules/XPCOMUtils.jsm"
-);
-var { AddonRepository } = ChromeUtils.import(
-  "resource://gre/modules/addons/AddonRepository.jsm"
-);
-var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
-
-var { AddonTestUtils, MockAsyncShutdown } = ChromeUtils.import(
-  "resource://testing-common/AddonTestUtils.jsm"
-);
-
-ChromeUtils.defineModuleGetter(
-  this,
-  "Blocklist",
-  "resource://gre/modules/Blocklist.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "Extension",
-  "resource://gre/modules/Extension.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "ExtensionTestUtils",
-  "resource://testing-common/ExtensionXPCShellUtils.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "ExtensionTestCommon",
-  "resource://testing-common/ExtensionTestCommon.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "HttpServer",
-  "resource://testing-common/httpd.js"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "MockRegistrar",
-  "resource://testing-common/MockRegistrar.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "MockRegistry",
-  "resource://testing-common/MockRegistry.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "PromiseTestUtils",
-  "resource://testing-common/PromiseTestUtils.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "TestUtils",
-  "resource://testing-common/TestUtils.jsm"
-);
-
-XPCOMUtils.defineLazyServiceGetter(
-  this,
-  "aomStartup",
-  "@mozilla.org/addons/addon-manager-startup;1",
-  "amIAddonManagerStartup"
-);
-
-const {
-  createAppInfo,
-  createHttpServer,
-  createTempWebExtensionFile,
-  getFileForAddon,
-  manuallyInstall,
-  manuallyUninstall,
-  overrideBuiltIns,
-  promiseAddonEvent,
-  promiseCompleteAllInstalls,
-  promiseCompleteInstall,
-  promiseConsoleOutput,
-  promiseFindAddonUpdates,
-  promiseInstallAllFiles,
-  promiseInstallFile,
-  promiseRestartManager,
-  promiseSetExtensionModifiedTime,
-  promiseShutdownManager,
-  promiseStartupManager,
-  promiseWebExtensionStartup,
-  promiseWriteProxyFileToDir,
-  registerDirectory,
-  setExtensionModifiedTime,
-  writeFilesToZip,
-} = AddonTestUtils;
-
-// WebExtension wrapper for ease of testing
-ExtensionTestUtils.init(this);
-
-AddonTestUtils.init(this, false);
-AddonTestUtils.overrideCertDB();
-
-XPCOMUtils.defineLazyGetter(
-  this,
-  "BOOTSTRAP_REASONS",
-  () => AddonManagerPrivate.BOOTSTRAP_REASONS
-);
-
-function getReasonName(reason) {
-  for (let key of Object.keys(BOOTSTRAP_REASONS)) {
-    if (BOOTSTRAP_REASONS[key] == reason) {
-      return key;
-    }
-  }
-  throw new Error("This shouldn't happen.");
-}
-
-Object.defineProperty(this, "gAppInfo", {
-  get() {
-    return AddonTestUtils.appInfo;
-  },
-});
-
-Object.defineProperty(this, "gAddonStartup", {
-  get() {
-    return AddonTestUtils.addonStartup.clone();
-  },
-});
-
-Object.defineProperty(this, "gInternalManager", {
-  get() {
-    return AddonTestUtils.addonIntegrationService.QueryInterface(
-      Ci.nsITimerCallback
-    );
-  },
-});
-
-Object.defineProperty(this, "gProfD", {
-  get() {
-    return AddonTestUtils.profileDir.clone();
-  },
-});
-
-Object.defineProperty(this, "gTmpD", {
-  get() {
-    return AddonTestUtils.tempDir.clone();
-  },
-});
-
-Object.defineProperty(this, "gUseRealCertChecks", {
-  get() {
-    return AddonTestUtils.useRealCertChecks;
-  },
-  set(val) {
-    return (AddonTestUtils.useRealCertChecks = val);
-  },
-});
-
-Object.defineProperty(this, "TEST_UNPACKED", {
-  get() {
-    return AddonTestUtils.testUnpacked;
-  },
-  set(val) {
-    return (AddonTestUtils.testUnpacked = val);
-  },
-});
-
-// We need some internal bits of AddonManager
-var {
-  AddonManager,
-  AddonManagerInternal,
-  AddonManagerPrivate,
-} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-
-const promiseAddonByID = AddonManager.getAddonByID;
-const promiseAddonsByIDs = AddonManager.getAddonsByIDs;
-
-var gPort = null;
-var gUrlToFileMap = {};
-
-// Map resource://xpcshell-data/ to the data directory
-var resHandler = Services.io
-  .getProtocolHandler("resource")
-  .QueryInterface(Ci.nsISubstitutingProtocolHandler);
-// Allow non-existent files because of bug 1207735
-var dataURI = NetUtil.newURI(do_get_file("data", true));
-resHandler.setSubstitution("xpcshell-data", dataURI);
-
-function isManifestRegistered(file) {
-  let manifests = Components.manager.getManifestLocations();
-  for (let i = 0; i < manifests.length; i++) {
-    let manifest = manifests.queryElementAt(i, Ci.nsIURI);
-
-    // manifest is the url to the manifest file either in an XPI or a directory.
-    // We want the location of the XPI or directory itself.
-    if (manifest instanceof Ci.nsIJARURI) {
-      manifest = manifest.JARFile.QueryInterface(Ci.nsIFileURL).file;
-    } else if (manifest instanceof Ci.nsIFileURL) {
-      manifest = manifest.file.parent;
-    } else {
-      continue;
-    }
-
-    if (manifest.equals(file)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-const BOOTSTRAP_MONITOR_BOOTSTRAP_JS = `
-  ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
-`;
-
-// Listens to messages from bootstrap.js telling us what add-ons were started
-// and stopped etc. and performs some sanity checks that only installed add-ons
-// are started etc.
-this.BootstrapMonitor = {
-  inited: false,
-
-  // Contain the current state of add-ons in the system
-  installed: new Map(),
-  started: new Map(),
-
-  // Contain the last state of shutdown and uninstall calls for an add-on
-  stopped: new Map(),
-  uninstalled: new Map(),
-
-  startupPromises: [],
-  installPromises: [],
-
-  restartfulIds: new Set(),
-
-  init() {
-    this.inited = true;
-    Services.obs.addObserver(this, "bootstrapmonitor-event");
-  },
-
-  shutdownCheck() {
-    if (!this.inited) {
-      return;
-    }
-
-    Assert.equal(this.started.size, 0);
-  },
-
-  clear(id) {
-    this.installed.delete(id);
-    this.started.delete(id);
-    this.stopped.delete(id);
-    this.uninstalled.delete(id);
-  },
-
-  promiseAddonStartup(id) {
-    return new Promise(resolve => {
-      this.startupPromises.push(resolve);
-    });
-  },
-
-  promiseAddonInstall(id) {
-    return new Promise(resolve => {
-      this.installPromises.push(resolve);
-    });
-  },
-
-  checkMatches(cached, current) {
-    Assert.notEqual(cached, undefined);
-    Assert.equal(current.data.version, cached.data.version);
-    Assert.equal(current.data.installPath, cached.data.installPath);
-    Assert.ok(
-      Services.io
-        .newURI(current.data.resourceURI)
-        .equals(Services.io.newURI(cached.data.resourceURI)),
-      `Resource URIs match: "${current.data.resourceURI}" == "${
-        cached.data.resourceURI
-      }"`
-    );
-  },
-
-  checkAddonStarted(id, version = undefined) {
-    let started = this.started.get(id);
-    Assert.notEqual(started, undefined);
-    if (version != undefined) {
-      Assert.equal(started.data.version, version);
-    }
-
-    // Chrome should be registered by now
-    // Get the install path from the resource URI, since it is not passed any longer.
-    let uri = Services.io.newURI(started.data.resourceURI);
-    let jarFile = uri.QueryInterface(Ci.nsIJARURI).JARFile;
-    let installPath = jarFile.QueryInterface(Ci.nsIFileURL).file;
-
-    let isRegistered = isManifestRegistered(installPath);
-    Assert.ok(isRegistered);
-  },
-
-  checkAddonNotStarted(id) {
-    Assert.ok(!this.started.has(id));
-  },
-
-  checkAddonInstalled(id, version = undefined) {
-    const installed = this.installed.get(id);
-    notEqual(installed, undefined);
-    if (version !== undefined) {
-      equal(installed.data.version, version);
-    }
-    return installed;
-  },
-
-  checkAddonNotInstalled(id) {
-    Assert.ok(!this.installed.has(id));
-  },
-
-  observe(subject, topic, data) {
-    let info = JSON.parse(data);
-    let id = info.data.id;
-
-    // Get the install path from the resource URI, since it is not passed any longer.
-    let uri = Services.io.newURI(info.data.resourceURI);
-    let jarFile = uri.QueryInterface(Ci.nsIJARURI).JARFile;
-    let installPath = jarFile.QueryInterface(Ci.nsIFileURL).file;
-
-    if (subject && subject.wrappedJSObject) {
-      // NOTE: in some of the new tests, we need to received the real objects instead of
-      // their JSON representations, but most of the current tests expect intallPath
-      // and resourceURI to have been converted to strings.
-      info.data = Object.assign({}, subject.wrappedJSObject.data, {
-        installPath: info.data.installPath,
-        resourceURI: info.data.resourceURI,
-      });
-    }
-
-    // If this is the install event the add-ons shouldn't already be installed
-    if (info.event == "install") {
-      this.checkAddonNotInstalled(id);
-
-      this.installed.set(id, info);
-
-      for (let resolve of this.installPromises) {
-        resolve();
-      }
-      this.installPromises = [];
-    } else {
-      this.checkMatches(this.installed.get(id), info);
-    }
-
-    // If this is the shutdown event than the add-on should already be started
-    if (info.event == "shutdown") {
-      this.checkMatches(this.started.get(id), info);
-
-      this.started.delete(id);
-      this.stopped.set(id, info);
-
-      // Chrome should still be registered at this point
-      let isRegistered = isManifestRegistered(installPath);
-      Assert.ok(isRegistered);
-
-      // XPIProvider doesn't bother unregistering chrome on app shutdown but
-      // since we simulate restarts we must do so manually to keep the registry
-      // consistent.
-      if (info.reason == 2 /* APP_SHUTDOWN */) {
-        Components.manager.removeBootstrappedManifestLocation(installPath);
-      }
-    } else {
-      this.checkAddonNotStarted(id);
-    }
-
-    if (info.event == "uninstall") {
-      // We currently support registering, but not unregistering,
-      // restartful add-on manifests during xpcshell AOM "restarts".
-      if (!this.restartfulIds.has(id)) {
-        // Chrome should be unregistered at this point
-        let isRegistered = isManifestRegistered(installPath);
-        Assert.ok(!isRegistered);
-      }
-
-      this.installed.delete(id);
-      this.uninstalled.set(id, info);
-    } else if (info.event == "startup") {
-      this.started.set(id, info);
-
-      // Chrome should be registered at this point
-      let isRegistered = isManifestRegistered(installPath);
-      Assert.ok(isRegistered);
-
-      for (let resolve of this.startupPromises) {
-        resolve();
-      }
-      this.startupPromises = [];
-    }
-  },
-};
-
-AddonTestUtils.on("addon-manager-shutdown", () => {
-  BootstrapMonitor.shutdownCheck();
-  Cu.unload("resource:///modules/BootstrapLoader.jsm");
-});
-
-var SlightlyLessDodgyBootstrapMonitor = {
-  started: new Map(),
-  stopped: new Map(),
-  installed: new Map(),
-  uninstalled: new Map(),
-
-  init() {
-    this.onEvent = this.onEvent.bind(this);
-
-    AddonTestUtils.on("addon-manager-shutdown", this.onEvent);
-    AddonTestUtils.on("bootstrap-method", this.onEvent);
-  },
-
-  shutdownCheck() {
-    equal(
-      this.started.size,
-      0,
-      "Should have no add-ons that were started but not shutdown"
-    );
-  },
-
-  onEvent(msg, data) {
-    switch (msg) {
-      case "addon-manager-shutdown":
-        this.shutdownCheck();
-        break;
-      case "bootstrap-method":
-        this.onBootstrapMethod(data.method, data.params, data.reason);
-        break;
-    }
-  },
-
-  onBootstrapMethod(method, params, reason) {
-    let { id } = params;
-
-    info(
-      `Bootstrap method ${method} for ${params.id} version ${params.version}`
-    );
-
-    if (method !== "install") {
-      this.checkInstalled(id);
-    }
-
-    switch (method) {
-      case "install":
-        this.checkNotInstalled(id);
-        this.installed.set(id, { reason, params });
-        this.uninstalled.delete(id);
-        break;
-      case "startup":
-        this.checkNotStarted(id);
-        this.started.set(id, { reason, params });
-        this.stopped.delete(id);
-        break;
-      case "shutdown":
-        this.checkMatches("shutdown", "startup", params, this.started.get(id));
-        this.checkStarted(id);
-        this.stopped.set(id, { reason, params });
-        this.started.delete(id);
-        break;
-      case "uninstall":
-        this.checkMatches(
-          "uninstall",
-          "install",
-          params,
-          this.installed.get(id)
-        );
-        this.uninstalled.set(id, { reason, params });
-        this.installed.delete(id);
-        break;
-      case "update":
-        this.checkMatches("update", "install", params, this.installed.get(id));
-        this.installed.set(id, { reason, params });
-        break;
-    }
-  },
-
-  clear(id) {
-    this.installed.delete(id);
-    this.started.delete(id);
-    this.stopped.delete(id);
-    this.uninstalled.delete(id);
-  },
-
-  checkMatches(method, lastMethod, params, { params: lastParams } = {}) {
-    ok(
-      lastParams,
-      `Expecting matching ${lastMethod} call for add-on ${
-        params.id
-      } ${method} call`
-    );
-
-    if (method == "update") {
-      equal(
-        params.oldVersion,
-        lastParams.version,
-        "params.version should match last call"
-      );
-    } else {
-      equal(
-        params.version,
-        lastParams.version,
-        "params.version should match last call"
-      );
-    }
-
-    if (method !== "update" && method !== "uninstall") {
-      equal(
-        params.installPath.path,
-        lastParams.installPath.path,
-        `params.installPath should match last call`
-      );
-
-      ok(
-        params.resourceURI.equals(lastParams.resourceURI),
-        `params.resourceURI should match: "${params.resourceURI.spec}" == "${
-          lastParams.resourceURI.spec
-        }"`
-      );
-    }
-  },
-
-  checkStarted(id, version = undefined) {
-    let started = this.started.get(id);
-    ok(started, `Should have seen startup method call for ${id}`);
-
-    if (version !== undefined) {
-      equal(started.params.version, version, "Expected version number");
-    }
-  },
-
-  checkNotStarted(id) {
-    ok(
-      !this.started.has(id),
-      `Should not have seen startup method call for ${id}`
-    );
-  },
-
-  checkInstalled(id, version = undefined) {
-    const installed = this.installed.get(id);
-    ok(installed, `Should have seen install call for ${id}`);
-
-    if (version !== undefined) {
-      equal(installed.params.version, version, "Expected version number");
-    }
-
-    return installed;
-  },
-
-  checkNotInstalled(id) {
-    ok(
-      !this.installed.has(id),
-      `Should not have seen install method call for ${id}`
-    );
-  },
-};
-
-function isNightlyChannel() {
-  var channel = Services.prefs.getCharPref("app.update.channel", "default");
-
-  return (
-    channel != "aurora" &&
-    channel != "beta" &&
-    channel != "release" &&
-    channel != "esr"
-  );
-}
-
-async function restartWithLocales(locales) {
-  Services.locale.requestedLocales = locales;
-  await promiseRestartManager();
-}
-
-/**
- * Returns a map of Addon objects for installed add-ons with the given
- * IDs. The returned map contains a key for the ID of each add-on that
- * is found. IDs for add-ons which do not exist are not present in the
- * map.
- *
- * @param {sequence<string>} ids
- *        The list of add-on IDs to get.
- * @returns {Promise<string, Addon>}
- *        Map of add-ons that were found.
- */
-async function getAddons(ids) {
-  let addons = new Map();
-  for (let addon of await AddonManager.getAddonsByIDs(ids)) {
-    if (addon) {
-      addons.set(addon.id, addon);
-    }
-  }
-  return addons;
-}
-
-/**
- * Checks that the given add-on has the given expected properties.
- *
- * @param {string} id
- *        The id of the add-on.
- * @param {Addon?} addon
- *        The add-on object, or null if the add-on does not exist.
- * @param {object?} expected
- *        An object containing the expected values for properties of the
- *        add-on, or null if the add-on is expected not to exist.
- */
-function checkAddon(id, addon, expected) {
-  info(`Checking state of addon ${id}`);
-
-  if (expected === null) {
-    ok(!addon, `Addon ${id} should not exist`);
-  } else {
-    ok(addon, `Addon ${id} should exist`);
-    for (let [key, value] of Object.entries(expected)) {
-      if (value instanceof Ci.nsIURI) {
-        equal(
-          addon[key] && addon[key].spec,
-          value.spec,
-          `Expected value of addon.${key}`
-        );
-      } else {
-        deepEqual(addon[key], value, `Expected value of addon.${key}`);
-      }
-    }
-  }
-}
-
-/**
- * Tests that an add-on does appear in the crash report annotations, if
- * crash reporting is enabled. The test will fail if the add-on is not in the
- * annotation.
- * @param  aId
- *         The ID of the add-on
- * @param  aVersion
- *         The version of the add-on
- */
-function do_check_in_crash_annotation(aId, aVersion) {
-  if (!AppConstants.MOZ_CRASHREPORTER) {
-    return;
-  }
-
-  if (!("Add-ons" in gAppInfo.annotations)) {
-    Assert.ok(false, "Cannot find Add-ons entry in crash annotations");
-    return;
-  }
-
-  let addons = gAppInfo.annotations["Add-ons"].split(",");
-  Assert.ok(
-    addons.includes(
-      `${encodeURIComponent(aId)}:${encodeURIComponent(aVersion)}`
-    )
-  );
-}
-
-/**
- * Tests that an add-on does not appear in the crash report annotations, if
- * crash reporting is enabled. The test will fail if the add-on is in the
- * annotation.
- * @param  aId
- *         The ID of the add-on
- * @param  aVersion
- *         The version of the add-on
- */
-function do_check_not_in_crash_annotation(aId, aVersion) {
-  if (!AppConstants.MOZ_CRASHREPORTER) {
-    return;
-  }
-
-  if (!("Add-ons" in gAppInfo.annotations)) {
-    Assert.ok(true);
-    return;
-  }
-
-  let addons = gAppInfo.annotations["Add-ons"].split(",");
-  Assert.ok(
-    !addons.includes(
-      `${encodeURIComponent(aId)}:${encodeURIComponent(aVersion)}`
-    )
-  );
-}
-
-function do_get_file_hash(aFile, aAlgorithm) {
-  if (!aAlgorithm) {
-    aAlgorithm = "sha1";
-  }
-
-  let crypto = Cc["@mozilla.org/security/hash;1"].createInstance(
-    Ci.nsICryptoHash
-  );
-  crypto.initWithString(aAlgorithm);
-  let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
-    Ci.nsIFileInputStream
-  );
-  fis.init(aFile, -1, -1, false);
-  crypto.updateFromStream(fis, aFile.fileSize);
-
-  // return the two-digit hexadecimal code for a byte
-  let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
-
-  let binary = crypto.finish(false);
-  let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
-  return aAlgorithm + ":" + hash.join("");
-}
-
-/**
- * Returns an extension uri spec
- *
- * @param  aProfileDir
- *         The extension install directory
- * @return a uri spec pointing to the root of the extension
- */
-function do_get_addon_root_uri(aProfileDir, aId) {
-  let path = aProfileDir.clone();
-  path.append(aId);
-  if (!path.exists()) {
-    path.leafName += ".xpi";
-    return "jar:" + Services.io.newFileURI(path).spec + "!/";
-  }
-  return Services.io.newFileURI(path).spec;
-}
-
-function do_get_expected_addon_name(aId) {
-  if (TEST_UNPACKED) {
-    return aId;
-  }
-  return aId + ".xpi";
-}
-
-/**
- * Check that an array of actual add-ons is the same as an array of
- * expected add-ons.
- *
- * @param  aActualAddons
- *         The array of actual add-ons to check.
- * @param  aExpectedAddons
- *         The array of expected add-ons to check against.
- * @param  aProperties
- *         An array of properties to check.
- */
-function do_check_addons(aActualAddons, aExpectedAddons, aProperties) {
-  Assert.notEqual(aActualAddons, null);
-  Assert.equal(aActualAddons.length, aExpectedAddons.length);
-  for (let i = 0; i < aActualAddons.length; i++) {
-    do_check_addon(aActualAddons[i], aExpectedAddons[i], aProperties);
-  }
-}
-
-/**
- * Check that the actual add-on is the same as the expected add-on.
- *
- * @param  aActualAddon
- *         The actual add-on to check.
- * @param  aExpectedAddon
- *         The expected add-on to check against.
- * @param  aProperties
- *         An array of properties to check.
- */
-function do_check_addon(aActualAddon, aExpectedAddon, aProperties) {
-  Assert.notEqual(aActualAddon, null);
-
-  aProperties.forEach(function(aProperty) {
-    let actualValue = aActualAddon[aProperty];
-    let expectedValue = aExpectedAddon[aProperty];
-
-    // Check that all undefined expected properties are null on actual add-on
-    if (!(aProperty in aExpectedAddon)) {
-      if (actualValue !== undefined && actualValue !== null) {
-        do_throw(
-          "Unexpected defined/non-null property for add-on " +
-            aExpectedAddon.id +
-            " (addon[" +
-            aProperty +
-            "] = " +
-            actualValue.toSource() +
-            ")"
-        );
-      }
-
-      return;
-    }
-    if (expectedValue && !actualValue) {
-      do_throw(
-        "Missing property for add-on " +
-          aExpectedAddon.id +
-          ": expected addon[" +
-          aProperty +
-          "] = " +
-          expectedValue
-      );
-      return;
-    }
-
-    switch (aProperty) {
-      case "creator":
-        do_check_author(actualValue, expectedValue);
-        break;
-
-      case "developers":
-        Assert.equal(actualValue.length, expectedValue.length);
-        for (let i = 0; i < actualValue.length; i++) {
-          do_check_author(actualValue[i], expectedValue[i]);
-        }
-        break;
-
-      case "screenshots":
-        Assert.equal(actualValue.length, expectedValue.length);
-        for (let i = 0; i < actualValue.length; i++) {
-          do_check_screenshot(actualValue[i], expectedValue[i]);
-        }
-        break;
-
-      case "sourceURI":
-        Assert.equal(actualValue.spec, expectedValue);
-        break;
-
-      case "updateDate":
-        Assert.equal(actualValue.getTime(), expectedValue.getTime());
-        break;
-
-      case "compatibilityOverrides":
-        Assert.equal(actualValue.length, expectedValue.length);
-        for (let i = 0; i < actualValue.length; i++) {
-          do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
-        }
-        break;
-
-      case "icons":
-        do_check_icons(actualValue, expectedValue);
-        break;
-
-      default:
-        if (actualValue !== expectedValue) {
-          do_throw(
-            "Failed for " +
-              aProperty +
-              " for add-on " +
-              aExpectedAddon.id +
-              " (" +
-              actualValue +
-              " === " +
-              expectedValue +
-              ")"
-          );
-        }
-    }
-  });
-}
-
-/**
- * Check that the actual author is the same as the expected author.
- *
- * @param  aActual
- *         The actual author to check.
- * @param  aExpected
- *         The expected author to check against.
- */
-function do_check_author(aActual, aExpected) {
-  Assert.equal(aActual.toString(), aExpected.name);
-  Assert.equal(aActual.name, aExpected.name);
-  Assert.equal(aActual.url, aExpected.url);
-}
-
-/**
- * Check that the actual screenshot is the same as the expected screenshot.
- *
- * @param  aActual
- *         The actual screenshot to check.
- * @param  aExpected
- *         The expected screenshot to check against.
- */
-function do_check_screenshot(aActual, aExpected) {
-  Assert.equal(aActual.toString(), aExpected.url);
-  Assert.equal(aActual.url, aExpected.url);
-  Assert.equal(aActual.width, aExpected.width);
-  Assert.equal(aActual.height, aExpected.height);
-  Assert.equal(aActual.thumbnailURL, aExpected.thumbnailURL);
-  Assert.equal(aActual.thumbnailWidth, aExpected.thumbnailWidth);
-  Assert.equal(aActual.thumbnailHeight, aExpected.thumbnailHeight);
-  Assert.equal(aActual.caption, aExpected.caption);
-}
-
-/**
- * Check that the actual compatibility override is the same as the expected
- * compatibility override.
- *
- * @param  aAction
- *         The actual compatibility override to check.
- * @param  aExpected
- *         The expected compatibility override to check against.
- */
-function do_check_compatibilityoverride(aActual, aExpected) {
-  Assert.equal(aActual.type, aExpected.type);
-  Assert.equal(aActual.minVersion, aExpected.minVersion);
-  Assert.equal(aActual.maxVersion, aExpected.maxVersion);
-  Assert.equal(aActual.appID, aExpected.appID);
-  Assert.equal(aActual.appMinVersion, aExpected.appMinVersion);
-  Assert.equal(aActual.appMaxVersion, aExpected.appMaxVersion);
-}
-
-function do_check_icons(aActual, aExpected) {
-  for (var size in aExpected) {
-    Assert.equal(aActual[size], aExpected[size]);
-  }
-}
-
-function isThemeInAddonsList(aDir, aId) {
-  return AddonTestUtils.addonsList.hasTheme(aDir, aId);
-}
-
-function isExtensionInBootstrappedList(aDir, aId) {
-  return AddonTestUtils.addonsList.hasExtension(aDir, aId);
-}
-
-/**
- * Writes a manifest.json manifest into an extension using the properties passed
- * in a JS object.
- *
- * @param   aManifest
- *          The data to write
- * @param   aDir
- *          The install directory to add the extension to
- * @param   aId
- *          An optional string to override the default installation aId
- * @return  A file pointing to where the extension was installed
- */
-function promiseWriteWebManifestForExtension(
-  aData,
-  aDir,
-  aId = aData.applications.gecko.id
-) {
-  let files = {
-    "manifest.json": JSON.stringify(aData),
-  };
-  return AddonTestUtils.promiseWriteFilesToExtension(aDir.path, aId, files);
-}
-
-/**
- * Creates an XPI file for some manifest data in the temporary directory and
- * returns the nsIFile for it. The file will be deleted when the test completes.
- *
- * @param   aData
- *          The object holding data about the add-on
- * @return  A file pointing to the created XPI file
- */
-function createTempXPIFile(aData, aExtraFile) {
-  let files = {
-    "install.rdf": aData,
-  };
-  if (typeof aExtraFile == "object") {
-    Object.assign(files, aExtraFile);
-  } else if (aExtraFile) {
-    files[aExtraFile] = "";
-  }
-
-  return AddonTestUtils.createTempXPIFile(files);
-}
-
-var gExpectedEvents = {};
-var gExpectedInstalls = [];
-var gNext = null;
-
-function getExpectedEvent(aId) {
-  if (!(aId in gExpectedEvents)) {
-    do_throw("Wasn't expecting events for " + aId);
-  }
-  if (gExpectedEvents[aId].length == 0) {
-    do_throw("Too many events for " + aId);
-  }
-  let event = gExpectedEvents[aId].shift();
-  if (event instanceof Array) {
-    return event;
-  }
-  return [event, true];
-}
-
-function getExpectedInstall(aAddon) {
-  if (gExpectedInstalls instanceof Array) {
-    return gExpectedInstalls.shift();
-  }
-  if (!aAddon || !aAddon.id) {
-    return gExpectedInstalls.NO_ID.shift();
-  }
-  let id = aAddon.id;
-  if (!(id in gExpectedInstalls) || !(gExpectedInstalls[id] instanceof Array)) {
-    do_throw("Wasn't expecting events for " + id);
-  }
-  if (gExpectedInstalls[id].length == 0) {
-    do_throw("Too many events for " + id);
-  }
-  return gExpectedInstalls[id].shift();
-}
-
-const AddonListener = {
-  onPropertyChanged(aAddon, aProperties) {
-    info(`Got onPropertyChanged event for ${aAddon.id}`);
-    let [event, properties] = getExpectedEvent(aAddon.id);
-    Assert.equal("onPropertyChanged", event);
-    Assert.equal(aProperties.length, properties.length);
-    properties.forEach(function(aProperty) {
-      // Only test that the expected properties are listed, having additional
-      // properties listed is not necessary a problem
-      if (!aProperties.includes(aProperty)) {
-        do_throw("Did not see property change for " + aProperty);
-      }
-    });
-    return check_test_completed(arguments);
-  },
-
-  onEnabling(aAddon, aRequiresRestart) {
-    info(`Got onEnabling event for ${aAddon.id}`);
-    let [event, expectedRestart] = getExpectedEvent(aAddon.id);
-    Assert.equal("onEnabling", event);
-    Assert.equal(aRequiresRestart, expectedRestart);
-    if (expectedRestart) {
-      Assert.ok(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
-    }
-    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
-    return check_test_completed(arguments);
-  },
-
-  onEnabled(aAddon) {
-    info(`Got onEnabled event for ${aAddon.id}`);
-    let [event] = getExpectedEvent(aAddon.id);
-    Assert.equal("onEnabled", event);
-    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
-    return check_test_completed(arguments);
-  },
-
-  onDisabling(aAddon, aRequiresRestart) {
-    info(`Got onDisabling event for ${aAddon.id}`);
-    let [event, expectedRestart] = getExpectedEvent(aAddon.id);
-    Assert.equal("onDisabling", event);
-    Assert.equal(aRequiresRestart, expectedRestart);
-    if (expectedRestart) {
-      Assert.ok(
-        hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE)
-      );
-    }
-    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
-    return check_test_completed(arguments);
-  },
-
-  onDisabled(aAddon) {
-    info(`Got onDisabled event for ${aAddon.id}`);
-    let [event] = getExpectedEvent(aAddon.id);
-    Assert.equal("onDisabled", event);
-    Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
-    return check_test_completed(arguments);
-  },
-
-  onInstalling(aAddon, aRequiresRestart) {
-    info(`Got onInstalling event for ${aAddon.id}`);
-    let [event, expectedRestart] = getExpectedEvent(aAddon.id);
-    Assert.equal("onInstalling", event);
-    Assert.equal(aRequiresRestart, expectedRestart);
-    if (expectedRestart) {
-      Assert.ok(
-        hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL)
-      );
-    }
-    return check_test_completed(arguments);
-  },
-
-  onInstalled(aAddon) {
-    info(`Got onInstalled event for ${aAddon.id}`);
-    let [event] = getExpectedEvent(aAddon.id);
-    Assert.equal("onInstalled", event);
-    return check_test_completed(arguments);
-  },
-
-  onUninstalling(aAddon, aRequiresRestart) {
-    info(`Got onUninstalling event for ${aAddon.id}`);
-    let [event, expectedRestart] = getExpectedEvent(aAddon.id);
-    Assert.equal("onUninstalling", event);
-    Assert.equal(aRequiresRestart, expectedRestart);
-    if (expectedRestart) {
-      Assert.ok(
-        hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL)
-      );
-    }
-    return check_test_completed(arguments);
-  },
-
-  onUninstalled(aAddon) {
-    info(`Got onUninstalled event for ${aAddon.id}`);
-    let [event] = getExpectedEvent(aAddon.id);
-    Assert.equal("onUninstalled", event);
-    return check_test_completed(arguments);
-  },
-
-  onOperationCancelled(aAddon) {
-    info(`Got onOperationCancelled event for ${aAddon.id}`);
-    let [event] = getExpectedEvent(aAddon.id);
-    Assert.equal("onOperationCancelled", event);
-    return check_test_completed(arguments);
-  },
-};
-
-const InstallListener = {
-  onNewInstall(install) {
-    if (
-      install.state != AddonManager.STATE_DOWNLOADED &&
-      install.state != AddonManager.STATE_DOWNLOAD_FAILED &&
-      install.state != AddonManager.STATE_AVAILABLE
-    ) {
-      do_throw("Bad install state " + install.state);
-    }
-    if (install.state != AddonManager.STATE_DOWNLOAD_FAILED) {
-      Assert.equal(install.error, 0);
-    } else {
-      Assert.notEqual(install.error, 0);
-    }
-    Assert.equal("onNewInstall", getExpectedInstall());
-    return check_test_completed(arguments);
-  },
-
-  onDownloadStarted(install) {
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOADING);
-    Assert.equal(install.error, 0);
-    Assert.equal("onDownloadStarted", getExpectedInstall());
-    return check_test_completed(arguments);
-  },
-
-  onDownloadEnded(install) {
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
-    Assert.equal(install.error, 0);
-    Assert.equal("onDownloadEnded", getExpectedInstall());
-    return check_test_completed(arguments);
-  },
-
-  onDownloadFailed(install) {
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
-    Assert.equal("onDownloadFailed", getExpectedInstall());
-    return check_test_completed(arguments);
-  },
-
-  onDownloadCancelled(install) {
-    Assert.equal(install.state, AddonManager.STATE_CANCELLED);
-    Assert.equal(install.error, 0);
-    Assert.equal("onDownloadCancelled", getExpectedInstall());
-    return check_test_completed(arguments);
-  },
-
-  onInstallStarted(install) {
-    Assert.equal(install.state, AddonManager.STATE_INSTALLING);
-    Assert.equal(install.error, 0);
-    Assert.equal("onInstallStarted", getExpectedInstall(install.addon));
-    return check_test_completed(arguments);
-  },
-
-  onInstallEnded(install, newAddon) {
-    Assert.equal(install.state, AddonManager.STATE_INSTALLED);
-    Assert.equal(install.error, 0);
-    Assert.equal("onInstallEnded", getExpectedInstall(install.addon));
-    return check_test_completed(arguments);
-  },
-
-  onInstallFailed(install) {
-    Assert.equal(install.state, AddonManager.STATE_INSTALL_FAILED);
-    Assert.equal("onInstallFailed", getExpectedInstall(install.addon));
-    return check_test_completed(arguments);
-  },
-
-  onInstallCancelled(install) {
-    // If the install was cancelled by a listener returning false from
-    // onInstallStarted, then the state will revert to STATE_DOWNLOADED.
-    let possibleStates = [
-      AddonManager.STATE_CANCELLED,
-      AddonManager.STATE_DOWNLOADED,
-    ];
-    Assert.ok(possibleStates.includes(install.state));
-    Assert.equal(install.error, 0);
-    Assert.equal("onInstallCancelled", getExpectedInstall(install.addon));
-    return check_test_completed(arguments);
-  },
-
-  onExternalInstall(aAddon, existingAddon, aRequiresRestart) {
-    Assert.equal("onExternalInstall", getExpectedInstall(aAddon));
-    Assert.ok(!aRequiresRestart);
-    return check_test_completed(arguments);
-  },
-};
-
-function hasFlag(aBits, aFlag) {
-  return (aBits & aFlag) != 0;
-}
-
-// Just a wrapper around setting the expected events.
-function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
-  AddonManager.addAddonListener(AddonListener);
-  AddonManager.addInstallListener(InstallListener);
-
-  gExpectedInstalls = aExpectedInstalls;
-  gExpectedEvents = aExpectedEvents;
-  gNext = aNext;
-}
-
-function clearListeners() {
-  AddonManager.removeAddonListener(AddonListener);
-  AddonManager.removeInstallListener(InstallListener);
-}
-
-function end_test() {
-  clearListeners();
-}
-
-// Checks if all expected events have been seen and if so calls the callback.
-function check_test_completed(aArgs) {
-  if (!gNext) {
-    return undefined;
-  }
-
-  if (gExpectedInstalls instanceof Array && gExpectedInstalls.length > 0) {
-    return undefined;
-  }
-
-  for (let id in gExpectedInstalls) {
-    let installList = gExpectedInstalls[id];
-    if (installList.length > 0) {
-      return undefined;
-    }
-  }
-
-  for (let id in gExpectedEvents) {
-    if (gExpectedEvents[id].length > 0) {
-      return undefined;
-    }
-  }
-
-  return gNext.apply(null, aArgs);
-}
-
-// Verifies that all the expected events for all add-ons were seen.
-function ensure_test_completed() {
-  for (let i in gExpectedEvents) {
-    if (gExpectedEvents[i].length > 0) {
-      do_throw(
-        `Didn't see all the expected events for ${i}: Still expecting ${
-          gExpectedEvents[i]
-        }`
-      );
-    }
-  }
-  gExpectedEvents = {};
-  if (gExpectedInstalls) {
-    Assert.equal(gExpectedInstalls.length, 0);
-  }
-}
-
-/**
- * A helper method to install an array of AddonInstall to completion and then
- * call a provided callback.
- *
- * @param   aInstalls
- *          The array of AddonInstalls to install
- * @param   aCallback
- *          The callback to call when all installs have finished
- */
-function completeAllInstalls(aInstalls, aCallback) {
-  promiseCompleteAllInstalls(aInstalls).then(aCallback);
-}
-
-/**
- * A helper method to install an array of files and call a callback after the
- * installs are completed.
- *
- * @param   aFiles
- *          The array of files to install
- * @param   aCallback
- *          The callback to call when all installs have finished
- * @param   aIgnoreIncompatible
- *          Optional parameter to ignore add-ons that are incompatible in
- *          aome way with the application
- */
-function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
-  promiseInstallAllFiles(aFiles, aIgnoreIncompatible).then(aCallback);
-}
-
-const EXTENSIONS_DB = "extensions.json";
-var gExtensionsJSON = gProfD.clone();
-gExtensionsJSON.append(EXTENSIONS_DB);
-
-async function promiseInstallWebExtension(aData) {
-  let addonFile = createTempWebExtensionFile(aData);
-
-  let { addon } = await promiseInstallFile(addonFile);
-  return addon;
-}
-
-// By default use strict compatibility.
-Services.prefs.setBoolPref("extensions.strictCompatibility", true);
-
-// Ensure signature checks are enabled by default.
-Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
-
-Services.prefs.setBoolPref("extensions.legacy.enabled", true);
-
-// Copies blocklistFile (an nsIFile) to gProfD/blocklist.xml.
-function copyBlocklistToProfile(blocklistFile) {
-  var dest = gProfD.clone();
-  dest.append("blocklist.xml");
-  if (dest.exists()) {
-    dest.remove(false);
-  }
-  blocklistFile.copyTo(gProfD, "blocklist.xml");
-  dest.lastModifiedTime = Date.now();
-}
-
-// Make sure that a given path does not exist.
-function pathShouldntExist(file) {
-  if (file.exists()) {
-    do_throw(`Test cleanup: path ${file.path} exists when it should not`);
-  }
-}
-
-// Wrap a function (typically a callback) to catch and report exceptions.
-function do_exception_wrap(func) {
-  return function() {
-    try {
-      func.apply(null, arguments);
-    } catch (e) {
-      do_report_unexpected_exception(e);
-    }
-  };
-}
-
-/**
- * Change the schema version of the JSON extensions database.
- */
-async function changeXPIDBVersion(aNewVersion) {
-  let json = await loadJSON(gExtensionsJSON.path);
-  json.schemaVersion = aNewVersion;
-  await saveJSON(json, gExtensionsJSON.path);
-}
-
-/**
- * Load a file into a string.
- */
-async function loadFile(aFile) {
-  let buffer = await OS.File.read(aFile);
-  return new TextDecoder().decode(buffer);
-}
-
-/**
- * Raw load of a JSON file.
- */
-async function loadJSON(aFile) {
-  let data = await loadFile(aFile);
-  info("Loaded JSON file " + aFile);
-  return JSON.parse(data);
-}
-
-/**
- * Raw save of a JSON blob to file.
- */
-async function saveJSON(aData, aFile) {
-  info("Starting to save JSON file " + aFile);
-  await OS.File.writeAtomic(
-    aFile,
-    new TextEncoder().encode(JSON.stringify(aData, null, 2))
-  );
-  info("Done saving JSON file " + aFile.path);
-}
-
-/**
- * Create a callback function that calls do_execute_soon on an actual callback and arguments.
- */
-function callback_soon(aFunction) {
-  return function(...args) {
-    executeSoon(
-      function() {
-        aFunction.apply(null, args);
-      },
-      aFunction.name ? "delayed callback " + aFunction.name : "delayed callback"
-    );
-  };
-}
-
-XPCOMUtils.defineLazyServiceGetter(
-  this,
-  "pluginHost",
-  "@mozilla.org/plugin/host;1",
-  "nsIPluginHost"
-);
-
-class MockPluginTag {
-  constructor(opts, enabledState = Ci.nsIPluginTag.STATE_ENABLED) {
-    this.pluginTag = pluginHost.createFakePlugin({
-      handlerURI: "resource://fake-plugin/${Math.random()}.xhtml",
-      mimeEntries: [{ type: "application/x-fake-plugin" }],
-      fileName: `${opts.name}.so`,
-      ...opts,
-    });
-    this.pluginTag.enabledState = enabledState;
-
-    this.name = opts.name;
-    this.version = opts.version;
-  }
-  async isBlocklisted() {
-    let state = await Blocklist.getPluginBlocklistState(this.pluginTag);
-    return state == Services.blocklist.STATE_BLOCKED;
-  }
-  get disabled() {
-    return this.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
-  }
-  set disabled(val) {
-    this.enabledState =
-      Ci.nsIPluginTag[val ? "STATE_DISABLED" : "STATE_ENABLED"];
-  }
-  get enabledState() {
-    return this.pluginTag.enabledState;
-  }
-  set enabledState(val) {
-    this.pluginTag.enabledState = val;
-  }
-}
-
-function mockPluginHost(plugins) {
-  let PluginHost = {
-    getPluginTags(count) {
-      count.value = plugins.length;
-      return plugins.map(p => p.pluginTag);
-    },
-
-    QueryInterface: ChromeUtils.generateQI(["nsIPluginHost"]),
-  };
-
-  MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
-}
-
-async function setInitialState(addon, initialState) {
-  if (initialState.userDisabled) {
-    await addon.disable();
-  } else if (initialState.userDisabled === false) {
-    await addon.enable();
-  }
-}
-
-/**
- * Escapes any occurrences of &, ", < or > with XML entities.
- *
- * @param {string} str
- *        The string to escape.
- * @returns {string} The escaped string.
- */
-function escapeXML(str) {
-  let replacements = {
-    "&": "&amp;",
-    '"': "&quot;",
-    "'": "&apos;",
-    "<": "&lt;",
-    ">": "&gt;",
-  };
-  return String(str).replace(/[&"''<>]/g, m => replacements[m]);
-}
-
-/**
- * A tagged template function which escapes any XML metacharacters in
- * interpolated values.
- *
- * @param {Array<string>} strings
- *        An array of literal strings extracted from the templates.
- * @param {Array} values
- *        An array of interpolated values extracted from the template.
- * @returns {string}
- *        The result of the escaped values interpolated with the literal
- *        strings.
- */
-function escaped(strings, ...values) {
-  let result = [];
-
-  for (let [i, string] of strings.entries()) {
-    result.push(string);
-    if (i < values.length) {
-      result.push(escapeXML(values[i]));
-    }
-  }
-
-  return result.join("");
-}
-
-function _writeProps(obj, props, indent = "  ") {
-  let items = [];
-  for (let prop of props) {
-    if (obj[prop] !== undefined) {
-      items.push(escaped`${indent}<em:${prop}>${obj[prop]}</em:${prop}>\n`);
-    }
-  }
-  return items.join("");
-}
-
-function _writeArrayProps(obj, props, indent = "  ") {
-  let items = [];
-  for (let prop of props) {
-    for (let val of obj[prop] || []) {
-      items.push(escaped`${indent}<em:${prop}>${val}</em:${prop}>\n`);
-    }
-  }
-  return items.join("");
-}
-
-function _writeLocaleStrings(data) {
-  let items = [];
-
-  items.push(
-    this._writeProps(data, ["name", "description", "creator", "homepageURL"])
-  );
-  items.push(
-    this._writeArrayProps(data, ["developer", "translator", "contributor"])
-  );
-
-  return items.join("");
-}
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrap.js
+++ /dev/null
@@ -1,819 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const APP_STARTUP = 1;
-const APP_SHUTDOWN = 2;
-const ADDON_ENABLE = 3;
-const ADDON_DISABLE = 4;
-const ADDON_INSTALL = 5;
-const ADDON_UNINSTALL = 6;
-const ADDON_UPGRADE = 7;
-const ADDON_DOWNGRADE = 8;
-
-const ID1 = "bootstrap1@tests.mozilla.org";
-const ID2 = "bootstrap2@tests.mozilla.org";
-
-// This verifies that bootstrappable add-ons can be used without restarts.
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// Enable loading extensions from the user scopes
-Services.prefs.setIntPref(
-  "extensions.enabledScopes",
-  AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER
-);
-
-// Make Cu.isInAutomation true.
-Services.prefs.setBoolPref(
-  "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
-  true
-);
-Services.prefs.setIntPref("extensions.sideloadScopes", AddonManager.SCOPE_ALL);
-
-BootstrapMonitor.init();
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-
-const profileDir = gProfD.clone();
-profileDir.append("extensions");
-const userExtDir = gProfD.clone();
-userExtDir.append("extensions2");
-userExtDir.append(gAppInfo.ID);
-registerDirectory("XREUSysExt", userExtDir.parent);
-
-const ADDONS = {
-  test_bootstrap1_1: {
-    "manifest.json": JSON.stringify({
-      applications: {
-        gecko: {
-          id: "bootstrap1@tests.mozilla.org",
-        },
-      },
-      legacy: {
-        type: "bootstrap",
-      },
-      manifest_version: 2,
-
-      name: "Test Bootstrap 1",
-      version: "1.0",
-    }),
-    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
-  },
-  test_bootstrap1_2: {
-    "manifest.json": JSON.stringify({
-      applications: {
-        gecko: {
-          id: "bootstrap1@tests.mozilla.org",
-        },
-      },
-      legacy: {
-        type: "bootstrap",
-      },
-      manifest_version: 2,
-      version: "2.0",
-
-      name: "Test Bootstrap 1",
-    }),
-    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
-  },
-  test_bootstrap1_3: {
-    "manifest.json": JSON.stringify({
-      applications: {
-        gecko: {
-          id: "bootstrap1@tests.mozilla.org",
-        },
-      },
-      legacy: {
-        type: "bootstrap",
-      },
-      manifest_version: 2,
-      version: "3.0",
-
-      name: "Test Bootstrap 1",
-
-      targetApplications: [
-        {
-          applications: {
-            gecko: {
-              id: "undefined",
-            },
-          },
-          legacy: {
-            type: "bootstrap",
-          },
-          manifest_version: 2,
-          minVersion: "1",
-          maxVersion: "1",
-        },
-      ],
-    }),
-    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
-  },
-  test_bootstrap2_1: {
-    "manifest.json": JSON.stringify({
-      applications: {
-        gecko: {
-          id: "bootstrap2@tests.mozilla.org",
-        },
-      },
-      legacy: {
-        type: "bootstrap",
-      },
-      manifest_version: 2,
-    }),
-    "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
-  },
-};
-
-var startupCacheMonitor = {
-  notificationFired: false,
-  observe() {
-    this.notificationFired = true;
-  },
-  // Checks that the notification has been fired since the last time this was called.
-  check(expected) {
-    equal(this.notificationFired, expected);
-    this.notificationFired = false;
-  },
-};
-Services.obs.addObserver(startupCacheMonitor, "startupcache-invalidate");
-
-var testserver = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
-
-const XPIS = {};
-for (let [name, addon] of Object.entries(ADDONS)) {
-  XPIS[name] = AddonTestUtils.createTempXPIFile(addon);
-  testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
-}
-
-function getStartupReason() {
-  let info = BootstrapMonitor.started.get(ID1);
-  return info ? info.reason : undefined;
-}
-
-function getShutdownReason() {
-  let info = BootstrapMonitor.stopped.get(ID1);
-  return info ? info.reason : undefined;
-}
-
-function getInstallReason() {
-  let info = BootstrapMonitor.installed.get(ID1);
-  return info ? info.reason : undefined;
-}
-
-function getUninstallReason() {
-  let info = BootstrapMonitor.uninstalled.get(ID1);
-  return info ? info.reason : undefined;
-}
-
-function getStartupOldVersion() {
-  let info = BootstrapMonitor.started.get(ID1);
-  return info ? info.data.oldVersion : undefined;
-}
-
-function getShutdownNewVersion() {
-  let info = BootstrapMonitor.stopped.get(ID1);
-  return info ? info.data.newVersion : undefined;
-}
-
-function getInstallOldVersion() {
-  let info = BootstrapMonitor.installed.get(ID1);
-  return info ? info.data.oldVersion : undefined;
-}
-
-function getUninstallNewVersion() {
-  let info = BootstrapMonitor.uninstalled.get(ID1);
-  return info ? info.data.newVersion : undefined;
-}
-
-async function checkBootstrappedPref() {
-  let { XPIInternal } = ChromeUtils.import(
-    "resource://gre/modules/addons/XPIProvider.jsm"
-  );
-
-  let data = new Map();
-  for (let entry of XPIInternal.XPIStates.enabledAddons()) {
-    data.set(entry.id, entry);
-  }
-
-  let addons = await AddonManager.getAddonsByTypes(["extension"]);
-  for (let addon of addons) {
-    if (!addon.id.endsWith("@tests.mozilla.org")) {
-      continue;
-    }
-    if (!addon.isActive) {
-      continue;
-    }
-    if (
-      addon.operationsRequiringRestart != AddonManager.OP_NEEDS_RESTART_NONE
-    ) {
-      continue;
-    }
-
-    ok(data.has(addon.id));
-    let addonData = data.get(addon.id);
-    data.delete(addon.id);
-
-    equal(addonData.version, addon.version);
-    equal(addonData.type, addon.type);
-
-    let resourceURI = addon.getResourceURI();
-    if (resourceURI instanceof Ci.nsIJARURI) {
-      resourceURI = resourceURI.JARFile;
-    }
-    let file = resourceURI.QueryInterface(Ci.nsIFileURL).file;
-    equal(addonData.path, file.path);
-  }
-  equal(data.size, 0);
-}
-
-add_task(async function run_test() {
-  promiseStartupManager();
-
-  ok(!gExtensionsJSON.exists());
-  ok(!gAddonStartup.exists());
-});
-
-// Tests that installing doesn't require a restart
-add_task(async function test_1() {
-  prepare_test({}, ["onNewInstall"]);
-
-  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_1);
-  ensure_test_completed();
-
-  notEqual(install, null);
-  equal(install.type, "extension");
-  equal(install.version, "1.0");
-  equal(install.name, "Test Bootstrap 1");
-  equal(install.state, AddonManager.STATE_DOWNLOADED);
-  notEqual(install.addon.syncGUID, null);
-  equal(
-    install.addon.operationsRequiringRestart &
-      AddonManager.OP_NEEDS_RESTART_INSTALL,
-    0
-  );
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  let addon = install.addon;
-
-  await Promise.all([
-    BootstrapMonitor.promiseAddonStartup(ID1),
-    new Promise(resolve => {
-      prepare_test(
-        {
-          [ID1]: [["onInstalling", false], "onInstalled"],
-        },
-        ["onInstallStarted", "onInstallEnded"],
-        function() {
-          // startup should not have been called yet.
-          BootstrapMonitor.checkAddonNotStarted(ID1);
-          resolve();
-        }
-      );
-      install.install();
-    }),
-  ]);
-
-  await checkBootstrappedPref();
-  let installSyncGUID = addon.syncGUID;
-
-  let installs = await AddonManager.getAllInstalls();
-  // There should be no active installs now since the install completed and
-  // doesn't require a restart.
-  equal(installs.length, 0);
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  notEqual(b1.syncGUID, null);
-  equal(b1.syncGUID, installSyncGUID);
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  equal(getStartupReason(), ADDON_INSTALL);
-  equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "1.0");
-
-  let dir = do_get_addon_root_uri(profileDir, ID1);
-  equal(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
-
-  startupCacheMonitor.check(false);
-});
-
-// Tests that disabling doesn't require a restart
-add_task(async function test_2() {
-  let b1 = await AddonManager.getAddonByID(ID1);
-  prepare_test({
-    [ID1]: [["onDisabling", false], "onDisabled"],
-  });
-
-  equal(
-    b1.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE,
-    0
-  );
-  await b1.disable();
-  ensure_test_completed();
-
-  await new Promise(executeSoon);
-
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(b1.userDisabled);
-  ok(!b1.isActive);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  equal(getShutdownReason(), ADDON_DISABLE);
-  equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  let newb1 = await AddonManager.getAddonByID(ID1);
-  notEqual(newb1, null);
-  equal(newb1.version, "1.0");
-  ok(!newb1.appDisabled);
-  ok(newb1.userDisabled);
-  ok(!newb1.isActive);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Test that restarting doesn't accidentally re-enable
-add_task(async function test_3() {
-  await promiseShutdownManager();
-
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  equal(getShutdownReason(), ADDON_DISABLE);
-  equal(getShutdownNewVersion(), undefined);
-
-  await promiseStartupManager();
-
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  equal(getShutdownReason(), ADDON_DISABLE);
-  equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  ok(gAddonStartup.exists());
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(b1.userDisabled);
-  ok(!b1.isActive);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Tests that enabling doesn't require a restart
-add_task(async function test_4() {
-  let b1 = await AddonManager.getAddonByID(ID1);
-  prepare_test({
-    [ID1]: [["onEnabling", false], "onEnabled"],
-  });
-
-  equal(
-    b1.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE,
-    0
-  );
-  await b1.enable();
-  ensure_test_completed();
-
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  equal(getStartupReason(), ADDON_ENABLE);
-  equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "1.0");
-
-  let newb1 = await AddonManager.getAddonByID(ID1);
-  notEqual(newb1, null);
-  equal(newb1.version, "1.0");
-  ok(!newb1.appDisabled);
-  ok(!newb1.userDisabled);
-  ok(newb1.isActive);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Tests that a restart shuts down and restarts the add-on
-add_task(async function test_5() {
-  await promiseShutdownManager();
-  // By the time we've shut down, the database must have been written
-  ok(gExtensionsJSON.exists());
-
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  equal(getShutdownReason(), APP_SHUTDOWN);
-  equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-  await promiseStartupManager();
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  equal(getStartupReason(), APP_STARTUP);
-  equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "1.0");
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Tests that installing an upgrade doesn't require a restart
-add_task(async function test_6() {
-  prepare_test({}, ["onNewInstall"]);
-
-  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
-  ensure_test_completed();
-
-  notEqual(install, null);
-  equal(install.type, "extension");
-  equal(install.version, "2.0");
-  equal(install.name, "Test Bootstrap 1");
-  equal(install.state, AddonManager.STATE_DOWNLOADED);
-
-  await Promise.all([
-    BootstrapMonitor.promiseAddonStartup(ID1),
-    new Promise(resolve => {
-      prepare_test(
-        {
-          [ID1]: [["onInstalling", false], "onInstalled"],
-        },
-        ["onInstallStarted", "onInstallEnded"],
-        resolve
-      );
-      install.install();
-    }),
-  ]);
-
-  startupCacheMonitor.check(true);
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "2.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-  equal(getStartupReason(), ADDON_UPGRADE);
-  equal(getInstallOldVersion(), 1);
-  equal(getStartupOldVersion(), 1);
-  equal(getShutdownReason(), ADDON_UPGRADE);
-  equal(getShutdownNewVersion(), 2);
-  equal(getUninstallNewVersion(), 2);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-  do_check_in_crash_annotation(ID1, "2.0");
-
-  await checkBootstrappedPref();
-});
-
-// Tests that uninstalling doesn't require a restart
-add_task(async function test_7() {
-  let b1 = await AddonManager.getAddonByID(ID1);
-  prepare_test({
-    [ID1]: [["onUninstalling", false], "onUninstalled"],
-  });
-
-  equal(
-    b1.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL,
-    0
-  );
-  await b1.uninstall();
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(true);
-
-  ensure_test_completed();
-  BootstrapMonitor.checkAddonNotInstalled(ID1);
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  equal(getShutdownReason(), ADDON_DISABLE);
-  equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "2.0");
-
-  b1 = await AddonManager.getAddonByID(ID1);
-  equal(b1, null);
-
-  await promiseRestartManager();
-
-  let newb1 = await AddonManager.getAddonByID(ID1);
-  equal(newb1, null);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Test that a bootstrapped extension dropped into the profile loads properly
-// on startup and doesn't cause an EM restart
-add_task(async function test_8() {
-  await promiseShutdownManager();
-
-  await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID1);
-
-  startupCacheMonitor.check(false);
-  await promiseStartupManager();
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  equal(getStartupReason(), ADDON_INSTALL);
-  equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "1.0");
-
-  await checkBootstrappedPref();
-});
-
-// Test that items detected as removed during startup get removed properly
-add_task(async function test_9() {
-  await promiseShutdownManager();
-
-  manuallyUninstall(profileDir, ID1);
-
-  await promiseStartupManager();
-  startupCacheMonitor.check(true);
-  BootstrapMonitor.clear(ID1);
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  equal(b1, null);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Tests that installing a downgrade sends the right reason
-add_task(async function test_10() {
-  prepare_test({}, ["onNewInstall"]);
-
-  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
-  ensure_test_completed();
-
-  notEqual(install, null);
-  equal(install.type, "extension");
-  equal(install.version, "2.0");
-  equal(install.name, "Test Bootstrap 1");
-  equal(install.state, AddonManager.STATE_DOWNLOADED);
-  do_check_not_in_crash_annotation(ID1, "2.0");
-
-  await Promise.all([
-    BootstrapMonitor.promiseAddonStartup(ID1),
-    new Promise(resolve => {
-      prepare_test(
-        {
-          [ID1]: [["onInstalling", false], "onInstalled"],
-        },
-        ["onInstallStarted", "onInstallEnded"],
-        resolve
-      );
-      install.install();
-    }),
-  ]);
-
-  startupCacheMonitor.check(false);
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "2.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-  equal(getStartupReason(), ADDON_INSTALL);
-  equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "2.0");
-
-  prepare_test({}, ["onNewInstall"]);
-
-  install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_1);
-  ensure_test_completed();
-
-  notEqual(install, null);
-  equal(install.type, "extension");
-  equal(install.version, "1.0");
-  equal(install.name, "Test Bootstrap 1");
-  equal(install.state, AddonManager.STATE_DOWNLOADED);
-
-  await Promise.all([
-    BootstrapMonitor.promiseAddonStartup(ID1),
-    new Promise(resolve => {
-      prepare_test(
-        {
-          [ID1]: [["onInstalling", false], "onInstalled"],
-        },
-        ["onInstallStarted", "onInstallEnded"],
-        resolve
-      );
-      install.install();
-    }),
-  ]);
-
-  startupCacheMonitor.check(true);
-
-  b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  equal(getStartupReason(), ADDON_DOWNGRADE);
-  equal(getInstallOldVersion(), 2);
-  equal(getStartupOldVersion(), 2);
-  equal(getShutdownReason(), ADDON_DOWNGRADE);
-  equal(getShutdownNewVersion(), 1);
-  equal(getUninstallNewVersion(), 1);
-  do_check_in_crash_annotation(ID1, "1.0");
-  do_check_not_in_crash_annotation(ID1, "2.0");
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Tests that uninstalling a disabled add-on still calls the uninstall method
-add_task(async function test_11() {
-  let b1 = await AddonManager.getAddonByID(ID1);
-  prepare_test({
-    [ID1]: [
-      ["onDisabling", false],
-      "onDisabled",
-      ["onUninstalling", false],
-      "onUninstalled",
-    ],
-  });
-
-  await b1.disable();
-
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  equal(getShutdownReason(), ADDON_DISABLE);
-  equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  await b1.uninstall();
-  startupCacheMonitor.check(true);
-
-  ensure_test_completed();
-  BootstrapMonitor.checkAddonNotInstalled(ID1);
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  await checkBootstrappedPref();
-});
-
-// Tests that bootstrapped extensions are correctly loaded even if the app is
-// upgraded at the same time
-add_task(async function test_12() {
-  await promiseShutdownManager();
-
-  await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID1);
-
-  await promiseStartupManager();
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(!b1.appDisabled);
-  ok(!b1.userDisabled);
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  equal(getStartupReason(), ADDON_INSTALL);
-  equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "1.0");
-
-  await b1.uninstall();
-  startupCacheMonitor.check(true);
-
-  await promiseRestartManager();
-  await checkBootstrappedPref();
-});
-
-// Check that a bootstrapped extension in a non-profile location is loaded
-add_task(async function test_17() {
-  await promiseShutdownManager();
-
-  await manuallyInstall(XPIS.test_bootstrap1_1, userExtDir, ID1);
-
-  await promiseStartupManager();
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  // Should have installed and started
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Check that installing a new bootstrapped extension in the profile replaces
-// the existing one
-add_task(async function test_18() {
-  await Promise.all([
-    BootstrapMonitor.promiseAddonStartup(ID1),
-    promiseInstallFile(XPIS.test_bootstrap1_2),
-  ]);
-  startupCacheMonitor.check(true);
-
-  let b1 = await AddonManager.getAddonByID(ID1);
-  // Should have installed and started
-  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-  notEqual(b1, null);
-  equal(b1.version, "2.0");
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-
-  equal(getShutdownReason(), ADDON_UPGRADE);
-  equal(getUninstallReason(), ADDON_UPGRADE);
-  equal(getInstallReason(), ADDON_UPGRADE);
-  equal(getStartupReason(), ADDON_UPGRADE);
-
-  equal(getShutdownNewVersion(), 2);
-  equal(getUninstallNewVersion(), 2);
-  equal(getInstallOldVersion(), 1);
-  equal(getStartupOldVersion(), 1);
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
-
-// Check that uninstalling the profile version reveals the non-profile one
-add_task(async function test_19() {
-  let b1 = await AddonManager.getAddonByID(ID1);
-  // The revealed add-on gets activated asynchronously
-  await new Promise(resolve => {
-    prepare_test(
-      {
-        [ID1]: [
-          ["onUninstalling", false],
-          "onUninstalled",
-          ["onInstalling", false],
-          "onInstalled",
-        ],
-      },
-      [],
-      resolve
-    );
-
-    b1.uninstall();
-  });
-
-  startupCacheMonitor.check(true);
-
-  b1 = await AddonManager.getAddonByID(ID1);
-  // Should have reverted to the older version
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  notEqual(b1, null);
-  equal(b1.version, "1.0");
-  ok(b1.isActive);
-  ok(!b1.isSystem);
-
-  equal(getShutdownReason(), ADDON_DOWNGRADE);
-  equal(getUninstallReason(), ADDON_DOWNGRADE);
-  equal(getInstallReason(), ADDON_DOWNGRADE);
-  equal(getStartupReason(), ADDON_DOWNGRADE);
-
-  equal(getShutdownNewVersion(), "1.0");
-  equal(getUninstallNewVersion(), "1.0");
-  equal(getInstallOldVersion(), "2.0");
-  equal(getStartupOldVersion(), "2.0");
-
-  await checkBootstrappedPref();
-  startupCacheMonitor.check(false);
-});
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrap_const.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
-
-const ADDONS = {
-  test_bootstrap_const: {
-    "manifest.json": JSON.stringify({
-      applications: {
-        gecko: {
-          id: "bootstrap@tests.mozilla.org",
-        },
-      },
-      legacy: {
-        type: "bootstrap",
-      },
-      manifest_version: 2,
-      name: "Test Bootstrap 1",
-      version: "1.0",
-    }),
-    "bootstrap.js":
-      'var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");\n\nconst install = function() {\n  Services.obs.notifyObservers(null, "addon-install");\n};\n',
-  },
-};
-
-add_task(async function() {
-  await promiseStartupManager();
-
-  let sawInstall = false;
-  Services.obs.addObserver(function() {
-    sawInstall = true;
-  }, "addon-install");
-
-  await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap_const);
-
-  ok(sawInstall);
-});
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrap_globals.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// This verifies that bootstrap.js has the expected globals defined
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
-
-const ADDONS = {
-  bootstrap_globals: {
-    "manifest.json": JSON.stringify({
-      applications: {
-        gecko: {
-          id: "bootstrap_globals@tests.mozilla.org",
-        },
-      },
-      legacy: {
-        type: "bootstrap",
-      },
-      manifest_version: 2,
-      name: "Test Bootstrap 1",
-      version: "1.0",
-    }),
-    "bootstrap.js": String.raw`var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-var seenGlobals = new Set();
-var scope = this;
-function checkGlobal(name, type) {
-  if (scope[name] && typeof(scope[name]) == type)
-    seenGlobals.add(name);
-}
-
-var wrapped = {};
-Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals");
-for (let [name, type] of wrapped.expectedGlobals) {
-  checkGlobal(name, type);
-}
-
-function startup(data, reason) {
-  Services.obs.notifyObservers({ wrappedJSObject: seenGlobals }, "bootstrap-seen-globals");
-}
-
-function install(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
-`,
-  },
-};
-
-const EXPECTED_GLOBALS = [["console", "object"]];
-
-async function run_test() {
-  do_test_pending();
-  await promiseStartupManager();
-  let sawGlobals = false;
-
-  Services.obs.addObserver(function(subject) {
-    subject.wrappedJSObject.expectedGlobals = EXPECTED_GLOBALS;
-  }, "bootstrap-request-globals");
-
-  Services.obs.addObserver(function({ wrappedJSObject: seenGlobals }) {
-    for (let [name] of EXPECTED_GLOBALS) {
-      Assert.ok(seenGlobals.has(name));
-    }
-
-    sawGlobals = true;
-  }, "bootstrap-seen-globals");
-
-  await AddonTestUtils.promiseInstallXPI(ADDONS.bootstrap_globals);
-  Assert.ok(sawGlobals);
-  await promiseShutdownManager();
-  do_test_finished();
-}
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrapped_chrome_manifest.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const ADDON = {
-  "manifest.json": JSON.stringify({
-    applications: {
-      gecko: {
-        id: "bug675371@tests.mozilla.org",
-      },
-    },
-    legacy: {
-      type: "bootstrap",
-    },
-    manifest_version: 2,
-    name: "Test Bootstrap 1",
-    version: "1.0",
-  }),
-  "chrome.manifest": `content bug675371 .`,
-  "test.js": `var active = true;`,
-};
-
-add_task(async function run_test() {
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-  await promiseStartupManager();
-});
-
-function checkActive(expected) {
-  let target = { active: false };
-  let load = () => {
-    Services.scriptloader.loadSubScript(
-      "chrome://bug675371/content/test.js",
-      target
-    );
-  };
-
-  if (expected) {
-    load();
-  } else {
-    Assert.throws(load, /Error opening input stream/);
-  }
-  equal(target.active, expected, "Manifest is active?");
-}
-
-add_task(async function test() {
-  let { addon } = await AddonTestUtils.promiseInstallXPI(ADDON);
-
-  Assert.ok(addon.isActive);
-
-  // Tests that chrome.manifest is registered when the addon is installed.
-  checkActive(true);
-
-  await addon.disable();
-  checkActive(false);
-
-  await addon.enable();
-  checkActive(true);
-
-  await promiseShutdownManager();
-
-  // Tests that chrome.manifest remains registered at app shutdown.
-  checkActive(true);
-});
deleted file mode 100644
--- a/common/test/xpcshell/xpcshell.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[DEFAULT]
-tags = addons
-head = head_addons.js
-support-files =
-  data/**
-
-[test_bootstrap.js]
-[test_bootstrap_const.js]
-[test_bootstrap_globals.js]
-[test_bootstrapped_chrome_manifest.js]
--- a/mail/base/content/aboutAddonsExtra.js
+++ b/mail/base/content/aboutAddonsExtra.js
@@ -1,26 +1,14 @@
 /* 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/. */
 
 /* import-globals-from ../../../../toolkit/mozapps/extensions/content/aboutaddons.js */
 
-var { ExtensionSupport } = ChromeUtils.import(
-  "resource:///modules/ExtensionSupport.jsm"
-);
-var { BrowserUtils } = ChromeUtils.import(
-  "resource://gre/modules/BrowserUtils.jsm"
-);
-
-var mailExtBundle = Services.strings.createBundle(
-  "chrome://messenger/locale/extensionsOverlay.properties"
-);
-var extensionsNeedingRestart = new Set();
-
 const THUNDERBIRD_THEME_PREVIEWS = new Map([
   [
     "thunderbird-compact-light@mozilla.org",
     "chrome://mozapps/content/extensions/firefox-compact-light.svg",
   ],
   [
     "thunderbird-compact-dark@mozilla.org",
     "chrome://mozapps/content/extensions/firefox-compact-dark.svg",
@@ -62,136 +50,9 @@ const THUNDERBIRD_THEME_PREVIEWS = new M
 
   let _getScreenshotUrlForAddon = getScreenshotUrlForAddon;
   getScreenshotUrlForAddon = function(addon) {
     if (THUNDERBIRD_THEME_PREVIEWS.has(addon.id)) {
       return THUNDERBIRD_THEME_PREVIEWS.get(addon.id);
     }
     return _getScreenshotUrlForAddon(addon);
   };
-
-  let _getAddonMessageInfo = getAddonMessageInfo;
-  getAddonMessageInfo = async function(addon) {
-    let result = await _getAddonMessageInfo(addon);
-    if (!result.message) {
-      let { stringName } = getTrueState(addon, "gDetailView._addon");
-      if (stringName) {
-        result.message = mailExtBundle.formatStringFromName(stringName, [
-          addon.name,
-          brandBundle.GetStringFromName("brandShortName"),
-        ]);
-        result.type = "success";
-        extensionsNeedingRestart.add(addon.id);
-      } else {
-        extensionsNeedingRestart.delete(addon.id);
-      }
-      setRestartBar();
-    }
-    return result;
-  };
-
-  let listener = {
-    onUninstalling(addon) {
-      if (ExtensionSupport.loadedLegacyExtensions.hasAnyState(addon.id)) {
-        extensionsNeedingRestart.add(addon.id);
-        setRestartBar();
-      }
-    },
-    onUninstalled(addon) {
-      if (ExtensionSupport.loadedLegacyExtensions.hasAnyState(addon.id)) {
-        extensionsNeedingRestart.add(addon.id);
-        setRestartBar();
-      }
-    },
-  };
-  AddonManager.addAddonListener(listener);
-  window.addEventListener("unload", () =>
-    AddonManager.removeAddonListener(listener)
-  );
-
-  // If a legacy extension has been removed, it needs a restart but is not in the list
-  // - show the restart bar anyway.
-  let removed = await ExtensionSupport.loadedLegacyExtensions.listRemoved();
-  for (let removedExtension of removed) {
-    extensionsNeedingRestart.add(removedExtension.id);
-  }
-  setRestartBar();
 })();
-
-function setRestartBar() {
-  let list = document.querySelector("addon-list");
-  if (!list || list.type != "extension") {
-    return;
-  }
-
-  let restartBar = document.getElementById("restartBar");
-  if (extensionsNeedingRestart.size == 0) {
-    if (restartBar) {
-      restartBar.remove();
-    }
-    return;
-  }
-  if (restartBar) {
-    return;
-  }
-
-  restartBar = document.createElement("message-bar");
-  restartBar.id = "restartBar";
-  restartBar.setAttribute("type", "warning");
-
-  const message = document.createElement("span");
-  message.textContent = mailExtBundle.formatStringFromName(
-    "globalRestartMessage",
-    [brandBundle.GetStringFromName("brandShortName")]
-  );
-
-  const restart = document.createElement("button");
-  restart.textContent = mailExtBundle.GetStringFromName("globalRestartButton");
-  restart.addEventListener("click", () => {
-    BrowserUtils.restartApplication();
-  });
-
-  restartBar.append(message, restart);
-  list.pendingUninstallStack.append(restartBar);
-}
-
-/**
- * The true status of legacy extensions, which AddonManager doesn't know
- * about because it thinks all extensions are restartless.
- *
- * @return An object of three properties:
- *         stringName: a string to display to the user, from extensionsOverlay.properties.
- *         undoFunction: function to call, should the user want to return to the previous state.
- *         version: the current version of the extension.
- */
-function getTrueState(addon) {
-  let state = ExtensionSupport.loadedLegacyExtensions.get(addon.id);
-  let returnObject = {};
-
-  if (!state) {
-    return returnObject;
-  }
-
-  if (
-    addon.pendingOperations & AddonManager.PENDING_UNINSTALL &&
-    ExtensionSupport.loadedLegacyExtensions.has(addon.id)
-  ) {
-    returnObject.stringName = "warnLegacyUninstall";
-    returnObject.undoFunction = addon.cancelUninstall;
-  } else if (state.pendingOperation == "install") {
-    returnObject.stringName = "warnLegacyInstall";
-    returnObject.undoFunction = addon.uninstall;
-  } else if (addon.userDisabled) {
-    returnObject.stringName = "warnLegacyDisable";
-    returnObject.undoFunction = addon.enable;
-  } else if (state.pendingOperation == "enable") {
-    returnObject.stringName = "warnLegacyEnable";
-    returnObject.undoFunction = addon.disable;
-  } else if (state.pendingOperation == "upgrade") {
-    returnObject.stringName = "warnLegacyUpgrade";
-    returnObject.version = state.version;
-  } else if (state.pendingOperation == "downgrade") {
-    returnObject.stringName = "warnLegacyDowngrade";
-    returnObject.version = state.version;
-  }
-
-  return returnObject;
-}
--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -3968,35 +3968,16 @@ async function initAddonPrefsMenu(
       } else if (addon.optionsType === null || addon.optionsType == 3) {
         addonsFound.push({
           addon,
           optionsURL: addon.optionsURL,
           optionsOpenInTab: addon.optionsType == 3,
         });
       }
     }
-    if (
-      ExtensionSupport.loadedLegacyExtensions.has(addon.id) ||
-      ExtensionSupport.loadedBootstrapExtensions.has(addon.id)
-    ) {
-      let webextension = ExtensionParent.GlobalManager.getExtension(addon.id);
-      let legacy = webextension.manifest.legacy;
-      if (
-        typeof legacy == "boolean" ||
-        !legacy.options ||
-        !legacy.options.page
-      ) {
-        continue;
-      }
-      addonsFound.push({
-        addon,
-        optionsURL: legacy.options.page,
-        optionsOpenInTab: legacy.options.open_in_tab,
-      });
-    }
   }
 
   // Populate the menu with addon names and icons.
   // Note: Having the following code in the getAddonsByTypes() async callback
   // above works on Windows and Linux but doesn't work on Mac, see bug 1419145.
   if (addonsFound.length > 0) {
     addonsFound.sort((a, b) => a.addon.name.localeCompare(b.addon.name));
     for (let {
--- a/mail/base/content/messenger.xhtml
+++ b/mail/base/content/messenger.xhtml
@@ -484,17 +484,16 @@
       <label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
       <html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
     </popupnotificationcontent>
   </popupnotification>
 
   <popupnotification id="addon-installed-notification" hidden="true">
     <popupnotificationcontent class="addon-installed-notification-content" orient="vertical">
       <html:ul id="addon-installed-list" class="addon-installed-list"/>
-      <description id="addon-installed-restart-text" class="addon-installed-restart-text"/>
     </popupnotificationcontent>
   </popupnotification>
 
 #ifdef MOZ_UPDATER
   <popupnotification id="app-update-notification" hidden="true">
     <popupnotificationcontent class="app-update-notification-content" orient="vertical">
       <description id="app-update-text" class="app-update-text"/>
     </popupnotificationcontent>
--- a/mail/base/modules/ExtensionsUI.jsm
+++ b/mail/base/modules/ExtensionsUI.jsm
@@ -9,19 +9,16 @@ const ADDONS_PROPERTIES = "chrome://mess
 const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
 const DEFAULT_EXTENSION_ICON =
   "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
-const { BrowserUtils } = ChromeUtils.import(
-  "resource://gre/modules/BrowserUtils.jsm"
-);
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
   ExtensionData: "resource://gre/modules/Extension.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   Services: "resource://gre/modules/Services.jsm",
   setTimeout: "resource://gre/modules/Timer.jsm",
   StringBundle: "resource:///modules/StringBundle.jsm",
@@ -323,82 +320,46 @@ var gXPInstallObserver = {
     let brandBundle = document.getElementById("bundle_brand");
     let appName = brandBundle.getString("brandShortName");
 
     let message = addonsBundle.getFormattedString("addonPostInstall.message1", [
       "<>",
       appName,
     ]);
 
-    let restartRequired = await this._installRequiresRestart(addon);
     let icon = DEFAULT_EXTENSION_ICON;
     if (addon.isWebExtension) {
       icon = AddonManager.getPreferredIconURL(addon, 32, window) || icon;
     }
 
     let options = {
       hideClose: true,
       timeout: Date.now() + 30000,
       popupIconURL: icon,
       name: addon.name,
     };
 
     let list = document.getElementById("addon-installed-list");
     list.hidden = true;
 
-    this._showInstallNotification(browser, restartRequired, message, options);
+    this._showInstallNotification(browser, message, options);
   },
 
-  _showInstallNotification(browser, restartRequired, message, options) {
-    let document = getTopWindow().document;
-
-    let brandBundle = document.getElementById("bundle_brand");
-    let appName = brandBundle.getString("brandShortName");
-
-    let action;
-    let secondaryActions = null;
-    let textEl = document.getElementById("addon-installed-restart-text");
-    if (restartRequired) {
-      action = {
-        label: addonsBundle.getString("addonPostInstall.restart.label"),
-        accessKey: addonsBundle.getString("addonPostInstall.restart.accesskey"),
-        callback: () => {
-          BrowserUtils.restartApplication();
-        },
-      };
-      secondaryActions = [
-        {
-          label: addonsBundle.getString("addonPostInstall.noRestart.label"),
-          accessKey: addonsBundle.getString(
-            "addonPostInstall.noRestart.accesskey"
-          ),
-          callback: () => {},
-        },
-      ];
-      textEl.textContent = addonsBundle.getFormattedString(
-        "addonPostInstall.restartRequired.message",
-        [appName]
-      );
-      textEl.hidden = false;
-    } else {
-      action = {
-        label: addonsBundle.getString("addonPostInstall.okay.label"),
-        accessKey: addonsBundle.getString("addonPostInstall.okay.accesskey"),
-        callback: () => {},
-      };
-      textEl.hidden = true;
-    }
-
+  _showInstallNotification(browser, message, options) {
     showNotification(
       browser,
       "addon-installed",
       message,
       "addons-notification-icon",
-      action,
-      secondaryActions,
+      {
+        label: addonsBundle.getString("addonPostInstall.okay.label"),
+        accessKey: addonsBundle.getString("addonPostInstall.okay.accesskey"),
+        callback: () => {},
+      },
+      null,
       options
     );
   },
 
   /* eslint-disable complexity */
   observe(subject, topic, data) {
     let installInfo = subject.wrappedJSObject;
     let browser = installInfo.browser || installInfo.target;
@@ -783,40 +744,16 @@ var gXPInstallObserver = {
 
   _removeProgressNotification(browser) {
     let notification = getNotification("addon-progress", browser);
     if (notification) {
       notification.remove();
     }
   },
 
-  async _installRequiresRestart(addon) {
-    if (!addon.isWebExtension) {
-      return false;
-    }
-
-    let data = new ExtensionData(addon.getResourceURI());
-    await data.loadManifest();
-
-    if (!data.manifest.legacy) {
-      return false;
-    }
-
-    switch (typeof data.manifest.legacy) {
-      case "boolean":
-        return data.manifest.legacy;
-      case "object":
-        return !(
-          data.manifest.legacy.type && data.manifest.legacy.type == "bootstrap"
-        );
-      default:
-        return false;
-    }
-  },
-
   async _checkForSideloaded(browser) {
     let sideloaded = await AddonManagerPrivate.getNewSideloads();
     if (sideloaded.length == 0) {
       return;
     }
 
     // Check if the user wants any sideloaded add-ons installed.
 
@@ -859,39 +796,30 @@ var gXPInstallObserver = {
       [appName]
     );
 
     let list = document.getElementById("addon-installed-list");
     list.hidden = false;
     while (list.lastChild) {
       list.lastChild.remove();
     }
-    let textEl = document.getElementById("addon-installed-restart-text");
-    textEl.textContent = addonsBundle.getFormattedString(
-      "addonPostInstall.restartRequired.message",
-      [appName]
-    );
-    textEl.hidden = false;
 
-    let restartRequired = false;
     for (let addon of enabled) {
       let item = document.createElementNS(HTML_NS, "li");
       item.textContent = addon.name;
       list.appendChild(item);
-      restartRequired =
-        restartRequired || (await this._installRequiresRestart(addon));
     }
 
     let options = {
       popupIconURL: DEFAULT_EXTENSION_ICON,
       hideClose: true,
       timeout: Date.now() + 30000,
     };
 
-    this._showInstallNotification(browser, restartRequired, message, options);
+    this._showInstallNotification(browser, message, options);
   },
 };
 
 Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
 Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked");
 Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
 Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
 Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
--- a/mail/components/extensions/ext-mail.json
+++ b/mail/components/extensions/ext-mail.json
@@ -80,23 +80,16 @@
   "geckoProfiler": {
     "url": "chrome://extensions/content/parent/ext-geckoProfiler.js",
     "schema": "chrome://extensions/content/schemas/geckoProfiler.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["geckoProfiler"]
     ]
   },
-  "legacy": {
-    "url": "chrome://messenger/content/parent/ext-legacy.js",
-    "schema": "chrome://messenger/content/schemas/legacy.json",
-    "scopes": ["addon_parent"],
-    "events": ["disable", "uninstall", "update"],
-    "manifest": ["legacy"]
-  },
   "mailTabs": {
     "url": "chrome://messenger/content/parent/ext-mailTabs.js",
     "schema": "chrome://messenger/content/schemas/mailTabs.json",
     "scopes": ["addon_parent"],
     "manifest": ["mailTabs"],
     "paths": [
       ["mailTabs"]
     ]
--- a/mail/components/extensions/jar.mn
+++ b/mail/components/extensions/jar.mn
@@ -15,17 +15,16 @@ messenger.jar:
     content/messenger/parent/ext-addressBook.js    (parent/ext-addressBook.js)
     content/messenger/parent/ext-browserAction.js  (parent/ext-browserAction.js)
     content/messenger/parent/ext-chrome-settings-overrides.js      (parent/ext-chrome-settings-overrides.js)
     content/messenger/parent/ext-cloudFile.js      (parent/ext-cloudFile.js)
     content/messenger/parent/ext-commands.js       (../../../../browser/components/extensions/parent/ext-commands.js)
     content/messenger/parent/ext-compose.js        (parent/ext-compose.js)
     content/messenger/parent/ext-composeAction.js  (parent/ext-composeAction.js)
     content/messenger/parent/ext-folders.js        (parent/ext-folders.js)
-    content/messenger/parent/ext-legacy.js         (parent/ext-legacy.js)
     content/messenger/parent/ext-mail.js           (parent/ext-mail.js)
     content/messenger/parent/ext-mailTabs.js       (parent/ext-mailTabs.js)
     content/messenger/parent/ext-menus.js          (parent/ext-menus.js)
     content/messenger/parent/ext-messageDisplay.js (parent/ext-messageDisplay.js)
     content/messenger/parent/ext-messageDisplayAction.js  (parent/ext-messageDisplayAction.js)
     content/messenger/parent/ext-messages.js       (parent/ext-messages.js)
     content/messenger/parent/ext-pkcs11.js         (../../../../browser/components/extensions/parent/ext-pkcs11.js)
     content/messenger/parent/ext-tabs.js           (parent/ext-tabs.js)
@@ -35,17 +34,16 @@ messenger.jar:
     content/messenger/schemas/addressBook.json     (schemas/addressBook.json)
     content/messenger/schemas/browserAction.json   (schemas/browserAction.json)
     content/messenger/schemas/chrome_settings_overrides.json       (schemas/chrome_settings_overrides.json)
     content/messenger/schemas/cloudFile.json       (schemas/cloudFile.json)
     content/messenger/schemas/commands.json        (../../../../browser/components/extensions/schemas/commands.json)
     content/messenger/schemas/compose.json         (schemas/compose.json)
     content/messenger/schemas/composeAction.json   (schemas/composeAction.json)
     content/messenger/schemas/folders.json         (schemas/folders.json)
-    content/messenger/schemas/legacy.json          (schemas/legacy.json)
     content/messenger/schemas/mailTabs.json        (schemas/mailTabs.json)
     content/messenger/schemas/menus.json           (schemas/menus.json)
     content/messenger/schemas/menus_child.json     (schemas/menus_child.json)
     content/messenger/schemas/messageDisplay.json  (schemas/messageDisplay.json)
     content/messenger/schemas/messageDisplayAction.json  (schemas/messageDisplayAction.json)
     content/messenger/schemas/messages.json        (schemas/messages.json)
     content/messenger/schemas/pkcs11.json          (../../../../browser/components/extensions/schemas/pkcs11.json)
     content/messenger/schemas/tabs.json            (schemas/tabs.json)
deleted file mode 100644
--- a/mail/components/extensions/parent/ext-legacy.js
+++ /dev/null
@@ -1,469 +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/. */
-
-ChromeUtils.defineModuleGetter(
-  this,
-  "ChromeManifest",
-  "resource:///modules/ChromeManifest.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "ExtensionSupport",
-  "resource:///modules/ExtensionSupport.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "Overlays",
-  "resource:///modules/Overlays.jsm"
-);
-ChromeUtils.defineModuleGetter(
-  this,
-  "XPIInternal",
-  "resource://gre/modules/addons/XPIProvider.jsm"
-);
-
-Cu.importGlobalProperties(["fetch"]);
-
-var { XPCOMUtils } = ChromeUtils.import(
-  "resource://gre/modules/XPCOMUtils.jsm"
-);
-var { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "BOOTSTRAP_REASONS", () => {
-  const { XPIProvider } = ChromeUtils.import(
-    "resource://gre/modules/addons/XPIProvider.jsm"
-  );
-  return XPIProvider.BOOTSTRAP_REASONS;
-});
-
-const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
-var logger = Log.repository.getLogger("addons.bootstrap");
-
-let bootstrapScopes = new Map();
-let cachedParams = new Map();
-
-Services.obs.addObserver(() => {
-  for (let [id, scope] of bootstrapScopes.entries()) {
-    if (ExtensionSupport.loadedBootstrapExtensions.has(id)) {
-      scope.shutdown(
-        { ...cachedParams.get(id) },
-        BOOTSTRAP_REASONS.APP_SHUTDOWN
-      );
-    }
-  }
-}, "quit-application-granted");
-
-var { ExtensionError } = ExtensionUtils;
-
-this.legacy = class extends ExtensionAPI {
-  async onManifestEntry(entryName) {
-    if (this.extension.manifest.legacy) {
-      if (this.extension.manifest.legacy.type == "bootstrap") {
-        await this.registerBootstrapped();
-      } else {
-        await this.registerNonBootstrapped();
-      }
-    }
-  }
-
-  // This function is for non-bootstrapped add-ons.
-
-  async registerNonBootstrapped() {
-    this.extension.legacyLoaded = true;
-
-    let state = {
-      id: this.extension.id,
-      pendingOperation: null,
-      version: this.extension.version,
-    };
-    if (ExtensionSupport.loadedLegacyExtensions.has(this.extension.id)) {
-      state = ExtensionSupport.loadedLegacyExtensions.get(this.extension.id);
-      let versionComparison = Services.vc.compare(
-        this.extension.version,
-        state.version
-      );
-      if (versionComparison != 0) {
-        if (versionComparison > 0) {
-          state.pendingOperation = "upgrade";
-          ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
-        } else if (versionComparison < 0) {
-          state.pendingOperation = "downgrade";
-          ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
-        }
-
-        // Forget any cached files we might've had from another version of this extension.
-        Services.obs.notifyObservers(null, "startupcache-invalidate");
-      }
-      console.log(
-        `Legacy WebExtension ${
-          this.extension.id
-        } has already been loaded in this run, refusing to do so again. Please restart.`
-      );
-      return;
-    }
-
-    ExtensionSupport.loadedLegacyExtensions.set(this.extension.id, state);
-    if (this.extension.startupReason == "ADDON_INSTALL") {
-      // Usually, sideloaded extensions are disabled when they first appear,
-      // but to run calendar tests, we disable this.
-      let scope = XPIInternal.XPIStates.findAddon(this.extension.id).location
-        .scope;
-      let autoDisableScopes = Services.prefs.getIntPref(
-        "extensions.autoDisableScopes"
-      );
-
-      // If the extension was just installed from the distribution folder,
-      // it's in the profile extensions folder. We don't want to disable it.
-      let isDistroAddon = Services.prefs.getBoolPref(
-        "extensions.installedDistroAddon." + this.extension.id,
-        false
-      );
-
-      if (!isDistroAddon && scope & autoDisableScopes) {
-        state.pendingOperation = "install";
-        console.log(
-          `Legacy WebExtension ${
-            this.extension.id
-          } loading for other reason than startup (${
-            this.extension.startupReason
-          }), refusing to load immediately.`
-        );
-        ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
-
-        // Forget any cached files we might've had if this extension was previously installed.
-        Services.obs.notifyObservers(null, "startupcache-invalidate");
-        return;
-      }
-    }
-    if (this.extension.startupReason == "ADDON_ENABLE") {
-      state.pendingOperation = "enable";
-      console.log(
-        `Legacy WebExtension ${
-          this.extension.id
-        } loading for other reason than startup (${
-          this.extension.startupReason
-        }), refusing to load immediately.`
-      );
-      ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
-      return;
-    }
-
-    let extensionRoot;
-    if (this.extension.rootURI instanceof Ci.nsIJARURI) {
-      extensionRoot = this.extension.rootURI.JARFile.QueryInterface(
-        Ci.nsIFileURL
-      ).file;
-      console.log("Loading packed extension from", extensionRoot.path);
-    } else {
-      extensionRoot = this.extension.rootURI.QueryInterface(Ci.nsIFileURL).file;
-      console.log("Loading unpacked extension from", extensionRoot.path);
-    }
-
-    // Have Gecko do as much loading as is still possible
-    try {
-      Cc["@mozilla.org/component-manager-extra;1"]
-        .getService(Ci.nsIComponentManagerExtra)
-        .addLegacyExtensionManifestLocation(extensionRoot);
-    } catch (e) {
-      throw new ExtensionError(e.message, e.fileName, e.lineNumber);
-    }
-
-    // Load chrome.manifest
-    let appinfo = Services.appinfo;
-    let options = {
-      application: appinfo.ID,
-      appversion: appinfo.version,
-      platformversion: appinfo.platformVersion,
-      os: appinfo.OS,
-      osversion: Services.sysinfo.getProperty("version"),
-      abi: appinfo.XPCOMABI,
-    };
-    let loader = async filename => {
-      let url = this.extension.getURL(filename);
-      return fetch(url).then(response => response.text());
-    };
-    let chromeManifest = new ChromeManifest(loader, options);
-    await chromeManifest.parse("chrome.manifest");
-
-    // Load preference files
-    console.log("Loading add-on preferences from ", extensionRoot.path);
-    ExtensionSupport.loadAddonPrefs(extensionRoot);
-
-    // Fire profile-after-change notifications, because we are past that event by now
-    console.log("Firing profile-after-change listeners for", this.extension.id);
-    let profileAfterChange = chromeManifest.category.get(
-      "profile-after-change"
-    );
-    for (let contractid of profileAfterChange.values()) {
-      let service = contractid.startsWith("service,");
-      let instance;
-      try {
-        if (service) {
-          instance = Cc[contractid.substr(8)].getService(Ci.nsIObserver);
-        } else {
-          instance = Cc[contractid].createInstance(Ci.nsIObserver);
-        }
-
-        instance.observe(null, "profile-after-change", null);
-      } catch (e) {
-        console.error(
-          "Error firing profile-after-change listener for",
-          contractid
-        );
-      }
-    }
-
-    // Overlays.load must only be called once per window per extension.
-    // We use this WeakSet to remember all windows we've already seen.
-    let seenDocuments = new WeakSet();
-
-    // Listen for new windows to overlay.
-    let documentObserver = {
-      observe(doc) {
-        if (
-          ExtensionCommon.instanceOf(doc, "HTMLDocument") &&
-          !seenDocuments.has(doc)
-        ) {
-          seenDocuments.add(doc);
-          Overlays.load(chromeManifest, doc.defaultView);
-        }
-      },
-    };
-    Services.obs.addObserver(documentObserver, "chrome-document-interactive");
-
-    // Add overlays to all existing windows.
-    getAllWindows().forEach(win => {
-      if (
-        ["interactive", "complete"].includes(win.document.readyState) &&
-        !seenDocuments.has(win.document)
-      ) {
-        seenDocuments.add(win.document);
-        Overlays.load(chromeManifest, win);
-      }
-    });
-
-    this.extension.callOnClose({
-      close: () => {
-        Services.obs.removeObserver(
-          documentObserver,
-          "chrome-document-interactive"
-        );
-      },
-    });
-  }
-
-  // The following functions are for bootstrapped add-ons.
-
-  async registerBootstrapped() {
-    let oldParams = cachedParams.get(this.extension.id);
-    let params = {
-      id: this.extension.id,
-      version: this.extension.version,
-      resourceURI: Services.io.newURI(this.extension.resourceURL),
-      installPath: this.extensionFile.path,
-    };
-    cachedParams.set(this.extension.id, { ...params });
-
-    if (
-      oldParams &&
-      ["ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(
-        this.extension.startupReason
-      )
-    ) {
-      params.oldVersion = oldParams.version;
-    }
-
-    let scope = await this.loadScope();
-    bootstrapScopes.set(this.extension.id, scope);
-
-    if (
-      ["ADDON_INSTALL", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(
-        this.extension.startupReason
-      )
-    ) {
-      scope.install(params, BOOTSTRAP_REASONS[this.extension.startupReason]);
-    }
-    scope.startup(params, BOOTSTRAP_REASONS[this.extension.startupReason]);
-    ExtensionSupport.loadedBootstrapExtensions.add(this.extension.id);
-  }
-
-  static onDisable(id) {
-    if (bootstrapScopes.has(id)) {
-      bootstrapScopes
-        .get(id)
-        .shutdown({ ...cachedParams.get(id) }, BOOTSTRAP_REASONS.ADDON_DISABLE);
-      ExtensionSupport.loadedBootstrapExtensions.delete(id);
-    }
-  }
-
-  static onUpdate(id, manifest) {
-    if (bootstrapScopes.has(id)) {
-      let params = {
-        ...cachedParams.get(id),
-        newVersion: manifest.version,
-      };
-      let reason = BOOTSTRAP_REASONS.ADDON_UPGRADE;
-      if (Services.vc.compare(params.newVersion, params.version) < 0) {
-        reason = BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
-      }
-
-      let scope = bootstrapScopes.get(id);
-      scope.shutdown(params, reason);
-      scope.uninstall(params, reason);
-      ExtensionSupport.loadedBootstrapExtensions.delete(id);
-      bootstrapScopes.delete(id);
-    }
-  }
-
-  static onUninstall(id) {
-    if (bootstrapScopes.has(id)) {
-      bootstrapScopes
-        .get(id)
-        .uninstall(
-          { ...cachedParams.get(id) },
-          BOOTSTRAP_REASONS.ADDON_UNINSTALL
-        );
-      bootstrapScopes.delete(id);
-    }
-  }
-
-  get extensionFile() {
-    let uri = Services.io.newURI(this.extension.resourceURL);
-    if (uri instanceof Ci.nsIJARURI) {
-      uri = uri.QueryInterface(Ci.nsIJARURI).JARFile;
-    }
-    return uri.QueryInterface(Ci.nsIFileURL).file;
-  }
-
-  loadScope() {
-    let { extension } = this;
-    let file = this.extensionFile;
-    let uri = this.extension.getURL("bootstrap.js");
-    let principal = Services.scriptSecurityManager.getSystemPrincipal();
-
-    let sandbox = new Cu.Sandbox(principal, {
-      sandboxName: uri,
-      addonId: this.extension.id,
-      wantGlobalProperties: ["ChromeUtils"],
-      metadata: { addonID: this.extension.id, URI: uri },
-    });
-
-    try {
-      Object.assign(sandbox, BOOTSTRAP_REASONS);
-
-      XPCOMUtils.defineLazyGetter(
-        sandbox,
-        "console",
-        () => new ConsoleAPI({ consoleID: `addon/${this.extension.id}` })
-      );
-
-      Services.scriptloader.loadSubScript(uri, sandbox);
-    } catch (e) {
-      logger.warn(`Error loading bootstrap.js for ${this.extension.id}`, e);
-    }
-
-    function findMethod(name) {
-      if (sandbox.name) {
-        return sandbox.name;
-      }
-
-      try {
-        let method = Cu.evalInSandbox(name, sandbox);
-        return method;
-      } catch (err) {}
-
-      return () => {
-        logger.warn(
-          `Add-on ${extension.id} is missing bootstrap method ${name}`
-        );
-      };
-    }
-
-    let install = findMethod("install");
-    let uninstall = findMethod("uninstall");
-    let startup = findMethod("startup");
-    let shutdown = findMethod("shutdown");
-
-    return {
-      install(...args) {
-        try {
-          install(...args);
-        } catch (ex) {
-          logger.warn(
-            `Exception running bootstrap method install on ${extension.id}`,
-            ex
-          );
-        }
-      },
-
-      uninstall(...args) {
-        try {
-          uninstall(...args);
-        } catch (ex) {
-          logger.warn(
-            `Exception running bootstrap method uninstall on ${extension.id}`,
-            ex
-          );
-        } finally {
-          // Forget any cached files we might've had from this extension.
-          Services.obs.notifyObservers(null, "startupcache-invalidate");
-        }
-      },
-
-      startup(...args) {
-        logger.debug(`Registering manifest for ${file.path}\n`);
-        Components.manager.addBootstrappedManifestLocation(file);
-        try {
-          startup(...args);
-        } catch (ex) {
-          logger.warn(
-            `Exception running bootstrap method startup on ${extension.id}`,
-            ex
-          );
-        }
-      },
-
-      shutdown(data, reason) {
-        try {
-          shutdown(data, reason);
-        } catch (ex) {
-          logger.warn(
-            `Exception running bootstrap method shutdown on ${extension.id}`,
-            ex
-          );
-        } finally {
-          if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
-            logger.debug(`Removing manifest for ${file.path}\n`);
-            Components.manager.removeBootstrappedManifestLocation(file);
-          }
-        }
-      },
-    };
-  }
-};
-
-function getAllWindows() {
-  function getChildDocShells(parentDocShell) {
-    let docShells = parentDocShell.getAllDocShellsInSubtree(
-      Ci.nsIDocShellTreeItem.typeAll,
-      Ci.nsIDocShell.ENUMERATE_FORWARDS
-    );
-
-    for (let docShell of docShells) {
-      docShell
-        .QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIWebProgress);
-      domWindows.push(docShell.domWindow);
-    }
-  }
-
-  let domWindows = [];
-  for (let win of Services.ww.getWindowEnumerator()) {
-    let parentDocShell = win
-      .getInterface(Ci.nsIWebNavigation)
-      .QueryInterface(Ci.nsIDocShell);
-    getChildDocShells(parentDocShell);
-  }
-  return domWindows;
-}
deleted file mode 100644
--- a/mail/components/extensions/schemas/legacy.json
+++ /dev/null
@@ -1,43 +0,0 @@
-[
-  {
-    "namespace": "manifest",
-    "types": [
-      {
-        "$extend": "WebExtensionManifest",
-        "properties": {
-          "legacy": {
-            "optional": true,
-            "choices": [
-              {
-                "type": "boolean"
-              },
-              {
-                "type": "object",
-                "properties": {
-                  "type": {
-                    "type": "string",
-                    "enum": ["xul", "bootstrap"],
-                    "optional": true
-                  },
-                  "options": {
-                    "type": "object",
-                    "properties": {
-                      "page": {
-                        "type": "string"
-                      },
-                      "open_in_tab": {
-                        "type": "boolean",
-                        "optional": true
-                      }
-                    },
-                    "optional": true
-                  }
-                }
-              }
-            ]
-          }
-        }
-      }
-    ]
-  }
-]
--- a/mail/locales/en-US/chrome/messenger/addons.properties
+++ b/mail/locales/en-US/chrome/messenger/addons.properties
@@ -17,24 +17,16 @@ xpinstallDisabledButton.accesskey=n
 # %2$S is replaced with the localized name of the application.
 addonPostInstall.message1=%1$S has been added to %2$S.
 # LOCALIZATION NOTE (addonPostInstall.multiple.message1)
 # %1$S is replaced with the localized name of the application.
 addonPostInstall.multiple.message=These add-ons have been added to %1$S:
 addonPostInstall.okay.label=OK
 addonPostInstall.okay.accesskey=O
 
-# LOCALIZATION NOTE (addonPostInstall.restartRequired.message)
-# %S is the application name
-addonPostInstall.restartRequired.message=%S must be restarted to complete the installation.
-addonPostInstall.restart.label=Restart now
-addonPostInstall.restart.accesskey=R
-addonPostInstall.noRestart.label=Not now
-addonPostInstall.noRestart.accesskey=N
-
 # LOCALIZATION NOTE (addonDownloadingAndVerifying):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
 addonDownloadingAndVerifying=Downloading and verifying add-on…;Downloading and verifying #1 add-ons…
 addonDownloadVerifying=Verifying
 
 addonInstall.unsigned=(Unverified)
deleted file mode 100644
--- a/mail/locales/en-US/chrome/messenger/extensionsOverlay.properties
+++ /dev/null
@@ -1,17 +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/.
-
-# LOCALIZATION NOTE (warnLegacyUpgrade, warnLegacyDowngrade, warnLegacyEnable, warnLegacyDisable, warnLegacyInstall, warnLegacyUninstall)
-# %1$S is the add-on name, %2$S is brand name
-warnLegacyUpgrade=%1$S will be upgraded after you restart %2$S.
-warnLegacyDowngrade=%1$S will be downgraded after you restart %2$S.
-warnLegacyEnable=%1$S will be enabled after you restart %2$S.
-warnLegacyDisable=%1$S will be disabled after you restart %2$S.
-warnLegacyInstall=%1$S will be installed after you restart %2$S.
-warnLegacyUninstall=%1$S will be uninstalled after you restart %2$S.
-warnLegacyRestartButton=Restart
-warnLegacyUndoButton=Undo
-
-globalRestartMessage=Some operations will not be completed until you restart %S.
-globalRestartButton=Restart Now
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -83,17 +83,16 @@
   locale/@AB_CD@/messenger/folderpane.dtd                               (%chrome/messenger/folderpane.dtd)
   locale/@AB_CD@/messenger/folderProps.dtd                              (%chrome/messenger/folderProps.dtd)
   locale/@AB_CD@/messenger/folderWidgets.properties                     (%chrome/messenger/folderWidgets.properties)
   locale/@AB_CD@/messenger/subscribe.dtd                                (%chrome/messenger/subscribe.dtd)
   locale/@AB_CD@/messenger/subscribe.properties                         (%chrome/messenger/subscribe.properties)
   locale/@AB_CD@/messenger/msgHdrViewOverlay.dtd                        (%chrome/messenger/msgHdrViewOverlay.dtd)
   locale/@AB_CD@/messenger/editContactOverlay.dtd                       (%chrome/messenger/editContactOverlay.dtd)
   locale/@AB_CD@/messenger/editContactOverlay.properties                (%chrome/messenger/editContactOverlay.properties)
-  locale/@AB_CD@/messenger/extensionsOverlay.properties                 (%chrome/messenger/extensionsOverlay.properties)
   locale/@AB_CD@/messenger/mailEditorOverlay.dtd                        (%chrome/messenger/mailEditorOverlay.dtd)
   locale/@AB_CD@/messenger/msgSynchronize.dtd                           (%chrome/messenger/msgSynchronize.dtd)
   locale/@AB_CD@/messenger/offline.properties                           (%chrome/messenger/offline.properties)
   locale/@AB_CD@/messenger/junkMailInfo.dtd                             (%chrome/messenger/junkMailInfo.dtd)
   locale/@AB_CD@/messenger/viewLog.dtd                                  (%chrome/messenger/viewLog.dtd)
   locale/@AB_CD@/messenger/FilterListDialog.dtd                         (%chrome/messenger/FilterListDialog.dtd)
   locale/@AB_CD@/messenger/CustomHeaders.dtd                            (%chrome/messenger/CustomHeaders.dtd)
   locale/@AB_CD@/messenger/FilterEditor.dtd                             (%chrome/messenger/FilterEditor.dtd)