Bug 1565180 - Update Thunderbird modifications to about:addons. r=Paenglab
authorGeoff Lankow <geoff@darktrojan.net>
Tue, 16 Jul 2019 14:27:53 +1200
changeset 35373 26f109dd92ca8e79da388ad0a13c8533ba9de103
parent 35372 dd033e1d92122dd75630572da86411268332a9f2
child 35374 305c070b0794bd82da9c49ca88360213b303d190
push id2486
push userclokep@gmail.com
push dateMon, 02 Sep 2019 20:24:43 +0000
treeherdercomm-beta@1b30a9a6e7f6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersPaenglab
bugs1565180
Bug 1565180 - Update Thunderbird modifications to about:addons. r=Paenglab
.eslintignore
common/src/ExtensionSupport.jsm
mail/app/profile/all-thunderbird.js
mail/base/content/aboutAddonsExtra.css
mail/base/content/aboutAddonsExtra.js
mail/base/content/extensions.xml
mail/base/content/extensionsOverlay.css
mail/base/content/msgMail3PaneWindow.js
mail/base/content/specialTabs.js
mail/base/jar.mn
mail/locales/en-US/chrome/messenger/addons.properties
mail/locales/en-US/chrome/messenger/extensionsOverlay.properties
mail/themes/shared/jar.inc.mn
mail/themes/shared/mail/extensionsOverlay.css
--- a/.eslintignore
+++ b/.eslintignore
@@ -81,12 +81,8 @@ mail/locales/en-US/all-l10n.js
 
 # prefs files
 calendar/lightning/content/lightning.js
 calendar/providers/gdata/defaults/preferences.js
 calendar/timezones/preferences.js
 
 # third party library
 calendar/base/modules/ical.js
-
-# Temporarily disabled: see bug 1565177
-mail/base/content/aboutAddonsExtra.js
-mail/base/content/extensions.xml
--- a/common/src/ExtensionSupport.jsm
+++ b/common/src/ExtensionSupport.jsm
@@ -63,16 +63,27 @@ var ExtensionSupport = {
     },
     // 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("");
 
--- a/mail/app/profile/all-thunderbird.js
+++ b/mail/app/profile/all-thunderbird.js
@@ -96,16 +96,18 @@ pref("app.feedback.baseURL", "https://in
 
 // Show error messages in error console.
 pref("javascript.options.showInConsole", true);
 
 // Controls enabling of the extension system logging (can reduce performance)
 pref("extensions.logging.enabled", false);
 pref("extensions.overlayloader.loglevel", "warn");
 
+pref("extensions.abuseReport.enabled", false);
+
 // Strict compatibility makes add-ons incompatible by default.
 #ifndef RELEASE_OR_BETA
 pref("extensions.strictCompatibility", false);
 #else
 pref("extensions.strictCompatibility", true);
 #endif
 
 pref("extensions.update.autoUpdateDefault", true);
rename from mail/base/content/extensionsOverlay.css
rename to mail/base/content/aboutAddonsExtra.css
--- a/mail/base/content/extensionsOverlay.css
+++ b/mail/base/content/aboutAddonsExtra.css
@@ -1,47 +1,68 @@
 /* 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 url("chrome://messenger/skin/extensionsOverlay.css");
+:root {
+  --in-content-categories-background: #ebebef;
+}
+
+@supports -moz-bool-pref("browser.in-content.dark-mode") {
+  @media (prefers-color-scheme: dark) {
+    :root {
+      --in-content-categories-background: rgba(249,249,250,0.1);
+    }
+  }
+}
+
+#category-box {
+  background-color: var(--in-content-categories-background);
+}
+
+.nav-button {
+  -moz-appearance: none;
+  list-style-image: url(chrome://messenger/skin/icons/navigation.svg);
+  margin-top: 15px;
+  margin-bottom: 10px;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
 
-/* Hide FX specific elements we don't use */
-.legacy-warning,
-#private-browsing-notice,
-#detail-privateBrowsing-row,
-#detail-privateBrowsing-row-footer,
-.privateBrowsing-notice-container {
+.nav-button[disabled="true"] > .toolbarbutton-icon {
+  opacity: 0.4;
+}
+
+.nav-button:not([disabled="true"]):hover {
+  background-color: #bebebe;
+  cursor: pointer;
+}
+
+.nav-button > .toolbarbutton-text {
+  display: none;
+}
+
+#back-btn:-moz-locale-dir(rtl),
+#forward-btn:-moz-locale-dir(ltr) {
+  transform: scaleX(-1);
+}
+
+#categories {
+  padding-top: 0;
+}
+
+#preferencesButton {
+  margin-bottom: 36px;
+}
+
+.sidebar-footer-button.help-button {
   display: none;
 }
 
 #nav-header {
   height: 50px;
 }
 
