1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 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 "APZCTreeManagerTester.h"
8 #include "APZTestCommon.h"
9 #include "InputUtils.h"
10 #include "mozilla/layers/LayersTypes.h"
11
12 class APZEventRegionsTester : public APZCTreeManagerTester {
13 protected:
14 UniquePtr<ScopedLayerTreeRegistration> registration;
15 TestAsyncPanZoomController* rootApzc;
16
CreateEventRegionsLayerTree1()17 void CreateEventRegionsLayerTree1() {
18 const char* layerTreeSyntax = "c(tt)";
19 nsIntRegion layerVisibleRegions[] = {
20 nsIntRegion(IntRect(0, 0, 200, 200)), // root
21 nsIntRegion(IntRect(0, 0, 100, 200)), // left half
22 nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half
23 };
24 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
25 layers);
26 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID);
27 SetScrollableFrameMetrics(layers[1],
28 ScrollableLayerGuid::START_SCROLL_ID + 1);
29 SetScrollableFrameMetrics(layers[2],
30 ScrollableLayerGuid::START_SCROLL_ID + 2);
31 SetScrollHandoff(layers[1], root);
32 SetScrollHandoff(layers[2], root);
33
34 // Set up the event regions over a 200x200 area. The root layer has the
35 // whole 200x200 as the hit region; layers[1] has the left half and
36 // layers[2] has the bottom half. The bottom-left 100x100 area is also
37 // in the d-t-c region for both layers[1] and layers[2] (but layers[2] is
38 // on top so it gets the events by default if the main thread doesn't
39 // respond).
40 EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
41 root->SetEventRegions(regions);
42 regions.mDispatchToContentHitRegion =
43 nsIntRegion(IntRect(0, 100, 100, 100));
44 regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200));
45 layers[1]->SetEventRegions(regions);
46 regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
47 layers[2]->SetEventRegions(regions);
48
49 registration =
50 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, root, mcc);
51 UpdateHitTestingTree();
52 rootApzc = ApzcOf(root);
53 }
54
CreateEventRegionsLayerTree2()55 void CreateEventRegionsLayerTree2() {
56 const char* layerTreeSyntax = "c(t)";
57 nsIntRegion layerVisibleRegions[] = {
58 nsIntRegion(IntRect(0, 0, 100, 500)),
59 nsIntRegion(IntRect(0, 150, 100, 100)),
60 };
61 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
62 layers);
63 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID);
64
65 // Set up the event regions so that the child thebes layer is positioned far
66 // away from the scrolling container layer.
67 EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
68 root->SetEventRegions(regions);
69 regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100));
70 layers[1]->SetEventRegions(regions);
71
72 registration =
73 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, root, mcc);
74 UpdateHitTestingTree();
75 rootApzc = ApzcOf(root);
76 }
77
CreateObscuringLayerTree()78 void CreateObscuringLayerTree() {
79 const char* layerTreeSyntax = "c(c(t)t)";
80 // LayerID 0 1 2 3
81 // 0 is the root.
82 // 1 is a parent scrollable layer.
83 // 2 is a child scrollable layer.
84 // 3 is the Obscurer, who ruins everything.
85 nsIntRegion layerVisibleRegions[] = {
86 // x coordinates are uninteresting
87 nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
88 nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
89 nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150]
90 nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200]
91 };
92 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
93 layers);
94
95 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
96 CSSRect(0, 0, 200, 200));
97 SetScrollableFrameMetrics(layers[1],
98 ScrollableLayerGuid::START_SCROLL_ID + 1,
99 CSSRect(0, 0, 200, 300));
100 SetScrollableFrameMetrics(layers[2],
101 ScrollableLayerGuid::START_SCROLL_ID + 2,
102 CSSRect(0, 0, 200, 100));
103 SetScrollHandoff(layers[2], layers[1]);
104 SetScrollHandoff(layers[1], root);
105
106 EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
107 root->SetEventRegions(regions);
108 regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300));
109 layers[1]->SetEventRegions(regions);
110 regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
111 layers[2]->SetEventRegions(regions);
112
113 registration =
114 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, root, mcc);
115 UpdateHitTestingTree();
116 rootApzc = ApzcOf(root);
117 }
118
CreateBug1119497LayerTree()119 void CreateBug1119497LayerTree() {
120 const char* layerTreeSyntax = "c(tt)";
121 // LayerID 0 12
122 // 0 is the root and has an APZC
123 // 1 is behind 2 and has an APZC
124 // 2 entirely covers 1 and should take all the input events, but has no APZC
125 // so hits to 2 should go to to the root APZC
126 nsIntRegion layerVisibleRegions[] = {
127 nsIntRegion(IntRect(0, 0, 100, 100)),
128 nsIntRegion(IntRect(0, 0, 100, 100)),
129 nsIntRegion(IntRect(0, 0, 100, 100)),
130 };
131 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm,
132 layers);
133
134 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID);
135 SetScrollableFrameMetrics(layers[1],
136 ScrollableLayerGuid::START_SCROLL_ID + 1);
137
138 registration =
139 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, root, mcc);
140 UpdateHitTestingTree();
141 }
142
CreateBug1117712LayerTree()143 void CreateBug1117712LayerTree() {
144 const char* layerTreeSyntax = "c(c(t)t)";
145 // LayerID 0 1 2 3
146 // 0 is the root
147 // 1 is a container layer whose sole purpose to make a non-empty ancestor
148 // transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
149 // transforms are different from 3's.
150 // 2 is a small layer that is the actual target
151 // 3 is a big layer obscuring 2 with a dispatch-to-content region
152 nsIntRegion layerVisibleRegions[] = {
153 nsIntRegion(IntRect(0, 0, 100, 100)),
154 nsIntRegion(IntRect(0, 0, 0, 0)),
155 nsIntRegion(IntRect(0, 0, 10, 10)),
156 nsIntRegion(IntRect(0, 0, 100, 100)),
157 };
158 Matrix4x4 layerTransforms[] = {
159 Matrix4x4(),
160 Matrix4x4::Translation(50, 0, 0),
161 Matrix4x4(),
162 Matrix4x4(),
163 };
164 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions,
165 layerTransforms, lm, layers);
166
167 SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID,
168 CSSRect(0, 0, 10, 10));
169 SetScrollableFrameMetrics(layers[3],
170 ScrollableLayerGuid::START_SCROLL_ID + 1,
171 CSSRect(0, 0, 100, 100));
172 SetScrollHandoff(layers[3], layers[2]);
173
174 EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10)));
175 layers[2]->SetEventRegions(regions);
176 regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
177 regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
178 layers[3]->SetEventRegions(regions);
179
180 registration =
181 MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, root, mcc);
182 UpdateHitTestingTree();
183 }
184 };
185
186 class APZEventRegionsTesterLayersOnly : public APZEventRegionsTester {
187 public:
APZEventRegionsTesterLayersOnly()188 APZEventRegionsTesterLayersOnly() { mLayersOnly = true; }
189 };
190
TEST_F(APZEventRegionsTesterLayersOnly,HitRegionImmediateResponse)191 TEST_F(APZEventRegionsTesterLayersOnly, HitRegionImmediateResponse) {
192 CreateEventRegionsLayerTree1();
193
194 TestAsyncPanZoomController* root = ApzcOf(layers[0]);
195 TestAsyncPanZoomController* left = ApzcOf(layers[1]);
196 TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
197
198 MockFunction<void(std::string checkPointName)> check;
199 {
200 InSequence s;
201 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _))
202 .Times(1);
203 EXPECT_CALL(check, Call("Tapped on left"));
204 EXPECT_CALL(*mcc,
205 HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _))
206 .Times(1);
207 EXPECT_CALL(check, Call("Tapped on bottom"));
208 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _))
209 .Times(1);
210 EXPECT_CALL(check, Call("Tapped on root"));
211 EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
212 EXPECT_CALL(*mcc,
213 HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _))
214 .Times(1);
215 EXPECT_CALL(check, Call("Tapped on bottom again"));
216 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _))
217 .Times(1);
218 EXPECT_CALL(check, Call("Tapped on left this time"));
219 }
220
221 TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
222
223 // Tap in the exposed hit regions of each of the layers once and ensure
224 // the clicks are dispatched right away
225 Tap(manager, ScreenIntPoint(10, 10), tapDuration);
226 mcc->RunThroughDelayedTasks(); // this runs the tap event
227 check.Call("Tapped on left");
228 Tap(manager, ScreenIntPoint(110, 110), tapDuration);
229 mcc->RunThroughDelayedTasks(); // this runs the tap event
230 check.Call("Tapped on bottom");
231 Tap(manager, ScreenIntPoint(110, 10), tapDuration);
232 mcc->RunThroughDelayedTasks(); // this runs the tap event
233 check.Call("Tapped on root");
234
235 // Now tap on the dispatch-to-content region where the layers overlap
236 Tap(manager, ScreenIntPoint(10, 110), tapDuration);
237 mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout
238 check.Call("Tap pending on d-t-c region");
239 mcc->RunThroughDelayedTasks(); // this runs the tap event
240 check.Call("Tapped on bottom again");
241
242 // Now let's do that again, but simulate a main-thread response
243 uint64_t inputBlockId = 0;
244 Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId);
245 nsTArray<ScrollableLayerGuid> targets;
246 targets.AppendElement(left->GetGuid());
247 manager->SetTargetAPZC(inputBlockId, targets);
248 while (mcc->RunThroughDelayedTasks())
249 ; // this runs the tap event
250 check.Call("Tapped on left this time");
251 }
252
TEST_F(APZEventRegionsTester,HitRegionAccumulatesChildren)253 TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
254 CreateEventRegionsLayerTree2();
255
256 // Tap in the area of the child layer that's not directly included in the
257 // parent layer's hit region. Verify that it comes out of the APZC's
258 // content controller, which indicates the input events got routed correctly
259 // to the APZC.
260 EXPECT_CALL(*mcc,
261 HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _))
262 .Times(1);
263 Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
264 }
265
TEST_F(APZEventRegionsTesterLayersOnly,Obscuration)266 TEST_F(APZEventRegionsTesterLayersOnly, Obscuration) {
267 CreateObscuringLayerTree();
268 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
269
270 UpdateHitTestingTree();
271
272 RefPtr<TestAsyncPanZoomController> parent = ApzcOf(layers[1]);
273 TestAsyncPanZoomController* child = ApzcOf(layers[2]);
274
275 Pan(parent, 75, 25, PanOptions::NoFling);
276
277 APZCTreeManager::HitTestResult hit =
278 manager->GetTargetAPZC(ScreenPoint(50, 75));
279 EXPECT_EQ(child, hit.mTargetApzc.get());
280 EXPECT_EQ(hit.mHitResult, CompositorHitTestFlags::eVisibleToHitTest);
281 }
282
TEST_F(APZEventRegionsTester,Bug1119497)283 TEST_F(APZEventRegionsTester, Bug1119497) {
284 CreateBug1119497LayerTree();
285
286 APZCTreeManager::HitTestResult hit =
287 manager->GetTargetAPZC(ScreenPoint(50, 50));
288 // We should hit layers[2], so |result| will be eVisibleToHitTest but there's
289 // no actual APZC on layers[2], so it will be the APZC of the root layer.
290 EXPECT_EQ(ApzcOf(layers[0]), hit.mTargetApzc.get());
291 EXPECT_EQ(hit.mHitResult, CompositorHitTestFlags::eVisibleToHitTest);
292 }
293
TEST_F(APZEventRegionsTester,Bug1117712)294 TEST_F(APZEventRegionsTester, Bug1117712) {
295 CreateBug1117712LayerTree();
296
297 TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
298
299 // These touch events should hit the dispatch-to-content region of layers[3]
300 // and so get queued with that APZC as the tentative target.
301 uint64_t inputBlockId = 0;
302 Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100),
303 nullptr, &inputBlockId);
304 // But now we tell the APZ that really it hit layers[2], and expect the tap
305 // to be delivered at the correct coordinates.
306 EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0,
307 apzc2->GetGuid(), _))
308 .Times(1);
309
310 nsTArray<ScrollableLayerGuid> targets;
311 targets.AppendElement(apzc2->GetGuid());
312 manager->SetTargetAPZC(inputBlockId, targets);
313 }
314