diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 9f5e56ccf0d4..4f99f896e53a 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -1417,6 +1417,14 @@ void APZCTreeManager::DispatchFling(AsyncPanZoomController* aPrev, FlingHandoffState& aHandoffState) { + // If immediate handoff is disallowed, do not allow handoff beyond the + // single APZC that's scrolled by the input block that triggered this fling. + if (aHandoffState.mIsHandoff && + !gfxPrefs::APZAllowImmediateHandoff() && + aHandoffState.mScrolledApzc == aPrev) { + return; + } + const OverscrollHandoffChain* chain = aHandoffState.mChain; RefPtr current; uint32_t overscrollHandoffChainLength = chain->Length(); diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 1ab89969a6bb..3d6d4584043b 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -444,10 +444,12 @@ class FlingAnimation: public AsyncPanZoomAnimation { public: FlingAnimation(AsyncPanZoomController& aApzc, const RefPtr& aOverscrollHandoffChain, - bool aApplyAcceleration) + bool aApplyAcceleration, + const RefPtr& aScrolledApzc) : AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval())) , mApzc(aApzc) , mOverscrollHandoffChain(aOverscrollHandoffChain) + , mScrolledApzc(aScrolledApzc) { MOZ_ASSERT(mOverscrollHandoffChain); TimeStamp now = aApzc.GetFrameTime(); @@ -572,7 +574,8 @@ public: mDeferredTasks.append(NewRunnableMethod(&mApzc, &AsyncPanZoomController::HandleFlingOverscroll, velocity, - mOverscrollHandoffChain)); + mOverscrollHandoffChain, + mScrolledApzc)); // If there is a remaining velocity on this APZC, continue this fling // as well. (This fling and the handed-off fling will run concurrently.) @@ -600,6 +603,7 @@ private: AsyncPanZoomController& mApzc; RefPtr mOverscrollHandoffChain; + RefPtr mScrolledApzc; }; class ZoomAnimation: public AsyncPanZoomAnimation { @@ -1375,7 +1379,8 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) { FlingHandoffState handoffState{flingVelocity, CurrentTouchBlock()->GetOverscrollHandoffChain(), - false /* not handoff */}; + false /* not handoff */, + CurrentTouchBlock()->GetScrolledApzc()}; treeManagerLocal->DispatchFling(this, handoffState); } return nsEventStatus_eConsumeNoDefault; @@ -2428,7 +2433,8 @@ void AsyncPanZoomController::AcceptFling(FlingHandoffState& aHandoffState) { SetState(FLING); FlingAnimation *fling = new FlingAnimation(*this, aHandoffState.mChain, - !aHandoffState.mIsHandoff); // only apply acceleration if this is an initial fling + !aHandoffState.mIsHandoff, // only apply acceleration if this is an initial fling + aHandoffState.mScrolledApzc); float friction = gfxPrefs::APZFlingFriction(); ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity()); @@ -2477,12 +2483,14 @@ bool AsyncPanZoomController::AttemptFling(FlingHandoffState& aHandoffState) { } void AsyncPanZoomController::HandleFlingOverscroll(const ParentLayerPoint& aVelocity, - const RefPtr& aOverscrollHandoffChain) { + const RefPtr& aOverscrollHandoffChain, + const RefPtr& aScrolledApzc) { APZCTreeManager* treeManagerLocal = GetApzcTreeManager(); if (treeManagerLocal) { FlingHandoffState handoffState{aVelocity, aOverscrollHandoffChain, - true /* handoff */}; + true /* handoff */, + aScrolledApzc}; treeManagerLocal->DispatchFling(this, handoffState); if (!IsZero(handoffState.mVelocity) && IsPannable() && gfxPrefs::APZOverscrollEnabled()) { StartOverscrollAnimation(handoffState.mVelocity); @@ -2493,7 +2501,7 @@ void AsyncPanZoomController::HandleFlingOverscroll(const ParentLayerPoint& aVelo void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity) { // We must call BuildOverscrollHandoffChain from this deferred callback // function in order to avoid a deadlock when acquiring the tree lock. - HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain()); + HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain(), nullptr); } void AsyncPanZoomController::StartSmoothScroll(ScrollSource aSource) { diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 69bec30a3f9e..1f8bf0f38d05 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -867,7 +867,8 @@ private: // later in the handoff chain, or if there are no takers, continuing the // fling and entering an overscrolled state. void HandleFlingOverscroll(const ParentLayerPoint& aVelocity, - const RefPtr& aOverscrollHandoffChain); + const RefPtr& aOverscrollHandoffChain, + const RefPtr& aScrolledApzc); void HandleSmoothScrollOverscroll(const ParentLayerPoint& aVelocity); diff --git a/gfx/layers/apz/src/OverscrollHandoffState.h b/gfx/layers/apz/src/OverscrollHandoffState.h index 3a6aacd7d678..9544aa22c4f1 100644 --- a/gfx/layers/apz/src/OverscrollHandoffState.h +++ b/gfx/layers/apz/src/OverscrollHandoffState.h @@ -143,6 +143,11 @@ struct FlingHandoffState { // Whether handoff has happened by this point, or we're still process // the original fling. bool mIsHandoff; + + // The single APZC that was scrolled by the pan that started this fling. + // The fling is only allowed to scroll this APZC, too. + // Used only if immediate scroll handoff is disallowed. + RefPtr mScrolledApzc; }; } // namespace layers diff --git a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp index 5155f950c3ed..20691c528cb5 100644 --- a/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp @@ -3114,6 +3114,38 @@ TEST_F(APZOverscrollHandoffTester, ImmediateHandoffDisallowed_Pan) { EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y); } +TEST_F(APZOverscrollHandoffTester, ImmediateHandoffDisallowed_Fling) { + SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false); + + CreateOverscrollHandoffLayerTree1(); + + RefPtr parentApzc = ApzcOf(root); + RefPtr childApzc = ApzcOf(layers[1]); + + // Pan on the child, enough to get very close to the end, so that the + // subsequent fling reaches the end and has leftover velocity to hand off. + Pan(childApzc, mcc, 60, 12); + + // Allow the fling to run its course. + childApzc->AdvanceAnimationsUntilEnd(); + parentApzc->AdvanceAnimationsUntilEnd(); + + // Verify that the parent has not scrolled. + EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y); + EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y); + + // Pan again on the child. This time, since the child was scrolled to + // its end when the gesture began, we expect the scroll to be handed off. + Pan(childApzc, mcc, 60, 50); + + // Allow the fling to run its course. The fling should also be handed off. + childApzc->AdvanceAnimationsUntilEnd(); + parentApzc->AdvanceAnimationsUntilEnd(); + + // Verify that the parent scrolled from the fling. + EXPECT_GT(parentApzc->GetFrameMetrics().GetScrollOffset().y, 10); +} + class APZEventRegionsTester : public APZCTreeManagerTester { protected: UniquePtr registration;