diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 5c0449d5a9b8..339816473da1 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2684,6 +2684,20 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, FrameLayerBuilder::GetPaintedLayerScaleForFrame(mScrolledFrame); nsPoint curPos = GetScrollPosition(); + // Below, we clamp |aPt| to the layout scroll range, compare the clamped + // value with the current scroll position, and early exit if they are the + // same. The early exit bypasses the update of |mLastScrollOrigin|, which + // is important to avoid sending a main-thread layout scroll update to APZ, + // which can clobber the visual scroll offset as well. + // If the layout viewport has shrunk in size since the last scroll, the + // clamped target position can be different from the current position, so + // we don't take the early exit, but conceptually we still want to avoid + // updating |mLastScrollOrigin| and clobbering the visual scroll offset. + bool suppressScrollOriginChange = false; + if (aPt == curPos) { + suppressScrollOriginChange = true; + } + nsPoint alignWithPos = mScrollPosForLayerPixelAlignment == nsPoint(-1, -1) ? curPos : mScrollPosForLayerPixelAlignment; @@ -2759,7 +2773,8 @@ void ScrollFrameHelper::ScrollToImpl(nsPoint aPt, const nsRect& aRange, // legitimate scroll offset updates because the origin has been masked by // a later change within the same refresh driver tick. allowScrollOriginChange = - mAllowScrollOriginDowngrade || !isScrollOriginDowngrade; + (mAllowScrollOriginDowngrade || !isScrollOriginDowngrade) && + !suppressScrollOriginChange; if (allowScrollOriginChange) { mLastScrollOrigin = aOrigin;