-#tb-legacy-extensions-notice {
-  display: none;
-  -moz-box-align: start;
-  margin-left: 28px;
-  margin-right: 28px;
-  margin-bottom: 8px;
-}
-
-#list-view[type="extension"] #tb-legacy-extensions-notice {
-  display: -moz-box;
-}
-
-#tb-legacy-extensions-notice > .alert {
-  width: 664px;
-  padding: 4px;
-}
-
-#tb-legacy-extensions-notice > .alert > description {
-  margin: 0;
-}
-
 #category-plugin {
   display: none;
 }
-
-.addon[status="installed"][type="extension"] {
-  -moz-binding: url("chrome://messenger/content/extensions.xml#thunderbird-addon-generic");
-}
--- a/mail/base/content/aboutAddonsExtra.js
+++ b/mail/base/content/aboutAddonsExtra.js
@@ -1,246 +1,178 @@
 /* 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/extensions.js */
+/* 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");
 
-gStrings.mailExt =
-  Services.strings.createBundle("chrome://messenger/locale/extensionsOverlay.properties");
+var mailExtBundle = Services.strings.createBundle("chrome://messenger/locale/extensionsOverlay.properties");
+var extensionsNeedingRestart = new Set();
+
+/* This file runs in both the outer window, which controls the categories list, search bar, etc.,
+ * and the inner window which is the list of add-ons or the detail view. */
+(async function() {
+  if (window.location.href == "about:addons") {
+    let contentStylesheet = document.createProcessingInstruction(
+      "xml-stylesheet",
+      'href="chrome://messenger/content/aboutAddonsExtra.css" type="text/css"');
+    document.insertBefore(contentStylesheet, document.documentElement);
+
+    // Add navigation buttons for back and forward on the addons page.
+    let hbox = document.createXULElement("hbox");
+    hbox.setAttribute("id", "nav-header");
+    hbox.setAttribute("align", "center");
+    hbox.setAttribute("pack", "center");
 
-(function() {
+    let backButton = document.createXULElement("toolbarbutton");
+    backButton.setAttribute("id", "back-btn");
+    backButton.setAttribute("class", "nav-button");
+    backButton.setAttribute("command", "cmd_back");
+    backButton.setAttribute("tooltiptext", mailExtBundle.GetStringFromName("cmdBackTooltip"));
+    backButton.setAttribute("disabled", "true");
+
+    let forwardButton = document.createXULElement("toolbarbutton");
+    forwardButton.setAttribute("id", "forward-btn");
+    forwardButton.setAttribute("class", "nav-button");
+    forwardButton.setAttribute("command", "cmd_forward");
+    forwardButton.setAttribute("tooltiptext", mailExtBundle.GetStringFromName("cmdForwardTooltip"));
+    forwardButton.setAttribute("disabled", "true");
+    hbox.appendChild(backButton);
+    hbox.appendChild(forwardButton);
+
+    document.getElementById("category-box")
+            .insertBefore(hbox, document.getElementById("categories"));
+
+    // Fix the "Search on addons.mozilla.org" placeholder text in the searchbox.
+    let textbox = document.getElementById("header-search");
+    let placeholder = textbox.getAttribute("placeholder");
+    placeholder = placeholder.replace("addons.mozilla.org", "addons.thunderbird.net");
+    textbox.setAttribute("placeholder", placeholder);
+    return;
+  }
+
   window.isCorrectlySigned = function() { return true; };
 
-  let contentStylesheet = document.createProcessingInstruction(
-    "xml-stylesheet",
-    'href="chrome://messenger/content/extensionsOverlay.css" type="text/css"');
-  document.insertBefore(contentStylesheet, document.documentElement);
-
-  // Add navigation buttons for back and forward on the addons page.
-  let hbox = document.createXULElement("hbox");
-  hbox.setAttribute("id", "nav-header");
-  hbox.setAttribute("align", "center");
-  hbox.setAttribute("pack", "center");
-
-  let backButton = document.createXULElement("toolbarbutton");
-  backButton.setAttribute("id", "back-btn");
-  backButton.setAttribute("class", "nav-button");
-  backButton.setAttribute("command", "cmd_back");
-  backButton.setAttribute("tooltiptext", gStrings.mailExt.GetStringFromName("cmdBackTooltip"));
-  backButton.setAttribute("disabled", "true");
-
-  let forwardButton = document.createXULElement("toolbarbutton");
-  forwardButton.setAttribute("id", "forward-btn");
-  forwardButton.setAttribute("class", "nav-button");
-  forwardButton.setAttribute("command", "cmd_forward");
-  forwardButton.setAttribute("tooltiptext", gStrings.mailExt.GetStringFromName("cmdForwardTooltip"));
-  forwardButton.setAttribute("disabled", "true");
-  hbox.appendChild(backButton);
-  hbox.appendChild(forwardButton);
-
-  document.getElementById("category-box")
-          .insertBefore(hbox, document.getElementById("categories"));
-
-  // Fix the "Search on addons.mozilla.org" placeholder text in the searchbox.
-  let textbox = document.getElementById("header-search");
-  let placeholder = textbox.getAttribute("placeholder");
-  placeholder = placeholder.replace("addons.mozilla.org", "addons.thunderbird.net");
-  textbox.setAttribute("placeholder", placeholder);
-
-  // Tell the world about legacy extensions.
-  let alertContainer = document.createXULElement("vbox");
-  alertContainer.id = "tb-legacy-extensions-notice";
-  alertContainer.className = "alert-container";
+  delete window.browserBundle;
+  window.browserBundle = Services.strings.createBundle("chrome://messenger/locale/addons.properties");
 
-  let alert = document.createXULElement("vbox");
-  alert.className = "alert";
-
-  let description = document.createXULElement("description");
-  let messageString = gStrings.mailExt.GetStringFromName("legacyInfo") + " ";
-  messageString = messageString.replace("#1", gStrings.brandShortName);
-  messageString = messageString.replace("#2", Services.appinfo.version);
-  description.textContent = messageString;
-
-  let label = document.createXULElement("label", {is: "text-link"});
-  label.className = "text-link plain";
-  label.href = "https://support.mozilla.org/kb/unable-install-add-on-extension-theme-thunderbird";
-  label.value = gStrings.mailExt.GetStringFromName("legacyLearnMore");
-
-  description.appendChild(label);
-  alert.appendChild(description);
-  alertContainer.appendChild(alert);
-
-  gListView.node.insertBefore(alertContainer, document.getElementById("legacy-extensions-notice"));
-})();
-
-window._oldSortElements = window.sortElements;
-window.sortElements = function(aElements, aSortBy, aAscending) {
-  if (aSortBy.length != 2 || aSortBy[0] != "uiState" || aSortBy[1] != "name") {
-    window._oldSortElements(aElements, aSortBy, aAscending);
-  }
-
-  let getUIState = function(addon) {
-    if (addon.pendingOperations == AddonManager.PENDING_DISABLE) {
-      return "pendingDisable";
+  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();
     }
-    if (ExtensionSupport.loadedLegacyExtensions.has(addon.id) && addon.userDisabled) {
-      return "pendingDisable";
-    }
-    if (addon.pendingOperations == AddonManager.PENDING_UNINSTALL) {
-      return "pendingUninstall";
-    }
-    if (!addon.isActive &&
-        (addon.pendingOperations != AddonManager.PENDING_ENABLE &&
-         addon.pendingOperations != AddonManager.PENDING_INSTALL)) {
-      return "disabled";
-    }
-    return "enabled";
+    return result;
   };
 
-  aElements.sort((a, b) => {
-    const UISTATE_ORDER = ["enabled", "askToActivate", "pendingDisable", "pendingUninstall", "disabled"];
+  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));
 
-    let aState = UISTATE_ORDER.indexOf(getUIState(a.mAddon));
-    let bState = UISTATE_ORDER.indexOf(getUIState(b.mAddon));
-    if (aState < bState) {
-      return -1;
-    }
-    if (aState > bState) {
-      return 1;
-    }
-    if (a.mAddon.name < b.mAddon.name) {
-      return -1;
-    }
-    if (a.mAddon.name > b.mAddon.name) {
-      return 1;
-    }
-    return 0;
-  });
-};
-if (window.gViewController.currentViewObj == window.gListView) {
-  window.sortList(window.gListView._listBox, ["uiState", "name"], true);
-}
+  // 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();
+})();
 
