Bug 1519650 - Port bug 1459556, part 2: Remove the "handler" binding. r=jorgk
authorRichard Marti <richard.marti@gmail.com>
Sat, 12 Jan 2019 20:17:42 +0100
changeset 33323 253562a57fb5
parent 33322 34939e75acc1
child 33324 4f89930a06e5
push id2368
push userclokep@gmail.com
push dateMon, 28 Jan 2019 21:12:50 +0000
treeherdercomm-beta@56d23c07d815 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorgk
bugs1519650, 1459556
Bug 1519650 - Port bug 1459556, part 2: Remove the "handler" binding. r=jorgk
mail/components/preferences/applications.inc.xul
mail/components/preferences/applications.js
mail/components/preferences/handlers.css
mail/components/preferences/handlers.xml
mail/components/preferences/jar.mn
--- a/mail/components/preferences/applications.inc.xul
+++ b/mail/components/preferences/applications.inc.xul
@@ -82,17 +82,17 @@
                      type="search"
                      placeholder="&filter.placeholder;"
                      aria-controls="handlersView"
                      oncommand="gApplicationsPane.rebuildView();"/>
           </hbox>
 
           <separator class="thin"/>
 
-          <listheader equalsize="always" style="border: 0; padding: 0; -moz-appearance: none;">
+          <listheader equalsize="always">
             <treecol id="typeColumn" label="&typeColumn.label;" value="type"
                       accesskey="&typeColumn.accesskey;" persist="sortDirection"
                       flex="1" onclick="gApplicationsPane.sort(event);"
                       sortDirection="ascending" sort="typeDescription"/>
             <treecol id="actionColumn" label="&actionColumn2.label;" value="action"
                       accesskey="&actionColumn2.accesskey;" persist="sortDirection"
                       flex="1" onclick="gApplicationsPane.sort(event);"/>
           </listheader>
--- a/mail/components/preferences/applications.js
+++ b/mail/components/preferences/applications.js
@@ -58,61 +58,96 @@ 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;
 }
 
