Bug 1546301 - [de-xbl] convert the ruleactiontype-menulist binding to <menulist is='ruleactiontype-menulist'>. r=mkmelin DONTBUILD
authorKhushil Mistry <khushil324@gmail.com>
Fri, 21 Jun 2019 00:12:00 +0200
changeset 35914 e9bc2e1580a08c373d4e5cc40802ccd3c71d06c1
parent 35913 0ad5f1f559f7e8ca396e208db772a41f16a34ea6
child 35915 705abdff6892967ed4532a5cddb358fc3da9e4d0
push id392
push userclokep@gmail.com
push dateMon, 02 Sep 2019 20:17:19 +0000
reviewersmkmelin
bugs1546301
Bug 1546301 - [de-xbl] convert the ruleactiontype-menulist binding to <menulist is='ruleactiontype-menulist'>. r=mkmelin DONTBUILD
mail/base/content/messenger.css
mailnews/base/search/content/FilterEditor.js
mailnews/base/search/content/searchWidgets.js
mailnews/base/search/content/searchWidgets.xml
--- a/mail/base/content/messenger.css
+++ b/mail/base/content/messenger.css
@@ -45,20 +45,16 @@ mail-tagfield[collapsed="true"] {
 search-value {
   display: -moz-deck;
 }
 
 .ruleaction {
   -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleaction");
 }
 
-.ruleactiontype {
-  -moz-binding: url("chrome://messenger/content/searchWidgets.xml#ruleactiontype-menulist");
-}
-
 #searchInput {
   -moz-binding: url("chrome://messenger/content/search.xml#glodaSearch");
 }
 
 .remote-gloda-search {
   -moz-binding: url("chrome://messenger/content/search.xml#glodaSearch") !important;
 }
 
