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.cpp336
1 files changed, 336 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp
new file mode 100644
index 000000000..acedcde5d
--- /dev/null
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et 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/. */
+
+#include "HitTestingTreeNode.h"
+
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "LayersLogging.h" // for Stringify
+#include "mozilla/gfx/Point.h" // for Point4D
+#include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread
+#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform::operator Matrix4x4()
+#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "UnitTransforms.h" // for ViewAs
+
+namespace mozilla {
+namespace layers {
+
+HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
+ bool aIsPrimaryHolder,
+ uint64_t aLayersId)
+ : mApzc(aApzc)
+ , mIsPrimaryApzcHolder(aIsPrimaryHolder)
+ , mLayersId(aLayersId)
+ , mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
+ , mScrollDir(Layer::NONE)
+ , mScrollSize(0)
+ , mIsScrollbarContainer(false)
+ , mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
+ , mOverride(EventRegionsOverride::NoOverride)
+{
+if (mIsPrimaryApzcHolder) {
+ MOZ_ASSERT(mApzc);
+ }
+ MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
+}
+
+void
+HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc,
+ uint64_t aLayersId)
+{
+ MOZ_ASSERT(!mIsPrimaryApzcHolder);
+ Destroy(); // clear out tree pointers
+ mApzc = aApzc;
+ mLayersId = aLayersId;
+ MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
+ // The caller is expected to call SetHitTestData to repopulate the hit-test
+ // fields.
+}
+
+HitTestingTreeNode::~HitTestingTreeNode()
+{
+}
+
+void
+HitTestingTreeNode::Destroy()
+{
+ APZThreadUtils::AssertOnCompositorThread();
+
+ mPrevSibling = nullptr;
+ mLastChild = nullptr;
+ mParent = nullptr;
+
+ if (mApzc) {
+ if (mIsPrimaryApzcHolder) {
+ mApzc->Destroy();
+ }
+ mApzc = nullptr;
+ }
+
+ mLayersId = 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(FrameMetrics::ViewID aScrollViewId,
+ Layer::ScrollDirection aDir,
+ int32_t aScrollSize,
+ bool aIsScrollContainer)
+{
+ mScrollViewId = aScrollViewId;
+ mScrollDir = aDir;
+ mScrollSize = aScrollSize;;
+ mIsScrollbarContainer = aIsScrollContainer;
+}
+
+bool
+HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const
+{
+ return ((mScrollDir == Layer::HORIZONTAL &&
+ aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) ||
+ (mScrollDir == Layer::VERTICAL &&
+ aDragMetrics.mDirection == AsyncDragMetrics::VERTICAL)) &&
+ mScrollViewId == aDragMetrics.mViewId;
+}
+
+int32_t
+HitTestingTreeNode::GetScrollSize() const
+{
+ return mScrollSize;
+}
+
+bool
+HitTestingTreeNode::IsScrollbarNode() const
+{
+ return mIsScrollbarContainer || (mScrollDir != Layer::NONE);
+}
+
+void
+HitTestingTreeNode::SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)
+{
+ mFixedPosTarget = aFixedPosTarget;
+}
+
+FrameMetrics::ViewID
+HitTestingTreeNode::GetFixedPosTarget() const
+{
+ return mFixedPosTarget;
+}
+
+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::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;
+}
+
+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;
+}
+
+uint64_t
+HitTestingTreeNode::GetLayersId() const
+{
+ return mLayersId;
+}
+
+void
+HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
+ const CSSTransformMatrix& aTransform,
+ const Maybe<ParentLayerIntRegion>& aClipRegion,
+ const EventRegionsOverride& aOverride)
+{
+ mEventRegions = aRegions;
+ mTransform = aTransform;
+ mClipRegion = aClipRegion;
+ mOverride = aOverride;
+}
+
+bool
+HitTestingTreeNode::IsOutsideClip(const ParentLayerPoint& aPoint) const
+{
+ // test against clip rect in ParentLayer coordinate space
+ return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
+}
+
+Maybe<LayerPoint>
+HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
+{
+ // convert into Layer coordinate space
+ LayerToParentLayerMatrix4x4 transform = mTransform *
+ CompleteAsyncTransform(
+ mApzc
+ ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL)
+ : AsyncTransformComponentMatrix());
+ return UntransformBy(transform.Inverse(), aPoint);
+}
+
+HitTestResult
+HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
+{
+ // This should only ever get called if the point is inside the clip region
+ // for this node.
+ MOZ_ASSERT(!IsOutsideClip(aPoint));
+
+ if (mOverride & EventRegionsOverride::ForceEmptyHitRegion) {
+ return HitTestResult::HitNothing;
+ }
+
+ // convert into Layer coordinate space
+ Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
+ if (!pointInLayerPixels) {
+ return HitTestResult::HitNothing;
+ }
+ auto point = LayerIntPoint::Round(pointInLayerPixels.ref());
+
+ // test against event regions in Layer coordinate space
+ if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
+ return HitTestResult::HitNothing;
+ }
+ if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
+ mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
+ {
+ return HitTestResult::HitDispatchToContentRegion;
+ }
+ if (gfxPrefs::TouchActionEnabled()) {
+ if (mEventRegions.mNoActionRegion.Contains(point.x, point.y)) {
+ return HitTestResult::HitLayerTouchActionNone;
+ }
+ bool panX = mEventRegions.mHorizontalPanRegion.Contains(point.x, point.y);
+ bool panY = mEventRegions.mVerticalPanRegion.Contains(point.x, point.y);
+ if (panX && panY) {
+ return HitTestResult::HitLayerTouchActionPanXY;
+ } else if (panX) {
+ return HitTestResult::HitLayerTouchActionPanX;
+ } else if (panY) {
+ return HitTestResult::HitLayerTouchActionPanY;
+ }
+ }
+ return HitTestResult::HitLayer;
+}
+
+EventRegionsOverride
+HitTestingTreeNode::GetEventRegionsOverride() const
+{
+ return mOverride;
+}
+
+void
+HitTestingTreeNode::Dump(const char* aPrefix) const
+{
+ if (mPrevSibling) {
+ mPrevSibling->Dump(aPrefix);
+ }
+ printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%sr=(%s) t=(%s) c=(%s)\n",
+ aPrefix, this, mApzc.get(),
+ mApzc ? Stringify(mApzc->GetGuid()).c_str() : nsPrintfCString("l=%" PRIu64, mLayersId).get(),
+ (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " : "",
+ (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "",
+ (mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) ? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get() : "",
+ Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
+ mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
+ if (mLastChild) {
+ mLastChild->Dump(nsPrintfCString("%s ", aPrefix).get());
+ }
+}
+
+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);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla