Bug 1423011 - Part 1: Allow APZ to async-scroll the layout viewport. r?botond
MozReview-Commit-ID: 7AD8wvthh2m
--- a/gfx/layers/FrameMetrics.cpp
+++ b/gfx/layers/FrameMetrics.cpp
@@ -11,29 +11,34 @@
namespace mozilla {
namespace layers {
const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0;
void
FrameMetrics::RecalculateViewportOffset()
{
- CSSRect cb = CalculateCompositedRectInCssPixels();
- if (!mIsRootContent || mViewport.Contains(cb + mScrollOffset)) {
+ CSSRect visualViewport;
+ if (!mIsRootContent ||
+ mViewport.Contains(visualViewport = GetVisualViewport())) {
return;
}
- if (mScrollOffset.X() < mViewport.X()) {
- mViewport.MoveToX(mScrollOffset.X());
- } else if (mScrollOffset.X() + cb.Width() > mViewport.XMost()) {
- mViewport.MoveByX(mScrollOffset.X() + cb.Width() - mViewport.XMost());
+ MOZ_ASSERT(visualViewport.Height() <= mViewport.Height(),
+ "Visual viewport taller than layout viewport");
+ MOZ_ASSERT(visualViewport.Width() <= mViewport.Width(),
+ "Visual viewport wider than layout viewport");
+ if (visualViewport.X() < mViewport.X()) {
+ mViewport.MoveToX(visualViewport.X());
+ } else if (visualViewport.XMost() > mViewport.XMost()) {
+ mViewport.MoveByX(visualViewport.XMost() - mViewport.XMost());
}
- if (mScrollOffset.Y() < mViewport.Y()) {
- mViewport.MoveToY(mScrollOffset.Y());
- } else if (mScrollOffset.Y() + cb.Height() > mViewport.YMost()) {
- mViewport.MoveByY(mScrollOffset.Y() + cb.Height() - mViewport.YMost());
+ if (visualViewport.Y() < mViewport.Y()) {
+ mViewport.MoveToY(visualViewport.Y());
+ } else if (visualViewport.YMost() > mViewport.YMost()) {
+ mViewport.MoveByY(visualViewport.YMost() - mViewport.YMost());
}
}
void
ScrollMetadata::SetUsesContainerScrolling(bool aValue) {
MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());
mUsesContainerScrolling = aValue;
}
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -463,16 +463,21 @@ public:
mViewport = aViewport;
}
const CSSRect& GetViewport() const
{
return mViewport;
}
+ const CSSRect GetVisualViewport() const
+ {
+ return CSSRect(mScrollOffset, CalculateCompositedSizeInCssPixels());
+ }
+
void SetExtraResolution(const ScreenToLayerScale2D& aExtraResolution)
{
mExtraResolution = aExtraResolution;
}
const ScreenToLayerScale2D& GetExtraResolution() const
{
return mExtraResolution;
@@ -511,16 +516,18 @@ public:
}
bool IsScrollInfoLayer() const {
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;
// The pres-shell resolution that has been induced on the document containing
// this scroll frame as a result of zooming this scroll frame (whether via
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -661,43 +661,47 @@ protected:
* Helper method to cancel any gesture currently going to Gecko. Used
* primarily when a user taps the screen over some clickable content but then
* pans down instead of letting go (i.e. to cancel a previous touch so that a
* new one can properly take effect.
*/
nsEventStatus OnCancelTap(const TapGestureInput& aEvent);
/**
- * Scroll the scroll frame to an X,Y offset and recalculate the viewport
- * offset.
+ * 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 scrollable rect and recalculate the viewport offset.
+ * 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;
* the caller must ensure it stays within range.
*/
void ScrollBy(const CSSPoint& aOffset);
/**
* 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| and
- * recalculate the viewport offset.
+ * 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
* point so that the page zooms inward/outward from that point.
*/
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -90,16 +90,32 @@ ScrollFrameTo(nsIScrollableFrame* aFrame
// If the repaint request was triggered due to a previous main-thread scroll
// offset update sent to the APZ, then we don't need to do another scroll here
// and we can just return.
if (!aMetrics.GetScrollOffsetUpdated()) {
return geckoScrollPosition;
}
+ // 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) {
+ MOZ_ASSERT(targetScrollPosition.y == geckoScrollPosition.y);
+ }
+ if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
+ MOZ_ASSERT(targetScrollPosition.x == geckoScrollPosition.x);
+ }
+
// If the scrollable frame is currently in the middle of an async or smooth
// scroll then we don't want to interrupt it (see bug 961280).
// Also if the scrollable frame got a scroll request from a higher priority origin
// since the last layers update, then we don't want to push our scroll request
// because we'll clobber that one, which is bad.
bool scrollInProgress = APZCCallbackHelper::IsScrollInProgress(aFrame);
if (!scrollInProgress) {
aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
@@ -145,17 +161,18 @@ ScrollFrame(nsIContent* aContent,
if (nsIFrame* frame = aContent->GetPrimaryFrame()) {
frame->SchedulePaint();
}
} else {
// Correct the display port due to the difference between mScrollOffset and the
// actual scroll offset.
APZCCallbackHelper::AdjustDisplayPortForScrollDelta(aMetrics, actualScrollOffset);
}
- } else if (aMetrics.IsRootContent()) {
+ } 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 {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9113,21 +9113,21 @@ nsLayoutUtils::ComputeScrollMetadata(nsI
nsIScrollableFrame* scrollableFrame = nullptr;
if (aScrollFrame)
scrollableFrame = aScrollFrame->GetScrollTargetFrame();
metrics.SetScrollableRect(CSSRect::FromAppUnits(
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(CSSPoint::FromAppUnits(scrollPosition));
+ viewport.MoveTo(scrollPosition);
metrics.SetViewport(viewport);
nsPoint smoothScrollPosition = scrollableFrame->LastScrollDestination();
metrics.SetSmoothScrollOffset(CSSPoint::FromAppUnits(smoothScrollPosition));
// If the frame was scrolled since the last layers update, and by something
// that is higher priority than APZ, we want to tell the APZ to update
// its scroll offset. We want to distinguish the case where the scroll offset