Bug 1472556 - Restore the listbox binding in C-C (will be removed in bug 1472555 and bug 1474258). r=jorgk
authorRichard Marti <richard.marti@gmail.com>
Tue, 10 Jul 2018 22:52:21 +0200
changeset 30120 be072a52c30e5bc56eddd6a042869ac8638b70d1
parent 30119 09c8809716341a274638cc133070cbd45af64d34
child 30121 a9602000da282dbed1c3480d5035a2631b2a7b01
push idunknown
push userunknown
push dateunknown
reviewersjorgk
bugs1472556, 1472555, 1474258
Bug 1472556 - Restore the listbox binding in C-C (will be removed in bug 1472555 and bug 1474258). r=jorgk
common/bindings/listbox.xml
mail/base/content/bindings.css
mail/base/jar.mn
mail/themes/linux/jar.mn
mail/themes/linux/mail/listbox.css
mail/themes/osx/jar.mn
mail/themes/osx/mail/listbox.css
mail/themes/windows/jar.mn
mail/themes/windows/mail/listbox.css
--- a/common/bindings/listbox.xml
+++ b/common/bindings/listbox.xml
@@ -6,29 +6,1125 @@
 
 <!-- This files relies on these specific Chrome/XBL globals -->
 <!-- globals ChromeNodeList -->
 
 <bindings id="listboxBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