+// eslint-disable-next-line no-undef
+let gHandlerListItemFragment = MozXULElement.parseXULToFragment(`
+  <richlistitem>
+    <hbox flex="1" equalsize="always">
+      <hbox class="typeContainer" flex="1" align="center">
+        <image class="typeIcon" width="16" height="16"
+               src="moz-icon://goat?size=16"/>
+        <label class="typeDescription" flex="1" crop="end"/>
+      </hbox>
+      <hbox class="actionContainer" flex="1" align="center">
+        <image class="actionIcon" width="16" height="16"/>
+        <label class="actionDescription" flex="1" crop="end"/>
+      </hbox>
+      <hbox class="actionsMenuContainer" flex="1">
+        <menulist class="actionsMenu" flex="1" crop="end" selectedIndex="1">
+          <menupopup/>
+        </menulist>
+      </hbox>
+    </hbox>
+  </richlistitem>
+`);
+
 /**
  * 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");
+    }
+
+  setOrRemoveAttributes(iterable) {
+    for (let [selector, name, value] of iterable) {
+      let node = selector ? this.node.querySelector(selector) : this.node;
+      if (value) {
+        node.setAttribute(name, value);
+      } else {
+        node.removeAttribute(name);
+      }
+    }
+  }
+
+  connectAndAppendToList(list) {
+    list.appendChild(document.importNode(gHandlerListItemFragment, true));
+    this.node = list.lastChild;
     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.node.querySelector(".actionsMenu").addEventListener("command",
+      event => gApplicationsPane.onSelectAction(event.originalTarget));
 
+    let typeDescription = this.handlerInfoWrapper.typeDescription;
+    this.setOrRemoveAttributes([
+      [null, "type", this.handlerInfoWrapper.type],
+      [".typeContainer", "tooltiptext", typeDescription],
+      [".typeDescription", "value", typeDescription],
+      [".typeIcon", "src", this.handlerInfoWrapper.smallIcon],
+    ]);
     this.refreshAction();
+    this.showActionsMenu = false;
   }
 
   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);
-    }
+    let { actionIconClass, actionDescription } = this.handlerInfoWrapper;
+    this.setOrRemoveAttributes([
+      [null, APP_ICON_ATTR_NAME, actionIconClass],
+      [".actionContainer", "tooltiptext", actionDescription],
+      [".actionDescription", "value", actionDescription],
+      [".actionIcon", "src", actionIconClass ? null :
+                             this.handlerInfoWrapper.actionIcon],
+    ]);
   }
 
   set showActionsMenu(value) {
-    document.getAnonymousElementByAttribute(this.node, "anonid", "selected")
-            .setAttribute("hidden", !value);
-    document.getAnonymousElementByAttribute(this.node, "anonid", "not-selected")
-            .setAttribute("hidden", !!value);
+    this.setOrRemoveAttributes([
+      [".actionContainer", "hidden", value],
+      [".actionsMenuContainer", "hidden", !value],
+    ]);
   }
 }
 
 /**
  * This object wraps nsIHandlerInfo with some additional functionality
  * the Applications prefpane needs to display and allow modification of
  * the list of handled types.
  *
@@ -758,17 +793,17 @@ var gCloudFileTab = {
     this.requestUserInfoForItem(item, true);
   },
 
   addCloudFileAccount() {
     let accountKey = cloudFileAccounts.addAccountDialog();
     if (!accountKey)
       return;
 
-    this._rebuildView();
+    this.rebuildView();
     let newItem = this._list.querySelector("richlistitem[value='" + accountKey + "']");
     this._list.selectItem(newItem);
     this._removeAccountButton.disabled = false;
   },
 
   removeCloudFileAccount() {
     // Get the selected account key
     let selection = this._list.selectedItem;
@@ -780,17 +815,17 @@ var gCloudFileTab = {
     // Does the user really want to remove this account?
     let confirmMessage = this._strings
                              .formatStringFromName("dialog_removeAccount",
                                                    [accountName], 1);
 
     if (Services.prompt.confirm(null, "", confirmMessage)) {
       this._list.clearSelection();
       cloudFileAccounts.removeAccount(accountKey);
-      this._rebuildView();
+      this.rebuildView();
       this._settingsDeck.selectedPanel = this._defaultPanel;
       delete this._accountCache[accountKey];
 
       this._removeAccountButton.disabled = (this._list.selectedCount == 0);
     }
   },
 
   handleEvent(aEvent) {
@@ -987,17 +1022,17 @@ var gApplicationsPane = {
     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 = new HandlerListItem(visibleType);
-      this._list.appendChild(item.node);
+      item.connectAndAppendToList(this._list);
 
       if (visibleType.type === lastSelectedType) {
         this._list.selectedItem = item.node;
       }
     }
   },
 
   _matchesFilter(aType) {
@@ -1082,18 +1117,17 @@ var gApplicationsPane = {
    */
   rebuildActionsMenu() {
     var typeItem = this._list.selectedItem;
 
     if (!typeItem)
       return;
 
     var handlerInfo = this.selectedHandlerListItem.handlerInfoWrapper;
-    var menu =
-      document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
+    var menu = typeItem.querySelector(".actionsMenu");
     var menuPopup = menu.menupopup;
 
     // Clear out existing items.
     while (menuPopup.hasChildNodes())
       menuPopup.lastChild.remove();
 
     var askMenuItem = document.createElement("menuitem");
     askMenuItem.setAttribute("alwaysAsk", "true");
@@ -1304,18 +1338,17 @@ var gApplicationsPane = {
   // which spawn "pref changed" notifications that would otherwise cause us
   // to rebuild the view unnecessarily.
   _storingAction: false,
 
   onSelectAction(aActionItem) {
     this._storingAction = true;
 
     let typeItem = this._list.selectedItem;
-    let menu = document.getAnonymousElementByAttribute(typeItem, "class",
-                                                       "actionsMenu");
+    let menu = typeItem.querySelector(".actionsMenu");
     menu.previousSelectedItem = aActionItem;
     try {
       this._storeAction(aActionItem);
     } finally {
       this._storingAction = false;
     }
   },
 
@@ -1380,18 +1413,17 @@ var gApplicationsPane = {
       // Rebuild the actions menu whether the user picked an app or canceled.
       // If they picked an app, we want to add the app to the menu and select it.
       // If they canceled, we want to go back to their previous selection.
       this.rebuildActionsMenu();
 
       // If the user picked a new app from the menu, select it.
       if (handlerApp) {
         let typeItem = this._list.selectedItem;
-        let actionsMenu =
-          document.getAnonymousElementByAttribute(typeItem, "class", "actionsMenu");
+        let actionsMenu = typeItem.querySelector(".actionsMenu");
         let menuItems = actionsMenu.menupopup.childNodes;
         for (let i = 0; i < menuItems.length; i++) {
           let menuItem = menuItems[i];
           if (menuItem.handlerApp && menuItem.handlerApp.equals(handlerApp)) {
             actionsMenu.selectedIndex = i;
             this.onSelectAction(menuItem);
             break;
           }
@@ -1454,18 +1486,18 @@ var gApplicationsPane = {
   confirmDelete(aEvent) {
     aEvent.stopPropagation();
     if (Services.prompt.confirm(null,
                                 this._prefsBundle.getString("confirmDeleteTitle"),
                                 this._prefsBundle.getString("confirmDeleteText"))) {
       this.onDelete(aEvent);
     } else {
       // They hit cancel, so return them to the previously selected item.
-      let menu = document.getAnonymousElementByAttribute(this._list.selectedItem,
-                                                         "class", "actionsMenu");
+      let typeItem = this._list.selectedItem;
+      let menu = typeItem.querySelector(".actionsMenu");
       menu.selectedItem = menu.previousSelectedItem;
     }
   },
 
   onDelete(aEvent) {
     // We want to delete if either the request came from the confirmDelete
     // method (which is the only thing that populates the aEvent parameter),
     // or we've hit the delete/backspace key while the list has focus.
--- a/mail/components/preferences/handlers.css
+++ b/mail/components/preferences/handlers.css
@@ -1,14 +1,12 @@
 /* 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/. */
-#handlersView > richlistitem {
-  -moz-binding: url("chrome://messenger/content/preferences/handlers.xml#handler");
-}
+
 .actionsMenu > menupopup > menuitem > .menu-iconic-left {
   /**
     * Make the icons appear.
     * Note: we display the icon box for every item whether or not it has an icon
     * so the labels of all the items align vertically.
     */
   display: -moz-box;
   min-width: 16px;
deleted file mode 100755
--- a/mail/components/preferences/handlers.xml
+++ /dev/null
@@ -1,37 +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/. -->
-
-<bindings id="handlerBindings"
-          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="handler" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
-    <content>
-      <xul:hbox flex="1" equalsize="always">
-        <xul:hbox flex="1" align="center" xbl:inherits="tooltiptext=typeDescription">
-          <xul:image src="moz-icon://goat?size=16" class="typeIcon"
-                     xbl:inherits="src=typeIcon" height="16" width="16"/>
-          <xul:label flex="1" class="shortDescription"
-                     xbl:inherits="value=shortTypeDescription"/>
-          <xul:label flex="1" crop="end" class="shortDetails"
-                     xbl:inherits="value=shortTypeDetails"/>
-        </xul:hbox>
-        <xul:hbox anonid="not-selected" flex="1" align="center" xbl:inherits="tooltiptext=actionDescription">
-          <xul:image xbl:inherits="src=actionIcon" height="16" width="16" class="actionIcon"/>
-          <xul:label flex="1" crop="end" xbl:inherits="value=actionDescription"/>
-        </xul:hbox>
-        <xul:hbox hidden="true" anonid="selected" flex="1">
-          <xul:menulist class="actionsMenu" flex="1" crop="end" selectedIndex="1"
-                        xbl:inherits="tooltiptext=actionDescription"
-                        oncommand="gApplicationsPane.onSelectAction(event.originalTarget)">
-            <xul:menupopup/>
-          </xul:menulist>
-        </xul:hbox>
-      </xul:hbox>
-    </content>
-
-  </binding>
-</bindings>
--- a/mail/components/preferences/jar.mn
+++ b/mail/components/preferences/jar.mn
@@ -30,17 +30,16 @@ messenger.jar:
     content/messenger/preferences/connection.js
 *   content/messenger/preferences/connection.xul
     content/messenger/preferences/downloads.js
     content/messenger/preferences/attachmentReminder.js
     content/messenger/preferences/attachmentReminder.xul
     content/messenger/preferences/applications.js
     content/messenger/preferences/applicationManager.xul
     content/messenger/preferences/applicationManager.js
-    content/messenger/preferences/handlers.xml
     content/messenger/preferences/handlers.css
     content/messenger/preferences/actionsshared.js
     content/messenger/preferences/fonts.js
     content/messenger/preferences/fonts.xul
 #ifndef XP_MACOSX
     content/messenger/preferences/notifications.xul
 #endif
     content/messenger/preferences/offline.js