Bug 1034376 - Avoid APZC being stuck in overscrolled state when CancelAnimation() is called during panning. r=kats

--HG--
extra : rebase_source : bbc3a02d6be7081be77b58cb2795aa57ce794002
This commit is contained in:
Botond Ballo 2014-07-14 12:54:41 -04:00
parent f2893d2ba1
commit 7c1923f565
3 changed files with 54 additions and 40 deletions

View File

@ -412,34 +412,13 @@ GetFrameTime() {
return sFrameTime;
}
// This is a base class for animations that can deal with
// overscroll states. In particular, it ensures that overscroll
// states are cleared when the animation is cancelled.
class OverscrollableAnimation: public AsyncPanZoomAnimation {
public:
OverscrollableAnimation(AsyncPanZoomController& aApzc,
const TimeDuration& aRepaintInterval = TimeDuration::Forever())
: AsyncPanZoomAnimation(aRepaintInterval)
, mApzc(aApzc)
{
}
virtual void Cancel() MOZ_OVERRIDE
{
mApzc.mX.ClearOverscroll();
mApzc.mY.ClearOverscroll();
}
protected:
AsyncPanZoomController& mApzc;
};
class FlingAnimation: public OverscrollableAnimation {
class FlingAnimation: public AsyncPanZoomAnimation {
public:
FlingAnimation(AsyncPanZoomController& aApzc,
bool aApplyAcceleration,
bool aAllowOverscroll)
: OverscrollableAnimation(aApzc, TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
, mApzc(aApzc)
, mAllowOverscroll(aAllowOverscroll)
{
TimeStamp now = GetFrameTime();
@ -594,6 +573,7 @@ private:
+ (aSupplemental * gfxPrefs::APZFlingAccelSupplementalMultiplier());
}
AsyncPanZoomController& mApzc;
bool mAllowOverscroll;
};
@ -656,10 +636,10 @@ private:
CSSToScreenScale mEndZoom;
};
class OverscrollSnapBackAnimation: public OverscrollableAnimation {
class OverscrollSnapBackAnimation: public AsyncPanZoomAnimation {
public:
OverscrollSnapBackAnimation(AsyncPanZoomController& aApzc)
: OverscrollableAnimation(aApzc)
: mApzc(aApzc)
{
}
@ -671,6 +651,8 @@ public:
bool continueY = mApzc.mY.SampleSnapBack(aDelta);
return continueX || continueY;
}
private:
AsyncPanZoomController& mApzc;
};
void
@ -1771,10 +1753,13 @@ void AsyncPanZoomController::CancelAnimation() {
ReentrantMonitorAutoEnter lock(mMonitor);
APZC_LOG("%p running CancelAnimation in state %d\n", this, mState);
SetState(NOTHING);
if (mAnimation) {
mAnimation->Cancel();
mAnimation = nullptr;
// mAnimation->Cancel() may have done something that requires a repaint.
mAnimation = nullptr;
// Setting the state to nothing and cancelling the animation can
// preempt normal mechanisms for relieving overscroll, so we need to clear
// overscroll here.
if (mX.IsOverscrolled() || mY.IsOverscrolled()) {
mX.ClearOverscroll();
mY.ClearOverscroll();
RequestContentRepaint();
ScheduleComposite();
UpdateSharedCompositorFrameMetrics();

View File

@ -800,7 +800,6 @@ public:
bool TakeOverFling(ScreenPoint aVelocity);
private:
friend class OverscrollableAnimation;
friend class FlingAnimation;
friend class OverscrollSnapBackAnimation;
// The initial velocity of the most recent fling.
@ -1039,9 +1038,6 @@ public:
virtual bool Sample(FrameMetrics& aFrameMetrics,
const TimeDuration& aDelta) = 0;
// Called if the animation is cancelled before it ends.
virtual void Cancel() {}
/**
* Get the deferred tasks in |mDeferredTasks|. See |mDeferredTasks|
* for more information.

View File

@ -212,7 +212,8 @@ void ApzcPan(AsyncPanZoomController* apzc,
int aTouchEndY,
bool expectIgnoredPan = false,
bool hasTouchListeners = false,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr) {
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
bool aKeepFingerDown = false) {
const int TIME_BETWEEN_TOUCH_EVENT = 100;
const int OVERCOME_TOUCH_TOLERANCE = 100;
@ -272,11 +273,13 @@ void ApzcPan(AsyncPanZoomController* apzc,
status = apzc->ReceiveInputEvent(mti);
EXPECT_EQ(touchMoveStatus, status);
mti =
MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
aTime += TIME_BETWEEN_TOUCH_EVENT;
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
status = apzc->ReceiveInputEvent(mti);
if (!aKeepFingerDown) {
mti =
MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
aTime += TIME_BETWEEN_TOUCH_EVENT;
mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
status = apzc->ReceiveInputEvent(mti);
}
// Since we've explicitly built the overscroll handoff chain before
// touch-start, we need to explicitly clear it after touch-end.
@ -966,6 +969,36 @@ TEST_F(AsyncPanZoomControllerTester, OverScrollAbort) {
apzc->Destroy();
}
TEST_F(AsyncPanZoomControllerTester, OverScrollPanningAbort) {
TestScopedBoolPref overscrollEnabledPref("apz.overscroll.enabled", true);
nsRefPtr<MockContentController> mcc = new NiceMock<MockContentController>();
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();
nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc, tm);
apzc->SetFrameMetrics(TestFrameMetrics());
apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
// Pan sufficiently to hit overscroll behaviour. Keep the finger down so
// the pan does not end.
int time = 0;
int touchStart = 500;
int touchEnd = 10;
ApzcPan(apzc, tm, time, touchStart, touchEnd,
false, false, nullptr, // filling it defaults, wish we had named arguments
true); // keep finger down
EXPECT_TRUE(apzc->IsOverscrolled());
// Check that calling CancelAnimation() while the user is still panning
// (and thus no fling or snap-back animation has had a chance to start)
// clears the overscroll.
apzc->CancelAnimation();
EXPECT_FALSE(apzc->IsOverscrolled());
apzc->AssertStateIsReset();
apzc->Destroy();
}
TEST_F(AsyncPanZoomControllerTester, ShortPress) {
nsRefPtr<MockContentControllerDelayed> mcc = new NiceMock<MockContentControllerDelayed>();
nsRefPtr<TestAPZCTreeManager> tm = new TestAPZCTreeManager();