summaryrefslogtreecommitdiffstats
path: root/gfx/layers/FrameMetrics.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/FrameMetrics.h')
-rw-r--r--gfx/layers/FrameMetrics.h1114
1 files changed, 1114 insertions, 0 deletions
diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h
new file mode 100644
index 0000000000..fde4176f2a
--- /dev/null
+++ b/gfx/layers/FrameMetrics.h
@@ -0,0 +1,1114 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_FRAMEMETRICS_H
+#define GFX_FRAMEMETRICS_H
+
+#include <stdint.h> // for uint8_t, uint32_t, uint64_t
+#include <iosfwd>
+
+#include "Units.h" // for CSSRect, CSSPixel, etc
+#include "UnitTransforms.h" // for ViewAs
+#include "mozilla/DefineEnum.h" // for MOZ_DEFINE_ENUM
+#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/layers/LayersTypes.h" // for ScrollDirection
+#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid
+#include "mozilla/ScrollPositionUpdate.h" // for ScrollPositionUpdate
+#include "mozilla/ScrollSnapTargetId.h"
+#include "mozilla/StaticPtr.h" // for StaticAutoPtr
+#include "mozilla/TimeStamp.h" // for TimeStamp
+#include "nsTHashMap.h" // for nsTHashMap
+#include "nsString.h"
+#include "PLDHashTable.h" // for PLDHashNumber
+
+struct nsStyleDisplay;
+namespace mozilla {
+enum class StyleScrollSnapStop : uint8_t;
+enum class StyleScrollSnapStrictness : uint8_t;
+enum class StyleOverscrollBehavior : uint8_t;
+class WritingMode;
+} // namespace mozilla
+
+namespace IPC {
+template <typename T>
+struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Metrics about a scroll frame that are sent to the compositor and used
+ * by APZ.
+ *
+ * This is used for two main purposes:
+ *
+ * (1) Sending information about a scroll frame to the compositor and APZ
+ * as part of a layers or WebRender transaction.
+ * (2) Storing information about a scroll frame in APZ that persists
+ * between transactions.
+ *
+ * TODO: Separate these two uses into two distinct structures.
+ *
+ * A related class, RepaintRequest, is used for sending information about a
+ * scroll frame back from the compositor to the main thread when requesting
+ * a repaint of the scroll frame's contents.
+ */
+struct FrameMetrics {
+ friend struct IPC::ParamTraits<mozilla::layers::FrameMetrics>;
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const FrameMetrics& aMetrics);
+
+ typedef ScrollableLayerGuid::ViewID ViewID;
+
+ public:
+ // clang-format off
+ MOZ_DEFINE_ENUM_WITH_BASE_AT_CLASS_SCOPE(
+ ScrollOffsetUpdateType, uint8_t, (
+ eNone, // The default; the scroll offset was not updated
+ eMainThread, // The scroll offset was updated by the main thread.
+ eRestore // The scroll offset was updated by the main thread, but
+ // as a restore from history or after a frame
+ // reconstruction. In this case, APZ can ignore the
+ // offset change if the user has done an APZ scroll
+ // already.
+ ));
+ // clang-format on
+
+ FrameMetrics()
+ : mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID),
+ mPresShellResolution(1),
+ mCompositionBounds(0, 0, 0, 0),
+ mCompositionBoundsWidthIgnoringScrollbars(0),
+ mDisplayPort(0, 0, 0, 0),
+ mScrollableRect(0, 0, 0, 0),
+ mCumulativeResolution(),
+ mDevPixelsPerCSSPixel(1),
+ mScrollOffset(0, 0),
+ mZoom(),
+ mBoundingCompositionSize(0, 0),
+ mPresShellId(-1),
+ mLayoutViewport(0, 0, 0, 0),
+ mTransformToAncestorScale(),
+ mPaintRequestTime(),
+ mVisualDestination(0, 0),
+ mVisualScrollUpdateType(eNone),
+ mCompositionSizeWithoutDynamicToolbar(),
+ mIsRootContent(false),
+ mIsScrollInfoLayer(false),
+ mHasNonZeroDisplayPortMargins(false),
+ mMinimalDisplayPort(false) {}
+
+ // Default copy ctor and operator= are fine
+
+ bool operator==(const FrameMetrics& aOther) const {
+ // Put mScrollId at the top since it's the most likely one to fail.
+ return mScrollId == aOther.mScrollId &&
+ mPresShellResolution == aOther.mPresShellResolution &&
+ mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
+ mCompositionBoundsWidthIgnoringScrollbars ==
+ aOther.mCompositionBoundsWidthIgnoringScrollbars &&
+ mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
+ mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
+ mCumulativeResolution == aOther.mCumulativeResolution &&
+ mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
+ mScrollOffset == aOther.mScrollOffset &&
+ // don't compare mZoom
+ mScrollGeneration == aOther.mScrollGeneration &&
+ mBoundingCompositionSize == aOther.mBoundingCompositionSize &&
+ mPresShellId == aOther.mPresShellId &&
+ mLayoutViewport.IsEqualEdges(aOther.mLayoutViewport) &&
+ mTransformToAncestorScale == aOther.mTransformToAncestorScale &&
+ mPaintRequestTime == aOther.mPaintRequestTime &&
+ mVisualDestination == aOther.mVisualDestination &&
+ mVisualScrollUpdateType == aOther.mVisualScrollUpdateType &&
+ mIsRootContent == aOther.mIsRootContent &&
+ mIsScrollInfoLayer == aOther.mIsScrollInfoLayer &&
+ mHasNonZeroDisplayPortMargins ==
+ aOther.mHasNonZeroDisplayPortMargins &&
+ mMinimalDisplayPort == aOther.mMinimalDisplayPort &&
+ mFixedLayerMargins == aOther.mFixedLayerMargins &&
+ mCompositionSizeWithoutDynamicToolbar ==
+ aOther.mCompositionSizeWithoutDynamicToolbar;
+ }
+
+ bool operator!=(const FrameMetrics& aOther) const {
+ return !operator==(aOther);
+ }
+
+ bool IsScrollable() const {
+ return mScrollId != ScrollableLayerGuid::NULL_SCROLL_ID;
+ }
+
+ CSSToScreenScale2D DisplayportPixelsPerCSSPixel() const {
+ // Note: mZoom includes the async zoom. We want to include the async zoom
+ // even though the size of the pixels of our *current* displayport does not
+ // yet reflect it, because this function is used in the context of a repaint
+ // request where we'll be asking for a *new* displayport which does reflect
+ // the async zoom. Note 2: we include the transform to ancestor scale
+ // because this function (as the name implies) is used only in various
+ // displayport calculation related places, and those calculations want the
+ // transform to ancestor scale to be included becaese they want to reason
+ // about pixels which are the same size as screen pixels (so displayport
+ // sizes are e.g. limited to a multiple of the screen size). Whereas mZoom
+ // and mCumulativeResolution do not include it because of expectations of
+ // the code where they are used.
+ return mZoom * mTransformToAncestorScale;
+ }
+
+ CSSToLayerScale LayersPixelsPerCSSPixel() const {
+ return mDevPixelsPerCSSPixel * mCumulativeResolution;
+ }
+
+ // Get the amount by which this frame has been zoomed since the last repaint.
+ LayerToParentLayerScale GetAsyncZoom() const {
+ return mZoom / LayersPixelsPerCSSPixel();
+ }
+
+ // Ensure the scrollableRect is at least as big as the compositionBounds
+ // because the scrollableRect can be smaller if the content is not large
+ // and the scrollableRect hasn't been updated yet.
+ // We move the scrollableRect up because we don't know if we can move it
+ // down. i.e. we know that scrollableRect can go back as far as zero.
+ // but we don't know how much further ahead it can go.
+ CSSRect GetExpandedScrollableRect() const {
+ CSSRect scrollableRect = mScrollableRect;
+ CSSSize compSize = CalculateCompositedSizeInCssPixels();
+ if (scrollableRect.Width() < compSize.width) {
+ scrollableRect.SetRectX(
+ std::max(0.f, scrollableRect.X() -
+ (compSize.width - scrollableRect.Width())),
+ compSize.width);
+ }
+
+ if (scrollableRect.Height() < compSize.height) {
+ scrollableRect.SetRectY(
+ std::max(0.f, scrollableRect.Y() -
+ (compSize.height - scrollableRect.Height())),
+ compSize.height);
+ }
+
+ return scrollableRect;
+ }
+
+ CSSSize CalculateCompositedSizeInCssPixels() const {
+ return CalculateCompositedSizeInCssPixels(mCompositionBounds, mZoom);
+ }
+
+ /*
+ * Calculate the composition bounds of this frame in the CSS pixels of
+ * the content surrounding the scroll frame (OuterCSS pixels).
+ * Note that it does not make sense to ask for the composition bounds in the
+ * CSS pixels of the scrolled content (that is, regular CSS pixels),
+ * because the origin of the composition bounds is not meaningful in that
+ * coordinate space. (The size is, use CalculateCompositedSizeInCssPixels()
+ * for that.)
+ */
+ OuterCSSRect CalculateCompositionBoundsInOuterCssPixels() const {
+ if (GetZoom() == CSSToParentLayerScale(0)) {
+ return OuterCSSRect(); // avoid division by zero
+ }
+ // The CSS pixels of the scrolled content and the CSS pixels of the
+ // surrounding content only differ if the scrolled content is rendered
+ // at a higher resolution, and the difference is the resolution.
+ return mCompositionBounds / GetZoom() * GetCSSToOuterCSSScale();
+ }
+
+ CSSSize CalculateBoundedCompositedSizeInCssPixels() const {
+ CSSSize size = CalculateCompositedSizeInCssPixels();
+ size.width = std::min(size.width, mBoundingCompositionSize.width);
+ size.height = std::min(size.height, mBoundingCompositionSize.height);
+ return size;
+ }
+
+ CSSRect CalculateScrollRange() const {
+ return CalculateScrollRange(mScrollableRect, mCompositionBounds, mZoom);
+ }
+
+ void ScrollBy(const CSSPoint& aPoint) {
+ SetVisualScrollOffset(GetVisualScrollOffset() + aPoint);
+ }
+
+ void ZoomBy(float aScale) { mZoom.scale *= aScale; }
+
+ /*
+ * Compares an APZ frame metrics with an incoming content frame metrics
+ * to see if APZ has a scroll offset that has not been incorporated into
+ * the content frame metrics.
+ */
+ bool HasPendingScroll(const FrameMetrics& aContentFrameMetrics) const {
+ return GetVisualScrollOffset() !=
+ aContentFrameMetrics.GetVisualScrollOffset();
+ }
+
+ /*
+ * Returns true if the layout scroll offset or visual scroll offset changed.
+ */
+ bool ApplyScrollUpdateFrom(const ScrollPositionUpdate& aUpdate);
+
+ /**
+ * Applies the relative scroll offset update contained in aOther to the
+ * scroll offset contained in this. The scroll delta is clamped to the
+ * scrollable region.
+ *
+ * @returns The clamped scroll offset delta that was applied
+ */
+ CSSPoint ApplyRelativeScrollUpdateFrom(const ScrollPositionUpdate& aUpdate);
+
+ CSSPoint ApplyPureRelativeScrollUpdateFrom(
+ const ScrollPositionUpdate& aUpdate);
+
+ void UpdatePendingScrollInfo(const ScrollPositionUpdate& aInfo);
+
+ public:
+ void SetPresShellResolution(float aPresShellResolution) {
+ mPresShellResolution = aPresShellResolution;
+ }
+
+ float GetPresShellResolution() const { return mPresShellResolution; }
+
+ void SetCompositionBounds(const ParentLayerRect& aCompositionBounds) {
+ mCompositionBounds = aCompositionBounds;
+ }
+
+ const ParentLayerRect& GetCompositionBounds() const {
+ return mCompositionBounds;
+ }
+
+ void SetCompositionBoundsWidthIgnoringScrollbars(
+ const ParentLayerCoord aCompositionBoundsWidthIgnoringScrollbars) {
+ mCompositionBoundsWidthIgnoringScrollbars =
+ aCompositionBoundsWidthIgnoringScrollbars;
+ }
+
+ const ParentLayerCoord GetCompositionBoundsWidthIgnoringScrollbars() const {
+ return mCompositionBoundsWidthIgnoringScrollbars;
+ }
+
+ void SetDisplayPort(const CSSRect& aDisplayPort) {
+ mDisplayPort = aDisplayPort;
+ }
+
+ const CSSRect& GetDisplayPort() const { return mDisplayPort; }
+
+ void SetCumulativeResolution(
+ const LayoutDeviceToLayerScale& aCumulativeResolution) {
+ mCumulativeResolution = aCumulativeResolution;
+ }
+
+ const LayoutDeviceToLayerScale& GetCumulativeResolution() const {
+ return mCumulativeResolution;
+ }
+
+ void SetDevPixelsPerCSSPixel(
+ const CSSToLayoutDeviceScale& aDevPixelsPerCSSPixel) {
+ mDevPixelsPerCSSPixel = aDevPixelsPerCSSPixel;
+ }
+
+ const CSSToLayoutDeviceScale& GetDevPixelsPerCSSPixel() const {
+ return mDevPixelsPerCSSPixel;
+ }
+
+ CSSToOuterCSSScale GetCSSToOuterCSSScale() const {
+ // The scale difference between CSS and OuterCSS pixels is the
+ // part of the zoom that's not subject to all enclosing content,
+ // i.e. the pres shell resolution.
+ return CSSToOuterCSSScale(mPresShellResolution);
+ }
+
+ void SetIsRootContent(bool aIsRootContent) {
+ mIsRootContent = aIsRootContent;
+ }
+
+ bool IsRootContent() const { return mIsRootContent; }
+
+ // Set scroll offset, first clamping to the scroll range.
+ // Return true if it changed.
+ bool ClampAndSetVisualScrollOffset(const CSSPoint& aScrollOffset) {
+ CSSPoint offsetBefore = GetVisualScrollOffset();
+ SetVisualScrollOffset(CalculateScrollRange().ClampPoint(aScrollOffset));
+ return (offsetBefore != GetVisualScrollOffset());
+ }
+
+ CSSPoint GetLayoutScrollOffset() const { return mLayoutViewport.TopLeft(); }
+ // Returns true if it changed.
+ bool SetLayoutScrollOffset(const CSSPoint& aLayoutScrollOffset) {
+ CSSPoint offsetBefore = GetLayoutScrollOffset();
+ mLayoutViewport.MoveTo(aLayoutScrollOffset);
+ return (offsetBefore != GetLayoutScrollOffset());
+ }
+
+ const CSSPoint& GetVisualScrollOffset() const { return mScrollOffset; }
+ void SetVisualScrollOffset(const CSSPoint& aVisualScrollOffset) {
+ mScrollOffset = aVisualScrollOffset;
+ }
+
+ void SetZoom(const CSSToParentLayerScale& aZoom) { mZoom = aZoom; }
+
+ const CSSToParentLayerScale& GetZoom() const { return mZoom; }
+
+ void SetScrollGeneration(
+ const MainThreadScrollGeneration& aScrollGeneration) {
+ mScrollGeneration = aScrollGeneration;
+ }
+
+ MainThreadScrollGeneration GetScrollGeneration() const {
+ return mScrollGeneration;
+ }
+
+ ViewID GetScrollId() const { return mScrollId; }
+
+ void SetScrollId(ViewID scrollId) { mScrollId = scrollId; }
+
+ void SetBoundingCompositionSize(const CSSSize& aBoundingCompositionSize) {
+ mBoundingCompositionSize = aBoundingCompositionSize;
+ }
+
+ const CSSSize& GetBoundingCompositionSize() const {
+ return mBoundingCompositionSize;
+ }
+
+ uint32_t GetPresShellId() const { return mPresShellId; }
+
+ void SetPresShellId(uint32_t aPresShellId) { mPresShellId = aPresShellId; }
+
+ void SetLayoutViewport(const CSSRect& aLayoutViewport) {
+ mLayoutViewport = aLayoutViewport;
+ }
+
+ const CSSRect& GetLayoutViewport() const { return mLayoutViewport; }
+
+ CSSRect GetVisualViewport() const {
+ return CSSRect(GetVisualScrollOffset(),
+ CalculateCompositedSizeInCssPixels());
+ }
+
+ void SetTransformToAncestorScale(
+ const ParentLayerToScreenScale2D& aTransformToAncestorScale) {
+ mTransformToAncestorScale = aTransformToAncestorScale;
+ }
+
+ const ParentLayerToScreenScale2D& GetTransformToAncestorScale() const {
+ return mTransformToAncestorScale;
+ }
+
+ const CSSRect& GetScrollableRect() const { return mScrollableRect; }
+
+ void SetScrollableRect(const CSSRect& aScrollableRect) {
+ mScrollableRect = aScrollableRect;
+ }
+
+ // If the frame is in vertical-RTL writing mode(E.g. "writing-mode:
+ // vertical-rl" in CSS), or if it's in horizontal-RTL writing-mode(E.g.
+ // "writing-mode: horizontal-tb; direction: rtl;" in CSS), then this function
+ // returns true. From the representation perspective, frames whose horizontal
+ // contents start at rightside also cause their horizontal scrollbars, if any,
+ // initially start at rightside. So we can also learn about the initial side
+ // of the horizontal scrollbar for the frame by calling this function.
+ bool IsHorizontalContentRightToLeft() const { return mScrollableRect.x < 0; }
+
+ void SetPaintRequestTime(const TimeStamp& aTime) {
+ mPaintRequestTime = aTime;
+ }
+ const TimeStamp& GetPaintRequestTime() const { return mPaintRequestTime; }
+
+ void SetIsScrollInfoLayer(bool aIsScrollInfoLayer) {
+ mIsScrollInfoLayer = aIsScrollInfoLayer;
+ }
+ bool IsScrollInfoLayer() const { return mIsScrollInfoLayer; }
+
+ void SetHasNonZeroDisplayPortMargins(bool aHasNonZeroDisplayPortMargins) {
+ mHasNonZeroDisplayPortMargins = aHasNonZeroDisplayPortMargins;
+ }
+ bool HasNonZeroDisplayPortMargins() const {
+ return mHasNonZeroDisplayPortMargins;
+ }
+
+ void SetMinimalDisplayPort(bool aMinimalDisplayPort) {
+ mMinimalDisplayPort = aMinimalDisplayPort;
+ }
+ bool IsMinimalDisplayPort() const { return mMinimalDisplayPort; }
+
+ void SetVisualDestination(const CSSPoint& aVisualDestination) {
+ mVisualDestination = aVisualDestination;
+ }
+ const CSSPoint& GetVisualDestination() const { return mVisualDestination; }
+
+ void SetVisualScrollUpdateType(ScrollOffsetUpdateType aUpdateType) {
+ mVisualScrollUpdateType = aUpdateType;
+ }
+ ScrollOffsetUpdateType GetVisualScrollUpdateType() const {
+ return mVisualScrollUpdateType;
+ }
+
+ // Determine if the visual viewport is outside of the layout viewport and
+ // adjust the x,y-offset in mLayoutViewport accordingly. This is necessary to
+ // allow APZ to async-scroll the layout viewport.
+ //
+ // This is a no-op if mIsRootContent is false.
+ void RecalculateLayoutViewportOffset();
+
+ void SetFixedLayerMargins(const ScreenMargin& aFixedLayerMargins) {
+ mFixedLayerMargins = aFixedLayerMargins;
+ }
+ const ScreenMargin& GetFixedLayerMargins() const {
+ return mFixedLayerMargins;
+ }
+
+ void SetCompositionSizeWithoutDynamicToolbar(const ParentLayerSize& aSize) {
+ MOZ_ASSERT(mIsRootContent);
+ mCompositionSizeWithoutDynamicToolbar = aSize;
+ }
+ const ParentLayerSize& GetCompositionSizeWithoutDynamicToolbar() const {
+ MOZ_ASSERT(mIsRootContent);
+ return mCompositionSizeWithoutDynamicToolbar;
+ }
+
+ // Helper function for RecalculateViewportOffset(). Exposed so that
+ // APZC can perform the operation on other copies of the layout
+ // and visual viewport rects (e.g. the "effective" ones used to implement
+ // the frame delay).
+ // Modifies |aLayoutViewport| to continue enclosing |aVisualViewport|
+ // if possible.
+ // The layout viewport needs to remain clamped to the scrollable rect,
+ // and we pass in the scrollable rect so this function can maintain that
+ // constraint.
+ static void KeepLayoutViewportEnclosingVisualViewport(
+ const CSSRect& aVisualViewport, const CSSRect& aScrollableRect,
+ CSSRect& aLayoutViewport);
+
+ // Helper functions exposed so we can perform operations on copies outside of
+ // frame metrics object.
+ static CSSRect CalculateScrollRange(const CSSRect& aScrollableRect,
+ const ParentLayerRect& aCompositionBounds,
+ const CSSToParentLayerScale& aZoom);
+ static CSSSize CalculateCompositedSizeInCssPixels(
+ const ParentLayerRect& aCompositionBounds,
+ const CSSToParentLayerScale& aZoom);
+
+ private:
+ // A ID assigned to each scrollable frame, unique within each LayersId..
+ 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
+ // user action, or choosing an initial zoom level on page load). This can
+ // only be different from 1.0 for frames that are zoomable, which currently
+ // is just the root content document's root scroll frame
+ // (mIsRootContent = true).
+ // This is a plain float rather than a ScaleFactor because in and of itself
+ // it does not convert between any coordinate spaces for which we have names.
+ float mPresShellResolution;
+
+ // This is the area within the widget that we're compositing to. It is in the
+ // layer coordinates of the scrollable content's parent layer.
+ //
+ // The size of the composition bounds corresponds to the size of the scroll
+ // frame's scroll port (but in a coordinate system where the size does not
+ // change during zooming).
+ //
+ // The origin of the composition bounds is relative to the scroll node origin.
+ // (The "scroll node origin" is the point such that applying the APZC's
+ // apzc-to-screen transform to it takes you to the window origin, which is
+ // what Screen event coordinates are relative to. In layout terms, it's
+ // the origin of the reference frame passed to ComputeScrollMetadata().)
+ // Unlike the scroll port's origin, it does not change during scrolling of
+ // the scrollable layer to which it is associated. However, it may change due
+ // to scrolling of ancestor layers.
+ //
+ // This value is provided by Gecko at layout/paint time.
+ ParentLayerRect mCompositionBounds;
+
+ // For RCD-RSF this is the width of the composition bounds ignoring
+ // scrollbars. For everything else this will be the same as the width of the
+ // composition bounds. Only needed for the "resolution changed" check in
+ // NotifyLayersUpdated, once that switches to using IsResolutionUpdated we can
+ // remove this.
+ ParentLayerCoord mCompositionBoundsWidthIgnoringScrollbars;
+
+ // The area of a scroll frame's contents that has been painted, relative to
+ // GetLayoutScrollOffset().
+ //
+ // Should not be larger than GetExpandedScrollableRect().
+ //
+ // To pre-render a margin of 100 CSS pixels around the scroll port,
+ // { x = -100, y = - 100,
+ // width = scrollPort.width + 200, height = scrollPort.height + 200 }
+ // where scrollPort = CalculateCompositedSizeInCssPixels().
+ CSSRect mDisplayPort;
+
+ // The scrollable bounds of a frame. This is determined by reflow.
+ // Ordinarily the x and y will be 0 and the width and height will be the
+ // size of the element being scrolled. However for RTL pages or elements
+ // the x value may be negative.
+ //
+ // For scrollable frames that are overflow:hidden the x and y are usually
+ // set to the value of the current scroll offset, and the width and height
+ // will match the composition bounds width and height. In effect this reduces
+ // the scrollable range to 0.
+ //
+ // This is in the same coordinate space as |mScrollOffset|, but a different
+ // coordinate space than |mDisplayPort|. Note also that this coordinate
+ // system is understood by window.scrollTo().
+ CSSRect mScrollableRect;
+
+ // The cumulative resolution of the current frame. This is the product of the
+ // pres-shell resolutions of the document containing this scroll frame and its
+ // in-process ancestors. This information is provided by Gecko at layout/paint
+ // time. Notably, for out of process iframes cumulative resolution will be 1.
+ // The reason for this is that AsyncPanZoomController::GetTransformToThis does
+ // not contain the resolution in the process of the root content document, but
+ // in oop iframes AsyncPanZoomController::GetTransformToThis does contain the
+ // resolution. This makes coordinate math work out in APZ code because in the
+ // old layers backend GetTransformToThis was a transform of rendered pixels,
+ // and the pixels were rendered with the scale applied already. The reason
+ // that AsyncPanZoomController::GetTransformToThis contains the scale in oop
+ // iframes is because we include the resolution in the transform that includes
+ // the iframe via this call
+ // https://searchfox.org/mozilla-central/rev/2eebd6e256fa0355e08421265e57ee1307836d92/layout/generic/nsSubDocumentFrame.cpp#1404
+ // So when coordinates are passed to the process of the oop iframe they have
+ // the resolution removed by unapplying that transform which includes the
+ // resolution.
+ LayoutDeviceToLayerScale mCumulativeResolution;
+
+ // 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.
+ CSSToLayoutDeviceScale mDevPixelsPerCSSPixel;
+
+ // The position of the top-left of the scroll frame's scroll port, relative
+ // to the scrollable content's origin.
+ //
+ // This is in the same coordinate space as |mScrollableRect|, but a different
+ // coordinate space than |mDisplayPort|.
+ //
+ // It is required that the rect:
+ // { x = mScrollOffset.x, y = mScrollOffset.y,
+ // width = scrollPort.width,
+ // height = scrollPort.height }
+ // (where scrollPort = CalculateCompositedSizeInCssPixels())
+ // be within |mScrollableRect|.
+ CSSPoint mScrollOffset;
+
+ // The "user zoom". Content is painted by gecko at mCumulativeResolution *
+ // mDevPixelsPerCSSPixel, but will be drawn to the screen at mZoom. In the
+ // steady state, the two will be the same, but during an async zoom action the
+ // two may diverge. This information is initialized in Gecko but updated in
+ // the APZC.
+ CSSToParentLayerScale mZoom;
+
+ // The scroll generation counter used to acknowledge the scroll offset update.
+ MainThreadScrollGeneration mScrollGeneration;
+
+ // A bounding size for our composition bounds (no larger than the
+ // cross-process RCD-RSF's composition size), in local CSS pixels.
+ CSSSize mBoundingCompositionSize;
+
+ uint32_t mPresShellId;
+
+ // For a root scroll frame (RSF), the document's layout viewport
+ // (sometimes called "CSS viewport" in older code).
+ //
+ // Its size is the dimensions we're using to constrain the <html> element
+ // of the document (i.e. the initial containing block (ICB) size).
+ //
+ // Its origin is the RSF's layout scroll position, i.e. the scroll position
+ // exposed to web content via window.scrollX/Y.
+ //
+ // Note that only the root content document's RSF has a layout viewport
+ // that's distinct from the visual viewport. For an iframe RSF, the two
+ // are the same.
+ //
+ // For a scroll frame that is not an RSF, this metric is meaningless and
+ // invalid.
+ CSSRect mLayoutViewport;
+
+ // The scale induced by css transforms and presshell resolution in this
+ // process and any ancestor processes that encloses this scroll frame that is
+ // _not_ included in mCumulativeResolution. This means that in the process of
+ // the root content document this only includes css transform scale (which
+ // happens in that process, but we assume there can be no css transform scale
+ // above the root content document). In other processes it includes css
+ // transform scale and any resolution scale in the current process and all
+ // ancestor processes.
+ ParentLayerToScreenScale2D mTransformToAncestorScale;
+
+ // The time at which the APZC last requested a repaint for this scroll frame.
+ TimeStamp mPaintRequestTime;
+
+ // These fields are used when the main thread wants to set a visual viewport
+ // offset that's distinct from the layout viewport offset.
+ // In this case, mVisualScrollUpdateType is set to eMainThread, and
+ // mVisualDestination is set to desired visual destination (relative
+ // to the document, like mScrollOffset).
+ CSSPoint mVisualDestination;
+ ScrollOffsetUpdateType mVisualScrollUpdateType;
+
+ // 'fixed layer margins' on the main-thread. This is only used for the
+ // root-content scroll frame.
+ ScreenMargin mFixedLayerMargins;
+
+ // Similar to mCompositionBounds.Size() but not including the dynamic toolbar
+ // height.
+ // If we are not using a dynamic toolbar, this has the same value as
+ // mCompositionBounds.Size().
+ ParentLayerSize mCompositionSizeWithoutDynamicToolbar;
+
+ // Whether or not this is the root scroll frame for the root content document.
+ bool mIsRootContent : 1;
+
+ // True if this scroll frame is a scroll info layer. A scroll info layer is
+ // not layerized and its content cannot be truly async-scrolled, but its
+ // metrics are still sent to and updated by the compositor, with the updates
+ // being reflected on the next paint rather than the next composite.
+ bool mIsScrollInfoLayer : 1;
+
+ // Whether there are non-zero display port margins set on this element.
+ bool mHasNonZeroDisplayPortMargins : 1;
+
+ // Whether this scroll frame is using a minimal display port, which means that
+ // any set display port margins are ignored when calculating the display port
+ // and instead zero margins are used and further no tile or alignment
+ // boundaries are used that could potentially expand the size.
+ bool mMinimalDisplayPort : 1;
+
+ // WARNING!!!!
+ //
+ // When adding a new field:
+ //
+ // - First, consider whether the field can be added to ScrollMetadata
+ // instead. If so, prefer that.
+ //
+ // - Otherwise, the following places should be updated to include them
+ // (as needed):
+ // FrameMetrics::operator ==
+ // AsyncPanZoomController::NotifyLayersUpdated
+ // The ParamTraits specialization in LayersMessageUtils.h
+ //
+ // Please add new fields above this comment.
+};
+
+struct ScrollSnapInfo {
+ ScrollSnapInfo();
+
+ bool operator==(const ScrollSnapInfo& aOther) const {
+ return mScrollSnapStrictnessX == aOther.mScrollSnapStrictnessX &&
+ mScrollSnapStrictnessY == aOther.mScrollSnapStrictnessY &&
+ mSnapTargets == aOther.mSnapTargets &&
+ mXRangeWiderThanSnapport == aOther.mXRangeWiderThanSnapport &&
+ mYRangeWiderThanSnapport == aOther.mYRangeWiderThanSnapport &&
+ mSnapportSize == aOther.mSnapportSize;
+ }
+
+ bool HasScrollSnapping() const;
+ bool HasSnapPositions() const;
+
+ void InitializeScrollSnapStrictness(WritingMode aWritingMode,
+ const nsStyleDisplay* aDisplay);
+
+ // The scroll frame's scroll-snap-type.
+ StyleScrollSnapStrictness mScrollSnapStrictnessX;
+ StyleScrollSnapStrictness mScrollSnapStrictnessY;
+
+ struct SnapTarget {
+ // The scroll positions corresponding to scroll-snap-align values.
+ Maybe<nscoord> mSnapPositionX;
+ Maybe<nscoord> mSnapPositionY;
+
+ // https://drafts.csswg.org/css-scroll-snap/#scroll-snap-area
+ nsRect mSnapArea;
+
+ // https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-stop
+ StyleScrollSnapStop mScrollSnapStop;
+
+ // Use for tracking the last snapped target.
+ ScrollSnapTargetId mTargetId;
+
+ SnapTarget() = default;
+
+ SnapTarget(Maybe<nscoord>&& aSnapPositionX, Maybe<nscoord>&& aSnapPositionY,
+ nsRect&& aSnapArea, StyleScrollSnapStop aScrollSnapStop,
+ ScrollSnapTargetId aTargetId)
+ : mSnapPositionX(std::move(aSnapPositionX)),
+ mSnapPositionY(std::move(aSnapPositionY)),
+ mSnapArea(std::move(aSnapArea)),
+ mScrollSnapStop(aScrollSnapStop),
+ mTargetId(aTargetId) {}
+
+ bool operator==(const SnapTarget& aOther) const {
+ return mSnapPositionX == aOther.mSnapPositionX &&
+ mSnapPositionY == aOther.mSnapPositionY &&
+ mSnapArea == aOther.mSnapArea &&
+ mScrollSnapStop == aOther.mScrollSnapStop &&
+ mTargetId == aOther.mTargetId;
+ }
+ bool IsValidFor(const nsPoint& aDestination,
+ const nsSize aSnapportSize) const {
+ nsPoint snapPoint(mSnapPositionX ? *mSnapPositionX : aDestination.x,
+ mSnapPositionY ? *mSnapPositionY : aDestination.y);
+ nsRect snappedPort = nsRect(snapPoint, aSnapportSize);
+ // Ignore snap points if snapping to the point would leave the snap area
+ // outside of the snapport.
+ // https://drafts.csswg.org/css-scroll-snap-1/#snap-scope
+ return snappedPort.Intersects(mSnapArea);
+ }
+ };
+
+ CopyableTArray<SnapTarget> mSnapTargets;
+
+ struct ScrollSnapRange {
+ ScrollSnapRange() = default;
+
+ ScrollSnapRange(nscoord aStart, nscoord aEnd, ScrollSnapTargetId aTargetId)
+ : mStart(aStart), mEnd(aEnd), mTargetId(aTargetId) {}
+
+ nscoord mStart;
+ nscoord mEnd;
+ ScrollSnapTargetId mTargetId;
+
+ bool operator==(const ScrollSnapRange& aOther) const {
+ return mStart == aOther.mStart && mEnd == aOther.mEnd &&
+ mTargetId == aOther.mTargetId;
+ }
+
+ // Returns true if |aPoint| is a valid snap position in this range.
+ bool IsValid(nscoord aPoint, nscoord aSnapportSize) const {
+ MOZ_ASSERT(mEnd - mStart > aSnapportSize);
+ return mStart <= aPoint && aPoint <= mEnd - aSnapportSize;
+ }
+ };
+ // An array of the range that the target element is larger than the snapport
+ // on the axis.
+ // Snap positions in this range will be valid snap positions in the case where
+ // the distance between the closest snap position and the second closest snap
+ // position is still larger than the snapport size.
+ // See https://drafts.csswg.org/css-scroll-snap-1/#snap-overflow
+ //
+ // Note: This range contains scroll-margin values.
+ CopyableTArray<ScrollSnapRange> mXRangeWiderThanSnapport;
+ CopyableTArray<ScrollSnapRange> mYRangeWiderThanSnapport;
+
+ // Note: This snapport size has been already deflated by scroll-padding.
+ nsSize mSnapportSize;
+};
+
+// clang-format off
+MOZ_DEFINE_ENUM_CLASS_WITH_BASE(
+ OverscrollBehavior, uint8_t, (
+ Auto,
+ Contain,
+ None
+));
+// clang-format on
+
+std::ostream& operator<<(std::ostream& aStream,
+ const OverscrollBehavior& aBehavior);
+
+struct OverscrollBehaviorInfo {
+ OverscrollBehaviorInfo();
+
+ // Construct from StyleOverscrollBehavior values.
+ static OverscrollBehaviorInfo FromStyleConstants(
+ StyleOverscrollBehavior aBehaviorX, StyleOverscrollBehavior aBehaviorY);
+
+ bool operator==(const OverscrollBehaviorInfo& aOther) const;
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const OverscrollBehaviorInfo& aInfo);
+
+ OverscrollBehavior mBehaviorX;
+ OverscrollBehavior mBehaviorY;
+};
+
+/**
+ * Metadata about a scroll frame that's sent to the compositor during a layers
+ * or WebRender transaction, and also stored by APZ between transactions.
+ * 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. stored in memory shared
+ * with the content process).
+ */
+struct ScrollMetadata {
+ friend struct IPC::ParamTraits<mozilla::layers::ScrollMetadata>;
+ friend std::ostream& operator<<(std::ostream& aStream,
+ const ScrollMetadata& aMetadata);
+
+ typedef ScrollableLayerGuid::ViewID ViewID;
+
+ public:
+ static StaticAutoPtr<const ScrollMetadata>
+ sNullMetadata; // We sometimes need an empty metadata
+
+ ScrollMetadata()
+ : mMetrics(),
+ mSnapInfo(),
+ mScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
+ mContentDescription(),
+ mLineScrollAmount(0, 0),
+ mPageScrollAmount(0, 0),
+ mHasScrollgrab(false),
+ mIsLayersIdRoot(false),
+ mIsAutoDirRootContentRTL(false),
+ mForceDisableApz(false),
+ mResolutionUpdated(false),
+ mIsRDMTouchSimulationActive(false),
+ mDidContentGetPainted(true),
+ mForceMousewheelAutodir(false),
+ mForceMousewheelAutodirHonourRoot(false),
+ mIsPaginatedPresentation(false),
+ mOverscrollBehavior() {}
+
+ bool operator==(const ScrollMetadata& aOther) const {
+ return mMetrics == aOther.mMetrics && mSnapInfo == aOther.mSnapInfo &&
+ mScrollParentId == aOther.mScrollParentId &&
+ // don't compare mContentDescription
+ mLineScrollAmount == aOther.mLineScrollAmount &&
+ mPageScrollAmount == aOther.mPageScrollAmount &&
+ mHasScrollgrab == aOther.mHasScrollgrab &&
+ mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
+ mIsAutoDirRootContentRTL == aOther.mIsAutoDirRootContentRTL &&
+ mForceDisableApz == aOther.mForceDisableApz &&
+ mResolutionUpdated == aOther.mResolutionUpdated &&
+ mIsRDMTouchSimulationActive == aOther.mIsRDMTouchSimulationActive &&
+ mDidContentGetPainted == aOther.mDidContentGetPainted &&
+ mForceMousewheelAutodir == aOther.mForceMousewheelAutodir &&
+ mForceMousewheelAutodirHonourRoot ==
+ aOther.mForceMousewheelAutodirHonourRoot &&
+ mIsPaginatedPresentation == aOther.mIsPaginatedPresentation &&
+ mDisregardedDirection == aOther.mDisregardedDirection &&
+ mOverscrollBehavior == aOther.mOverscrollBehavior &&
+ mScrollUpdates == aOther.mScrollUpdates;
+ }
+
+ bool operator!=(const ScrollMetadata& aOther) const {
+ return !operator==(aOther);
+ }
+
+ bool IsDefault() const {
+ ScrollMetadata def;
+
+ def.mMetrics.SetPresShellId(mMetrics.GetPresShellId());
+ return (def == *this);
+ }
+
+ FrameMetrics& GetMetrics() { return mMetrics; }
+ const FrameMetrics& GetMetrics() const { return mMetrics; }
+
+ void SetSnapInfo(ScrollSnapInfo&& aSnapInfo) {
+ mSnapInfo = std::move(aSnapInfo);
+ }
+ const ScrollSnapInfo& GetSnapInfo() const { return mSnapInfo; }
+
+ ViewID GetScrollParentId() const { return mScrollParentId; }
+
+ void SetScrollParentId(ViewID aParentId) { mScrollParentId = aParentId; }
+ const nsCString& GetContentDescription() const { return mContentDescription; }
+ void SetContentDescription(const nsCString& aContentDescription) {
+ mContentDescription = aContentDescription;
+ }
+ const LayoutDeviceIntSize& GetLineScrollAmount() const {
+ return mLineScrollAmount;
+ }
+ void SetLineScrollAmount(const LayoutDeviceIntSize& size) {
+ mLineScrollAmount = size;
+ }
+ const LayoutDeviceIntSize& GetPageScrollAmount() const {
+ return mPageScrollAmount;
+ }
+ void SetPageScrollAmount(const LayoutDeviceIntSize& size) {
+ mPageScrollAmount = size;
+ }
+ void SetHasScrollgrab(bool aHasScrollgrab) {
+ mHasScrollgrab = aHasScrollgrab;
+ }
+ bool GetHasScrollgrab() const { return mHasScrollgrab; }
+ void SetIsLayersIdRoot(bool aValue) { mIsLayersIdRoot = aValue; }
+ bool IsLayersIdRoot() const { return mIsLayersIdRoot; }
+ void SetIsAutoDirRootContentRTL(bool aValue) {
+ mIsAutoDirRootContentRTL = aValue;
+ }
+ bool IsAutoDirRootContentRTL() const { return mIsAutoDirRootContentRTL; }
+ void SetForceDisableApz(bool aForceDisable) {
+ mForceDisableApz = aForceDisable;
+ }
+ bool IsApzForceDisabled() const { return mForceDisableApz; }
+ void SetResolutionUpdated(bool aUpdated) { mResolutionUpdated = aUpdated; }
+ bool IsResolutionUpdated() const { return mResolutionUpdated; }
+
+ void SetIsRDMTouchSimulationActive(bool aValue) {
+ mIsRDMTouchSimulationActive = aValue;
+ }
+ bool GetIsRDMTouchSimulationActive() const {
+ return mIsRDMTouchSimulationActive;
+ }
+
+ void SetForceMousewheelAutodir(bool aValue) {
+ mForceMousewheelAutodir = aValue;
+ }
+ bool ForceMousewheelAutodir() const { return mForceMousewheelAutodir; }
+
+ void SetForceMousewheelAutodirHonourRoot(bool aValue) {
+ mForceMousewheelAutodirHonourRoot = aValue;
+ }
+ bool ForceMousewheelAutodirHonourRoot() const {
+ return mForceMousewheelAutodirHonourRoot;
+ }
+
+ void SetIsPaginatedPresentation(bool aValue) {
+ mIsPaginatedPresentation = aValue;
+ }
+ bool IsPaginatedPresentation() const { return mIsPaginatedPresentation; }
+
+ bool DidContentGetPainted() const { return mDidContentGetPainted; }
+
+ private:
+ // For use in IPC only
+ void SetDidContentGetPainted(bool aValue) { mDidContentGetPainted = aValue; }
+
+ public:
+ // For more details about the concept of a disregarded direction, refer to the
+ // code which defines mDisregardedDirection.
+ Maybe<ScrollDirection> GetDisregardedDirection() const {
+ return mDisregardedDirection;
+ }
+ void SetDisregardedDirection(const Maybe<ScrollDirection>& aValue) {
+ mDisregardedDirection = aValue;
+ }
+
+ void SetOverscrollBehavior(
+ const OverscrollBehaviorInfo& aOverscrollBehavior) {
+ mOverscrollBehavior = aOverscrollBehavior;
+ }
+ const OverscrollBehaviorInfo& GetOverscrollBehavior() const {
+ return mOverscrollBehavior;
+ }
+
+ void SetScrollUpdates(const nsTArray<ScrollPositionUpdate>& aUpdates) {
+ mScrollUpdates = aUpdates;
+ }
+
+ const nsTArray<ScrollPositionUpdate>& GetScrollUpdates() const {
+ return mScrollUpdates;
+ }
+
+ void UpdatePendingScrollInfo(nsTArray<ScrollPositionUpdate>&& aUpdates) {
+ MOZ_ASSERT(!aUpdates.IsEmpty());
+ mMetrics.UpdatePendingScrollInfo(aUpdates.LastElement());
+
+ mDidContentGetPainted = false;
+ mScrollUpdates.Clear();
+ mScrollUpdates.AppendElements(std::move(aUpdates));
+ }
+
+ private:
+ FrameMetrics mMetrics;
+
+ // Information used to determine where to snap to for a given scroll.
+ ScrollSnapInfo mSnapInfo;
+
+ // The ViewID of the scrollable frame to which overscroll should be handed
+ // off.
+ ViewID mScrollParentId;
+
+ // A description of the content element corresponding to this frame.
+ // This is empty unless this is a scrollable layer and the
+ // apz.printtree pref is turned on.
+ nsCString mContentDescription;
+
+ // The value of GetLineScrollAmount(), for scroll frames.
+ LayoutDeviceIntSize mLineScrollAmount;
+
+ // The value of GetPageScrollAmount(), for scroll frames.
+ LayoutDeviceIntSize mPageScrollAmount;
+
+ // Whether or not this frame is for an element marked 'scrollgrab'.
+ bool mHasScrollgrab : 1;
+
+ // Whether these framemetrics are for the root scroll frame (root element if
+ // we don't have a root scroll frame) for its layers id.
+ bool mIsLayersIdRoot : 1;
+
+ // The AutoDirRootContent is the <body> element in an HTML document, or the
+ // root scrollframe if there is no body. This member variable indicates
+ // whether this element's content in the horizontal direction starts from
+ // right to left (e.g. it's true either if "writing-mode: vertical-rl", or
+ // "writing-mode: horizontal-tb; direction: rtl" in CSS).
+ // When we do auto-dir scrolling (@see mozilla::WheelDeltaAdjustmentStrategy
+ // or refer to bug 1358017 for details), setting a pref can make the code use
+ // the writing mode of this root element instead of the target scrollframe,
+ // and so we need to know if the writing mode is RTL or not.
+ bool mIsAutoDirRootContentRTL : 1;
+
+ // Whether or not the compositor should actually do APZ-scrolling on this
+ // scrollframe.
+ bool mForceDisableApz : 1;
+
+ // Whether the pres shell resolution stored in mMetrics reflects a change
+ // originated by the main thread.
+ bool mResolutionUpdated : 1;
+
+ // Whether or not RDM and touch simulation are active for this document.
+ // It's important to note that if RDM is active then this field will be
+ // true for the content document but NOT the chrome document containing
+ // the browser UI and RDM controls.
+ bool mIsRDMTouchSimulationActive : 1;
+
+ // Whether this metadata is part of a transaction that also repainted the
+ // content (i.e. updated the displaylist or textures). This gets set to false
+ // for "paint-skip" transactions, where the main thread doesn't repaint but
+ // instead requests APZ to update the compositor scroll offset instead. APZ
+ // needs to be able to distinguish these paint-skip transactions so that it
+ // can use the correct transforms.
+ bool mDidContentGetPainted : 1;
+
+ // Whether privileged code has requested that autodir behaviour be
+ // enabled for the scroll frame.
+ bool mForceMousewheelAutodir : 1;
+ bool mForceMousewheelAutodirHonourRoot : 1;
+
+ // Whether this content is being displayed in a paginated fashion
+ // such as printing or print preview. In such cases, content that
+ // would normally only generate one display item may generated one
+ // display item per page, and the different instances may be subject
+ // to different transforms, which constrains the assumptions APZ can make.
+ bool mIsPaginatedPresentation : 1;
+
+ // The disregarded direction means the direction which is disregarded anyway,
+ // even if the scroll frame overflows in that direction and the direction is
+ // specified as scrollable. This could happen in some scenarios, for instance,
+ // a single-line text control frame should disregard wheel scroll in
+ // its block-flow direction even if it overflows in that direction.
+ Maybe<ScrollDirection> mDisregardedDirection;
+
+ // The overscroll behavior for this scroll frame.
+ OverscrollBehaviorInfo mOverscrollBehavior;
+
+ // The ordered list of scroll position updates for this scroll frame since
+ // the last transaction.
+ CopyableTArray<ScrollPositionUpdate> mScrollUpdates;
+
+ // WARNING!!!!
+ //
+ // When adding new fields to ScrollMetadata, the following places should be
+ // updated to include them (as needed):
+ // 1. ScrollMetadata::operator ==
+ // 2. AsyncPanZoomController::NotifyLayersUpdated
+ // 3. The ParamTraits specialization in LayersMessageUtils.h
+ //
+ // Please add new fields above this comment.
+};
+
+typedef nsTHashMap<ScrollableLayerGuid::ViewIDHashKey,
+ nsTArray<ScrollPositionUpdate>>
+ ScrollUpdatesMap;
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_FRAMEMETRICS_H */