Bug 950488 - Overhaul APZ handling of overflow:hidden. r=botond, a=1.3+
authorBenoit Girard <bgirard@mozilla.com>
Tue, 21 Jan 2014 12:33:52 -0500
changeset 175927 93fdb91a7e145ea706000244a436db0fff9dd115
parent 175926 bb2a6925b7124997394ff2f64483995b4e12e9be
child 175928 7824e4c50fae04fff19b68595eaedf1318048025
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond, 1.3
bugs950488
milestone28.0a2
Bug 950488 - Overhaul APZ handling of overflow:hidden. r=botond, a=1.3+
gfx/ipc/GfxMessageUtils.h
gfx/layers/FrameMetrics.h
gfx/layers/ipc/AsyncPanZoomController.cpp
gfx/layers/ipc/Axis.cpp
gfx/layers/ipc/Axis.h
layout/base/nsDisplayList.cpp
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -581,16 +581,18 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mCumulativeResolution);
     WriteParam(aMsg, aParam.mZoom);
     WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
     WriteParam(aMsg, aParam.mMayHaveTouchListeners);
     WriteParam(aMsg, aParam.mPresShellId);
     WriteParam(aMsg, aParam.mIsRoot);
     WriteParam(aMsg, aParam.mHasScrollgrab);
     WriteParam(aMsg, aParam.mUpdateScrollOffset);
+    WriteParam(aMsg, aParam.mDisableScrollingX);
+    WriteParam(aMsg, aParam.mDisableScrollingY);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     return (ReadParam(aMsg, aIter, &aResult->mScrollableRect) &&
             ReadParam(aMsg, aIter, &aResult->mViewport) &&
             ReadParam(aMsg, aIter, &aResult->mScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mDisplayPort) &&
@@ -600,17 +602,19 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mResolution) &&
             ReadParam(aMsg, aIter, &aResult->mCumulativeResolution) &&
             ReadParam(aMsg, aIter, &aResult->mZoom) &&
             ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
             ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
             ReadParam(aMsg, aIter, &aResult->mIsRoot) &&
             ReadParam(aMsg, aIter, &aResult->mHasScrollgrab) &&
