Add APZ support for mousewheel.acceleration prefs. (bug 1214170 part 1, r=kats)
authorDavid Anderson <danderson@mozilla.com>
Tue, 01 Dec 2015 13:45:49 -0800
changeset 275120 fffdbaaac8a1e76960816e02a08d673c2d59dc57
parent 275119 63946a132b19da9a770931f22abb427272f96e14
child 275121 81783ea8202490f831e6bf4db961cbcb42601c58
push id29747
push usercbook@mozilla.com
push dateWed, 02 Dec 2015 14:21:19 +0000
treeherdermozilla-central@f6ac392322b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1214170
milestone45.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
Add APZ support for mousewheel.acceleration prefs. (bug 1214170 part 1, r=kats)
dom/events/WheelHandlingHelper.cpp
dom/events/WheelHandlingHelper.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/InputBlockState.cpp
gfx/layers/apz/src/InputBlockState.h
gfx/layers/apz/src/InputQueue.cpp
gfx/thebes/gfxPrefs.h
layout/generic/AsyncScrollBase.h
widget/InputData.h
--- a/dom/events/WheelHandlingHelper.cpp
+++ b/dom/events/WheelHandlingHelper.cpp
@@ -16,16 +16,17 @@
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsITimer.h"
 #include "nsPluginFrame.h"
 #include "nsPresContext.h"
 #include "prtime.h"
 #include "Units.h"
+#include "AsyncScrollBase.h"
 
 namespace mozilla {
 
 /******************************************************************/
 /* mozilla::DeltaValues                                           */
 /******************************************************************/
 
 DeltaValues::DeltaValues(WidgetWheelEvent* aEvent)
@@ -132,17 +133,17 @@ WheelTransaction::UpdateTransaction(Widg
     OnFailToScrollTarget();
     // We should not modify the transaction state when the view will not be
     // scrolled actually.
     return false;
   }
 
   SetTimeout();
 
-  if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeout)) {
+  if (sScrollSeriesCounter != 0 && OutOfTime(sTime, kScrollSeriesTimeoutMs)) {
     sScrollSeriesCounter = 0;
   }
   sScrollSeriesCounter++;
 
   // We should use current time instead of WidgetEvent.time.
   // 1. Some events doesn't have the correct creation time.
   // 2. If the computer runs slowly by other processes eating the CPU resource,
   //    the event creation time doesn't keep real time.
@@ -378,24 +379,19 @@ WheelTransaction::AccelerateWheelDelta(W
       result.deltaY = ComputeAcceleratedWheelDelta(result.deltaY, factor);
     }
   }
 
   return result;
 }
 
 /* static */ double
-WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta,
-                                               int32_t aFactor)
+WheelTransaction::ComputeAcceleratedWheelDelta(double aDelta, int32_t aFactor)
 {
-  if (aDelta == 0.0) {
-    return 0;
-  }
-
-  return (aDelta * sScrollSeriesCounter * (double)aFactor / 10);
+  return mozilla::ComputeAcceleratedWheelDelta(aDelta, sScrollSeriesCounter, aFactor);
 }
 
 /* static */ int32_t
 WheelTransaction::GetAccelerationStart()
 {
   return Preferences::GetInt("mousewheel.acceleration.start", -1);
 }
 
--- a/dom/events/WheelHandlingHelper.h
+++ b/dom/events/WheelHandlingHelper.h
@@ -146,18 +146,16 @@ public:
   static uint32_t GetTimeoutTime();
 
   static void OwnScrollbars(bool aOwn);
 
   static DeltaValues AccelerateWheelDelta(WidgetWheelEvent* aEvent,
                                           bool aAllowScrollSpeedOverride);
 
 protected:
