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