gecko-dev/gfx/layers/FrameMetrics.h

1083 строки
41 KiB
C++

/* -*- 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 "Units.h" // for CSSRect, CSSPixel, etc
#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/StaticPtr.h" // for StaticAutoPtr
#include "mozilla/TimeStamp.h" // for TimeStamp
#include "nsDataHashtable.h" // for nsDataHashtable
#include "nsString.h"
#include "mozilla/ServoStyleConsts.h"
#include "PLDHashTable.h" // for PLDHashNumber
struct nsStyleDisplay;
namespace mozilla {
class WritingMode;
} // namespace mozilla
namespace IPC {
template <typename T>
struct ParamTraits;
} // namespace IPC
namespace mozilla {
namespace layers {
/**
* Helper struct to hold a couple of fields that can be updated as part of
* an empty transaction.
*/
struct ScrollUpdateInfo {
uint32_t mScrollGeneration;
CSSPoint mScrollOffset;
CSSPoint mBaseScrollOffset;
bool mIsRelative;
};
/**
* 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>;
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.
ePending, // The scroll offset was updated on the main thread, but
// not painted, so the layer texture data is still at the
// old offset.
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),
mDisplayPort(0, 0, 0, 0),
mCriticalDisplayPort(0, 0, 0, 0),
mScrollableRect(0, 0, 0, 0),
mCumulativeResolution(),
mDevPixelsPerCSSPixel(1),
mScrollOffset(0, 0),
mBaseScrollOffset(0, 0),
mZoom(),
mScrollGeneration(0),
mSmoothScrollOffset(0, 0),
mRootCompositionSize(0, 0),
mDisplayPortMargins(0, 0, 0, 0),
mPresShellId(-1),
mLayoutViewport(0, 0, 0, 0),
mExtraResolution(),
mPaintRequestTime(),
mScrollUpdateType(eNone),
mVisualViewportOffset(0, 0),
mVisualScrollUpdateType(eNone),
mIsRootContent(false),
mIsRelative(false),
mDoSmoothScroll(false),
mIsScrollInfoLayer(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) &&
mDisplayPort.IsEqualEdges(aOther.mDisplayPort) &&
mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) &&
mScrollableRect.IsEqualEdges(aOther.mScrollableRect) &&
mCumulativeResolution == aOther.mCumulativeResolution &&
mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel &&
mScrollOffset == aOther.mScrollOffset &&
mBaseScrollOffset == aOther.mBaseScrollOffset &&
// don't compare mZoom
mScrollGeneration == aOther.mScrollGeneration &&
mSmoothScrollOffset == aOther.mSmoothScrollOffset &&
mRootCompositionSize == aOther.mRootCompositionSize &&
mDisplayPortMargins == aOther.mDisplayPortMargins &&
mPresShellId == aOther.mPresShellId &&
mLayoutViewport.IsEqualEdges(aOther.mLayoutViewport) &&
mExtraResolution == aOther.mExtraResolution &&
mPaintRequestTime == aOther.mPaintRequestTime &&
mScrollUpdateType == aOther.mScrollUpdateType &&
mVisualViewportOffset == aOther.mVisualViewportOffset &&
mVisualScrollUpdateType == aOther.mVisualScrollUpdateType &&
mIsRootContent == aOther.mIsRootContent &&
mIsRelative == aOther.mIsRelative &&
mDoSmoothScroll == aOther.mDoSmoothScroll &&
mIsScrollInfoLayer == aOther.mIsScrollInfoLayer;
}
bool operator!=(const FrameMetrics& aOther) const {
return !operator==(aOther);
}
bool IsScrollable() const {
return mScrollId != ScrollableLayerGuid::NULL_SCROLL_ID;
}
CSSToScreenScale2D DisplayportPixelsPerCSSPixel() const {
// Note: use 'mZoom * ParentLayerToLayerScale(1.0f)' as the CSS-to-Layer
// scale instead of LayersPixelsPerCSSPixel(), because displayport
// calculations are done in the context of a repaint request, where we ask
// Layout to repaint at a new resolution that includes any async zoom. Until
// this repaint request is processed, LayersPixelsPerCSSPixel() does not yet
// include the async zoom, but it will when the displayport is interpreted
// for the repaint.
return mZoom * ParentLayerToLayerScale(1.0f) / mExtraResolution;
}
CSSToLayerScale2D LayersPixelsPerCSSPixel() const {
return mDevPixelsPerCSSPixel * mCumulativeResolution;
}
// Get the amount by which this frame has been zoomed since the last repaint.
LayerToParentLayerScale GetAsyncZoom() const {
// The async portion of the zoom should be the same along the x and y
// axes.
return (mZoom / LayersPixelsPerCSSPixel()).ToScaleFactor();
}
// 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 {
if (GetZoom() == CSSToParentLayerScale2D(0, 0)) {
return CSSSize(); // avoid division by zero
}
return mCompositionBounds.Size() / GetZoom();
}
/*
* 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 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() == CSSToParentLayerScale2D(0, 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, mRootCompositionSize.width);
size.height = std::min(size.height, mRootCompositionSize.height);
return size;
}
CSSRect CalculateScrollRange() const {
CSSSize scrollPortSize = CalculateCompositedSizeInCssPixels();
CSSRect scrollRange = mScrollableRect;
scrollRange.SetWidth(
std::max(scrollRange.Width() - scrollPortSize.width, 0.0f));
scrollRange.SetHeight(
std::max(scrollRange.Height() - scrollPortSize.height, 0.0f));
return scrollRange;
}
void ScrollBy(const CSSPoint& aPoint) { mScrollOffset += aPoint; }
void ZoomBy(float aScale) { ZoomBy(gfxSize(aScale, aScale)); }
void ZoomBy(const gfxSize& aScale) {
mZoom.xScale *= aScale.width;
mZoom.yScale *= aScale.height;
}
/*
* 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 mScrollOffset != aContentFrameMetrics.mBaseScrollOffset;
}
void ApplyScrollUpdateFrom(const FrameMetrics& aOther) {
mScrollOffset = aOther.mScrollOffset;
mScrollGeneration = aOther.mScrollGeneration;
}
void ApplySmoothScrollUpdateFrom(const FrameMetrics& aOther) {
mSmoothScrollOffset = aOther.mSmoothScrollOffset;
mScrollGeneration = aOther.mScrollGeneration;
mDoSmoothScroll = aOther.mDoSmoothScroll;
}
/**
* 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 FrameMetrics& aOther) {
MOZ_ASSERT(aOther.IsRelative());
CSSPoint origin = mScrollOffset;
CSSPoint delta = (aOther.mScrollOffset - aOther.mBaseScrollOffset);
ClampAndSetScrollOffset(mScrollOffset + delta);
mScrollGeneration = aOther.mScrollGeneration;
return mScrollOffset - origin;
}
/**
* Applies the relative scroll offset update contained in aOther to the
* smooth scroll destination offset contained in this. The scroll delta is
* clamped to the scrollable region.
*/
void ApplyRelativeSmoothScrollUpdateFrom(const FrameMetrics& aOther) {
MOZ_ASSERT(aOther.IsRelative());
CSSPoint delta = (aOther.mSmoothScrollOffset - aOther.mBaseScrollOffset);
ClampAndSetSmoothScrollOffset(mScrollOffset + delta);
mScrollGeneration = aOther.mScrollGeneration;
mDoSmoothScroll = aOther.mDoSmoothScroll;
}
void UpdatePendingScrollInfo(const ScrollUpdateInfo& aInfo) {
mScrollOffset = aInfo.mScrollOffset;
mBaseScrollOffset = aInfo.mBaseScrollOffset;
mScrollGeneration = aInfo.mScrollGeneration;
mScrollUpdateType = ePending;
mIsRelative = aInfo.mIsRelative;
}
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 SetDisplayPort(const CSSRect& aDisplayPort) {
mDisplayPort = aDisplayPort;
}
const CSSRect& GetDisplayPort() const { return mDisplayPort; }
void SetCriticalDisplayPort(const CSSRect& aCriticalDisplayPort) {
mCriticalDisplayPort = aCriticalDisplayPort;
}
const CSSRect& GetCriticalDisplayPort() const { return mCriticalDisplayPort; }
void SetCumulativeResolution(
const LayoutDeviceToLayerScale2D& aCumulativeResolution) {
mCumulativeResolution = aCumulativeResolution;
}
const LayoutDeviceToLayerScale2D& 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; }
void SetScrollOffset(const CSSPoint& aScrollOffset) {
mScrollOffset = aScrollOffset;
}
void SetBaseScrollOffset(const CSSPoint& aScrollOffset) {
mBaseScrollOffset = aScrollOffset;
}
// Set scroll offset, first clamping to the scroll range.
void ClampAndSetScrollOffset(const CSSPoint& aScrollOffset) {
SetScrollOffset(CalculateScrollRange().ClampPoint(aScrollOffset));
}
const CSSPoint& GetScrollOffset() const { return mScrollOffset; }
const CSSPoint& GetBaseScrollOffset() const { return mBaseScrollOffset; }
void SetSmoothScrollOffset(const CSSPoint& aSmoothScrollDestination) {
mSmoothScrollOffset = aSmoothScrollDestination;
}
void ClampAndSetSmoothScrollOffset(const CSSPoint& aSmoothScrollOffset) {
SetSmoothScrollOffset(
CalculateScrollRange().ClampPoint(aSmoothScrollOffset));
}
const CSSPoint& GetSmoothScrollOffset() const { return mSmoothScrollOffset; }
void SetZoom(const CSSToParentLayerScale2D& aZoom) { mZoom = aZoom; }
const CSSToParentLayerScale2D& GetZoom() const { return mZoom; }
void SetScrollGeneration(uint32_t aScrollGeneration) {
mScrollGeneration = aScrollGeneration;
}
void SetScrollOffsetUpdateType(ScrollOffsetUpdateType aScrollUpdateType) {
mScrollUpdateType = aScrollUpdateType;
}
void SetSmoothScrollOffsetUpdated(int32_t aScrollGeneration) {
mDoSmoothScroll = true;
mScrollGeneration = aScrollGeneration;
}
ScrollOffsetUpdateType GetScrollUpdateType() const {
return mScrollUpdateType;
}
bool GetScrollOffsetUpdated() const { return mScrollUpdateType != eNone; }
void SetIsRelative(bool aIsRelative) { mIsRelative = aIsRelative; }
bool IsRelative() const { return mIsRelative; }
bool GetDoSmoothScroll() const { return mDoSmoothScroll; }
uint32_t GetScrollGeneration() const { return mScrollGeneration; }
ViewID GetScrollId() const { return mScrollId; }
void SetScrollId(ViewID scrollId) { mScrollId = scrollId; }
void SetRootCompositionSize(const CSSSize& aRootCompositionSize) {
mRootCompositionSize = aRootCompositionSize;
}
const CSSSize& GetRootCompositionSize() const { return mRootCompositionSize; }
void SetDisplayPortMargins(const ScreenMargin& aDisplayPortMargins) {
mDisplayPortMargins = aDisplayPortMargins;
}
const ScreenMargin& GetDisplayPortMargins() const {
return mDisplayPortMargins;
}
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(mScrollOffset, CalculateCompositedSizeInCssPixels());
}
void SetExtraResolution(const ScreenToLayerScale2D& aExtraResolution) {
mExtraResolution = aExtraResolution;
}
const ScreenToLayerScale2D& GetExtraResolution() const {
return mExtraResolution;
}
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 SetVisualViewportOffset(const CSSPoint& aVisualViewportOffset) {
mVisualViewportOffset = aVisualViewportOffset;
}
const CSSPoint& GetVisualViewportOffset() const {
return mVisualViewportOffset;
}
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();
// 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);
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 layer tree origin.
// Unlike the scroll port's origin, it does not change during scrolling.
//
// This value is provided by Gecko at layout/paint time.
ParentLayerRect mCompositionBounds;
// The area of a scroll frame's contents that has been painted, relative to
// mScrollOffset.
//
// 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;
// If non-empty, the area of a frame's contents that is considered critical
// to paint. Area outside of this area (i.e. area inside mDisplayPort, but
// outside of mCriticalDisplayPort) is considered low-priority, and may be
// painted with lower precision, or not painted at all.
//
// The same restrictions for mDisplayPort apply here.
CSSRect mCriticalDisplayPort;
// 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 that the current frame has been painted at.
// This is the product of the pres-shell resolutions of the document
// containing this scroll frame and its ancestors, and any css-driven
// resolution. This information is provided by Gecko at layout/paint time.
// Note that this is allowed to have different x- and y-scales, but only
// for subframes (mIsRootContent = false). (The same applies to other scales
// that "inherit" the 2D-ness of this one, such as mZoom.)
LayoutDeviceToLayerScale2D 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 base scroll offset to use for calculating a relative update to a
// scroll offset.
CSSPoint mBaseScrollOffset;
// 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.
CSSToParentLayerScale2D mZoom;
// The scroll generation counter used to acknowledge the scroll offset update.
uint32_t mScrollGeneration;
// If mDoSmoothScroll is true, the scroll offset will be animated smoothly
// to this value.
CSSPoint mSmoothScrollOffset;
// The size of the root scrollable's composition bounds, but in local CSS
// pixels.
CSSSize mRootCompositionSize;
// A display port expressed as layer margins that apply to the rect of what
// is drawn of the scrollable element.
ScreenMargin mDisplayPortMargins;
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 extra resolution at which content in this scroll frame is drawn beyond
// that necessary to draw one Layer pixel per Screen pixel.
ScreenToLayerScale2D mExtraResolution;
// The time at which the APZC last requested a repaint for this scroll frame.
TimeStamp mPaintRequestTime;
// Whether mScrollOffset was updated by something other than the APZ code, and
// if the APZC receiving this metrics should update its local copy.
ScrollOffsetUpdateType mScrollUpdateType;
// 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
// mVisualViewportOffset is set to desired visual viewport offset (relative
// to the document, like mScrollOffset).
// TODO: Get rid of mVisualViewportOffset: between mViewport.TopLeft() and
// mScrollOffset, we have enough storage for the two scroll offsets.
// However, to avoid confusion, that first requires refactoring
// existing to consistently use the two fields for those two purposes.
CSSPoint mVisualViewportOffset;
ScrollOffsetUpdateType mVisualScrollUpdateType;
// Whether or not this is the root scroll frame for the root content document.
bool mIsRootContent : 1;
// When mIsRelative, the scroll offset was updated using a relative API,
// such as `ScrollBy`, and can combined with an async scroll.
bool mIsRelative : 1;
// When mDoSmoothScroll, the scroll offset should be animated to
// smoothly transition to mScrollOffset rather than be updated instantly.
bool mDoSmoothScroll : 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;
// 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.
// Private helpers for IPC purposes
void SetDoSmoothScroll(bool aValue) { mDoSmoothScroll = aValue; }
};
struct ScrollSnapInfo {
ScrollSnapInfo() = default;
bool operator==(const ScrollSnapInfo& aOther) const {
return mScrollSnapStrictnessX == aOther.mScrollSnapStrictnessX &&
mScrollSnapStrictnessY == aOther.mScrollSnapStrictnessY &&
mSnapPositionX == aOther.mSnapPositionX &&
mSnapPositionY == aOther.mSnapPositionY &&
mXRangeWiderThanSnapport == aOther.mXRangeWiderThanSnapport &&
mYRangeWiderThanSnapport == aOther.mYRangeWiderThanSnapport &&
mSnapportSize == aOther.mSnapportSize;
}
bool HasScrollSnapping() const {
return mScrollSnapStrictnessY != mozilla::StyleScrollSnapStrictness::None ||
mScrollSnapStrictnessX != mozilla::StyleScrollSnapStrictness::None;
}
bool HasSnapPositions() const {
return (!mSnapPositionX.IsEmpty() &&
mScrollSnapStrictnessX !=
mozilla::StyleScrollSnapStrictness::None) ||
(!mSnapPositionY.IsEmpty() &&
mScrollSnapStrictnessY != mozilla::StyleScrollSnapStrictness::None);
}
void InitializeScrollSnapStrictness(WritingMode aWritingMode,
const nsStyleDisplay* aDisplay);
// The scroll frame's scroll-snap-type.
mozilla::StyleScrollSnapStrictness mScrollSnapStrictnessX =
mozilla::StyleScrollSnapStrictness::None;
mozilla::StyleScrollSnapStrictness mScrollSnapStrictnessY =
mozilla::StyleScrollSnapStrictness::None;
// The scroll positions corresponding to scroll-snap-align values.
nsTArray<nscoord> mSnapPositionX;
nsTArray<nscoord> mSnapPositionY;
struct ScrollSnapRange {
ScrollSnapRange() = default;
ScrollSnapRange(nscoord aStart, nscoord aEnd)
: mStart(aStart), mEnd(aEnd) {}
nscoord mStart;
nscoord mEnd;
bool operator==(const ScrollSnapRange& aOther) const {
return mStart == aOther.mStart && mEnd == aOther.mEnd;
}
// 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.
nsTArray<ScrollSnapRange> mXRangeWiderThanSnapport;
nsTArray<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
struct OverscrollBehaviorInfo {
OverscrollBehaviorInfo()
: mBehaviorX(OverscrollBehavior::Auto),
mBehaviorY(OverscrollBehavior::Auto) {}
// Construct from StyleOverscrollBehavior values.
static OverscrollBehaviorInfo FromStyleConstants(
StyleOverscrollBehavior aBehaviorX, StyleOverscrollBehavior aBehaviorY);
bool operator==(const OverscrollBehaviorInfo& aOther) const {
return mBehaviorX == aOther.mBehaviorX && mBehaviorY == aOther.mBehaviorY;
}
OverscrollBehavior mBehaviorX;
OverscrollBehavior mBehaviorY;
};
/**
* A clip that applies to a layer, that may be scrolled by some of the
* scroll frames associated with the layer.
*/
struct LayerClip {
friend struct IPC::ParamTraits<mozilla::layers::LayerClip>;
public:
LayerClip() : mClipRect(), mMaskLayerIndex() {}
explicit LayerClip(const ParentLayerIntRect& aClipRect)
: mClipRect(aClipRect), mMaskLayerIndex() {}
bool operator==(const LayerClip& aOther) const {
return mClipRect == aOther.mClipRect &&
mMaskLayerIndex == aOther.mMaskLayerIndex;
}
void SetClipRect(const ParentLayerIntRect& aClipRect) {
mClipRect = aClipRect;
}
const ParentLayerIntRect& GetClipRect() const { return mClipRect; }
void SetMaskLayerIndex(const Maybe<size_t>& aIndex) {
mMaskLayerIndex = aIndex;
}
const Maybe<size_t>& GetMaskLayerIndex() const { return mMaskLayerIndex; }
private:
ParentLayerIntRect mClipRect;
// Optionally, specifies a mask layer that's part of the clip.
// This is an index into the MetricsMaskLayers array on the Layer.
Maybe<size_t> mMaskLayerIndex;
};
typedef Maybe<LayerClip> MaybeLayerClip; // for passing over IPDL
/**
* 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>;
typedef ScrollableLayerGuid::ViewID ViewID;
public:
static StaticAutoPtr<const ScrollMetadata>
sNullMetadata; // We sometimes need an empty metadata
ScrollMetadata()
: mMetrics(),
mSnapInfo(),
mScrollParentId(ScrollableLayerGuid::NULL_SCROLL_ID),
mBackgroundColor(),
mContentDescription(),
mLineScrollAmount(0, 0),
mPageScrollAmount(0, 0),
mScrollClip(),
mHasScrollgrab(false),
mIsLayersIdRoot(false),
mIsAutoDirRootContentRTL(false),
mForceDisableApz(false),
mResolutionUpdated(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 &&
mScrollClip == aOther.mScrollClip &&
mHasScrollgrab == aOther.mHasScrollgrab &&
mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
mIsAutoDirRootContentRTL == aOther.mIsAutoDirRootContentRTL &&
mForceDisableApz == aOther.mForceDisableApz &&
mResolutionUpdated == aOther.mResolutionUpdated &&
mDisregardedDirection == aOther.mDisregardedDirection &&
mOverscrollBehavior == aOther.mOverscrollBehavior;
}
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::Color& GetBackgroundColor() const { return mBackgroundColor; }
void SetBackgroundColor(const gfx::Color& aBackgroundColor) {
mBackgroundColor = 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 SetScrollClip(const Maybe<LayerClip>& aScrollClip) {
mScrollClip = aScrollClip;
}
const Maybe<LayerClip>& GetScrollClip() const { return mScrollClip; }
bool HasScrollClip() const { return mScrollClip.isSome(); }
const LayerClip& ScrollClip() const { return mScrollClip.ref(); }
LayerClip& ScrollClip() { return mScrollClip.ref(); }
bool HasMaskLayer() const {
return HasScrollClip() && ScrollClip().GetMaskLayerIndex();
}
Maybe<ParentLayerIntRect> GetClipRect() const {
return mScrollClip.isSome() ? Some(mScrollClip->GetClipRect()) : Nothing();
}
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; }
// 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;
}
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::Color 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;
// A clip to apply when compositing the layer bearing this ScrollMetadata,
// after applying any transform arising from scrolling this scroll frame.
// Note that, unlike most other fields of ScrollMetadata, this is allowed
// to differ between different layers scrolled by the same scroll frame.
// TODO: Group the fields of ScrollMetadata into sub-structures to separate
// fields with this property better.
Maybe<LayerClip> mScrollClip;
// 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. Plays a similar role for the resolution as
// FrameMetrics::mScrollUpdateType) does for the scroll offset.
bool mResolutionUpdated : 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;
// 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 nsDataHashtable<ScrollableLayerGuid::ViewIDHashKey, ScrollUpdateInfo>
ScrollUpdatesMap;
} // namespace layers
} // namespace mozilla
#endif /* GFX_FRAMEMETRICS_H */