-  static const uint32_t kScrollSeriesTimeout = 80; // in milliseconds
-
   static void BeginTransaction(nsIFrame* aTargetFrame,
                                WidgetWheelEvent* aEvent);
   // Be careful, UpdateTransaction may fire a DOM event, therefore, the target
   // frame might be destroyed in the event handler.
   static bool UpdateTransaction(WidgetWheelEvent* aEvent);
   static void MayEndTransaction();
 
   static nsIntPoint GetScreenPoint(WidgetGUIEvent* aEvent);
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -620,18 +620,18 @@ APZCTreeManager::UpdateHitTestingTree(Tr
 // scroll.
 //
 // Even if this returns false, all wheel events in APZ-aware widgets must
 // be sent through APZ so they are transformed correctly for TabParent.
 static bool
 WillHandleWheelEvent(WidgetWheelEvent* aEvent)
 {
   return EventStateManager::WheelEventIsScrollAction(aEvent) &&
-         (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE
-            || aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) &&
+         (aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_LINE ||
+          aEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) &&
          !EventStateManager::WheelEventNeedsDeltaMultipliers(aEvent);
 }
 
 static bool
 WillHandleMouseEvent(const WidgetMouseEventBase& aEvent)
 {
   if (!gfxPrefs::APZDragEnabled()) {
     return false;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1611,16 +1611,31 @@ AsyncPanZoomController::GetScrollWheelDe
     if (vfactor > 1.0) {
       delta.x *= hfactor;
     }
     if (hfactor > 1.0) {
       delta.y *= vfactor;
     }
   }
 
+  // If this is a line scroll, and this event was part of a scroll series, then
+  // it might need extra acceleration. See WheelHandlingHelper.cpp.
+  if (aEvent.mDeltaType == ScrollWheelInput::SCROLLDELTA_LINE &&
+      aEvent.mScrollSeriesNumber > 0)
+  {
+    int32_t start = gfxPrefs::MouseWheelAccelerationStart();
+    if (start >= 0 && aEvent.mScrollSeriesNumber >= uint32_t(start)) {
+      int32_t factor = gfxPrefs::MouseWheelAccelerationFactor();
+      if (factor > 0) {
+        delta.x = ComputeAcceleratedWheelDelta(delta.x, aEvent.mScrollSeriesNumber, factor);
+        delta.y = ComputeAcceleratedWheelDelta(delta.y, aEvent.mScrollSeriesNumber, factor);
+      }
+    }
+  }
+
   if (Abs(delta.x) > pageScrollSize.width) {
     delta.x = (delta.x >= 0)
               ? pageScrollSize.width
               : -pageScrollSize.width;
   }
   if (Abs(delta.y) > pageScrollSize.height) {
     delta.y = (delta.y >= 0)
               ? pageScrollSize.height
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "InputBlockState.h"
 #include "AsyncPanZoomController.h"         // for AsyncPanZoomController
+#include "AsyncScrollBase.h"                // for kScrollSeriesTimeoutMs
 #include "gfxPrefs.h"                       // for gfxPrefs
 #include "mozilla/MouseEvents.h"
 #include "mozilla/SizePrintfMacros.h"       // for PRIuSIZE
 #include "mozilla/layers/APZCTreeManager.h" // for AllowedTouchBehavior
 #include "OverscrollHandoffState.h"
 
 #define TBS_LOG(...)
 // #define TBS_LOG(...) printf_stderr("TBS: " __VA_ARGS__)
@@ -239,16 +240,17 @@ DragBlockState::Type()
 }
 // This is used to track the current wheel transaction.
 static uint64_t sLastWheelBlockId = InputBlockState::NO_BLOCK_ID;
 
 WheelBlockState::WheelBlockState(const RefPtr<AsyncPanZoomController>& aTargetApzc,
                                  bool aTargetConfirmed,
                                  const ScrollWheelInput& aInitialEvent)
   : CancelableBlockState(aTargetApzc, aTargetConfirmed)
+  , mScrollSeriesCounter(0)
   , mTransactionEnded(false)
 {
   sLastWheelBlockId = GetBlockId();
 
   if (aTargetConfirmed) {
     // Find the nearest APZC in the overscroll handoff chain that is scrollable.
     // If we get a content confirmation later that the apzc is different, then
     // content should have found a scrollable apzc, so we don't need to handle
@@ -290,24 +292,36 @@ WheelBlockState::SetConfirmedTargetApzc(
     apzc = apzc->BuildOverscrollHandoffChain()->FindFirstScrollable(event);
   }
 
   InputBlockState::SetConfirmedTargetApzc(apzc);
   return true;
 }
 
 void
-WheelBlockState::Update(const ScrollWheelInput& aEvent)
+WheelBlockState::Update(ScrollWheelInput& aEvent)
 {
   // We might not be in a transaction if the block never started in a
   // transaction - for example, if nothing was scrollable.
   if (!InTransaction()) {
     return;
   }
 
+  // The current "scroll series" is a like a sub-transaction. It has a separate
+  // timeout of 80ms. Since we need to compute wheel deltas at different phases
+  // of a transaction (for example, when it is updated, and later when the
+  // event action is taken), we affix the scroll series counter to the event.
+  // This makes GetScrollWheelDelta() consistent.
+  if (!mLastEventTime.IsNull() &&
+      (aEvent.mTimeStamp - mLastEventTime).ToMilliseconds() > kScrollSeriesTimeoutMs)
+  {
+    mScrollSeriesCounter = 0;
+  }
+  aEvent.mScrollSeriesNumber = ++mScrollSeriesCounter;
+
   // If we can't scroll in the direction of the wheel event, we don't update
   // the last move time. This allows us to timeout a transaction even if the
   // mouse isn't moving.
   //
   // We skip this check if the target is not yet confirmed, so that when it is
   // confirmed, we don't timeout the transaction.
   RefPtr<AsyncPanZoomController> apzc = GetTargetApzc();
   if (IsTargetConfirmed() && !apzc->CanScroll(aEvent)) {
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -239,25 +239,26 @@ public:
    *
    * @return True if the transaction ended, false otherwise.
    */
   bool MaybeTimeout(const TimeStamp& aTimeStamp);
 
   /**
    * Update the wheel transaction state for a new event.
    */
-  void Update(const ScrollWheelInput& aEvent);
+  void Update(ScrollWheelInput& aEvent);
 
 protected:
   void UpdateTargetApzc(const RefPtr<AsyncPanZoomController>& aTargetApzc) override;
 
 private:
   nsTArray<ScrollWheelInput> mEvents;
   TimeStamp mLastEventTime;
   TimeStamp mLastMouseMove;
+  uint32_t mScrollSeriesCounter;
   bool mTransactionEnded;
 };
 
 /**
  * A block of mouse events that are part of a drag
  */
 class DragBlockState : public CancelableBlockState
 {
--- a/gfx/layers/apz/src/InputQueue.cpp
+++ b/gfx/layers/apz/src/InputQueue.cpp
@@ -272,25 +272,27 @@ InputQueue::ReceiveScrollWheelInput(cons
   } else {
     INPQ_LOG("received new event in block %p\n", block);
   }
 
   if (aOutInputBlockId) {
     *aOutInputBlockId = block->GetBlockId();
   }
 
-  block->Update(aEvent);
+  // Copy the event, since WheelBlockState needs to affix a counter.
+  ScrollWheelInput event(aEvent);
+  block->Update(event);
 
   // Note that the |aTarget| the APZCTM sent us may contradict the confirmed
   // target set on the block. In this case the confirmed target (which may be
   // null) should take priority. This is equivalent to just always using the
   // target (confirmed or not) from the block, which is what
   // MaybeHandleCurrentBlock() does.
-  if (!MaybeHandleCurrentBlock(block, aEvent)) {
-    block->AddEvent(aEvent.AsScrollWheelInput());
+  if (!MaybeHandleCurrentBlock(block, event)) {
+    block->AddEvent(event);
   }
 
   return nsEventStatus_eConsumeDoDefault;
 }
 
 static bool
 CanScrollTargetHorizontally(const PanGestureInput& aInitialEvent,
                             PanGestureBlockState* aBlock)
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -378,16 +378,20 @@ private:
   DECL_GFX_PREF(Live, "layout.display-list.dump",              LayoutDumpDisplayList, bool, false);
   DECL_GFX_PREF(Live, "layout.event-regions.enabled",          LayoutEventRegionsEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);
 
   // This and code dependent on it should be removed once containerless scrolling looks stable.
   DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
 
+  // These affect how line scrolls from wheel events will be accelerated.
+  DECL_GFX_PREF(Live, "mousewheel.acceleration.factor",        MouseWheelAccelerationFactor, int32_t, -1);
+  DECL_GFX_PREF(Live, "mousewheel.acceleration.start",         MouseWheelAccelerationStart, int32_t, -1);
+
   // This affects whether events will be routed through APZ or not.
   DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
                                                                MouseWheelHasRootScrollDeltaOverride, bool, false);
   DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.horizontal.factor",
                                                                MouseWheelRootHScrollDeltaFactor, int32_t, 100);
   DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.vertical.factor",
                                                                MouseWheelRootVScrollDeltaFactor, int32_t, 100);
   DECL_GFX_PREF(Live, "mousewheel.transaction.ignoremovedelay",MouseWheelIgnoreMoveDelayMs, int32_t, (int32_t)100);
--- a/layout/generic/AsyncScrollBase.h
+++ b/layout/generic/AsyncScrollBase.h
@@ -82,11 +82,24 @@ protected:
 
   nsPoint mStartPos;
   TimeDuration mDuration;
   nsPoint mDestination;
   nsSMILKeySpline mTimingFunctionX;
   nsSMILKeySpline mTimingFunctionY;
 };
 
+// Helper for accelerated wheel deltas. This can be called from the main thread
+// or the APZ Controller thread.
+static inline double
+ComputeAcceleratedWheelDelta(double aDelta, int32_t aCounter, int32_t aFactor)
+{
+  if (!aDelta) {
+    return aDelta;
+  }
+  return (aDelta * aCounter * double(aFactor) / 10);
+}
+
+static const uint32_t kScrollSeriesTimeoutMs = 80; // in milliseconds
+
 } // namespace mozilla
 
 #endif // mozilla_layout_AsyncScrollBase_h_
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -577,16 +577,17 @@ public:
      mDeltaType(aDeltaType),
      mScrollMode(aScrollMode),
      mOrigin(aOrigin),
      mHandledByAPZ(false),
      mDeltaX(aDeltaX),
      mDeltaY(aDeltaY),
      mLineOrPageDeltaX(0),
      mLineOrPageDeltaY(0),
+     mScrollSeriesNumber(0),
      mIsMomentum(false)
   {}
 
   explicit ScrollWheelInput(const WidgetWheelEvent& aEvent);
 
   WidgetWheelEvent ToWidgetWheelEvent(nsIWidget* aWidget) const;
   bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
@@ -610,14 +611,18 @@ public:
   // The location of the scroll in local coordinates. This is set and used by
   // APZ.
   ParentLayerPoint mLocalOrigin;
 
   // See lineOrPageDeltaX/Y on WidgetWheelEvent.
   int32_t mLineOrPageDeltaX;
   int32_t mLineOrPageDeltaY;
 
+  // Indicates the order in which this event was added to a transaction. The
+  // first event is 1; if not a member of a transaction, this is 0.
+  uint32_t mScrollSeriesNumber;
+
   bool mIsMomentum;
 };
 
 } // namespace mozilla
 
 #endif // InputData_h__