Bug 1105109 - Add autoscrolling support to AsyncPanZoomController. r=kats
authorBotond Ballo <botond@mozilla.com>
Fri, 28 Jul 2017 15:04:00 -0400
changeset 420741 6b28c83f4ef4af43076a93e177aabc2d412c3c4c
parent 420740 bb484002766ec20f9d7167fec4ec995a043a0b13
child 420742 ab07a3654f59dfc98ed7a884d84936123af3fca3
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1105109
milestone56.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 1105109 - Add autoscrolling support to AsyncPanZoomController. r=kats This involves adding a new type of AsyncPanZoomAnimation, a new APZC state, and methods to start and stop autoscrolling. MozReview-Commit-ID: BEYPJIR30Lw
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/AutoscrollAnimation.cpp
gfx/layers/apz/src/AutoscrollAnimation.h
gfx/layers/moz.build
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -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/. */
 
 #include <math.h>                       // for fabsf, fabs, atan2
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include <sys/types.h>                  // for int32_t
 #include <algorithm>                    // for max, min
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController, etc
+#include "AutoscrollAnimation.h"        // for AutoscrollAnimation
 #include "Axis.h"                       // for AxisX, AxisY, Axis, etc
 #include "CheckerboardEvent.h"          // for CheckerboardEvent
 #include "Compositor.h"                 // for Compositor
 #include "FrameMetrics.h"               // for FrameMetrics, etc
 #include "GenericFlingAnimation.h"      // for GenericFlingAnimation
 #include "GestureEventListener.h"       // for GestureEventListener
 #include "HitTestingTreeNode.h"         // for HitTestingTreeNode
 #include "InputData.h"                  // for MultiTouchInput, etc
@@ -1065,29 +1066,46 @@ nsEventStatus AsyncPanZoomController::Ha
   return rv;
 }
 
 void AsyncPanZoomController::HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY)
 {
   mY.HandleTouchVelocity(aTimesampMs, aSpeedY);
 }
 
