Bug 1457027 - Part 8 - Add a HandlerListItem class. r=jaws
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Fri, 04 May 2018 15:57:43 +0100
changeset 792268 e9d78f354b26948a973ccedf28bd2c1682a46cbe
parent 792267 b29b502c0fcefcc98def52f610b6f5641786e78d
child 792269 344ff948dc28e761cc92dc3f6da6a4d23d3a5b1e
push id109056
push userhikezoe@mozilla.com
push dateMon, 07 May 2018 23:04:50 +0000
reviewersjaws
bugs1457027
milestone61.0a1
Bug 1457027 - Part 8 - Add a HandlerListItem class. r=jaws MozReview-Commit-ID: 2ohtRX1w0D3
browser/components/preferences/in-content/main.js
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -239,16 +239,18 @@ if (AppConstants.MOZ_UPDATER) {
     ]);
   }
 }
 
 // A promise that resolves when the list of application handlers is loaded.
 // We store this in a global so tests can await it.
 var promiseLoadHandlersList;
 
+var gNodeToObjectMap = new WeakMap();
+
 var gMainPane = {
   // The set of types the app knows how to handle.  A hash of HandlerInfoWrapper
   // objects, indexed by type.
   _handledTypes: {},
 
   // The list of types we can show, sorted by the sort column/direction.
   // An array of HandlerInfoWrapper objects.  We build this list when we first
   // load the data and then rebuild it when users change a pref that affects
@@ -1500,44 +1502,34 @@ var gMainPane = {
         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.removeChild(this._list.lastChild);
 
     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);
-      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);
@@ -1945,40 +1937,34 @@ var gMainPane = {
 
     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 onComplete = () => {
       // 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://browser/content/preferences/applicationManager.xul",
       "resizable=no", handlerInfo, onComplete);
 
   },
 
   chooseApp(aEvent) {
@@ -2062,31 +2048,16 @@ var gMainPane = {
       // Prompt the user to pick an app.  If they pick one, and it's a valid
       // selection, then add it to the list of possible handlers.
       fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
       fp.appendFilters(Ci.nsIFilePicker.filterApps);
       fp.open(fpCallback);
     }
   },
 
-  _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)
@@ -2451,17 +2422,54 @@ ArrayEnumerator.prototype = {
     return this._contents[this._index++];
   }
 };
 
 function isFeedType(t) {
   return t == TYPE_MAYBE_FEED || t == TYPE_MAYBE_VIDEO_FEED || t == TYPE_MAYBE_AUDIO_FEED;
 }
 
-// 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("typeDescription",
+                           this.handlerInfoWrapper.typeDescription);
+    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,