зеркало из https://github.com/mozilla/gecko-dev.git
517 строки
18 KiB
C++
517 строки
18 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_WEBRENDERSCROLLDATAWRAPPER_H
|
|
#define GFX_WEBRENDERSCROLLDATAWRAPPER_H
|
|
|
|
#include "FrameMetrics.h"
|
|
#include "mozilla/layers/APZUpdater.h"
|
|
#include "mozilla/layers/CompositorBridgeParent.h"
|
|
#include "mozilla/layers/WebRenderBridgeParent.h"
|
|
#include "mozilla/layers/WebRenderScrollData.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
/**
|
|
* A wrapper class around a target WebRenderLayerScrollData (henceforth,
|
|
* "layer") that allows user code to walk through the ScrollMetadata objects
|
|
* on the layer the same way it would walk through a layer tree.
|
|
* Consider the following layer tree:
|
|
*
|
|
* +---+
|
|
* | A |
|
|
* +---+
|
|
* / | \
|
|
* / | \
|
|
* / | \
|
|
* +---+ +-----+ +---+
|
|
* | B | | C | | D |
|
|
* +---+ +-----+ +---+
|
|
* | SMn |
|
|
* | . |
|
|
* | . |
|
|
* | . |
|
|
* | SM1 |
|
|
* | SM0 |
|
|
* +-----+
|
|
* / \
|
|
* / \
|
|
* +---+ +---+
|
|
* | E | | F |
|
|
* +---+ +---+
|
|
*
|
|
* In this layer tree, there are six layers with A being the root and B,D,E,F
|
|
* being leaf nodes. Layer C is in the middle and has n+1 ScrollMetadata,
|
|
* labelled SM0...SMn. SM0 is the ScrollMetadata you get by calling
|
|
* c->GetScrollMetadata(0) and SMn is the ScrollMetadata you can obtain by
|
|
* calling c->GetScrollMetadata(c->GetScrollMetadataCount() - 1). This layer
|
|
* tree is conceptually equivalent to this one below:
|
|
*
|
|
* +---+
|
|
* | A |
|
|
* +---+
|
|
* / | \
|
|
* / | \
|
|
* / | \
|
|
* +---+ +-----+ +---+
|
|
* | B | | Cn | | D |
|
|
* +---+ +-----+ +---+
|
|
* |
|
|
* .
|
|
* .
|
|
* .
|
|
* |
|
|
* +-----+
|
|
* | C1 |
|
|
* +-----+
|
|
* |
|
|
* +-----+
|
|
* | C0 |
|
|
* +-----+
|
|
* / \
|
|
* / \
|
|
* +---+ +---+
|
|
* | E | | F |
|
|
* +---+ +---+
|
|
*
|
|
* In this layer tree, the layer C has been expanded into a stack of layers
|
|
* C1...Cn, where C1 has ScrollMetadata SM1 and Cn has ScrollMetdata Fn.
|
|
*
|
|
* The WebRenderScrollDataWrapper class allows client code to treat the first
|
|
* layer tree as though it were the second. That is, instead of client code
|
|
* having to iterate through the ScrollMetadata objects directly, it can use a
|
|
* WebRenderScrollDataWrapper to encapsulate that aspect of the layer tree and
|
|
* just walk the tree as if it were a stack of layers.
|
|
*
|
|
* The functions on this class do different things depending on which
|
|
* simulated layer is being wrapped. For example, if the
|
|
* WebRenderScrollDataWrapper is pretending to be C0, the GetPrevSibling()
|
|
* function will return null even though the underlying layer C does actually
|
|
* have a prev sibling. The WebRenderScrollDataWrapper pretending to be Cn will
|
|
* return B as the prev sibling.
|
|
*
|
|
* Implementation notes:
|
|
*
|
|
* The AtTopLayer() and AtBottomLayer() functions in this class refer to
|
|
* Cn and C0 in the second layer tree above; that is, they are predicates
|
|
* to test if the wrapper is simulating the topmost or bottommost layer, as
|
|
* those can have special behaviour.
|
|
*
|
|
* It is possible to wrap a nullptr in a WebRenderScrollDataWrapper, in which
|
|
* case the IsValid() function will return false. This is required to allow
|
|
* WebRenderScrollDataWrapper to be a MOZ_STACK_CLASS (desirable because it is
|
|
* used in loops and recursion).
|
|
*
|
|
* This class purposely does not expose the wrapped layer directly to avoid
|
|
* user code from accidentally calling functions directly on it. Instead
|
|
* any necessary functions should be wrapped in this class. It does expose
|
|
* the wrapped layer as a void* for printf purposes.
|
|
*
|
|
* The implementation may look like it special-cases mIndex == 0 and/or
|
|
* GetScrollMetadataCount() == 0. This is an artifact of the fact that both
|
|
* mIndex and GetScrollMetadataCount() are uint32_t and GetScrollMetadataCount()
|
|
* can return 0 but mIndex cannot store -1. This seems better than the
|
|
* alternative of making mIndex a int32_t that can store -1, but then having
|
|
* to cast to uint32_t all over the place.
|
|
*
|
|
* Note that WebRenderLayerScrollData objects are owned by WebRenderScrollData,
|
|
* which stores them in a flattened representation. The field mData,
|
|
* mLayerIndex, and mContainingSubtreeIndex are used to move around the "layers"
|
|
* given the flattened representation. The mMetadataIndex is used to move around
|
|
* the ScrollMetadata within a single layer.
|
|
*
|
|
* One important note here is that this class holds a pointer to the "owning"
|
|
* WebRenderScrollData. The caller must ensure that this class does not outlive
|
|
* the owning WebRenderScrollData, or this may result in use-after-free errors.
|
|
* This class being declared a MOZ_STACK_CLASS should help with that.
|
|
*/
|
|
class MOZ_STACK_CLASS WebRenderScrollDataWrapper final {
|
|
public:
|
|
// Basic constructor for external callers. Starts the walker at the root of
|
|
// the tree.
|
|
explicit WebRenderScrollDataWrapper(
|
|
const APZUpdater& aUpdater, const WebRenderScrollData* aData = nullptr)
|
|
: mUpdater(&aUpdater),
|
|
mData(aData),
|
|
mLayerIndex(0),
|
|
mContainingSubtreeLastIndex(0),
|
|
mLayer(nullptr),
|
|
mMetadataIndex(0) {
|
|
if (!mData) {
|
|
return;
|
|
}
|
|
mLayer = mData->GetLayerData(mLayerIndex);
|
|
if (!mLayer) {
|
|
return;
|
|
}
|
|
|
|
// sanity check on the data
|
|
MOZ_ASSERT(mData->GetLayerCount() ==
|
|
(size_t)(1 + mLayer->GetDescendantCount()));
|
|
mContainingSubtreeLastIndex = mData->GetLayerCount();
|
|
|
|
// See documentation in LayerMetricsWrapper.h about this. mMetadataIndex
|
|
// in this class is equivalent to mIndex in that class.
|
|
mMetadataIndex = mLayer->GetScrollMetadataCount();
|
|
if (mMetadataIndex > 0) {
|
|
mMetadataIndex--;
|
|
}
|
|
}
|
|
|
|
private:
|
|
// Internal constructor for walking from one WebRenderLayerScrollData to
|
|
// another. In this case we need to recompute the mMetadataIndex to be the
|
|
// "topmost" scroll metadata on the new layer.
|
|
WebRenderScrollDataWrapper(const APZUpdater* aUpdater,
|
|
const WebRenderScrollData* aData,
|
|
size_t aLayerIndex,
|
|
size_t aContainingSubtreeLastIndex)
|
|
: mUpdater(aUpdater),
|
|
mData(aData),
|
|
mLayerIndex(aLayerIndex),
|
|
mContainingSubtreeLastIndex(aContainingSubtreeLastIndex),
|
|
mLayer(nullptr),
|
|
mMetadataIndex(0) {
|
|
MOZ_ASSERT(mData);
|
|
mLayer = mData->GetLayerData(mLayerIndex);
|
|
MOZ_ASSERT(mLayer);
|
|
|
|
// See documentation in LayerMetricsWrapper.h about this. mMetadataIndex
|
|
// in this class is equivalent to mIndex in that class.
|
|
mMetadataIndex = mLayer->GetScrollMetadataCount();
|
|
if (mMetadataIndex > 0) {
|
|
mMetadataIndex--;
|
|
}
|
|
}
|
|
|
|
// Internal constructor for walking from one metadata to another metadata on
|
|
// the same WebRenderLayerScrollData.
|
|
WebRenderScrollDataWrapper(const APZUpdater* aUpdater,
|
|
const WebRenderScrollData* aData,
|
|
size_t aLayerIndex,
|
|
size_t aContainingSubtreeLastIndex,
|
|
const WebRenderLayerScrollData* aLayer,
|
|
uint32_t aMetadataIndex)
|
|
: mUpdater(aUpdater),
|
|
mData(aData),
|
|
mLayerIndex(aLayerIndex),
|
|
mContainingSubtreeLastIndex(aContainingSubtreeLastIndex),
|
|
mLayer(aLayer),
|
|
mMetadataIndex(aMetadataIndex) {
|
|
MOZ_ASSERT(mData);
|
|
MOZ_ASSERT(mLayer);
|
|
MOZ_ASSERT(mLayer == mData->GetLayerData(mLayerIndex));
|
|
MOZ_ASSERT(mMetadataIndex == 0 ||
|
|
mMetadataIndex < mLayer->GetScrollMetadataCount());
|
|
}
|
|
|
|
public:
|
|
bool IsValid() const { return mLayer != nullptr; }
|
|
|
|
explicit operator bool() const { return IsValid(); }
|
|
|
|
WebRenderScrollDataWrapper GetLastChild() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (!AtBottomLayer()) {
|
|
// If we're still walking around in the virtual container layers created
|
|
// by the ScrollMetadata array, we just need to update the metadata index
|
|
// and that's it.
|
|
return WebRenderScrollDataWrapper(mUpdater, mData, mLayerIndex,
|
|
mContainingSubtreeLastIndex, mLayer,
|
|
mMetadataIndex - 1);
|
|
}
|
|
|
|
// Otherwise, we need to walk to a different WebRenderLayerScrollData in
|
|
// mData.
|
|
|
|
// Since mData contains the layer in depth-first, last-to-first order,
|
|
// the index after mLayerIndex must be mLayerIndex's last child, if it
|
|
// has any children (indicated by GetDescendantCount() > 0). Furthermore
|
|
// we compute the first index outside the subtree rooted at this node
|
|
// (in |subtreeLastIndex|) and pass that in to the child wrapper to use as
|
|
// its mContainingSubtreeLastIndex.
|
|
if (mLayer->GetDescendantCount() > 0) {
|
|
size_t prevSiblingIndex = mLayerIndex + 1 + mLayer->GetDescendantCount();
|
|
size_t subtreeLastIndex =
|
|
std::min(mContainingSubtreeLastIndex, prevSiblingIndex);
|
|
return WebRenderScrollDataWrapper(mUpdater, mData, mLayerIndex + 1,
|
|
subtreeLastIndex);
|
|
}
|
|
|
|
// We've run out of descendants. But! If the original layer was a RefLayer,
|
|
// then it connects to another layer tree and we need to traverse that too.
|
|
// So return a WebRenderScrollDataWrapper for the root of the child layer
|
|
// tree.
|
|
if (mLayer->GetReferentId()) {
|
|
return WebRenderScrollDataWrapper(
|
|
*mUpdater, mUpdater->GetScrollData(*mLayer->GetReferentId()));
|
|
}
|
|
|
|
return WebRenderScrollDataWrapper(*mUpdater);
|
|
}
|
|
|
|
WebRenderScrollDataWrapper GetPrevSibling() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (!AtTopLayer()) {
|
|
// The virtual container layers don't have siblings
|
|
return WebRenderScrollDataWrapper(*mUpdater);
|
|
}
|
|
|
|
// Skip past the descendants to get to the previous sibling. However, we
|
|
// might be at the last sibling already.
|
|
size_t prevSiblingIndex = mLayerIndex + 1 + mLayer->GetDescendantCount();
|
|
if (prevSiblingIndex < mContainingSubtreeLastIndex) {
|
|
return WebRenderScrollDataWrapper(mUpdater, mData, prevSiblingIndex,
|
|
mContainingSubtreeLastIndex);
|
|
}
|
|
return WebRenderScrollDataWrapper(*mUpdater);
|
|
}
|
|
|
|
const ScrollMetadata& Metadata() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (mMetadataIndex >= mLayer->GetScrollMetadataCount()) {
|
|
return *ScrollMetadata::sNullMetadata;
|
|
}
|
|
return mLayer->GetScrollMetadata(*mData, mMetadataIndex);
|
|
}
|
|
|
|
const FrameMetrics& Metrics() const { return Metadata().GetMetrics(); }
|
|
|
|
AsyncPanZoomController* GetApzc() const { return nullptr; }
|
|
|
|
void SetApzc(AsyncPanZoomController* aApzc) const {}
|
|
|
|
const char* Name() const { return "WebRenderScrollDataWrapper"; }
|
|
|
|
gfx::Matrix4x4 GetTransform() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
// See WebRenderLayerScrollData::Initialize for more context.
|
|
// * The ancestor transform is associated with whichever layer has scroll
|
|
// id matching GetAncestorTransformId().
|
|
// * The resolution is associated with the "topmost" layer.
|
|
// * The transform is associated with the "bottommost" layer.
|
|
// Multiple transforms may apply to the same layer (e.g. if there is only
|
|
// one scrollmetadata on the layer, then it is both "topmost" and
|
|
// "bottommost"), so we may need to combine the transforms.
|
|
|
|
gfx::Matrix4x4 transform;
|
|
// The ancestor transform is usually emitted at the layer with the
|
|
// matching scroll id. However, sometimes the transform ends up on
|
|
// a node with no scroll metadata at all. In such cases we generate
|
|
// a single layer, and the ancestor transform needs to be on that layer,
|
|
// otherwise it will be lost.
|
|
bool emitAncestorTransform =
|
|
!Metrics().IsScrollable() ||
|
|
Metrics().GetScrollId() == mLayer->GetAncestorTransformId();
|
|
if (emitAncestorTransform) {
|
|
transform = mLayer->GetAncestorTransform();
|
|
}
|
|
if (AtTopLayer()) {
|
|
float resolution = mLayer->GetResolution();
|
|
transform =
|
|
transform * gfx::Matrix4x4::Scaling(resolution, resolution, 1.f);
|
|
}
|
|
if (AtBottomLayer()) {
|
|
transform = mLayer->GetTransform() * transform;
|
|
}
|
|
return transform;
|
|
}
|
|
|
|
CSSTransformMatrix GetTransformTyped() const {
|
|
return ViewAs<CSSTransformMatrix>(GetTransform());
|
|
}
|
|
|
|
bool TransformIsPerspective() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
// mLayer->GetTransformIsPerspective() tells us whether
|
|
// mLayer->GetTransform() is a perspective transform. Since
|
|
// mLayer->GetTransform() is only used at the bottom layer, we only
|
|
// need to check GetTransformIsPerspective() at the bottom layer too.
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetTransformIsPerspective();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LayerIntRegion GetVisibleRegion() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetVisibleRegion();
|
|
}
|
|
|
|
return ViewAs<LayerPixel>(
|
|
TransformBy(mLayer->GetTransformTyped(), mLayer->GetVisibleRegion()),
|
|
PixelCastJustification::MovingDownToChildren);
|
|
}
|
|
|
|
LayerIntSize GetRemoteDocumentSize() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (mLayer->GetReferentId().isNothing()) {
|
|
return LayerIntSize();
|
|
}
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetRemoteDocumentSize();
|
|
}
|
|
|
|
return ViewAs<LayerPixel>(mLayer->GetRemoteDocumentSize(),
|
|
PixelCastJustification::MovingDownToChildren);
|
|
}
|
|
|
|
Maybe<LayersId> GetReferentId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetReferentId();
|
|
}
|
|
return Nothing();
|
|
}
|
|
|
|
EventRegionsOverride GetEventRegionsOverride() const {
|
|
MOZ_ASSERT(IsValid());
|
|
// Only ref layers can have an event regions override.
|
|
if (GetReferentId()) {
|
|
return mLayer->GetEventRegionsOverride();
|
|
}
|
|
return EventRegionsOverride::NoOverride;
|
|
}
|
|
|
|
const ScrollbarData& GetScrollbarData() const {
|
|
MOZ_ASSERT(IsValid());
|
|
return mLayer->GetScrollbarData();
|
|
}
|
|
|
|
Maybe<uint64_t> GetScrollbarAnimationId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
return mLayer->GetScrollbarAnimationId();
|
|
}
|
|
|
|
Maybe<uint64_t> GetFixedPositionAnimationId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetFixedPositionAnimationId();
|
|
}
|
|
return Nothing();
|
|
}
|
|
|
|
ScrollableLayerGuid::ViewID GetFixedPositionScrollContainerId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetFixedPositionScrollContainerId();
|
|
}
|
|
return ScrollableLayerGuid::NULL_SCROLL_ID;
|
|
}
|
|
|
|
SideBits GetFixedPositionSides() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetFixedPositionSides();
|
|
}
|
|
return SideBits::eNone;
|
|
}
|
|
|
|
ScrollableLayerGuid::ViewID GetStickyScrollContainerId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetStickyPositionScrollContainerId();
|
|
}
|
|
return ScrollableLayerGuid::NULL_SCROLL_ID;
|
|
}
|
|
|
|
const LayerRectAbsolute& GetStickyScrollRangeOuter() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetStickyScrollRangeOuter();
|
|
}
|
|
|
|
static const LayerRectAbsolute empty;
|
|
return empty;
|
|
}
|
|
|
|
const LayerRectAbsolute& GetStickyScrollRangeInner() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetStickyScrollRangeInner();
|
|
}
|
|
|
|
static const LayerRectAbsolute empty;
|
|
return empty;
|
|
}
|
|
|
|
Maybe<uint64_t> GetStickyPositionAnimationId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
|
|
if (AtBottomLayer()) {
|
|
return mLayer->GetStickyPositionAnimationId();
|
|
}
|
|
return Nothing();
|
|
}
|
|
|
|
Maybe<uint64_t> GetZoomAnimationId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
return mLayer->GetZoomAnimationId();
|
|
}
|
|
|
|
Maybe<ScrollableLayerGuid::ViewID> GetAsyncZoomContainerId() const {
|
|
MOZ_ASSERT(IsValid());
|
|
return mLayer->GetAsyncZoomContainerId();
|
|
}
|
|
|
|
// Expose an opaque pointer to the layer. Mostly used for printf
|
|
// purposes. This is not intended to be a general-purpose accessor
|
|
// for the underlying layer.
|
|
const void* GetLayer() const {
|
|
MOZ_ASSERT(IsValid());
|
|
return mLayer;
|
|
}
|
|
|
|
private:
|
|
bool AtBottomLayer() const { return mMetadataIndex == 0; }
|
|
|
|
bool AtTopLayer() const {
|
|
return mLayer->GetScrollMetadataCount() == 0 ||
|
|
mMetadataIndex == mLayer->GetScrollMetadataCount() - 1;
|
|
}
|
|
|
|
private:
|
|
const APZUpdater* mUpdater;
|
|
const WebRenderScrollData* mData;
|
|
// The index (in mData->mLayerScrollData) of the WebRenderLayerScrollData this
|
|
// wrapper is pointing to.
|
|
size_t mLayerIndex;
|
|
// The upper bound on the set of valid indices inside the subtree rooted at
|
|
// the parent of this "layer". That is, any layer index |i| in the range
|
|
// mLayerIndex <= i < mContainingSubtreeLastIndex is guaranteed to point to
|
|
// a layer that is a descendant of "parent", where "parent" is the parent
|
|
// layer of the layer at mLayerIndex. This is needed in order to implement
|
|
// GetPrevSibling() correctly.
|
|
size_t mContainingSubtreeLastIndex;
|
|
// The WebRenderLayerScrollData this wrapper is pointing to.
|
|
const WebRenderLayerScrollData* mLayer;
|
|
// The index of the scroll metadata within mLayer that this wrapper is
|
|
// pointing to.
|
|
uint32_t mMetadataIndex;
|
|
};
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|
|
|
|
#endif /* GFX_WEBRENDERSCROLLDATAWRAPPER_H */
|