-gDetailView._oldDetailUpdateState = gDetailView.updateState;
-gDetailView.updateState = function() {
-  this._oldDetailUpdateState();
-
-  let restartButton = document.getElementById("restart-btn");
-  let undoButton = document.getElementById("undo-btn");
-
-  if (ExtensionSupport.loadedLegacyExtensions.has(this._addon.id)) {
-    this.node.setAttribute("active", "true");
+function setRestartBar() {
+  let list = document.querySelector("addon-list");
+  if (!list || list.type != "extension") {
+    return;
   }
 
-  if (ExtensionSupport.loadedLegacyExtensions.hasAnyState(this._addon.id, true)) {
-    let { stringName, undoCommand, version } = getTrueState(this._addon, "gDetailView._addon");
-
-    if (stringName) {
-      this.node.setAttribute("notification", "warning");
-      this.node.removeAttribute("pending");
-
-      let warningContainer = document.getElementById("warning-container");
-      let warning = document.getElementById("detail-warning");
-      document.getElementById("detail-warning-link").hidden = true;
-      warning.textContent = gStrings.mailExt.formatStringFromName(
-        stringName, [this._addon.name, gStrings.brandShortName]
-      );
-
-      if (version) {
-        document.getElementById("detail-version").value = version;
-      }
-
-      if (!restartButton) {
-        restartButton = document.createXULElement("button");
-        restartButton.id = "restart-btn";
-        restartButton.className = "button-link restart-btn";
-        restartButton.setAttribute(
-          "label", gStrings.mailExt.GetStringFromName("warnLegacyRestartButton")
-        );
-        restartButton.setAttribute("oncommand", "BrowserUtils.restartApplication()");
-        warningContainer.insertBefore(restartButton, warningContainer.lastElementChild);
-      }
-      restartButton.hidden = false;
-      if (undoCommand) {
-        if (!undoButton) {
-          undoButton = document.createXULElement("button");
-          undoButton.className = "button-link undo-btn";
-          undoButton.setAttribute(
-            "label", gStrings.mailExt.GetStringFromName("warnLegacyUndoButton")
-          );
-          // We shouldn't really attach non-anonymous content to anonymous content, but we can.
-          warningContainer.insertBefore(undoButton, warningContainer.lastElementChild);
-        }
-        undoButton.setAttribute("oncommand", undoCommand);
-        undoButton.hidden = false;
-      } else if (undoButton) {
-        undoButton.hidden = true;
-      }
-      return;
+  let restartBar = document.getElementById("restartBar");
+  if (extensionsNeedingRestart.size == 0) {
+    if (restartBar) {
+      restartBar.remove();
     }
+    return;
+  }
+  if (restartBar) {
+    return;
   }
 
-  if (restartButton) {
-    restartButton.hidden = true;
-  }
-  if (undoButton) {
-    undoButton.hidden = true;
-  }
-};
+  restartBar = document.createElement("message-bar");
+  restartBar.id = "restartBar";
+  restartBar.setAttribute("type", "warning");
 
-/**
- * Update the UI when things change.
- */
-function statusChangedObserver(subject, topic, data) {
-  let { id } = subject.wrappedJSObject;
+  const message = document.createElement("span");
+  message.textContent = mailExtBundle.formatStringFromName(
+    "globalRestartMessage", [brandBundle.GetStringFromName("brandShortName")]
+  );
 
-  if (gViewController.currentViewObj == gListView) {
-    let listItem = gListView.getListItemForID(id);
-    if (listItem) {
-      setTimeout(() => listItem._updateState());
-    }
-  } else if (gViewController.currentViewObj == gDetailView) {
-    setTimeout(() => gDetailView.updateState());
-  }
+  const restart = document.createElement("button");
+  restart.textContent = mailExtBundle.GetStringFromName("globalRestartButton");
+  restart.addEventListener("click", () => {
+    BrowserUtils.restartApplication();
+  });
+
+  restartBar.append(message, restart);
+  list.pendingUninstallStack.append(restartBar);
 }
-Services.obs.addObserver(statusChangedObserver, "legacy-addon-status-changed");
-window.addEventListener("unload", () => {
-  Services.obs.removeObserver(statusChangedObserver, "legacy-addon-status-changed");
-});
 
 /**
  * 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.
- *         undoCommand: code to run, should the user want to return to the previous state.
+ *         undoFunction: function to call, should the user want to return to the previous state.
  *         version: the current version of the extension.
  */
-function getTrueState(addon, addonRef) {
+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.undoCommand = `${addonRef}.cancelUninstall()`;
+    returnObject.undoFunction = addon.cancelUninstall;
   } else if (state.pendingOperation == "install") {
     returnObject.stringName = "warnLegacyInstall";
-    returnObject.undoCommand = `${addonRef}.uninstall()`;
+    returnObject.undoFunction = addon.uninstall;
   } else if (addon.userDisabled) {
     returnObject.stringName = "warnLegacyDisable";
-    returnObject.undoCommand = `${addonRef}.enable()`;
+    returnObject.undoFunction = addon.enable;
   } else if (state.pendingOperation == "enable") {
     returnObject.stringName = "warnLegacyEnable";
-    returnObject.undoCommand = `${addonRef}.disable()`;
+    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;
   }
 
deleted file mode 100644
--- a/mail/base/content/extensions.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.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/. -->
-
-<!-- import-globals-from aboutAddonsExtra.js -->
-
-<!DOCTYPE bindings>
-
-<bindings id="thunderbird-addon-bindings"
-          xmlns="http://www.mozilla.org/xbl"
-          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-          xmlns:xbl="http://www.mozilla.org/xbl">
-  <binding id="thunderbird-addon-generic"
-           extends="chrome://mozapps/content/extensions/extensions.xml#addon-generic">
-    <implementation>
-      <constructor><![CDATA[
-        var {ExtensionParent} = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
-        this._updateState = this._updateState.bind(this);
-        ExtensionParent.apiManager.on("startup", this._updateState);
-        ExtensionParent.apiManager.on("shutdown", this._updateState);
-      ]]></constructor>
-      <destructor><![CDATA[
-        ExtensionParent.apiManager.off("startup", this._updateState);
-        ExtensionParent.apiManager.off("shutdown", this._updateState);
-      ]]></destructor>
-      <method name="_updateState">
-        <body><![CDATA[
-          this.__proto__.__proto__._updateState.call(this);
-          let undoButton = this._warningContainer.querySelector("button.undo-btn");
-
-          if (ExtensionSupport.loadedLegacyExtensions.has(this.mAddon.id)) {
-            this.setAttribute("active", "true");
-          }
-
-          if (ExtensionSupport.loadedLegacyExtensions.hasAnyState(this.mAddon.id, true)) {
-            let {
-              stringName,
-              undoCommand,
-            } = getTrueState(this.mAddon, "document.getBindingParent(this).mAddon");
-
-            if (stringName) {
-              this.setAttribute("notification", "warning");
-              this.removeAttribute("pending");
-
-              this._warningLink.hidden = true;
-              this._warning.textContent = gStrings.mailExt.formatStringFromName(
-                stringName, [this.mAddon.name, gStrings.brandShortName], 2
-              );
-
-              this._warningBtn.label = gStrings.mailExt.GetStringFromName(
-                "warnLegacyRestartButton"
-              );
-              this._warningBtn.setAttribute("oncommand", "BrowserUtils.restartApplication()");
-              this._warningBtn.hidden = false;
-
-              if (undoCommand) {
-                if (!undoButton) {
-                  undoButton = document.createXULElement("button");
-                  undoButton.className = "button-link undo-btn";
-                  undoButton.setAttribute(
-                    "label", gStrings.mailExt.GetStringFromName("warnLegacyUndoButton")
-                  );
-                  // We shouldn't really attach non-anonymous content to anonymous content,
-                  // but we can.
-                  this._warningContainer.insertBefore(
-                    undoButton, this._warningContainer.lastElementChild
-                  );
-                }
-                undoButton.setAttribute("oncommand", undoCommand);
-                undoButton.hidden = false;
-              } else if (undoButton) {
-                undoButton.hidden = true;
-              }
-              return;
-            }
-          }
-
-          if (undoButton) {
-            undoButton.hidden = true;
-          }
-        ]]></body>
-      </method>
-    </implementation>
-  </binding>
-</bindings>
--- a/mail/base/content/msgMail3PaneWindow.js
+++ b/mail/base/content/msgMail3PaneWindow.js
@@ -1825,8 +1825,12 @@ var gDragSpaceObserver = {
     if (Services.prefs.getBoolPref(this.pref)) {
       document.documentElement.setAttribute("extradragspace", "true");
     } else {
       document.documentElement.removeAttribute("extradragspace");
     }
     TabsInTitlebar.update();
   },
 };
+
+function promptRemoveExtension(addon) {
+  return { remove: true, report: false };
+}
--- a/mail/base/content/specialTabs.js
+++ b/mail/base/content/specialTabs.js
@@ -277,16 +277,19 @@ var contentTabBaseType = {
   // Code to run if a particular document is loaded in a tab.
   // The array members (functions) are for the respective document URLs
   // as specified in inContentWhitelist.
   inContentOverlays: [
     // about:addons
     function(aDocument, aTab) {
       // Switch off the context menu.
       aTab.browser.removeAttribute("context");
+      Services.scriptloader.loadSubScript(
+        "chrome://messenger/content/aboutAddonsExtra.js", aDocument.defaultView
+      );
     },
 
     // Let's not mess with about:blank.
     null,
 
     // Other about:* pages.
     function(aDocument, aTab) {
       // Provide context menu for about:* pages.
@@ -1420,8 +1423,22 @@ var specialTabs = {
         null, aTab.browser.contentPrincipal);
 
     // Save this off so we know about it later,
     aTab.browser.mIconURL = aIcon;
     // and display the new icon.
     document.getElementById("tabmail").setTabIcon(aTab, aIcon);
   },
 };
+
+let documentObserver = {
+  observe(document) {
+    if (!document.location ||
+        document.location.href != "chrome://mozapps/content/extensions/aboutaddons.html") {
+      return;
+    }
+
+    Services.scriptloader.loadSubScript(
+      "chrome://messenger/content/aboutAddonsExtra.js", document.defaultView
+    );
+  },
+};
+Services.obs.addObserver(documentObserver, "chrome-document-interactive");
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -7,18 +7,16 @@ messenger.jar:
 % override chrome://global/content/nsDragAndDrop.js chrome://messenger/content/nsDragAndDrop.js
 % override chrome://messagebody/skin/messageBody.css chrome://messenger/skin/messageBody.css
 #ifndef MOZILLA_OFFICIAL
 % override chrome://browser/content/browser-development-helpers.js chrome://messenger/content/browser-development-helpers.js
   content/messenger/browser-development-helpers.js (../../common/src/browser-development-helpers.js)
 #endif
     content/messenger/mailWindow.js                 (content/mailWindow.js)
     content/messenger/messageDisplay.js             (content/messageDisplay.js)
-    content/messenger/extensions.xml                (content/extensions.xml)
-    content/messenger/extensionsOverlay.css         (content/extensionsOverlay.css)
     content/messenger/folderDisplay.js              (content/folderDisplay.js)
     content/messenger/foldersummary.js              (content/foldersummary.js)
     content/messenger/mailWindowOverlay.js          (content/mailWindowOverlay.js)
 *   content/messenger/messageWindow.xul             (content/messageWindow.xul)
     content/messenger/messageWindow.js              (content/messageWindow.js)
     content/messenger/mailContextMenus.js           (content/mailContextMenus.js)
     content/messenger/nsContextMenu.js              (content/nsContextMenu.js)
 *   content/messenger/messenger.xul                 (content/messenger.xul)
@@ -51,16 +49,17 @@ messenger.jar:
     content/messenger/commandglue.js                (content/commandglue.js)
 *   content/messenger/SearchDialog.xul              (content/SearchDialog.xul)
     content/messenger/SearchDialog.js               (content/SearchDialog.js)
 *   content/messenger/ABSearchDialog.xul            (content/ABSearchDialog.xul)
     content/messenger/ABSearchDialog.js             (content/ABSearchDialog.js)
     content/messenger/FilterListDialog.xul          (content/FilterListDialog.xul)
     content/messenger/FilterListDialog.js           (content/FilterListDialog.js)
     content/messenger/specialTabs.js                (content/specialTabs.js)
+    content/messenger/aboutAddonsExtra.css          (content/aboutAddonsExtra.css)
     content/messenger/aboutAddonsExtra.js           (content/aboutAddonsExtra.js)
     content/messenger/aboutDialog-appUpdater.js     (content/aboutDialog-appUpdater.js)
 *   content/messenger/aboutDialog.xul               (content/aboutDialog.xul)
     content/messenger/aboutDialog.js                (content/aboutDialog.js)
 *   content/messenger/aboutRights.xhtml             (content/aboutRights.xhtml)
 *   content/messenger/systemIntegrationDialog.xul   (content/systemIntegrationDialog.xul)
     content/messenger/systemIntegrationDialog.js    (content/systemIntegrationDialog.js)
     content/messenger/folderPane.js                 (content/folderPane.js)
--- a/mail/locales/en-US/chrome/messenger/addons.properties
+++ b/mail/locales/en-US/chrome/messenger/addons.properties
@@ -98,16 +98,17 @@ addonInstallErrorBlocklisted=%S could no
 webextPerms.header=Add %S?
 
 webextPerms.unsignedWarning=Caution: This add-on is unverified. Malicious add-ons can steal your private information or compromise your computer. Only install this add-on if you trust the source.
 
 # LOCALIZATION NOTE (webextPerms.listIntro)
 # This string will be followed by a list of permissions requested
 # by the webextension.
 webextPerms.listIntro=It requires your permission to:
+webextPerms.learnMore=Learn more about permissions
 webextPerms.add.label=Add
 webextPerms.add.accessKey=A
 webextPerms.cancel.label=Cancel
 webextPerms.cancel.accessKey=C
 
 # LOCALIZATION NOTE (webextPerms.sideloadMenuItem)
 # %1$S will be replaced with the localized name of the sideloaded add-on.
 # %2$S will be replace with the name of the application (e.g., Firefox, Nightly)
--- a/mail/locales/en-US/chrome/messenger/extensionsOverlay.properties
+++ b/mail/locales/en-US/chrome/messenger/extensionsOverlay.properties
@@ -1,22 +1,20 @@
 # 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/.
 
 cmdBackTooltip=Go back one page
 cmdForwardTooltip=Go forward one page
 
-# LOCALIZATION NOTE (legacyInfo):
-# #1 is the application name, #2 is the application version
-legacyInfo=Legacy extensions must be updated to be compatible with #1 #2.
-legacyLearnMore=Learn moreā€¦
-
 # 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/themes/shared/jar.inc.mn
+++ b/mail/themes/shared/jar.inc.mn
@@ -6,17 +6,16 @@
 # actual theme-specific manifests, so that shared resources need only
 # be specified once. As a result, the source file paths are relative
 # to the location of the actual manifest.
 
   skin/classic/messenger/aboutNetError.css                    (../shared/mail/aboutNetError.css)
   skin/classic/messenger/aboutSupport.css                     (../shared/mail/aboutSupport.css)
   skin/classic/messenger/addressbook/icons/menu.svg           (../shared/mail/icons/menu.svg)
   skin/classic/messenger/downloads/download.svg               (../shared/mail/icons/download.svg)
-  skin/classic/messenger/extensionsOverlay.css                (../shared/mail/extensionsOverlay.css)
   skin/classic/messenger/icons/address.svg                    (../shared/mail/icons/address.svg)
   skin/classic/messenger/icons/addcontact.svg                 (../shared/mail/icons/addcontact.svg)
   skin/classic/messenger/icons/addlist.svg                    (../shared/mail/icons/addlist.svg)
   skin/classic/messenger/icons/accounts.svg                   (../shared/mail/icons/accounts.svg)
   skin/classic/messenger/icons/addon-install-blocked.svg      (../shared/mail/icons/addon-install-blocked.svg)
   skin/classic/messenger/icons/addon-install-confirm.svg      (../shared/mail/icons/addon-install-confirm.svg)
   skin/classic/messenger/icons/addon-install-downloading.svg  (../shared/mail/icons/addon-install-downloading.svg)
   skin/classic/messenger/icons/addon-install-error.svg        (../shared/mail/icons/addon-install-error.svg)
deleted file mode 100644
--- a/mail/themes/shared/mail/extensionsOverlay.css
+++ /dev/null
@@ -1,60 +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/. */
-
-:root {
-  --in-content-categories-background: #ebebef;
-}
-
-@supports -moz-bool-pref("browser.in-content.dark-mode") {
-  @media (prefers-color-scheme: dark) {
-    :root {
-      --in-content-categories-background: rgba(249,249,250,0.1);
-    }
-  }
-}
-
-#category-box {
-  background-color: var(--in-content-categories-background);
-}
-
-.nav-button {
-  -moz-appearance: none;
-  list-style-image: url(chrome://messenger/skin/icons/navigation.svg);
-  margin-top: 15px;
-  margin-bottom: 10px;
-  border: 1px solid transparent;
-  border-radius: 2px;
-  -moz-context-properties: fill;
-  fill: currentColor;
-}
-
-.nav-button[disabled="true"] > .toolbarbutton-icon {
-  opacity: 0.4;
-}
-
-.nav-button:not([disabled="true"]):hover {
-  background-color: #bebebe;
-  cursor: pointer;
-}
-
-.nav-button > .toolbarbutton-text {
-  display: none;
-}
-
-#back-btn:-moz-locale-dir(rtl),
-#forward-btn:-moz-locale-dir(ltr) {
-  transform: scaleX(-1);
-}
-
-#categories {
-  padding-top: 0;
-}
-
-#preferencesButton {
- margin-bottom: 36px;
-}
-
-.sidebar-footer-button.help-button {
-  display: none;
-}