diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/wr/WebRenderScrollDataWrapper.h | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/gfx/layers/wr/WebRenderScrollDataWrapper.h b/gfx/layers/wr/WebRenderScrollDataWrapper.h new file mode 100644 index 0000000000..9cbec76371 --- /dev/null +++ b/gfx/layers/wr/WebRenderScrollDataWrapper.h @@ -0,0 +1,542 @@ +/* -*- 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(); + // TODO(botond): Replace the min() with just prevSiblingIndex (which + // should be <= mContainingSubtreeLastIndex). + MOZ_ASSERT(prevSiblingIndex <= mContainingSubtreeLastIndex); + 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; + } + + template <int Level> + size_t Dump(gfx::TreeLog<Level>& aOut) const { + std::string result = "(invalid)"; + if (!IsValid()) { + aOut << result; + return result.length(); + } + if (AtBottomLayer()) { + if (mData != nullptr) { + const WebRenderLayerScrollData* layerData = + mData->GetLayerData(mLayerIndex); + if (layerData != nullptr) { + std::stringstream ss; + layerData->Dump(ss, *mData); + result = ss.str(); + aOut << result; + return result.length(); + } + } + } + return 0; + } + + 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 */ |