Bug 1460392 - Port bug 1457027 to TB: Part 8 - Add a HandlerListItem class. r=aceman
authorRichard Marti <richard.marti@gmail.com>
Fri, 28 Sep 2018 08:54:42 +0200
changeset 33267 2131ecd0173c88f2a080af5c8ce46ecbb36dd272
parent 33266 eca38904a853f703807ce93917e6704492f476e8
child 33268 d945c1cb9d917bfaa6d4d8bfacecde30889567cc
push id387
push userclokep@gmail.com
push dateMon, 10 Dec 2018 21:30:47 +0000
reviewersaceman
bugs1460392, 1457027
Bug 1460392 - Port bug 1457027 to TB: Part 8 - Add a HandlerListItem class. r=aceman
mail/components/preferences/applications.js
--- a/mail/components/preferences/applications.js
+++ b/mail/components/preferences/applications.js
@@ -25,16 +25,18 @@ var PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS
 // But since nsIHandlerInfo doesn't support plugins, there's no value
 // identifying the "use plugin" action, so we use this constant instead.
 var kActionUsePlugin = 5;
 
 // For CSS. Can be one of "ask", "save", "plugin" or "feed". If absent, the icon URL
 // was set by us to a custom handler icon and CSS should not try to override it.
 var APP_ICON_ATTR_NAME = "appHandlerIcon";
 
+var gNodeToObjectMap = new WeakMap();
+
 // CloudFile account tools used by gCloudFileTab.
 ChromeUtils.import("resource:///modules/cloudFileAccounts.js");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   gCategoryManager: ["@mozilla.org/categorymanager;1", "nsICategoryManager"],
@@ -72,18 +74,56 @@ function getLocalHandlerApp(aFile) {
   var localHandlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]
                           .createInstance(Ci.nsILocalHandlerApp);
   localHandlerApp.name = getDisplayNameForFile(aFile);
   localHandlerApp.executable = aFile;
 
   return localHandlerApp;
 }
 