+#if 0
+  <!--
+    Interface binding that is base for bindings of xul:listbox and
+    xul:richlistbox elements. This binding assumes that successors bindings
+    will implement the following properties and methods:
+
+    /** Return the number of items */
+    readonly itemCount
+
+    /** Return index of given item
+    * @param aItem - given item element */
+    getIndexOfItem(aItem)
+
+    /** Return item at given index
+    * @param aIndex - index of item element */
+    getItemAtIndex(aIndex)
+
+    /** Return count of item elements */
+    getRowCount()
+
+    /** Return count of visible item elements */
+    getNumberOfVisibleRows()
+
+    /** Return index of first visible item element */
+    getIndexOfFirstVisibleRow()
+
+    /** Return true if item of given index is visible
+     * @param aIndex - index of item element
+     *
+     * @note XXX: this method should be removed after bug 364612 is fixed
+     */
+    ensureIndexIsVisible(aIndex)
+
+    /** Return true if item element is visible
+     * @param aElement - given item element */
+    ensureElementIsVisible(aElement)
+
+    /** Scroll list control to make visible item of given index
+     * @param aIndex - index of item element
+     *
+     * @note XXX: this method should be removed after bug 364612 is fixed
+     */
+    scrollToIndex(aIndex)
+
+    /** Create item element and append it to the end of listbox
+     * @param aLabel - label of new item element
+     * @param aValue - value of new item element */
+    appendItem(aLabel, aValue)
+
+    /** Scroll up/down one page
+     * @param aDirection - specifies scrolling direction, should be either -1 or 1
+     * @return the number of elements the selection scrolled
+     */
+    scrollOnePage(aDirection)
+
+    /** Fire "select" event */
+    _fireOnSelect()
+   -->
+#endif
+   <binding id="listbox-base"
+            extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+    <implementation implements="nsIDOMXULMultiSelectControlElement">
+      <field name="_lastKeyTime">0</field>
+      <field name="_incrementalString">""</field>
+
+    <!-- nsIDOMXULSelectControlElement -->
+      <property name="selectedItem"
+                onset="this.selectItem(val);">
+        <getter>
+        <![CDATA[
+          return this.selectedItems.length > 0 ? this.selectedItems[0] : null;
+        ]]>
+        </getter>
+      </property>
+
+      <property name="selectedIndex">
+        <getter>
+        <![CDATA[
+          if (this.selectedItems.length > 0)
+            return this.getIndexOfItem(this.selectedItems[0]);
+          return -1;
+        ]]>
+        </getter>
+        <setter>
+        <![CDATA[
+          if (val >= 0) {
+            // This is a micro-optimization so that a call to getIndexOfItem or
+            // getItemAtIndex caused by _fireOnSelect (especially for derived
+            // widgets) won't loop the children.
+            this._selecting = {
+              item: this.getItemAtIndex(val),
+              index: val
+            };
+            this.selectItem(this._selecting.item);
+            delete this._selecting;
+          } else {
+            this.clearSelection();
+            this.currentItem = null;
+          }
+        ]]>
+        </setter>
+      </property>
+
+      <property name="value">
+        <getter>
+        <![CDATA[
+          if (this.selectedItems.length > 0)
+            return this.selectedItem.value;
+          return null;
+        ]]>
+        </getter>
+        <setter>
+        <![CDATA[
+          var kids = this.getElementsByAttribute("value", val);
+          if (kids && kids.item(0))
+            this.selectItem(kids[0]);
+          return val;
+        ]]>
+        </setter>
+      </property>
+
+    <!-- nsIDOMXULMultiSelectControlElement -->
+      <property name="selType"
+                onget="return this.getAttribute('seltype');"
+                onset="this.setAttribute('seltype', val); return val;"/>
+
+      <property name="currentItem" onget="return this._currentItem;">
+        <setter>
+          if (this._currentItem == val)
+            return val;
+
+          if (this._currentItem)
+            this._currentItem.current = false;
+          this._currentItem = val;
+
+          if (val)
+            val.current = true;
+
+          return val;
+        </setter>
+      </property>
+
+      <property name="currentIndex">
+        <getter>
+          return this.currentItem ? this.getIndexOfItem(this.currentItem) : -1;
+        </getter>
+        <setter>
+        <![CDATA[
+          if (val >= 0)
+            this.currentItem = this.getItemAtIndex(val);
+          else
+            this.currentItem = null;
+        ]]>
+        </setter>
+      </property>
+
+      <field name="selectedItems">new ChromeNodeList()</field>
+
+      <method name="addItemToSelection">
+        <parameter name="aItem"/>
+        <body>
+        <![CDATA[
+          if (this.selType != "multiple" && this.selectedCount)
+            return;
+
+          if (aItem.selected)
+            return;
+
+          this.selectedItems.append(aItem);
+          aItem.selected = true;
+
+          this._fireOnSelect();
+        ]]>
+        </body>
+      </method>
+
+      <method name="removeItemFromSelection">
+        <parameter name="aItem"/>
+        <body>
+        <![CDATA[
+          if (!aItem.selected)
+            return;
+
+          this.selectedItems.remove(aItem);
+          aItem.selected = false;
+          this._fireOnSelect();
+        ]]>
+        </body>
+      </method>
+
+      <method name="toggleItemSelection">
+        <parameter name="aItem"/>
+        <body>
+        <![CDATA[
+          if (aItem.selected)
+            this.removeItemFromSelection(aItem);
+          else
+            this.addItemToSelection(aItem);
+        ]]>
+        </body>
+      </method>
+
+      <method name="selectItem">
+        <parameter name="aItem"/>
+        <body>
+        <![CDATA[
+          if (!aItem)
+            return;
+
+          if (this.selectedItems.length == 1 && this.selectedItems[0] == aItem)
+            return;
+
+          this._selectionStart = null;
+
+          var suppress = this._suppressOnSelect;
+          this._suppressOnSelect = true;
+
+          this.clearSelection();
+          this.addItemToSelection(aItem);
+          this.currentItem = aItem;
+
+          this._suppressOnSelect = suppress;
+          this._fireOnSelect();
+        ]]>
+        </body>
+      </method>
+
+      <method name="selectItemRange">
+        <parameter name="aStartItem"/>
+        <parameter name="aEndItem"/>
+        <body>
+        <![CDATA[
+          if (this.selType != "multiple")
+            return;
+
+          if (!aStartItem)
+            aStartItem = this._selectionStart ?
+              this._selectionStart : this.currentItem;
+
+          if (!aStartItem)
+            aStartItem = aEndItem;
+
+          var suppressSelect = this._suppressOnSelect;
+          this._suppressOnSelect = true;
+
+          this._selectionStart = aStartItem;
+
+          var currentItem;
+          var startIndex = this.getIndexOfItem(aStartItem);
+          var endIndex = this.getIndexOfItem(aEndItem);
+          if (endIndex < startIndex) {
+            currentItem = aEndItem;
+            aEndItem = aStartItem;
+            aStartItem = currentItem;
+          } else {
+            currentItem = aStartItem;
+          }
+
+          while (currentItem) {
+            this.addItemToSelection(currentItem);
+            if (currentItem == aEndItem) {
+              currentItem = this.getNextItem(currentItem, 1);
+              break;
+            }
+            currentItem = this.getNextItem(currentItem, 1);
+          }
+
+          // Clear around new selection
+          // Don't use clearSelection() because it causes a lot of noise
+          // with respect to selection removed notifications used by the
+          // accessibility API support.
+          var userSelecting = this._userSelecting;
+          this._userSelecting = false; // that's US automatically unselecting
+          for (; currentItem; currentItem = this.getNextItem(currentItem, 1))
+            this.removeItemFromSelection(currentItem);
+
+          for (currentItem = this.getItemAtIndex(0); currentItem != aStartItem;
+               currentItem = this.getNextItem(currentItem, 1))
+            this.removeItemFromSelection(currentItem);
+          this._userSelecting = userSelecting;
+
+          this._suppressOnSelect = suppressSelect;
+
+          this._fireOnSelect();
+        ]]>
+        </body>
+      </method>
+
+      <method name="selectAll">
+        <body>
+          this._selectionStart = null;
+
+          var suppress = this._suppressOnSelect;
+          this._suppressOnSelect = true;
+
+          var item = this.getItemAtIndex(0);
+          while (item) {
+            this.addItemToSelection(item);
+            item = this.getNextItem(item, 1);
+          }
+
+          this._suppressOnSelect = suppress;
+          this._fireOnSelect();
+        </body>
+      </method>
+
+      <method name="invertSelection">
+        <body>
+          this._selectionStart = null;
+
+          var suppress = this._suppressOnSelect;
+          this._suppressOnSelect = true;
+
+          var item = this.getItemAtIndex(0);
+          while (item) {
+            if (item.selected)
+              this.removeItemFromSelection(item);
+            else
+              this.addItemToSelection(item);
+            item = this.getNextItem(item, 1);
+          }
+
+          this._suppressOnSelect = suppress;
+          this._fireOnSelect();
+        </body>
+      </method>
+
+      <method name="clearSelection">
+        <body>
+        <![CDATA[
+          if (this.selectedItems) {
+            while (this.selectedItems.length > 0) {
+              let item = this.selectedItems[0];
+              item.selected = false;
+              this.selectedItems.remove(item);
+            }
+          }
+
+          this._selectionStart = null;
+          this._fireOnSelect();
+        ]]>
+        </body>
+      </method>
+
+      <property name="selectedCount" readonly="true"
+                onget="return this.selectedItems.length;"/>
+
+      <method name="getSelectedItem">
+        <parameter name="aIndex"/>
+        <body>
+        <![CDATA[
+          return aIndex < this.selectedItems.length ?
+            this.selectedItems[aIndex] : null;
+        ]]>
+        </body>
+      </method>
+
+    <!-- Other public members -->
+      <property name="disableKeyNavigation"
+                onget="return this.hasAttribute('disableKeyNavigation');">
+        <setter>
+          if (val)
+            this.setAttribute("disableKeyNavigation", "true");
+          else
+            this.removeAttribute("disableKeyNavigation");
+          return val;
+        </setter>
+      </property>
+
+      <property name="suppressOnSelect"
+                onget="return this.getAttribute('suppressonselect') == 'true';"
+                onset="this.setAttribute('suppressonselect', val);"/>
+
+      <property name="_selectDelay"
+                onset="this.setAttribute('_selectDelay', val);"
+                onget="return this.getAttribute('_selectDelay') || 50;"/>
+
+      <method name="timedSelect">
+        <parameter name="aItem"/>
+        <parameter name="aTimeout"/>
+        <body>
+        <![CDATA[
+          var suppress = this._suppressOnSelect;
+          if (aTimeout != -1)
+            this._suppressOnSelect = true;
+
+          this.selectItem(aItem);
+
+          this._suppressOnSelect = suppress;
+
+          if (aTimeout != -1) {
+            if (this._selectTimeout)
+              window.clearTimeout(this._selectTimeout);
+            this._selectTimeout =
+              window.setTimeout(this._selectTimeoutHandler, aTimeout, this);
+          }
+        ]]>
+        </body>
+      </method>
+
+      <method name="moveByOffset">
+        <parameter name="aOffset"/>
+        <parameter name="aIsSelecting"/>
+        <parameter name="aIsSelectingRange"/>
+        <body>
+        <![CDATA[
+          if ((aIsSelectingRange || !aIsSelecting) &&
+              this.selType != "multiple")
+            return;
+
+          var newIndex = this.currentIndex + aOffset;
+          if (newIndex < 0)
+            newIndex = 0;
+
+          var numItems = this.getRowCount();
+          if (newIndex > numItems - 1)
+            newIndex = numItems - 1;
+
+          var newItem = this.getItemAtIndex(newIndex);
+          // make sure that the item is actually visible/selectable
+          if (this._userSelecting && newItem && !this._canUserSelect(newItem))
+            newItem =
+              aOffset > 0 ? this.getNextItem(newItem, 1) || this.getPreviousItem(newItem, 1) :
+                            this.getPreviousItem(newItem, 1) || this.getNextItem(newItem, 1);
+          if (newItem) {
+            this.ensureIndexIsVisible(this.getIndexOfItem(newItem));
+            if (aIsSelectingRange)
+              this.selectItemRange(null, newItem);
+            else if (aIsSelecting)
+              this.selectItem(newItem);
+
+            this.currentItem = newItem;
+          }
+        ]]>
+        </body>
+      </method>
+
+    <!-- Private -->
+      <method name="getNextItem">
+        <parameter name="aStartItem"/>
+        <parameter name="aDelta"/>
+        <body>
+        <![CDATA[
+          while (aStartItem) {
+            aStartItem = aStartItem.nextSibling;
+            if (aStartItem && aStartItem instanceof
+                Ci.nsIDOMXULSelectControlItemElement &&
+                (!this._userSelecting || this._canUserSelect(aStartItem))) {
+              --aDelta;
+              if (aDelta == 0)
+                return aStartItem;
+            }
+          }
+          return null;
+        ]]></body>
+      </method>
+
+      <method name="getPreviousItem">
+        <parameter name="aStartItem"/>
+        <parameter name="aDelta"/>
+        <body>
+        <![CDATA[
+          while (aStartItem) {
+            aStartItem = aStartItem.previousSibling;
+            if (aStartItem && aStartItem instanceof
+                Ci.nsIDOMXULSelectControlItemElement &&
+                (!this._userSelecting || this._canUserSelect(aStartItem))) {
+              --aDelta;
+              if (aDelta == 0)
+                return aStartItem;
+            }
+          }
+          return null;
+        ]]>
+        </body>
+      </method>
+
+      <method name="_moveByOffsetFromUserEvent">
+        <parameter name="aOffset"/>
+        <parameter name="aEvent"/>
+        <body>
+        <![CDATA[
+          if (!aEvent.defaultPrevented) {
+            this._userSelecting = true;
+            this._mayReverse = true;
+            this.moveByOffset(aOffset, !aEvent.ctrlKey, aEvent.shiftKey);
+            this._userSelecting = false;
+            this._mayReverse = false;
+            aEvent.preventDefault();
+          }
+        ]]>
+        </body>
+      </method>
+
+      <method name="_canUserSelect">
+        <parameter name="aItem"/>
+        <body>
+        <![CDATA[
+          var style = document.defaultView.getComputedStyle(aItem);
+          return style.display != "none" && style.visibility == "visible";
+        ]]>
+        </body>
+      </method>
+
+      <method name="_selectTimeoutHandler">
+        <parameter name="aMe"/>
+        <body>
+          aMe._fireOnSelect();
+          aMe._selectTimeout = null;
+        </body>
+      </method>
+
+      <field name="_suppressOnSelect">false</field>
+      <field name="_userSelecting">false</field>
+      <field name="_mayReverse">false</field>
+      <field name="_selectTimeout">null</field>
+      <field name="_currentItem">null</field>
+      <field name="_selectionStart">null</field>
+    </implementation>
+
+    <handlers>
+      <handler event="keypress" keycode="VK_UP" modifiers="control shift any"
+               action="this._moveByOffsetFromUserEvent(-1, event);"
+               group="system"/>
+      <handler event="keypress" keycode="VK_DOWN" modifiers="control shift any"
+               action="this._moveByOffsetFromUserEvent(1, event);"
+               group="system"/>
+      <handler event="keypress" keycode="VK_HOME" modifiers="control shift any"
+               group="system">
+        <![CDATA[
+          this._mayReverse = true;
+          this._moveByOffsetFromUserEvent(-this.currentIndex, event);
+          this._mayReverse = false;
+        ]]>
+      </handler>
+      <handler event="keypress" keycode="VK_END" modifiers="control shift any"
+               group="system">
+        <![CDATA[
+          this._mayReverse = true;
+          this._moveByOffsetFromUserEvent(this.getRowCount() - this.currentIndex - 1, event);
+          this._mayReverse = false;
+        ]]>
+      </handler>
+      <handler event="keypress" keycode="VK_PAGE_UP" modifiers="control shift any"
+               group="system">
+        <![CDATA[
+          this._mayReverse = true;
+          this._moveByOffsetFromUserEvent(this.scrollOnePage(-1), event);
+          this._mayReverse = false;
+        ]]>
+      </handler>
+      <handler event="keypress" keycode="VK_PAGE_DOWN" modifiers="control shift any"
+               group="system">
+        <![CDATA[
+          this._mayReverse = true;
+          this._moveByOffsetFromUserEvent(this.scrollOnePage(1), event);
+          this._mayReverse = false;
+        ]]>
+      </handler>
+      <handler event="keypress" key=" " modifiers="control" phase="target">
+      <![CDATA[
+        if (this.currentItem && this.selType == "multiple")
+          this.toggleItemSelection(this.currentItem);
+      ]]>
+      </handler>
+      <handler event="focus">
+      <![CDATA[
+        if (this.getRowCount() > 0) {
+          if (this.currentIndex == -1) {
+            this.currentIndex = this.getIndexOfFirstVisibleRow();
+          } else {
+            this.currentItem._fireEvent("DOMMenuItemActive");
+          }
+        }
+        this._lastKeyTime = 0;
+      ]]>
+      </handler>
+      <handler event="keypress" phase="target">
+      <![CDATA[
+        if (this.disableKeyNavigation || !event.charCode ||
+            event.altKey || event.ctrlKey || event.metaKey)
+          return;
+
+        if (event.timeStamp - this._lastKeyTime > 1000)
+          this._incrementalString = "";
+
+        var key = String.fromCharCode(event.charCode).toLowerCase();
+        this._incrementalString += key;
+        this._lastKeyTime = event.timeStamp;
+
+        // If all letters in the incremental string are the same, just
+        // try to match the first one
+        var incrementalString = /^(.)\1+$/.test(this._incrementalString) ?
+                                RegExp.$1 : this._incrementalString;
+        var length = incrementalString.length;
+
+        var rowCount = this.getRowCount();
+        var l = this.selectedItems.length;
+        var start = l > 0 ? this.getIndexOfItem(this.selectedItems[l - 1]) : -1;
+        // start from the first element if none was selected or from the one
+        // following the selected one if it's a new or a repeated-letter search
+        if (start == -1 || length == 1)
+          start++;
+
+        for (var i = 0; i < rowCount; i++) {
+          var k = (start + i) % rowCount;
+          var listitem = this.getItemAtIndex(k);
+          if (!this._canUserSelect(listitem))
+            continue;
+          // allow richlistitems to specify the string being searched for
+          var searchText = "searchLabel" in listitem ? listitem.searchLabel :
+                           listitem.getAttribute("label"); // (see also bug 250123)
+          searchText = searchText.substring(0, length).toLowerCase();
+          if (searchText == incrementalString) {
+            this.ensureIndexIsVisible(k);
+            this.timedSelect(listitem, this._selectDelay);
+            break;
+          }
+        }
+      ]]>
+      </handler>
+    </handlers>
+  </binding>
+
+
+  <!-- Binding for xul:listbox element.
+  -->
+  <binding id="listbox"
+           extends="#listbox-base">
+
+    <content>
+      <children includes="listcols">
+        <xul:listcols>
+          <xul:listcol flex="1"/>
+        </xul:listcols>
+      </children>
+      <xul:listrows>
+        <children includes="listhead"/>
+        <xul:listboxbody xbl:inherits="rows,size,minheight">
+          <children includes="listitem"/>
+        </xul:listboxbody>
+      </xul:listrows>
+    </content>
+
+    <implementation>
+
+      <!-- ///////////////// public listbox members ///////////////// -->
+
+      <property name="listBoxObject"
+                onget="return this.boxObject;"
+                readonly="true"/>
+
+      <!-- ///////////////// private listbox members ///////////////// -->
+
+      <field name="_touchY">-1</field>
+
+      <method name="_fireOnSelect">
+        <body>
+        <![CDATA[
+          if (!this._suppressOnSelect && !this.suppressOnSelect) {
+            var event = document.createEvent("Events");
+            event.initEvent("select", true, true);
+            this.dispatchEvent(event);
+          }
+        ]]>
+        </body>
+      </method>
+
+      <constructor>
+      <![CDATA[
+        var count = this.itemCount;
+        for (var index = 0; index < count; index++) {
+          var item = this.getItemAtIndex(index);
+          if (item.getAttribute("selected") == "true")
+            this.selectedItems.append(item);
+        }
+      ]]>
+      </constructor>
+
+      <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
+
+      <method name="appendItem">
+        <parameter name="aLabel"/>
+        <parameter name="aValue"/>
+        <body>
+          const XULNS =
+            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+          var item = this.ownerDocument.createElementNS(XULNS, "listitem");
+          item.setAttribute("label", aLabel);
+          item.setAttribute("value", aValue);
+          this.appendChild(item);
+          return item;
+        </body>
+      </method>
+
+      <property name="itemCount" readonly="true"
+                onget="return this.listBoxObject.getRowCount()"/>
+
+      <!-- ///////////////// nsIListBoxObject ///////////////// -->
+      <method name="getIndexOfItem">
+        <parameter name="item"/>
+        <body>
+          <![CDATA[
+            if (this._selecting && this._selecting.item == item)
+              return this._selecting.index;
+            return this.listBoxObject.getIndexOfItem(item);
+          ]]>
+        </body>
+      </method>
+      <method name="getItemAtIndex">
+        <parameter name="index"/>
+        <body>
+          <![CDATA[
+            if (this._selecting && this._selecting.index == index)
+              return this._selecting.item;
+            return this.listBoxObject.getItemAtIndex(index);
+          ]]>
+        </body>
+      </method>
+      <method name="ensureIndexIsVisible">
+        <parameter name="index"/>
+        <body>
+          return this.listBoxObject.ensureIndexIsVisible(index);
+        </body>
+      </method>
+      <method name="ensureElementIsVisible">
+        <parameter name="element"/>
+        <body>
+          return this.ensureIndexIsVisible(this.listBoxObject.getIndexOfItem(element));
+        </body>
+      </method>
+      <method name="scrollToIndex">
+        <parameter name="index"/>
+        <body>
+          return this.listBoxObject.scrollToIndex(index);
+        </body>
+      </method>
+      <method name="getNumberOfVisibleRows">
+        <body>
+          return this.listBoxObject.getNumberOfVisibleRows();
+        </body>
+      </method>
+      <method name="getIndexOfFirstVisibleRow">
+        <body>
+          return this.listBoxObject.getIndexOfFirstVisibleRow();
+        </body>
+      </method>
+      <method name="getRowCount">
+        <body>
+          return this.listBoxObject.getRowCount();
+        </body>
+      </method>
+
+      <method name="scrollOnePage">
+        <parameter name="direction"/>  <!-- Must be -1 or 1 -->
+        <body>
+          <![CDATA[
+            var pageOffset = this.getNumberOfVisibleRows() * direction;
+            // skip over invisible elements - the user won't care about them
+            for (var i = 0; i != pageOffset; i += direction) {
+              var item = this.getItemAtIndex(this.currentIndex + i);
+              if (item && !this._canUserSelect(item))
+                pageOffset += direction;
+            }
+            var newTop = this.getIndexOfFirstVisibleRow() + pageOffset;
+            if (direction == 1) {
+              var maxTop = this.getRowCount() - this.getNumberOfVisibleRows();
+              for (i = this.getRowCount(); i >= 0 && i > maxTop; i--) {
+                item = this.getItemAtIndex(i);
+                if (item && !this._canUserSelect(item))
+                  maxTop--;
+              }
+              if (newTop >= maxTop)
+                newTop = maxTop;
+            }
+            if (newTop < 0)
+              newTop = 0;
+            this.scrollToIndex(newTop);
+            return pageOffset;
+          ]]>
+        </body>
+      </method>
+    </implementation>
+
+    <handlers>
+      <handler event="keypress" key=" " phase="target">
+        <![CDATA[
+          if (this.currentItem) {
+            if (this.currentItem.getAttribute("type") != "checkbox") {
+              this.addItemToSelection(this.currentItem);
+              // Prevent page from scrolling on the space key.
+              event.preventDefault();
+            } else if (!this.currentItem.disabled) {
+              this.currentItem.checked = !this.currentItem.checked;
+              this.currentItem.doCommand();
+              // Prevent page from scrolling on the space key.
+              event.preventDefault();
+            }
+          }
+        ]]>
+      </handler>
+
+      <handler event="MozSwipeGesture">
+        <![CDATA[
+          // Figure out which index to show
+          let targetIndex = 0;
+
+          // Only handle swipe gestures up and down
+          switch (event.direction) {
+            case event.DIRECTION_DOWN:
+              targetIndex = this.itemCount - 1;
+              // Fall through for actual action
+            case event.DIRECTION_UP:
+              this.ensureIndexIsVisible(targetIndex);
+              break;
+          }
+        ]]>
+      </handler>
+
+      <handler event="touchstart">
+        <![CDATA[
+          if (event.touches.length > 1) {
+            // Multiple touch points detected, abort. In particular this aborts
+            // the panning gesture when the user puts a second finger down after
+            // already panning with one finger. Aborting at this point prevents
+            // the pan gesture from being resumed until all fingers are lifted
+            // (as opposed to when the user is back down to one finger).
+            this._touchY = -1;
+          } else {
+            this._touchY = event.touches[0].screenY;
+          }
+        ]]>
+      </handler>
+      <handler event="touchmove">
+        <![CDATA[
+          if (event.touches.length == 1 &&
+              this._touchY >= 0) {
+            let deltaY = this._touchY - event.touches[0].screenY;
+            let lines = Math.trunc(deltaY / this.listBoxObject.getRowHeight());
+            if (Math.abs(lines) > 0) {
+              this.listBoxObject.scrollByLines(lines);
+              deltaY -= lines * this.listBoxObject.getRowHeight();
+              this._touchY = event.touches[0].screenY + deltaY;
+            }
+            event.preventDefault();
+          }
+        ]]>
+      </handler>
+      <handler event="touchend">
+        <![CDATA[
+          this._touchY = -1;
+        ]]>
+      </handler>
+
+    </handlers>
+  </binding>
+
+  <binding id="listrows">
+
+    <handlers>
+      <handler event="DOMMouseScroll" phase="capturing">
+      <![CDATA[
+        if (event.axis == event.HORIZONTAL_AXIS)
+          return;
+
+        var listBox = this.parentNode.listBoxObject;
+        var rows = event.detail;
+        if (rows == UIEvent.SCROLL_PAGE_UP)
+          rows = -listBox.getNumberOfVisibleRows();
+        else if (rows == UIEvent.SCROLL_PAGE_DOWN)
+          rows = listBox.getNumberOfVisibleRows();
+
+        listBox.scrollByLines(rows);
+        event.preventDefault();
+      ]]>
+      </handler>
+
+      <handler event="MozMousePixelScroll" phase="capturing">
+      <![CDATA[
+        if (event.axis == event.HORIZONTAL_AXIS)
+          return;
+
+        // shouldn't be scrolled by pixel scrolling events before a line/page
+        // scrolling event.
+        event.preventDefault();
+      ]]>
+      </handler>
+    </handlers>
+  </binding>
+
+  <binding id="listitem"
+           extends="chrome://global/content/bindings/general.xml#basetext">
+
+    <content>
+      <children>
+        <xul:listcell xbl:inherits="label,crop,disabled,flexlabel"/>
+      </children>
+    </content>
+
+    <implementation implements="nsIDOMXULSelectControlItemElement">
+      <property name="current" onget="return this.getAttribute('current') == 'true';">
+        <setter><![CDATA[
+          if (val)
+            this.setAttribute("current", "true");
+          else
+            this.removeAttribute("current");
+
+          let control = this.control;
+          if (!control || !control.suppressMenuItemEvent) {
+            this._fireEvent(val ? "DOMMenuItemActive" : "DOMMenuItemInactive");
+          }
+
+          return val;
+        ]]></setter>
+      </property>
+
+      <!-- ///////////////// nsIDOMXULSelectControlItemElement ///////////////// -->
+
+      <property name="value" onget="return this.getAttribute('value');"
+                             onset="this.setAttribute('value', val); return val;"/>
+      <property name="label" onget="return this.getAttribute('label');"
+                             onset="this.setAttribute('label', val); return val;"/>
+
+      <property name="selected" onget="return this.getAttribute('selected') == 'true';">
+        <setter><![CDATA[
+          if (val)
+            this.setAttribute("selected", "true");
+          else
+            this.removeAttribute("selected");
+
+          return val;
+        ]]></setter>
+      </property>
+
+      <property name="control">
+        <getter><![CDATA[
+          var parent = this.parentNode;
+          while (parent) {
+            if (parent instanceof Ci.nsIDOMXULSelectControlElement)
+              return parent;
+            parent = parent.parentNode;
+          }
+          return null;
+        ]]></getter>
+      </property>
+
+      <method name="_fireEvent">
+        <parameter name="name"/>
+        <body>
+        <![CDATA[
+          var event = document.createEvent("Events");
+          event.initEvent(name, true, true);
+          this.dispatchEvent(event);
+        ]]>
+        </body>
+      </method>
+    </implementation>
+    <handlers>
+      <!-- If there is no modifier key, we select on mousedown, not
+           click, so that drags work correctly. -->
+      <handler event="mousedown">
+      <![CDATA[
+        var control = this.control;
+        if (!control || control.disabled)
+          return;
+        if ((!event.ctrlKey || (/Mac/.test(navigator.platform) && event.button == 2)) &&
+            !event.shiftKey && !event.metaKey) {
+          if (!this.selected) {
+            control.selectItem(this);
+          }
+          control.currentItem = this;
+        }
+      ]]>
+      </handler>
+
+      <!-- On a click (up+down on the same item), deselect everything
+           except this item. -->
+      <handler event="click" button="0">
+      <![CDATA[
+        var control = this.control;
+        if (!control || control.disabled)
+          return;
+        control._userSelecting = true;
+        if (control.selType != "multiple") {
+          control.selectItem(this);
+        } else if (event.ctrlKey || event.metaKey) {
+          control.toggleItemSelection(this);
+          control.currentItem = this;
+        } else if (event.shiftKey) {
+          control.selectItemRange(null, this);
+          control.currentItem = this;
+        } else {
+          /* We want to deselect all the selected items except what was
+            clicked, UNLESS it was a right-click.  We have to do this
+            in click rather than mousedown so that you can drag a
+            selected group of items */
+
+          // use selectItemRange instead of selectItem, because this
+          // doesn't de- and reselect this item if it is selected
+          control.selectItemRange(this, this);
+        }
+        control._userSelecting = false;
+      ]]>
+      </handler>
+    </handlers>
+  </binding>
 
   <binding id="listitem-iconic"
