Bug 1382689 Fix "Remove" button on details page for a legacy extension draft
authorAndrew Swan <aswan@mozilla.com>
Mon, 31 Jul 2017 17:23:01 -0700
changeset 641161 b74f3a0d89035873065931c94989ad20cd9e251a
parent 618576 87824406b9feb420a3150720707b424d7cee5915
child 724742 1f8774c68f1778b6fe7aed0d5cbe3431d8321b86
push id72464
push useraswan@mozilla.com
push dateSun, 06 Aug 2017 05:36:16 +0000
bugs1382689
milestone56.0a1
Bug 1382689 Fix "Remove" button on details page for a legacy extension Also updated addon listeners in extensions.js to use Set instead of arrays and added a way to listen to AddonListener events on any addon. MozReview-Commit-ID: Ev3kJgcr30G
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/test/browser/browser_legacy.js
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -102,17 +102,17 @@ XPCOMUtils.defineLazyGetter(gStrings, "a
 });
 
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "legacyWarningExceptions",
                                       PREF_LEGACY_EXCEPTIONS, "",
                                       raw => raw.split(","));
 XPCOMUtils.defineLazyPreferenceGetter(this, "legacyExtensionsEnabled",
                                       PREF_LEGACY_ENABLED, true,
-                                      () => gLegacyView.refresh());
+                                      () => gLegacyView.refreshVisibility());
 
 document.addEventListener("load", initialize, true);
 window.addEventListener("unload", shutdown);
 
 function promiseEvent(event, target, capture = false) {
   return new Promise(resolve => {
     target.addEventListener(event, resolve, {capture, once: true});
   });
@@ -478,17 +478,17 @@ if (window.QueryInterface(Ci.nsIInterfac
           .sessionHistory) {
   var gHistory = HTML5History;
 } else {
   gHistory = FakeHistory;
 }
 
 var gEventManager = {
   _listeners: {},
-  _installListeners: [],
+  _installListeners: new Set(),
 
   initialize() {
     const ADDON_EVENTS = ["onEnabling", "onEnabled", "onDisabling",
                           "onDisabled", "onUninstalling", "onUninstalled",
                           "onInstalled", "onOperationCancelled",
                           "onUpdateAvailable", "onUpdateFinished",
                           "onCompatibilityUpdateAvailable",
                           "onPropertyChanged"];
@@ -564,60 +564,56 @@ var gEventManager = {
   shutdown() {
     AddonManager.removeManagerListener(this);
     AddonManager.removeInstallListener(this);
     AddonManager.removeAddonListener(this);
   },
 
   registerAddonListener(aListener, aAddonId) {
     if (!(aAddonId in this._listeners))
-      this._listeners[aAddonId] = [];
-    else if (this._listeners[aAddonId].indexOf(aListener) != -1)
-      return;
-    this._listeners[aAddonId].push(aListener);
+      this._listeners[aAddonId] = new Set();
+    this._listeners[aAddonId].add(aListener);
   },
 
   unregisterAddonListener(aListener, aAddonId) {
     if (!(aAddonId in this._listeners))
       return;
-    var index = this._listeners[aAddonId].indexOf(aListener);
-    if (index == -1)
-      return;
-    this._listeners[aAddonId].splice(index, 1);
+    this._listeners[aAddonId].delete(aListener);
   },
 
   registerInstallListener(aListener) {
-    if (this._installListeners.indexOf(aListener) != -1)
-      return;
-    this._installListeners.push(aListener);
+    this._installListeners.add(aListener);
   },
 
   unregisterInstallListener(aListener) {
-    var i = this._installListeners.indexOf(aListener);
-    if (i == -1)
-      return;
-    this._installListeners.splice(i, 1);
+    this._installListeners.delete(aListener);
   },
 
   delegateAddonEvent(aEvent, aParams) {
     var addon = aParams.shift();
     if (!(addon.id in this._listeners))
       return;
 
-    var listeners = this._listeners[addon.id];
-    for (let listener of listeners) {
+    function tryListener(listener) {
       if (!(aEvent in listener))
-        continue;
+        return;
       try {
         listener[aEvent].apply(listener, aParams);
       } catch (e) {
         // this shouldn't be fatal
         Cu.reportError(e);
       }
     }
+
+    for (let listener of this._listeners[addon.id]) {
+      tryListener(listener);
+    }
+    for (let listener of this._listeners["ANY"]) {
+      tryListener(listener);
+    }
   },
 
   delegateInstallEvent(aEvent, aParams) {
     var existingAddon = aEvent == "onExternalInstall" ? aParams[1] : aParams[0].existingAddon;
     // If the install is an update then send the event to all listeners
     // registered for the existing add-on
     if (existingAddon)
       this.delegateAddonEvent(aEvent, [existingAddon].concat(aParams));
@@ -2783,17 +2779,27 @@ var gLegacyView = {
 
   initialize() {
     this.node = document.getElementById("legacy-view");
     this._listBox = document.getElementById("legacy-list");
     this._categoryItem = gCategories.get("addons://legacy/");
 
     document.getElementById("legacy-learnmore").href = SUPPORT_URL + "webextensions";
 
-    this.refresh();
+    gEventManager.registerAddonListener(this, "ANY");
+
+    this.refreshVisibility();
+  },
+
+  shutdown() {
+    gEventManager.unregisterAddonListener(this, "ANY");
+  },
+
+  onUninstalled() {
+    this.refreshVisibility();
   },
 
   async show(type, request) {
     let addons = await AddonManager.getAddonsByTypes(["extension"]);
     addons = addons.filter(a => !a.hidden &&
                               (isDisabledLegacy(a) || isDisabledUnsigned(a)));
 
     while (this._listBox.itemCount > 0)
@@ -2819,17 +2825,17 @@ var gLegacyView = {
 
   getSelectedAddon() {
     var item = this._listBox.selectedItem;
     if (item)
       return item.mAddon;
     return null;
   },
 
-  async refresh() {
+  async refreshVisibility() {
     if (legacyExtensionsEnabled) {
       this._categoryItem.disabled = true;
       return;
     }
 
     let extensions = await AddonManager.getAddonsByTypes(["extension"]);
 
     let haveUnsigned = false;
@@ -2847,16 +2853,26 @@ var gLegacyView = {
       this._categoryItem.disabled = false;
       let name = gStrings.ext.GetStringFromName(`type.${haveUnsigned ? "unsupported" : "legacy"}.name`);
       this._categoryItem.setAttribute("name", name);
       this._categoryItem.tooltiptext = name;
     } else {
       this._categoryItem.disabled = true;
     }
   },
+
+  getListItemForID(aId) {
+    var listitem = this._listBox.firstChild;
+    while (listitem) {
+      if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId)
+        return listitem;
+      listitem = listitem.nextSibling;
+    }
+    return null;
+  }
 };
 
 var gListView = {
   node: null,
   _listBox: null,
   _emptyNotice: null,
   _type: null,
 
--- a/toolkit/mozapps/extensions/test/browser/browser_legacy.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_legacy.js
@@ -106,17 +106,17 @@ add_task(async function() {
   is(mgrWin.gLegacyView._categoryItem.disabled, true, "Legacy category is hidden");
 
   // Now add a legacy extension
   provider.createAddons(disabledAddon);
 
   // The legacy category does not watch for new installs since new
   // legacy extensions cannot be installed while legacy extensions
   // are disabled, so manually refresh it here.
-  await mgrWin.gLegacyView.refresh();
+  await mgrWin.gLegacyView.refreshVisibility();
 
   // Make sure we re-render the extensions list, after that we should
   // still just have the original two entries.
   await catUtils.openType("plugin");
   await catUtils.openType("extension");
 
   checkList("addon-list",
             ["webextension@tests.mozilla.org", "mozilla@tests.mozilla.org"]);
@@ -151,17 +151,17 @@ add_task(async function() {
   provider.createAddons(unsignedAddons);
   SpecialPowers.pushPrefEnv({
     set: [
       ["xpinstall.signatures.required", true],
     ],
   });
 
   // The entry on the left side should now read "Unsupported"
-  await mgrWin.gLegacyView.refresh();
+  await mgrWin.gLegacyView.refreshVisibility();
   is(catItem.disabled, false, "Legacy category is visible");
   is(catItem.getAttribute("name"), get_string("type.unsupported.name"),
      "Category label with unsigned extensions is correct");
 
   // The main extensions list should still have the original two
   // good extensions and the legacy banner.
   await catUtils.openType("extension");
   checkList("addon-list",
@@ -181,17 +181,17 @@ add_task(async function() {
   // Disable unsigned extensions
   SpecialPowers.pushPrefEnv({
     set: [
       ["xpinstall.signatures.required", false],
     ],
   });
 
   // The name of the pane should go back to "Legacy Extensions"
-  await mgrWin.gLegacyView.refresh();
+  await mgrWin.gLegacyView.refreshVisibility();
   is(catItem.disabled, false, "Legacy category is visible");
   is(catItem.getAttribute("name"), get_string("type.legacy.name"),
      "Category label with no unsigned extensions is correct");
 
   // The unsigned extension should be present in the main extensions pane
   await catUtils.openType("extension");
   checkList("addon-list", [
     "webextension@tests.mozilla.org",