--- a/mailnews/base/search/content/FilterEditor.js
+++ b/mailnews/base/search/content/FilterEditor.js
@@ -705,10 +705,10 @@ function getFilterScope(aServerFilterSco
  */
 function setLastActionFocus() {
   let lastAction = gFilterActionList.getAttribute("focusedAction");
   if (!lastAction || lastAction < 0)
     lastAction = 0;
   if (lastAction >= gFilterActionList.itemCount)
     lastAction = gFilterActionList.itemCount - 1;
 
-  gFilterActionList.getItemAtIndex(lastAction).mRuleActionType.menulist.focus();
+  gFilterActionList.getItemAtIndex(lastAction).mRuleActionType.focus();
 }
--- a/mailnews/base/search/content/searchWidgets.js
+++ b/mailnews/base/search/content/searchWidgets.js
@@ -1,20 +1,21 @@
 /**
  * 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/. */
 
-/* global MozXULElement, gFilter, gFilterList, onEnterInSearchTerm, convertDateToString,
-   convertPRTimeToString, convertStringToPRTime, UpdateAfterCustomHeaderChange,
-   initializeTermFromId */
+/* global MozXULElement, gFilter, gFilterList, onEnterInSearchTerm, convertDateToString, initializeTermFromId,
+   convertPRTimeToString, convertStringToPRTime, UpdateAfterCustomHeaderChange, checkActionsReorder,
+   initializeTermFrom, IdcheckActionsReorder, getScopeFromFilterList, gCustomActions, gFilterType */
 
-var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var {MailServices} = ChromeUtils.import("resource:///modules/MailServices.jsm");
-var {MailUtils} = ChromeUtils.import("resource:///modules/MailUtils.jsm");
+var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
+var { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.jsm");
+var { fixIterator } = ChromeUtils.import("resource:///modules/iteratorUtils.jsm");
 
 const updateParentNode = (parentNode) => {
   if (parentNode.hasAttribute("initialActionIndex")) {
     let actionIndex = parentNode.getAttribute("initialActionIndex");
     let filterAction = gFilter.getActionAt(actionIndex);
     parentNode.initWithAction(filterAction);
   }
   parentNode.updateRemoveButton();
@@ -85,18 +86,18 @@ class MozRuleactiontargetReplyto extends
     const menuPopup = document.createXULElement("menupopup");
 
     menulist.classList.add("ruleactionitem");
     menulist.setAttribute("flex", "1");
     menulist.appendChild(menuPopup);
 
     this.appendChild(menulist);
 
-    document.getAnonymousElementByAttribute(this.closest(".ruleaction"), "class", "ruleactiontype")
-            .getTemplates(true, menulist);
+    document.getAnonymousElementByAttribute(this.closest(".ruleaction"), "is", "ruleactiontype-menulist")
+      .getTemplates(true, menulist);
 
     updateParentNode(this.closest(".ruleaction"));
   }
 }
 
 
 class MozRuleactiontargetForwardto extends MozXULElement {
   connectedCallback() {
@@ -623,17 +624,17 @@ class MozSearchValue extends MozXULEleme
 
     this.internalOperator = null;
     this.internalAttribute = null;
     this.internalValue = null;
   }
 
   connectedCallback() {
     if (this.delayConnectedCallback()) {
-        return;
+      return;
     }
 
     // Initialize strings.
     const bundle = Services.strings.createBundle("chrome://messenger/locale/messenger.properties");
 
     if (!this.hasChildNodes()) {
       this.appendChild(MozXULElement.parseXULToFragment(`
         <textbox flex="1" class="search-value-textbox" inherits="disabled"></textbox>
@@ -1022,8 +1023,233 @@ class MozSearchValue extends MozXULEleme
       }
     }
 
     // Force initialization of the menulist custom element.
     customElements.upgrade(parentNode);
   }
 }
 customElements.define("search-value", MozSearchValue);
+
+customElements.whenDefined("menulist").then(() => {
+  /**
+   * The MozRuleactiontypeMenulist is a widget that allows selecting the actions from the given menulist for
+   * the selected folder. It gets displayed in the message filter dialog box.
+   *
+   * @extends {MozMenuList}
+   */
+  class MozRuleactiontypeMenulist extends customElements.get("menulist") {
+    connectedCallback() {
+      super.connectedCallback();
+      if (this.delayConnectedCallback() || this.hasConnected) {
+        return;
+      }
+      this.hasConnected = true;
+
+      this.setAttribute("is", "ruleactiontype-menulist");
+      this.addEventListener("command", (event) => {
+        this.parentNode.setAttribute("value", this.value);
+        checkActionsReorder();
+      });
+
+      this.addEventListener("popupshowing", (event) => {
+        let unavailableActions = this.usedActionsList();
+        for (let index = 0; index < this.menuitems.length; index++) {
+          let menu = this.menuitems[index];
+          menu.setAttribute("disabled", menu.value in unavailableActions);
+        }
+      });
+
+      this.menuitems = this.getElementsByTagNameNS(this.namespaceURI, "menuitem");
+
+      // Force initialization of the menulist custom element first.
+      customElements.upgrade(this);
+      this.addCustomActions();
+      this.hideInvalidActions();
+      // Differentiate between creating a new, next available action,
+      // and creating a row which will be initialized with an action.
+      if (!this.parentNode.hasAttribute("initialActionIndex")) {
+        let unavailableActions = this.usedActionsList();
+        // Select the first one that's not in the list.
+        for (let index = 0; index < this.menuitems.length; index++) {
+          let menu = this.menuitems[index];
+          if (!(menu.value in unavailableActions) && !menu.hidden) {
+            this.value = menu.value;
+            this.parentNode.setAttribute("value", menu.value);
+            break;
+          }
+        }
+      } else {
+        this.parentNode.mActionTypeInitialized = true;
+        this.parentNode.clearInitialActionIndex();
+      }
+    }
+
+    hideInvalidActions() {
+      let menupopup = this.menupopup;
+      let scope = getScopeFromFilterList(gFilterList);
+
+      // Walk through the list of filter actions and hide any actions which aren't valid
+      // for our given scope (news, imap, pop, etc) and context.
+      let elements;
+
+      // Disable / enable all elements in the "filteractionlist"
+      // based on the scope and the "enablefornews" attribute.
+      elements = menupopup.getElementsByAttribute("enablefornews", "true");
+      for (let i = 0; i < elements.length; i++) {
+        elements[i].hidden = scope != Ci.nsMsgSearchScope.newsFilter;
+      }
+
+      elements = menupopup.getElementsByAttribute("enablefornews", "false");
+      for (let i = 0; i < elements.length; i++) {
+        elements[i].hidden = scope == Ci.nsMsgSearchScope.newsFilter;
+      }
+
+      elements = menupopup.getElementsByAttribute("enableforpop3", "true");
+      for (let i = 0; i < elements.length; i++) {
+        elements[i].hidden = !((gFilterList.folder.server.type == "pop3") ||
+          (gFilterList.folder.server.type == "none"));
+      }
+
+      elements = menupopup.getElementsByAttribute("isCustom", "true");
+      // Note there might be an additional element here as a placeholder
+      // for a missing action, so we iterate over the known actions
+      // instead of the elements.
+      for (let i = 0; i < gCustomActions.length; i++) {
+        elements[i].hidden = !gCustomActions[i]
+          .isValidForType(gFilterType, scope);
+      }
+
+      // Disable "Reply with Template" if there are no templates.
+      if (!this.getTemplates(false)) {
+        elements = menupopup.getElementsByAttribute("value", "replytomessage");
+        if (elements.length == 1) {
+          elements[0].hidden = true;
+        }
+      }
+    }
+
+    addCustomActions() {
+      var menupopup = this.menupopup;
+      for (let i = 0; i < gCustomActions.length; i++) {
+        let customAction = gCustomActions[i];
+        let menuitem = document.createXULElement("menuitem");
+        menuitem.setAttribute("label", customAction.name);
+        menuitem.setAttribute("value", customAction.id);
+        menuitem.setAttribute("isCustom", "true");
+        menupopup.appendChild(menuitem);
+      }
+    }
+
+    /**
+     * Returns a hash containing all of the filter actions which are currently
+     * being used by other filteractionrows.
+     *
+     * @return {Object} - a hash containing all of the filter actions which are
+     *                    currently being used by other filteractionrows.
+     */
+    usedActionsList() {
+      let usedActions = {};
+      let currentFilterActionRow = this.parentNode;
+      let listBox = currentFilterActionRow.mListBox; // need to account for the list item.
+      // Now iterate over each list item in the list box.
+      for (let index = 0; index < listBox.getRowCount(); index++) {
+        let filterActionRow = listBox.getItemAtIndex(index);
+        if (filterActionRow != currentFilterActionRow) {
+          let actionValue = filterActionRow.getAttribute("value");
+
+          // Let custom actions decide if dups are allowed.
+          let isCustom = false;
+          for (let i = 0; i < gCustomActions.length; i++) {
+            if (gCustomActions[i].id == actionValue) {
+              isCustom = true;
+              if (!gCustomActions[i].allowDuplicates) {
+                usedActions[actionValue] = true;
+              }
+              break;
+            }
+          }
+
+          if (!isCustom) {
+            // The following actions can appear more than once in a single filter
+            // so do not set them as already used.
+            if (actionValue != "addtagtomessage" &&
+              actionValue != "forwardmessage" &&
+              actionValue != "copymessage") {
+              usedActions[actionValue] = true;
+            }
+            // If either Delete message or Move message exists, disable the other one.
+            // It does not make sense to apply both to the same message.
+            if (actionValue == "deletemessage") {
+              usedActions.movemessage = true;
+            } else if (actionValue == "movemessage") {
+              usedActions.deletemessage = true;
+            } else if (actionValue == "markasread") {
+              // The same with Mark as read/Mark as Unread.
+              usedActions.markasunread = true;
+            } else if (actionValue == "markasunread") {
+              usedActions.markasread = true;
+            }
+          }
+        }
+      }
+      return usedActions;
+    }
+
+    /**
+     * Check if there exist any templates in this account.
+     *
+     * @param populateTemplateList  If true, create menuitems representing
+     *                              the found templates.
+     * @param templateMenuList      The menulist element to create items in.
+     *
+     * @return {boolean}           True if at least one template was found,
+     *                              otherwise false.
+     */
+    getTemplates(populateTemplateList, templateMenuList) {
+      let identitiesRaw = MailServices.accounts
+        .getIdentitiesForServer(gFilterList.folder.server);
+      let identities = Array.from(fixIterator(identitiesRaw,
+        Ci.nsIMsgIdentity));
+      // Typically if this is Local Folders.
+      if (identities.length == 0) {
+        if (MailServices.accounts.defaultAccount) {
+          identities.push(MailServices.accounts.defaultAccount.defaultIdentity);
+        }
+      }
+
+      let templateFound = false;
+      let foldersScanned = [];
+
+      for (let identity of identities) {
+        let enumerator = null;
+        let msgFolder = MailUtils.getExistingFolder(identity.stationeryFolder);
+        // If we already processed this folder, do not set enumerator
+        // so that we skip this identity.
+        if (msgFolder && !foldersScanned.includes(msgFolder)) {
+          foldersScanned.push(msgFolder);
+          enumerator = msgFolder.msgDatabase.EnumerateMessages();
+        }
+
+        if (!enumerator) {
+          continue;
+        }
+
+        while (enumerator.hasMoreElements()) {
+          let header = enumerator.getNext();
+          if (header instanceof Ci.nsIMsgDBHdr) {
+            templateFound = true;
+            if (!populateTemplateList) {
+              return true;
+            }
+            let msgTemplateUri = msgFolder.URI + "?messageId=" +
+              header.messageId + "&subject=" + header.mime2DecodedSubject;
+            templateMenuList.appendItem(header.mime2DecodedSubject, msgTemplateUri);
+          }
+        }
+      }
+      return templateFound;
+    }
+  }
+
+  customElements.define("ruleactiontype-menulist", MozRuleactiontypeMenulist,
+    { extends: "menulist" });
+});
--- a/mailnews/base/search/content/searchWidgets.xml
+++ b/mailnews/base/search/content/searchWidgets.xml
@@ -22,271 +22,16 @@
 %messengerDTD;
 ]>
 
 <bindings id="filterBindings"
           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="ruleactiontype-menulist">
-    <content>
-      <xul:menulist class="ruleaction-type">
-          <xul:menupopup>
-            <xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
-            <xul:menuitem label="&copyMessage.label;" value="copymessage"/>
-            <xul:menuseparator enablefornews="false"/>
-            <xul:menuitem label="&forwardTo.label;" value="forwardmessage" enablefornews="false"/>
-            <xul:menuitem label="&replyWithTemplate.label;" value="replytomessage" enablefornews="false"/>
-            <xul:menuseparator/>
-            <xul:menuitem label="&markMessageRead.label;" value="markasread"/>
-            <xul:menuitem label="&markMessageUnread.label;" value="markasunread"/>
-            <xul:menuitem label="&markMessageStarred.label;" value="markasflagged"/>
-            <xul:menuitem label="&setPriority.label;"  value="setpriorityto"/>
-            <xul:menuitem label="&addTag.label;"  value="addtagtomessage"/>
-            <xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
-            <xul:menuseparator enableforpop3="true"/>
-            <xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
-            <xul:menuitem label="&deleteFromPOP.label;" value="deletefrompopserver" enableforpop3="true"/>
-            <xul:menuitem label="&fetchFromPOP.label;"  value="fetchfrompopserver" enableforpop3="true"/>
-            <xul:menuseparator/>
-            <xul:menuitem label="&ignoreThread.label;" value="ignorethread"/>
-            <xul:menuitem label="&ignoreSubthread.label;" value="ignoresubthread"/>
-            <xul:menuitem label="&watchThread.label;" value="watchthread"/>
-            <xul:menuseparator/>
-            <xul:menuitem label="&stopExecution.label;" value="stopexecution"/>
-          </xul:menupopup>
-      </xul:menulist>
-    </content>
-
-    <implementation>
-      <constructor>
-        <![CDATA[
-          // Force initialization of the menulist custom element first.
-          customElements.upgrade(this.menulist);
-          this.addCustomActions();
-          this.hideInvalidActions();
-          // differentiate between creating a new, next available action,
-          // and creating a row which will be initialized with an action
-          if (!this.parentNode.hasAttribute("initialActionIndex")) {
-            var unavailableActions = this.usedActionsList();
-            // select the first one that's not in the list
-            for (let index = 0; index < this.menuitems.length; index++) {
-              var menu = this.menuitems[index];
-              if (!(menu.value in unavailableActions) && !menu.hidden) {
-                this.menulist.value = menu.value;
-                this.parentNode.setAttribute("value", menu.value);
-                break;
-              }
-            }
-          } else {
-            this.parentNode.mActionTypeInitialized = true;
-            this.parentNode.clearInitialActionIndex();
-          }
-        ]]>
-      </constructor>
-
-      <field name="menulist">document.getAnonymousNodes(this)[0]</field>
-      <field name="menuitems"><![CDATA[
-        this.menulist.getElementsByTagNameNS(this.menulist.namespaceURI, "menuitem")
-      ]]></field>
-
-      <method name="hideInvalidActions">
-        <body>
-          <![CDATA[
-            let menupopup = this.menulist.menupopup;
-            let scope = getScopeFromFilterList(gFilterList);
-
-            // walk through the list of filter actions and hide any actions which aren't valid
-            // for our given scope (news, imap, pop, etc) and context
-            let elements, i;
-
-            // disable / enable all elements in the "filteractionlist"
-            // based on the scope and the "enablefornews" attribute
-            elements = menupopup.getElementsByAttribute("enablefornews", "true");
-            for (i = 0; i < elements.length; i++)
-              elements[i].hidden = scope != Ci.nsMsgSearchScope.newsFilter;
-
-            elements = menupopup.getElementsByAttribute("enablefornews", "false");
-            for (i = 0; i < elements.length; i++)
-              elements[i].hidden = scope == Ci.nsMsgSearchScope.newsFilter;
-
-            elements = menupopup.getElementsByAttribute("enableforpop3", "true");
-            for (i = 0; i < elements.length; i++)
-              elements[i].hidden = !((gFilterList.folder.server.type == "pop3") ||
-                                     (gFilterList.folder.server.type == "none"));
-
-            elements = menupopup.getElementsByAttribute("isCustom", "true");
-            // Note there might be an additional element here as a placeholder
-            // for a missing action, so we iterate over the known actions
-            // instead of the elements.
-            for (i = 0; i < gCustomActions.length; i++)
-              elements[i].hidden = !gCustomActions[i]
-                                     .isValidForType(gFilterType, scope);
-
-            // Disable "Reply with Template" if there are no templates.
-            if (!this.getTemplates(false)) {
-              elements = menupopup.getElementsByAttribute("value", "replytomessage");
-              if (elements.length == 1)
-                elements[0].hidden = true;
-            }
-          ]]>
-        </body>
-      </method>
-
-      <method name="addCustomActions">
-        <body>
-          <![CDATA[
-            var menupopup = this.menulist.menupopup;
-            for (let i = 0; i < gCustomActions.length; i++) {
-              var customAction = gCustomActions[i];
-              var menuitem = document.createElementNS(
-                "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                "xul:menuitem");
-              menuitem.setAttribute("label", customAction.name);
-              menuitem.setAttribute("value", customAction.id);
-              menuitem.setAttribute("isCustom", "true");
-              menupopup.appendChild(menuitem);
-            }
-          ]]>
-        </body>
-      </method>
-
-      <!-- returns a hash containing all of the filter actions which are currently being used by other filteractionrows -->
-      <method name="usedActionsList">
-        <body>
-          <![CDATA[
-            var usedActions = {};
-            var currentFilterActionRow = this.parentNode;
-            var listBox = currentFilterActionRow.mListBox; // need to account for the list item
-            // now iterate over each list item in the list box
-            for (let index = 0; index < listBox.getRowCount(); index++) {
-              var filterActionRow = listBox.getItemAtIndex(index);
-              if (filterActionRow != currentFilterActionRow) {
-                let actionValue = filterActionRow.getAttribute("value");
-
-                // let custom actions decide if dups are allowed
-                var isCustom = false;
-                for (let i = 0; i < gCustomActions.length; i++) {
-                  if (gCustomActions[i].id == actionValue) {
-                    isCustom = true;
-                    if (!gCustomActions[i].allowDuplicates)
-                      usedActions[actionValue] = true;
-                    break;
-                  }
-                }
-
-                if (!isCustom) {
-                  // The following actions can appear more than once in a single filter
-                  // so do not set them as already used.
-                  if (actionValue != "addtagtomessage" &&
-                      actionValue != "forwardmessage" &&
-                      actionValue != "copymessage")
-                    usedActions[actionValue] = true;
-                  // If either Delete message or Move message exists, disable the other one.
-                  // It does not make sense to apply both to the same message.
-                  if (actionValue == "deletemessage")
-                    usedActions.movemessage = true;
-                  else if (actionValue == "movemessage")
-                    usedActions.deletemessage = true;
-                  // The same with Mark as read/Mark as Unread.
-                  else if (actionValue == "markasread")
-                    usedActions.markasunread = true;
-                  else if (actionValue == "markasunread")
-                    usedActions.markasread = true;
-                }
-              }
-            }
-            return usedActions;
-          ]]>
-        </body>
-      </method>
-
-      <!--
-        - Check if there exist any templates in this account.
-        -
-        - @param populateTemplateList  If true, create menuitems representing
-        -                              the found templates.
-        - @param templateMenuList      The menulist element to create items in.
-        -
-        - @return  True if at least one template was found, otherwise false.
-      -->
-      <method name="getTemplates">
-        <parameter name="populateTemplateList"/>
-        <parameter name="templateMenuList"/>
-        <body>
-          <![CDATA[
-            ChromeUtils.import("resource:///modules/iteratorUtils.jsm", this);
-            ChromeUtils.import("resource:///modules/MailUtils.jsm", this);
-            let identitiesRaw = MailServices.accounts
-              .getIdentitiesForServer(gFilterList.folder.server);
-            let identities = Array.from(this.fixIterator(identitiesRaw,
-                                                         Ci.nsIMsgIdentity));
-
-            if (identities.length == 0) { // typically if this is Local Folders
-              if (MailServices.accounts.defaultAccount)
-                identities.push(MailServices.accounts.defaultAccount.defaultIdentity);
-            }
-
-            let templateFound = false;
-            let foldersScanned = [];
-
-            for (let identity of identities) {
-              let enumerator = null;
-              let msgFolder = this.MailUtils.getExistingFolder(identity.stationeryFolder);
-              // If we already processed this folder, do not set enumerator
-              // so that we skip this identity.
-              if (msgFolder && !foldersScanned.includes(msgFolder)) {
-                foldersScanned.push(msgFolder);
-                enumerator = msgFolder.msgDatabase.EnumerateMessages();
-              }
-
-              if (!enumerator)
-                continue;
-
-              while (enumerator.hasMoreElements()) {
-                let header = enumerator.getNext();
-                if (header instanceof Ci.nsIMsgDBHdr) {
-                  templateFound = true;
-                  if (!populateTemplateList)
-                    return true;
-                  let msgTemplateUri = msgFolder.URI + "?messageId=" +
-                    header.messageId + "&subject=" + header.mime2DecodedSubject;
-                  templateMenuList.appendItem(header.mime2DecodedSubject, msgTemplateUri);
-                }
-              }
-            }
-
-            return templateFound;
-          ]]>
-        </body>
-      </method>
-
-    </implementation>
-
-    <handlers>
-      <handler event="command">
-        <![CDATA[
-          this.parentNode.setAttribute("value", this.menulist.value);
-          checkActionsReorder();
-        ]]>
-      </handler>
-
-      <handler event="popupshowing">
-        <![CDATA[
-          var unavailableActions = this.usedActionsList();
-          for (let index = 0; index < this.menuitems.length; index++) {
-            var menu = this.menuitems[index];
-            menu.setAttribute("disabled", menu.value in unavailableActions);
-          }
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
-
   <!-- This binding exists to disable the default binding of a richlistitem
        in the search terms. -->
   <binding id="filterlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
     <implementation>
       <property name="selected" onget="return false;">
         <setter>
           <![CDATA[
             /* This provides a dummy selected property that
@@ -306,18 +51,42 @@
           ]]>
         </body>
       </method>
     </implementation>
   </binding>
 
   <binding id="ruleaction" extends="#filterlistitem">
     <content allowevents="true">
-      <xul:hbox class="ruleactiontype"
-                    flex="&filterActionTypeFlexValue;"/>
+      <xul:menulist is="ruleactiontype-menulist" flex="&filterActionTypeFlexValue;">
+        <xul:menupopup>
+          <xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
+          <xul:menuitem label="&copyMessage.label;" value="copymessage"/>
+          <xul:menuseparator enablefornews="false"/>
+          <xul:menuitem label="&forwardTo.label;" value="forwardmessage" enablefornews="false"/>
+          <xul:menuitem label="&replyWithTemplate.label;" value="replytomessage" enablefornews="false"/>
+          <xul:menuseparator/>
+          <xul:menuitem label="&markMessageRead.label;" value="markasread"/>
+          <xul:menuitem label="&markMessageUnread.label;" value="markasunread"/>
+          <xul:menuitem label="&markMessageStarred.label;" value="markasflagged"/>
+          <xul:menuitem label="&setPriority.label;" value="setpriorityto"/>
+          <xul:menuitem label="&addTag.label;" value="addtagtomessage"/>
+          <xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
+          <xul:menuseparator enableforpop3="true"/>
+          <xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
+          <xul:menuitem label="&deleteFromPOP.label;" value="deletefrompopserver" enableforpop3="true"/>
+          <xul:menuitem label="&fetchFromPOP.label;" value="fetchfrompopserver" enableforpop3="true"/>
+          <xul:menuseparator/>
+          <xul:menuitem label="&ignoreThread.label;" value="ignorethread"/>
+          <xul:menuitem label="&ignoreSubthread.label;" value="ignoresubthread"/>
+          <xul:menuitem label="&watchThread.label;" value="watchthread"/>
+          <xul:menuseparator/>
+          <xul:menuitem label="&stopExecution.label;" value="stopexecution"/>
+        </xul:menupopup>
+      </xul:menulist>
       <xul:ruleactiontarget-wrapper xbl:inherits="type=value"
                                     class="ruleactiontarget"
                                     flex="&filterActionTargetFlexValue;"/>
       <xul:hbox>
         <xul:button class="small-button"
                     label="+"
                     tooltiptext="&addAction.tooltip;"
                     oncommand="this.parentNode.parentNode.addRow();"/>
@@ -377,17 +146,17 @@
                 if (needCustomLabel) {
                   var menuitem = document.createElementNS(
                       "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                       "xul:menuitem");
                   menuitem.setAttribute("label",
                       gFilterBundle.getString("filterMissingCustomAction"));
                   menuitem.setAttribute("value", filterActionStr);
                   menuitem.disabled = true;
-                  this.mRuleActionType.menulist.menupopup.appendChild(menuitem);
+                  this.mRuleActionType.menupopup.appendChild(menuitem);
                   var scriptError = Cc["@mozilla.org/scripterror;1"]
                       .createInstance(Ci.nsIScriptError);
                   scriptError.init("Missing custom action " + filterActionStr,
                       null, null, 0, 0,
                       Ci.nsIScriptError.errorFlag,
                       "component javascript");
                   Services.console.logMessage(scriptError);
                 }
@@ -412,18 +181,17 @@
               case nsMsgFilterAction.AddTag:
                 actionItem.childNodes[0].value = aFilterAction.strValue;
                 break;
               default:
                 break;
             }
             if (aFilterAction.type != nsMsgFilterAction.Custom)
               filterActionStr = gFilterActionStrings[aFilterAction.type];
-            document.getAnonymousNodes(this.mRuleActionType)[0]
-                    .value = filterActionStr;
+            this.mRuleActionType.value = filterActionStr;
             this.mRuleActionTargetInitialized = true;
             this.clearInitialActionIndex();
             checkActionsReorder();
           ]]>
         </body>
       </method>
 
       <method name="validateAction">
@@ -526,17 +294,17 @@
         <body>
           <![CDATA[
             // Collect the action names and arguments in a plain string form.
             let actionTarget = document.getAnonymousNodes(this)[1];
             let actionItem = actionTarget.ruleactiontargetElement;
             let actionItemLabel = actionItem && actionItem.childNodes[0].label;
 
             let actionString = {
-              label: document.getAnonymousNodes(this.mRuleActionType)[0].label,
+              label: this.mRuleActionType.label,
               argument: "",
             };
             if (actionItem) {
               if (actionItemLabel) {
                 actionString.argument = actionItemLabel;
               } else {
                 actionString.argument = actionItem.childNodes[0].value;
               }