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