Bug 1542715 - [de-xbl] convert folder-menupopup binding to custom element <menupopup is='folder-menupopup'>. r=mkmelin
authorKhushil Mistry <khushil324@gmail.com>
Thu, 06 Jun 2019 02:04:00 +0200
changeset 35794 b492a5b976afeb2461bd0a8bffb9ab835d679dab
parent 35793 0f025fb08630db2c60540b8a82fb0fbc9cc17964
child 35795 2600d6fff1bf31331265cb78a3ce576b8ab9257e
push id392
push userclokep@gmail.com
push dateMon, 02 Sep 2019 20:17:19 +0000
reviewersmkmelin
bugs1542715
Bug 1542715 - [de-xbl] convert folder-menupopup binding to custom element <menupopup is='folder-menupopup'>. r=mkmelin
mail/base/content/FilterListDialog.xul
mail/base/content/SearchDialog.xul
mail/base/content/customElements.js
mail/base/content/mailWidgets.js
mail/base/content/mainMailToolbox.inc.xul
mail/base/content/mainNavigationToolbox.inc.xul
mail/base/content/mainPopupSet.inc.xul
mail/base/content/messenger.css
mail/components/compose/content/messengercompose.xul
mail/test/mozmill/folder-widget/test-message-filters.js
mail/themes/windows/mail/messenger.css
mailnews/base/content/folder-menupopup.js
mailnews/base/content/folderWidgets.xml
mailnews/base/content/newFolderDialog.xul
mailnews/base/content/subscribe.xul
mailnews/base/content/virtualFolderProperties.xul
mailnews/base/prefs/content/am-copies.inc.xul
mailnews/base/prefs/content/am-junk.xul
mailnews/base/prefs/content/am-server-advanced.xul
mailnews/base/prefs/content/am-server.xul
mailnews/base/search/content/searchWidgets.js
mailnews/extensions/newsblog/content/feed-subscriptions.xul
mailnews/jar.mn
--- a/mail/base/content/FilterListDialog.xul
+++ b/mail/base/content/FilterListDialog.xul
@@ -38,17 +38,18 @@
   </keyset>
 
   <hbox id="filterHeader" align="center">
     <label value="&filtersForPrefix.label;"
            accesskey="&filtersForPrefix.accesskey;" control="serverMenu"/>
 
     <menulist id="serverMenu"
               class="folderMenuItem">
-      <menupopup id="serverMenuPopup" type="folder" mode="filters"
+      <menupopup is="folder-menupopup" id="serverMenuPopup"
+                 mode="filters"
                  class="menulist-menupopup"
                  expandFolders="nntp"
                  showFileHereLabel="true"
                  showAccountsFileHere="true"
                  oncommand="setFilterFolder(event.target._folder);"/>
     </menulist>
     <textbox is="search-textbox" id="searchBox"
              class="searchBox"
@@ -80,19 +81,18 @@
           <separator class="thin"/>
           <hbox align="center">
             <label id="folderPickerPrefix" value="&folderPickerPrefix.label;"
                    accesskey="&folderPickerPrefix.accesskey;"
                    disabled="true" control="runFiltersFolder"/>
             <menulist id="runFiltersFolder" disabled="true" flex="1"
                       class="folderMenuItem"
                       displayformat="verbose">
-              <menupopup id="runFiltersPopup"
+              <menupopup is="folder-menupopup" id="runFiltersPopup"
                          class="menulist-menupopup"
-                         type="folder"
                          showFileHereLabel="true"
                          showAccountsFileHere="false"
                          oncommand="setRunFolder(event.target._folder);"/>
             </menulist>
           <button id="runFiltersButton"
                   label="&runFilters.label;"
                   accesskey="&runFilters.accesskey;"
                   runlabel="&runFilters.label;"
--- a/mail/base/content/SearchDialog.xul
+++ b/mail/base/content/SearchDialog.xul
@@ -81,19 +81,18 @@
 
     <vbox id="searchTerms" flex="3" persist="height">
       <vbox>
         <hbox align="center">
           <label value="&searchHeading.label;" accesskey="&searchHeading.accesskey;"
                  control="searchableFolders"/>
           <menulist id="searchableFolders" flex="2" class="folderMenuItem"
                     displayformat="verbose">
-            <menupopup class="menulist-menupopup"
-                       type="folder" mode="search"
-                       showAccountsFileHere="true" showFileHereLabel="true"
+            <menupopup is="folder-menupopup" class="menulist-menupopup"
+                       mode="search" showAccountsFileHere="true" showFileHereLabel="true"
                        oncommand="updateSearchFolderPicker(event.target._folder);"/>
           </menulist>
           <spacer flex="10"/>
           <button id="search-button" oncommand="onSearchButton(event);" default="true"/>
         </hbox>
 
         <hbox align="center">
           <checkbox id="checkSearchSubFolders"
@@ -190,17 +189,17 @@
           <treechildren ondragstart="ThreadPaneOnDragStart(event);"/>
         </tree>
       </vbox>
       <hbox align="start">
         <button label="&openButton.label;" id="openButton" command="cmd_open" accesskey="&openButton.accesskey;"/>
         <button id="fileMessageButton" type="menu" label="&moveButton.label;"
                 accesskey="&moveButton.accesskey;"
                 command="file_message_button">
-          <menupopup type="folder" showFileHereLabel="true" mode="filing"/>
+          <menupopup is="folder-menupopup" showFileHereLabel="true" mode="filing"/>
         </button>
 
       <button label="&deleteButton.label;" id="deleteButton" command="button_delete" accesskey="&deleteButton.accesskey;"/>
       <button label="&openInFolder.label;" id="openInFolderButton" command="open_in_folder_button" accesskey="&openInFolder.accesskey;" />
       <button label="&saveAsVFButton.label;" id="saveAsVFButton" command="saveas_vf_button" accesskey="&saveAsVFButton.accesskey;" />
       <spacer flex="1" />
     </hbox>
   </vbox>
--- a/mail/base/content/customElements.js
+++ b/mail/base/content/customElements.js
@@ -13,15 +13,16 @@ const isDummyDocument = document.documen
 if (!isDummyDocument) {
   for (let script of [
     "chrome://chat/content/conversation-browser.js",
     "chrome://messenger/content/mailWidgets.js",
     "chrome://messenger/content/generalBindings.js",
     "chrome://messenger/content/statuspanel.js",
     "chrome://messenger/content/foldersummary.js",
     "chrome://messenger/content/addressbook/menulist-addrbooks.js",
+    "chrome://messenger/content/folder-menupopup.js",
     "chrome://messenger/content/toolbarbutton-menu-button.js",
     "chrome://messenger/content/toolbarbutton-appmenu.js",
   ]) {
     Services.scriptloader.loadSubScript(script, window);
   }
 }
 })();