-            ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset));
+            ReadParam(aMsg, aIter, &aResult->mUpdateScrollOffset) &&
+            ReadParam(aMsg, aIter, &aResult->mDisableScrollingX) &&
+            ReadParam(aMsg, aIter, &aResult->mDisableScrollingY));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -7,16 +7,20 @@
 #define GFX_FRAMEMETRICS_H
 
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "Units.h"                      // for CSSRect, CSSPixel, etc
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 
+namespace IPC {
+template <typename T> struct ParamTraits;
+} // namespace IPC
+
 namespace mozilla {
 namespace layers {
 
 // The layer coordinates of the parent layer. Like the layer coordinates
 // of the current layer (LayerPixel) but doesn't include the current layer's
 // resolution.
 struct ParentLayerPixel {};
 
@@ -27,16 +31,17 @@ typedef gfx::ScaleFactor<ParentLayerPixe
 
 /**
  * The viewport and displayport metrics for the painted frame at the
  * time of a layer-tree transaction.  These metrics are especially
  * useful for shadow layers, because the metrics values are updated
  * atomically with new pixels.
  */
 struct FrameMetrics {
+  friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
 public:
   // We use IDs to identify frames across processes.
   typedef uint64_t ViewID;
   static const ViewID NULL_SCROLL_ID;   // This container layer does not scroll.
   static const ViewID START_SCROLL_ID = 2;  // This is the ID that scrolling subframes
                                         // will begin at.
 
   FrameMetrics()
@@ -46,21 +51,23 @@ public:
     , mViewport(0, 0, 0, 0)
     , mScrollOffset(0, 0)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollableRect(0, 0, 0, 0)
     , mResolution(1)
     , mCumulativeResolution(1)
     , mZoom(1)
     , mDevPixelsPerCSSPixel(1)
+    , mPresShellId(-1)
     , mMayHaveTouchListeners(false)
-    , mPresShellId(-1)
     , mIsRoot(false)
     , mHasScrollgrab(false)
     , mUpdateScrollOffset(false)
+    , mDisableScrollingX(false)
+    , mDisableScrollingY(false)
   {}
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
            mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
@@ -70,16 +77,19 @@ public:
            mScrollId == aOther.mScrollId &&
            mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
            mResolution == aOther.mResolution &&
            mCumulativeResolution == aOther.mCumulativeResolution &&
            mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
            mMayHaveTouchListeners == aOther.mMayHaveTouchListeners &&
            mPresShellId == aOther.mPresShellId &&
            mIsRoot == aOther.mIsRoot &&
+           mHasScrollgrab == aOther.mHasScrollgrab &&
+           mDisableScrollingX == aOther.mDisableScrollingX &&
+           mDisableScrollingY == aOther.mDisableScrollingY &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsDefault() const
@@ -272,30 +282,60 @@ public:
   CSSToScreenScale mZoom;
 
   // The conversion factor between CSS pixels and device pixels for this frame.
   // This can vary based on a variety of things, such as reflowing-zoom. The
   // conversion factor for device pixels to layers pixels is just the
   // resolution.
   CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
 
+  uint32_t mPresShellId;
+
   // Whether or not this frame may have touch listeners.
   bool mMayHaveTouchListeners;
 
-  uint32_t mPresShellId;
-
   // Whether or not this is the root scroll frame for the root content document.
   bool mIsRoot;
 
   // Whether or not this frame is for an element marked 'scrollgrab'.
   bool mHasScrollgrab;
 
   // Whether mScrollOffset was updated by something other than the APZ code, and
   // if the APZC receiving this metrics should update its local copy.
   bool mUpdateScrollOffset;
+
+public:
+  bool GetDisableScrollingX() const
+  {
+    return mDisableScrollingX;
+  }
+
+  void SetDisableScrollingX(bool aDisableScrollingX)
+  {
+    mDisableScrollingX = aDisableScrollingX;
+  }
+
+  bool GetDisableScrollingY() const
+  {
+    return mDisableScrollingY;
+  }
+
+  void SetDisableScrollingY(bool aDisableScrollingY)
+  {
+    mDisableScrollingY = aDisableScrollingY;
+  }
+
+private:
+  // New fields from now on should be made private and old fields should
+  // be refactored to be private.
+
+  // Allow disabling scrolling in individual axis to support
+  // |overflow: hidden|.
+  bool mDisableScrollingX;
+  bool mDisableScrollingY;
 };
 
 /**
  * This class allows us to uniquely identify a scrollable layer. The
  * mLayersId identifies the layer tree (corresponding to a child process
  * and/or tab) that the scrollable layer belongs to. The mPresShellId
  * is a temporal identifier (corresponding to the document loaded that
  * contains the scrollable layer, which may change over time). The
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -958,34 +958,39 @@ void AsyncPanZoomController::UpdateWithT
 
   mX.UpdateWithTouchAtDevicePoint(point.x, timeDelta);
   mY.UpdateWithTouchAtDevicePoint(point.y, timeDelta);
 }
 
 void AsyncPanZoomController::AttemptScroll(const ScreenPoint& aStartPoint,
                                            const ScreenPoint& aEndPoint,
                                            uint32_t aOverscrollHandoffChainIndex) {
+
   // "start - end" rather than "end - start" because e.g. moving your finger
   // down (*positive* direction along y axis) causes the vertical scroll offset
   // to *decrease* as the page follows your finger.
   ScreenPoint displacement = aStartPoint - aEndPoint;
 
   ScreenPoint overscroll;  // will be used outside monitor block
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
 
     CSSToScreenScale zoom = mFrameMetrics.mZoom;
 
     // Inversely scale the offset by the resolution (when you're zoomed further in,
     // a larger swipe should move you a shorter distance).
     CSSPoint cssDisplacement = displacement / zoom;
 
     CSSPoint cssOverscroll;
-    gfx::Point scrollOffset(mX.AdjustDisplacement(cssDisplacement.x, cssOverscroll.x),
-                            mY.AdjustDisplacement(cssDisplacement.y, cssOverscroll.y));
+    gfx::Point scrollOffset(mX.AdjustDisplacement(cssDisplacement.x,
+                                                  cssOverscroll.x,
+                                                  mFrameMetrics.GetDisableScrollingX()),
+                            mY.AdjustDisplacement(cssDisplacement.y,
+                                                  cssOverscroll.y,
+                                                  mFrameMetrics.GetDisableScrollingY()));
     overscroll = cssOverscroll * zoom;
 
     if (fabs(scrollOffset.x) > EPSILON || fabs(scrollOffset.y) > EPSILON) {
       ScrollBy(CSSPoint::FromUnknownPoint(scrollOffset));
       ScheduleComposite();
 
       TimeDuration timePaintDelta = mPaintThrottler.TimeSinceLastRequest(GetFrameTime());
       if (timePaintDelta.ToMilliseconds() > gPanRepaintInterval) {
@@ -1032,22 +1037,22 @@ void AsyncPanZoomController::TrackTouch(
     double angle = atan2(dy, dx); // range [-pi, pi]
     angle = fabs(angle); // range [0, pi]
 
     float breakThreshold = AXIS_BREAKOUT_THRESHOLD * APZCTreeManager::GetDPI();
 
     if (fabs(dx) > breakThreshold || fabs(dy) > breakThreshold) {
       if (mState == PANNING_LOCKED_X || mState == CROSS_SLIDING_X) {
         if (!IsCloseToHorizontal(angle, AXIS_BREAKOUT_ANGLE)) {
-          mY.SetScrollingDisabled(false);
+          mY.SetAxisLocked(false);
           SetState(PANNING);
         }
       } else if (mState == PANNING_LOCKED_Y || mState == CROSS_SLIDING_Y) {
         if (!IsCloseToVertical(angle, AXIS_BREAKOUT_ANGLE)) {
-          mX.SetScrollingDisabled(false);
+          mX.SetAxisLocked(false);
           SetState(PANNING);
         }
       }
     }
   }
 
   UpdateWithTouchAtDevicePoint(aEvent);
 
@@ -1070,18 +1075,20 @@ bool FlingAnimation::Sample(FrameMetrics
   CSSPoint overscroll; // overscroll is ignored for flings
   ScreenPoint offset(aDelta.ToMilliseconds() * mX.GetVelocity(),
                      aDelta.ToMilliseconds() * mY.GetVelocity());
 
   // Inversely scale the offset by the resolution (when you're zoomed further in,
   // a larger swipe should move you a shorter distance).
   CSSPoint cssOffset = offset / aFrameMetrics.mZoom;
   aFrameMetrics.mScrollOffset += CSSPoint::FromUnknownPoint(gfx::Point(
-    mX.AdjustDisplacement(cssOffset.x, overscroll.x),
-    mY.AdjustDisplacement(cssOffset.y, overscroll.y)
+    mX.AdjustDisplacement(cssOffset.x, overscroll.x,
+                          aFrameMetrics.GetDisableScrollingX()),
+    mY.AdjustDisplacement(cssOffset.y, overscroll.y,
+                          aFrameMetrics.GetDisableScrollingX())
   ));
 
   return true;
 }
 
 void AsyncPanZoomController::StartAnimation(AsyncPanZoomAnimation* aAnimation)
 {
   ReentrantMonitorAutoEnter lock(mMonitor);
@@ -1414,16 +1421,18 @@ void AsyncPanZoomController::NotifyLayer
     // determined by Gecko and our copy in mFrameMetrics may be stale.
     mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
     float parentResolutionChange = aLayerMetrics.GetParentResolution().scale
                                  / mFrameMetrics.GetParentResolution().scale;
     mFrameMetrics.mZoom.scale *= parentResolutionChange;
     mFrameMetrics.mResolution = aLayerMetrics.mResolution;
     mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
+    mFrameMetrics.SetDisableScrollingX(aLayerMetrics.GetDisableScrollingX());
+    mFrameMetrics.SetDisableScrollingY(aLayerMetrics.GetDisableScrollingY());
 
     // If the layers update was not triggered by our own repaint request, then
     // we want to take the new scroll offset.
     if (aLayerMetrics.mUpdateScrollOffset) {
       APZC_LOG("Updating scroll offset from (%f, %f) to (%f, %f)\n",
         mFrameMetrics.mScrollOffset.x, mFrameMetrics.mScrollOffset.y,
         aLayerMetrics.mScrollOffset.x, aLayerMetrics.mScrollOffset.y);
 
--- a/gfx/layers/ipc/Axis.cpp
+++ b/gfx/layers/ipc/Axis.cpp
@@ -96,24 +96,24 @@ static void InitAxisPrefs()
     NS_DispatchToMainThread(new ReadAxisPref());
   }
 }
 
 Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
   : mPos(0),
     mVelocity(0.0f),
     mAcceleration(0),
-    mScrollingDisabled(false),
+    mAxisLocked(false),
     mAsyncPanZoomController(aAsyncPanZoomController)
 {
   InitAxisPrefs();
 }
 
 void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) {
-  float newVelocity = mScrollingDisabled ? 0 : (mPos - aPos) / aTimeDelta.ToMilliseconds();
+  float newVelocity = mAxisLocked ? 0 : (mPos - aPos) / aTimeDelta.ToMilliseconds();
 
   bool curVelocityBelowThreshold = fabsf(newVelocity) < gVelocityThreshold;
   bool directionChange = (mVelocity > 0) != (newVelocity > 0);
 
   // If we've changed directions, or the current velocity threshold, stop any
   // acceleration we've accumulated.
   if (directionChange || curVelocityBelowThreshold) {
     mAcceleration = 0;
@@ -127,25 +127,33 @@ void Axis::UpdateWithTouchAtDevicePoint(
   if (mVelocityQueue.Length() > gMaxVelocityQueueSize) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
 void Axis::StartTouch(int32_t aPos) {
   mStartPos = aPos;
   mPos = aPos;
-  mScrollingDisabled = false;
+  mAxisLocked = false;
 }
 
-float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut) {
-  if (mScrollingDisabled) {
+float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut,
+                               bool aScrollingDisabled) {
+  if (mAxisLocked) {
     aOverscrollAmountOut = 0;
     return 0;
   }
 
+  if (aScrollingDisabled) {
+    // Scrolling is disabled on this axis, stop scrolling.
+    aOverscrollAmountOut = aDisplacement;
+    mAcceleration = 0;
+    return 0;
+  }
+
   if (fabsf(mVelocity) < gVelocityThreshold) {
     mAcceleration = 0;
   }
 
   float accelerationFactor = GetAccelerationFactor();
   float displacement = aDisplacement * accelerationFactor;
   // If this displacement will cause an overscroll, throttle it. Can potentially
   // bring it to 0 even if the velocity is high.
@@ -187,17 +195,17 @@ void Axis::CancelTouch() {
   mVelocity = 0.0f;
   mAcceleration = 0;
   while (!mVelocityQueue.IsEmpty()) {
     mVelocityQueue.RemoveElementAt(0);
   }
 }
 
 bool Axis::Scrollable() {
-    if (mScrollingDisabled) {
+    if (mAxisLocked) {
         return false;
     }
     return GetCompositionLength() < GetPageLength();
 }
 
 bool Axis::FlingApplyFrictionOrCancel(const TimeDuration& aDelta) {
   if (fabsf(mVelocity) <= gFlingStoppedThreshold) {
     // If the velocity is very low, just set it to 0 and stop the fling,
@@ -286,17 +294,17 @@ float Axis::ScaleWillOverscrollAmount(fl
   }
   if (plus) {
     return originAfterScale + (GetCompositionLength() / aScale) - GetPageEnd();
   }
   return 0;
 }
 
 float Axis::GetVelocity() {
-  return mScrollingDisabled ? 0 : mVelocity;
+  return mAxisLocked ? 0 : mVelocity;
 }
 
 float Axis::GetAccelerationFactor() {
   return powf(gAccelerationMultiplier, std::max(0, (mAcceleration - 4) * 3));
 }
 
 float Axis::GetCompositionEnd() {
   return GetOrigin() + GetCompositionLength();
--- a/gfx/layers/ipc/Axis.h
+++ b/gfx/layers/ipc/Axis.h
@@ -72,18 +72,22 @@ public:
   /**
    * Takes a requested displacement to the position of this axis, and adjusts
    * it to account for acceleration (which might increase the displacement),
    * overscroll (which might decrease the displacement; this is to prevent the
    * viewport from overscrolling the page rect), and axis locking (which might
    * prevent any displacement from happening). If overscroll ocurred, its amount
    * is written to |aOverscrollAmountOut|.
    * The adjusted displacement is returned.
+   *
+   * aScrollingDisabled is used to indicate that no scrolling should happen
+   * in this axis. This is used to implement overflow: hidden;
    */
-  float AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut);
+  float AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut,
+                           bool aScrollingDisabled);
 
   /**
    * Gets the distance between the starting position of the touch supplied in
    * startTouch() and the current touch from the last
    * updateWithTouchAtDevicePoint().
    */
   float PanDistance();
 