-           extends="chrome://global/content/bindings/listbox.xml#listitem">
+#ifdef MOZ_SUITE
+           extends="chrome://communicator/content/bindings/listbox.xml#listitem">
+#else
+           extends="chrome://messenger/content/listbox.xml#listitem">
+#endif
     <content>
       <children>
         <xul:listcell class="listcell-iconic" xbl:inherits="label,image,crop,disabled,flexlabel"/>
       </children>
     </content>
   </binding>
 
   <binding id="listcell-iconic"
-           extends="chrome://global/content/bindings/listbox.xml#listcell">
+#ifdef MOZ_SUITE
+           extends="chrome://communicator/content/bindings/listbox.xml#listcell">
+#else
+           extends="chrome://messenger/content/listbox.xml#listcell">
+#endif
     <content>
       <children>
         <xul:image class="listcell-icon" xbl:inherits="src=image"/>
         <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
       </children>
     </content>
   </binding>
 
+
+
+  <binding id="listitem-checkbox"
+#ifdef MOZ_SUITE
+           extends="chrome://communicator/content/bindings/listbox.xml#listitem">
+#else
+           extends="chrome://messenger/content/listbox.xml#listitem">
+#endif
+    <content>
+      <children>
+        <xul:listcell type="checkbox" xbl:inherits="label,crop,checked,disabled,flexlabel"/>
+      </children>
+    </content>
+
+    <implementation>
+      <property name="checked"
+                onget="return this.getAttribute('checked') == 'true';">
+        <setter><![CDATA[
+          if (val)
+            this.setAttribute("checked", "true");
+          else
+            this.removeAttribute("checked");
+          var event = document.createEvent("Events");
+          event.initEvent("CheckboxStateChange", true, true);
+          this.dispatchEvent(event);
+          return val;
+        ]]></setter>
+      </property>
+    </implementation>
+
+    <handlers>
+      <handler event="mousedown" button="0">
+      <![CDATA[
+        if (!this.disabled && !this.control.disabled) {
+          this.checked = !this.checked;
+          this.doCommand();
+        }
+      ]]>
+      </handler>
+    </handlers>
+  </binding>
+
+  <binding id="listcell"
+           extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+    <content>
+      <children>
+        <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
+      </children>
+    </content>
+  </binding>
+
+  <binding id="listcell-checkbox"
+           extends="chrome://global/content/bindings/listbox.xml#listcell">
+    <content>
+      <children>
+        <xul:image class="listcell-check" xbl:inherits="checked,disabled"/>
+        <xul:label class="listcell-label" xbl:inherits="value=label,flex=flexlabel,crop,disabled" flex="1" crop="right"/>
+      </children>
+    </content>
+  </binding>
+
+  <binding id="listhead">
+
+    <content>
+      <xul:listheaditem>
+        <children includes="listheader"/>
+      </xul:listheaditem>
+    </content>
+  </binding>
+
+  <binding id="listheader" display="xul:button">
+
+    <content>
+      <xul:image class="listheader-icon"/>
+      <xul:label class="listheader-label" xbl:inherits="value=label,crop" flex="1" crop="right"/>
+      <xul:image class="listheader-sortdirection" xbl:inherits="sortDirection"/>
+    </content>
+  </binding>
+
 </bindings>
