Bug 1105109 - Add autoscrolling support to AsyncPanZoomController. r=kats draft
authorBotond Ballo <botond@mozilla.com>
Fri, 28 Jul 2017 15:04:00 -0400
changeset 617934 81a96fd73348853f9e1a94a9f72f7de0073cc66f
parent 617933 c5773713b1ca8b1b7b95fc476372f0286690e211
child 617935 c482816ecbaec0a889d817851ee15be186b2a49c
push id71164
push userbballo@mozilla.com
push dateSat, 29 Jul 2017 00:34:06 +0000
reviewerskats
bugs1105109
milestone56.0a1
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',