+void AsyncPanZoomController::StartAutoscroll(const ScreenPoint& aPoint)
+{
+  // Cancel any existing animation.
+  CancelAnimation();
+
+  SetState(AUTOSCROLL);
+  StartAnimation(new AutoscrollAnimation(*this, aPoint));
+}
+
+void AsyncPanZoomController::StopAutoscroll()
+{
+  if (mState == AUTOSCROLL) {
+    CancelAnimation();
+  }
+}
+
 nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent) {
   APZC_LOG("%p got a touch-start in state %d\n", this, mState);
   mPanDirRestricted = false;
   ParentLayerPoint point = GetFirstTouchPoint(aEvent);
 
   switch (mState) {
     case FLING:
     case ANIMATING_ZOOM:
     case SMOOTH_SCROLL:
     case OVERSCROLL_ANIMATION:
     case WHEEL_SCROLL:
     case KEYBOARD_SCROLL:
     case PAN_MOMENTUM:
+    case AUTOSCROLL:
       MOZ_ASSERT(GetCurrentTouchBlock());
       GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations(ExcludeOverscroll);
       MOZ_FALLTHROUGH;
     case NOTHING: {
       mX.StartTouch(point.x, aEvent.mTime);
       mY.StartTouch(point.y, aEvent.mTime);
       if (RefPtr<GeckoContentController> controller = GetGeckoContentController()) {
         MOZ_ASSERT(GetCurrentTouchBlock());
@@ -1154,16 +1172,17 @@ nsEventStatus AsyncPanZoomController::On
     case PINCHING:
       // The scale gesture listener should have handled this.
       NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
       return nsEventStatus_eIgnore;
 
     case WHEEL_SCROLL:
     case KEYBOARD_SCROLL:
     case OVERSCROLL_ANIMATION:
+    case AUTOSCROLL:
       // Should not receive a touch-move in the OVERSCROLL_ANIMATION state
       // as touch blocks that begin in an overscrolled state cancel the
       // animation. The same is true for wheel scroll animations.
       NS_WARNING("Received impossible touch in OnTouchMove");
       break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
@@ -1235,16 +1254,17 @@ nsEventStatus AsyncPanZoomController::On
     SetState(NOTHING);
     // Scale gesture listener should have handled this.
     NS_WARNING("Gesture listener should have handled pinching in OnTouchEnd.");
     return nsEventStatus_eIgnore;
 
   case WHEEL_SCROLL:
   case KEYBOARD_SCROLL:
   case OVERSCROLL_ANIMATION:
+  case AUTOSCROLL:
     // Should not receive a touch-end in the OVERSCROLL_ANIMATION state
     // as touch blocks that begin in an overscrolled state cancel the
     // animation. The same is true for WHEEL_SCROLL.
     NS_WARNING("Received impossible touch in OnTouchEnd");
     break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -284,16 +284,26 @@ public:
    * Handler for touch velocity.
    * Sometimes the touch move event will have a velocity even though no scrolling
    * is occurring such as when the toolbar is being hidden/shown in Fennec.
    * This function can be called to have the y axis' velocity queue updated.
    */
   void HandleTouchVelocity(uint32_t aTimesampMs, float aSpeedY);
 
   /**
+   * Start autoscrolling this APZC, anchored at the provided location.
+   */
+  void StartAutoscroll(const ScreenPoint& aAnchorLocation);
+
+  /**
+   * Stop autoscrolling this APZC.
+   */
+  void StopAutoscroll();
+
+  /**
    * Populates the provided object (if non-null) with the scrollable guid of this apzc.
    */
   void GetGuid(ScrollableLayerGuid* aGuidOut) const;
 
   /**
    * Returns the scrollable guid of this apzc.
    */
   ScrollableLayerGuid GetGuid() const;
@@ -894,17 +904,18 @@ protected:
 
     PINCHING,                 /* nth touch-start, where n > 1. this mode allows pan and zoom */
     ANIMATING_ZOOM,           /* animated zoom to a new rect */
     OVERSCROLL_ANIMATION,     /* Spring-based animation used to relieve overscroll once
                                  the finger is lifted. */
     SMOOTH_SCROLL,            /* Smooth scrolling to destination. Used by
                                  CSSOM-View smooth scroll-behavior */
     WHEEL_SCROLL,             /* Smooth scrolling to a destination for a wheel event. */
-    KEYBOARD_SCROLL           /* Smooth scrolling to a destination for a keyboard event. */
+    KEYBOARD_SCROLL,          /* Smooth scrolling to a destination for a keyboard event. */
+    AUTOSCROLL                /* Autoscroll animation. */
   };
 
   // This is in theory protected by |mMonitor|; that is, it should be held whenever
   // this is updated. In practice though... see bug 897017.
   PanZoomState mState;
 
 private:
   friend class StateChangeNotificationBlocker;
@@ -988,16 +999,17 @@ public:
    * |aHandoffState.mIsHandoff| should be true iff. the fling was handed off
    * from a previous APZC, and determines whether acceleration is applied
    * to the fling.
    */
   bool AttemptFling(FlingHandoffState& aHandoffState);
 
 private:
   friend class AndroidFlingAnimation;
+  friend class AutoscrollAnimation;
   friend class GenericFlingAnimation;
   friend class OverscrollAnimation;
   friend class SmoothScrollAnimation;
   friend class GenericScrollAnimation;
   friend class WheelScrollAnimation;
   friend class KeyboardScrollAnimation;
 
   friend class GenericOverscrollEffect;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/AutoscrollAnimation.cpp
@@ -0,0 +1,84 @@
+/* -*- 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 "AutoscrollAnimation.h"
+
+#include <cmath>  // for sqrtf()
+
+#include "AsyncPanZoomController.h"
+
+namespace mozilla {
+namespace layers {
+
+// Helper function for AutoscrollAnimation::DoSample().
+// Basically copied as-is from toolkit/content/browser-content.js.
+static float
+Accelerate(ScreenCoord curr, ScreenCoord start)
+{
+  static const int speed = 12;
+  float val = (curr - start) / speed;
+  if (val > 1) {
+    return val * sqrtf(val) - 1;
+  }
+  if (val < -1) {
+    return val * sqrtf(-val) + 1;
+  }
+  return 0;
+}
+
+AutoscrollAnimation::AutoscrollAnimation(AsyncPanZoomController& aApzc,
+                                         const ScreenPoint& aAnchorLocation)
+  : mApzc(aApzc)
+  , mAnchorLocation(aAnchorLocation)
+{
+}
+
+bool
+AutoscrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta)
+{
+  APZCTreeManager* treeManager = mApzc.GetApzcTreeManager();
+  if (!treeManager) {
+    return false;
+  }
+
+  ScreenPoint mouseLocation = treeManager->GetCurrentMousePosition();
+
+  // The implementation of this function closely mirrors that of its main-
+  // thread equivalent, the autoscrollLoop() function in
+  // toolkit/content/browser-content.js.
+
+  // Avoid long jumps when the browser hangs for more than |maxTimeDelta| ms.
+  static const TimeDuration maxTimeDelta = TimeDuration::FromMilliseconds(100);
+  TimeDuration timeDelta = TimeDuration::Max(aDelta, maxTimeDelta);
+
+  float timeCompensation = timeDelta.ToMilliseconds() / 20;
+
+  // Notes:
+  //   - The main-thread implementation rounds the scroll delta to an integer,
+  //     and keeps track of the fractional part as an "error". It does this
+  //     because it uses Window.scrollBy() or Element.scrollBy() to perform
+  //     the scrolling, and those functions truncate the fractional part of
+  //     the offset. APZ does no such truncation, so there's no need to keep
+  //     track of the fractional part separately.
+  //   - The Accelerate() function takes Screen coordinates as inputs, but
+  //     its output is interpreted as CSS coordinates. This is intentional,
+  //     insofar as autoscrollLoop() does the same thing.
+  CSSPoint scrollDelta{
+    Accelerate(mouseLocation.x, mAnchorLocation.x) * timeCompensation,
+    Accelerate(mouseLocation.y, mAnchorLocation.y) * timeCompensation
+  };
+
+  mApzc.ScrollByAndClamp(scrollDelta);
+
+  // An autoscroll animation never ends of its own accord.
+  // It can be stopped in response to various input events, in which case
+  // AsyncPanZoomController::StopAutoscroll() will stop it via
+  // CancelAnimation().
+  return true;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/AutoscrollAnimation.h
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_AutocrollAnimation_h_
+#define mozilla_layers_AutocrollAnimation_h_
+
+#include "AsyncPanZoomAnimation.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+
+class AutoscrollAnimation : public AsyncPanZoomAnimation
+{
+public:
+  AutoscrollAnimation(AsyncPanZoomController& aApzc,
+                      const ScreenPoint& aAnchorLocation);
+
+  bool DoSample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) override;
+
+private:
+  AsyncPanZoomController& mApzc;
+  ScreenPoint mAnchorLocation;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_AutoscrollAnimation_h_
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -286,16 +286,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr
     ]
 
 UNIFIED_SOURCES += [
     'AnimationHelper.cpp',
     'AnimationInfo.cpp',
     'apz/public/IAPZCTreeManager.cpp',
     'apz/src/APZCTreeManager.cpp',
     'apz/src/AsyncPanZoomController.cpp',
+    'apz/src/AutoscrollAnimation.cpp',
     'apz/src/Axis.cpp',
     'apz/src/CheckerboardEvent.cpp',
     'apz/src/DragTracker.cpp',
     'apz/src/FocusState.cpp',
     'apz/src/FocusTarget.cpp',
     'apz/src/GenericScrollAnimation.cpp',
     'apz/src/GestureEventListener.cpp',
     'apz/src/HitTestingTreeNode.cpp',