-// ------------------
-// HandlerInfoWrapper
+/**
+ * This is associated to <richlistitem> elements in the handlers view.
+ */
+class HandlerListItem {
+  static forNode(node) {
+    return gNodeToObjectMap.get(node);
+  }
+
+  constructor(handlerInfoWrapper) {
+    this.handlerInfoWrapper = handlerInfoWrapper;
+    this.node = document.createElement("richlistitem");
+    gNodeToObjectMap.set(this.node, this);
+
+    this.node.setAttribute("type", this.handlerInfoWrapper.type);
+    this.node.setAttribute("shortTypeDescription",
+                           this.handlerInfoWrapper.typeDescription);
+    this.node.setAttribute("shortTypeDetails",
+                           gApplicationsPane._typeDetails(this.handlerInfoWrapper));
+    if (this.handlerInfoWrapper.smallIcon) {
+      this.node.setAttribute("typeIcon", this.handlerInfoWrapper.smallIcon);
+    } else {
+      this.node.removeAttribute("typeIcon");
+    }
+
+    this.refreshAction();
+  }
+
+  refreshAction() {
+    this.node.setAttribute("actionDescription",
+                           this.handlerInfoWrapper.actionDescription);
+    if (this.handlerInfoWrapper.actionIconClass) {
+      this.node.setAttribute(APP_ICON_ATTR_NAME,
+                             this.handlerInfoWrapper.actionIconClass);
+      this.node.removeAttribute("actionIcon");
+    } else {
+      this.node.removeAttribute(APP_ICON_ATTR_NAME);
+      this.node.setAttribute("actionIcon", this.handlerInfoWrapper.actionIcon);
+    }
+  }
+}
 
 /**
  * This object wraps nsIHandlerInfo with some additional functionality
  * the Applications prefpane needs to display and allow modification of
  * the list of handled types.
  *
  * We create an instance of this wrapper for each entry we might display
  * in the prefpane, and we compose the instances from various sources,
@@ -1138,45 +1178,34 @@ var gApplicationsPane = {
         handlerInfo.disambiguateDescription = true;
         otherHandlerInfo.disambiguateDescription = true;
       }
     }
   },
 
   rebuildView() {
     let lastSelectedType = this._list.selectedItem &&
-                           this._list.selectedItem.getAttribute("type");
+                           HandlerListItem.forNode(this._list.selectedItem)
+                           .handlerInfoWrapper.type;
 
     // Clear the list of entries.
     while (this._list.childNodes.length > 1)
       this._list.lastChild.remove();
     var visibleTypes = this._visibleTypes;
 
     // If the user is filtering the list, then only show matching types.
     if (this._filter.value)
       visibleTypes = visibleTypes.filter(this._matchesFilter, this);
 
     for (let visibleType of visibleTypes) {
-      let item = document.createElement("richlistitem");
-      item.setAttribute("type", visibleType.type);
-      item.setAttribute("typeDescription", visibleType.typeDescription);
-      item.setAttribute("shortTypeDescription", visibleType.description);
-      item.setAttribute("shortTypeDetails", this._typeDetails(visibleType));
-      if (visibleType.smallIcon)
-        item.setAttribute("typeIcon", visibleType.smallIcon);
-      item.setAttribute("actionDescription", visibleType.actionDescription);
-
-      if (!this._setIconClassForPreferredAction(visibleType, item)) {
-        item.setAttribute("actionIcon", visibleType.actionIcon);
-      }
-
-      this._list.appendChild(item);
+      let item = new HandlerListItem(visibleType);
+      this._list.appendChild(item.node);
 
       if (visibleType.type === lastSelectedType) {
-        this._list.selectedItem = item;
+        this._list.selectedItem = item.node;
       }
     }
   },
 
   _matchesFilter(aType) {
     var filterValue = this._filter.value.toLowerCase();
     return aType.typeDescription.toLowerCase().includes(filterValue) ||
            aType.actionDescription.toLowerCase().includes(filterValue);
@@ -1542,40 +1571,34 @@ var gApplicationsPane = {
 
     handlerInfo.store();
 
     // Make sure the handler info object is flagged to indicate that there is
     // now some user configuration for the type.
     handlerInfo.handledOnlyByPlugin = false;
 
     // Update the action label and image to reflect the new preferred action.
-    typeItem.setAttribute("actionDescription", handlerInfo.actionDescription);
-    if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
-      typeItem.setAttribute("actionIcon", handlerInfo.actionIcon);
-    }
+    HandlerListItem.forNode(typeItem).refreshAction();
   },
 
   manageApp(aEvent) {
     // Don't let the normal "on select action" handler get this event,
     // as we handle it specially ourselves.
     aEvent.stopPropagation();
 
     var typeItem = this._list.selectedItem;
     var handlerInfo = this._handledTypes[typeItem.type];
 
     let closingCallback = () => {
       // Rebuild the actions menu so that we revert to the previous selection,
       // or "Always ask" if the previous default application has been removed.
       this.rebuildActionsMenu();
 
       // Update the richlistitem too. Will be visible when selecting another row.
-      typeItem.setAttribute("actionDescription", handlerInfo.actionDescription);
-      if (!this._setIconClassForPreferredAction(handlerInfo, typeItem)) {
-        typeItem.setAttribute("actionIcon", handlerInfo.actionIcon);
-      }
+      HandlerListItem.forNode(typeItem).refreshAction();
     };
 
     gSubDialog.open(
       "chrome://messenger/content/preferences/applicationManager.xul",
       "resizable=no", handlerInfo, closingCallback);
   },
 
   chooseApp(aEvent) {
@@ -1685,31 +1708,16 @@ var gApplicationsPane = {
       if (index != -1)
         this._visibleTypes.splice(index, 1);
       handlerInfo.remove();
       delete this._handledTypes[typeItem.type];
       typeItem.remove();
     }
   },
 
-  _setIconClassForPreferredAction(aHandlerInfo, aElement) {
-    // If this returns true, the attribute that CSS sniffs for was set to something
-    // so you shouldn't manually set an icon URI.
-    // This removes the existing actionIcon attribute if any, even if returning false.
-    aElement.removeAttribute("actionIcon");
-
-    if (aHandlerInfo.actionIconClass) {
-      aElement.setAttribute(APP_ICON_ATTR_NAME, aHandlerInfo.actionIconClass);
-      return true;
-    }
-
-    aElement.removeAttribute(APP_ICON_ATTR_NAME);
-    return false;
-  },
-
   _getIconURLForHandlerApp(aHandlerApp) {
     if (aHandlerApp instanceof Ci.nsILocalHandlerApp)
       return this._getIconURLForFile(aHandlerApp.executable);
 
     if (aHandlerApp instanceof Ci.nsIWebHandlerApp)
       return this._getIconURLForWebApp(aHandlerApp.uriTemplate);
 
     if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo)