Bug 1359211 - Handle touch-scrolling of XUL <listbox> in JS. r=kats,dao,bz draft
authorBotond Ballo <botond@mozilla.com>
Mon, 24 Apr 2017 17:12:21 -0400
changeset 570222 7ce41f435a008fc15ffa1f3c454f427d77b78021
parent 566473 0b1d1dfe7055a8568e1cc7ea5f74c6fd63ada14c
child 626447 8d46eafcca2e5579960069420a346f96b24573d1
push id56439
push userbballo@mozilla.com
push dateFri, 28 Apr 2017 16:47:46 +0000
reviewerskats, dao, bz
bugs1359211, 1302736
milestone55.0a1
Bug 1359211 - Handle touch-scrolling of XUL <listbox> in JS. r=kats,dao,bz The APZ scrolling codepath doesn't do the right thing for <listbox> without special handling, so have it scroll in JS instead, like we did in bug 1302736 for <tree>. MozReview-Commit-ID: LWJCBfhZ3Hc
dom/webidl/ListBoxObject.webidl
layout/xul/ListBoxObject.cpp
layout/xul/ListBoxObject.h
layout/xul/nsListBoxBodyFrame.cpp
layout/xul/nsListBoxBodyFrame.h
toolkit/content/widgets/listbox.xml
--- a/dom/webidl/ListBoxObject.webidl
+++ b/dom/webidl/ListBoxObject.webidl
@@ -4,16 +4,17 @@
  * 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/.
  */
 
 [NoInterfaceObject]
 interface ListBoxObject : BoxObject {
 
   long getRowCount();
+  long getRowHeight();
   long getNumberOfVisibleRows();
   long getIndexOfFirstVisibleRow();
 
   void ensureIndexIsVisible(long rowIndex);
   void scrollToIndex(long rowIndex);
   void scrollByLines(long numLines);
 
   Element? getItemAtIndex(long index);
--- a/layout/xul/ListBoxObject.cpp
+++ b/layout/xul/ListBoxObject.cpp
@@ -71,16 +71,26 @@ ListBoxObject::GetRowCount()
   nsListBoxBodyFrame* body = GetListBoxBody(true);
   if (body) {
     return body->GetRowCount();
   }
   return 0;
 }
 
 int32_t
+ListBoxObject::GetRowHeight()
+{
+  nsListBoxBodyFrame* body = GetListBoxBody(true);
+  if (body) {
+    return body->GetRowHeightPixels();
+  }
+  return 0;
+}
+
+int32_t
 ListBoxObject::GetNumberOfVisibleRows()
 {
   nsListBoxBodyFrame* body = GetListBoxBody(true);
   if (body) {
     return body->GetNumberOfVisibleRows();
   }
   return 0;
 }
--- a/layout/xul/ListBoxObject.h
+++ b/layout/xul/ListBoxObject.h
@@ -28,16 +28,17 @@ public:
   virtual nsListBoxBodyFrame* GetListBoxBody(bool aFlush) override;
 
   // nsPIBoxObject
   virtual void Clear() override;
   virtual void ClearCachedValues() override;
 
   // ListBoxObject.webidl
   int32_t GetRowCount();
+  int32_t GetRowHeight();
   int32_t GetNumberOfVisibleRows();
   int32_t GetIndexOfFirstVisibleRow();
   void EnsureIndexIsVisible(int32_t rowIndex);
   void ScrollToIndex(int32_t rowIndex);
   void ScrollByLines(int32_t numLines);
   already_AddRefed<Element> GetItemAtIndex(int32_t index);
   int32_t GetIndexOfItem(Element& item);
 
--- a/layout/xul/nsListBoxBodyFrame.cpp
+++ b/layout/xul/nsListBoxBodyFrame.cpp
@@ -642,16 +642,22 @@ int32_t
 nsListBoxBodyFrame::GetRowCount()
 {
   if (mRowCount < 0)
     ComputeTotalRowCount();
   return mRowCount;
 }
 
 int32_t
+nsListBoxBodyFrame::GetRowHeightPixels() const
+{
+  return nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
+}
+
+int32_t
 nsListBoxBodyFrame::GetFixedRowSize()
 {
   nsresult dummy;
 
   nsAutoString rows;
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
   if (!rows.IsEmpty())
     return rows.ToInteger(&dummy);
--- a/layout/xul/nsListBoxBodyFrame.h
+++ b/layout/xul/nsListBoxBodyFrame.h
@@ -88,16 +88,17 @@ public:
   virtual void MarkIntrinsicISizesDirty() override;
 
   virtual nsSize GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) override;
   virtual nsSize GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState) override;
 
   // size calculation 
   int32_t GetRowCount();
   int32_t GetRowHeightAppUnits() { return mRowHeight; }
+  int32_t GetRowHeightPixels() const;
   int32_t GetFixedRowSize();
   void SetRowHeight(nscoord aRowHeight);
   nscoord GetYPosition();
   nscoord GetAvailableHeight();
   nscoord ComputeIntrinsicISize(nsBoxLayoutState& aBoxLayoutState);
 
   // scrolling
   nsresult InternalPositionChangedCallback();
--- a/toolkit/content/widgets/listbox.xml
+++ b/toolkit/content/widgets/listbox.xml
@@ -682,16 +682,18 @@
       <!-- ///////////////// 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);
           }
@@ -865,16 +867,52 @@
               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">
 
     <resources>
       <stylesheet src="chrome://global/skin/listbox.css"/>
     </resources>