Bug 1219296 - Factor out scroll snap information into a form that's usable by the compositor. r=kats
MozReview-Commit-ID: DTvu7UsKsBg
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -11,16 +11,17 @@
#include "mozilla/HashFunctions.h" // for HashGeneric
#include "mozilla/Maybe.h"
#include "mozilla/gfx/BasePoint.h" // for BasePoint
#include "mozilla/gfx/Rect.h" // for RoundedIn
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
#include "mozilla/gfx/Logging.h" // for Log
#include "mozilla/TimeStamp.h" // for TimeStamp
#include "nsString.h"
+#include "nsStyleCoord.h" // for nsStyleCoord
namespace IPC {
template <typename T> struct ParamTraits;
} // namespace IPC
namespace mozilla {
namespace layers {
@@ -746,16 +747,39 @@ private:
void SetUpdateScrollOffset(bool aValue) {
mUpdateScrollOffset = aValue;
}
void SetDoSmoothScroll(bool aValue) {
mDoSmoothScroll = aValue;
}
};
+struct ScrollSnapInfo {
+ ScrollSnapInfo()
+ : mScrollSnapTypeX(NS_STYLE_SCROLL_SNAP_TYPE_NONE)
+ , mScrollSnapTypeY(NS_STYLE_SCROLL_SNAP_TYPE_NONE)
+ {}
+
+ // The scroll frame's scroll-snap-type.
+ // One of NS_STYLE_SCROLL_SNAP_{NONE, MANDATORY, PROXIMITY}.
+ uint8_t mScrollSnapTypeX;
+ uint8_t mScrollSnapTypeY;
+
+ // The intervals derived from the scroll frame's scroll-snap-points.
+ Maybe<nscoord> mScrollSnapIntervalX;
+ Maybe<nscoord> mScrollSnapIntervalY;
+
+ // The scroll frame's scroll-snap-destination, in cooked form (to avoid
+ // shipping the raw nsStyleCoord::CalcValue over IPC).
+ nsPoint mScrollSnapDestination;
+
+ // The scroll-snap-coordinates of any descendant frames of the scroll frame,
+ // relative to the origin of the scrolled frame.
+ nsTArray<nsPoint> mScrollSnapCoordinates;
+};
/**
* Metadata about a scroll frame that's stored in the layer tree for use by
* the compositor (including APZ). This includes the scroll frame's FrameMetrics,
* as well as other metadata. We don't put the other metadata into FrameMetrics
* to avoid FrameMetrics becoming too bloated (as a FrameMetrics is e.g. sent
* over IPC for every repaint request for every active scroll frame).
*/
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -6005,109 +6005,168 @@ CalcSnapPoints::AddEdgeInterval(nscoord
edge += aInterval;
if (edge >= aMinPos && edge <= aMaxPos) {
AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge,
aEdgeFound);
}
}
static void
-ScrollSnapHelper(SnappingEdgeCallback& aCallback, nsIFrame* aFrame,
- nsIFrame* aScrolledFrame,
- const nsPoint &aScrollSnapDestination) {
+ProcessScrollSnapCoordinates(SnappingEdgeCallback& aCallback,
+ const nsTArray<nsPoint>& aScrollSnapCoordinates,
+ const nsPoint& aScrollSnapDestination) {
+ for (nsPoint snapCoords : aScrollSnapCoordinates) {
+ // Make them relative to the scroll snap destination.
+ snapCoords -= aScrollSnapDestination;
+
+ aCallback.AddVerticalEdge(snapCoords.x);
+ aCallback.AddHorizontalEdge(snapCoords.y);
+ }
+}
+
+/**
+ * Collect the scroll-snap-coordinates of frames in the subtree rooted at
+ * |aFrame|, relative to |aScrolledFrame|, into |aOutCoords|.
+ */
+void
+CollectScrollSnapCoordinates(nsIFrame* aFrame, nsIFrame* aScrolledFrame,
+ nsTArray<nsPoint>& aOutCoords)
+{
nsIFrame::ChildListIterator childLists(aFrame);
for (; !childLists.IsDone(); childLists.Next()) {
nsFrameList::Enumerator childFrames(childLists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* f = childFrames.get();
const nsStyleDisplay* styleDisplay = f->StyleDisplay();
size_t coordCount = styleDisplay->mScrollSnapCoordinate.Length();
if (coordCount) {
nsRect frameRect = f->GetRect();
nsPoint offset = f->GetOffsetTo(aScrolledFrame);
nsRect edgesRect = nsRect(offset, frameRect.Size());
for (size_t coordNum = 0; coordNum < coordCount; coordNum++) {
const nsStyleImageLayers::Position &coordPosition =
f->StyleDisplay()->mScrollSnapCoordinate[coordNum];
- nsPoint coordPoint = edgesRect.TopLeft() - aScrollSnapDestination;
+ nsPoint coordPoint = edgesRect.TopLeft();
coordPoint += nsPoint(coordPosition.mXPosition.mLength,
coordPosition.mYPosition.mLength);
if (coordPosition.mXPosition.mHasPercent) {
coordPoint.x += NSToCoordRound(coordPosition.mXPosition.mPercent *
frameRect.width);
}
if (coordPosition.mYPosition.mHasPercent) {
coordPoint.y += NSToCoordRound(coordPosition.mYPosition.mPercent *
frameRect.height);
}
- aCallback.AddVerticalEdge(coordPoint.x);
- aCallback.AddHorizontalEdge(coordPoint.y);
+ aOutCoords.AppendElement(coordPoint);
}
}
- ScrollSnapHelper(aCallback, f, aScrolledFrame, aScrollSnapDestination);
+ CollectScrollSnapCoordinates(f, aScrolledFrame, aOutCoords);
}
}
}
+layers::ScrollSnapInfo
+ComputeScrollSnapInfo(const ScrollFrameHelper& aScrollFrame)
+{
+ ScrollSnapInfo result;
+
+ ScrollbarStyles styles = aScrollFrame.GetScrollbarStylesFromFrame();
+
+ if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
+ styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
+ // We won't be snapping, short-circuit the computation.
+ return result;
+ }
+
+ result.mScrollSnapTypeX = styles.mScrollSnapTypeX;
+ result.mScrollSnapTypeY = styles.mScrollSnapTypeY;
+
+ nsSize scrollPortSize = aScrollFrame.GetScrollPortRect().Size();
+
+ result.mScrollSnapDestination = nsPoint(styles.mScrollSnapDestinationX.mLength,
+ styles.mScrollSnapDestinationY.mLength);
+ if (styles.mScrollSnapDestinationX.mHasPercent) {
+ result.mScrollSnapDestination.x +=
+ NSToCoordFloorClamped(styles.mScrollSnapDestinationX.mPercent *
+ scrollPortSize.width);
+ }
+ if (styles.mScrollSnapDestinationY.mHasPercent) {
+ result.mScrollSnapDestination.y +=
+ NSToCoordFloorClamped(styles.mScrollSnapDestinationY.mPercent *
+ scrollPortSize.height);
+ }
+
+ if (styles.mScrollSnapPointsX.GetUnit() != eStyleUnit_None) {
+ result.mScrollSnapIntervalX = Some(nsRuleNode::ComputeCoordPercentCalc(
+ styles.mScrollSnapPointsX, scrollPortSize.width));
+ }
+ if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) {
+ result.mScrollSnapIntervalY = Some(nsRuleNode::ComputeCoordPercentCalc(
+ styles.mScrollSnapPointsY, scrollPortSize.height));
+ }
+
+ CollectScrollSnapCoordinates(aScrollFrame.GetScrolledFrame(),
+ aScrollFrame.GetScrolledFrame(),
+ result.mScrollSnapCoordinates);
+
+ return result;
+}
+
+layers::ScrollSnapInfo
+ScrollFrameHelper::GetScrollSnapInfo() const
+{
+ // TODO(botond): Should we cache it?
+ return ComputeScrollSnapInfo(*this);
+}
+
bool
ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUnit,
nsPoint aStartPos,
nsPoint &aDestination)
{
- ScrollbarStyles styles = GetScrollbarStylesFromFrame();
- if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
- styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
+ ScrollSnapInfo snapInfo = GetScrollSnapInfo();
+
+ if (snapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE &&
+ snapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) {
return false;
}
nsSize scrollPortSize = mScrollPort.Size();
nsRect scrollRange = GetScrollRangeForClamping();
- nsPoint destPos = nsPoint(styles.mScrollSnapDestinationX.mLength,
- styles.mScrollSnapDestinationY.mLength);
- if (styles.mScrollSnapDestinationX.mHasPercent) {
- destPos.x += NSToCoordFloorClamped(styles.mScrollSnapDestinationX.mPercent
- * scrollPortSize.width);
- }
-
- if (styles.mScrollSnapDestinationY.mHasPercent) {
- destPos.y += NSToCoordFloorClamped(styles.mScrollSnapDestinationY.mPercent
- * scrollPortSize.height);
- }
+ nsPoint destPos = snapInfo.mScrollSnapDestination;
CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos);
- if (styles.mScrollSnapPointsX.GetUnit() != eStyleUnit_None) {
- nscoord interval = nsRuleNode::ComputeCoordPercentCalc(styles.mScrollSnapPointsX,
- scrollPortSize.width);
+ if (snapInfo.mScrollSnapIntervalX.isSome()) {
+ nscoord interval = snapInfo.mScrollSnapIntervalX.value();
calcSnapPoints.AddVerticalEdgeInterval(scrollRange, interval, destPos.x);
}
- if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) {
- nscoord interval = nsRuleNode::ComputeCoordPercentCalc(styles.mScrollSnapPointsY,
- scrollPortSize.height);
+ if (snapInfo.mScrollSnapIntervalY.isSome()) {
+ nscoord interval = snapInfo.mScrollSnapIntervalY.value();
calcSnapPoints.AddHorizontalEdgeInterval(scrollRange, interval, destPos.y);
}
- ScrollSnapHelper(calcSnapPoints, mScrolledFrame, mScrolledFrame, destPos);
+ ProcessScrollSnapCoordinates(calcSnapPoints, snapInfo.mScrollSnapCoordinates, destPos);
bool snapped = false;
nsPoint finalPos = calcSnapPoints.GetBestEdge();
nscoord proximityThreshold =
Preferences::GetInt("layout.css.scroll-snap.proximity-threshold", 0);
proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold);
- if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
+ if (snapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
std::abs(aDestination.y - finalPos.y) > proximityThreshold) {
finalPos.y = aDestination.y;
} else {
snapped = true;
}
- if (styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
+ if (snapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY &&
std::abs(aDestination.x - finalPos.x) > proximityThreshold) {
finalPos.x = aDestination.x;
} else {
snapped = true;
}
if (snapped) {
aDestination = finalPos;
}
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -44,16 +44,17 @@ class ScrollbarActivity;
namespace mozilla {
class ScrollFrameHelper : public nsIReflowCallback {
public:
typedef nsIFrame::Sides Sides;
typedef mozilla::CSSIntPoint CSSIntPoint;
typedef mozilla::layout::ScrollbarActivity ScrollbarActivity;
typedef mozilla::layers::FrameMetrics FrameMetrics;
+ typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo;
typedef mozilla::layers::Layer Layer;
class AsyncScroll;
class AsyncSmoothMSDScroll;
ScrollFrameHelper(nsContainerFrame* aOuter, bool aIsRoot);
~ScrollFrameHelper();
@@ -385,16 +386,18 @@ public:
bool IsTransformingByAPZ() const {
return mTransformingByAPZ;
}
void SetScrollableByAPZ(bool aScrollable);
void SetZoomableByAPZ(bool aZoomable);
bool UsesContainerScrolling() const;
+ ScrollSnapInfo GetScrollSnapInfo() const;
+
bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder,
nsRect* aDirtyRect,
bool aAllowCreateDisplayPort);
void NotifyApproximateFrameVisibilityUpdate();
bool GetDisplayPortAtLastApproximateFrameVisibilityUpdate(nsRect* aDisplayPort);
bool AllowDisplayPortExpiration();
void TriggerDisplayPortExpiration();