--- a/mail/base/content/bindings.css
+++ b/mail/base/content/bindings.css
@@ -1,14 +1,15 @@
 /* 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/. */
 
 @import url("chrome://global/content/textbox.css");
 @import url("chrome://global/skin/textbox.css");
+@import url("chrome://messenger/skin/listbox.css");
 @import url("chrome://messenger/skin/numberbox.css");
 @import url("chrome://messenger/skin/spinbuttons.css");
 @import url("chrome://messenger/skin/datetimepicker.css");
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 toolbox {
   -moz-binding: url("chrome://messenger/content/toolbar.xml#toolbox");
@@ -70,16 +71,107 @@ textbox[type="number"] {
 spinbuttons {
   -moz-binding: url("chrome://messenger/content/spinbuttons.xml#spinbuttons");
 }
 
 .spinbuttons-button {
   -moz-user-focus: ignore;
 }
 
+/******** listbox **********/
+
+listbox {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listbox");
+}
+
+listhead {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listhead");
+}
+
+listrows {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listrows");
+}
+
+listitem {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listitem");
+}
+
+listitem[type="checkbox"] {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listitem-checkbox");
+}
+
+listheader {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listheader");
+  -moz-box-ordinal-group: 2147483646;
+}
+
+richlistbox > listheader {
+  -moz-box-ordinal-group: 1;
+}
+
+listcell {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listcell");
+}
+
+listcell[type="checkbox"] {
+  -moz-binding: url("chrome://messenger/content/listbox.xml#listcell-checkbox");
+}
+
+listbox {
+  display: -moz-grid;
+}
+
+listbox[rows] {
+  height: auto;
+}
+
+listcols, listhead, listrows, listboxbody {
+  display: -moz-grid-group;
+}
+
+listcol, listitem, listheaditem {
+  display: -moz-grid-line;
+}
+
+listbox {
+  -moz-user-focus: normal;
+  -moz-box-orient: vertical;
+  min-width: 0;
+  min-height: 0;
+  width: 200px;
+  height: 200px;
+}
+
+listhead {
+  -moz-box-orient: vertical;
+}
+
+listrows {
+  -moz-box-orient: vertical;
+  -moz-box-flex: 1;
+}
+
+listboxbody {
+  -moz-box-orient: vertical;
+  -moz-box-flex: 1;
+  /* Don't permit a horizontal scrollbar. See bug 285449 */
+  overflow-x: hidden !important;
+  overflow-y: auto;
+  min-height: 0;
+}
+
+listcol {
+  -moz-box-orient: vertical;
+  min-width: 16px;
+}
+
+listcell {
+  -moz-box-align: center;
+}
+
 .listitem-iconic:not([type="checkbox"]) {
   -moz-binding: url("chrome://messenger/content/listbox.xml#listitem-iconic");
 }
 
 .listcell-iconic {
   -moz-binding: url("chrome://messenger/content/listbox.xml#listcell-iconic");
 }
 
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -29,17 +29,17 @@ messenger.jar:
     content/messenger/mailWidgets.xml               (content/mailWidgets.xml)
     content/messenger/customizeToolbar.css          (../../common/src/customizeToolbar.css)
     content/messenger/customizeToolbar.js           (../../common/src/customizeToolbar.js)
 *   content/messenger/customizeToolbar.xul          (../../common/src/customizeToolbar.xul)
     content/messenger/viewSource.css                (../../common/src/viewSource.css)
     content/messenger/viewSource.js                 (../../common/src/viewSource.js)
 *   content/messenger/viewSource.xul                (../../common/src/viewSource.xul)
 *   content/messenger/datetimepicker.xml            (../../common/bindings/datetimepicker.xml)
-    content/messenger/listbox.xml                   (../../common/bindings/listbox.xml)
+*   content/messenger/listbox.xml                   (../../common/bindings/listbox.xml)
     content/messenger/numberbox.xml                 (../../common/bindings/numberbox.xml)
     content/messenger/generalBindings.xml           (../../common/bindings/generalBindings.xml)
     content/messenger/spinbuttons.xml               (../../common/bindings/spinbuttons.xml)
 *   content/messenger/textbox.xml                   (../../common/bindings/textbox.xml)
 *   content/messenger/toolbar.xml                   (../../common/bindings/toolbar.xml)
     content/messenger/attachmentList.css            (content/attachmentList.css)
 *   content/messenger/bindings.css                  (content/bindings.css)
     content/messenger/nsDragAndDrop.js              (content/nsDragAndDrop.js)
--- a/mail/themes/linux/jar.mn
+++ b/mail/themes/linux/jar.mn
@@ -21,16 +21,17 @@ classic.jar:
   skin/classic/messenger/half-operator.png                    (../../components/im/themes/half-operator.png)
   skin/classic/messenger/voice.png                            (../../components/im/themes/voice.png)
   skin/classic/messenger/browserRequest.css                   (mail/browserRequest.css)
   skin/classic/messenger/compacttheme.css                     (mail/compacttheme.css)
   skin/classic/messenger/customizeToolbar.css                 (mail/customizeToolbar.css)
   skin/classic/messenger/datetimepicker.css                   (mail/datetimepicker.css)
   skin/classic/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                 (mail/section_expanded.png)
+  skin/classic/messenger/listbox.css                          (mail/listbox.css)
   skin/classic/messenger/messageBody.css                      (mail/messageBody.css)
   skin/classic/messenger/messageHeader.css                    (mail/messageHeader.css)
   skin/classic/messenger/messageIcons.css                     (mail/messageIcons.css)
   skin/classic/messenger/messageQuotes.css                    (mail/messageQuotes.css)
   skin/classic/messenger/messenger.css                        (mail/messenger.css)
   skin/classic/messenger/numberbox.css                        (mail/numberbox.css)
   skin/classic/messenger/attachmentList.css                   (mail/attachmentList.css)
   skin/classic/messenger/imageFilters.svg                     (mail/imageFilters.svg)
new file mode 100644
--- /dev/null
+++ b/mail/themes/linux/mail/listbox.css
@@ -0,0 +1,91 @@
+/* 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/. */
+
+/* ===== listbox.css =======================================================
+  == Styles used by XUL listbox-related elements.
+  ======================================================================= */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: listbox ::::: */
+
+listbox {
+  -moz-appearance: listbox;
+  margin: 2px 4px;
+  background-color: -moz-Field;
+  color: -moz-FieldText;
+}
+
+listbox[disabled="true"] {
+  color: GrayText;
+}
+
+/* ::::: listitem ::::: */
+
+listbox:focus > listitem[selected="true"][current="true"] {
+  outline: 1px dotted #F3D982;
+}
+
+listbox:focus > listitem[current="true"] {
+  outline: 1px dotted Highlight;
+  outline-offset: -1px;
+}
+
+listitem[selected="true"] {
+  background-color: -moz-cellhighlight;
+  color: -moz-cellhighlighttext;
+}
+
+listbox:focus > listitem[selected="true"] {
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+  -moz-appearance: treeheadercell;
+  -moz-box-align: center;
+  background-color: -moz-Dialog;
+  color: -moz-DialogText;
+}
+
+.listheader-icon {
+  margin-inline-end: 2px;
+}
+
+.listheader-label {
+  margin: 0 !important;
+}
+
+/* ..... sort direction icon ..... */
+
+.listheader-sortdirection {
+  -moz-appearance: treeheadersortarrow;
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+  margin: 0 !important;
+  padding-top: 0;
+  padding-bottom: 1px;
+  padding-inline-start: 4px;
+  padding-inline-end: 0;
+  white-space: nowrap;
+}
+
+.listcell-label[disabled="true"] {
+  color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0 2px;
+  min-width: 13px;
+  min-height: 13px;
+}
--- a/mail/themes/osx/jar.mn
+++ b/mail/themes/osx/jar.mn
@@ -26,16 +26,17 @@ classic.jar:
   skin/classic/messenger/half-operator.png                       (../../components/im/themes/half-operator.png)
   skin/classic/messenger/voice.png                               (../../components/im/themes/voice.png)
   skin/classic/messenger/browserRequest.css                      (mail/browserRequest.css)
   skin/classic/messenger/compacttheme.css                        (mail/compacttheme.css)
   skin/classic/messenger/customizeToolbar.css                    (mail/customizeToolbar.css)
   skin/classic/messenger/datetimepicker.css                      (mail/datetimepicker.css)
   skin/classic/messenger/section_collapsed.png                   (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                    (mail/section_expanded.png)
+  skin/classic/messenger/listbox.css                             (mail/listbox.css)
   skin/classic/messenger/messageBody.css                         (mail/messageBody.css)
   skin/classic/messenger/messageHeader.css                       (mail/messageHeader.css)
   skin/classic/messenger/messageIcons.css                        (mail/messageIcons.css)
   skin/classic/messenger/messageQuotes.css                       (mail/messageQuotes.css)
   skin/classic/messenger/messageWindow.css                       (mail/messageWindow.css)
   skin/classic/messenger/numberbox.css                           (mail/numberbox.css)
   skin/classic/messenger/attachmentList.css                      (mail/attachmentList.css)
   skin/classic/messenger/msgSelectOffline.css                    (mail/msgSelectOffline.css)
new file mode 100644
--- /dev/null
+++ b/mail/themes/osx/mail/listbox.css
@@ -0,0 +1,95 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+listbox {
+  -moz-appearance: listbox;
+  margin: 2px 4px;
+  background-color: #FFFFFF;
+  color: -moz-FieldText;
+}
+
+.listcell-label {
+  margin: 0px !important;
+  padding-bottom: 1px;
+  padding-inline-start: 4px;
+  white-space: nowrap;
+}
+
+/* ::::: listitem ::::: */
+
+listitem {
+  border: 1px solid transparent;
+}
+
+listitem[selected="true"] {
+  background-color: -moz-mac-secondaryhighlight;
+  color: -moz-DialogText;
+}
+
+listbox:focus > listitem[selected="true"] {
+   background-color: Highlight;
+   color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+  -moz-appearance: treeheadercell;
+  -moz-box-align: center;
+  color: -moz-DialogText;
+  padding: 0 4px;
+}
+
+listheader[sortable="true"]:hover:active {
+  padding-top: 1px;
+  padding-inline-start: 5px;
+  padding-inline-end: 4px;
+}
+
+.listheader-icon {
+  margin-inline-end: 2px;
+}
+
+.listheader-label {
+  margin: 0px !important;
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+  margin: 0px !important;
+  padding-bottom: 1px;
+  padding-inline-start: 4px;
+  white-space: nowrap;
+}
+
+.listcell-label[disabled="true"] {
+  color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0px 2px;
+  border: 1px solid -moz-DialogText;
+  min-width: 13px;
+  min-height: 13px;
+  background: -moz-Field no-repeat 50% 50%;
+}
+
+.listcell-check[checked="true"] {
+  background-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
+.listcell-check[disabled="true"] {
+  border-color: GrayText;
+}
+
+.listcell-check[disabled="true"][checked="true"] {
+  background-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
+}
--- a/mail/themes/windows/jar.mn
+++ b/mail/themes/windows/jar.mn
@@ -21,16 +21,17 @@ classic.jar:
   skin/classic/messenger/half-operator.png                    (../../components/im/themes/half-operator.png)
   skin/classic/messenger/voice.png                            (../../components/im/themes/voice.png)
   skin/classic/messenger/browserRequest.css                   (mail/browserRequest.css)
   skin/classic/messenger/compacttheme.css                     (mail/compacttheme.css)
   skin/classic/messenger/customizeToolbar.css                 (mail/customizeToolbar.css)
   skin/classic/messenger/datetimepicker.css                   (mail/datetimepicker.css)
   skin/classic/messenger/section_collapsed.png                (mail/section_collapsed.png)
   skin/classic/messenger/section_expanded.png                 (mail/section_expanded.png)
+  skin/classic/messenger/listbox.css                          (mail/listbox.css)
   skin/classic/messenger/messageBody.css                      (mail/messageBody.css)
   skin/classic/messenger/messageHeader.css                    (mail/messageHeader.css)
   skin/classic/messenger/messageIcons.css                     (mail/messageIcons.css)
   skin/classic/messenger/messageQuotes.css                    (mail/messageQuotes.css)
   skin/classic/messenger/messenger.css                        (mail/messenger.css)
   skin/classic/messenger/numberbox.css                        (mail/numberbox.css)
   skin/classic/messenger/attachmentList.css                   (mail/attachmentList.css)
   skin/classic/messenger/imageFilters.svg                     (mail/imageFilters.svg)
new file mode 100644
--- /dev/null
+++ b/mail/themes/windows/mail/listbox.css
@@ -0,0 +1,215 @@
+/* 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/. */
+
+/* ===== listbox.css =======================================================
+  == Styles used by XUL listbox-related elements.
+  ======================================================================= */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: listbox ::::: */
+
+listbox {
+  -moz-appearance: listbox;
+  margin: 2px 4px;
+  color: -moz-FieldText;
+}
+
+listbox[disabled="true"] {
+  color: GrayText;
+}
+
+/* ::::: listitem ::::: */
+
+listitem {
+  border: 1px solid transparent;
+}
+
+listbox:focus > listitem[selected="true"][current="true"] {
+  outline: 1px dotted #F3D982;
+}
+
+listbox:focus > listitem[current="true"] {
+  outline: 1px dotted Highlight;
+  outline-offset: -1px;
+}
+
+listitem[selected="true"] {
+  background-color: -moz-cellhighlight;
+  color: -moz-cellhighlighttext;
+}
+
+listbox:focus > listitem[selected="true"] {
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+  -moz-appearance: treeheadercell;
+  -moz-box-align: center;
+  color: -moz-DialogText;
+  padding: 0 4px;
+}
+
+listheader[sortable="true"]:hover:active {
+  padding-top: 1px;
+  padding-bottom: 0px;
+  padding-inline-start: 5px;
+  padding-inline-end: 4px;
+}
+
+.listheader-icon {
+  margin-inline-end: 2px;
+}
+
+.listheader-label {
+  margin: 0px !important;
+}
+
+/* ..... sort direction icon ..... */
+
+.listheader-sortdirection {
+  list-style-image: none;
+}
+
+.listheader-sortdirection[sortDirection="ascending"] {
+  list-style-image: url("chrome://global/skin/tree/sort-asc.png");
+}
+
+.listheader-sortdirection[sortDirection="descending"] {
+  list-style-image: url("chrome://global/skin/tree/sort-dsc.png");
+}
+
+@media (-moz-windows-classic) {
+  .listheader-sortdirection[sortDirection="ascending"] {
+    list-style-image: url("chrome://global/skin/tree/sort-asc-classic.png");
+  }
+
+  .listheader-sortdirection[sortDirection="descending"] {
+    list-style-image: url("chrome://global/skin/tree/sort-dsc-classic.png");
+  }
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+  margin: 0px !important;
+  padding-top: 0px;
+  padding-bottom: 1px;
+  padding-inline-start: 4px;
+  padding-inline-end: 0px;
+  white-space: nowrap;
+}
+
+.listcell-label[disabled="true"] {
+  color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+  -moz-appearance: checkbox;
+  -moz-box-align: center;
+  margin: 0px 2px;
+  border: 1px solid -moz-DialogText;
+  min-width: 13px;
+  min-height: 13px;
+  background: -moz-Field no-repeat 50% 50%;
+}
+
+@media (-moz-windows-default-theme) {
+  listitem {
+    --listitem-selectedColor: rgb(217,217,217);
+    --listitem-selectedBorder: var(--listitem-selectedColor);
+    --listitem-selectedBottomBorder: rgb(204,204,204);
+    --listitem-selectedBackground: var(--listitem-selectedColor);
+    --listitem-selectedImage: none;
+    --listitem-selectedCurrentBorder: rgb(123,195,255);
+    --listitem-selectedFocusColor: rgb(205,232,255);
+    --listitem-selectedFocusBorder: var(--listitem-selectedFocusColor);
+    --listitem-selectedFocusBottomBorder: rgb(165,214,255);
+    --listitem-selectedFocusBackground: var(--listitem-selectedFocusColor);
+    --listitem-selectedFocusImage: none;
+    --listitem-selectedFocusCurrentBorder: var(--listitem-selectedFocusColor);
+    --listitem-selectedFocusCurrentBottomBorder: var(--listitem-selectedFocusBottomBorder);
+    --listitem-selectedFocusCurrentBackground: var(--listitem-selectedFocusColor);
+
+    color: -moz-FieldText;
+    margin-inline-start: 1px;
+    margin-inline-end: 1px;
+    padding-top: 1px;
+    padding-bottom: 1px;
+    border-width: 1px;
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+  }
+
+  listitem[selected="true"] {
+    border-top-color: var(--listitem-selectedBorder);
+    border-right-color: var(--listitem-selectedBorder);
+    border-left-color: var(--listitem-selectedBorder);
+    border-bottom-color: var(--listitem-selectedBottomBorder);
+    background-image: var(--listitem-selectedImage);
+    background-color: var(--listitem-selectedBackground);
+    color: -moz-DialogText;
+  }
+
+  listbox:focus > listitem[selected="true"] {
+    border-top-color: var(--listitem-selectedFocusBorder);
+    border-right-color: var(--listitem-selectedFocusBorder);
+    border-left-color: var(--listitem-selectedFocusBorder);
+    border-bottom-color: var(--listitem-selectedFocusBottomBorder);
+    background-image: var(--listitem-selectedFocusImage);
+    background-color: var(--listitem-selectedFocusBackground);
+    color: -moz-DialogText;
+  }
+
+  listbox:focus > listitem[current="true"] {
+    border-color: var(--listitem-selectedCurrentBorder);
+    outline: none;
+  }
+
+  listbox:focus > listitem[selected="true"][current="true"] {
+    border-top-color: var(--listitem-selectedFocusCurrentBorder);
+    border-right-color: var(--listitem-selectedFocusCurrentBorder);
+    border-left-color: var(--listitem-selectedFocusCurrentBorder);
+    border-bottom-color: var(--listitem-selectedFocusCurrentBottomBorder);
+    background-color: var(--listitem-selectedFocusCurrentBackground);
+    outline: none;
+  }
+
+  @media (-moz-os-version: windows-win7) {
+    listitem {
+      --listitem-selectedBottomBorder: var(--listitem-selectedColor);
+      --listitem-selectedBackground: rgba(190,190,190,.15);
+      --listitem-selectedImage: linear-gradient(rgba(190,190,190,.1), rgba(190,190,190,.4));
+      --listitem-selectedCurrentBorder: rgb(125,162,206);
+      --listitem-selectedFocusColor: rgb(132,172,221);
+      --listitem-selectedFocusBottomBorder: var(--listitem-selectedFocusColor);
+      --listitem-selectedFocusBackground: rgba(131,183,249,.02);
+      --listitem-selectedFocusImage: linear-gradient(rgba(131,183,249,.16), rgba(131,183,249,.375));
+      --listitem-selectedFocusCurrentBackground: rgba(131,183,249,.15);
+
+      border-radius: 3px;
+      box-shadow: inset 0 0 0 1px rgba(255,255,255,.4), inset 0 -1px 0 1px rgba(255,255,255,.2);
+    }
+  }
+
+  @media (-moz-os-version: windows-win8) {
+    listitem {
+      --listitem-selectedBottomBorder: var(--listitem-selectedColor);
+      --listitem-selectedBackground: rgba(190,190,190,.15);
+      --listitem-selectedImage: linear-gradient(rgba(190,190,190,.4), rgba(190,190,190,.4));
+      --listitem-selectedCurrentBorder: rgb(125,162,206);
+      --listitem-selectedFocusColor: rgb(132,172,221);
+      --listitem-selectedFocusBottomBorder: var(--listitem-selectedFocusColor);
+      --listitem-selectedFocusBackground: rgba(131,183,249,.02);
+      --listitem-selectedFocusImage: linear-gradient(rgba(131,183,249,.375), rgba(131,183,249,.375));
+      --listitem-selectedFocusCurrentBackground: rgba(131,183,249,.15);
+    }
+  }
+}