Bug 1062483 - Make the overscroll effect harder to trigger accidentally. r=kats
authorBotond Ballo <botond@mozilla.com>
Tue, 23 Sep 2014 11:38:05 -0400
changeset 206907 1b87674ddc9c1ca3302d34a93ab528d06e5a9bf2
parent 206906 78d2993b85c93bca9a09cc69df4241c1758121a4
child 206908 0ecad18902f3350147f39d20dbc74ca82088d5cd
push id27544
push userryanvm@gmail.com
push dateWed, 24 Sep 2014 21:10:36 +0000
treeherdermozilla-central@1735ff2bb23e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1062483
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1062483 - Make the overscroll effect harder to trigger accidentally. r=kats
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/InputBlockState.cpp
gfx/layers/apz/src/OverscrollHandoffChain.cpp
gfx/layers/apz/src/OverscrollHandoffChain.h
gfx/layers/apz/src/OverscrollHandoffState.cpp
gfx/layers/apz/src/OverscrollHandoffState.h
gfx/layers/moz.build
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -18,17 +18,17 @@
 #include "mozilla/TouchEvents.h"
 #include "mozilla/Preferences.h"        // for Preferences
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "UnitTransforms.h"             // for ViewAs
 #include "gfxPrefs.h"                   // for gfxPrefs
-#include "OverscrollHandoffChain.h"     // for OverscrollHandoffChain
+#include "OverscrollHandoffState.h"     // for OverscrollHandoffState
 #include "LayersLogging.h"              // for Stringify
 
 #define APZCTM_LOG(...)
 // #define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
@@ -891,46 +891,46 @@ TransformDisplacement(APZCTreeManager* a
   ApplyTransform(&aStartPoint, transformToApzc);
   ApplyTransform(&aEndPoint, transformToApzc);
 }
 
 bool
 APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
                                 ScreenPoint aStartPoint,
                                 ScreenPoint aEndPoint,
