/* -*- 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 // for uint8_t, uint32_t, uint64_t #include #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 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; 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. (This can be thought of as * "parent CSS" 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.) */ CSSRect CalculateCompositionBoundsInCssPixelsOfSurroundingContent() const { if (GetZoom() == CSSToParentLayerScale(0)) { return CSSRect(); // 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() * CSSToCSSScale{mPresShellResolution}; } 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; } 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 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 mSnapPositionX; Maybe 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&& aSnapPositionX, Maybe&& 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 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 mXRangeWiderThanSnapport; CopyableTArray 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; friend std::ostream& operator<<(std::ostream& aStream, const ScrollMetadata& aMetadata); typedef ScrollableLayerGuid::ViewID ViewID; public: static StaticAutoPtr sNullMetadata; // We sometimes need an empty metadata ScrollMetadata() : mMetrics(), mSnapInfo(), mScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID), mBackgroundColor(), 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 && mBackgroundColor == aOther.mBackgroundColor && // 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 gfx::DeviceColor& GetBackgroundColor() const { return mBackgroundColor; } void SetBackgroundColor(const gfx::sRGBColor& aBackgroundColor); 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 GetDisregardedDirection() const { return mDisregardedDirection; } void SetDisregardedDirection(const Maybe& aValue) { mDisregardedDirection = aValue; } void SetOverscrollBehavior( const OverscrollBehaviorInfo& aOverscrollBehavior) { mOverscrollBehavior = aOverscrollBehavior; } const OverscrollBehaviorInfo& GetOverscrollBehavior() const { return mOverscrollBehavior; } void SetScrollUpdates(const nsTArray& aUpdates) { mScrollUpdates = aUpdates; } const nsTArray& GetScrollUpdates() const { return mScrollUpdates; } void UpdatePendingScrollInfo(nsTArray&& 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; // The background color to use when overscrolling. gfx::DeviceColor mBackgroundColor; // 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 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 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 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> ScrollUpdatesMap; } // namespace layers } // namespace mozilla #endif /* GFX_FRAMEMETRICS_H */