summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/HitTestingTreeNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/apz/src/HitTestingTreeNode.cpp')
-rw-r--r--gfx/layers/apz/src/HitTestingTreeNode.cpp419
1 files changed, 419 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp
new file mode 100644
index 0000000000..d25a9c1d5f
--- /dev/null
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -0,0 +1,419 @@
+/* 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/. */
+
+#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 LayerIntRegion& aVisibleRegion,
+ const LayerIntSize& aRemoteDocumentSize,
+ const CSSTransformMatrix& aTransform, const EventRegionsOverride& aOverride,
+ const Maybe<ScrollableLayerGuid::ViewID>& aAsyncZoomContainerId) {
+ mVisibleRegion = aVisibleRegion;
+ mRemoteDocumentSize = aRemoteDocumentSize;
+ mTransform = aTransform;
+ mOverride = aOverride;
+ mAsyncZoomContainerId = aAsyncZoomContainerId;
+}
+
+EventRegionsOverride HitTestingTreeNode::GetEventRegionsOverride() const {
+ return mOverride;
+}
+
+const CSSTransformMatrix& HitTestingTreeNode::GetTransform() const {
+ return mTransform;
+}
+
+LayerToScreenMatrix4x4 HitTestingTreeNode::GetTransformToGecko() const {
+ if (mParent) {
+ LayerToParentLayerMatrix4x4 thisToParent =
+ mTransform * AsyncTransformMatrix();
+ if (mApzc) {
+ thisToParent =
+ thisToParent * ViewAs<ParentLayerToParentLayerMatrix4x4>(
+ mApzc->GetTransformToLastDispatchedPaint());
+ }
+ ParentLayerToScreenMatrix4x4 parentToRoot =
+ ViewAs<ParentLayerToScreenMatrix4x4>(
+ mParent->GetTransformToGecko(),
+ PixelCastJustification::MovingDownToChildren);
+ return thisToParent * parentToRoot;
+ }
+
+ return ViewAs<LayerToScreenMatrix4x4>(
+ mTransform * AsyncTransformMatrix(),
+ PixelCastJustification::ScreenIsParentLayerForRoot);
+}
+
+const LayerIntRegion& HitTestingTreeNode::GetVisibleRegion() const {
+ return mVisibleRegion;
+}
+
+ScreenRect HitTestingTreeNode::GetRemoteDocumentScreenRect() const {
+ ScreenRect result = TransformBy(
+ GetTransformToGecko(),
+ 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()
+ : 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