-                                const OverscrollHandoffChain& aOverscrollHandoffChain,
-                                uint32_t aOverscrollHandoffChainIndex)
+                                OverscrollHandoffState& aOverscrollHandoffState)
 {
+  const OverscrollHandoffChain& overscrollHandoffChain = aOverscrollHandoffState.mChain;
+  uint32_t overscrollHandoffChainIndex = aOverscrollHandoffState.mChainIndex;
   nsRefPtr<AsyncPanZoomController> next;
   // If we have reached the end of the overscroll handoff chain, there is
   // nothing more to scroll, so we ignore the rest of the pan gesture.
-  if (aOverscrollHandoffChainIndex >= aOverscrollHandoffChain.Length()) {
+  if (overscrollHandoffChainIndex >= overscrollHandoffChain.Length()) {
     // Nothing more to scroll - ignore the rest of the pan gesture.
     return false;
   }
 
-  next = aOverscrollHandoffChain.GetApzcAtIndex(aOverscrollHandoffChainIndex);
+  next = overscrollHandoffChain.GetApzcAtIndex(overscrollHandoffChainIndex);
 
   if (next == nullptr || next->IsDestroyed()) {
     return false;
   }
 
   // Convert the start and end points from |aPrev|'s coordinate space to
   // |next|'s coordinate space. Since |aPrev| may be the same as |next|
   // (if |aPrev| is the APZC that is initiating the scroll and there is no
   // scroll grabbing to grab the scroll from it), don't bother doing the
   // transformations in that case.
   if (next != aPrev) {
     TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint);
   }
 
   // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
   // again with an incremented index.
-  return next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChain,
-      aOverscrollHandoffChainIndex);
+  return next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState);
 }
 
 bool
 APZCTreeManager::DispatchFling(AsyncPanZoomController* aPrev,
                                ScreenPoint aVelocity,
                                nsRefPtr<const OverscrollHandoffChain> aOverscrollHandoffChain,
                                bool aHandoff)
 {
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -39,16 +39,17 @@ enum AllowedTouchBehavior {
   UNKNOWN =            1 << 4
 };
 
 class Layer;
 class AsyncPanZoomController;
 class CompositorParent;
 class APZPaintLogHelper;
 class OverscrollHandoffChain;
+class OverscrollHandoffState;
 class LayerMetricsWrapper;
 
 /**
  * ****************** NOTE ON LOCK ORDERING IN APZ **************************
  *
  * There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock
  * ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks").
  *
@@ -307,18 +308,17 @@ public:
    *   - TM.DispatchScroll() discards the rest of the scroll as there are no more elements in the chain.
    *
    * Note: this should be used for panning only. For handing off overscroll for
    *       a fling, use DispatchFling().
    */
   bool DispatchScroll(AsyncPanZoomController* aApzc,
                       ScreenPoint aStartPoint,
                       ScreenPoint aEndPoint,
-                      const OverscrollHandoffChain& aOverscrollHandoffChain,
-                      uint32_t aOverscrollHandoffChainIndex);
+                      OverscrollHandoffState& aOverscrollHandoffState);
 
   /**
    * This is a callback for AsyncPanZoomController to call when it wants to
    * start a fling in response to a touch-end event, or when it needs to hand
    * off a fling to the next APZC. Note that because of scroll grabbing, the
    * first APZC to fling may not be the one that is receiving the touch events.
    *
    * @param aApzc the APZC that wants to start or hand off the fling
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -10,17 +10,17 @@
 #include <algorithm>                    // for max, min
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
 #include "Compositor.h"                 // for Compositor
 #include "CompositorParent.h"           // for CompositorParent
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "InputData.h"                  // for MultiTouchInput, etc
 #include "InputBlockState.h"            // for InputBlockState, TouchBlockState
-#include "OverscrollHandoffChain.h"     // for OverscrollHandoffChain
+#include "OverscrollHandoffState.h"     // for OverscrollHandoffState
 #include "Units.h"                      // for CSSRect, CSSPoint, etc
 #include "UnitTransforms.h"             // for TransformTo
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "base/tracked.h"               // for FROM_HERE
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxTypes.h"                   // for gfxFloat
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
@@ -264,16 +264,21 @@ typedef mozilla::gfx::Matrix4x4 Matrix4x
  * "apz.overscroll.fling_friction"
  * Amount of friction applied during flings when in overscroll.
  *
  * "apz.overscroll.fling_stopped_threshold"
  * When flinging in an overscrolled state, if the velocity goes below this
  * number, we stop the fling.
  * Units: screen pixels per millisecond
  *
+ * "apz.overscroll.min_pan_distance_ratio"
+ * The minimum ratio of the pan distance along one axis to the pan distance
+ * along the other axis needed to initiate overscroll along the first axis
+ * during panning.
+ *
  * "apz.overscroll.stretch_factor"
  * How much overscrolling can stretch content along an axis.
  * The maximum stretch along an axis is a factor of (1 + kStretchFactor).
  * (So if kStretchFactor is 0, you can't stretch at all; if kStretchFactor
  * is 1, you can stretch at most by a factor of 2).
  *
  * "apz.overscroll.snap_back.spring_stiffness"
  * The stiffness of the spring used in the physics model for the overscroll
@@ -1568,18 +1573,20 @@ nsEventStatus AsyncPanZoomController::On
   mY.UpdateWithTouchAtDevicePoint(aEvent.mPanStartPoint.y, aEvent.mTime);
 
   ScreenPoint panDisplacement = aEvent.mPanDisplacement;
   ToGlobalScreenCoordinates(&panDisplacement, aEvent.mPanStartPoint);
   HandlePanningUpdate(panDisplacement);
 
   // TODO: Handle pan events sent without pan begin / pan end events properly.
   if (mPanGestureState) {
+    OverscrollHandoffState handoffState(
+        *mPanGestureState->GetOverscrollHandoffChain(), panDisplacement);
     CallDispatchScroll(aEvent.mPanStartPoint, aEvent.mPanStartPoint + aEvent.mPanDisplacement,
-                       *mPanGestureState->GetOverscrollHandoffChain(), 0);
+                       handoffState);
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-end in state %d\n", this, mState);
 
@@ -1832,26 +1839,26 @@ void AsyncPanZoomController::HandlePanni
       SetState(CROSS_SLIDING_Y);
       mY.SetAxisLocked(true);
     }
   } else {
     SetState(PANNING);
   }
 }
 
-void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aDelta) {
+void AsyncPanZoomController::HandlePanningUpdate(const ScreenPoint& aPanDistance) {
   // If we're axis-locked, check if the user is trying to break the lock
   if (GetAxisLockMode() == STICKY && !mPanDirRestricted) {
 
-    double angle = atan2(aDelta.y, aDelta.x); // range [-pi, pi]
+    double angle = atan2(aPanDistance.y, aPanDistance.x); // range [-pi, pi]
     angle = fabs(angle); // range [0, pi]
 
     float breakThreshold = gfxPrefs::APZAxisBreakoutThreshold() * APZCTreeManager::GetDPI();
 
-    if (fabs(aDelta.x) > breakThreshold || fabs(aDelta.y) > breakThreshold) {
+    if (fabs(aPanDistance.x) > breakThreshold || fabs(aPanDistance.y) > breakThreshold) {
       if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
         if (!IsCloseToHorizontal(angle, gfxPrefs::APZAxisBreakoutAngle())) {
           mY.SetAxisLocked(false);
           SetState(PANNING);
         }
       } else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
         if (!IsCloseToVertical(angle, gfxPrefs::APZAxisLockAngle())) {
           mX.SetAxisLocked(false);
@@ -1900,18 +1907,17 @@ nsEventStatus AsyncPanZoomController::St
 void AsyncPanZoomController::UpdateWithTouchAtDevicePoint(const MultiTouchInput& aEvent) {
   ScreenPoint point = GetFirstTouchScreenPoint(aEvent);
   mX.UpdateWithTouchAtDevicePoint(point.x, aEvent.mTime);
   mY.UpdateWithTouchAtDevicePoint(point.y, aEvent.mTime);
 }
 
 bool AsyncPanZoomController::AttemptScroll(const ScreenPoint& aStartPoint,
                                            const ScreenPoint& aEndPoint,
-                                           const OverscrollHandoffChain& aOverscrollHandoffChain,
-                                           uint32_t aOverscrollHandoffChainIndex) {
+                                           OverscrollHandoffState& aOverscrollHandoffState) {
 
   // "start - end" rather than "end - start" because e.g. moving your finger
   // down (*positive* direction along y axis) causes the vertical scroll offset
   // to *decrease* as the page follows your finger.
   ScreenPoint displacement = aStartPoint - aEndPoint;
 
   ScreenPoint overscroll;  // will be used outside monitor block
   {
@@ -1936,26 +1942,44 @@ bool AsyncPanZoomController::AttemptScro
     return true;
   }
 
   // If there is overscroll, first try to hand it off to an APZC later
   // in the handoff chain to consume (either as a normal scroll or as
   // overscroll).
   // Note: "+ overscroll" rather than "- overscroll" because "overscroll"
   // is what's left of "displacement", and "displacement" is "start - end".
+  ++aOverscrollHandoffState.mChainIndex;
   if (CallDispatchScroll(aEndPoint + overscroll, aEndPoint,
-                         aOverscrollHandoffChain, aOverscrollHandoffChainIndex + 1)) {
+                         aOverscrollHandoffState)) {
     return true;
   }
 
   // If there is no APZC later in the handoff chain that accepted the
   // overscroll, try to accept it ourselves. We only accept it if we
   // are pannable.
   APZC_LOG("%p taking overscroll during panning\n", this);
-  return OverscrollBy(overscroll);
+  return OverscrollForPanning(overscroll, aOverscrollHandoffState.mPanDistance);
+}
+
+bool AsyncPanZoomController::OverscrollForPanning(ScreenPoint aOverscroll,
+                                                  const ScreenPoint& aPanDistance) {
+  // Only allow entering overscroll along an axis if the pan distance along
+  // that axis is greater than the pan distance along the other axis by a
+  // configurable factor. If we are already overscrolled, don't check this.
+  if (!IsOverscrolled()) {
+    if (aPanDistance.x < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.y) {
+      aOverscroll.x = 0;
+    }
+    if (aPanDistance.y < gfxPrefs::APZMinPanDistanceRatio() * aPanDistance.x) {
+      aOverscroll.y = 0;
+    }
+  }
+
+  return OverscrollBy(aOverscroll);
 }
 
 bool AsyncPanZoomController::OverscrollBy(const ScreenPoint& aOverscroll) {
   if (!gfxPrefs::APZOverscrollEnabled()) {
     return false;
   }
 
   ReentrantMonitorAutoEnter lock(mMonitor);
@@ -2061,43 +2085,42 @@ void AsyncPanZoomController::StartSmooth
 
 void AsyncPanZoomController::StartSnapBack() {
   SetState(SNAP_BACK);
   StartAnimation(new OverscrollSnapBackAnimation(*this));
 }
 
 bool AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint,
                                                 const ScreenPoint& aEndPoint,
-                                                const OverscrollHandoffChain& aOverscrollHandoffChain,
-                                                uint32_t aOverscrollHandoffChainIndex) {
+                                                OverscrollHandoffState& aOverscrollHandoffState) {
   // Make a local copy of the tree manager pointer and check if it's not
   // null before calling DispatchScroll(). This is necessary because
   // Destroy(), which nulls out mTreeManager, could be called concurrently.
   APZCTreeManager* treeManagerLocal = GetApzcTreeManager();
   return treeManagerLocal
       && treeManagerLocal->DispatchScroll(this, aStartPoint, aEndPoint,
-                                          aOverscrollHandoffChain,
-                                          aOverscrollHandoffChainIndex);
+                                          aOverscrollHandoffState);
 }
 
 void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
   ScreenPoint prevTouchPoint(mX.GetPos(), mY.GetPos());
   ScreenPoint touchPoint = GetFirstTouchScreenPoint(aEvent);
 
-  ScreenPoint delta(mX.PanDistance(touchPoint.x),
-                    mY.PanDistance(touchPoint.y));
+  ScreenPoint panDistance(mX.PanDistance(touchPoint.x),
+                          mY.PanDistance(touchPoint.y));
   const ScreenPoint panStart = PanStart();
-  ToGlobalScreenCoordinates(&delta, panStart);
-  HandlePanningUpdate(delta);
+  ToGlobalScreenCoordinates(&panDistance, panStart);
+  HandlePanningUpdate(panDistance);
 
   UpdateWithTouchAtDevicePoint(aEvent);
 
   if (prevTouchPoint != touchPoint) {
-    CallDispatchScroll(prevTouchPoint, touchPoint,
-        *CurrentTouchBlock()->GetOverscrollHandoffChain(), 0);
+    OverscrollHandoffState handoffState(
+        *CurrentTouchBlock()->GetOverscrollHandoffChain(), panDistance);
+    CallDispatchScroll(prevTouchPoint, touchPoint, handoffState);
   }
 }
 
 ScreenPoint AsyncPanZoomController::GetFirstTouchScreenPoint(const MultiTouchInput& aEvent) {
   return ((SingleTouchData&)aEvent.mTouches[0]).mScreenPoint;
 }
 
 void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1005,18 +1005,17 @@ public:
    * to; this function increments the chain and the index and passes it on to
    * APZCTreeManager::DispatchScroll() in the event of overscroll.
    * Returns true iff. this APZC, or an APZC further down the
    * handoff chain, accepted the scroll (possibly entering an overscrolled
    * state). If this returns false, the caller APZC knows that it should enter
    * an overscrolled state itself if it can.
    */
   bool AttemptScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