@@ -102,17 +106,17 @@ public:
   bool FlingApplyFrictionOrCancel(const TimeDuration& aDelta);
 
   /*
    * Returns true if the page is zoomed in to some degree along this axis such that scrolling is
    * possible and this axis has not been scroll locked while panning. Otherwise, returns false.
    */
   bool Scrollable();
 
-  void SetScrollingDisabled(bool aDisabled) { mScrollingDisabled = aDisabled; }
+  void SetAxisLocked(bool aAxisLocked) { mAxisLocked = aAxisLocked; }
 
   /**
    * Gets the overscroll state of the axis in its current position.
    */
   Overscroll GetOverscroll();
 
   /**
    * If there is overscroll, returns the amount. Sign depends on in what
@@ -185,17 +189,17 @@ protected:
   int32_t mStartPos;
   float mVelocity;
   // Acceleration is represented by an int, which is the power we raise a
   // constant to and then multiply the velocity by whenever it is sampled. We do
   // this only when we detect that the user wants to do a fast fling; that is,
   // they are flinging multiple times in a row very quickly, probably trying to
   // reach one of the extremes of the page.
   int32_t mAcceleration;
-  bool mScrollingDisabled;     // Whether movement on this axis is locked.
+  bool mAxisLocked;     // Whether movement on this axis is locked.
   AsyncPanZoomController* mAsyncPanZoomController;
   nsTArray<float> mVelocityQueue;
 };
 
 class AxisX : public Axis {
 public:
   AxisX(AsyncPanZoomController* mAsyncPanZoomController);
   virtual float GetPointOffset(const CSSPoint& aPoint);
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -616,20 +616,20 @@ static void RecordFrameMetrics(nsIFrame*
 
   nsIScrollableFrame* scrollableFrame = nullptr;
   if (aScrollFrame)
     scrollableFrame = aScrollFrame->GetScrollTargetFrame();
 
   if (scrollableFrame) {
     nsRect contentBounds = scrollableFrame->GetScrollRange();
     if (scrollableFrame->GetScrollbarStyles().mVertical == NS_STYLE_OVERFLOW_HIDDEN) {
-      contentBounds.height = 0;
+      metrics.SetDisableScrollingY(true);
     }
     if (scrollableFrame->GetScrollbarStyles().mHorizontal == NS_STYLE_OVERFLOW_HIDDEN) {
-      contentBounds.width = 0;
+      metrics.SetDisableScrollingX(true);
     }
     contentBounds.width += scrollableFrame->GetScrollPortRect().width;
     contentBounds.height += scrollableFrame->GetScrollPortRect().height;
     metrics.mScrollableRect = CSSRect::FromAppUnits(contentBounds);
     nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
     metrics.mScrollOffset = CSSPoint::FromAppUnits(scrollPosition);
 
     // If the frame was scrolled since the last layers update, and by