--- a/mail/base/content/mailWidgets.js
+++ b/mail/base/content/mailWidgets.js
@@ -479,20 +479,20 @@ class MozThreadPaneTreeColpicker extends
     }
     let popup = this.querySelector(`menupopup[anonid="popup"]`);
 
     // We'll add an "Apply columns to..." menu
     popup.appendChild(MozXULElement.parseXULToFragment(`
       <menu class="applyTo-menu" label="&columnPicker.applyTo.label;">
         <menupopup>
           <menu class="applyToFolder-menu" label="&columnPicker.applyToFolder.label;">
-            <menupopup class="applyToFolder" type="folder" showFileHereLabel="true" position="start_before"></menupopup>
+            <menupopup is="folder-menupopup" class="applyToFolder" showFileHereLabel="true" position="start_before"></menupopup>
           </menu>
           <menu class="applyToFolderAndChildren-menu" label="&columnPicker.applyToFolderAndChildren.label;">
-            <menupopup class="applyToFolderAndChildren" type="folder" showFileHereLabel="true" showAccountsFileHere="true" position="start_before"></menupopup>
+            <menupopup is="folder-menupopup" class="applyToFolderAndChildren" showFileHereLabel="true" showAccountsFileHere="true" position="start_before"></menupopup>
           </menu>
         </menupopup>
       </menu>
     `, ["chrome://messenger/locale/messenger.dtd"]));
 
     let confirmApply = (destFolder, useChildren) => {
       // Confirm the action with the user.
       let bundle = document.getElementById("bundle_messenger");
--- a/mail/base/content/mainMailToolbox.inc.xul
+++ b/mail/base/content/mainMailToolbox.inc.xul
@@ -16,20 +16,19 @@
   <toolbarpalette id="MailToolbarPalette">
     <toolbarbutton is="toolbarbutton-menu-button" id="button-getmsg"
                    type="menu-button"
                    class="toolbarbutton-1"
                    label="&getMsgButton1.label;"
                    tooltiptext="&getMsgButton.tooltip;"
                    oncommand="MsgGetMessagesForAccount();"
                    observes="button_getNewMessages">
-      <menupopup id="button-getMsgPopup"
+      <menupopup is="folder-menupopup" id="button-getMsgPopup"
                  onpopupshowing="getMsgToolbarMenu_init();"
                  oncommand="MsgGetMessagesForAccount(event.target._folder); event.stopPropagation();"
-                 type="folder"
                  expandFolders="false"
                  mode="getMail">
         <menuitem id="button-getAllNewMsg"
                   label="&getAllNewMsgCmd.label;"
                   accesskey="&getAllNewMsgCmd.accesskey;"
                   command="cmd_getMsgsForAuthAccounts"/>
         <menuseparator id="button-getAllNewMsgSeparator"/>
       </menupopup>
@@ -77,18 +76,17 @@
     </toolbarbutton>
     <toolbarbutton id="button-file"
                    type="menu"
                    class="toolbarbutton-1"
                    label="&fileButton.label;"
                    tooltiptext="&fileButton.tooltip;"
                    observes="button_file"
                    oncommand="MsgMoveMessage(event.target._folder)">
-      <menupopup id="button-filePopup"
-                 type="folder"
+      <menupopup is="folder-menupopup" id="button-filePopup"
                  mode="filing"
                  showRecent="true"
                  showFileHereLabel="true"
                  recentLabel="&moveCopyMsgRecentMenu.label;"
                  recentAccessKey="&moveCopyMsgRecentMenu.accesskey;"/>
     </toolbarbutton>
     <toolbarbutton id="button-archive"
                    class="toolbarbutton-1"
@@ -364,19 +362,18 @@
                  align="center"
                  class="chromeclass-toolbar-additional">
       <menulist id="locationFolders"
                 class="folderMenuItem"
                 label="&folderLocationToolbarItem.title;"
                 context="folderPaneContext"
                 maxwidth="300"
                 crop="center">
-        <menupopup id="folderLocationPopup"
+        <menupopup is="folder-menupopup" id="folderLocationPopup"
                    class="menulist-menupopup"
-                   type="folder"
                    flex="1"
                    mode="notDeferred"
                    showFileHereLabel="true"
                    oncommand="gFolderTreeView.selectFolder(event.target._folder, true);"/>
       </menulist>
     </toolbaritem>
     <toolbaritem id="mailviews-container"
                  title="&mailViewsToolbarItem.title;"
--- a/mail/base/content/mainNavigationToolbox.inc.xul
+++ b/mail/base/content/mainNavigationToolbox.inc.xul
@@ -108,17 +108,17 @@
                accesskey="&saveAsTemplateCmd.accesskey;"
                command="cmd_saveAsTemplate"/>
             </menupopup>
           </menu>
           <menuseparator id="fileMenuAfterSaveSeparator"/>
           <menu label="&getNewMsgForCmd.label;" accesskey="&getNewMsgForCmd.accesskey;"
                 id="menu_getAllNewMsg"
                 oncommand="MsgGetMessagesForAccount();">
-            <menupopup type="folder" mode="getMail" id="menu_getAllNewMsgPopup"
+            <menupopup is="folder-menupopup" id="menu_getAllNewMsgPopup"
                        expandFolders="false"
                        oncommand="MsgGetMessagesForAccount(event.target._folder); event.stopPropagation();">
               <menuitem id="menu_getnewmsgs_all_accounts"
                         label="&getAllNewMsgCmdPopupMenu.label;"
                         accesskey="&getAllNewMsgCmdPopupMenu.accesskey;"
                         key="key_getAllNewMessages"
                         command="cmd_getMsgsForAuthAccounts"/>
               <menuitem id="menu_getnewmsgs_current_account"
@@ -647,18 +647,17 @@
                     accesskey="&goChatCmd.accesskey;"
                     command="cmd_chat"
                     key="key_goChat"/>
           <menuseparator id="goChatSeparator"/>
           <menu id="goFolderMenu"
                 label="&folderMenu.label;"
                 accesskey="&folderMenu.accesskey;"
                 command="cmd_goFolder">
-            <menupopup id="menu_GoFolderPopup"
-                       type="folder"
+            <menupopup is="folder-menupopup" id="menu_GoFolderPopup"
                        showFileHereLabel="true"
                        showRecent="true"
                        recentLabel="&contextMoveCopyMsgRecentMenu.label;"
                        recentAccessKey="&contextMoveCopyMsgRecentMenu.accesskey;"
                        showFavorites="true"
                        favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                        favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
           </menu>
@@ -869,30 +868,32 @@
                       command="cmd_archive"/>
             <menuitem id="menu_cancel" command="cmd_cancel"
                       label="&cancelNewsMsgCmd.label;"
                       accesskey="&cancelNewsMsgCmd.accesskey;"/>
             <menu id="moveMenu"
                   label="&moveMsgToMenu.label;"
                   accesskey="&moveMsgToMenu.accesskey;"
                   oncommand="MsgMoveMessage(event.target._folder)">
-              <menupopup type="folder" mode="filing"
+              <menupopup is="folder-menupopup"
+                         mode="filing"
                          showFileHereLabel="true"
                          showRecent="true"
                          recentLabel="&moveCopyMsgRecentMenu.label;"
                          recentAccessKey="&moveCopyMsgRecentMenu.accesskey;"
                          showFavorites="true"
                          favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                          favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
             </menu>
             <menu id="copyMenu"
                   label="&copyMsgToMenu.label;"
                   accesskey="&copyMsgToMenu.accesskey;"
                   oncommand="MsgCopyMessage(event.target._folder)">
-              <menupopup type="folder" mode="filing"
+              <menupopup is="folder-menupopup"
+                         mode="filing"
                          showFileHereLabel="true"
                          showRecent="true"
                          recentLabel="&moveCopyMsgRecentMenu.label;"
                          recentAccessKey="&moveCopyMsgRecentMenu.accesskey;"
                          showFavorites="true"
                          favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                          favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
             </menu>
--- a/mail/base/content/mainPopupSet.inc.xul
+++ b/mail/base/content/mainPopupSet.inc.xul
@@ -374,19 +374,18 @@
             <menuitem id="appmenu_close"
                       label="&closeCmd.label;"
                       key="key_close"
                       command="cmd_close"/>
             <menuseparator id="appmenu_fileMenuAfterCloseSeparator"/>
             <menu id="appmenu_getNewMsgFor"
                   label="&getNewMsgForCmd.label;"
                   oncommand="MsgGetMessagesForAccount();">
-              <menupopup type="folder"
+              <menupopup is="folder-menupopup" id="appmenu_getAllNewMsgPopup"
                          mode="getMail"
-                         id="appmenu_getAllNewMsgPopup"
                          expandFolders="false"
                          oncommand="MsgGetMessagesForAccount(event.target._folder); event.stopPropagation();">
                 <menuitem id="appmenu_getnewmsgs_all_accounts"
                           label="&getAllNewMsgCmdPopupMenu.label;"
                           key="key_getAllNewMessages"
                           command="cmd_getMsgsForAuthAccounts"/>
                 <menuitem id="appmenu_getnewmsgs_current_account"
                           label="&getNewMsgCurrentAccountCmdPopupMenu.label;"
@@ -823,18 +822,17 @@
             <menuitem id="appmenu_goChat"
                       label="&goChatCmd.label;"
                       key="key_goChat"
                       command="cmd_chat"/>
             <menuseparator id="appmenu_goChatSeparator"/>
             <menu id="appmenu_goFolderMenu"
                   label="&folderMenu.label;"
                   command="cmd_goFolder">
-              <menupopup id="appmenu_GoFolderPopup"
-                         type="folder"
+              <menupopup is="folder-menupopup" id="appmenu_GoFolderPopup"
                          showFileHereLabel="true"
                          showRecent="true"
                          recentLabel="&contextMoveCopyMsgRecentMenu.label;"/>
             </menu>
             <menuseparator id="appmenu_goFolderSeparator"/>
             <menu id="appmenu_goRecentlyClosedTabs"
                   label="&goRecentlyClosedTabs.label;"
                   observes="cmd_undoCloseTab">
@@ -1004,29 +1002,29 @@
                       key="key_archive"
                       command="cmd_archive"/>
             <menuitem id="appmenu_cancel"
                       command="cmd_cancel"
                       label="&cancelNewsMsgCmd.label;"/>
             <menu id="appmenu_moveMenu"
                   label="&moveMsgToMenu.label;"
                   oncommand="MsgMoveMessage(event.target._folder)">
-              <menupopup type="folder"
+              <menupopup is="folder-menupopup"
                          mode="filing"
                          showFileHereLabel="true"
                          showRecent="true"
                          recentLabel="&moveCopyMsgRecentMenu.label;"
                          showFavorites="true"
                          favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                          favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
             </menu>
             <menu id="appmenu_copyMenu"
                   label="&copyMsgToMenu.label;"
                   oncommand="MsgCopyMessage(event.target._folder)">
-              <menupopup type="folder"
+              <menupopup is="folder-menupopup"
                          mode="filing"
                          showFileHereLabel="true"
                          showRecent="true"
                          recentLabel="&moveCopyMsgRecentMenu.label;"
                          showFavorites="true"
                          favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                          favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
             </menu>
@@ -1482,33 +1480,31 @@
     <menuitem id="mailContext-archive"
               label="&contextArchive.label;"
               accesskey="&contextArchive.accesskey;"
               oncommand="MsgArchiveSelectedMessages(event);"/>
     <menu id="mailContext-moveMenu"
           label="&contextMoveMsgMenu.label;"
           accesskey="&contextMoveMsgMenu.accesskey;"
           oncommand="MsgMoveMessage(event.target._folder)">
-      <menupopup id="mailContext-fileHereMenu"
-                 type="folder"
+      <menupopup is="folder-menupopup" id="mailContext-fileHereMenu"
                  mode="filing"
                  showFileHereLabel="true"
                  showRecent="true"
                  recentLabel="&contextMoveCopyMsgRecentMenu.label;"
                  recentAccessKey="&contextMoveCopyMsgRecentMenu.accesskey;"
                  showFavorites="true"
                  favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                  favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
     </menu>
     <menu id="mailContext-copyMenu"
           label="&contextCopyMsgMenu.label;"
           accesskey="&contextCopyMsgMenu.accesskey;"
           oncommand="MsgCopyMessage(event.target._folder)">
-      <menupopup id="mailContext-copyHereMenu"
-                 type="folder"
+      <menupopup is="folder-menupopup" id="mailContext-copyHereMenu"
                  mode="filing"
                  showFileHereLabel="true"
                  showRecent="true"
                  recentLabel="&contextMoveCopyMsgRecentMenu.label;"
                  recentAccessKey="&contextMoveCopyMsgRecentMenu.accesskey;"
                  showFavorites="true"
                  favoritesLabel="&contextMoveCopyMsgFavoritesMenu.label;"
                  favoritesAccessKey="&contextMoveCopyMsgFavoritesMenu.accesskey;"/>
--- a/mail/base/content/messenger.css
+++ b/mail/base/content/messenger.css
@@ -67,20 +67,16 @@ searchterm {
 .remote-gloda-search-container {
   min-width: 10em;
 }
 
 tabmail {
   -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail");
 }
 
-menupopup[type="folder"] {
-  -moz-binding: url("chrome://messenger/content/folderWidgets.xml#folder-menupopup");
-}
-
 .chromeclass-toolbar {
   overflow-x: hidden;
 }
 
 /* Lightweight themes support */
 
 :root:-moz-lwtheme {
   --toolbar-color: var(--lwt-text-color, inherit);
--- a/mail/components/compose/content/messengercompose.xul
+++ b/mail/components/compose/content/messengercompose.xul
@@ -1687,18 +1687,18 @@
               <menuitem type="radio" name="priority" label="&normalPriorityCmd.label;" accesskey="&normalPriorityCmd.accesskey;" value="Normal" id="priority_normal" checked="true"/>
               <menuitem type="radio" name="priority" label="&lowPriorityCmd.label;" accesskey="&lowPriorityCmd.accesskey;" value="Low" id="priority_low"/>
               <menuitem type="radio" name="priority" label="&lowestPriorityCmd.label;" accesskey="&lowestPriorityCmd.accesskey;" value="Lowest"  id="priority_lowest"/>
             </menupopup>
           </menu>
           <menu id="fccMenu" label="&fileCarbonCopyCmd.label;"
                 accesskey="&fileCarbonCopyCmd.accesskey;"
                 oncommand="MessageFcc(event.target._folder)">
-            <menupopup id="fccMenuPopup" type="folder" mode="filing" showFileHereLabel="true"
-                       fileHereLabel="&fileHereMenu.label;"/>
+            <menupopup is="folder-menupopup" id="fccMenuPopup" mode="filing"
+                       showFileHereLabel="true" fileHereLabel="&fileHereMenu.label;"/>
           </menu>
           <menuseparator/>
           <menuitem type="checkbox" command="cmd_customizeFromAddress"
                     accesskey="&customizeFromAddress.accesskey;"/>
           <menuseparator id="smimeOptionsSeparator"/>
 
           <menuitem id="menu_securityEncryptRequire1"
                     type="checkbox"
--- a/mail/test/mozmill/folder-widget/test-message-filters.js
+++ b/mail/test/mozmill/folder-widget/test-message-filters.js
@@ -69,17 +69,17 @@ function test_message_filter_shows_newsg
   // filterc.select(popup, 2);
   // let nntpPopup = new elib.Elem(nntp.node.menupopup);
   // filterc.click(nntpPopup);
   // filterc.mouseover(nntpPopup);
   // filterc.select(nntpPopup, 2);
 
   // This one initializes the menuitems, but it's kinda hacky.
   nntp.node.menupopup._ensureInitialized();
-  assert_equals(nntp.node.itemCount, 5,
+  assert_equals(nntp.node.itemCount, 3,
                 "Incorrect number of children for the NNTP server");
   close_window(filterc);
 }
 
 /*
  * Test that customizing the toolbar doesn't lead to doubled accounts in
  * the Get Mail menu.  (bug 520457)
  */
--- a/mail/themes/windows/mail/messenger.css
+++ b/mail/themes/windows/mail/messenger.css
@@ -520,29 +520,29 @@ treechildren::-moz-tree-indentation {
 
 :root[lwt-tree-brighttext] #threadTree treechildren::-moz-tree-row(untagged, selected),
 :root[lwt-tree-brighttext]:-moz-window-inactive #threadTree treechildren::-moz-tree-row(selected),
 :root[lwt-tree-brighttext] tree:not(#threadTree) treechildren::-moz-tree-row(selected) {
   border-color: var(--sidebar-highlight-background-color, rgba(249,249,250,.1));
   background: var(--sidebar-highlight-background-color, rgba(249,249,250,.1));
 }
 
-/* Give menupopup[type="folder"] a menu look */
-menupopup[type="folder"] {
+/* Give menupopup[is="folder-menupopup"] a menu look */
+menupopup[is="folder-menupopup"] {
   -moz-appearance: menupopup;
 }
 
-menupopup[type="folder"] menu[_moz-menuactive="true"],
-menupopup[type="folder"] menuitem[_moz-menuactive="true"] {
+menupopup[is="folder-menupopup"] menu[_moz-menuactive="true"],
+menupopup[is="folder-menupopup"] menuitem[_moz-menuactive="true"] {
   color: -moz-MenuHoverText;
   background-color: -moz-MenuHover;
 }
 
-menupopup[type="folder"] > menu,
-menupopup[type="folder"] > menuitem {
+menupopup[is="folder-menupopup"] > menu,
+menupopup[is="folder-menupopup"] > menuitem {
   -moz-appearance: menuitem !important;
   padding-right: 0;
   padding-left: 0;
 }
 
 menulist.folderMenuItem > .menulist-label-box > .menulist-label {
   margin-inline-start: 2px !important;
 }
@@ -745,17 +745,17 @@ toolbar[mode="icons"] .toolbarbutton-1:n
 }
 
 .findbar-button[checked=true]:not(:active):hover,
 .toolbarbutton-1[checked=true]:not(:active):hover {
   background-color: var(--toolbarbutton-checkedhover-backgroundcolor);
   transition: background-color .4s;
 }
 
-menulist > menupopup:not([type="folder"]) > menuseparator {
+menulist > menupopup:not([is="folder-menupopup"]) > menuseparator {
   -moz-appearance: none;
   margin-top: 3px;
   margin-bottom: 3px;
   padding-top: 0;
   padding-bottom: 0;
   border-top-color: #d7d7d7;
 }
 
rename from mailnews/base/content/folderWidgets.xml
rename to mailnews/base/content/folder-menupopup.js
--- a/mailnews/base/content/folderWidgets.xml
+++ b/mailnews/base/content/folder-menupopup.js
@@ -1,890 +1,914 @@
-<?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/.
--->
+/* 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="mailFolderBindings"
-          xmlns="http://www.mozilla.org/xbl"
-          xmlns:xbl="http://www.mozilla.org/xbl"
-          xmlns:html="http://www.w3.org/1999/xhtml"
-          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+"use strict";
+
+/* globals MozElements */
 
-  <binding id="folder-menupopup"
-           extends="chrome://global/content/bindings/popup.xml#popup">
-    <implementation>
-      <constructor><![CDATA[
-        ChromeUtils.import("resource:///modules/FeedUtils.jsm", this);
-        ChromeUtils.import("resource:///modules/folderUtils.jsm", this);
-        ChromeUtils.import("resource:///modules/iteratorUtils.jsm", this);
-        ChromeUtils.import("resource:///modules/MailServices.jsm", this);
-        ChromeUtils.import("resource:///modules/MailUtils.jsm", this);
-        ChromeUtils.import("resource:///modules/StringBundle.js", this);
-        this._stringBundle = new this
-            .StringBundle("chrome://messenger/locale/folderWidgets.properties");
+// This is loaded into all XUL windows. Wrap in a block to prevent
+// leaking to window scope.
+{
+  const { FeedUtils } = ChromeUtils.import("resource:///modules/FeedUtils.jsm");
+  const { allAccountsSorted, folderNameCompare, getSpecialFolderString, getMostRecentFolders } =
+    ChromeUtils.import("resource:///modules/folderUtils.jsm");
+  const { fixIterator, toArray } = ChromeUtils.import("resource:///modules/iteratorUtils.jsm");
+  const { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
+  const { MailUtils } = ChromeUtils.import("resource:///modules/MailUtils.jsm");
+  const { StringBundle } = ChromeUtils.import("resource:///modules/StringBundle.js");
 
-        // Get the displayformat if set.
-        if (this.parentNode && this.parentNode.localName == "menulist")
-          this._displayformat = this.parentNode.getAttribute("displayformat");
+  /**
+   * The MozFolderMenupopup widget is used as a menupopup for selecting
+   * a folder from the list of all the folders from every account. It is also
+   * used for selecting the account from the list of all the accounts. The each
+   * menuitem gets displayed with the folder or account name and icon.
+   *
+   * @extends {MozElements.MozMenuPopup}
+   */
+  class MozFolderMenupopup extends MozElements.MozMenuPopup {
+    constructor() {
+      super();
+
+      // In order to improve performance, we're not going to build any of the
+      // menu until we're shown.
+      // note: _ensureInitialized can be called repeatedly without issue, so
+      //       don't worry about it here.
+      this.addEventListener("popupshowing", (event) => {
+        this._ensureInitialized();
+      }, true);
 
-        // Find out if we are in a wrapper (customize toolbars mode is active).
-        let inWrapper = false;
-        let node = this;
-        while (node instanceof XULElement) {
-          if (node.id.startsWith("wrapper-")) {
-            inWrapper = true;
-            break;
-          }
-          node = node.parentNode;
-        }
+      window.addEventListener("unload", () => {
+        this._removeListener();
+      }, { once: true });
+
+      // If non-null, the subFolders of this nsIMsgFolder will be used to
+      // populate this menu.  If this is null, the menu will be populated
+      // using the root-folders for all accounts.
+      this._parentFolder = null;
+
+      this._stringBundle = null;
 
-        if (!inWrapper) {
-          if (this.hasAttribute("original-width")) {
-            // If we were in a wrapper before and have a width stored, restore it now.
-            if (this.getAttribute("original-width") == "none")
-              this.removeAttribute("width");
-            else
-              this.setAttribute("width", this.getAttribute("original-width"));
-
-            this.removeAttribute("original-width");
+      // Various filtering modes can be used with this menu-binding. To use
+      // one of them, append the mode="foo" attribute to the element. When
+      // building the menu, we will then use this._filters[mode] as a filter
+      // function to eliminate folders that should not be shown.
+      // note: extensions should feel free to plug in here.
+      this._filters = {
+        // Returns true if messages can be filed in the folder.
+        filing(folder) {
+          if (!folder.server.canFileMessagesOnServer) {
+            return false;
           }
 
-          // If we are a child of a menulist, and we aren't in a wrapper, we
-          // need to build our content right away, otherwise the menulist
-          // won't have proper sizing.
-          if (this.parentNode && this.parentNode.localName == "menulist")
-            this._ensureInitialized();
-        } else {
-          // But if we're in a wrapper, remove our children, because we're
-          // getting re-created when the toolbar customization closes.
-          this._teardown();
-
-          // Store our current width and set a safe small width when we show
-          // in a wrapper.
-          if (!this.hasAttribute("original-width")) {
-            this.setAttribute("original-width", this.hasAttribute("width") ?
-                                                this.getAttribute("width") : "none");
-            this.setAttribute("width", "100");
-          }
-        }
-      ]]></constructor>
-      <destructor><![CDATA[
-        // Clean up when being destroyed.
-        this._removeListener();
-        this._teardown();
-      ]]></destructor>
-      <!--
-         - Make sure we remove our listener when the window is being destroyed
-         - or the widget teared down.
-        -->
-      <method name="_removeListener">
-        <body><![CDATA[
-          if (!this._initialized)
-            return;
-
-          this.MailServices.mailSession.RemoveFolderListener(this._listener);
-        ]]></body>
-      </method>
-
-      <field name="_stringBundle">null</field>
-
-      <!--
-         - If non-null, the subFolders of this nsIMsgFolder will be used to
-         - populate this menu.  If this is null, the menu will be populated
-         - using the root-folders for all accounts
-        -->
-      <field name="_parentFolder">null</field>
-      <property name="parentFolder"
-                onget="return this._parentFolder;"
-                onset="return this._parentFolder = val;"/>
-
-      <!--
-         - Various filtering modes can be used with this menu-binding.  To use
-         - one of them, append the mode="foo" attribute to the element.  When
-         - building the menu, we will then use this._filters[mode] as a filter
-         - function to eliminate folders that should not be shown.
-         -
-         - Note that extensions should feel free to plug in here!
-        -->
-      <field name="_filters"><![CDATA[({
-        // Returns true if messages can be filed in the folder
-        filing(aFolder) {
-          if (!aFolder.server.canFileMessagesOnServer)
-            return false;
-
-          return (aFolder.canFileMessages || aFolder.hasSubFolders);
+          return (folder.canFileMessages || folder.hasSubFolders);
         },
 
         // Returns true if we can get mail for this folder. (usually this just
-        // means the "root" fake folder)
-        getMail(aFolder) {
-          if (aFolder.isServer && aFolder.server.type != "none")
+        // means the "root" fake folder).
+        getMail(folder) {
+          if (folder.isServer && folder.server.type != "none") {
             return true;
-          if (aFolder.server.type == "nntp" || aFolder.server.type == "rss")
+          }
+          if (folder.server.type == "nntp" || folder.server.type == "rss") {
             return true;
+          }
           return false;
         },
 
-        // Returns true if we can add filters to this folder/account
-        filters(aFolder) {
-          // We can always filter news
-          if (aFolder.server.type == "nntp")
+        // Returns true if we can add filters to this folder/account.
+        filters(folder) {
+          // We can always filter news.
+          if (folder.server.type == "nntp") {
             return true;
+          }
 
-          return aFolder.server.canHaveFilters;
+          return folder.server.canHaveFilters;
         },
 
-        subscribe(aFolder) {
-          return aFolder.canSubscribe;
+        subscribe(folder) {
+          return folder.canSubscribe;
         },
 
-        newFolder(aFolder) {
-          return aFolder.canCreateSubfolders &&
-                 aFolder.server.canCreateFoldersOnServer;
+        newFolder(folder) {
+          return folder.canCreateSubfolders &&
+            folder.server.canCreateFoldersOnServer;
         },
 
-        deferred(aFolder) {
-          return aFolder.server.canCreateFoldersOnServer &&
-                 !aFolder.supportsOffline;
+        deferred(folder) {
+          return folder.server.canCreateFoldersOnServer &&
+            !folder.supportsOffline;
         },
 
-        // Folders that are not in a deferred account
-        notDeferred(aFolder) {
-          let server = aFolder.server;
+        // Folders that are not in a deferred account.
+        notDeferred(folder) {
+          let server = folder.server;
           return !(server instanceof Ci.nsIPop3IncomingServer &&
-                   server.deferredToAccount);
+            server.deferredToAccount);
         },
 
         // Folders that can be searched.
-        search(aFolder) {
-          if (!aFolder.server.canSearchMessages ||
-              aFolder.getFlag(Ci.nsMsgFolderFlags.Virtual))
+        search(folder) {
+          if (!folder.server.canSearchMessages ||
+            folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
             return false;
+          }
           return true;
         },
 
         // Folders that can subscribe feeds.
-        feeds(aFolder) {
-          if (aFolder.server.type != "rss" ||
-              aFolder.getFlag(Ci.nsMsgFolderFlags.Trash) ||
-              aFolder.getFlag(Ci.nsMsgFolderFlags.Virtual))
+        feeds(folder) {
+          if (folder.server.type != "rss" ||
+            folder.getFlag(Ci.nsMsgFolderFlags.Trash) ||
+            folder.getFlag(Ci.nsMsgFolderFlags.Virtual)) {
             return false;
+          }
           return true;
         },
 
-        junk(aFolder) {
+        junk(folder) {
           // Don't show servers (nntp & any others) which do not allow search or filing
           // I don't really understand why canSearchMessages is needed, but it was included in
           // earlier code, so I include it as well.
-          if (!aFolder.server.canFileMessagesOnServer || !aFolder.server.canSearchMessages)
+          if (!folder.server.canFileMessagesOnServer || !folder.server.canSearchMessages) {
             return false;
-          // show parents that might have usable subfolders, or usable folders
-          return aFolder.hasSubFolders || aFolder.canFileMessages;
+          }
+          // Show parents that might have usable subfolders, or usable folders.
+          return folder.hasSubFolders || folder.canFileMessages;
         },
-      })]]></field>
+      };
 
-      <!--
-         - The maximum number of entries in the "Recent" menu
-        -->
-      <field name="_MAXRECENT">15</field>
+      // The maximum number of entries in the "Recent" menu.
+      this._MAXRECENT = 15;
 
-      <!--
-         - Is this list containing only servers (accounts) and no real folders?
-        -->
-      <field name="_serversOnly">true</field>
+      // Is this list containing only servers (accounts) and no real folders?
+      this._serversOnly = true;
 
-      <!--
-         - Our listener to let us know when folders change/appear/disappear so
-         - we can know to rebuild ourselves.
-        -->
-      <field name="_listener">
-          <![CDATA[({
+      /**
+       * Our listener to let us know when folders change/appear/disappear so
+       * we can know to rebuild ourselves.
+       *
+       * @implements {nsIFolderListener}
+       */
+      this._listener = {
         _menu: this,
         _clearMenu(menu) {
-          // xxx I'm not quite sure why this isn't always a function (bug 514445)
-          if (menu._teardown)
+          // I'm not quite sure why this isn't always a function (bug 514445).
+          if (menu._teardown) {
             menu._teardown();
+          }
         },
-        OnItemAdded(aRDFParentItem, aItem) {
-          if (!(aItem instanceof Ci.nsIMsgFolder))
-            return;
-          if (this._filterFunction && !this._filterFunction(aItem)) {
+        OnItemAdded(RDFParentItem, item) {
+          if (!(item instanceof Ci.nsIMsgFolder)) {
             return;
           }
-          // xxx we can optimize this later
-          this._clearMenu(this._menu);
-        },
-
-        OnItemRemoved(aRDFParentItem, aItem) {
-          if (!(aItem instanceof Ci.nsIMsgFolder))
-            return;
-          if (this._filterFunction && !this._filterFunction(aItem)) {
+          if (this._filterFunction && !this._filterFunction(item)) {
             return;
           }
           // xxx we can optimize this later
           this._clearMenu(this._menu);
         },
 
+        OnItemRemoved(RDFParentItem, item) {
+          if (!(item instanceof Ci.nsIMsgFolder)) {
+            return;
+          }
+          if (this._filterFunction && !this._filterFunction(item)) {
+            return;
+          }
+          // xxx we can optimize this later.
+          this._clearMenu(this._menu);
+        },
+
         // xxx I stole this listener list from nsMsgFolderDatasource.cpp, but
-        //     someone should really document what events are fired when, so that
-        //     we make sure we're updating at the right times.
-        OnItemPropertyChanged(aItem, aProperty, aOld, aNew) {},
-        OnItemIntPropertyChanged(aItem, aProperty, aOld, aNew) {
-          if (aItem instanceof Ci.nsIMsgFolder) {
-            if (aProperty == "FolderFlag") {
+        // someone should really document what events are fired when, so that
+        // we make sure we're updating at the right times.
+        OnItemPropertyChanged(item, property, old, newItem) { },
+        OnItemIntPropertyChanged(item, property, old, aNew) {
+          if (item instanceof Ci.nsIMsgFolder) {
+            if (property == "FolderFlag") {
               if (this._menu.getAttribute("showFavorites") != "true" ||
-                  !this._menu._initializedSpecials.has("favorites"))
+                !this._menu._initializedSpecials.has("favorites")) {
                 return;
+              }
 
-              if ((aOld & Ci.nsMsgFolderFlags.Favorite) !=
-                 (aNew & Ci.nsMsgFolderFlags.Favorite))
-               setTimeout(this._clearMenu, 0, this._menu);
+              if ((old & Ci.nsMsgFolderFlags.Favorite) !=
+                (aNew & Ci.nsMsgFolderFlags.Favorite)) {
+                setTimeout(this._clearMenu, 0, this._menu);
+              }
             }
           }
-          var child = this._getChildForItem(aItem);
-          if (child)
+          let child = this._getChildForItem(item);
+          if (child) {
             this._menu._setCssSelectors(child._folder, child);
+          }
         },
-        OnItemBoolPropertyChanged(aItem, aProperty, aOld, aNew) {
-          var child = this._getChildForItem(aItem);
-          if (child)
+        OnItemBoolPropertyChanged(item, property, old, newItem) {
+          let child = this._getChildForItem(item);
+          if (child) {
             this._menu._setCssSelectors(child._folder, child);
+          }
         },
-        OnItemUnicharPropertyChanged(aItem, aProperty, aOld, aNew) {
-          var child = this._getChildForItem(aItem);
-          if (child)
+        OnItemUnicharPropertyChanged(item, property, old, newItem) {
+          let child = this._getChildForItem(item);
+          if (child) {
             this._menu._setCssSelectors(child._folder, child);
+          }
         },
-        OnItemPropertyFlagChanged(aItem, aProperty, aOld, aNew) {},
-        OnItemEvent(aFolder, aEvent) {
-          if (aEvent == "MRMTimeChanged") {
+        OnItemPropertyFlagChanged(item, property, old, newItem) { },
+        OnItemEvent(folder, event) {
+          if (event == "MRMTimeChanged") {
             if (this._menu.getAttribute("showRecent") != "true" ||
-                !this._menu._initializedSpecials.has("recent") ||
-                !this._menu.firstChild || !this._menu.firstChild.firstChild)
+              !this._menu._initializedSpecials.has("recent") ||
+              !this._menu.firstChild || !this._menu.firstChild.firstChild) {
               return;
-            // if this folder is already in the recent menu, return.
-            if (this._getChildForItem(aFolder,
-                                      this._menu.firstChild.firstChild))
+            }
+            // If this folder is already in the recent menu, return.
+            if (this._getChildForItem(folder,
+              this._menu.firstChild.firstChild)) {
               return;
-          } else if (aEvent == "RenameCompleted") {
+            }
+          } else if (event == "RenameCompleted") {
             // Special casing folder renames here, since they require more work
             // since sort-order may have changed.
-            if (!this._getChildForItem(aFolder))
+            if (!this._getChildForItem(folder)) {
               return;
+            }
           } else {
             return;
           }
-          // folder renamed, or new recent folder, so rebuild.
+          // Folder renamed, or new recent folder, so rebuild.
           setTimeout(this._clearMenu, 0, this._menu);
         },
 
         /**
          * Helper function to check and see whether we have a menuitem for this
-         * particular nsIMsgFolder
+         * particular nsIMsgFolder.
          *
-         * @param aItem  the nsIMsgFolder to check
-         * @param aMenu  (optional) menu to look in, defaults to this._menu.
-         * @returns      null if no child for that folder exists, otherwise the
-         *               menuitem for that child
+         * @param {nsIMsgFolder} item - the folder to check
+         * @param {Menu}              - (optional) menu to look in, defaults to this._menu.
+         * @returns {Menuitem|null}   - if no child for that folder exists, otherwise the
+         *                              menuitem for that child.
          */
-        _getChildForItem(aItem, aMenu) {
-          aMenu = aMenu || this._menu;
-          if (!aMenu || !aMenu.hasChildNodes())
+        _getChildForItem(item, menu) {
+          let _menu = menu || this._menu;
+          if (!_menu || !_menu.hasChildNodes()) {
+            return null;
+          }
+          if (!(item instanceof Ci.nsIMsgFolder)) {
             return null;
+          }
+          for (let i = 0; i < _menu.childNodes.length; i++) {
+            let folder = _menu.childNodes[i]._folder;
+            if (folder && folder.URI == item.URI) {
+              return _menu.childNodes[i];
+            }
+          }
+          return null;
+        },
+      };
 
-           if (!(aItem instanceof Ci.nsIMsgFolder))
-             return null;
-           for (let i = 0; i < aMenu.childNodes.length; i++) {
-             let folder = aMenu.childNodes[i]._folder;
-             if (folder && folder.URI == aItem.URI)
-               return aMenu.childNodes[i];
-           }
-           return null;
-        },
-      })]]></field>
+      // True if we have already built our menu-items and are now just
+      // listening for changes.
+      this._initialized = false;
 
-       <!--
-         - True if we have already built our menu-items and are now just
-         - listening for changes.
-         -->
-      <field name="_initialized">false</field>
+      // A Set listing which of our special menus are already built.
+      // E.g. "recent", "favorites".
+      this._initializedSpecials = new Set();
 
-      <!--
-         - A Set listing which of our special menus are already built.
-         - E.g. "recent", "favorites".
-        -->
-      <field name="_initializedSpecials">new Set()</field>
+      this._displayformat = null;
+    }
+
+    connectedCallback() {
+      if (this.delayConnectedCallback()) {
+        return;
+      }
 
-      <!--
-         - Call this if you are unsure whether the menu-items have been built,
-         - but know that they need to be built now if they haven't.
-        -->
-      <method name="_ensureInitialized">
-        <body><![CDATA[
-          if (this._initialized)
-            return;
+      this.setAttribute("is", "folder-menupopup");
+
+      this._stringBundle = new StringBundle("chrome://messenger/locale/folderWidgets.properties");
+
+      // Get the displayformat if set.
+      if (this.parentNode && this.parentNode.localName == "menulist") {
+        this._displayformat = this.parentNode.getAttribute("displayformat");
+      }
 
-          let folders;
-
-          // Figure out which folders to build.  If we don't have a parent, then
-          // we assume we should build the top-level accounts. (Actually we
-          // build the fake root folders for those accounts.)
-          if (!this._parentFolder) {
-            let accounts = this.allAccountsSorted(true);
+      // Find out if we are in a wrapper (customize toolbars mode is active).
+      let inWrapper = false;
+      let node = this;
+      while (node instanceof XULElement) {
+        if (node.id.startsWith("wrapper-")) {
+          inWrapper = true;
+          break;
+        }
+        node = node.parentNode;
+      }
 
-            // Now generate our folder-list.  Note that we'll special case this
-            // situation below, to avoid destroying the sort order we just made
-            folders = accounts.map(acct => acct.incomingServer.rootFolder);
+      if (!inWrapper) {
+        if (this.hasAttribute("original-width")) {
+           // If we were in a wrapper before and have a width stored, restore it now.
+          if (this.getAttribute("original-width") == "none") {
+            this.removeAttribute("width");
           } else {
-            // If we do have a parent folder, then we just build based on those
-            // subFolders for that parent.
-            folders = this.toArray(this.fixIterator(this._parentFolder.subFolders,
-                                                    Ci.nsIMsgFolder));
+            this.setAttribute("width", this.getAttribute("original-width"));
           }
 
-          this._build(folders);
+          this.removeAttribute("original-width");
+        }
 
-          // Lastly, we add a listener to get notified of changes in the folder
-          // structure.
-          this.MailServices.mailSession.AddFolderListener(this._listener,
-                                                          Ci.nsIFolderListener.all);
-
-          this._initialized = true;
-        ]]></body>
-      </method>
+        // If we are a child of a menulist, and we aren't in a wrapper, we
+        // need to build our content right away, otherwise the menulist
+        // won't have proper sizing.
+        if (this.parentNode && this.parentNode.localName == "menulist") {
+          this._ensureInitialized();
+        }
+      } else {
+        // But if we're in a wrapper, remove our children, because we're
+        // getting re-created when the toolbar customization closes.
+        this._teardown();
 
-      <!--
-         - Actually constructs the menu-items based on the folders given.
-         -
-         - @param aFolders  An array of nsIMsgFolders to use for building.
-        -->
-      <method name="_build">
-        <parameter name="aFolders"/>
-        <body><![CDATA[
-          let folders;
-          let excludeServers = [];
-          let disableServers = [];
+        // Store our current width and set a safe small width when we show
+        // in a wrapper.
+        if (!this.hasAttribute("original-width")) {
+          this.setAttribute("original-width", this.hasAttribute("width") ?
+            this.getAttribute("width") : "none");
+          this.setAttribute("width", "100");
+        }
+      }
+    }
 
-          // excludeServers attribute is a comma separated list of server keys.
-          if (this.hasAttribute("excludeServers"))
-            excludeServers = this.getAttribute("excludeServers").split(",");
+    set parentFolder(val) {
+      return this._parentFolder = val;
+    }
+
+    get parentFolder() {
+      return this._parentFolder;
+    }
 
-          // disableServers attribute is a comma separated list of server keys.
-          if (this.hasAttribute("disableServers"))
-            disableServers = this.getAttribute("disableServers").split(",");
+    /**
+     * Make sure we remove our listener when the window is being destroyed
+     * or the widget teared down.
+     */
+    _removeListener() {
+      if (!this._initialized) {
+        return;
+      }
+      MailServices.mailSession.RemoveFolderListener(this._listener);
+    }
 
-          // Extensions and other consumers can add to these modes too, see the
-          // above note on the _filters field.
-          var mode = this.getAttribute("mode");
-          if (mode && mode != "") {
-            var filterFunction = this._filters[mode];
-            folders = aFolders.filter(filterFunction);
-            this._listener._filterFunction = filterFunction;
-          } else {
-            folders = aFolders;
-            this._listener._filterFunction = function(folder) { return true; };
-          }
+    /**
+     * Call this if you are unsure whether the menu-items have been built,
+     * but know that they need to be built now if they haven't.
+     */
+    _ensureInitialized() {
+      if (this._initialized) {
+        return;
+      }
 
-          if (excludeServers.length > 0) {
-            folders = folders.filter(function(aFolder) {
-              return !excludeServers.includes(aFolder.server.key);
-            });
-          }
+      let folders;
+      // Figure out which folders to build. If we don't have a parent, then
+      // we assume we should build the top-level accounts. (Actually we
+      // build the fake root folders for those accounts.)
+      if (!this._parentFolder) {
+        let accounts = allAccountsSorted(true);
 
-          /* This code block will do the following: Add a menu item that refers
-             back to the parent folder when there is a showFileHereLabel
-             attribute or no mode attribute. However the code won't add such a
-             menu item if one of the following conditions is met:
-             (*) There is no parent folder
-             (*) Folder is server and showAccountsFileHere is explicitly false
-             (*) Current folder has a mode, the parent folder can be selected,
-                 no messages can be filed into the parent folder (e.g. when the
-                 parent folder is a news group or news server) and the folder
-                 mode is not equal to newFolder
+        // Now generate our folder-list. Note that we'll special case this
+        // situation below, to avoid destroying the sort order we just made.
+        folders = accounts.map(acct => acct.incomingServer.rootFolder);
+      } else {
+        // If we do have a parent folder, then we just build based on those
+        // subFolders for that parent.
+        folders = toArray(fixIterator(this._parentFolder.subFolders,
+          Ci.nsIMsgFolder));
+      }
+
+      this._build(folders);
+      // Lastly, we add a listener to get notified of changes in the folder
+      // structure.
+      MailServices.mailSession.AddFolderListener(this._listener,
+        Ci.nsIFolderListener.all);
 
-             The menu item will have the value of the fileHereLabel attribute as
-             label or if the attribute does not exist the name of the parent
-             folder instead.
-          */
-          let parent = this._parentFolder;
-          if (parent && (this.getAttribute("showFileHereLabel") == "true" || !mode)) {
-            let showAccountsFileHere = this.getAttribute("showAccountsFileHere");
-            if ((!parent.isServer || showAccountsFileHere != "false") &&
-                (!mode || mode == "newFolder" || parent.noSelect ||
-                 parent.canFileMessages || showAccountsFileHere == "true")) {
-              var menuitem = document.createXULElement("menuitem");
-              menuitem._folder = this._parentFolder;
-              menuitem.setAttribute("generated", "true");
-              if (this.hasAttribute("fileHereLabel")) {
-                menuitem.setAttribute("label", this.getAttribute("fileHereLabel"));
-                menuitem.setAttribute("accesskey", this.getAttribute("fileHereAccessKey"));
-              } else {
-                menuitem.setAttribute("label", this._parentFolder.prettyName);
-                menuitem.setAttribute("class", "folderMenuItem menuitem-iconic");
-                this._setCssSelectors(this._parentFolder, menuitem);
-              }
-              // Eww. have to support some legacy code here...
-              menuitem.setAttribute("id", this._parentFolder.URI);
-              this.appendChild(menuitem);
+      this._initialized = true;
+    }
 
-              if (this._parentFolder.noSelect)
-                menuitem.setAttribute("disabled", "true");
+    /**
+     * Actually constructs the menu-items based on the folders given.
+     *
+     * @param [nsIMsgFolder] inputFolders - An array of nsIMsgFolders to use for building.
+     */
+    // eslint-disable-next-line complexity
+    _build(inputFolders) {
+      let folders;
+      let excludeServers = [];
+      let disableServers = [];
 
-              let sep = document.createXULElement("menuseparator");
-              sep.setAttribute("generated", "true");
-              this.appendChild(sep);
-            }
-          }
+      // excludeServers attribute is a comma separated list of server keys.
+      if (this.hasAttribute("excludeServers")) {
+        excludeServers = this.getAttribute("excludeServers").split(",");
+      }
+
+      // disableServers attribute is a comma separated list of server keys.
+      if (this.hasAttribute("disableServers")) {
+        disableServers = this.getAttribute("disableServers").split(",");
+      }
 
-          let globalInboxFolder = null;
-          // See if this is the toplevel menu (usually with accounts).
-          if (!this._parentFolder) {
-            let addSeparator = false;
-            // Some menus want a "Recent" option, but that should only be on our
-            // top-level menu.
-            if (this.getAttribute("showRecent") == "true") {
-              this._buildSpecialMenu("recent");
-              addSeparator = true;
-            }
-            if (this.getAttribute("showFavorites") == "true") {
-              this._buildSpecialMenu("favorites");
-              addSeparator = true;
-            }
-            if (addSeparator) {
-              // If we added Recent and/or Favorites, separate them from the rest of the items.
-              let sep = document.createXULElement("menuseparator");
-              sep.setAttribute("generated", "true");
-              this.appendChild(sep);
-            }
-            // If we are showing the accounts for deferring, move Local Folders to the top.
-            if (mode == "deferred") {
-              globalInboxFolder = this.MailServices.accounts.localFoldersServer
-                                                            .rootFolder;
-              let localFoldersIndex = folders.indexOf(globalInboxFolder);
-              if (localFoldersIndex != -1) {
-                folders.splice(localFoldersIndex, 1);
-                folders.unshift(globalInboxFolder);
-              }
-            }
-            // If we're the root of the folder hierarchy, then we actually don't
-            // want to sort the folders, but rather the accounts to which the
-            // folders belong.  Since that sorting was already done, we don't need
-            // to do anything for that case here.
+      // Extensions and other consumers can add to these modes too, see the
+      // above note on the _filters field.
+      let mode = this.getAttribute("mode");
+      if (mode && mode != "") {
+        let filterFunction = this._filters[mode];
+        folders = inputFolders.filter(filterFunction);
+        this._listener._filterFunction = filterFunction;
+      } else {
+        folders = inputFolders;
+        this._listener._filterFunction = function(folder) {
+          return true;
+        };
+      }
+      if (excludeServers.length > 0) {
+        folders = folders.filter(function(aFolder) {
+          return !excludeServers.includes(aFolder.server.key);
+        });
+      }
+      // This code block will do the following: Add a menu item that refers
+      // back to the parent folder when there is a showFileHereLabel
+      // attribute or no mode attribute. However the code won't add such a
+      // menu item if one of the following conditions is met:
+      // (-) There is no parent folder.
+      // (-) Folder is server and showAccountsFileHere is explicitly false.
+      // (-) Current folder has a mode, the parent folder can be selected,
+      //     no messages can be filed into the parent folder (e.g. when the
+      //     parent folder is a news group or news server) and the folder
+      //     mode is not equal to newFolder.
+      //  The menu item will have the value of the fileHereLabel attribute as
+      //  label or if the attribute does not exist the name of the parent
+      //  folder instead.
+      let parent = this._parentFolder;
+      if (parent && (this.getAttribute("showFileHereLabel") == "true" || !mode)) {
+        let showAccountsFileHere = this.getAttribute("showAccountsFileHere");
+        if ((!parent.isServer || showAccountsFileHere != "false") &&
+          (!mode || mode == "newFolder" || parent.noSelect ||
+            parent.canFileMessages || showAccountsFileHere == "true")) {
+          let menuitem = document.createXULElement("menuitem");
+          menuitem._folder = this._parentFolder;
+          menuitem.setAttribute("generated", "true");
+          if (this.hasAttribute("fileHereLabel")) {
+            menuitem.setAttribute("label", this.getAttribute("fileHereLabel"));
+            menuitem.setAttribute("accesskey", this.getAttribute("fileHereAccessKey"));
           } else {
-            // Sorts the list of folders. We give first priority to the sortKey
-            // property if it is available, otherwise a case-insensitive
-            // comparison of names.
-            folders = folders.sort((a, b) => a.compareSortKeys(b));
+            menuitem.setAttribute("label", this._parentFolder.prettyName);
+            menuitem.setAttribute("class", "folderMenuItem menuitem-iconic");
+            this._setCssSelectors(this._parentFolder, menuitem);
+          }
+          // Eww. have to support some legacy code here.
+          menuitem.setAttribute("id", this._parentFolder.URI);
+          this.appendChild(menuitem);
+
+          if (this._parentFolder.noSelect) {
+            menuitem.setAttribute("disabled", "true");
           }
 
-          /* In some cases, the user wants to have a list of subfolders for only
-           * some account types (or maybe all of them). So we use this to
-           * determine what the user wanted.
-           */
-           var shouldExpand;
-           var labels = null;
-           if (this.getAttribute("expandFolders") == "true" ||
-               !this.hasAttribute("expandFolders")) {
-             shouldExpand = () => true;
-           } else if (this.getAttribute("expandFolders") == "false") {
-             shouldExpand = () => false;
-           } else {
-             /* We want a subfolder list for only some servers. We also may need
-              * to create headers to select the servers. If so, then headlabels
-              * is a comma-delimited list of labels corresponding to the server
-              * types specified in expandFolders.
-              */
-             var types = this.getAttribute("expandFolders").split(/ *, */);
-             // Set the labels. labels[type] = label
-             if (this.hasAttribute("headlabels")) {
-               var labelNames = this.getAttribute("headlabels").split(/ *, */);
-               labels = {};
-               // If the length isn't equal, don't give them any of the labels,
-               // since any combination will probably be wrong.
-               if (labelNames.length == types.length) {
-                 for (var index in types)
-                   labels[types[index]] = labelNames[index];
-               }
-             }
-             shouldExpand = (e) => types.includes(e);
-           }
+          let sep = document.createXULElement("menuseparator");
+          sep.setAttribute("generated", "true");
+          this.appendChild(sep);
+        }
+      }
 
-          // We need to call this, or hasSubFolders will always return false.
-          // Remove this workaround when Bug 502900 is fixed.
-          this.MailUtils.discoverFolders();
-          this._serversOnly = true;
-
-          for (let folder of folders) {
-            let node;
-            if (!folder.isServer)
-              this._serversOnly = false;
+      let globalInboxFolder = null;
+      // See if this is the toplevel menu (usually with accounts).
+      if (!this._parentFolder) {
+        let addSeparator = false;
 
-            // If we're going to add subFolders, we need to make menus, not
-            // menuitems.
-            if (!folder.hasSubFolders || !shouldExpand(folder.server.type)) {
-              node = document.createXULElement("menuitem");
-              // Grumble, grumble, legacy code support
-              node.setAttribute("id", folder.URI);
-              node.setAttribute("class", "folderMenuItem menuitem-iconic");
-              node.setAttribute("generated", "true");
-              this.appendChild(node);
-            } else {
-              this._serversOnly = false;
-              // xxx this is slightly problematic in that we haven't confirmed
-              //     whether any of the subfolders will pass the filter
-              node = document.createXULElement("menu");
-              node.setAttribute("class", "folderMenuItem menu-iconic");
-              node.setAttribute("generated", "true");
-              this.appendChild(node);
-
-              // Create the submenu
-              // (We must use cloneNode here because on OS X the native menu
-              // functionality and very sad limitations of XBL1 cause the bindings
-              // to never get created for popup if we create a new element.  We
-              // perform a shallow clone to avoid picking up any of our children.)
-              var popup = this.cloneNode(false);
-              popup._parentFolder = folder;
-              popup.setAttribute("class", this.getAttribute("class"));
-              popup.setAttribute("type", this.getAttribute("type"));
-              if (this.hasAttribute("fileHereLabel"))
-                popup.setAttribute("fileHereLabel",
-                                   this.getAttribute("fileHereLabel"));
-              popup.setAttribute("showFileHereLabel",
-                                 this.getAttribute("showFileHereLabel"));
-              popup.setAttribute("oncommand",
-                                 this.getAttribute("oncommand"));
-              popup.setAttribute("mode",
-                                 this.getAttribute("mode"));
-              if (this.hasAttribute("disableServers"))
-                popup.setAttribute("disableServers",
-                                   this.getAttribute("disableServers"));
-              if (this.hasAttribute("position"))
-                popup.setAttribute("position",
-                                   this.getAttribute("position"));
-
-              // If there are labels, add the labels now
-              if (labels) {
-                var serverNode = document.createXULElement("menuitem");
-                serverNode.setAttribute("label", labels[folder.server.type]);
-                serverNode._folder = folder;
-                serverNode.setAttribute("generated", "true");
-                popup.appendChild(serverNode);
-                let sep = document.createXULElement("menuseparator");
-                sep.setAttribute("generated", "true");
-                popup.appendChild(sep);
-              }
+        // Some menus want a "Recent" option, but that should only be on our
+        // top-level menu.
+        if (this.getAttribute("showRecent") == "true") {
+          this._buildSpecialMenu("recent");
+          addSeparator = true;
+        }
+        if (this.getAttribute("showFavorites") == "true") {
+          this._buildSpecialMenu("favorites");
+          addSeparator = true;
+        }
+        if (addSeparator) {
+          // If we added Recent and/or Favorites, separate them from the rest of the items.
+          let sep = document.createXULElement("menuseparator");
+          sep.setAttribute("generated", "true");
+          this.appendChild(sep);
+        }
+        // If we are showing the accounts for deferring, move Local Folders to the top.
+        if (mode == "deferred") {
+          globalInboxFolder = MailServices.accounts.localFoldersServer
+            .rootFolder;
+          let localFoldersIndex = folders.indexOf(globalInboxFolder);
+          if (localFoldersIndex != -1) {
+            folders.splice(localFoldersIndex, 1);
+            folders.unshift(globalInboxFolder);
+          }
+        }
+        // If we're the root of the folder hierarchy, then we actually don't
+        // want to sort the folders, but rather the accounts to which the
+        // folders belong. Since that sorting was already done, we don't need
+        // to do anything for that case here.
+      } else {
+        // Sorts the list of folders. We give first priority to the sortKey
+        // property if it is available, otherwise a case-insensitive
+        // comparison of names.
+        folders = folders.sort((a, b) => a.compareSortKeys(b));
+      }
 
-              popup.setAttribute("generated", "true");
-              node.appendChild(popup);
+      // In some cases, the user wants to have a list of subfolders for only
+      // some account types (or maybe all of them). So we use this to
+      // determine what the user wanted.
+      let shouldExpand;
+      let labels = null;
+      if (this.getAttribute("expandFolders") == "true" ||
+        !this.hasAttribute("expandFolders")) {
+        shouldExpand = () => true;
+      } else if (this.getAttribute("expandFolders") == "false") {
+        shouldExpand = () => false;
+      } else {
+        // We want a subfolder list for only some servers. We also may need
+        // to create headers to select the servers. If so, then headlabels
+        // is a comma-delimited list of labels corresponding to the server
+        // types specified in expandFolders.
+        let types = this.getAttribute("expandFolders").split(/ *, */);
+        // Set the labels. labels[type] = label
+        if (this.hasAttribute("headlabels")) {
+          let labelNames = this.getAttribute("headlabels").split(/ *, */);
+          labels = {};
+          // If the length isn't equal, don't give them any of the labels,
+          // since any combination will probably be wrong.
+          if (labelNames.length == types.length) {
+            for (let index in types) {
+              labels[types[index]] = labelNames[index];
             }
-
-            if (disableServers.includes(folder.server.key))
-              node.setAttribute("disabled", "true");
-
-            node._folder = folder;
-            let label = "";
-            if (mode == "deferred" && folder.isServer &&
-                folder.server.rootFolder == globalInboxFolder) {
-              label = this._stringBundle.get("globalInbox", [folder.prettyName]);
-            } else {
-              label = folder.prettyName;
-            }
-            node.setAttribute("label", label);
-            this._setCssSelectors(folder, node);
           }
-        ]]></body>
-      </method>
+        }
+        shouldExpand = (e) => types.includes(e);
+      }
 
-      <!--
-         - This only builds the menu item in the top-level menulist.
-         - The real submenu will be created once the popup is really shown,
-         - via the _buildSpecialSubmenu method.
-         -->
-      <method name="_buildSpecialMenu">
-        <parameter name="type"/>
-        <body><![CDATA[
-          // Now create the Recent folder and its children
-          let menu = document.createXULElement("menu");
-          if (type == "recent") {
-            menu.setAttribute("label", this.getAttribute("recentLabel"));
-            menu.setAttribute("accesskey", this.getAttribute("recentAccessKey"));
-          } else {
-            menu.setAttribute("label", this.getAttribute("favoritesLabel"));
-            menu.setAttribute("accesskey", this.getAttribute("favoritesAccessKey"));
-          }
-          menu.setAttribute("special", type);
-          menu.setAttribute("generated", "true");
-
-          let popup = document.createXULElement("menupopup");
-          popup.setAttribute("class", this.getAttribute("class"));
-          popup.addEventListener("popupshowing",
-                                 () => this._buildSpecialSubmenu(menu),
-                                 { once: true });
+      // We need to call this, or hasSubFolders will always return false.
+      // Remove this workaround when Bug 502900 is fixed.
+      MailUtils.discoverFolders();
+      this._serversOnly = true;
 
-          menu.appendChild(popup);
-          this.appendChild(menu);
-        ]]></body>
-      </method>
-
-      <!--
-         - Builds a submenu with all of the recently used folders in it, to
-         - allow for easy access.
-        -->
-      <method name="_buildSpecialSubmenu">
-        <parameter name="menu"/>
-        <body><![CDATA[
-          let specialType = menu.getAttribute("special");
-          if (this._initializedSpecials.has(specialType))
-            return;
-
-          // Iterate through all folders in all accounts matching the current filter.
-          let specialFolders = this.toArray(
-            this.fixIterator(this.MailServices.accounts.allFolders, Ci.nsIMsgFolder));
-          if (this._listener._filterFunction)
-            specialFolders = specialFolders.filter(this._listener._filterFunction);
+      for (let folder of folders) {
+        let node;
+        if (!folder.isServer) {
+          this._serversOnly = false;
+        }
 
-          switch (specialType) {
-            case "recent":
-              // Find 15 (_MAXRECENT) of most recently modified ones.
-              specialFolders = this.getMostRecentFolders(specialFolders,
-                                                         this._MAXRECENT,
-                                                         "MRMTime");
-              break;
-            case "favorites":
-              specialFolders = specialFolders.filter(folder => folder.getFlag(Ci.nsMsgFolderFlags.Favorite));
-              break;
-          }
+        // If we're going to add subFolders, we need to make menus, not
+        // menuitems.
+        if (!folder.hasSubFolders || !shouldExpand(folder.server.type)) {
+          node = document.createXULElement("menuitem");
+          // Grumble, grumble, legacy code support
+          node.setAttribute("id", folder.URI);
+          node.setAttribute("class", "folderMenuItem menuitem-iconic");
+          node.setAttribute("generated", "true");
+          this.appendChild(node);
+        } else {
+          this._serversOnly = false;
+          // xxx this is slightly problematic in that we haven't confirmed
+          //     whether any of the subfolders will pass the filter.
+          node = document.createXULElement("menu");
+          node.setAttribute("class", "folderMenuItem menu-iconic");
+          node.setAttribute("generated", "true");
+          this.appendChild(node);
 
-          // Cache the pretty names so that they do not need to be fetched
-          // _MAXRECENT^2 times later.
-          let specialFoldersMap = specialFolders.map(function(f) {
-            return {
-              folder: f,
-              name: f.prettyName,
-            };
-          });
-
-          // Because we're scanning across multiple accounts, we can end up with
-          // several folders with the same name. Find those dupes.
-          let dupeNames = new Set();
-          for (let i = 0; i < specialFoldersMap.length; i++) {
-            for (let j = i + 1; j < specialFoldersMap.length; j++) {
-              if (specialFoldersMap[i].name == specialFoldersMap[j].name)
-                dupeNames.add(specialFoldersMap[i].name);
-            }
+          // Create the submenu.
+          let popup = document.createXULElement("menupopup", { "is": "folder-menupopup" });
+          popup._parentFolder = folder;
+          popup.setAttribute("class", this.getAttribute("class"));
+          popup.setAttribute("type", this.getAttribute("type"));
+          if (this.hasAttribute("fileHereLabel")) {
+            popup.setAttribute("fileHereLabel",
+              this.getAttribute("fileHereLabel"));
+          }
+          popup.setAttribute("showFileHereLabel",
+            this.getAttribute("showFileHereLabel"));
+          popup.setAttribute("oncommand",
+            this.getAttribute("oncommand"));
+          popup.setAttribute("mode",
+            this.getAttribute("mode"));
+          if (this.hasAttribute("disableServers")) {
+            popup.setAttribute("disableServers",
+              this.getAttribute("disableServers"));
+          }
+          if (this.hasAttribute("position")) {
+            popup.setAttribute("position",
+              this.getAttribute("position"));
           }
 
-          for (let folderItem of specialFoldersMap) {
-            // If this folder name appears multiple times in the recent list,
-            // append the server name to disambiguate.
-            // TODO:
-            // - maybe this could use verboseFolderFormat from messenger.properties
-            //   instead of hardcoded " - ".
-            // - disambiguate folders with same name in same account
-            //   (in different subtrees).
-            let label = folderItem.name;
-            if (dupeNames.has(label))
-              label += " - " + folderItem.folder.server.prettyName;
+          // If there are labels, add the labels now.
+          if (labels) {
+            let serverNode = document.createXULElement("menuitem");
+            serverNode.setAttribute("label", labels[folder.server.type]);
+            serverNode._folder = folder;
+            serverNode.setAttribute("generated", "true");
+            popup.appendChild(serverNode);
+            let sep = document.createXULElement("menuseparator");
+            sep.setAttribute("generated", "true");
+            popup.appendChild(sep);
+          }
+          popup.setAttribute("generated", "true");
+          node.appendChild(popup);
+        }
 
-            folderItem.label = label;
-          }
+        if (disableServers.includes(folder.server.key)) {
+          node.setAttribute("disabled", "true");
+        }
 
-          // Make sure the entries are sorted alphabetically.
-          specialFoldersMap.sort((a, b) => this.folderNameCompare(a.label, b.label));
+        node._folder = folder;
+        let label = "";
+        if (mode == "deferred" && folder.isServer &&
+          folder.server.rootFolder == globalInboxFolder) {
+          label = this._stringBundle.get("globalInbox", [folder.prettyName]);
+        } else {
+          label = folder.prettyName;
+        }
+        node.setAttribute("label", label);
+        this._setCssSelectors(folder, node);
+      }
+    }
 
-          // Create entries for each of the recent folders.
-          for (let folderItem of specialFoldersMap) {
-            let node = document.createXULElement("menuitem");
-
-            node.setAttribute("label", folderItem.label);
-            node._folder = folderItem.folder;
+    /**
+     * This only builds the menu item in the top-level menulist.
+     * The real submenu will be created once the popup is really shown,
+     * via the _buildSpecialSubmenu method.
+     *
+     * @param {string} type - the type of the special menu to build.
+     */
+    _buildSpecialMenu(type) {
+       // Now create the Recent folder and its children.
+      let menu = document.createXULElement("menu");
+      if (type == "recent") {
+        menu.setAttribute("label", this.getAttribute("recentLabel"));
+        menu.setAttribute("accesskey", this.getAttribute("recentAccessKey"));
+      } else {
+        menu.setAttribute("label", this.getAttribute("favoritesLabel"));
+        menu.setAttribute("accesskey", this.getAttribute("favoritesAccessKey"));
+      }
+      menu.setAttribute("special", type);
+      menu.setAttribute("generated", "true");
 
-            node.setAttribute("class", "folderMenuItem menuitem-iconic");
-            this._setCssSelectors(folderItem.folder, node);
-            node.setAttribute("generated", "true");
-            menu.menupopup.appendChild(node);
-          }
+      let popup = document.createXULElement("menupopup");
+      popup.setAttribute("class", this.getAttribute("class"));
+      popup.addEventListener("popupshowing", (event) => {
+        this._buildSpecialSubmenu(menu);
+      }, { once: true });
+
+      menu.appendChild(popup);
+      this.appendChild(menu);
+    }
 
-          if (specialFoldersMap.length == 0)
-            menu.setAttribute("disabled", "true");
-
-          this._initializedSpecials.add(specialType);
-        ]]></body>
-      </method>
+    /**
+     * Builds a submenu with all of the recently used folders in it, to
+     * allow for easy access.
+     *
+     * @param {Menu} menu - the menu for which one wants to build the special sub menu.
+     */
+    _buildSpecialSubmenu(menu) {
+      let specialType = menu.getAttribute("special");
+      if (this._initializedSpecials.has(specialType)) {
+        return;
+      }
 
-      <!--
-         - This function adds attributes on menu/menuitems to make it easier for
-         - css to style them.
-         -
-         - @param aFolder    the folder that corresponds to the menu/menuitem
-         - @param aMenuNode  the actual DOM node to set attributes on
-        -->
-      <method name="_setCssSelectors">
-        <parameter name="aFolder"/>
-        <parameter name="aMenuNode"/>
-        <body><![CDATA[
+      // Iterate through all folders in all accounts matching the current filter.
+      let specialFolders = toArray(
+        fixIterator(MailServices.accounts.allFolders, Ci.nsIMsgFolder));
+      if (this._listener._filterFunction) {
+        specialFolders = specialFolders.filter(this._listener._filterFunction);
+      }
+
+      switch (specialType) {
+        case "recent":
+          // Find 15 (_MAXRECENT) of most recently modified ones.
+          specialFolders = getMostRecentFolders(specialFolders,
+            this._MAXRECENT,
+            "MRMTime");
+          break;
+        case "favorites":
+          specialFolders = specialFolders.filter(folder => folder.getFlag(Ci.nsMsgFolderFlags.Favorite));
+          break;
+      }
 
-          // First set the SpecialFolder attribute
-          aMenuNode.setAttribute("SpecialFolder", this.getSpecialFolderString(aFolder));
+      // Cache the pretty names so that they do not need to be fetched
+      // _MAXRECENT^2 times later.
+      let specialFoldersMap = specialFolders.map(function(f) {
+        return {
+          folder: f,
+          name: f.prettyName,
+        };
+      });
 
-          // Now set the biffState
-          let biffStates = ["NewMail", "NoMail", "UnknownMail"];
-          for (let state of biffStates) {
-            if (aFolder.biffState ==
-                Ci.nsIMsgFolder["nsMsgBiffState_" + state]) {
-              aMenuNode.setAttribute("BiffState", state);
-              break;
-            }
+      // Because we're scanning across multiple accounts, we can end up with
+      // several folders with the same name. Find those dupes.
+      let dupeNames = new Set();
+      for (let i = 0; i < specialFoldersMap.length; i++) {
+        for (let j = i + 1; j < specialFoldersMap.length; j++) {
+          if (specialFoldersMap[i].name == specialFoldersMap[j].name) {
+            dupeNames.add(specialFoldersMap[i].name);
           }
+        }
+      }
 
-          aMenuNode.setAttribute("IsServer", aFolder.isServer);
-          aMenuNode.setAttribute("IsSecure", aFolder.server.isSecure);
-          aMenuNode.setAttribute("ServerType", aFolder.server.type);
-          aMenuNode.setAttribute("IsFeedFolder", !!this.FeedUtils.getFeedUrlsInFolder(aFolder));
-        ]]></body>
-      </method>
+      for (let folderItem of specialFoldersMap) {
+        // If this folder name appears multiple times in the recent list,
+        // append the server name to disambiguate.
+        // TODO:
+        //  - maybe this could use verboseFolderFormat from messenger.properties
+        //  instead of hardcoded " - ".
+        //  - disambiguate folders with same name in same account
+        //  (in different subtrees).
+        let label = folderItem.name;
+        if (dupeNames.has(label)) {
+          label += " - " + folderItem.folder.server.prettyName;
+        }
+
+        folderItem.label = label;
+      }
+
+      // Make sure the entries are sorted alphabetically.
+      specialFoldersMap.sort((a, b) => folderNameCompare(a.label, b.label));
 
-      <!--
-         - This function returns a formatted display name for a menulist
-         - selected folder.  The desired format is set as the 'displayformat'
-         - attribute of the folderpicker's <menulist>, one of:
-         - 'name' (default) - Folder
-         - 'verbose'        - Folder on Account
-         - 'path'           - Account/Folder/Subfolder
-         -
-         - @param aFolder    the folder that corresponds to the menu/menuitem
-         - @return string    display name
-        -->
-      <field name="_displayformat">null</field>
-      <method name="getDisplayName">
-        <parameter name="aFolder"/>
-        <body><![CDATA[
-          if (aFolder.isServer)
-            return aFolder.prettyName;
+      // Create entries for each of the recent folders.
+      for (let folderItem of specialFoldersMap) {
+        let node = document.createXULElement("menuitem");
+
+        node.setAttribute("label", folderItem.label);
+        node._folder = folderItem.folder;
 
-          if (this._displayformat == "verbose")
-            return this._stringBundle.getFormattedString("verboseFolderFormat",
-                     [aFolder.prettyName, aFolder.server.prettyName]);
+        node.setAttribute("class", "folderMenuItem menuitem-iconic");
+        this._setCssSelectors(folderItem.folder, node);
+        node.setAttribute("generated", "true");
+        menu.menupopup.appendChild(node);
+      }
 
-          if (this._displayformat == "path")
-            return this.FeedUtils.getFolderPrettyPath(aFolder) || aFolder.name;
+      if (specialFoldersMap.length == 0) {
+        menu.setAttribute("disabled", "true");
+      }
 
-          return aFolder.name;
-        ]]></body>
-      </method>
+      this._initializedSpecials.add(specialType);
+    }
 
-      <!--
-         - Makes a given folder selected.
-         -
-         - @param aFolder  the folder to select (if none, then Choose Folder)
-         - @note  If aFolder is not in this popup, but is instead a descendant of
-         -        a member of the popup, that ancestor will be selected.
-         - @return  true if any usable folder was found, otherwise false.
-        -->
-      <method name="selectFolder">
-        <parameter name="aFolder"/>
-        <body><![CDATA[
-          // Set the label of the menulist element as if aFolder had been selected.
-          function setupParent(aFolder, aMenulist, aNoFolders) {
-            let menupopup = aMenulist.menupopup;
-            if (aFolder) {
-              aMenulist.setAttribute("label", menupopup.getDisplayName(aFolder));
-            } else if (aNoFolders) {
-              aMenulist.setAttribute("label", menupopup._stringBundle.getString("noFolders"));
-            } else if (menupopup._serversOnly) {
-              aMenulist.setAttribute("label", menupopup._stringBundle.getString("chooseAccount"));
-            } else {
-              aMenulist.setAttribute("label", menupopup._stringBundle.getString("chooseFolder"));
-            }
-            aMenulist.setAttribute("value",
-              aFolder ? aFolder.URI : "");
-            aMenulist.setAttribute("IsServer",
-              aFolder ? aFolder.isServer : false);
-            aMenulist.setAttribute("IsSecure",
-              aFolder ? aFolder.server.isSecure : false);
-            aMenulist.setAttribute("ServerType",
-              aFolder ? aFolder.server.type : "none");
-            aMenulist.setAttribute("SpecialFolder",
-              aFolder ? menupopup.getSpecialFolderString(aFolder) : "none");
-            aMenulist.setAttribute("IsFeedFolder", Boolean(
-              aFolder && menupopup.FeedUtils.getFeedUrlsInFolder(aFolder)));
-          }
+    /**
+     * This function adds attributes on menu/menuitems to make it easier for
+     * css to style them.
+     *
+     * @param {nsIMsgFolder} folder - the folder that corresponds to the menu/menuitem
+     * @param {Menu} menuNode       - the actual DOM node to set attributes on
+     */
+    _setCssSelectors(folder, menuNode) {
+      // First set the SpecialFolder attribute.
+      menuNode.setAttribute("SpecialFolder", getSpecialFolderString(folder));
+
+      // Now set the biffState.
+      let biffStates = ["NewMail", "NoMail", "UnknownMail"];
+      for (let state of biffStates) {
+        if (folder.biffState ==
+          Ci.nsIMsgFolder["nsMsgBiffState_" + state]) {
+          menuNode.setAttribute("BiffState", state);
+          break;
+        }
+      }
+
+      menuNode.setAttribute("IsServer", folder.isServer);
+      menuNode.setAttribute("IsSecure", folder.server.isSecure);
+      menuNode.setAttribute("ServerType", folder.server.type);
+      menuNode.setAttribute("IsFeedFolder", !!FeedUtils.getFeedUrlsInFolder(folder));
+    }
 
-          let folder;
-          if (aFolder) {
-            for (let child of this.childNodes) {
-              if (child && child._folder && !child.disabled &&
-                  (child._folder.URI == aFolder.URI ||
-                   (child.tagName == "menu" &&
-                    child._folder.isAncestorOf(aFolder)))) {
-                if (child._folder.URI == aFolder.URI)
-                  this.parentNode.selectedItem = child;
-                folder = aFolder;
-                break;
-              }
-            }
-          }
+    /**
+     * This function returns a formatted display name for a menulist
+     * selected folder. The desired format is set as the 'displayformat'
+     * attribute of the folderpicker's <menulist>, one of:
+     * 'name' (default) - Folder
+     * 'verbose'        - Folder on Account
+     * 'path'           - Account/Folder/Subfolder
+     *
+     * @param {nsIMsgFolder} folder - the folder that corresponds to the menu/menuitem
+     * @return {string}             - display name
+     */
+    getDisplayName(folder) {
+      if (folder.isServer) {
+        return folder.prettyName;
+      }
+
+      if (this._displayformat == "verbose") {
+        return this._stringBundle.getFormattedString("verboseFolderFormat",
+          [folder.prettyName, folder.server.prettyName]);
+      }
+
+      if (this._displayformat == "path") {
+        return FeedUtils.getFolderPrettyPath(folder) || folder.name;
+      }
+
+      return folder.name;
+    }
 
-          // If the caller specified a folder to select and it was not
-          // found, or if the caller didn't pass a folder (meaning a logical
-          // and valid folder wasn't determined), don't blow up but reset
-          // attributes and set a nice Choose Folder label so the user may
-          // select a valid folder per the filter for this picker. If there are
-          // no children, then no folder passed the filter; disable the menulist
-          // as there's nothing to choose from.
-          let noFolders;
-          if (!this.childElementCount) {
-            this.parentNode.setAttribute("disabled", true);
-            noFolders = true;
-          } else {
-            this.parentNode.removeAttribute("disabled");
-            noFolders = false;
+    /**
+     * Makes a given folder selected.
+     *
+     * @param {nsIMsgFolder} inputFolder - the folder to select (if none, then Choose Folder)
+     * @return {boolean}                 - true if any usable folder was found, otherwise false.
+     * @note  If inputFolder is not in this popup, but is instead a descendant of
+     *        a member of the popup, that ancestor will be selected.
+     */
+    selectFolder(inputFolder) {
+      // Set the label of the menulist element as if folder had been selected.
+      function setupParent(folder, menulist, noFolders) {
+        let menupopup = menulist.menupopup;
+        if (folder) {
+          menulist.setAttribute("label", menupopup.getDisplayName(folder));
+        } else if (noFolders) {
+          menulist.setAttribute("label", menupopup._stringBundle.getString("noFolders"));
+        } else if (menupopup._serversOnly) {
+          menulist.setAttribute("label", menupopup._stringBundle.getString("chooseAccount"));
+        } else {
+          menulist.setAttribute("label", menupopup._stringBundle.getString("chooseFolder"));
+        }
+        menulist.setAttribute("value",
+          folder ? folder.URI : "");
+        menulist.setAttribute("IsServer",
+          folder ? folder.isServer : false);
+        menulist.setAttribute("IsSecure",
+          folder ? folder.server.isSecure : false);
+        menulist.setAttribute("ServerType",
+          folder ? folder.server.type : "none");
+        menulist.setAttribute("SpecialFolder",
+          folder ? getSpecialFolderString(folder) : "none");
+        menulist.setAttribute("IsFeedFolder", Boolean(
+          folder && FeedUtils.getFeedUrlsInFolder(folder)));
+      }
+
+      let folder;
+      if (inputFolder) {
+        for (let child of this.childNodes) {
+          if (child && child._folder && !child.disabled &&
+            (child._folder.URI == inputFolder.URI ||
+              (child.tagName == "menu" &&
+                child._folder.isAncestorOf(inputFolder)))) {
+            if (child._folder.URI == inputFolder.URI) {
+              this.parentNode.selectedItem = child;
+            }
+            folder = inputFolder;
+            break;
           }
-
-          setupParent(folder, this.parentNode, noFolders);
-          return !!folder;
-        ]]></body>
-      </method>
-
-      <!--
-         - Removes all menu-items for this popup, resets all fields, and
-         - removes the listener.  This function is invoked when a change
-         - that affects this menu is detected by our listener.
-        -->
-      <method name="_teardown">
-        <body><![CDATA[
-          if (!this._initialized)
-            return;
+        }
+      }
 
-          for (let i = this.childNodes.length - 1; i >= 0; i--) {
-            let child = this.childNodes[i];
-            if (child.getAttribute("generated") != "true")
-              continue;
-            if ("_teardown" in child)
-              child._teardown();
-            child.remove();
-          }
+      // If the caller specified a folder to select and it was not
+      // found, or if the caller didn't pass a folder (meaning a logical
+      // and valid folder wasn't determined), don't blow up but reset
+      // attributes and set a nice Choose Folder label so the user may
+      // select a valid folder per the filter for this picker. If there are
+      // no children, then no folder passed the filter; disable the menulist
+      // as there's nothing to choose from.
+      let noFolders;
+      if (!this.childElementCount) {
+        this.parentNode.setAttribute("disabled", true);
+        noFolders = true;
+      } else {
+        this.parentNode.removeAttribute("disabled");
+        noFolders = false;
+      }
 
-          this._removeListener();
+      setupParent(folder, this.parentNode, noFolders);
+      return !!folder;
+    }
 
-          this._initialized = false;
-          this._initializedSpecials.clear();
-        ]]></body>
-      </method>
-    </implementation>
+    /**
+     * Removes all menu-items for this popup, resets all fields, and
+     * removes the listener. This function is invoked when a change
+     * that affects this menu is detected by our listener.
+     */
+    _teardown() {
+      if (!this._initialized) {
+        return;
+      }
 
-    <handlers>
-      <!--
-         - In order to improve performance, we're not going to build any of the
-         - menu until we're shown (unless we're the child of a menulist, see
-         - note in the constructor).
-         -
-         - @note _ensureInitialized can be called repeatedly without issue, so
-         -       don't worry about it here.
-        -->
-      <handler event="popupshowing" phase="capturing">
-        this._ensureInitialized();
-      </handler>
-    </handlers>
-  </binding>
-</bindings>
+      for (let i = this.childNodes.length - 1; i >= 0; i--) {
+        let child = this.childNodes[i];
+        if (child.getAttribute("generated") != "true") {
+          continue;
+        }
+        if ("_teardown" in child) {
+          child._teardown();
+        }
+        child.remove();
+      }
+
+      this._removeListener();
+
+      this._initialized = false;
+      this._initializedSpecials.clear();
+    }
+    disconnectedCallback() {
+      // Clean up when being destroyed.
+      this._removeListener();
+      this._teardown();
+    }
+  }
+
+  customElements.define("folder-menupopup", MozFolderMenupopup, { extends: "menupopup" });
+}
--- a/mailnews/base/content/newFolderDialog.xul
+++ b/mailnews/base/content/newFolderDialog.xul
@@ -15,27 +15,29 @@
 <dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
         title="&newFolderDialog.title;"
         onload="onLoad();"
         buttonlabelaccept="&accept.label;"
         buttonaccesskeyaccept="&accept.accesskey;">
 
   <stringbundleset id="stringbundleset"/>
   <script src="chrome://messenger/content/newFolderDialog.js"/>
+  <script src="chrome://messenger/content/folder-menupopup.js"/>
 
     <label value="&name.label;" accesskey="&name.accesskey;" control="name"/>
     <textbox id="name" oninput="doEnabling();"/>
 
     <separator/>
 
     <label value="&description.label;" accesskey="&description.accesskey;" control="msgNewFolderPicker"/>
 
     <menulist id="msgNewFolderPicker" class="folderMenuItem"
                  displayformat="verbose">
-      <menupopup id="MsgNewFolderPopup" type="folder" showFileHereLabel="true"
+      <menupopup is="folder-menupopup" id="MsgNewFolderPopup"
+                 showFileHereLabel="true"
                  class="menulist-menupopup"
                  mode="newFolder"
                  oncommand="onFolderSelect(event)"/>
     </menulist>
 
     <vbox id="newFolderTypeBox">
 
       <separator class="thin"/>
--- a/mailnews/base/content/subscribe.xul
+++ b/mailnews/base/content/subscribe.xul
@@ -20,16 +20,17 @@
         windowtype="mailnews:subscribe">
 
   <stringbundle id="bundle_subscribe" src="chrome://messenger/locale/subscribe.properties"/>
   <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
 
   <script src="chrome://global/content/globalOverlay.js"/>
   <script src="chrome://messenger/content/mailWindow.js"/>
   <script src="chrome://messenger/content/subscribe.js"/>
+  <script src="chrome://messenger/content/folder-menupopup.js"/>
 
   <vbox flex="1" style="width: 40em; height: 25em;">
     <hbox>
       <vbox>
         <hbox pack="end" align="center" flex="1">
           <label value="&server.label;"
                  accesskey="&server.accesskey;"
                  control="serverMenu"/>
@@ -38,17 +39,17 @@
           <label value="&namefield.label;"
                  accesskey="&namefield.accesskey;"
                  id="namefieldlabel"
                  control="namefield"/>
         </hbox>
       </vbox>
       <vbox flex="1">
         <menulist id="serverMenu" flex="1" class="folderMenuItem">
-          <menupopup type="folder"
+          <menupopup is="folder-menupopup"
                      mode="subscribe"
                      expandFolders="false"
                      oncommand="onServerClick(event.target._folder);"/>
         </menulist>
         <textbox is="search-textbox" id="namefield"
                  flex="1"
                  timeout="300"
                  aria-controls="subscribeTree"
--- a/mailnews/base/content/virtualFolderProperties.xul
+++ b/mailnews/base/content/virtualFolderProperties.xul
@@ -55,19 +55,18 @@
         <label value="&folderSelectionCaption.label;"/>
       </hbox>
     </vbox>
     <vbox flex="1">
       <textbox id="name" hidden="true" oninput="doEnabling();"/>
       <textbox id="existingName" readonly="true" hidden="true" tabindex="0"/>
       <menulist id="msgNewFolderPicker" class="folderMenuItem" align="center"
                 displayformat="verbose">
-        <menupopup id="msgNewFolderPopup" class="menulist-menupopup"
-                   type="folder" mode="newFolder"
-                   showFileHereLabel="true" oncommand="onFolderPick(event);"/>
+        <menupopup is="folder-menupopup" id="msgNewFolderPopup" class="menulist-menupopup"
+                   mode="newFolder" showFileHereLabel="true" oncommand="onFolderPick(event);"/>
       </menulist>
       <hbox align="center">
         <label id="chosenFoldersCount"/>
         <spacer flex="1"/>
         <button id="folderListPicker" label="&chooseFoldersButton.label;"
                 accesskey="&chooseFoldersButton.accesskey;"
                 oncommand="chooseFoldersToSearch();"/>
       </hbox>
--- a/mailnews/base/prefs/content/am-copies.inc.xul
+++ b/mailnews/base/prefs/content/am-copies.inc.xul
@@ -1,13 +1,14 @@
 # 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/.
 
   <vbox flex="1" id="copiesAndFolders">
+    <script src="chrome://messenger/content/folder-menupopup.js"/>
     <stringbundleset id="stringbundleset">
     <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
     </stringbundleset>
 
     <label id="identity.fccFolder" hidden="true" wsm_persist="true"
            pref="true" preftype="string" prefattribute="value"
            prefstring="mail.identity.%identitykey%.fcc_folder"/>
     <label id="identity.draftFolder" hidden="true" wsm_persist="true"
@@ -59,25 +60,27 @@
                      accesskey="&sentInOtherFolder.accesskey;"
                      oncommand="setPickersState('msgFccFolderPicker', 'msgFccAccountPicker', event)"/>
             </hbox>
           </vbox>
           <vbox flex="1">
              <menulist id="msgFccAccountPicker"
                        class="folderMenuItem depends-on-do-fcc"
                        aria-labelledby="fcc_selectAccount">
-                <menupopup id="msgFccAccountPopup" type="folder" mode="filing"
+                <menupopup is="folder-menupopup" id="msgFccAccountPopup"
+                           mode="filing"
                            expandFolders="false"
                            oncommand="noteSelectionChange('fcc', 'Account', event)"/>
               </menulist>
               <menulist id="msgFccFolderPicker"
                         class="folderMenuItem depends-on-do-fcc"
                         aria-labelledby="fcc_selectFolder"
                         displayformat="verbose">
-                <menupopup id="msgFccFolderPopup" type="folder" mode="filing"
+                <menupopup is="folder-menupopup" id="msgFccFolderPopup"
+                           mode="filing"
                            class="menulist-menupopup"
                            showFileHereLabel="true"
                            oncommand="noteSelectionChange('fcc', 'Folder', event)"/>
               </menulist>
           </vbox>
         </hbox>
       </radiogroup>
 
@@ -158,25 +161,27 @@
                      accesskey="&archiveInOtherFolder.accesskey;"
                      oncommand="setPickersState('msgArchivesFolderPicker', 'msgArchivesAccountPicker', event)"/>
             </hbox>
           </vbox>
           <vbox flex="1">
             <menulist id="msgArchivesAccountPicker"
                       class="folderMenuItem depends-on-archive"
                       aria-labelledby="archive_selectAccount">
-              <menupopup id="msgArchivesAccountPopup" type="folder" mode="filing"
+              <menupopup is="folder-menupopup" id="msgArchivesAccountPopup"
+                         mode="filing"
                          expandFolders="false"
                          oncommand="noteSelectionChange('archive', 'Account', event)"/>
             </menulist>
             <menulist id="msgArchivesFolderPicker"
                       class="folderMenuItem depends-on-archive"
                       aria-labelledby="archive_selectFolder"
                       displayformat="verbose">
-              <menupopup id="msgArchivesFolderPopup" type="folder" mode="filing"
+              <menupopup is="folder-menupopup" id="msgArchivesFolderPopup"
+                         mode="filing"
                          class="menulist-menupopup"
                          showFileHereLabel="true"
                          oncommand="noteSelectionChange('archive', 'Folder', event)"/>
             </menulist>
           </vbox>
         </hbox>
       </radiogroup>
     </groupbox>
@@ -203,25 +208,27 @@
                      value="1" label="&draftInOtherFolder.label;"
                      accesskey="&draftInOtherFolder.accesskey;"/>
             </hbox>
           </vbox>
           <vbox flex="1">
             <menulist id="msgDraftsAccountPicker"
                       class="folderMenuItem"
                       aria-labelledby="draft_selectAccount">
-              <menupopup id="msgDraftAccountPopup" type="folder" mode="filing"
+              <menupopup is="folder-menupopup" id="msgDraftAccountPopup"
+                         mode="filing"
                          expandFolders="false"
                          oncommand="noteSelectionChange('draft', 'Account', event)"/>
             </menulist>
             <menulist id="msgDraftsFolderPicker"
                       class="folderMenuItem"
                       aria-labelledby="draft_selectFolder"
                       displayformat="verbose">
-              <menupopup id="msgDraftFolderPopup" type="folder" mode="filing"
+              <menupopup is="folder-menupopup" id="msgDraftFolderPopup"
+                         mode="filing"
                          class="menulist-menupopup"
                          showFileHereLabel="true"
                          oncommand="noteSelectionChange('draft', 'Folder', event)"/>
             </menulist>
           </vbox>
         </hbox>
       </radiogroup>
 
@@ -244,25 +251,27 @@
                      value="1" label="&templateInOtherFolder.label;"
                      accesskey="&templateInOtherFolder.accesskey;"/>
             </hbox>
           </vbox>
           <vbox flex="1">
             <menulist id="msgStationeryAccountPicker"
                         class="folderMenuItem"
                         aria-labelledby="tmpl_selectAccount">
-              <menupopup id="msgFccAccountPopup" type="folder" mode="filing"
+              <menupopup is="folder-menupopup" id="msgFccAccountPopup"
+                         mode="filing"
                          expandFolders="false"
                          oncommand="noteSelectionChange('tmpl', 'Account', event)"/>
             </menulist>
             <menulist id="msgStationeryFolderPicker"
                         class="folderMenuItem"
                         aria-labelledby="tmpl_selectFolder"
                         displayformat="verbose">
-              <menupopup id="msgTemplFolderPopup" type="folder" mode="filing"
+              <menupopup is="folder-menupopup" id="msgTemplFolderPopup"
+                         mode="filing"
                          class="menulist-menupopup"
                          showFileHereLabel="true"
                          oncommand="noteSelectionChange('tmpl', 'Folder', event)"/>
             </menulist>
           </vbox>
         </hbox>
       </radiogroup>
       <hbox align="center">
--- a/mailnews/base/prefs/content/am-junk.xul
+++ b/mailnews/base/prefs/content/am-junk.xul
@@ -19,16 +19,17 @@
 <page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
       title="&junkSettings.label;"
       onload="parent.onPanelLoaded('am-junk.xul');">
 
   <vbox flex="1" style="overflow: auto;">
     <script src="chrome://messenger/content/am-junk.js"/>
     <script src="chrome://messenger/content/am-prefs.js"/>
     <script src="chrome://messenger/content/amUtils.js"/>
+    <script src="chrome://messenger/content/folder-menupopup.js"/>
     <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
 
     <hbox class="dialogheader">
       <label class="dialogheader-title" value="&junkSettings.label;"/>
     </hbox>
 
     <groupbox>
       <label class="header">&junkClassification.label;</label>
@@ -162,29 +163,27 @@
                      label="&otherFolder.label;"
                      accesskey="&otherFolder.accesskey;"/>
             </hbox>
           </vbox>
           <vbox flex="1">
             <menulist id="actionTargetAccount"
                       class="folderMenuItem"
                       aria-labelledby="moveTargetMode0">
-              <menupopup id="actionAccountPopup"
-                         type="folder"
+              <menupopup is="folder-menupopup" id="actionAccountPopup"
                          class="menulist-menupopup"
                          expandFolders="false"
                          mode="filing"
                          oncommand="onActionTargetChange(event, 'server.spamActionTargetAccount');"/>
             </menulist>
             <menulist id="actionTargetFolder"
                       class="folderMenuItem"
                       aria-labelledby="moveTargetMode1"
                       displayformat="verbose">
-              <menupopup id="actionFolderPopup"
-                         type="folder"
+              <menupopup is="folder-menupopup" id="actionFolderPopup"
                          mode="junk"
                          showFileHereLabel="true"
                          oncommand="onActionTargetChange(event, 'server.spamActionTargetFolder');"/>
             </menulist>
           </vbox>
         </hbox>
       </radiogroup>
 
--- a/mailnews/base/prefs/content/am-server-advanced.xul
+++ b/mailnews/base/prefs/content/am-server-advanced.xul
@@ -12,16 +12,17 @@
 
 <dialog
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
         title="&serverAdvanced.label;"
         buttons="accept,cancel"
         onload="onLoad();">
 
   <script src="chrome://messenger/content/am-server-advanced.js"/>
+  <script src="chrome://messenger/content/folder-menupopup.js"/>
   <stringbundle id="bundle_prefs"
                 src="chrome://messenger/locale/prefs.properties"/>
 
     <label id="serverPrettyName"/>
 
     <separator class="thin"/>
 
     <!-- IMAP Panel -->
@@ -105,18 +106,17 @@
               <radio id="deferToOtherAccount"
                      value="otherAccount"
                      label="&deferToServer.label;"
                      accesskey="&deferToServer.accesskey;">
               </radio>
               <menulist id="deferredServerFolderPicker"
                         class="folderMenuItem"
                         aria-labelledby="deferToServer">
-                <menupopup id="deferredServerPopup"
-                           type="folder"
+                <menupopup is="folder-menupopup" id="deferredServerPopup"
                            expandFolders="false"
                            mode="deferred"
                            oncommand="this.selectFolder(event.target._folder);"/>
               </menulist>
               </hbox>
               <checkbox amsa_persist="true" id="deferGetNewMail"
                         label="&deferGetNewMail.label;" class="indent"
                         accesskey="&deferGetNewMail.accesskey;"/>
--- a/mailnews/base/prefs/content/am-server.xul
+++ b/mailnews/base/prefs/content/am-server.xul
@@ -18,16 +18,17 @@
       title="&serverSettings.label;"
       onload="parent.onPanelLoaded('am-server.xul');">
 
   <vbox flex="1" style="overflow: auto;">
     <script src="chrome://messenger/content/am-server.js"/>
     <script src="chrome://messenger/content/am-prefs.js"/>
     <script src="chrome://messenger/content/amUtils.js"/>
     <script src="chrome://messenger/content/menulist-charsetpicker.js"/>
+    <script src="chrome://messenger/content/folder-menupopup.js"/>
     <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
 
     <label hidden="true" wsm_persist="true" id="server.type"/>
     <label hidden="true"
            wsm_persist="true"
            preftype="string"
            prefattribute="value"
            prefstring="mail.server.%serverkey%.storeContractID"
@@ -275,18 +276,17 @@
               </hbox>
             </vbox>
             <vbox>
               <menulist id="msgTrashFolderPicker"
                         class="folderMenuItem"
                         maxwidth="300" crop="center"
                         aria-labelledby="modelMoveToTrash"
                         displayformat="verbose">
-                <menupopup id="msgTrashFolderPopup"
-                           type="folder"
+                <menupopup is="folder-menupopup" id="msgTrashFolderPopup"
                            mode="filing"
                            showFileHereLabel="true"
                            oncommand="folderPickerChange(event);"/>
               </menulist>
             </vbox>
           </hbox>
         </radiogroup>
       </hbox>
--- a/mailnews/base/search/content/searchWidgets.js
+++ b/mailnews/base/search/content/searchWidgets.js
@@ -113,17 +113,17 @@ class MozRuleactiontargetForwardto exten
 
 class MozRuleactiontargetFolder extends MozXULElement {
   connectedCallback() {
     this.appendChild(MozXULElement.parseXULToFragment(`
       <menulist class="ruleactionitem
                 folderMenuItem"
                 flex="1"
                 displayformat="verbose">
-        <menupopup type="folder"
+        <menupopup is="folder-menupopup"
                    mode="filing"
                    class="menulist-menupopup"
                    showRecent="true"
                    recentLabel="&recentFolders.label;"
                    showFileHereLabel="true">
         </menupopup>
       </menulist>
       `, [
--- a/mailnews/extensions/newsblog/content/feed-subscriptions.xul
+++ b/mailnews/extensions/newsblog/content/feed-subscriptions.xul
@@ -26,16 +26,17 @@
         persist="width height screenX screenY sizemode"
         onload="FeedSubscriptions.onLoad();"
         onclose="return FeedSubscriptions.onClose();"
         onkeypress="FeedSubscriptions.onKeyPress(event);"
         onmousedown="FeedSubscriptions.onMouseDown(event);">
 
   <script src="chrome://messenger/content/specialTabs.js"/>
   <script src="chrome://messenger-newsblog/content/feed-subscriptions.js"/>
+  <script src="chrome://messenger/content/folder-menupopup.js"/>
 
   <linkset>
     <html:link rel="localization" href="security/certificates/certManager.ftl"/>
   </linkset>
 
   <keyset id="extensionsKeys">
     <key id="key_close"
          key="&cmd.close.commandKey;"
@@ -119,19 +120,18 @@
                        onclick="FeedSubscriptions.checkValidation(event);"/>
               </hbox>
             </hbox>
             <hbox>
               <menulist id="selectFolder"
                         flex="1"
                         class="folderMenuItem"
                         hidden="true">
-                <menupopup id="selectFolderPopup"
+                <menupopup is="folder-menupopup" id="selectFolderPopup"
                            class="menulist-menupopup"
-                           type="folder"
                            mode="feeds"
                            showFileHereLabel="true"
                            showAccountsFileHere="true"
                            oncommand="FeedSubscriptions.setNewFolder(event);"/>
               </menulist>
               <textbox id="selectFolderValue"
                        flex="1"
                        readonly="true"
--- a/mailnews/jar.mn
+++ b/mailnews/jar.mn
@@ -64,17 +64,17 @@ messenger.jar:
     content/messenger/removeAccount.js                                         (base/prefs/content/removeAccount.js)
     content/messenger/msgSelectOfflineFolders.xul                              (base/content/msgSelectOfflineFolders.xul)
     content/messenger/msgSelectOfflineFolders.js                               (base/content/msgSelectOfflineFolders.js)
     content/messenger/msgSynchronize.xul                                       (base/content/msgSynchronize.xul)
     content/messenger/msgSynchronize.js                                        (base/content/msgSynchronize.js)
     content/messenger/folderProps.xul                                          (base/content/folderProps.xul)
     content/messenger/menulist-charsetpicker.js                                (base/content/menulist-charsetpicker.js)
     content/messenger/folderProps.js                                           (base/content/folderProps.js)
-    content/messenger/folderWidgets.xml                                        (base/content/folderWidgets.xml)
+    content/messenger/folder-menupopup.js                                      (base/content/folder-menupopup.js)
     content/messenger/newFolderDialog.xul                                      (base/content/newFolderDialog.xul)
     content/messenger/newFolderDialog.js                                       (base/content/newFolderDialog.js)
 *   content/messenger/msgAccountCentral.xul                                    (base/content/msgAccountCentral.xul)
     content/messenger/msgAccountCentral.js                                     (base/content/msgAccountCentral.js)
     content/messenger/msgFolderPickerOverlay.js                                (base/content/msgFolderPickerOverlay.js)
     content/messenger/renameFolderDialog.xul                                   (base/content/renameFolderDialog.xul)
     content/messenger/renameFolderDialog.js                                    (base/content/renameFolderDialog.js)
     content/messenger/retention.js                                             (base/content/retention.js)