Bug 998025 - Support overscrolling during flinging. r=kats

--HG--
extra : rebase_source : 2c85f4a8ec9e21c3a7012f769a560d0153805d63
This commit is contained in:
Botond Ballo 2014-05-12 18:51:07 -04:00
parent 1f21eb4459
commit 949302511d
4 changed files with 99 additions and 39 deletions

View File

@ -747,7 +747,7 @@ APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev, ScreenPoint aStar
return next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffChainIndex);
}
void
bool
APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVelocity)
{
// Build the overscroll handoff chain. This is necessary because it is
@ -786,7 +786,7 @@ APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVeloci
// Nothing to hand off fling to.
if (next == nullptr) {
return;
return false;
}
// The fling's velocity needs to be transformed from the screen coordinates
@ -802,7 +802,7 @@ APZCTreeManager::HandOffFling(AsyncPanZoomController* aPrev, ScreenPoint aVeloci
ScreenPoint transformedVelocity = endPoint - startPoint;
// Tell |next| to start a fling with the transformed velocity.
next->TakeOverFling(transformedVelocity);
return next->TakeOverFling(transformedVelocity);
}
bool

View File

@ -280,8 +280,11 @@ public:
* @param aApzc the APZC that is handing off the fling
* @param aVelocity the current velocity of the fling, in |aApzc|'s screen
* pixels per millisecond
* Returns true iff. another APZC accepted the handed-off fling. The caller
* (|aApzc|) uses this return value to determine whether it should consume
* the excess fling itself by going into an overscroll fling.
*/
void HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
bool HandOffFling(AsyncPanZoomController* aApzc, ScreenPoint aVelocity);
bool FlushRepaintsForOverscrollHandoffChain();

View File

@ -45,7 +45,7 @@
#include "mozilla/layers/TaskThrottler.h" // for TaskThrottler
#include "mozilla/mozalloc.h" // for operator new, etc
#include "mozilla/unused.h" // for unused
#include "mozilla/FloatingPoint.h" // for FuzzyEqualsMultiplicative
#include "mozilla/FloatingPoint.h" // for FuzzyEquals*
#include "nsAlgorithm.h" // for clamped
#include "nsAutoPtr.h" // for nsRefPtr
#include "nsCOMPtr.h" // for already_AddRefed
@ -352,9 +352,12 @@ GetFrameTime() {
class FlingAnimation: public AsyncPanZoomAnimation {
public:
FlingAnimation(AsyncPanZoomController& aApzc, bool aApplyAcceleration)
FlingAnimation(AsyncPanZoomController& aApzc,
bool aApplyAcceleration,
bool aAllowOverscroll)
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(gfxPrefs::APZFlingRepaintInterval()))
, mApzc(aApzc)
, mAllowOverscroll(aAllowOverscroll)
{
TimeStamp now = GetFrameTime();
ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
@ -410,6 +413,7 @@ private:
}
AsyncPanZoomController& mApzc;
bool mAllowOverscroll;
};
class ZoomAnimation: public AsyncPanZoomAnimation {
@ -849,7 +853,9 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
mX.EndTouch();
mY.EndTouch();
SetState(FLING);
StartAnimation(new FlingAnimation(*this, true));
StartAnimation(new FlingAnimation(*this,
true /* apply acceleration */,
false /* allow overscroll */));
return nsEventStatus_eConsumeNoDefault;
case PINCHING:
@ -1309,13 +1315,42 @@ bool AsyncPanZoomController::OverscrollBy(const CSSPoint& aOverscroll) {
return false;
}
void AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
void AsyncPanZoomController::AcceptFling(const ScreenPoint& aVelocity,
bool aAllowOverscroll) {
// We may have a pre-existing velocity for whatever reason (for example,
// a previously handed off fling). We don't want to clobber that.
mX.SetVelocity(mX.GetVelocity() + aVelocity.x);
mY.SetVelocity(mY.GetVelocity() + aVelocity.y);
SetState(FLING);
StartAnimation(new FlingAnimation(*this, false));
StartAnimation(new FlingAnimation(*this, false /* no acceleration */,
aAllowOverscroll));
}
bool AsyncPanZoomController::TakeOverFling(ScreenPoint aVelocity) {
// If we are pannable, take over the fling ourselves.
if (IsPannable()) {
AcceptFling(aVelocity, false /* do not allow overscroll */);
return true;
}
// Otherwise, hand the fling back to the tree manager to pass on to the
// next APZC in the handoff chain. Had we started a fling animation in this
// APZC, we would have done this hand-off on its first frame anyways, but
// doing it here allows the tree manager to tell the previous APZC to enter
// an overscroll fling if nothing further in the chain wants the fling.
APZCTreeManager* treeManagerLocal = mTreeManager;
return treeManagerLocal
&& treeManagerLocal->HandOffFling(this, aVelocity);
}
void AsyncPanZoomController::HandleFlingOverscroll(const ScreenPoint& aVelocity) {
APZCTreeManager* treeManagerLocal = mTreeManager;
if (!(treeManagerLocal && treeManagerLocal->HandOffFling(this, aVelocity))) {
// No one wanted the fling, so we enter into an overscroll fling ourselves.
if (IsPannable()) {
AcceptFling(aVelocity, true /* allow overscroll */);
}
}
}
bool AsyncPanZoomController::CallDispatchScroll(const ScreenPoint& aStartPoint, const ScreenPoint& aEndPoint,
@ -1413,36 +1448,45 @@ bool FlingAnimation::Sample(FrameMetrics& aFrameMetrics,
mApzc.mY.AdjustDisplacement(cssOffset.y, overscroll.y)
));
// If the fling has caused us to reach the end of our scroll range, hand
// off the fling to the next APZC in the overscroll handoff chain.
// The fling may have caused us to reach the end of our scroll range.
if (!IsZero(overscroll)) {
// We may have reached the end of the scroll range along one axis but
// not the other. In such a case we only want to hand off the relevant
// component of the fling.
if (FuzzyEqualsMultiplicative(overscroll.x, 0.0f)) {
velocity.x = 0;
} else if (FuzzyEqualsMultiplicative(overscroll.y, 0.0f)) {
velocity.y = 0;
}
if (mAllowOverscroll) {
// If this is a fling that allows overscroll, then go into overscroll.
// To hand off the fling, we call APZCTreeManager::HandOffFling()
// which starts a new fling in the next APZC in the handoff chain with
// the same velocity. For simplicity, the actual overscroll of the current
// sample is discarded rather than being handed off. The compositor should
// sample animations sufficiently frequently that this is not noticeable.
mApzc.OverscrollBy(overscroll);
// Make a local copy of the tree manager pointer and check if it's not
// null before calling HandOffFling(). This is necessary because
// Destroy(), which nulls out mTreeManager, could be called concurrently.
APZCTreeManager* treeManagerLocal = mApzc.mTreeManager;
if (treeManagerLocal) {
// APZC is holding mMonitor, so directly calling HandOffFling()
// (which acquires the tree lock) would violate the lock ordering. Instead
// we schedule HandOffFling() to be called after mMonitor is
// released.
mDeferredTasks.append(NewRunnableMethod(treeManagerLocal,
&APZCTreeManager::HandOffFling,
&mApzc,
// Restore the velocity of the fling, which was zeroed out by
// AdjustDisplacement().
mApzc.mX.SetVelocity(velocity.x);
mApzc.mY.SetVelocity(velocity.y);
} else {
// Otherwise, hand off the fling to the next APZC in the overscroll
// handoff chain.
// We may have reached the end of the scroll range along one axis but
// not the other. In such a case we only want to hand off the relevant
// component of the fling.
if (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
velocity.x = 0;
} else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
velocity.y = 0;
}
// To hand off the fling, we attempt to find a target APZC and start a new
// fling with the same velocity on that APZC. For simplicity, the actual
// overscroll of the current sample is discarded rather than being handed
// off. The compositor should sample animations sufficiently frequently
// that this is not noticeable. The target APZC is chosen by seeing if
// there is an APZC further in the handoff chain which is pannable; if
// there isn't, we take the new fling ourselves, entering an overscrolled
// state.
// Note: APZC is holding mMonitor, so directly calling
// HandleFlingOverscroll() (which acquires the tree lock) would violate
// the lock ordering. Instead we schedule HandleFlingOverscroll() to be
// called after mMonitor is released.
mDeferredTasks.append(NewRunnableMethod(&mApzc,
&AsyncPanZoomController::HandleFlingOverscroll,
velocity));
}
}

View File

@ -759,14 +759,17 @@ private:
/* ===================================================================
* The functions and members in this section are used to manage
* fling animations.
* fling animations and handling overscroll during a fling.
*/
public:
/**
* Take over a fling with the given velocity from another APZC. Used for
* during overscroll handoff for a fling.
* during overscroll handoff for a fling. If we are not pannable, calls
* mTreeManager->HandOffFling() to hand the fling off further.
* Returns true iff. any APZC (whether this one or one further in the handoff
* chain accepted the fling).
*/
void TakeOverFling(ScreenPoint aVelocity);
bool TakeOverFling(ScreenPoint aVelocity);
private:
friend class FlingAnimation;
@ -775,6 +778,16 @@ private:
// The time at which the most recent fling started.
TimeStamp mLastFlingTime;
// Deal with overscroll resulting from a fling animation. This is only ever
// called on APZC instances that were actually performing a fling.
// The overscroll is handled by trying to hand the fling off to an APZC
// later in the handoff chain, or if there are no takers, continuing the
// fling and entering an overscrolled state.
void HandleFlingOverscroll(const ScreenPoint& aVelocity);
// Helper function used by TakeOverFling() and HandleFlingOverscroll().
void AcceptFling(const ScreenPoint& aVelocity, bool aAllowOverscroll);
/* ===================================================================
* The functions and members in this section are used to build a tree