Bug 1423011 - Part 1: Allow APZ to async-scroll the layout viewport. r=botond

There are 3 main components to this change:

  a) Store the origin of the layout viewport in APZ (until now we only stored
     it's size). This required updating the offset stored in mViewport, which
     was previously (0, 0).
  b) Adjust the layout viewport in APZ each time the visual viewport exceeds
     the bounds of the layout viewport.
  c) Update the main thread to store the layout viewport offset for the
     RCD-RSF (currently it uses the layout viewport offset for overflow:hidden
     pages, and the visual viewport offset otherwise).

MozReview-Commit-ID: 7AD8wvthh2m

--HG--
extra : rebase_source : df8704146740f4b2522c80b20b603617993b6c83
This commit is contained in:
Kashav Madan ext:(%2C%20Tanushree%20Podder%20%3Ctpodder%40mozilla.com%3E) 2018-06-07 17:01:36 -04:00
Родитель 4188ff378b
Коммит 557bb0376f
9 изменённых файлов: 157 добавлений и 46 удалений

Просмотреть файл

@ -13,6 +13,39 @@ namespace layers {
const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0;
void
FrameMetrics::RecalculateViewportOffset()
{
if (!mIsRootContent) {
return;
}
CSSRect visualViewport = GetVisualViewport();
// If the visual viewport is contained within the layout viewport, we don't
// need to make any adjustments, so we can exit early.
//
// Additionally, if the composition bounds changes (due to an orientation
// change, window resize, etc.), it may take a few frames for mViewport to
// update and during that time, the visual viewport may be larger than the
// layout viewport, so don't attempt to make any adjustments.
if (mViewport.Contains(visualViewport) ||
(mViewport.Width() < visualViewport.Width() &&
!FuzzyEqualsMultiplicative(mViewport.Width(), visualViewport.Width())) ||
(mViewport.Height() < visualViewport.Height() &&
!FuzzyEqualsMultiplicative(mViewport.Height(), visualViewport.Height()))) {
return;
}
if (visualViewport.X() < mViewport.X()) {
mViewport.MoveToX(visualViewport.X());
} else if (mViewport.XMost() < visualViewport.XMost()) {
mViewport.MoveByX(visualViewport.XMost() - mViewport.XMost());
}
if (visualViewport.Y() < mViewport.Y()) {
mViewport.MoveToY(visualViewport.Y());
} else if (mViewport.YMost() < visualViewport.YMost()) {
mViewport.MoveByY(visualViewport.YMost() - mViewport.YMost());
}
}
void
ScrollMetadata::SetUsesContainerScrolling(bool aValue) {
MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());

Просмотреть файл

@ -481,6 +481,11 @@ public:
return mViewport;
}
CSSRect GetVisualViewport() const
{
return CSSRect(mScrollOffset, CalculateCompositedSizeInCssPixels());
}
void SetExtraResolution(const ScreenToLayerScale2D& aExtraResolution)
{
mExtraResolution = aExtraResolution;
@ -526,6 +531,13 @@ public:
return mIsScrollInfoLayer;
}
// Determine if the visual viewport is outside of the layout viewport and
// adjust the x,y-offset in mViewport accordingly. This is necessary to
// allow APZ to async-scroll the layout viewport.
//
// This is a no-op if mIsRootContent is false.
void RecalculateViewportOffset();
private:
// A unique ID assigned to each scrollable frame.
ViewID mScrollId;

Просмотреть файл

