diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/FrameMetrics.h | 1114 |
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 */ |