Bug 1235899 - Don't allow frame reconstruction to clobber APZ flings. r?botond draft
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 10 Feb 2016 11:28:43 -0500
changeset 330113 2157966a9f2fb811630c814e4785437338799d39
parent 330112 ac39fba33c6daf95b2cda71e588ca18e2eb752ab
child 330114 c17aa8a2e8c079fc66ec0cc0e23463a93f20f175
push id10679
push userkgupta@mozilla.com
push dateWed, 10 Feb 2016 16:59:41 +0000
reviewersbotond
bugs1235899
milestone47.0a1
Bug 1235899 - Don't allow frame reconstruction to clobber APZ flings. r?botond MozReview-Commit-ID: 8E4e9tNr1Vy
dom/base/nsGkAtomList.h
gfx/layers/apz/util/APZCCallbackHelper.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsGfxScrollFrame.cpp
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -1041,16 +1041,17 @@ GK_ATOM(replace, "replace")
 GK_ATOM(required, "required")
 GK_ATOM(reset, "reset")
 GK_ATOM(resizeafter, "resizeafter")
 GK_ATOM(resizebefore, "resizebefore")
 GK_ATOM(resizer, "resizer")
 GK_ATOM(resolution, "resolution")
 GK_ATOM(resource, "resource")
 GK_ATOM(resources, "resources")
+GK_ATOM(restore, "restore")
 GK_ATOM(result, "result")
 GK_ATOM(resultPrefix, "result-prefix")
 GK_ATOM(retargetdocumentfocus, "retargetdocumentfocus")
 GK_ATOM(rev, "rev")
 GK_ATOM(reverse, "reverse")
 GK_ATOM(reversed, "reversed")
 GK_ATOM(richlistbox, "richlistbox")
 GK_ATOM(richlistitem, "richlistitem")
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -87,21 +87,21 @@ ScrollFrameTo(nsIScrollableFrame* aFrame
     targetScrollPosition.y = geckoScrollPosition.y;
   }
   if (aFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
     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 something other than us
+  // 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 = aFrame->IsProcessingAsyncScroll()
-      || (aFrame->LastScrollOrigin() && aFrame->LastScrollOrigin() != nsGkAtoms::apz)
+      || nsLayoutUtils::CanScrollOriginClobberApz(aFrame->LastScrollOrigin())
       || aFrame->LastSmoothScrollOrigin();
   if (!scrollInProgress) {
     aFrame->ScrollToCSSPixelsApproximate(targetScrollPosition, nsGkAtoms::apz);
     geckoScrollPosition = CSSPoint::FromAppUnits(aFrame->GetScrollPosition());
     aSuccessOut = true;
   }
   // Return the final scroll position after setting it so that anything that relies
   // on it can have an accurate value. Note that even if we set it above re-querying it
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8576,16 +8576,24 @@ nsLayoutUtils::SetScrollPositionClamping
   // layout depends on the size of the screen.  Since when the size
   // of the screen changes, the scroll position clamping scroll port
   // size also changes, we hook in the needed updates here rather
   // than adding a separate notification just for this change.
   nsPresContext* presContext = aPresShell->GetPresContext();
   MaybeReflowForInflationScreenSizeChange(presContext);
 }
 
+/* static */ bool
+nsLayoutUtils::CanScrollOriginClobberApz(nsIAtom* aScrollOrigin)
+{
+  return aScrollOrigin != nullptr
+      && aScrollOrigin != nsGkAtoms::apz
+      && aScrollOrigin != nsGkAtoms::restore;
+}
+
 /* static */ FrameMetrics
 nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame,
                                    nsIFrame* aScrollFrame,
                                    nsIContent* aContent,
                                    const nsIFrame* aReferenceFrame,
                                    Layer* aLayer,
                                    ViewID aScrollParentId,
                                    const nsRect& aViewport,
@@ -8632,21 +8640,20 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFr
 
   if (scrollableFrame) {
     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
+    // 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.
-    nsIAtom* lastScrollOrigin = scrollableFrame->LastScrollOrigin();
-    if (lastScrollOrigin && lastScrollOrigin != nsGkAtoms::apz) {
+    if (CanScrollOriginClobberApz(scrollableFrame->LastScrollOrigin())) {
       metrics.SetScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
     }
     nsIAtom* lastSmoothScrollOrigin = scrollableFrame->LastSmoothScrollOrigin();
     if (lastSmoothScrollOrigin) {
       metrics.SetSmoothScrollOffsetUpdated(scrollableFrame->CurrentScrollGeneration());
     }
 
     nsSize lineScrollAmount = scrollableFrame->GetLineScrollAmount();
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -2717,16 +2717,25 @@ public:
   /**
    * Set the scroll port size for the purpose of clamping the scroll position
    * for the root scroll frame of this document
    * (see nsIDOMWindowUtils.setScrollPositionClampingScrollPortSize).
    */
   static void SetScrollPositionClampingScrollPortSize(nsIPresShell* aPresShell,
                                                       CSSSize aSize);
 
+  /**
+   * Returns true if the given scroll origin is "higher priority" than APZ.
+   * In general any content programmatic scrolls (e.g. scrollTo calls) are
+   * higher priority, and take precedence over APZ scrolling. This function
+   * returns true for those, and returns false for other origins like APZ
+   * itself, or scroll position updates from the history restore code.
+   */
+  static bool CanScrollOriginClobberApz(nsIAtom* aScrollOrigin);
+
   static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame,
                                           nsIFrame* aScrollFrame,
                                           nsIContent* aContent,
                                           const nsIFrame* aReferenceFrame,
                                           Layer* aLayer,
                                           ViewID aScrollParentId,
                                           const nsRect& aViewport,
                                           const mozilla::Maybe<nsRect>& aClipRect,
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3933,17 +3933,18 @@ ScrollFrameHelper::ScrollToRestoredPosit
     // and scroll many times.
     if (mRestorePos != mLastPos /* GetLogicalScrollPosition() */) {
       nsPoint scrollToPos = mRestorePos;
       if (!IsLTR())
         // convert from logical to physical scroll position
         scrollToPos.x = mScrollPort.x -
           (mScrollPort.XMost() - scrollToPos.x - mScrolledFrame->GetRect().width);
       nsWeakFrame weakFrame(mOuter);
-      ScrollTo(scrollToPos, nsIScrollableFrame::INSTANT);
+      ScrollToWithOrigin(scrollToPos, nsIScrollableFrame::INSTANT,
+                         nsGkAtoms::restore, nullptr);
       if (!weakFrame.IsAlive()) {
         return;
       }
       // Re-get the scroll position, it might not be exactly equal to
       // mRestorePos due to rounding and clamping.
       mLastPos = GetLogicalScrollPosition();
     } else {
       // if we reached the position then stop