mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-01 05:48:26 +00:00
Bug 1022825 - Implement Asynchronous Smooth Scrolling on Compositor Thread. r=kats
- Extended nsIScrollableFrame and nsGfxScrollFrame to return destination of smooth scrolls which are to be animated on the compositor thread. - Added apz.smooth_scroll_repaint_interval preference. - Implemented AsyncPanZoomController::PanZoomState::SMOOTH_MSD_SCROLL state and AsyncPanZoomController::SmoothScrollAnimation class to animate smooth scroll animations on the compositor thread. - Extended FrameMetrics to report requests for smooth scrolls to be animated on the compositor thread and their corresponding destination positions. - AsyncPanZoomController now checks FrameMetrics for requests to perform smooth scrolling on the compositor thread. It will ensure that they are cancelled as needed by mousewheel, touchpanel, keyboard, and CSSOM-View instant scrolling DOM methods. - The layout/generic/test/test_scroll_behavior.html mochitest has been commented as depending on Bug 1062609 before being enabled for APZ.
This commit is contained in:
parent
b7556664b4
commit
5f59b02c90
@ -51,6 +51,7 @@ pref("layers.componentalpha.enabled", false);
|
||||
pref("apz.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold
|
||||
pref("apz.pan_repaint_interval", 50); // prefer 20 fps
|
||||
pref("apz.fling_repaint_interval", 50); // prefer 20 fps
|
||||
pref("apz.smooth_scroll_repaint_interval", 50); // prefer 20 fps
|
||||
pref("apz.fling_stopped_threshold", "0.2");
|
||||
pref("apz.x_skate_size_multiplier", "2.5");
|
||||
pref("apz.y_skate_size_multiplier", "2.5");
|
||||
|
@ -754,6 +754,8 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
|
||||
WriteParam(aMsg, aParam.mScrollGeneration);
|
||||
WriteParam(aMsg, aParam.mTransformScale);
|
||||
WriteParam(aMsg, aParam.mBackgroundColor);
|
||||
WriteParam(aMsg, aParam.mDoSmoothScroll);
|
||||
WriteParam(aMsg, aParam.mSmoothScrollOffset);
|
||||
WriteParam(aMsg, aParam.GetContentDescription());
|
||||
}
|
||||
|
||||
@ -793,6 +795,8 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
|
||||
ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mTransformScale) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
|
||||
ReadContentDescription(aMsg, aIter, aResult));
|
||||
}
|
||||
};
|
||||
|
@ -90,6 +90,8 @@ public:
|
||||
, mZoom(1)
|
||||
, mUpdateScrollOffset(false)
|
||||
, mScrollGeneration(0)
|
||||
, mDoSmoothScroll(false)
|
||||
, mSmoothScrollOffset(0, 0)
|
||||
, mRootCompositionSize(0, 0)
|
||||
, mDisplayPortMargins(0, 0, 0, 0)
|
||||
, mUseDisplayPortMargins(false)
|
||||
@ -121,9 +123,11 @@ public:
|
||||
mScrollId == aOther.mScrollId &&
|
||||
mScrollParentId == aOther.mScrollParentId &&
|
||||
mScrollOffset == aOther.mScrollOffset &&
|
||||
mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
|
||||
mHasScrollgrab == aOther.mHasScrollgrab &&
|
||||
mUpdateScrollOffset == aOther.mUpdateScrollOffset &&
|
||||
mBackgroundColor == aOther.mBackgroundColor;
|
||||
mBackgroundColor == aOther.mBackgroundColor &&
|
||||
mDoSmoothScroll == aOther.mDoSmoothScroll;
|
||||
}
|
||||
bool operator!=(const FrameMetrics& aOther) const
|
||||
{
|
||||
@ -241,6 +245,13 @@ public:
|
||||
mScrollGeneration = aOther.mScrollGeneration;
|
||||
}
|
||||
|
||||
void CopySmoothScrollInfoFrom(const FrameMetrics& aOther)
|
||||
{
|
||||
mSmoothScrollOffset = aOther.mSmoothScrollOffset;
|
||||
mScrollGeneration = aOther.mScrollGeneration;
|
||||
mDoSmoothScroll = aOther.mDoSmoothScroll;
|
||||
}
|
||||
|
||||
// Make a copy of this FrameMetrics object which does not have any pointers
|
||||
// to heap-allocated memory (i.e. is Plain Old Data, or 'POD'), and is
|
||||
// therefore safe to be placed into shared memory.
|
||||
@ -382,6 +393,16 @@ public:
|
||||
return mScrollOffset;
|
||||
}
|
||||
|
||||
void SetSmoothScrollOffset(const CSSPoint& aSmoothScrollDestination)
|
||||
{
|
||||
mSmoothScrollOffset = aSmoothScrollDestination;
|
||||
}
|
||||
|
||||
const CSSPoint& GetSmoothScrollOffset() const
|
||||
{
|
||||
return mSmoothScrollOffset;
|
||||
}
|
||||
|
||||
void SetZoom(const CSSToScreenScale& aZoom)
|
||||
{
|
||||
mZoom = aZoom;
|
||||
@ -398,11 +419,22 @@ public:
|
||||
mScrollGeneration = aScrollGeneration;
|
||||
}
|
||||
|
||||
void SetSmoothScrollOffsetUpdated(int32_t aScrollGeneration)
|
||||
{
|
||||
mDoSmoothScroll = true;
|
||||
mScrollGeneration = aScrollGeneration;
|
||||
}
|
||||
|
||||
bool GetScrollOffsetUpdated() const
|
||||
{
|
||||
return mUpdateScrollOffset;
|
||||
}
|
||||
|
||||
bool GetDoSmoothScroll() const
|
||||
{
|
||||
return mDoSmoothScroll;
|
||||
}
|
||||
|
||||
uint32_t GetScrollGeneration() const
|
||||
{
|
||||
return mScrollGeneration;
|
||||
@ -543,6 +575,11 @@ private:
|
||||
// The scroll generation counter used to acknowledge the scroll offset update.
|
||||
uint32_t mScrollGeneration;
|
||||
|
||||
// When mDoSmoothScroll, the scroll offset should be animated to
|
||||
// smoothly transition to mScrollOffset rather than be updated instantly.
|
||||
bool mDoSmoothScroll;
|
||||
CSSPoint mSmoothScrollOffset;
|
||||
|
||||
// The size of the root scrollable's composition bounds, but in local CSS pixels.
|
||||
CSSSize mRootCompositionSize;
|
||||
|
||||
|
@ -121,6 +121,9 @@ AppendToString(std::stringstream& aStream, const FrameMetrics& m,
|
||||
AppendToString(aStream, m.mCompositionBounds, "{ cb=");
|
||||
AppendToString(aStream, m.mScrollableRect, " sr=");
|
||||
AppendToString(aStream, m.GetScrollOffset(), " s=");
|
||||
if (m.GetDoSmoothScroll()) {
|
||||
AppendToString(aStream, m.GetSmoothScrollOffset(), " ss=");
|
||||
}
|
||||
AppendToString(aStream, m.mDisplayPort, " dp=");
|
||||
AppendToString(aStream, m.mCriticalDisplayPort, " cdp=");
|
||||
AppendToString(aStream, m.GetBackgroundColor(), " color=");
|
||||
@ -139,8 +142,9 @@ AppendToString(std::stringstream& aStream, const FrameMetrics& m,
|
||||
m.mDevPixelsPerCSSPixel.scale, m.mResolution.scale,
|
||||
m.mCumulativeResolution.scale, m.GetZoom().scale,
|
||||
m.mTransformScale.scale).get();
|
||||
aStream << nsPrintfCString(" u=(%d %lu)",
|
||||
m.GetScrollOffsetUpdated(), m.GetScrollGeneration()).get();
|
||||
aStream << nsPrintfCString(" u=(%d %d %lu)",
|
||||
m.GetScrollOffsetUpdated(), m.GetDoSmoothScroll(),
|
||||
m.GetScrollGeneration()).get();
|
||||
AppendToString(aStream, m.GetScrollParentId(), " p=");
|
||||
aStream << nsPrintfCString(" i=(%ld %lld) }",
|
||||
m.GetPresShellId(), m.GetScrollId()).get();
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include "mozilla/layers/APZCTreeManager.h" // for ScrollableLayerGuid
|
||||
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
|
||||
#include "mozilla/layers/Axis.h" // for AxisX, AxisY, Axis, etc
|
||||
#include "mozilla/layers/AxisPhysicsModel.h" // for AxisPhysicsModel
|
||||
#include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
|
||||
#include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
|
||||
#include "mozilla/layers/PCompositorParent.h" // for PCompositorParent
|
||||
#include "mozilla/layers/TaskThrottler.h" // for TaskThrottler
|
||||
@ -270,6 +272,11 @@ typedef mozilla::gfx::Matrix4x4 Matrix4x4;
|
||||
* Maximum amount of time while panning before sending a viewport change. This
|
||||
* will asynchronously repaint the page. It is also forced when panning stops.
|
||||
*
|
||||
* "apz.smooth_scroll_repaint_interval"
|
||||
* Maximum amount of time doing a smooth scroll before sending a viewport
|
||||
* change. This will asynchronously repaint the page.
|
||||
* Units: milliseconds
|
||||
*
|
||||
* "apz.test.logging_enabled"
|
||||
* Enable logging of APZ test data (see bug 961289).
|
||||
*
|
||||
@ -555,6 +562,8 @@ public:
|
||||
&AsyncPanZoomController::HandleFlingOverscroll,
|
||||
velocity,
|
||||
mOverscrollHandoffChain));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,6 +672,119 @@ private:
|
||||
AsyncPanZoomController& mApzc;
|
||||
};
|
||||
|
||||
class SmoothScrollAnimation : public AsyncPanZoomAnimation {
|
||||
public:
|
||||
SmoothScrollAnimation(AsyncPanZoomController& aApzc,
|
||||
const nsPoint &aInitialPosition,
|
||||
const nsPoint &aInitialVelocity,
|
||||
const nsPoint& aDestination, double aSpringConstant,
|
||||
double aDampingRatio)
|
||||
: AsyncPanZoomAnimation(TimeDuration::FromMilliseconds(
|
||||
gfxPrefs::APZSmoothScrollRepaintInterval()))
|
||||
, mApzc(aApzc)
|
||||
, mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
|
||||
aSpringConstant, aDampingRatio)
|
||||
, mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
|
||||
aSpringConstant, aDampingRatio)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances a smooth scroll simulation based on the time passed in |aDelta|.
|
||||
* This should be called whenever sampling the content transform for this
|
||||
* frame. Returns true if the smooth scroll should be advanced by one frame,
|
||||
* or false if the smooth scroll has ended.
|
||||
*/
|
||||
bool Sample(FrameMetrics& aFrameMetrics, const TimeDuration& aDelta) {
|
||||
|
||||
if (aDelta.ToMilliseconds() <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mXAxisModel.IsFinished() && mYAxisModel.IsFinished()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mXAxisModel.Simulate(aDelta);
|
||||
mYAxisModel.Simulate(aDelta);
|
||||
|
||||
CSSPoint position = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetPosition(),
|
||||
mYAxisModel.GetPosition()));
|
||||
CSSPoint css_velocity = CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetVelocity(),
|
||||
mYAxisModel.GetVelocity()));
|
||||
|
||||
// Convert from points/second to points/ms
|
||||
ScreenPoint velocity = ScreenPoint(css_velocity.x, css_velocity.y) / 1000.0f;
|
||||
|
||||
// Keep the velocity updated for the Axis class so that any animations
|
||||
// chained off of the smooth scroll will inherit it.
|
||||
if (mXAxisModel.IsFinished()) {
|
||||
mApzc.mX.SetVelocity(0);
|
||||
} else {
|
||||
mApzc.mX.SetVelocity(velocity.x);
|
||||
}
|
||||
if (mYAxisModel.IsFinished()) {
|
||||
mApzc.mY.SetVelocity(0);
|
||||
} else {
|
||||
mApzc.mY.SetVelocity(velocity.y);
|
||||
}
|
||||
|
||||
// If we overscroll, hand off to a fling animation that will complete the
|
||||
// spring back.
|
||||
float displacement_x = position.x - mApzc.mX.GetOrigin();
|
||||
float displacement_y = position.y - mApzc.mY.GetOrigin();
|
||||
|
||||
CSSPoint overscroll;
|
||||
CSSPoint adjustedOffset;
|
||||
mApzc.mX.AdjustDisplacement(displacement_x, adjustedOffset.x, overscroll.x);
|
||||
mApzc.mY.AdjustDisplacement(displacement_y, adjustedOffset.y, overscroll.y);
|
||||
|
||||
aFrameMetrics.ScrollBy(adjustedOffset);
|
||||
|
||||
// The smooth scroll may have caused us to reach the end of our scroll range.
|
||||
// This can happen if either the layout.css.scroll-behavior.damping-ratio
|
||||
// preference is set to less than 1 (underdamped) or if a smooth scroll
|
||||
// inherits velocity from a fling gesture.
|
||||
if (!IsZero(overscroll)) {
|
||||
|
||||
// Hand off a fling with the remaining momentum 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
|
||||
// HandleSmoothScrollOverscroll() (which acquires the tree lock) would violate
|
||||
// the lock ordering. Instead we schedule HandleSmoothScrollOverscroll() to be
|
||||
// called after mMonitor is released.
|
||||
mDeferredTasks.append(NewRunnableMethod(&mApzc,
|
||||
&AsyncPanZoomController::HandleSmoothScrollOverscroll,
|
||||
velocity));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
AsyncPanZoomController& mApzc;
|
||||
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
|
||||
};
|
||||
|
||||
void
|
||||
AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) {
|
||||
sFrameTime = aTime;
|
||||
@ -1019,6 +1141,7 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
case ANIMATING_ZOOM:
|
||||
case SMOOTH_SCROLL:
|
||||
CurrentTouchBlock()->GetOverscrollHandoffChain()->CancelAnimations();
|
||||
// Fall through.
|
||||
case NOTHING: {
|
||||
@ -1053,6 +1176,7 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent)
|
||||
APZC_LOG("%p got a touch-move in state %d\n", this, mState);
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
case SMOOTH_SCROLL:
|
||||
case NOTHING:
|
||||
case ANIMATING_ZOOM:
|
||||
// May happen if the user double-taps and drags without lifting after the
|
||||
@ -1127,6 +1251,7 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
||||
NS_WARNING("Received impossible touch end in OnTouchEnd.");
|
||||
// Fall through.
|
||||
case ANIMATING_ZOOM:
|
||||
case SMOOTH_SCROLL:
|
||||
case NOTHING:
|
||||
// May happen if the user double-taps and drags without lifting after the
|
||||
// second tap. Ignore if this happens.
|
||||
@ -1375,6 +1500,11 @@ nsEventStatus AsyncPanZoomController::OnPanCancelled(const PanGestureInput& aEve
|
||||
nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a pan-begin in state %d\n", this, mState);
|
||||
|
||||
if (mState == SMOOTH_SCROLL) {
|
||||
// SMOOTH_SCROLL scrolls are cancelled by pan gestures.
|
||||
CancelAnimation();
|
||||
}
|
||||
|
||||
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
|
||||
|
||||
mX.StartTouch(aEvent.mPanStartPoint.x, aEvent.mTime);
|
||||
@ -1397,6 +1527,20 @@ nsEventStatus AsyncPanZoomController::OnPanBegin(const PanGestureInput& aEvent)
|
||||
nsEventStatus AsyncPanZoomController::OnPan(const PanGestureInput& aEvent, bool aFingersOnTouchpad) {
|
||||
APZC_LOG("%p got a pan-pan in state %d\n", this, mState);
|
||||
|
||||
if (mState == SMOOTH_SCROLL) {
|
||||
if (aEvent.mType == PanGestureInput::PANGESTURE_MOMENTUMPAN) {
|
||||
// When a SMOOTH_SCROLL scroll is being processed on a frame, mouse
|
||||
// wheel and trackpad momentum scroll position updates will not cancel the
|
||||
// SMOOTH_SCROLL scroll animations, enabling scripts that depend on
|
||||
// them to be responsive without forcing the user to wait for the momentum
|
||||
// scrolling to completely stop.
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
} else {
|
||||
// SMOOTH_SCROLL scrolls are cancelled by pan gestures.
|
||||
CancelAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
// We need to update the axis velocity in order to get a useful display port
|
||||
// size and position. We need to do so even if this is a momentum pan (i.e.
|
||||
// aFingersOnTouchpad == false); in that case the "with touch" part is not
|
||||
@ -1430,6 +1574,11 @@ nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
|
||||
nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
|
||||
|
||||
if (mState == SMOOTH_SCROLL) {
|
||||
// SMOOTH_SCROLL scrolls are cancelled by pan gestures.
|
||||
CancelAnimation();
|
||||
}
|
||||
|
||||
mPanGestureState = MakeUnique<InputBlockState>(BuildOverscrollHandoffChain());
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
@ -1846,6 +1995,28 @@ void AsyncPanZoomController::HandleFlingOverscroll(const ScreenPoint& aVelocity,
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ScreenPoint& aVelocity) {
|
||||
// We must call BuildOverscrollHandoffChain from this deferred callback
|
||||
// function in order to avoid a deadlock when acquiring the tree lock.
|
||||
HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain());
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::StartSmoothScroll() {
|
||||
SetState(SMOOTH_SCROLL);
|
||||
nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset());
|
||||
// Cast velocity from ScreenPoints/ms to CSSPoints/ms then convert to
|
||||
// appunits/second
|
||||
nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(),
|
||||
mY.GetVelocity())) * 1000.0f;
|
||||
nsPoint destination = CSSPoint::ToAppUnits(mFrameMetrics.GetSmoothScrollOffset());
|
||||
|
||||
StartAnimation(new SmoothScrollAnimation(*this,
|
||||
initialPosition, initialVelocity,
|
||||
destination,
|
||||
gfxPrefs::ScrollBehaviorSpringConstant(),
|
||||
gfxPrefs::ScrollBehaviorDampingRatio()));
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::StartSnapBack() {
|
||||
SetState(SNAP_BACK);
|
||||
StartAnimation(new OverscrollSnapBackAnimation(*this));
|
||||
@ -2434,6 +2605,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
needContentRepaint = true;
|
||||
}
|
||||
} else {
|
||||
bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll()
|
||||
&& (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration());
|
||||
|
||||
// If we're not taking the aLayerMetrics wholesale we still need to pull
|
||||
// in some things into our local mFrameMetrics because these things are
|
||||
// determined by Gecko and our copy in mFrameMetrics may be stale.
|
||||
@ -2478,6 +2652,23 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
// last thing we know was painted by Gecko.
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
}
|
||||
|
||||
if (smoothScrollRequested) {
|
||||
// A smooth scroll has been requested for animation on the compositor
|
||||
// thread. This flag will be reset by the main thread when it receives
|
||||
// the scroll update acknowledgement.
|
||||
|
||||
APZC_LOG("%p smooth scrolling from %s to %s\n", this,
|
||||
Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
|
||||
Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str());
|
||||
|
||||
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
|
||||
CancelAnimation();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
StartSmoothScroll();
|
||||
|
||||
scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
|
||||
}
|
||||
}
|
||||
|
||||
if (scrollOffsetUpdated) {
|
||||
|
@ -344,6 +344,8 @@ protected:
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
ANIMATING_ZOOM, /* animated zoom to a new rect */
|
||||
SNAP_BACK, /* snap-back animation to relieve overscroll */
|
||||
SMOOTH_SCROLL, /* Smooth scrolling to destination. Used by
|
||||
CSSOM-View smooth scroll-behavior */
|
||||
};
|
||||
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
@ -799,7 +801,8 @@ private:
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to manage
|
||||
* fling animations and handling overscroll during a fling.
|
||||
* fling animations, smooth scroll animations, and overscroll
|
||||
* during a fling or smooth scroll.
|
||||
*/
|
||||
public:
|
||||
/**
|
||||
@ -818,6 +821,7 @@ public:
|
||||
private:
|
||||
friend class FlingAnimation;
|
||||
friend class OverscrollSnapBackAnimation;
|
||||
friend class SmoothScrollAnimation;
|
||||
// The initial velocity of the most recent fling.
|
||||
ScreenPoint mLastFlingVelocity;
|
||||
// The time at which the most recent fling started.
|
||||
@ -831,6 +835,8 @@ private:
|
||||
void HandleFlingOverscroll(const ScreenPoint& aVelocity,
|
||||
const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain);
|
||||
|
||||
void HandleSmoothScrollOverscroll(const ScreenPoint& aVelocity);
|
||||
|
||||
// Helper function used by TakeOverFling() and HandleFlingOverscroll().
|
||||
void AcceptFling(const ScreenPoint& aVelocity,
|
||||
const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
|
||||
@ -840,6 +846,7 @@ private:
|
||||
// Start a snap-back animation to relieve overscroll.
|
||||
void StartSnapBack();
|
||||
|
||||
void StartSmoothScroll();
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to build a tree
|
||||
|
@ -86,8 +86,10 @@ ScrollFrameTo(nsIScrollableFrame* aFrame, const CSSPoint& aPoint, bool& aSuccess
|
||||
// Also if the scrollable frame got a scroll request from something other than us
|
||||
// since the last layers update, then we don't want to push our scroll request
|
||||
// because we'll clobber that one, which is bad.
|
||||
if (!aFrame->IsProcessingAsyncScroll() &&
|
||||
(!aFrame->OriginOfLastScroll() || aFrame->OriginOfLastScroll() == nsGkAtoms::apz)) {
|
||||
bool scrollInProgress = aFrame->IsProcessingAsyncScroll()
|
||||
|| (aFrame->LastScrollOrigin() && aFrame->LastScrollOrigin() != nsGkAtoms::apz)
|
||||
|| aFrame->LastSmoothScrollOrigin();
|
||||
if (!scrollInProgress) {
|
||||
aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
|
||||
geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
|
||||
aSuccessOut = true;
|
||||
@ -287,7 +289,7 @@ public:
|
||||
|
||||
nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(mScrollId);
|
||||
if (sf) {
|
||||
sf->ResetOriginIfScrollAtGeneration(mScrollGeneration);
|
||||
sf->ResetScrollInfoIfGeneration(mScrollGeneration);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -158,6 +158,7 @@ private:
|
||||
DECL_GFX_PREF(Live, "apz.overscroll.snap_back.mass", APZOverscrollSnapBackMass, float, 1000.0f);
|
||||
DECL_GFX_PREF(Live, "apz.pan_repaint_interval", APZPanRepaintInterval, int32_t, 250);
|
||||
DECL_GFX_PREF(Live, "apz.printtree", APZPrintTree, bool, false);
|
||||
DECL_GFX_PREF(Live, "apz.smooth_scroll_repaint_interval", APZSmoothScrollRepaintInterval, int32_t, 75);
|
||||
DECL_GFX_PREF(Live, "apz.subframe.enabled", APZSubframeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "apz.test.logging_enabled", APZTestLoggingEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "apz.touch_start_tolerance", APZTouchStartTolerance, float, 1.0f/4.5f);
|
||||
|
@ -699,13 +699,20 @@ nsDisplayScrollLayer::ComputeFrameMetrics(nsIFrame* aForFrame,
|
||||
nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
|
||||
metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));
|
||||
|
||||
nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
|
||||
metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
|
||||
|
||||
// If the frame was scrolled since the last layers update, and by
|
||||
// something other than the APZ code, we want to tell the APZ to update
|
||||
// its scroll offset.
|
||||
nsIAtom* originOfLastScroll = scrollableFrame->OriginOfLastScroll();
|
||||
if (originOfLastScroll && originOfLastScroll != nsGkAtoms::apz) {
|
||||
nsIAtom* lastScrollOrigin = scrollableFrame->LastScrollOrigin();
|
||||
if (lastScrollOrigin && lastScrollOrigin != nsGkAtoms::apz) {
|
||||
metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
|
||||
}
|
||||
nsIAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin();
|
||||
if (lastSmoothScrollOrigin) {
|
||||
metrics.SetSmoothScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
|
||||
}
|
||||
}
|
||||
|
||||
metrics.SetScrollId(scrollId);
|
||||
|
@ -1824,7 +1824,8 @@ ScrollFrameHelper::ScrollFrameHelper(nsContainerFrame* aOuter,
|
||||
, mOuter(aOuter)
|
||||
, mAsyncScroll(nullptr)
|
||||
, mAsyncSmoothMSDScroll(nullptr)
|
||||
, mOriginOfLastScroll(nsGkAtoms::other)
|
||||
, mLastScrollOrigin(nsGkAtoms::other)
|
||||
, mLastSmoothScrollOrigin(nullptr)
|
||||
, mScrollGeneration(++sScrollGenerationCounter)
|
||||
, mDestination(0, 0)
|
||||
, mScrollPosAtLastPaint(0, 0)
|
||||
@ -2053,6 +2054,19 @@ ScrollFrameHelper::ScrollToWithOrigin(nsPoint aScrollPosition,
|
||||
mAsyncScroll = nullptr;
|
||||
}
|
||||
|
||||
if (gfxPrefs::AsyncPanZoomEnabled()) {
|
||||
// The animation will be handled in the compositor, pass the
|
||||
// information needed to start the animation and skip the main-thread
|
||||
// animation for this scroll.
|
||||
mLastSmoothScrollOrigin = aOrigin;
|
||||
mScrollGeneration = ++sScrollGenerationCounter;
|
||||
|
||||
// Schedule a paint to ensure that the frame metrics get updated on
|
||||
// the compositor thread.
|
||||
mOuter->SchedulePaint();
|
||||
return;
|
||||
}
|
||||
|
||||
mAsyncSmoothMSDScroll =
|
||||
new AsyncSmoothMSDScroll(GetScrollPosition(), mDestination,
|
||||
currentVelocity, GetScrollRangeForClamping(),
|
||||
@ -2404,7 +2418,8 @@ ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, nsIAtom* aOri
|
||||
nsPoint oldScrollFramePos = mScrolledFrame->GetPosition();
|
||||
// Update frame position for scrolling
|
||||
mScrolledFrame->SetPosition(mScrollPort.TopLeft() - pt);
|
||||
mOriginOfLastScroll = aOrigin;
|
||||
mLastScrollOrigin = aOrigin;
|
||||
mLastSmoothScrollOrigin = nullptr;
|
||||
mScrollGeneration = ++sScrollGenerationCounter;
|
||||
|
||||
// We pass in the amount to move visually
|
||||
|
@ -322,11 +322,14 @@ public:
|
||||
|
||||
void HandleScrollbarStyleSwitching();
|
||||
|
||||
nsIAtom* OriginOfLastScroll() const { return mOriginOfLastScroll; }
|
||||
nsIAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
|
||||
nsIAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
|
||||
uint32_t CurrentScrollGeneration() const { return mScrollGeneration; }
|
||||
void ResetOriginIfScrollAtGeneration(uint32_t aGeneration) {
|
||||
nsPoint LastScrollDestination() const { return mDestination; }
|
||||
void ResetScrollInfoIfGeneration(uint32_t aGeneration) {
|
||||
if (aGeneration == mScrollGeneration) {
|
||||
mOriginOfLastScroll = nullptr;
|
||||
mLastScrollOrigin = nullptr;
|
||||
mLastSmoothScrollOrigin = nullptr;
|
||||
}
|
||||
}
|
||||
bool WantAsyncScroll() const;
|
||||
@ -367,7 +370,8 @@ public:
|
||||
nsRefPtr<AsyncSmoothMSDScroll> mAsyncSmoothMSDScroll;
|
||||
nsRefPtr<ScrollbarActivity> mScrollbarActivity;
|
||||
nsTArray<nsIScrollPositionListener*> mListeners;
|
||||
nsIAtom* mOriginOfLastScroll;
|
||||
nsIAtom* mLastScrollOrigin;
|
||||
nsIAtom* mLastSmoothScrollOrigin;
|
||||
uint32_t mScrollGeneration;
|
||||
nsRect mScrollPort;
|
||||
// Where we're currently scrolling to, if we're scrolling asynchronously.
|
||||
@ -707,14 +711,20 @@ public:
|
||||
virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const MOZ_OVERRIDE {
|
||||
return mHelper.ExpandRectToNearlyVisible(aRect);
|
||||
}
|
||||
virtual nsIAtom* OriginOfLastScroll() MOZ_OVERRIDE {
|
||||
return mHelper.OriginOfLastScroll();
|
||||
virtual nsIAtom* LastScrollOrigin() MOZ_OVERRIDE {
|
||||
return mHelper.LastScrollOrigin();
|
||||
}
|
||||
virtual nsIAtom* LastSmoothScrollOrigin() MOZ_OVERRIDE {
|
||||
return mHelper.LastSmoothScrollOrigin();
|
||||
}
|
||||
virtual uint32_t CurrentScrollGeneration() MOZ_OVERRIDE {
|
||||
return mHelper.CurrentScrollGeneration();
|
||||
}
|
||||
virtual void ResetOriginIfScrollAtGeneration(uint32_t aGeneration) MOZ_OVERRIDE {
|
||||
mHelper.ResetOriginIfScrollAtGeneration(aGeneration);
|
||||
virtual nsPoint LastScrollDestination() MOZ_OVERRIDE {
|
||||
return mHelper.LastScrollDestination();
|
||||
}
|
||||
virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) MOZ_OVERRIDE {
|
||||
mHelper.ResetScrollInfoIfGeneration(aGeneration);
|
||||
}
|
||||
virtual bool WantAsyncScroll() const MOZ_OVERRIDE {
|
||||
return mHelper.WantAsyncScroll();
|
||||
@ -1053,14 +1063,20 @@ public:
|
||||
virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const MOZ_OVERRIDE {
|
||||
return mHelper.ExpandRectToNearlyVisible(aRect);
|
||||
}
|
||||
virtual nsIAtom* OriginOfLastScroll() MOZ_OVERRIDE {
|
||||
return mHelper.OriginOfLastScroll();
|
||||
virtual nsIAtom* LastScrollOrigin() MOZ_OVERRIDE {
|
||||
return mHelper.LastScrollOrigin();
|
||||
}
|
||||
virtual nsIAtom* LastSmoothScrollOrigin() MOZ_OVERRIDE {
|
||||
return mHelper.LastSmoothScrollOrigin();
|
||||
}
|
||||
virtual uint32_t CurrentScrollGeneration() MOZ_OVERRIDE {
|
||||
return mHelper.CurrentScrollGeneration();
|
||||
}
|
||||
virtual void ResetOriginIfScrollAtGeneration(uint32_t aGeneration) MOZ_OVERRIDE {
|
||||
mHelper.ResetOriginIfScrollAtGeneration(aGeneration);
|
||||
virtual nsPoint LastScrollDestination() MOZ_OVERRIDE {
|
||||
return mHelper.LastScrollDestination();
|
||||
}
|
||||
virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) MOZ_OVERRIDE {
|
||||
mHelper.ResetScrollInfoIfGeneration(aGeneration);
|
||||
}
|
||||
virtual bool WantAsyncScroll() const MOZ_OVERRIDE {
|
||||
return mHelper.WantAsyncScroll();
|
||||
|
@ -330,21 +330,42 @@ public:
|
||||
*/
|
||||
virtual nsRect ExpandRectToNearlyVisible(const nsRect& aRect) const = 0;
|
||||
/**
|
||||
* Returns the origin passed in to the last ScrollToImpl call that took
|
||||
* effect.
|
||||
* Returns the origin that triggered the last instant scroll. Will equal
|
||||
* nsGkAtoms::apz when the compositor's replica frame metrics includes the
|
||||
* latest instant scroll.
|
||||
*/
|
||||
virtual nsIAtom* OriginOfLastScroll() = 0;
|
||||
virtual nsIAtom* LastScrollOrigin() = 0;
|
||||
/**
|
||||
* Returns the origin that triggered the last smooth scroll.
|
||||
* Will equal nsGkAtoms::apz when the compositor's replica frame
|
||||
* metrics includes the latest smooth scroll. The compositor will always
|
||||
* perform an instant scroll prior to instantiating any smooth scrolls
|
||||
* if LastScrollOrigin and LastSmoothScrollOrigin indicate that
|
||||
* an instant scroll and a smooth scroll have occurred since the last
|
||||
* replication of the frame metrics.
|
||||
*
|
||||
* This is set to nullptr to when the compositor thread acknowledges that
|
||||
* the smooth scroll has been started. If the smooth scroll has been stomped
|
||||
* by an instant scroll before the smooth scroll could be started by the
|
||||
* compositor, this is set to nullptr to clear the smooth scroll.
|
||||
*/
|
||||
virtual nsIAtom* LastSmoothScrollOrigin() = 0;
|
||||
/**
|
||||
* Returns the current generation counter for the scroll. This counter
|
||||
* increments every time the scroll position is set.
|
||||
*/
|
||||
virtual uint32_t CurrentScrollGeneration() = 0;
|
||||
/**
|
||||
* LastScrollDestination returns the destination of the most recently
|
||||
* requested smooth scroll animation.
|
||||
*/
|
||||
virtual nsPoint LastScrollDestination() = 0;
|
||||
/**
|
||||
* Clears the "origin of last scroll" property stored in this frame, if
|
||||
* the generation counter passed in matches the current scroll generation
|
||||
* counter.
|
||||
*/
|
||||
virtual void ResetOriginIfScrollAtGeneration(uint32_t aGeneration) = 0;
|
||||
virtual void ResetScrollInfoIfGeneration(uint32_t aGeneration) = 0;
|
||||
/**
|
||||
* Determine whether it is desirable to be able to asynchronously scroll this
|
||||
* scroll frame.
|
||||
|
@ -123,7 +123,7 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
|
||||
[test_plugin_position.xhtml]
|
||||
skip-if = e10s
|
||||
[test_scroll_behavior.html]
|
||||
skip-if = e10s || buildapp == 'b2g' #Bug 1022825 will enable this for APZ and e10s
|
||||
skip-if = e10s || buildapp == 'b2g' # Bug 1062609
|
||||
[test_selection_expanding.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' # b2g(mouse selection not working) b2g-debug(mouse selection not working) b2g-desktop(mouse selection not working)
|
||||
support-files = selection_expanding_xbl.xml
|
||||
|
@ -480,12 +480,14 @@ pref("apz.zoom_animation_duration_ms", 250);
|
||||
// Layerize scrollable subframes to allow async panning
|
||||
pref("apz.subframe.enabled", true);
|
||||
pref("apz.fling_repaint_interval", 16);
|
||||
pref("apz.smooth_scroll_repaint_interval", 16);
|
||||
pref("apz.pan_repaint_interval", 16);
|
||||
pref("apz.x_skate_size_multiplier", "2.5");
|
||||
pref("apz.y_skate_size_multiplier", "3.5");
|
||||
#else
|
||||
pref("apz.subframe.enabled", false);
|
||||
pref("apz.fling_repaint_interval", 75);
|
||||
pref("apz.smooth_scroll_repaint_interval", 75);
|
||||
pref("apz.pan_repaint_interval", 250);
|
||||
pref("apz.x_skate_size_multiplier", "1.5");
|
||||
pref("apz.y_skate_size_multiplier", "2.5");
|
||||
|
Loading…
x
Reference in New Issue
Block a user