@ -242,7 +242,7 @@ StackScrollerFlingAnimation::DoSample(FrameMetrics& aFrameMetrics,
mPreviousOffset = offset;
mApzc.SetVelocityVector(velocity);
aFrameMetrics.SetScrollOffset(offset / aFrameMetrics.GetZoom());
mApzc.SetScrollOffset(offset / aFrameMetrics.GetZoom());
// If we hit a bounds while flinging, send the velocity so that the bounce
// animation can play.

Просмотреть файл

@ -581,11 +581,13 @@ private:
class ZoomAnimation: public AsyncPanZoomAnimation {
public:
ZoomAnimation(const CSSPoint& aStartOffset,
ZoomAnimation(AsyncPanZoomController& aApzc,
const CSSPoint& aStartOffset,
const CSSToParentLayerScale2D& aStartZoom,
const CSSPoint& aEndOffset,
const CSSToParentLayerScale2D& aEndZoom)
: mTotalDuration(
: mApzc(aApzc)
, mTotalDuration(
TimeDuration::FromMilliseconds(gfxPrefs::APZZoomAnimationDuration()))
, mStartOffset(aStartOffset)
, mStartZoom(aStartZoom)
@ -602,7 +604,7 @@ public:
if (animPosition >= 1.0) {
aFrameMetrics.SetZoom(mEndZoom);
aFrameMetrics.SetScrollOffset(mEndOffset);
mApzc.SetScrollOffset(mEndOffset);
return false;
}
@ -618,7 +620,7 @@ public:
1 / (sampledPosition / mEndZoom.xScale + (1 - sampledPosition) / mStartZoom.xScale),
1 / (sampledPosition / mEndZoom.yScale + (1 - sampledPosition) / mStartZoom.yScale)));
aFrameMetrics.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
mApzc.SetScrollOffset(CSSPoint::FromUnknownPoint(gfx::Point(
mEndOffset.x * sampledPosition + mStartOffset.x * (1 - sampledPosition),
mEndOffset.y * sampledPosition + mStartOffset.y * (1 - sampledPosition)
)));
@ -632,6 +634,8 @@ public:
}
private:
AsyncPanZoomController& mApzc;
TimeDuration mDuration;
const TimeDuration mTotalDuration;
@ -680,7 +684,7 @@ public:
// offset to end up being a bit off from the destination, we can get
// artefacts like "scroll to the next snap point in this direction"
// scrolling to the snap point we're already supposed to be at.
aFrameMetrics.ClampAndSetScrollOffset(
mApzc.ClampAndSetScrollOffset(
CSSPoint::FromAppUnits(nsPoint(mXAxisModel.GetDestination(),
mYAxisModel.GetDestination())));
return false;
@ -719,7 +723,7 @@ public:
mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y);
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
mApzc.ScrollBy(adjustedOffset / zoom);
// 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
@ -1075,7 +1079,7 @@ nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent,
scrollOffset.y = scrollPosition;
}
APZC_LOG("%p set scroll offset to %s from scrollbar drag\n", this, Stringify(scrollOffset).c_str());
mFrameMetrics.SetScrollOffset(scrollOffset);
SetScrollOffset(scrollOffset);
ScheduleCompositeAndMaybeRepaint();
UpdateSharedCompositorFrameMetrics();
@ -3310,7 +3314,7 @@ void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShi
this, Stringify(adjustment).c_str());
CSSRect scrollRange = mFrameMetrics.CalculateScrollRange();
// Apply shift to mFrameMetrics.mScrollOffset.
mFrameMetrics.SetScrollOffset(scrollRange.ClampPoint(
SetScrollOffset(scrollRange.ClampPoint(
mFrameMetrics.GetScrollOffset() + adjustment));
// Apply shift to mCompositedScrollOffset, since the dynamic toolbar expects
// the shift to take effect right away, without the usual frame delay.
@ -3320,12 +3324,27 @@ void AsyncPanZoomController::AdjustScrollForSurfaceShift(const ScreenPoint& aShi
UpdateSharedCompositorFrameMetrics();
}
void AsyncPanZoomController::SetScrollOffset(const CSSPoint& aOffset) {
mFrameMetrics.SetScrollOffset(aOffset);
mFrameMetrics.RecalculateViewportOffset();
}
void AsyncPanZoomController::ClampAndSetScrollOffset(const CSSPoint& aOffset) {
mFrameMetrics.ClampAndSetScrollOffset(aOffset);
mFrameMetrics.RecalculateViewportOffset();
}
void AsyncPanZoomController::ScrollBy(const CSSPoint& aOffset) {
mFrameMetrics.ScrollBy(aOffset);
SetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset);
}
void AsyncPanZoomController::ScrollByAndClamp(const CSSPoint& aOffset) {
mFrameMetrics.ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset);
ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset() + aOffset);
}
void AsyncPanZoomController::CopyScrollInfoFrom(const FrameMetrics& aFrameMetrics) {
mFrameMetrics.CopyScrollInfoFrom(aFrameMetrics);
mFrameMetrics.RecalculateViewportOffset();
}
void AsyncPanZoomController::ScaleWithFocus(float aScale,
@ -3335,7 +3354,7 @@ void AsyncPanZoomController::ScaleWithFocus(float aScale,
// at the same position on the screen before and after the change in zoom. The below code
// accomplishes this; see https://bugzilla.mozilla.org/show_bug.cgi?id=923431#c6 for an
// in-depth explanation of how.
mFrameMetrics.SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
SetScrollOffset((mFrameMetrics.GetScrollOffset() + aFocus) - (aFocus / aScale));
}
/**
@ -3458,9 +3477,8 @@ const ScreenMargin AsyncPanZoomController::CalculatePendingDisplayPort(
displayPort.MoveBy(velocity * paintFactor * gfxPrefs::APZVelocityBias());
APZC_LOG_FM(aFrameMetrics,
"Calculated displayport as (%f %f %f %f) from velocity %s paint time %f metrics",
displayPort.x, displayPort.y, displayPort.Width(), displayPort.Height(),
ToString(aVelocity).c_str(), paintFactor);
"Calculated displayport as %s from velocity %s paint time %f metrics",
Stringify(displayPort).c_str(), ToString(aVelocity).c_str(), paintFactor);
CSSMargin cssMargins;
cssMargins.left = -displayPort.X();
@ -3610,6 +3628,10 @@ AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
mLastPaintRequestMetrics.GetViewport().Width()) < EPSILON &&
fabsf(aFrameMetrics.GetViewport().Height() -
mLastPaintRequestMetrics.GetViewport().Height()) < EPSILON &&
fabsf(aFrameMetrics.GetViewport().X() -
mLastPaintRequestMetrics.GetViewport().X()) < EPSILON &&
fabsf(aFrameMetrics.GetViewport().Y() -
mLastPaintRequestMetrics.GetViewport().Y()) < EPSILON &&
aFrameMetrics.GetScrollGeneration() ==
mLastPaintRequestMetrics.GetScrollGeneration() &&
aFrameMetrics.GetScrollUpdateType() ==
@ -4068,20 +4090,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
}
}
bool needContentRepaint = false;
bool viewportUpdated = false;
if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), mFrameMetrics.GetCompositionBounds().Width()) &&
FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), mFrameMetrics.GetCompositionBounds().Height())) {
// Remote content has sync'd up to the composition geometry
// change, so we can accept the viewport it's calculated.
if (mFrameMetrics.GetViewport().Width() != aLayerMetrics.GetViewport().Width() ||
mFrameMetrics.GetViewport().Height() != aLayerMetrics.GetViewport().Height()) {
needContentRepaint = true;
viewportUpdated = true;
}
mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
}
// If the layers update was not triggered by our own repaint request, then
// we want to take the new scroll offset. Check the scroll generation as well
// to filter duplicate calls to NotifyLayersUpdated with the same scroll offset
@ -4101,6 +4109,22 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
// TODO if we're in a drag and scrollOffsetUpdated is set then we want to
// ignore it
bool needContentRepaint = false;
bool viewportUpdated = false;
if (FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Width(), mFrameMetrics.GetCompositionBounds().Width()) &&
FuzzyEqualsAdditive(aLayerMetrics.GetCompositionBounds().Height(), mFrameMetrics.GetCompositionBounds().Height())) {
// Remote content has sync'd up to the composition geometry
// change, so we can accept the viewport it's calculated.
if (mFrameMetrics.GetViewport().Width() != aLayerMetrics.GetViewport().Width() ||
mFrameMetrics.GetViewport().Height() != aLayerMetrics.GetViewport().Height()) {
needContentRepaint = true;
viewportUpdated = true;
}
if (viewportUpdated || scrollOffsetUpdated) {
mFrameMetrics.SetViewport(aLayerMetrics.GetViewport());
}
}
#if defined(MOZ_WIDGET_ANDROID)
if (aLayerMetrics.IsRootContent()) {
if (APZCTreeManager* manager = GetApzcTreeManager()) {
@ -4204,7 +4228,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
// becomes incorrect for the purposes of calculating the LD transform. To
// correct this we need to update mExpectedGeckoMetrics to be the
// last thing we know was painted by Gecko.
mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
CopyScrollInfoFrom(aLayerMetrics);
mCompositedLayoutViewport = mFrameMetrics.GetViewport();
mCompositedScrollOffset = mFrameMetrics.GetScrollOffset();
mExpectedGeckoMetrics = aLayerMetrics;
@ -4229,7 +4253,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe
// Even if we didn't accept a new scroll offset from content, the
// scrollable rect may have changed in a way that makes our local
// scroll offset out of bounds, so re-clamp it.
mFrameMetrics.ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset());
ClampAndSetScrollOffset(mFrameMetrics.GetScrollOffset());
}
}
@ -4407,8 +4431,10 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
}
endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
endZoomToMetrics.RecalculateViewportOffset();
StartAnimation(new ZoomAnimation(
*this,
mFrameMetrics.GetScrollOffset(),
mFrameMetrics.GetZoom(),
endZoomToMetrics.GetScrollOffset(),

Просмотреть файл

@ -665,6 +665,23 @@ protected:
*/
nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
/**
* The following five methods modify the scroll offset. For the APZC
* representing the RCD-RSF, they also recalculate the offset of the layout
* viewport.
*/
/**
* Scroll the scroll frame to an X,Y offset.
*/
void SetScrollOffset(const CSSPoint& aOffset);
/**
* Scroll the scroll frame to an X,Y offset, clamping the resulting scroll
* offset to the scroll range.
*/
void ClampAndSetScrollOffset(const CSSPoint& aOffset);
/**
* Scroll the scroll frame by an X,Y offset.
* The resulting scroll offset is not clamped to the scrollable rect;
@ -674,10 +691,15 @@ protected:
/**
* Scroll the scroll frame by an X,Y offset, clamping the resulting
* scroll offset to the scrollable rect.
* scroll offset to the scroll range.
*/
void ScrollByAndClamp(const CSSPoint& aOffset);
/**
* Copy the scroll offset and scroll generation from |aFrameMetrics|.
*/
void CopyScrollInfoFrom(const FrameMetrics& aFrameMetrics);
/**
* Scales the viewport by an amount (note that it multiplies this scale in to
* the current scale, it doesn't set it to |aScale|). Also considers a focus
@ -1185,6 +1207,7 @@ private:
friend class GenericScrollAnimation;
friend class WheelScrollAnimation;
friend class KeyboardScrollAnimation;
friend class ZoomAnimation;
friend class GenericOverscrollEffect;
friend class WidgetOverscrollEffect;

Просмотреть файл

@ -155,7 +155,7 @@ public:
mApzc.mX.AdjustDisplacement(offset.x, adjustedOffset.x, overscroll.x);
mApzc.mY.AdjustDisplacement(offset.y, adjustedOffset.y, overscroll.y);
aFrameMetrics.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
mApzc.ScrollBy(adjustedOffset / aFrameMetrics.GetZoom());
// The fling may have caused us to reach the end of our scroll range.
if (!IsZero(overscroll)) {

Просмотреть файл

@ -105,7 +105,7 @@ GenericScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, const TimeDuration
return false;
}
aFrameMetrics.ScrollBy(adjustedOffset / zoom);
mApzc.ScrollBy(adjustedOffset / zoom);
return !finished;
}

Просмотреть файл

@ -78,7 +78,9 @@ static CSSPoint
ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aSuccessOut)
{
aSuccessOut = false;
CSSPoint targetScrollPosition = aMetrics.GetScrollOffset();
CSSPoint targetScrollPosition = aMetrics.IsRootContent()
? aMetrics.GetViewport().TopLeft()
: aMetrics.GetScrollOffset();
if (!aFrame) {
return targetScrollPosition;
@ -93,18 +95,20 @@ ScrollFrameTo(nsIScrollableFrame* aFrame, const FrameMetrics& aMetrics, bool& aS
return geckoScrollPosition;
}
// If the frame is overflow:hidden on a particular axis, we don't want to allow
// user-driven scroll on that axis. Simply set the scroll position on that axis
// to whatever it already is. Note that this will leave the APZ's async scroll
// position out of sync with the gecko scroll position, but APZ can deal with that
// (by design). Note also that when we run into this case, even if both axes
// have overflow:hidden, we want to set aSuccessOut to true, so that the displayport
// follows the async scroll position rather than the gecko scroll position.
// If this is the root content with overflow:hidden, then APZ should not
// allow scrolling in such a way that moves the layout viewport.
//
// If this is overflow:hidden, but not the root content, then
// nsLayoutUtils::CalculateScrollableRectForFrame should have sized the
// scrollable rect in a way that prevents APZ from scrolling it at all.
//
// In either case, targetScrollPosition should be the same as
// geckoScrollPosition here.
if (aFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
targetScrollPosition.y = geckoScrollPosition.y;
MOZ_ASSERT(targetScrollPosition.y == geckoScrollPosition.y);
}
if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
targetScrollPosition.x = geckoScrollPosition.x;
MOZ_ASSERT(targetScrollPosition.x == geckoScrollPosition.x);
}
// If the scrollable frame is currently in the middle of an async or smooth
@ -162,6 +166,15 @@ ScrollFrame(nsIContent* aContent,
// actual scroll offset.
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
}
} else if (aMetrics.IsRootContent() &&
aMetrics.GetScrollOffset() != aMetrics.GetViewport().TopLeft()) {
// APZ uses the visual viewport's offset to calculate where to place the
// display port, so the display port is misplaced when a pinch zoom occurs.
//
// We need to force a display port adjustment in the following paint to
// account for a difference between mScrollOffset and the actual scroll
// offset in repaints requested by AsyncPanZoomController::NotifyLayersUpdated.
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
} else {
// For whatever reason we couldn't update the scroll offset on the scroll frame,
// which means the data APZ used for its displayport calculation is stale. Fall

Просмотреть файл

@ -9121,8 +9121,12 @@ nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame,
nsLayoutUtils::CalculateScrollableRectForFrame(scrollableFrame, aForFrame)));
if (scrollableFrame) {
nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
metrics.SetScrollOffset(CSSPoint::FromAppUnits(scrollPosition));
CSSPoint scrollPosition = CSSPoint::FromAppUnits(scrollableFrame->GetScrollPosition());
metrics.SetScrollOffset(scrollPosition);
CSSRect viewport = metrics.GetViewport();
viewport.MoveTo(scrollPosition);
metrics.SetViewport(viewport);
nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));