Source code
Revision control
Copy as Markdown
Other Tools
/* 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
#include "HitTestingTreeNode.h"
#include <stack>
#include "AsyncPanZoomController.h" // for AsyncPanZoomController
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/gfx/Point.h" // for Point4D
#include "mozilla/layers/APZUtils.h" // for AsyncTransform, CompleteAsyncTransform
#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
#include "mozilla/ToString.h" // for ToString
#include "nsPrintfCString.h" // for nsPrintfCString
#include "UnitTransforms.h" // for ViewAs
static mozilla::LazyLogModule sApzMgrLog("apz.manager");
namespace mozilla {
namespace layers {
using gfx::CompositorHitTestInfo;
HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
bool aIsPrimaryHolder,
LayersId aLayersId)
: mApzc(aApzc),
mIsPrimaryApzcHolder(aIsPrimaryHolder),
mLockCount(0),
mLayersId(aLayersId),
mFixedPosTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
mStickyPosTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
mOverride(EventRegionsOverride::NoOverride) {
if (mIsPrimaryApzcHolder) {
MOZ_ASSERT(mApzc);
}
MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
}
void HitTestingTreeNode::RecycleWith(
const RecursiveMutexAutoLock& aProofOfTreeLock,
AsyncPanZoomController* aApzc, LayersId aLayersId) {
MOZ_ASSERT(IsRecyclable(aProofOfTreeLock));
Destroy(); // clear out tree pointers
mApzc = aApzc;
mLayersId = aLayersId;
MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
// The caller is expected to call appropriate setters (SetHitTestData,
// SetScrollbarData, SetFixedPosData, SetStickyPosData, etc.) to repopulate
// all the data fields before using this node for "real work". Otherwise
// those data fields may contain stale information from the previous use
// of this node object.
}
HitTestingTreeNode::~HitTestingTreeNode() = default;
void HitTestingTreeNode::Destroy() {
// This runs on the updater thread, it's not worth passing around extra raw
// pointers just to assert it.
mPrevSibling = nullptr;
mLastChild = nullptr;
mParent = nullptr;
if (mApzc) {
if (mIsPrimaryApzcHolder) {
mApzc->Destroy();
}
mApzc = nullptr;
}
}
bool HitTestingTreeNode::IsRecyclable(
const RecursiveMutexAutoLock& aProofOfTreeLock) {
return !(IsPrimaryHolder() || (mLockCount > 0));
}
void HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild) {
mLastChild = aChild;
if (aChild) {
aChild->mParent = this;
if (aChild->GetApzc()) {
AsyncPanZoomController* parent = GetNearestContainingApzc();
// We assume that HitTestingTreeNodes with an ancestor/descendant
// relationship cannot both point to the same APZC instance. This
// assertion only covers a subset of cases in which that might occur,
// but it's better than nothing.
MOZ_ASSERT(aChild->GetApzc() != parent);
aChild->SetApzcParent(parent);
}
}
}
void HitTestingTreeNode::SetScrollbarData(
const Maybe<uint64_t>& aScrollbarAnimationId,
const ScrollbarData& aScrollbarData) {
mScrollbarAnimationId = aScrollbarAnimationId;
mScrollbarData = aScrollbarData;
}
bool HitTestingTreeNode::MatchesScrollDragMetrics(
const AsyncDragMetrics& aDragMetrics, LayersId aLayersId) const {
return IsScrollThumbNode() && mLayersId == aLayersId &&
mScrollbarData.mDirection == aDragMetrics.mDirection &&
mScrollbarData.mTargetViewId == aDragMetrics.mViewId;
}
bool HitTestingTreeNode::IsScrollThumbNode() const {
return mScrollbarData.mScrollbarLayerType ==
layers::ScrollbarLayerType::Thumb;
}
bool HitTestingTreeNode::IsScrollbarNode() const {
return mScrollbarData.mScrollbarLayerType != layers::ScrollbarLayerType::None;
}
bool HitTestingTreeNode::IsScrollbarContainerNode() const {
return mScrollbarData.mScrollbarLayerType ==
layers::ScrollbarLayerType::Container;
}
ScrollDirection HitTestingTreeNode::GetScrollbarDirection() const {
MOZ_ASSERT(IsScrollbarNode());
MOZ_ASSERT(mScrollbarData.mDirection.isSome());
return *mScrollbarData.mDirection;
}
ScrollableLayerGuid::ViewID HitTestingTreeNode::GetScrollTargetId() const {
return mScrollbarData.mTargetViewId;
}
Maybe<uint64_t> HitTestingTreeNode::GetScrollbarAnimationId() const {
return mScrollbarAnimationId;
}
const ScrollbarData& HitTestingTreeNode::GetScrollbarData() const {
return mScrollbarData;
}
void HitTestingTreeNode::SetFixedPosData(
ScrollableLayerGuid::ViewID aFixedPosTarget, SideBits aFixedPosSides,
const Maybe<uint64_t>& aFixedPositionAnimationId) {
mFixedPosTarget = aFixedPosTarget;
mFixedPosSides = aFixedPosSides;
mFixedPositionAnimationId = aFixedPositionAnimationId;
}
ScrollableLayerGuid::ViewID HitTestingTreeNode::GetFixedPosTarget() const {
return mFixedPosTarget;
}
SideBits HitTestingTreeNode::GetFixedPosSides() const { return mFixedPosSides; }
Maybe<uint64_t> HitTestingTreeNode::GetFixedPositionAnimationId() const {
return mFixedPositionAnimationId;
}
void HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling) {
mPrevSibling = aSibling;
if (aSibling) {
aSibling->mParent = mParent;
if (aSibling->GetApzc()) {
AsyncPanZoomController* parent =
mParent ? mParent->GetNearestContainingApzc() : nullptr;
aSibling->SetApzcParent(parent);
}
}
}
void HitTestingTreeNode::SetStickyPosData(
ScrollableLayerGuid::ViewID aStickyPosTarget,
const LayerRectAbsolute& aScrollRangeOuter,
const LayerRectAbsolute& aScrollRangeInner,
const Maybe<uint64_t>& aStickyPositionAnimationId) {
mStickyPosTarget = aStickyPosTarget;
mStickyScrollRangeOuter = aScrollRangeOuter;
mStickyScrollRangeInner = aScrollRangeInner;
mStickyPositionAnimationId = aStickyPositionAnimationId;
}
ScrollableLayerGuid::ViewID HitTestingTreeNode::GetStickyPosTarget() const {
return mStickyPosTarget;
}
const LayerRectAbsolute& HitTestingTreeNode::GetStickyScrollRangeOuter() const {
return mStickyScrollRangeOuter;
}
const LayerRectAbsolute& HitTestingTreeNode::GetStickyScrollRangeInner() const {
return mStickyScrollRangeInner;
}
Maybe<uint64_t> HitTestingTreeNode::GetStickyPositionAnimationId() const {
return mStickyPositionAnimationId;
}
void HitTestingTreeNode::MakeRoot() {
mParent = nullptr;
if (GetApzc()) {
SetApzcParent(nullptr);
}
}
HitTestingTreeNode* HitTestingTreeNode::GetFirstChild() const {
HitTestingTreeNode* child = GetLastChild();
while (child && child->GetPrevSibling()) {
child = child->GetPrevSibling();
}
return child;
}
HitTestingTreeNode* HitTestingTreeNode::GetLastChild() const {
return mLastChild;
}
HitTestingTreeNode* HitTestingTreeNode::GetPrevSibling() const {
return mPrevSibling;
}
HitTestingTreeNode* HitTestingTreeNode::GetParent() const { return mParent; }
bool HitTestingTreeNode::IsAncestorOf(const HitTestingTreeNode* aOther) const {
for (const HitTestingTreeNode* cur = aOther; cur; cur = cur->GetParent()) {
if (cur == this) {
return true;
}
}
return false;
}
AsyncPanZoomController* HitTestingTreeNode::GetApzc() const { return mApzc; }
AsyncPanZoomController* HitTestingTreeNode::GetNearestContainingApzc() const {
for (const HitTestingTreeNode* n = this; n; n = n->GetParent()) {
if (n->GetApzc()) {
return n->GetApzc();
}
}
return nullptr;
}
bool HitTestingTreeNode::IsPrimaryHolder() const {
return mIsPrimaryApzcHolder;
}
LayersId HitTestingTreeNode::GetLayersId() const { return mLayersId; }
void HitTestingTreeNode::SetHitTestData(
const LayerIntRect& aVisibleRect, const LayerIntSize& aRemoteDocumentSize,
const CSSTransformMatrix& aTransform, const EventRegionsOverride& aOverride,
const Maybe<ScrollableLayerGuid::ViewID>& aAsyncZoomContainerId) {
mVisibleRect = aVisibleRect;
mRemoteDocumentSize = aRemoteDocumentSize;
mTransform = aTransform;
mOverride = aOverride;
mAsyncZoomContainerId = aAsyncZoomContainerId;
}
EventRegionsOverride HitTestingTreeNode::GetEventRegionsOverride() const {
return mOverride;
}
const CSSTransformMatrix& HitTestingTreeNode::GetTransform() const {
return mTransform;
}
LayerToScreenMatrix4x4 HitTestingTreeNode::GetTransformToGecko(
LayersId aRemoteLayersId) const {
if (mParent) {
LayerToParentLayerMatrix4x4 thisToParent =
mTransform * AsyncTransformMatrix();
if (mApzc) {
thisToParent =
thisToParent * ViewAs<AsyncTransformComponentMatrix>(
mApzc->GetTransformToLastDispatchedPaint(
LayoutAndVisual, aRemoteLayersId));
}
ParentLayerToScreenMatrix4x4 parentToRoot =
ViewAs<ParentLayerToScreenMatrix4x4>(
mParent->GetTransformToGecko(aRemoteLayersId),
PixelCastJustification::MovingDownToChildren);
return thisToParent * parentToRoot;
}
return ViewAs<LayerToScreenMatrix4x4>(
mTransform * AsyncTransformMatrix(),
PixelCastJustification::ScreenIsParentLayerForRoot);
}
const LayerIntRect& HitTestingTreeNode::GetVisibleRect() const {
return mVisibleRect;
}
ScreenRect HitTestingTreeNode::GetRemoteDocumentScreenRect(
LayersId aRemoteDocumentLayersId) const {
ScreenRect result = TransformBy(
GetTransformToGecko(aRemoteDocumentLayersId),
IntRectToRect(LayerIntRect(LayerIntPoint(), mRemoteDocumentSize)));
for (const HitTestingTreeNode* node = this; node; node = node->GetParent()) {
if (!node->GetApzc()) {
continue;
}
ParentLayerRect compositionBounds = node->GetApzc()->GetCompositionBounds();
if (compositionBounds.IsEmpty()) {
return ScreenRect();
}
ScreenRect scrollPortOnScreenCoordinate = TransformBy(
node->GetParent()
? node->GetParent()->GetTransformToGecko(node->GetLayersId())
: LayerToScreenMatrix4x4(),
ViewAs<LayerPixel>(compositionBounds,
PixelCastJustification::MovingDownToChildren));
if (scrollPortOnScreenCoordinate.IsEmpty()) {
return ScreenRect();
}
result = result.Intersect(scrollPortOnScreenCoordinate);
if (result.IsEmpty()) {
return ScreenRect();
}
}
return result;
}
Maybe<ScrollableLayerGuid::ViewID> HitTestingTreeNode::GetAsyncZoomContainerId()
const {
return mAsyncZoomContainerId;
}
void HitTestingTreeNode::Dump(const char* aPrefix) const {
MOZ_LOG(
sApzMgrLog, LogLevel::Debug,
("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%s t=(%s) "
"%s%s\n",
aPrefix, this, mApzc.get(),
mApzc ? ToString(mApzc->GetGuid()).c_str()
: nsPrintfCString("l=0x%" PRIx64, uint64_t(mLayersId)).get(),
(mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc "
: "",
(mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "",
(mFixedPosTarget != ScrollableLayerGuid::NULL_SCROLL_ID)
? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get()
: "",
ToString(mTransform).c_str(),
mScrollbarData.mDirection.isSome() ? " scrollbar" : "",
IsScrollThumbNode() ? " scrollthumb" : ""));
if (!mLastChild) {
return;
}
// Dump the children in order from first child to last child
std::stack<HitTestingTreeNode*> children;
for (HitTestingTreeNode* child = mLastChild.get(); child;
child = child->mPrevSibling) {
children.push(child);
}
nsPrintfCString childPrefix("%s ", aPrefix);
while (!children.empty()) {
children.top()->Dump(childPrefix.get());
children.pop();
}
}
void HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent) {
// precondition: GetApzc() is non-null
MOZ_ASSERT(GetApzc() != nullptr);
if (IsPrimaryHolder()) {
GetApzc()->SetParent(aParent);
} else {
MOZ_ASSERT(GetApzc()->GetParent() == aParent);
}
}
void HitTestingTreeNode::Lock(const RecursiveMutexAutoLock& aProofOfTreeLock) {
mLockCount++;
}
void HitTestingTreeNode::Unlock(
const RecursiveMutexAutoLock& aProofOfTreeLock) {
MOZ_ASSERT(mLockCount > 0);
mLockCount--;
}
HitTestingTreeNodeAutoLock::HitTestingTreeNodeAutoLock()
: mTreeMutex(nullptr) {}
HitTestingTreeNodeAutoLock::~HitTestingTreeNodeAutoLock() { Clear(); }
void HitTestingTreeNodeAutoLock::Initialize(
const RecursiveMutexAutoLock& aProofOfTreeLock,
already_AddRefed<HitTestingTreeNode> aNode, RecursiveMutex& aTreeMutex) {
MOZ_ASSERT(!mNode);
mNode = aNode;
mTreeMutex = &aTreeMutex;
mNode->Lock(aProofOfTreeLock);
}
void HitTestingTreeNodeAutoLock::Clear() {
if (!mNode) {
return;
}
MOZ_ASSERT(mTreeMutex);
{ // scope lock
RecursiveMutexAutoLock lock(*mTreeMutex);
mNode->Unlock(lock);
}
mNode = nullptr;
mTreeMutex = nullptr;
}
} // namespace layers
} // namespace mozilla