-                     const OverscrollHandoffChain& aOverscrollHandoffChain,
-                     uint32_t aOverscrollHandoffChainIndex = 0);
+                     OverscrollHandoffState& aOverscrollHandoffState);
 
   void FlushRepaintForOverscrollHandoff();
 
   /**
    * If overscrolled, start a snap-back animation and return true.
    * Otherwise return false.
    */
   bool SnapBackIfOverscrolled();
@@ -1024,18 +1023,25 @@ public:
 private:
   /**
    * A helper function for calling APZCTreeManager::DispatchScroll().
    * Guards against the case where the APZC is being concurrently destroyed
    * (and thus mTreeManager is being nulled out).
    */
   bool CallDispatchScroll(const ScreenPoint& aStartPoint,
                           const ScreenPoint& aEndPoint,
-                          const OverscrollHandoffChain& aOverscrollHandoffChain,
-                          uint32_t aOverscrollHandoffChainIndex);
+                          OverscrollHandoffState& aOverscrollHandoffState);
+
+  /**
+   * A helper function for overscrolling during panning. This is a wrapper
+   * around OverscrollBy() that also implements restrictions on entering
+   * overscroll based on the pan angle.
+   */
+  bool OverscrollForPanning(ScreenPoint aOverscroll,
+                            const ScreenPoint& aPanDistance);
 
   /**
    * Try to overscroll by 'aOverscroll'.
    * If we are pannable, 'aOverscroll' is added to any existing overscroll,
    * and the function returns true.
    * Otherwise, nothing happens and the function return false.
    */
   bool OverscrollBy(const ScreenPoint& aOverscroll);
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -2,17 +2,17 @@
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* 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/. */
 
 #include "InputBlockState.h"
 #include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
 #include "gfxPrefs.h"                       // for gfxPrefs
