1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "HitTestingTreeNode.h"
8 
9 #include "AsyncPanZoomController.h"                     // for AsyncPanZoomController
10 #include "LayersLogging.h"                              // for Stringify
11 #include "mozilla/gfx/Point.h"                          // for Point4D
12 #include "mozilla/layers/APZThreadUtils.h"              // for AssertOnCompositorThread
13 #include "mozilla/layers/APZUtils.h"                    // for CompleteAsyncTransform
14 #include "mozilla/layers/AsyncCompositionManager.h"     // for ViewTransform::operator Matrix4x4()
15 #include "mozilla/layers/AsyncDragMetrics.h"            // for AsyncDragMetrics
16 #include "nsPrintfCString.h"                            // for nsPrintfCString
17 #include "UnitTransforms.h"                             // for ViewAs
18 
19 namespace mozilla {
20 namespace layers {
21 
HitTestingTreeNode(AsyncPanZoomController * aApzc,bool aIsPrimaryHolder,uint64_t aLayersId)22 HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
23                                        bool aIsPrimaryHolder,
24                                        uint64_t aLayersId)
25   : mApzc(aApzc)
26   , mIsPrimaryApzcHolder(aIsPrimaryHolder)
27   , mLayersId(aLayersId)
28   , mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
29   , mScrollDir(Layer::NONE)
30   , mScrollSize(0)
31   , mIsScrollbarContainer(false)
32   , mFixedPosTarget(FrameMetrics::NULL_SCROLL_ID)
33   , mOverride(EventRegionsOverride::NoOverride)
34 {
35 if (mIsPrimaryApzcHolder) {
36     MOZ_ASSERT(mApzc);
37   }
38   MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
39 }
40 
41 void
RecycleWith(AsyncPanZoomController * aApzc,uint64_t aLayersId)42 HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc,
43                                 uint64_t aLayersId)
44 {
45   MOZ_ASSERT(!mIsPrimaryApzcHolder);
46   Destroy(); // clear out tree pointers
47   mApzc = aApzc;
48   mLayersId = aLayersId;
49   MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
50   // The caller is expected to call SetHitTestData to repopulate the hit-test
51   // fields.
52 }
53 
~HitTestingTreeNode()54 HitTestingTreeNode::~HitTestingTreeNode()
55 {
56 }
57 
58 void
Destroy()59 HitTestingTreeNode::Destroy()
60 {
61   APZThreadUtils::AssertOnCompositorThread();
62 
63   mPrevSibling = nullptr;
64   mLastChild = nullptr;
65   mParent = nullptr;
66 
67   if (mApzc) {
68     if (mIsPrimaryApzcHolder) {
69       mApzc->Destroy();
70     }
71     mApzc = nullptr;
72   }
73 
74   mLayersId = 0;
75 }
76 
77 void
SetLastChild(HitTestingTreeNode * aChild)78 HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
79 {
80   mLastChild = aChild;
81   if (aChild) {
82     aChild->mParent = this;
83 
84     if (aChild->GetApzc()) {
85       AsyncPanZoomController* parent = GetNearestContainingApzc();
86       // We assume that HitTestingTreeNodes with an ancestor/descendant
87       // relationship cannot both point to the same APZC instance. This
88       // assertion only covers a subset of cases in which that might occur,
89       // but it's better than nothing.
90       MOZ_ASSERT(aChild->GetApzc() != parent);
91       aChild->SetApzcParent(parent);
92     }
93   }
94 }
95 
96 void
SetScrollbarData(FrameMetrics::ViewID aScrollViewId,Layer::ScrollDirection aDir,int32_t aScrollSize,bool aIsScrollContainer)97 HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
98                                      Layer::ScrollDirection aDir,
99                                      int32_t aScrollSize,
100                                      bool aIsScrollContainer)
101 {
102   mScrollViewId = aScrollViewId;
103   mScrollDir = aDir;
104   mScrollSize = aScrollSize;;
105   mIsScrollbarContainer = aIsScrollContainer;
106 }
107 
108 bool
MatchesScrollDragMetrics(const AsyncDragMetrics & aDragMetrics) const109 HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const
110 {
111   return ((mScrollDir == Layer::HORIZONTAL &&
112            aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) ||
113           (mScrollDir == Layer::VERTICAL &&
114            aDragMetrics.mDirection == AsyncDragMetrics::VERTICAL)) &&
115          mScrollViewId == aDragMetrics.mViewId;
116 }
117 
118 int32_t
GetScrollSize() const119 HitTestingTreeNode::GetScrollSize() const
120 {
121   return mScrollSize;
122 }
123 
124 bool
IsScrollbarNode() const125 HitTestingTreeNode::IsScrollbarNode() const
126 {
127   return mIsScrollbarContainer || (mScrollDir != Layer::NONE);
128 }
129 
130 void
SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)131 HitTestingTreeNode::SetFixedPosData(FrameMetrics::ViewID aFixedPosTarget)
132 {
133   mFixedPosTarget = aFixedPosTarget;
134 }
135 
136 FrameMetrics::ViewID
GetFixedPosTarget() const137 HitTestingTreeNode::GetFixedPosTarget() const
138 {
139   return mFixedPosTarget;
140 }
141 
142 void
SetPrevSibling(HitTestingTreeNode * aSibling)143 HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
144 {
145   mPrevSibling = aSibling;
146   if (aSibling) {
147     aSibling->mParent = mParent;
148 
149     if (aSibling->GetApzc()) {
150       AsyncPanZoomController* parent = mParent ? mParent->GetNearestContainingApzc() : nullptr;
151       aSibling->SetApzcParent(parent);
152     }
153   }
154 }
155 
156 void
MakeRoot()157 HitTestingTreeNode::MakeRoot()
158 {
159   mParent = nullptr;
160 
161   if (GetApzc()) {
162     SetApzcParent(nullptr);
163   }
164 }
165 
166 HitTestingTreeNode*
GetFirstChild() const167 HitTestingTreeNode::GetFirstChild() const
168 {
169   HitTestingTreeNode* child = GetLastChild();
170   while (child && child->GetPrevSibling()) {
171     child = child->GetPrevSibling();
172   }
173   return child;
174 }
175 
176 HitTestingTreeNode*
GetLastChild() const177 HitTestingTreeNode::GetLastChild() const
178 {
179   return mLastChild;
180 }
181 
182 HitTestingTreeNode*
GetPrevSibling() const183 HitTestingTreeNode::GetPrevSibling() const
184 {
185   return mPrevSibling;
186 }
187 
188 HitTestingTreeNode*
GetParent() const189 HitTestingTreeNode::GetParent() const
190 {
191   return mParent;
192 }
193 
194 AsyncPanZoomController*
GetApzc() const195 HitTestingTreeNode::GetApzc() const
196 {
197   return mApzc;
198 }
199 
200 AsyncPanZoomController*
GetNearestContainingApzc() const201 HitTestingTreeNode::GetNearestContainingApzc() const
202 {
203   for (const HitTestingTreeNode* n = this; n; n = n->GetParent()) {
204     if (n->GetApzc()) {
205       return n->GetApzc();
206     }
207   }
208   return nullptr;
209 }
210 
211 bool
IsPrimaryHolder() const212 HitTestingTreeNode::IsPrimaryHolder() const
213 {
214   return mIsPrimaryApzcHolder;
215 }
216 
217 uint64_t
GetLayersId() const218 HitTestingTreeNode::GetLayersId() const
219 {
220   return mLayersId;
221 }
222 
223 void
SetHitTestData(const EventRegions & aRegions,const CSSTransformMatrix & aTransform,const Maybe<ParentLayerIntRegion> & aClipRegion,const EventRegionsOverride & aOverride)224 HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
225                                    const CSSTransformMatrix& aTransform,
226                                    const Maybe<ParentLayerIntRegion>& aClipRegion,
227                                    const EventRegionsOverride& aOverride)
228 {
229   mEventRegions = aRegions;
230   mTransform = aTransform;
231   mClipRegion = aClipRegion;
232   mOverride = aOverride;
233 }
234 
235 bool
IsOutsideClip(const ParentLayerPoint & aPoint) const236 HitTestingTreeNode::IsOutsideClip(const ParentLayerPoint& aPoint) const
237 {
238   // test against clip rect in ParentLayer coordinate space
239   return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
240 }
241 
242 Maybe<LayerPoint>
Untransform(const ParentLayerPoint & aPoint) const243 HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
244 {
245   // convert into Layer coordinate space
246   LayerToParentLayerMatrix4x4 transform = mTransform *
247       CompleteAsyncTransform(
248         mApzc
249       ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL)
250       : AsyncTransformComponentMatrix());
251   return UntransformBy(transform.Inverse(), aPoint);
252 }
253 
254 HitTestResult
HitTest(const ParentLayerPoint & aPoint) const255 HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
256 {
257   // This should only ever get called if the point is inside the clip region
258   // for this node.
259   MOZ_ASSERT(!IsOutsideClip(aPoint));
260 
261   if (mOverride & EventRegionsOverride::ForceEmptyHitRegion) {
262     return HitTestResult::HitNothing;
263   }
264 
265   // convert into Layer coordinate space
266   Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
267   if (!pointInLayerPixels) {
268     return HitTestResult::HitNothing;
269   }
270   auto point = LayerIntPoint::Round(pointInLayerPixels.ref());
271 
272   // test against event regions in Layer coordinate space
273   if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
274     return HitTestResult::HitNothing;
275   }
276   if ((mOverride & EventRegionsOverride::ForceDispatchToContent) ||
277       mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
278   {
279     return HitTestResult::HitDispatchToContentRegion;
280   }
281   if (gfxPrefs::TouchActionEnabled()) {
282     if (mEventRegions.mNoActionRegion.Contains(point.x, point.y)) {
283       return HitTestResult::HitLayerTouchActionNone;
284     }
285     bool panX = mEventRegions.mHorizontalPanRegion.Contains(point.x, point.y);
286     bool panY = mEventRegions.mVerticalPanRegion.Contains(point.x, point.y);
287     if (panX && panY) {
288       return HitTestResult::HitLayerTouchActionPanXY;
289     } else if (panX) {
290       return HitTestResult::HitLayerTouchActionPanX;
291     } else if (panY) {
292       return HitTestResult::HitLayerTouchActionPanY;
293     }
294   }
295   return HitTestResult::HitLayer;
296 }
297 
298 EventRegionsOverride
GetEventRegionsOverride() const299 HitTestingTreeNode::GetEventRegionsOverride() const
300 {
301   return mOverride;
302 }
303 
304 void
Dump(const char * aPrefix) const305 HitTestingTreeNode::Dump(const char* aPrefix) const
306 {
307   if (mPrevSibling) {
308     mPrevSibling->Dump(aPrefix);
309   }
310   printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %s%s%sr=(%s) t=(%s) c=(%s)\n",
311     aPrefix, this, mApzc.get(),
312     mApzc ? Stringify(mApzc->GetGuid()).c_str() : nsPrintfCString("l=%" PRIu64, mLayersId).get(),
313     (mOverride & EventRegionsOverride::ForceDispatchToContent) ? "fdtc " : "",
314     (mOverride & EventRegionsOverride::ForceEmptyHitRegion) ? "fehr " : "",
315     (mFixedPosTarget != FrameMetrics::NULL_SCROLL_ID) ? nsPrintfCString("fixed=%" PRIu64 " ", mFixedPosTarget).get() : "",
316     Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
317     mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
318   if (mLastChild) {
319     mLastChild->Dump(nsPrintfCString("%s  ", aPrefix).get());
320   }
321 }
322 
323 void
SetApzcParent(AsyncPanZoomController * aParent)324 HitTestingTreeNode::SetApzcParent(AsyncPanZoomController* aParent)
325 {
326   // precondition: GetApzc() is non-null
327   MOZ_ASSERT(GetApzc() != nullptr);
328   if (IsPrimaryHolder()) {
329     GetApzc()->SetParent(aParent);
330   } else {
331     MOZ_ASSERT(GetApzc()->GetParent() == aParent);
332   }
333 }
334 
335 } // namespace layers
336 } // namespace mozilla
337