michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef GFX_FRAMEMETRICS_H michael@0: #define GFX_FRAMEMETRICS_H michael@0: michael@0: #include // for uint32_t, uint64_t michael@0: #include // for std::string michael@0: #include "Units.h" // for CSSRect, CSSPixel, etc michael@0: #include "mozilla/gfx/BasePoint.h" // for BasePoint michael@0: #include "mozilla/gfx/Rect.h" // for RoundedIn michael@0: #include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor michael@0: #include "mozilla/gfx/Logging.h" // for Log michael@0: michael@0: namespace IPC { michael@0: template struct ParamTraits; michael@0: } // namespace IPC michael@0: michael@0: namespace mozilla { michael@0: michael@0: // The layer coordinates of the parent layer. michael@0: // This can be arrived at in two ways: michael@0: // - Start with the CSS coordinates of the parent layer (note: NOT the michael@0: // CSS coordinates of the current layer, that will give you the wrong michael@0: // answer), multiply by the device scale and the resolutions of all michael@0: // layers from the root down to and including the parent. michael@0: // - Start with global screen coordinates and unapply all CSS and async michael@0: // transforms from the root down to and including the parent. michael@0: // It's helpful to look at https://wiki.mozilla.org/Platform/GFX/APZ#Coordinate_systems michael@0: // to get a picture of how the various coordinate systems relate to each other. michael@0: struct ParentLayerPixel {}; michael@0: michael@0: typedef gfx::PointTyped ParentLayerPoint; michael@0: typedef gfx::RectTyped ParentLayerRect; michael@0: typedef gfx::SizeTyped ParentLayerSize; michael@0: michael@0: typedef gfx::IntMarginTyped ParentLayerIntMargin; michael@0: typedef gfx::IntPointTyped ParentLayerIntPoint; michael@0: typedef gfx::IntRectTyped ParentLayerIntRect; michael@0: typedef gfx::IntSizeTyped ParentLayerIntSize; michael@0: michael@0: typedef gfx::ScaleFactor CSSToParentLayerScale; michael@0: typedef gfx::ScaleFactor LayoutDeviceToParentLayerScale; michael@0: typedef gfx::ScaleFactor ScreenToParentLayerScale; michael@0: michael@0: typedef gfx::ScaleFactor ParentLayerToLayerScale; michael@0: typedef gfx::ScaleFactor ParentLayerToScreenScale; michael@0: michael@0: michael@0: namespace layers { michael@0: michael@0: /** michael@0: * The viewport and displayport metrics for the painted frame at the michael@0: * time of a layer-tree transaction. These metrics are especially michael@0: * useful for shadow layers, because the metrics values are updated michael@0: * atomically with new pixels. michael@0: */ michael@0: struct FrameMetrics { michael@0: friend struct IPC::ParamTraits; michael@0: public: michael@0: // We use IDs to identify frames across processes. michael@0: typedef uint64_t ViewID; michael@0: static const ViewID NULL_SCROLL_ID; // This container layer does not scroll. michael@0: static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes michael@0: // will begin at. michael@0: michael@0: FrameMetrics() michael@0: : mCompositionBounds(0, 0, 0, 0) michael@0: , mDisplayPort(0, 0, 0, 0) michael@0: , mCriticalDisplayPort(0, 0, 0, 0) michael@0: , mViewport(0, 0, 0, 0) michael@0: , mScrollableRect(0, 0, 0, 0) michael@0: , mResolution(1) michael@0: , mCumulativeResolution(1) michael@0: , mTransformScale(1) michael@0: , mDevPixelsPerCSSPixel(1) michael@0: , mPresShellId(-1) michael@0: , mMayHaveTouchListeners(false) michael@0: , mIsRoot(false) michael@0: , mHasScrollgrab(false) michael@0: , mScrollId(NULL_SCROLL_ID) michael@0: , mScrollOffset(0, 0) michael@0: , mZoom(1) michael@0: , mUpdateScrollOffset(false) michael@0: , mScrollGeneration(0) michael@0: , mRootCompositionSize(0, 0) michael@0: , mDisplayPortMargins(0, 0, 0, 0) michael@0: , mUseDisplayPortMargins(false) michael@0: {} michael@0: michael@0: // Default copy ctor and operator= are fine michael@0: michael@0: bool operator==(const FrameMetrics& aOther) const michael@0: { michael@0: // mContentDescription is not compared on purpose as it's only used michael@0: // for debugging. michael@0: return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) && michael@0: mRootCompositionSize == aOther.mRootCompositionSize && michael@0: mDisplayPort.IsEqualEdges(aOther.mDisplayPort) && michael@0: mDisplayPortMargins == aOther.mDisplayPortMargins && michael@0: mUseDisplayPortMargins == aOther.mUseDisplayPortMargins && michael@0: mCriticalDisplayPort.IsEqualEdges(aOther.mCriticalDisplayPort) && michael@0: mViewport.IsEqualEdges(aOther.mViewport) && michael@0: mScrollableRect.IsEqualEdges(aOther.mScrollableRect) && michael@0: mResolution == aOther.mResolution && michael@0: mCumulativeResolution == aOther.mCumulativeResolution && michael@0: mDevPixelsPerCSSPixel == aOther.mDevPixelsPerCSSPixel && michael@0: mMayHaveTouchListeners == aOther.mMayHaveTouchListeners && michael@0: mPresShellId == aOther.mPresShellId && michael@0: mIsRoot == aOther.mIsRoot && michael@0: mScrollId == aOther.mScrollId && michael@0: mScrollOffset == aOther.mScrollOffset && michael@0: mHasScrollgrab == aOther.mHasScrollgrab && michael@0: mUpdateScrollOffset == aOther.mUpdateScrollOffset; michael@0: } michael@0: bool operator!=(const FrameMetrics& aOther) const michael@0: { michael@0: return !operator==(aOther); michael@0: } michael@0: michael@0: bool IsDefault() const michael@0: { michael@0: FrameMetrics def; michael@0: michael@0: def.mPresShellId = mPresShellId; michael@0: return (def == *this); michael@0: } michael@0: michael@0: bool IsRootScrollable() const michael@0: { michael@0: return mIsRoot; michael@0: } michael@0: michael@0: bool IsScrollable() const michael@0: { michael@0: return mScrollId != NULL_SCROLL_ID; michael@0: } michael@0: michael@0: CSSToLayerScale LayersPixelsPerCSSPixel() const michael@0: { michael@0: return mCumulativeResolution * mDevPixelsPerCSSPixel; michael@0: } michael@0: michael@0: LayerPoint GetScrollOffsetInLayerPixels() const michael@0: { michael@0: return GetScrollOffset() * LayersPixelsPerCSSPixel(); michael@0: } michael@0: michael@0: LayoutDeviceToParentLayerScale GetParentResolution() const michael@0: { michael@0: return mCumulativeResolution / mResolution; michael@0: } michael@0: michael@0: // Ensure the scrollableRect is at least as big as the compositionBounds michael@0: // because the scrollableRect can be smaller if the content is not large michael@0: // and the scrollableRect hasn't been updated yet. michael@0: // We move the scrollableRect up because we don't know if we can move it michael@0: // down. i.e. we know that scrollableRect can go back as far as zero. michael@0: // but we don't know how much further ahead it can go. michael@0: CSSRect GetExpandedScrollableRect() const michael@0: { michael@0: CSSRect scrollableRect = mScrollableRect; michael@0: CSSSize compSize = CalculateCompositedSizeInCssPixels(); michael@0: if (scrollableRect.width < compSize.width) { michael@0: scrollableRect.x = std::max(0.f, michael@0: scrollableRect.x - (compSize.width - scrollableRect.width)); michael@0: scrollableRect.width = compSize.width; michael@0: } michael@0: michael@0: if (scrollableRect.height < compSize.height) { michael@0: scrollableRect.y = std::max(0.f, michael@0: scrollableRect.y - (compSize.height - scrollableRect.height)); michael@0: scrollableRect.height = compSize.height; michael@0: } michael@0: michael@0: return scrollableRect; michael@0: } michael@0: michael@0: // Return the scale factor needed to fit the viewport michael@0: // into its composition bounds. michael@0: CSSToScreenScale CalculateIntrinsicScale() const michael@0: { michael@0: return CSSToScreenScale(float(mCompositionBounds.width) / float(mViewport.width)); michael@0: } michael@0: michael@0: // Return the scale factor for converting from CSS pixels (for this layer) michael@0: // to layer pixels of our parent layer. Much as mZoom is used to interface michael@0: // between inputs we get in screen pixels and quantities in CSS pixels, michael@0: // this is used to interface between mCompositionBounds and quantities michael@0: // in CSS pixels. michael@0: CSSToParentLayerScale GetZoomToParent() const michael@0: { michael@0: return mZoom * mTransformScale; michael@0: } michael@0: michael@0: CSSSize CalculateCompositedSizeInCssPixels() const michael@0: { michael@0: return mCompositionBounds.Size() / GetZoomToParent(); michael@0: } michael@0: michael@0: CSSRect CalculateCompositedRectInCssPixels() const michael@0: { michael@0: return mCompositionBounds / GetZoomToParent(); michael@0: } michael@0: michael@0: void ScrollBy(const CSSPoint& aPoint) michael@0: { michael@0: mScrollOffset += aPoint; michael@0: } michael@0: michael@0: void ZoomBy(float aFactor) michael@0: { michael@0: mZoom.scale *= aFactor; michael@0: } michael@0: michael@0: void CopyScrollInfoFrom(const FrameMetrics& aOther) michael@0: { michael@0: mScrollOffset = aOther.mScrollOffset; michael@0: mScrollGeneration = aOther.mScrollGeneration; michael@0: } michael@0: michael@0: // --------------------------------------------------------------------------- michael@0: // The following metrics are all in widget space/device pixels. michael@0: // michael@0: michael@0: // This is the area within the widget that we're compositing to. It is relative michael@0: // to the layer tree origin. michael@0: // michael@0: // This is useful because, on mobile, the viewport and composition dimensions michael@0: // are not always the same. In this case, we calculate the displayport using michael@0: // an area bigger than the region we're compositing to. If we used the michael@0: // viewport dimensions to calculate the displayport, we'd run into situations michael@0: // where we're prerendering the wrong regions and the content may be clipped, michael@0: // or too much of it prerendered. If the composition dimensions are the same as the michael@0: // viewport dimensions, there is no need for this and we can just use the viewport michael@0: // instead. michael@0: // michael@0: // This value is valid for nested scrollable layers as well, and is still michael@0: // relative to the layer tree origin. This value is provided by Gecko at michael@0: // layout/paint time. michael@0: ParentLayerIntRect mCompositionBounds; michael@0: michael@0: // --------------------------------------------------------------------------- michael@0: // The following metrics are all in CSS pixels. They are not in any uniform michael@0: // space, so each is explained separately. michael@0: // michael@0: michael@0: // The area of a frame's contents that has been painted, relative to the michael@0: // viewport. It is in the same coordinate space as |mViewport|. For example, michael@0: // if it is at 0,0, then it's at the same place at the viewport, which is at michael@0: // the top-left in the layer, and at the same place as the scroll offset of michael@0: // the document. michael@0: // michael@0: // Note that this is structured in such a way that it doesn't depend on the michael@0: // method layout uses to scroll content. michael@0: // michael@0: // May be larger or smaller than |mScrollableRect|. michael@0: // michael@0: // To pre-render a margin of 100 CSS pixels around the window, michael@0: // { x = -100, y = - 100, michael@0: // width = window.innerWidth + 200, height = window.innerHeight + 200 } michael@0: CSSRect mDisplayPort; michael@0: michael@0: // If non-empty, the area of a frame's contents that is considered critical michael@0: // to paint. Area outside of this area (i.e. area inside mDisplayPort, but michael@0: // outside of mCriticalDisplayPort) is considered low-priority, and may be michael@0: // painted with lower precision, or not painted at all. michael@0: // michael@0: // The same restrictions for mDisplayPort apply here. michael@0: CSSRect mCriticalDisplayPort; michael@0: michael@0: // The CSS viewport, which is the dimensions we're using to constrain the michael@0: // element of this frame, relative to the top-left of the layer. Note michael@0: // that its offset is structured in such a way that it doesn't depend on the michael@0: // method layout uses to scroll content. michael@0: // michael@0: // This is mainly useful on the root layer, however nested iframes can have michael@0: // their own viewport, which will just be the size of the window of the michael@0: // iframe. For layers that don't correspond to a document, this metric is michael@0: // meaningless and invalid. michael@0: CSSRect mViewport; michael@0: michael@0: // The scrollable bounds of a frame. This is determined by reflow. michael@0: // Ordinarily the x and y will be 0 and the width and height will be the michael@0: // size of the element being scrolled. However for RTL pages or elements michael@0: // the x value may be negative. michael@0: // michael@0: // This is relative to the document. It is in the same coordinate space as michael@0: // |mScrollOffset|, but a different coordinate space than |mViewport| and michael@0: // |mDisplayPort|. Note also that this coordinate system is understood by michael@0: // window.scrollTo(). michael@0: // michael@0: // This is valid on any layer unless it has no content. michael@0: CSSRect mScrollableRect; michael@0: michael@0: // --------------------------------------------------------------------------- michael@0: // The following metrics are dimensionless. michael@0: // michael@0: michael@0: // The incremental resolution that the current frame has been painted at michael@0: // relative to the parent frame's resolution. This information is provided michael@0: // by Gecko at layout/paint time. michael@0: ParentLayerToLayerScale mResolution; michael@0: michael@0: // The cumulative resolution that the current frame has been painted at. michael@0: // This is the product of our mResolution and the mResolutions of our parent frames. michael@0: // This information is provided by Gecko at layout/paint time. michael@0: LayoutDeviceToLayerScale mCumulativeResolution; michael@0: michael@0: // The conversion factor between local screen pixels (the coordinate michael@0: // system in which APZCs receive input events) and our parent layer's michael@0: // layer pixels (the coordinate system of mCompositionBounds). michael@0: // This consists of the scale of the local CSS transform and the michael@0: // nontransient async transform. michael@0: // TODO: APZ does not currently work well if there is a CSS transform michael@0: // on the layer being scrolled that's not just a scale that's michael@0: // the same in both directions. When we fix this, mTransformScale michael@0: // will probably need to turn into a matrix. michael@0: ScreenToParentLayerScale mTransformScale; michael@0: michael@0: // The conversion factor between CSS pixels and device pixels for this frame. michael@0: // This can vary based on a variety of things, such as reflowing-zoom. The michael@0: // conversion factor for device pixels to layers pixels is just the michael@0: // resolution. michael@0: CSSToLayoutDeviceScale mDevPixelsPerCSSPixel; michael@0: michael@0: uint32_t mPresShellId; michael@0: michael@0: // Whether or not this frame may have touch listeners. michael@0: bool mMayHaveTouchListeners; michael@0: michael@0: // Whether or not this is the root scroll frame for the root content document. michael@0: bool mIsRoot; michael@0: michael@0: // Whether or not this frame is for an element marked 'scrollgrab'. michael@0: bool mHasScrollgrab; michael@0: michael@0: public: michael@0: void SetScrollOffset(const CSSPoint& aScrollOffset) michael@0: { michael@0: mScrollOffset = aScrollOffset; michael@0: } michael@0: michael@0: const CSSPoint& GetScrollOffset() const michael@0: { michael@0: return mScrollOffset; michael@0: } michael@0: michael@0: void SetZoom(const CSSToScreenScale& aZoom) michael@0: { michael@0: mZoom = aZoom; michael@0: } michael@0: michael@0: CSSToScreenScale GetZoom() const michael@0: { michael@0: return mZoom; michael@0: } michael@0: michael@0: void SetScrollOffsetUpdated(uint32_t aScrollGeneration) michael@0: { michael@0: mUpdateScrollOffset = true; michael@0: mScrollGeneration = aScrollGeneration; michael@0: } michael@0: michael@0: bool GetScrollOffsetUpdated() const michael@0: { michael@0: return mUpdateScrollOffset; michael@0: } michael@0: michael@0: uint32_t GetScrollGeneration() const michael@0: { michael@0: return mScrollGeneration; michael@0: } michael@0: michael@0: const std::string& GetContentDescription() const michael@0: { michael@0: return mContentDescription; michael@0: } michael@0: michael@0: void SetContentDescription(const std::string& aContentDescription) michael@0: { michael@0: mContentDescription = aContentDescription; michael@0: } michael@0: michael@0: ViewID GetScrollId() const michael@0: { michael@0: return mScrollId; michael@0: } michael@0: michael@0: void SetScrollId(ViewID scrollId) michael@0: { michael@0: mScrollId = scrollId; michael@0: } michael@0: michael@0: void SetRootCompositionSize(const CSSSize& aRootCompositionSize) michael@0: { michael@0: mRootCompositionSize = aRootCompositionSize; michael@0: } michael@0: michael@0: const CSSSize& GetRootCompositionSize() const michael@0: { michael@0: return mRootCompositionSize; michael@0: } michael@0: michael@0: void SetDisplayPortMargins(const LayerMargin& aDisplayPortMargins) michael@0: { michael@0: mDisplayPortMargins = aDisplayPortMargins; michael@0: } michael@0: michael@0: const LayerMargin& GetDisplayPortMargins() const michael@0: { michael@0: return mDisplayPortMargins; michael@0: } michael@0: michael@0: void SetUseDisplayPortMargins() michael@0: { michael@0: mUseDisplayPortMargins = true; michael@0: } michael@0: michael@0: bool GetUseDisplayPortMargins() const michael@0: { michael@0: return mUseDisplayPortMargins; michael@0: } michael@0: michael@0: private: michael@0: // New fields from now on should be made private and old fields should michael@0: // be refactored to be private. michael@0: michael@0: // A unique ID assigned to each scrollable frame. michael@0: ViewID mScrollId; michael@0: michael@0: // The position of the top-left of the CSS viewport, relative to the document michael@0: // (or the document relative to the viewport, if that helps understand it). michael@0: // michael@0: // Thus it is relative to the document. It is in the same coordinate space as michael@0: // |mScrollableRect|, but a different coordinate space than |mViewport| and michael@0: // |mDisplayPort|. michael@0: // michael@0: // It is required that the rect: michael@0: // { x = mScrollOffset.x, y = mScrollOffset.y, michael@0: // width = mCompositionBounds.x / mResolution.scale, michael@0: // height = mCompositionBounds.y / mResolution.scale } michael@0: // Be within |mScrollableRect|. michael@0: // michael@0: // This is valid for any layer, but is always relative to this frame and michael@0: // not any parents, regardless of parent transforms. michael@0: CSSPoint mScrollOffset; michael@0: michael@0: // The "user zoom". Content is painted by gecko at mResolution * mDevPixelsPerCSSPixel, michael@0: // but will be drawn to the screen at mZoom. In the steady state, the michael@0: // two will be the same, but during an async zoom action the two may michael@0: // diverge. This information is initialized in Gecko but updated in the APZC. michael@0: CSSToScreenScale mZoom; michael@0: michael@0: // Whether mScrollOffset was updated by something other than the APZ code, and michael@0: // if the APZC receiving this metrics should update its local copy. michael@0: bool mUpdateScrollOffset; michael@0: // The scroll generation counter used to acknowledge the scroll offset update. michael@0: uint32_t mScrollGeneration; michael@0: michael@0: // A description of the content element corresponding to this frame. michael@0: // This is empty unless the apz.printtree pref is turned on. michael@0: std::string mContentDescription; michael@0: michael@0: // The size of the root scrollable's composition bounds, but in local CSS pixels. michael@0: CSSSize mRootCompositionSize; michael@0: michael@0: // A display port expressed as layer margins that apply to the rect of what michael@0: // is drawn of the scrollable element. michael@0: LayerMargin mDisplayPortMargins; michael@0: michael@0: // If this is true then we use the display port margins on this metrics, michael@0: // otherwise use the display port rect. michael@0: bool mUseDisplayPortMargins; michael@0: }; michael@0: michael@0: /** michael@0: * This class allows us to uniquely identify a scrollable layer. The michael@0: * mLayersId identifies the layer tree (corresponding to a child process michael@0: * and/or tab) that the scrollable layer belongs to. The mPresShellId michael@0: * is a temporal identifier (corresponding to the document loaded that michael@0: * contains the scrollable layer, which may change over time). The michael@0: * mScrollId corresponds to the actual frame that is scrollable. michael@0: */ michael@0: struct ScrollableLayerGuid { michael@0: uint64_t mLayersId; michael@0: uint32_t mPresShellId; michael@0: FrameMetrics::ViewID mScrollId; michael@0: michael@0: ScrollableLayerGuid() michael@0: : mLayersId(0) michael@0: , mPresShellId(0) michael@0: , mScrollId(0) michael@0: { michael@0: MOZ_COUNT_CTOR(ScrollableLayerGuid); michael@0: } michael@0: michael@0: ScrollableLayerGuid(uint64_t aLayersId, uint32_t aPresShellId, michael@0: FrameMetrics::ViewID aScrollId) michael@0: : mLayersId(aLayersId) michael@0: , mPresShellId(aPresShellId) michael@0: , mScrollId(aScrollId) michael@0: { michael@0: MOZ_COUNT_CTOR(ScrollableLayerGuid); michael@0: } michael@0: michael@0: ScrollableLayerGuid(uint64_t aLayersId, const FrameMetrics& aMetrics) michael@0: : mLayersId(aLayersId) michael@0: , mPresShellId(aMetrics.mPresShellId) michael@0: , mScrollId(aMetrics.GetScrollId()) michael@0: { michael@0: MOZ_COUNT_CTOR(ScrollableLayerGuid); michael@0: } michael@0: michael@0: ~ScrollableLayerGuid() michael@0: { michael@0: MOZ_COUNT_DTOR(ScrollableLayerGuid); michael@0: } michael@0: michael@0: bool operator==(const ScrollableLayerGuid& other) const michael@0: { michael@0: return mLayersId == other.mLayersId michael@0: && mPresShellId == other.mPresShellId michael@0: && mScrollId == other.mScrollId; michael@0: } michael@0: michael@0: bool operator!=(const ScrollableLayerGuid& other) const michael@0: { michael@0: return !(*this == other); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: gfx::Log& operator<<(gfx::Log& log, const ScrollableLayerGuid& aGuid) { michael@0: return log << '(' << aGuid.mLayersId << ',' << aGuid.mPresShellId << ',' << aGuid.mScrollId << ')'; michael@0: } michael@0: michael@0: struct ZoomConstraints { michael@0: bool mAllowZoom; michael@0: bool mAllowDoubleTapZoom; michael@0: CSSToScreenScale mMinZoom; michael@0: CSSToScreenScale mMaxZoom; michael@0: michael@0: ZoomConstraints() michael@0: : mAllowZoom(true) michael@0: , mAllowDoubleTapZoom(true) michael@0: { michael@0: MOZ_COUNT_CTOR(ZoomConstraints); michael@0: } michael@0: michael@0: ZoomConstraints(bool aAllowZoom, michael@0: bool aAllowDoubleTapZoom, michael@0: const CSSToScreenScale& aMinZoom, michael@0: const CSSToScreenScale& aMaxZoom) michael@0: : mAllowZoom(aAllowZoom) michael@0: , mAllowDoubleTapZoom(aAllowDoubleTapZoom) michael@0: , mMinZoom(aMinZoom) michael@0: , mMaxZoom(aMaxZoom) michael@0: { michael@0: MOZ_COUNT_CTOR(ZoomConstraints); michael@0: } michael@0: michael@0: ~ZoomConstraints() michael@0: { michael@0: MOZ_COUNT_DTOR(ZoomConstraints); michael@0: } michael@0: michael@0: bool operator==(const ZoomConstraints& other) const michael@0: { michael@0: return mAllowZoom == other.mAllowZoom michael@0: && mAllowDoubleTapZoom == other.mAllowDoubleTapZoom michael@0: && mMinZoom == other.mMinZoom michael@0: && mMaxZoom == other.mMaxZoom; michael@0: } michael@0: michael@0: bool operator!=(const ZoomConstraints& other) const michael@0: { michael@0: return !(*this == other); michael@0: } michael@0: }; michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* GFX_FRAMEMETRICS_H */