Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#ifndef mozilla_layers_HitTestingTreeNode_h
#define mozilla_layers_HitTestingTreeNode_h
#include "mozilla/gfx/CompositorHitTestInfo.h"
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
#include "mozilla/layers/LayersTypes.h" // for EventRegions
#include "mozilla/layers/ScrollableLayerGuid.h" // for ScrollableLayerGuid
#include "mozilla/layers/ScrollbarData.h" // for ScrollbarData
#include "mozilla/Maybe.h" // for Maybe
#include "mozilla/RecursiveMutex.h" // for RecursiveMutexAutoLock
#include "mozilla/RefPtr.h" // for nsRefPtr
namespace mozilla {
namespace layers {
class AsyncDragMetrics;
class AsyncPanZoomController;
/**
* This class represents a node in a tree that is used by the APZCTreeManager
* to do hit testing. The tree is roughly a copy of the layer tree, but will
* contain multiple nodes in cases where the layer has multiple FrameMetrics.
* In other words, the structure of this tree should be identical to the
* WebRenderScrollDataWrapper tree (see documentation in
* WebRenderScrollDataWrapper.h).
*
* Not all HitTestingTreeNode instances will have an APZC associated with them;
* only HitTestingTreeNodes that correspond to layers with scrollable metrics
* have APZCs.
* Multiple HitTestingTreeNode instances may share the same underlying APZC
* instance if the layers they represent share the same scrollable metrics (i.e.
* are part of the same animated geometry root). If this happens, exactly one of
* the HitTestingTreeNode instances will be designated as the "primary holder"
* of the APZC. When this primary holder is destroyed, it will destroy the APZC
* along with it; in contrast, destroying non-primary-holder nodes will not
* destroy the APZC.
* Code should not make assumptions about which of the nodes will be the
* primary holder, only that that there will be exactly one for each APZC in
* the tree.
*
* The reason this tree exists at all is so that we can do hit-testing on the
* thread that we receive input on (referred to the as the controller thread in
* APZ terminology), which may be different from the compositor thread.
* Accessing the compositor layer tree can only be done on the compositor
* thread, and so it is simpler to make a copy of the hit-testing related
* properties into a separate tree.
*
* The tree pointers on the node (mLastChild, etc.) can only be manipulated
* while holding the APZ tree lock. Any code that wishes to use a
* HitTestingTreeNode outside of holding the tree lock should do so by using
* the HitTestingTreeNodeAutoLock wrapper, which prevents the node from
* being recycled (and also holds a RefPtr to the node to prevent it from
* getting freed).
*/
class HitTestingTreeNode {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HitTestingTreeNode);
private:
~HitTestingTreeNode();
public:
HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder,
LayersId aLayersId);
void RecycleWith(const RecursiveMutexAutoLock& aProofOfTreeLock,
AsyncPanZoomController* aApzc, LayersId aLayersId);
// Clears the tree pointers on the node, thereby breaking RefPtr cycles. This
// can trigger free'ing of this and other HitTestingTreeNode instances.
void Destroy();
// Returns true if and only if the node is available for recycling as part
// of a hit-testing tree update. Note that this node can have Destroy() called
// on it whether or not it is recyclable.
bool IsRecyclable(const RecursiveMutexAutoLock& aProofOfTreeLock);
/* Tree construction methods */
void SetLastChild(HitTestingTreeNode* aChild);
void SetPrevSibling(HitTestingTreeNode* aSibling);
void MakeRoot();
/* Tree walking methods. GetFirstChild is O(n) in the number of children. The
* other tree walking methods are all O(1). */
HitTestingTreeNode* GetFirstChild() const;
HitTestingTreeNode* GetLastChild() const;
HitTestingTreeNode* GetPrevSibling() const;
HitTestingTreeNode* GetParent() const;
bool IsAncestorOf(const HitTestingTreeNode* aOther) const;
/* APZC related methods */
AsyncPanZoomController* GetApzc() const;
AsyncPanZoomController* GetNearestContainingApzc() const;
bool IsPrimaryHolder() const;
LayersId GetLayersId() const;
/* Hit test related methods */
void SetHitTestData(
const LayerIntRect& aVisibleRect, const LayerIntSize& aRemoteDocumentSize,
const CSSTransformMatrix& aTransform,
const EventRegionsOverride& aOverride,
const Maybe<ScrollableLayerGuid::ViewID>& aAsyncZoomContainerId);
/* Scrollbar info */
void SetScrollbarData(const Maybe<uint64_t>& aScrollbarAnimationId,
const ScrollbarData& aScrollbarData);
bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics,
LayersId aLayersId) const;
bool IsScrollbarNode() const; // Scroll thumb or scrollbar container layer.
bool IsScrollbarContainerNode() const; // Scrollbar container layer.
// This can only be called if IsScrollbarNode() is true
ScrollDirection GetScrollbarDirection() const;
bool IsScrollThumbNode() const; // Scroll thumb container layer.
ScrollableLayerGuid::ViewID GetScrollTargetId() const;
const ScrollbarData& GetScrollbarData() const;
Maybe<uint64_t> GetScrollbarAnimationId() const;
/* Fixed pos info */
void SetFixedPosData(ScrollableLayerGuid::ViewID aFixedPosTarget,
SideBits aFixedPosSides,
const Maybe<uint64_t>& aFixedPositionAnimationId);
ScrollableLayerGuid::ViewID GetFixedPosTarget() const;
SideBits GetFixedPosSides() const;
Maybe<uint64_t> GetFixedPositionAnimationId() const;
/* Sticky pos info */
void SetStickyPosData(ScrollableLayerGuid::ViewID aStickyPosTarget,
const LayerRectAbsolute& aScrollRangeOuter,
const LayerRectAbsolute& aScrollRangeInner,
const Maybe<uint64_t>& aStickyPositionAnimationId);
ScrollableLayerGuid::ViewID GetStickyPosTarget() const;
const LayerRectAbsolute& GetStickyScrollRangeOuter() const;
const LayerRectAbsolute& GetStickyScrollRangeInner() const;
Maybe<uint64_t> GetStickyPositionAnimationId() const;
/* Returns the mOverride flag. */
EventRegionsOverride GetEventRegionsOverride() const;
const CSSTransformMatrix& GetTransform() const;
/* This is similar to APZCTreeManager::GetApzcToGeckoTransform but without
* the async bits. It's used on the main-thread for transforming coordinates
* across a BrowserParent/BrowserChild interface.
* |aRemoteLayersId| is the LayersId of the remote subtree for which this
* transform will be used. */
LayerToScreenMatrix4x4 GetTransformToGecko(LayersId aRemoteLayersId) const;
const LayerIntRect& GetVisibleRect() const;
/* Returns the screen coordinate rectangle of remote iframe corresponding to
* this node. The rectangle is the result of clipped by ancestor async
* scrolling. */
ScreenRect GetRemoteDocumentScreenRect(
LayersId aRemoteDocumentLayersId) const;
Maybe<ScrollableLayerGuid::ViewID> GetAsyncZoomContainerId() const;
/* Debug helpers */
void Dump(const char* aPrefix = "") const;
private:
friend class HitTestingTreeNodeAutoLock;
// Functions that are private but called from HitTestingTreeNodeAutoLock
void Lock(const RecursiveMutexAutoLock& aProofOfTreeLock);
void Unlock(const RecursiveMutexAutoLock& aProofOfTreeLock);
void SetApzcParent(AsyncPanZoomController* aApzc);
RefPtr<HitTestingTreeNode> mLastChild;
RefPtr<HitTestingTreeNode> mPrevSibling;
RefPtr<HitTestingTreeNode> mParent;
RefPtr<AsyncPanZoomController> mApzc;
bool mIsPrimaryApzcHolder;
int mLockCount;
LayersId mLayersId;
// This is only set for HTTNs where IsScrollThumbNode() returns true. It holds
// the animation id that we use to move the thumb node to reflect async
// scrolling.
Maybe<uint64_t> mScrollbarAnimationId;
// This is set for scrollbar Container and Thumb layers.
ScrollbarData mScrollbarData;
// This holds the animation id that we use to adjust fixed position content
// for the toolbar.
Maybe<uint64_t> mFixedPositionAnimationId;
ScrollableLayerGuid::ViewID mFixedPosTarget;
SideBits mFixedPosSides;
ScrollableLayerGuid::ViewID mStickyPosTarget;
LayerRectAbsolute mStickyScrollRangeOuter;
LayerRectAbsolute mStickyScrollRangeInner;
// This holds the animation id that we use to adjust sticky position content
// for the toolbar.
Maybe<uint64_t> mStickyPositionAnimationId;
LayerIntRect mVisibleRect;
/* The size of remote iframe on the corresponding layer coordinate.
* It's empty if this node is not for remote iframe. */
LayerIntSize mRemoteDocumentSize;
/* This is the transform from layer L. This does NOT include any async
* transforms. */
CSSTransformMatrix mTransform;
/* If the layer is the async zoom container layer then this will hold the id.
*/
Maybe<ScrollableLayerGuid::ViewID> mAsyncZoomContainerId;
/* Indicates whether or not the event regions on this node need to be
* overridden in a certain way. */
EventRegionsOverride mOverride;
};
/**
* A class that allows safe usage of a HitTestingTreeNode outside of the APZ
* tree lock. In general, this class should be Initialize()'d inside the tree
* lock (enforced by the proof-of-lock to Initialize), and then can be returned
* to a scope outside the tree lock and used safely. Upon destruction or
* Clear() being called, it unlocks the underlying node at which point it can
* be recycled or freed.
*/
class HitTestingTreeNodeAutoLock final {
public:
HitTestingTreeNodeAutoLock();
~HitTestingTreeNodeAutoLock();
// Make it move-only. Note that the default implementations of the move
// constructor and assignment operator are correct: they'll call the
// move constructor of mNode, which will null out mNode on the moved-from
// object, and Clear() will early-exit when the moved-from object's
// destructor is called.
HitTestingTreeNodeAutoLock(HitTestingTreeNodeAutoLock&&) = default;
HitTestingTreeNodeAutoLock& operator=(HitTestingTreeNodeAutoLock&&) = default;
void Initialize(const RecursiveMutexAutoLock& aProofOfTreeLock,
already_AddRefed<HitTestingTreeNode> aNode,
RecursiveMutex& aTreeMutex);
void Clear();
// Convenience operators to simplify the using code.
explicit operator bool() const { return !!mNode; }
bool operator!() const { return !mNode; }
HitTestingTreeNode* operator->() const { return mNode.get(); }
// Allow getting back a raw pointer to the node, but only inside the scope
// of the tree lock. The caller is responsible for ensuring that they do not
// use the raw pointer outside that scope.
HitTestingTreeNode* Get(
mozilla::RecursiveMutexAutoLock& aProofOfTreeLock) const {
return mNode.get();
}
private:
RefPtr<HitTestingTreeNode> mNode;
RecursiveMutex* mTreeMutex;
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_HitTestingTreeNode_h