Mypal/gfx/layers/LayerMetricsWrapper.h

517 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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_LAYERMETRICSWRAPPER_H
#define GFX_LAYERMETRICSWRAPPER_H
#include "Layers.h"
#include "UnitTransforms.h"
namespace mozilla {
namespace layers {
/**
* A wrapper class around a target Layer with that allows user code to
* walk through the FrameMetrics objects on the layer the same way it
* would walk through a ContainerLayer hierarchy. Consider the following
* layer tree:
*
* +---+
* | A |
* +---+
* / | \
* / | \
* / | \
* +---+ +-----+ +---+
* | B | | C | | D |
* +---+ +-----+ +---+
* | FMn |
* | . |
* | . |
* | . |
* | FM1 |
* | FM0 |
* +-----+
* / \
* / \
* +---+ +---+
* | 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 FrameMetrics, labelled
* FM0...FMn. FM0 is the FrameMetrics you get by calling c->GetFrameMetrics(0)
* and FMn is the FrameMetrics you can obtain by calling
* c->GetFrameMetrics(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 container
* layers C1...Cn, where C1 has FrameMetrics FM1 and Cn has FrameMetrics Fn.
* Although in this example C (in the first layer tree) and C0 (in the second
* layer tree) are both ContainerLayers (because they have children), they
* do not have to be. They may just be PaintedLayers or ColorLayers, for example,
* which do not have any children. However, the type of C will always be the
* same as the type of C0.
*
* The LayerMetricsWrapper 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 FrameMetrics objects directly, it can use a
* LayerMetricsWrapper to encapsulate that aspect of the layer tree and just
* walk the tree as if it were a stack of ContainerLayers.
*
* The functions on this class do different things depending on which
* simulated ContainerLayer is being wrapped. For example, if the
* LayerMetricsWrapper is pretending to be C0, the GetNextSibling() function
* will return null even though the underlying layer C does actually have
* a next sibling. The LayerMetricsWrapper pretending to be Cn will return
* D as the next 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 LayerMetricsWrapper is simulating the topmost or bottommost
* layer, as those will have special behaviour.
*
* It is possible to wrap a nullptr in a LayerMetricsWrapper, in which case
* the IsValid() function will return false. This is required to allow
* LayerMetricsWrapper 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.
*/
class MOZ_STACK_CLASS LayerMetricsWrapper {
public:
enum StartAt {
TOP,
BOTTOM,
};
LayerMetricsWrapper()
: mLayer(nullptr)
, mIndex(0)
{
}
explicit LayerMetricsWrapper(Layer* aRoot, StartAt aStart = StartAt::TOP)
: mLayer(aRoot)
, mIndex(0)
{
if (!mLayer) {
return;
}
switch (aStart) {
case StartAt::TOP:
mIndex = mLayer->GetScrollMetadataCount();
if (mIndex > 0) {
mIndex--;
}
break;
case StartAt::BOTTOM:
mIndex = 0;
break;
default:
MOZ_ASSERT_UNREACHABLE("Unknown startAt value");
break;
}
}
explicit LayerMetricsWrapper(Layer* aLayer, uint32_t aMetricsIndex)
: mLayer(aLayer)
, mIndex(aMetricsIndex)
{
MOZ_ASSERT(mLayer);
MOZ_ASSERT(mIndex == 0 || mIndex < mLayer->GetScrollMetadataCount());
}
bool IsValid() const
{
return mLayer != nullptr;
}
explicit operator bool() const
{
return IsValid();
}
bool IsScrollInfoLayer() const
{
MOZ_ASSERT(IsValid());
// If we are not at the bottommost layer then it's
// a stack of container layers all the way down to
// mLayer, which we can ignore. We only care about
// non-container descendants.
return Metrics().IsScrollable()
&& mLayer->AsContainerLayer()
&& !mLayer->GetFirstChild();
}
LayerMetricsWrapper GetParent() const
{
MOZ_ASSERT(IsValid());
if (!AtTopLayer()) {
return LayerMetricsWrapper(mLayer, mIndex + 1);
}
if (mLayer->GetParent()) {
return LayerMetricsWrapper(mLayer->GetParent(), StartAt::BOTTOM);
}
return LayerMetricsWrapper(nullptr);
}
LayerMetricsWrapper GetFirstChild() const
{
MOZ_ASSERT(IsValid());
if (!AtBottomLayer()) {
return LayerMetricsWrapper(mLayer, mIndex - 1);
}
return LayerMetricsWrapper(mLayer->GetFirstChild());
}
LayerMetricsWrapper GetLastChild() const
{
MOZ_ASSERT(IsValid());
if (!AtBottomLayer()) {
return LayerMetricsWrapper(mLayer, mIndex - 1);
}
return LayerMetricsWrapper(mLayer->GetLastChild());
}
LayerMetricsWrapper GetPrevSibling() const
{
MOZ_ASSERT(IsValid());
if (AtTopLayer()) {
return LayerMetricsWrapper(mLayer->GetPrevSibling());
}
return LayerMetricsWrapper(nullptr);
}
LayerMetricsWrapper GetNextSibling() const
{
MOZ_ASSERT(IsValid());
if (AtTopLayer()) {
return LayerMetricsWrapper(mLayer->GetNextSibling());
}
return LayerMetricsWrapper(nullptr);
}
const ScrollMetadata& Metadata() const
{
MOZ_ASSERT(IsValid());
if (mIndex >= mLayer->GetScrollMetadataCount()) {
return *ScrollMetadata::sNullMetadata;
}
return mLayer->GetScrollMetadata(mIndex);
}
const FrameMetrics& Metrics() const
{
return Metadata().GetMetrics();
}
AsyncPanZoomController* GetApzc() const
{
MOZ_ASSERT(IsValid());
if (mIndex >= mLayer->GetScrollMetadataCount()) {
return nullptr;
}
return mLayer->GetAsyncPanZoomController(mIndex);
}
void SetApzc(AsyncPanZoomController* aApzc) const
{
MOZ_ASSERT(IsValid());
if (mLayer->GetScrollMetadataCount() == 0) {
MOZ_ASSERT(mIndex == 0);
MOZ_ASSERT(aApzc == nullptr);
return;
}
MOZ_ASSERT(mIndex < mLayer->GetScrollMetadataCount());
mLayer->SetAsyncPanZoomController(mIndex, aApzc);
}
const char* Name() const
{
MOZ_ASSERT(IsValid());
if (AtBottomLayer()) {
return mLayer->Name();
}
return "DummyContainerLayer";
}
LayerManager* Manager() const
{
MOZ_ASSERT(IsValid());
return mLayer->Manager();
}
gfx::Matrix4x4 GetTransform() const
{
MOZ_ASSERT(IsValid());
if (AtBottomLayer()) {
return mLayer->GetTransform();
}
return gfx::Matrix4x4();
}
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;
}
EventRegions GetEventRegions() const
{
MOZ_ASSERT(IsValid());
if (AtBottomLayer()) {
return mLayer->GetEventRegions();
}
return EventRegions();
}
bool HasTransformAnimation() const
{
MOZ_ASSERT(IsValid());
if (AtBottomLayer()) {
return mLayer->HasTransformAnimation();
}
return false;
}
RefLayer* AsRefLayer() const
{
MOZ_ASSERT(IsValid());
if (AtBottomLayer()) {
return mLayer->AsRefLayer();
}
return nullptr;
}
LayerIntRegion GetVisibleRegion() const
{
MOZ_ASSERT(IsValid());
if (AtBottomLayer()) {
return mLayer->GetVisibleRegion();
}
LayerIntRegion region = mLayer->GetVisibleRegion();
region.Transform(mLayer->GetTransform());
return region;
}
Maybe<ParentLayerIntRect> GetClipRect() const
{
MOZ_ASSERT(IsValid());
Maybe<ParentLayerIntRect> result;
// The layer can have a clip rect and a scrolled clip, which are considered
// to apply only to the bottommost LayerMetricsWrapper.
// TODO: These actually apply in a different coordinate space than the
// scroll clip of the bottommost metrics, so we shouldn't be intersecting
// them with the scroll clip; bug 1269537 tracks fixing this.
if (AtBottomLayer()) {
result = mLayer->GetClipRect();
result = IntersectMaybeRects(result, mLayer->GetScrolledClipRect());
}
// The scroll metadata can have a clip rect as well.
result = IntersectMaybeRects(result, Metadata().GetClipRect());
return result;
}
float GetPresShellResolution() const
{
MOZ_ASSERT(IsValid());
if (AtTopLayer() && mLayer->AsContainerLayer()) {
return mLayer->AsContainerLayer()->GetPresShellResolution();
}
return 1.0f;
}
EventRegionsOverride GetEventRegionsOverride() const
{
MOZ_ASSERT(IsValid());
if (mLayer->AsContainerLayer()) {
return mLayer->AsContainerLayer()->GetEventRegionsOverride();
}
return EventRegionsOverride::NoOverride;
}
Layer::ScrollDirection GetScrollbarDirection() const
{
MOZ_ASSERT(IsValid());
return mLayer->GetScrollbarDirection();
}
FrameMetrics::ViewID GetScrollbarTargetContainerId() const
{
MOZ_ASSERT(IsValid());
return mLayer->GetScrollbarTargetContainerId();
}
int32_t GetScrollbarSize() const
{
if (GetScrollbarDirection() == Layer::VERTICAL) {
return mLayer->GetVisibleRegion().GetBounds().height;
} else {
return mLayer->GetVisibleRegion().GetBounds().width;
}
}
bool IsScrollbarContainer() const
{
MOZ_ASSERT(IsValid());
return mLayer->IsScrollbarContainer();
}
FrameMetrics::ViewID GetFixedPositionScrollContainerId() const
{
MOZ_ASSERT(IsValid());
return mLayer->GetFixedPositionScrollContainerId();
}
// 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 (void*)mLayer;
}
bool operator==(const LayerMetricsWrapper& aOther) const
{
return mLayer == aOther.mLayer
&& mIndex == aOther.mIndex;
}
bool operator!=(const LayerMetricsWrapper& aOther) const
{
return !(*this == aOther);
}
static const FrameMetrics& TopmostScrollableMetrics(Layer* aLayer)
{
for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) {
if (aLayer->GetFrameMetrics(i - 1).IsScrollable()) {
return aLayer->GetFrameMetrics(i - 1);
}
}
return ScrollMetadata::sNullMetadata->GetMetrics();
}
static const FrameMetrics& BottommostScrollableMetrics(Layer* aLayer)
{
for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
if (aLayer->GetFrameMetrics(i).IsScrollable()) {
return aLayer->GetFrameMetrics(i);
}
}
return ScrollMetadata::sNullMetadata->GetMetrics();
}
static const FrameMetrics& BottommostMetrics(Layer* aLayer)
{
if (aLayer->GetScrollMetadataCount() > 0) {
return aLayer->GetFrameMetrics(0);
}
return ScrollMetadata::sNullMetadata->GetMetrics();
}
private:
bool AtBottomLayer() const
{
return mIndex == 0;
}
bool AtTopLayer() const
{
return mLayer->GetScrollMetadataCount() == 0 || mIndex == mLayer->GetScrollMetadataCount() - 1;
}
private:
Layer* mLayer;
uint32_t mIndex;
};
} // namespace layers
} // namespace mozilla
#endif /* GFX_LAYERMETRICSWRAPPER_H */