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
--- 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',