-#include "OverscrollHandoffChain.h"
+#include "OverscrollHandoffState.h"
 
 #define TBS_LOG(...)
 // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 InputBlockState::InputBlockState(const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain)
rename from gfx/layers/apz/src/OverscrollHandoffChain.cpp
rename to gfx/layers/apz/src/OverscrollHandoffState.cpp
--- a/gfx/layers/apz/src/OverscrollHandoffChain.cpp
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* 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/. */
 
-#include "OverscrollHandoffChain.h"
+#include "OverscrollHandoffState.h"
 
 #include <algorithm>              // for std::stable_sort
 #include "mozilla/Assertions.h"
 #include "AsyncPanZoomController.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -85,23 +85,35 @@ void
 OverscrollHandoffChain::ClearOverscroll() const
 {
   ForEachApzc(&AsyncPanZoomController::ClearOverscroll);
 }
 
 void
 OverscrollHandoffChain::SnapBackOverscrolledApzc() const
 {
-  for (uint32_t i = 0; i < Length(); ++i) {
+  uint32_t i = 0;
+  for (i = 0; i < Length(); ++i) {
     AsyncPanZoomController* apzc = mChain[i];
     if (!apzc->IsDestroyed() && apzc->SnapBackIfOverscrolled()) {
       // At most one APZC along the hand-off chain can be overscrolled.
       break;
     }
   }
+
+  // In debug builds, verify our assumption that only one APZC is overscrolled.
+#ifdef DEBUG
+  ++i;
+  for (; i < Length(); ++i) {
+    AsyncPanZoomController* apzc = mChain[i];
+    if (!apzc->IsDestroyed()) {
+      MOZ_ASSERT(!apzc->IsOverscrolled());
+    }
+  }
+#endif
 }
 
 bool
 OverscrollHandoffChain::CanBePanned(const AsyncPanZoomController* aApzc) const
 {
   // Find |aApzc| in the handoff chain.
   uint32_t i = IndexOf(aApzc);
 
rename from gfx/layers/apz/src/OverscrollHandoffChain.h
rename to gfx/layers/apz/src/OverscrollHandoffState.h
--- a/gfx/layers/apz/src/OverscrollHandoffChain.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -105,15 +105,37 @@ public:
   bool CanBePanned(const AsyncPanZoomController* aApzc) const;
 private:
   std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
 
   typedef void (AsyncPanZoomController::*APZCMethod)();
   void ForEachApzc(APZCMethod aMethod) const;
 };
 
+/**
+ * This class groups the state maintained during overscroll handoff.
+ */
+struct OverscrollHandoffState {
+  OverscrollHandoffState(const OverscrollHandoffChain& aChain,
+                         const ScreenPoint& aPanDistance)
+      : mChain(aChain), mChainIndex(0), mPanDistance(aPanDistance) {}
+
+  // The chain of APZCs along which we hand off scroll.
+  // This is const to indicate that the chain does not change over the
+  // course of handoff.
+  const OverscrollHandoffChain& mChain;
+
+  // The index of the APZC in the chain that we are currently giving scroll to.
+  // This is non-const to indicate that this changes over the course of handoff.
+  uint32_t mChainIndex;
+
+  // The total distance since touch-start of the pan that triggered the
+  // handoff. This is const to indicate that it does not change over the
+  // course of handoff.
+  const ScreenPoint mPanDistance;
+};
 // Don't pollute other files with this macro for now.
 #undef NS_INLINE_DECL_THREADSAFE_MUTABLE_REFCOUNTING
 
 }
 }
 
 #endif /* mozilla_layers_OverscrollHandoffChain_h */
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -239,17 +239,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         ]
 
 UNIFIED_SOURCES += [
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/Axis.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/InputBlockState.cpp',
-    'apz/src/OverscrollHandoffChain.cpp',
+    'apz/src/OverscrollHandoffState.cpp',
     'apz/src/TaskThrottler.cpp',
     'apz/testutil/APZTestData.cpp',
     'apz/util/ActiveElementManager.cpp',
     'apz/util/APZCCallbackHelper.cpp',
     'AxisPhysicsModel.cpp',
     'AxisPhysicsMSDModel.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -152,16 +152,17 @@ private:
   DECL_GFX_PREF(Once, "apz.fling_stopped_threshold",           APZFlingStoppedThreshold, float, 0.01f);
   DECL_GFX_PREF(Once, "apz.max_velocity_inches_per_ms",        APZMaxVelocity, float, -1.0f);
   DECL_GFX_PREF(Once, "apz.max_velocity_queue_size",           APZMaxVelocityQueueSize, uint32_t, 5);
   DECL_GFX_PREF(Live, "apz.min_skate_speed",                   APZMinSkateSpeed, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.num_paint_duration_samples",        APZNumPaintDurationSamples, int32_t, 3);
   DECL_GFX_PREF(Live, "apz.overscroll.enabled",                APZOverscrollEnabled, bool, false);
   DECL_GFX_PREF(Live, "apz.overscroll.fling_friction",         APZOverscrollFlingFriction, float, 0.02f);
   DECL_GFX_PREF(Live, "apz.overscroll.fling_stopped_threshold", APZOverscrollFlingStoppedThreshold, float, 0.4f);
+  DECL_GFX_PREF(Live, "apz.overscroll.min_pan_distance_ratio", APZMinPanDistanceRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "apz.overscroll.stretch_factor",         APZOverscrollStretchFactor, float, 0.5f);
   DECL_GFX_PREF(Live, "apz.overscroll.snap_back.spring_stiffness", APZOverscrollSnapBackSpringStiffness, float, 0.6f);
   DECL_GFX_PREF(Live, "apz.overscroll.snap_back.spring_friction", APZOverscrollSnapBackSpringFriction, float, 0.1f);
   DECL_GFX_PREF(Live, "apz.overscroll.snap_back.mass",         APZOverscrollSnapBackMass, float, 1000.0f);
   DECL_GFX_PREF(Live, "apz.pan_repaint_interval",              APZPanRepaintInterval, int32_t, 250);
   DECL_GFX_PREF(Live, "apz.printtree",                         APZPrintTree, bool, false);
   DECL_GFX_PREF(Live, "apz.smooth_scroll_repaint_interval",    APZSmoothScrollRepaintInterval, int32_t, 75);
   DECL_GFX_PREF(Live, "apz.subframe.enabled",                  APZSubframeEnabled, bool, false);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -459,16 +459,17 @@ pref("apz.fling_stop_on_tap_threshold", 
 pref("apz.fling_stopped_threshold", "0.01");
 pref("apz.max_velocity_inches_per_ms", "-1.0");
 pref("apz.max_velocity_queue_size", 5);
 pref("apz.min_skate_speed", "1.0");
 pref("apz.num_paint_duration_samples", 3);
 pref("apz.overscroll.enabled", false);
 pref("apz.overscroll.fling_friction", "0.02");
 pref("apz.overscroll.fling_stopped_threshold", "0.4");
+pref("apz.overscroll.min_pan_distance_ratio", "1.0");
 pref("apz.overscroll.stretch_factor", "0.5");
 pref("apz.overscroll.snap_back.spring_stiffness", "0.6");
 pref("apz.overscroll.snap_back.spring_friction", "0.1");
 pref("apz.overscroll.snap_back.mass", "1000.0");
 
 // Whether to print the APZC tree for debugging
 pref("apz.printtree", false);