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
10 #include "InputUtils.h"
11
12 class APZHitTestingTester : public APZCTreeManagerTester {
13 protected:
14 ScreenToParentLayerMatrix4x4 transformToApzc;
15 ParentLayerToScreenMatrix4x4 transformToGecko;
16
GetTargetAPZC(const ScreenPoint & aPoint)17 already_AddRefed<AsyncPanZoomController> GetTargetAPZC(
18 const ScreenPoint& aPoint) {
19 RefPtr<AsyncPanZoomController> hit =
20 manager->GetTargetAPZC(aPoint).mTargetApzc;
21 if (hit) {
22 transformToApzc = manager->GetScreenToApzcTransform(hit.get());
23 transformToGecko = manager->GetApzcToGeckoTransform(hit.get());
24 }
25 return hit.forget();
26 }
27
28 protected:
CreateHitTesting1LayerTree()29 void CreateHitTesting1LayerTree() {
30 const char* layerTreeSyntax = "c(tttt)";
31 // LayerID 0 1234
32 nsIntRegion layerVisibleRegion[] = {
33 nsIntRegion(IntRect(0, 0, 100, 100)),
34 nsIntRegion(IntRect(0, 0, 100, 100)),
35 nsIntRegion(IntRect(10, 10, 20, 20)),
36 nsIntRegion(IntRect(10, 10, 20, 20)),
37 nsIntRegion(IntRect(5, 5, 20, 20)),
38 };
39 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm,
40 layers);
41 }
42
CreateHitTesting2LayerTree()43 void CreateHitTesting2LayerTree() {
44 const char* layerTreeSyntax = "c(tc(t))";
45 // LayerID 0 12 3
46 nsIntRegion layerVisibleRegion[] = {
47 nsIntRegion(IntRect(0, 0, 100, 100)),
48 nsIntRegion(IntRect(10, 10, 40, 40)),
49 nsIntRegion(IntRect(10, 60, 40, 40)),
50 nsIntRegion(IntRect(10, 60, 40, 40)),
51 };
52 Matrix4x4 transforms[] = {
53 Matrix4x4(),
54 Matrix4x4(),
55 Matrix4x4::Scaling(2, 1, 1),
56 Matrix4x4(),
57 };
58 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm,
59 layers);
60
61 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
62 CSSRect(0, 0, 200, 200));
63 SetScrollableFrameMetrics(layers[1],
64 ScrollableLayerGuid::START_SCROLL_ID + 1,
65 CSSRect(0, 0, 80, 80));
66 SetScrollableFrameMetrics(layers[3],
67 ScrollableLayerGuid::START_SCROLL_ID + 2,
68 CSSRect(0, 0, 80, 80));
69 }
70
DisableApzOn(Layer * aLayer)71 void DisableApzOn(Layer* aLayer) {
72 ScrollMetadata m = aLayer->GetScrollMetadata(0);
73 m.SetForceDisableApz(true);
74 aLayer->SetScrollMetadata(m);
75 }
76
CreateComplexMultiLayerTree()77 void CreateComplexMultiLayerTree() {
78 const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
79 // LayerID 0 12 3 45 6 7 89
80 nsIntRegion layerVisibleRegion[] = {
81 nsIntRegion(IntRect(0, 0, 300, 400)), // root(0)
82 nsIntRegion(IntRect(0, 0, 100, 100)), // thebes(1) in top-left
83 nsIntRegion(
84 IntRect(50, 50, 200, 300)), // container(2) centered in root(0)
85 nsIntRegion(
86 IntRect(50, 50, 200,
87 300)), // thebes(3) fully occupying parent container(2)
88 nsIntRegion(IntRect(0, 200, 100, 100)), // thebes(4) in bottom-left
89 nsIntRegion(
90 IntRect(200, 0, 100,
91 400)), // container(5) along the right 100px of root(0)
92 nsIntRegion(
93 IntRect(200, 0, 100, 200)), // container(6) taking up the top half
94 // of parent container(5)
95 nsIntRegion(
96 IntRect(200, 0, 100,
97 200)), // thebes(7) fully occupying parent container(6)
98 nsIntRegion(IntRect(200, 200, 100,
99 100)), // thebes(8) in bottom-right (below (6))
100 nsIntRegion(IntRect(200, 300, 100,
101 100)), // thebes(9) in bottom-right (below (8))
102 };
103 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm,
104 layers);
105 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID);
106 SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID);
107 SetScrollableFrameMetrics(layers[4],
108 ScrollableLayerGuid::START_SCROLL_ID + 1);
109 SetScrollableFrameMetrics(layers[6],
110 ScrollableLayerGuid::START_SCROLL_ID + 1);
111 SetScrollableFrameMetrics(layers[7],
112 ScrollableLayerGuid::START_SCROLL_ID + 2);
113 SetScrollableFrameMetrics(layers[8],
114 ScrollableLayerGuid::START_SCROLL_ID + 1);
115 SetScrollableFrameMetrics(layers[9],
116 ScrollableLayerGuid::START_SCROLL_ID + 3);
117 }
118
CreateBug1148350LayerTree()119 void CreateBug1148350LayerTree() {
120 const char* layerTreeSyntax = "c(t)";
121 // LayerID 0 1
122 nsIntRegion layerVisibleRegion[] = {
123 nsIntRegion(IntRect(0, 0, 200, 200)),
124 nsIntRegion(IntRect(0, 0, 200, 200)),
125 };
126 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm,
127 layers);
128 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID);
129 }
130 };
131
132 class APZHitTestingTesterLayersOnly : public APZHitTestingTester {
133 public:
APZHitTestingTesterLayersOnly()134 APZHitTestingTesterLayersOnly() { mLayersOnly = true; }
135 };
136
137 // A simple hit testing test that doesn't involve any transforms on layers.
TEST_F(APZHitTestingTesterLayersOnly,HitTesting1)138 TEST_F(APZHitTestingTesterLayersOnly, HitTesting1) {
139 CreateHitTesting1LayerTree();
140 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
141
142 // No APZC attached so hit testing will return no APZC at (20,20)
143 RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
144 TestAsyncPanZoomController* nullAPZC = nullptr;
145 EXPECT_EQ(nullAPZC, hit.get());
146 EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
147 EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
148
149 uint32_t paintSequenceNumber = 0;
150
151 // Now we have a root APZC that will match the page
152 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
153 CSSRect(0, 0, 100, 100));
154 UpdateHitTestingTree(paintSequenceNumber++);
155 hit = GetTargetAPZC(ScreenPoint(15, 15));
156 EXPECT_EQ(ApzcOf(root), hit.get());
157 // expect hit point at LayerIntPoint(15, 15)
158 EXPECT_EQ(ParentLayerPoint(15, 15),
159 transformToApzc.TransformPoint(ScreenPoint(15, 15)));
160 EXPECT_EQ(ScreenPoint(15, 15),
161 transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
162
163 // Now we have a sub APZC with a better fit
164 SetScrollableFrameMetrics(layers[3], ScrollableLayerGuid::START_SCROLL_ID + 1,
165 CSSRect(0, 0, 100, 100));
166 UpdateHitTestingTree(paintSequenceNumber++);
167 EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
168 hit = GetTargetAPZC(ScreenPoint(25, 25));
169 EXPECT_EQ(ApzcOf(layers[3]), hit.get());
170 // expect hit point at LayerIntPoint(25, 25)
171 EXPECT_EQ(ParentLayerPoint(25, 25),
172 transformToApzc.TransformPoint(ScreenPoint(25, 25)));
173 EXPECT_EQ(ScreenPoint(25, 25),
174 transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
175
176 // At this point, layers[4] obscures layers[3] at the point (15, 15) so
177 // hitting there should hit the root APZC
178 hit = GetTargetAPZC(ScreenPoint(15, 15));
179 EXPECT_EQ(ApzcOf(root), hit.get());
180
181 // Now test hit testing when we have two scrollable layers
182 SetScrollableFrameMetrics(layers[4], ScrollableLayerGuid::START_SCROLL_ID + 2,
183 CSSRect(0, 0, 100, 100));
184 UpdateHitTestingTree(paintSequenceNumber++);
185 hit = GetTargetAPZC(ScreenPoint(15, 15));
186 EXPECT_EQ(ApzcOf(layers[4]), hit.get());
187 // expect hit point at LayerIntPoint(15, 15)
188 EXPECT_EQ(ParentLayerPoint(15, 15),
189 transformToApzc.TransformPoint(ScreenPoint(15, 15)));
190 EXPECT_EQ(ScreenPoint(15, 15),
191 transformToGecko.TransformPoint(ParentLayerPoint(15, 15)));
192
193 // Hit test ouside the reach of layer[3,4] but inside root
194 hit = GetTargetAPZC(ScreenPoint(90, 90));
195 EXPECT_EQ(ApzcOf(root), hit.get());
196 // expect hit point at LayerIntPoint(90, 90)
197 EXPECT_EQ(ParentLayerPoint(90, 90),
198 transformToApzc.TransformPoint(ScreenPoint(90, 90)));
199 EXPECT_EQ(ScreenPoint(90, 90),
200 transformToGecko.TransformPoint(ParentLayerPoint(90, 90)));
201
202 // Hit test ouside the reach of any layer
203 hit = GetTargetAPZC(ScreenPoint(1000, 10));
204 EXPECT_EQ(nullAPZC, hit.get());
205 EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
206 EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
207 hit = GetTargetAPZC(ScreenPoint(-1000, 10));
208 EXPECT_EQ(nullAPZC, hit.get());
209 EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
210 EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
211 }
212
213 // A more involved hit testing test that involves css and async transforms.
TEST_F(APZHitTestingTesterLayersOnly,HitTesting2)214 TEST_F(APZHitTestingTesterLayersOnly, HitTesting2) {
215 // Velocity bias can cause extra repaint requests.
216 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0);
217
218 CreateHitTesting2LayerTree();
219 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
220
221 UpdateHitTestingTree();
222
223 // At this point, the following holds (all coordinates in screen pixels):
224 // layers[0] has content from (0,0)-(200,200), clipped by composition bounds
225 // (0,0)-(100,100)
226 // layers[1] has content from (10,10)-(90,90), clipped by composition bounds
227 // (10,10)-(50,50)
228 // layers[2] has content from (20,60)-(100,100). no clipping as it's not a
229 // scrollable layer
230 // layers[3] has content from (20,60)-(180,140), clipped by composition
231 // bounds (20,60)-(100,100)
232
233 RefPtr<TestAsyncPanZoomController> apzcroot = ApzcOf(root);
234 TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
235 TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
236
237 // Hit an area that's clearly on the root layer but not any of the child
238 // layers.
239 RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
240 EXPECT_EQ(apzcroot, hit.get());
241 EXPECT_EQ(ParentLayerPoint(75, 25),
242 transformToApzc.TransformPoint(ScreenPoint(75, 25)));
243 EXPECT_EQ(ScreenPoint(75, 25),
244 transformToGecko.TransformPoint(ParentLayerPoint(75, 25)));
245
246 // Hit an area on the root that would be on layers[3] if layers[2]
247 // weren't transformed.
248 // Note that if layers[2] were scrollable, then this would hit layers[2]
249 // because its composition bounds would be at (10,60)-(50,100) (and the
250 // scale-only transform that we set on layers[2] would be invalid because
251 // it would place the layer into overscroll, as its composition bounds
252 // start at x=10 but its content at x=20).
253 hit = GetTargetAPZC(ScreenPoint(15, 75));
254 EXPECT_EQ(apzcroot, hit.get());
255 EXPECT_EQ(ParentLayerPoint(15, 75),
256 transformToApzc.TransformPoint(ScreenPoint(15, 75)));
257 EXPECT_EQ(ScreenPoint(15, 75),
258 transformToGecko.TransformPoint(ParentLayerPoint(15, 75)));
259
260 // Hit an area on layers[1].
261 hit = GetTargetAPZC(ScreenPoint(25, 25));
262 EXPECT_EQ(apzc1, hit.get());
263 EXPECT_EQ(ParentLayerPoint(25, 25),
264 transformToApzc.TransformPoint(ScreenPoint(25, 25)));
265 EXPECT_EQ(ScreenPoint(25, 25),
266 transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
267
268 // Hit an area on layers[3].
269 hit = GetTargetAPZC(ScreenPoint(25, 75));
270 EXPECT_EQ(apzc3, hit.get());
271 // transformToApzc should unapply layers[2]'s transform
272 EXPECT_EQ(ParentLayerPoint(12.5, 75),
273 transformToApzc.TransformPoint(ScreenPoint(25, 75)));
274 // and transformToGecko should reapply it
275 EXPECT_EQ(ScreenPoint(25, 75),
276 transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
277
278 // Hit an area on layers[3] that would be on the root if layers[2]
279 // weren't transformed.
280 hit = GetTargetAPZC(ScreenPoint(75, 75));
281 EXPECT_EQ(apzc3, hit.get());
282 // transformToApzc should unapply layers[2]'s transform
283 EXPECT_EQ(ParentLayerPoint(37.5, 75),
284 transformToApzc.TransformPoint(ScreenPoint(75, 75)));
285 // and transformToGecko should reapply it
286 EXPECT_EQ(ScreenPoint(75, 75),
287 transformToGecko.TransformPoint(ParentLayerPoint(37.5, 75)));
288
289 // Pan the root layer upward by 50 pixels.
290 // This causes layers[1] to scroll out of view, and an async transform
291 // of -50 to be set on the root layer.
292 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(3);
293
294 // This first pan will move the APZC by 50 pixels, and dispatch a paint
295 // request. Since this paint request is in the queue to Gecko,
296 // transformToGecko will take it into account.
297 Pan(apzcroot, 100, 50, PanOptions::NoFling);
298
299 // Hit where layers[3] used to be. It should now hit the root.
300 hit = GetTargetAPZC(ScreenPoint(75, 75));
301 EXPECT_EQ(apzcroot, hit.get());
302 // transformToApzc doesn't unapply the root's own async transform
303 EXPECT_EQ(ParentLayerPoint(75, 75),
304 transformToApzc.TransformPoint(ScreenPoint(75, 75)));
305 // and transformToGecko unapplies it and then reapplies it, because by the
306 // time the event being transformed reaches Gecko the new paint request will
307 // have been handled.
308 EXPECT_EQ(ScreenPoint(75, 75),
309 transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
310
311 // Hit where layers[1] used to be and where layers[3] should now be.
312 hit = GetTargetAPZC(ScreenPoint(25, 25));
313 EXPECT_EQ(apzc3, hit.get());
314 // transformToApzc unapplies both layers[2]'s css transform and the root's
315 // async transform
316 EXPECT_EQ(ParentLayerPoint(12.5, 75),
317 transformToApzc.TransformPoint(ScreenPoint(25, 25)));
318 // transformToGecko reapplies both the css transform and the async transform
319 // because we have already issued a paint request with it.
320 EXPECT_EQ(ScreenPoint(25, 25),
321 transformToGecko.TransformPoint(ParentLayerPoint(12.5, 75)));
322
323 // This second pan will move the APZC by another 50 pixels.
324 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(3);
325 Pan(apzcroot, 100, 50, PanOptions::NoFling);
326
327 // Hit where layers[3] used to be. It should now hit the root.
328 hit = GetTargetAPZC(ScreenPoint(75, 75));
329 EXPECT_EQ(apzcroot, hit.get());
330 // transformToApzc doesn't unapply the root's own async transform
331 EXPECT_EQ(ParentLayerPoint(75, 75),
332 transformToApzc.TransformPoint(ScreenPoint(75, 75)));
333 // transformToGecko unapplies the full async transform of -100 pixels
334 EXPECT_EQ(ScreenPoint(75, 75),
335 transformToGecko.TransformPoint(ParentLayerPoint(75, 75)));
336
337 // Hit where layers[1] used to be. It should now hit the root.
338 hit = GetTargetAPZC(ScreenPoint(25, 25));
339 EXPECT_EQ(apzcroot, hit.get());
340 // transformToApzc doesn't unapply the root's own async transform
341 EXPECT_EQ(ParentLayerPoint(25, 25),
342 transformToApzc.TransformPoint(ScreenPoint(25, 25)));
343 // transformToGecko unapplies the full async transform of -100 pixels
344 EXPECT_EQ(ScreenPoint(25, 25),
345 transformToGecko.TransformPoint(ParentLayerPoint(25, 25)));
346 }
347
TEST_F(APZHitTestingTesterLayersOnly,HitTesting3)348 TEST_F(APZHitTestingTesterLayersOnly, HitTesting3) {
349 const char* layerTreeSyntax = "c(t)";
350 // LayerID 0 1
351 nsIntRegion layerVisibleRegions[] = {nsIntRegion(IntRect(0, 0, 200, 200)),
352 nsIntRegion(IntRect(0, 0, 50, 50))};
353 Matrix4x4 transforms[] = {Matrix4x4(), Matrix4x4::Scaling(2, 2, 1)};
354 root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, transforms, lm,
355 layers);
356 // No actual room to scroll
357 SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
358 CSSRect(0, 0, 200, 200));
359 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID + 1,
360 CSSRect(0, 0, 50, 50));
361
362 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
363
364 UpdateHitTestingTree();
365
366 RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 75));
367 EXPECT_EQ(ApzcOf(layers[1]), hit.get());
368 }
369
TEST_F(APZHitTestingTesterLayersOnly,ComplexMultiLayerTree)370 TEST_F(APZHitTestingTesterLayersOnly, ComplexMultiLayerTree) {
371 CreateComplexMultiLayerTree();
372 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
373 UpdateHitTestingTree();
374
375 /* The layer tree looks like this:
376
377 0
378 |----|--+--|----|
379 1 2 4 5
380 | /|\
381 3 6 8 9
382 |
383 7
384
385 Layers 1,2 have the same APZC
386 Layers 4,6,8 have the same APZC
387 Layer 7 has an APZC
388 Layer 9 has an APZC
389 */
390
391 TestAsyncPanZoomController* nullAPZC = nullptr;
392 // Ensure all the scrollable layers have an APZC
393 EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
394 EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
395 EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
396 EXPECT_FALSE(layers[3]->HasScrollableFrameMetrics());
397 EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
398 EXPECT_FALSE(layers[5]->HasScrollableFrameMetrics());
399 EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
400 EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
401 EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
402 EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
403 // Ensure those that scroll together have the same APZCs
404 EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
405 EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
406 EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
407 // Ensure those that don't scroll together have different APZCs
408 EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
409 EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
410 EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
411 EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
412 EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
413 EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
414 // Ensure the APZC parent chains are set up correctly
415 TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
416 TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
417 TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
418 TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
419 EXPECT_EQ(nullptr, layers1_2->GetParent());
420 EXPECT_EQ(nullptr, layers4_6_8->GetParent());
421 EXPECT_EQ(layers4_6_8, layer7->GetParent());
422 EXPECT_EQ(nullptr, layer9->GetParent());
423 // Ensure the hit-testing tree looks like the layer tree
424 RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
425 RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
426 RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
427 RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
428 RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
429 RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
430 RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
431 RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
432 RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
433 RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
434 EXPECT_EQ(nullptr, node1->GetPrevSibling());
435 EXPECT_EQ(nullptr, node3->GetPrevSibling());
436 EXPECT_EQ(nullptr, node6->GetPrevSibling());
437 EXPECT_EQ(nullptr, node7->GetPrevSibling());
438 EXPECT_EQ(nullptr, node1->GetLastChild());
439 EXPECT_EQ(nullptr, node3->GetLastChild());
440 EXPECT_EQ(nullptr, node4->GetLastChild());
441 EXPECT_EQ(nullptr, node7->GetLastChild());
442 EXPECT_EQ(nullptr, node8->GetLastChild());
443 EXPECT_EQ(nullptr, node9->GetLastChild());
444
445 RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
446 EXPECT_EQ(ApzcOf(layers[1]), hit.get());
447 hit = GetTargetAPZC(ScreenPoint(275, 375));
448 EXPECT_EQ(ApzcOf(layers[9]), hit.get());
449 hit = GetTargetAPZC(ScreenPoint(250, 100));
450 EXPECT_EQ(ApzcOf(layers[7]), hit.get());
451 }
452
TEST_F(APZHitTestingTester,TestRepaintFlushOnNewInputBlock)453 TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
454 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", false);
455
456 // The main purpose of this test is to verify that touch-start events (or
457 // anything that starts a new input block) don't ever get untransformed. This
458 // should always hold because the APZ code should flush repaints when we start
459 // a new input block and the transform to gecko space should be empty.
460
461 CreateSimpleScrollingLayer();
462 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
463 UpdateHitTestingTree();
464 RefPtr<TestAsyncPanZoomController> apzcroot = ApzcOf(root);
465
466 // At this point, the following holds (all coordinates in screen pixels):
467 // layers[0] has content from (0,0)-(500,500), clipped by composition bounds
468 // (0,0)-(200,200)
469
470 MockFunction<void(std::string checkPointName)> check;
471
472 {
473 InSequence s;
474
475 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
476 EXPECT_CALL(check, Call("post-first-touch-start"));
477 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
478 EXPECT_CALL(check, Call("post-second-fling"));
479 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
480 EXPECT_CALL(check, Call("post-second-touch-start"));
481 }
482
483 // This first pan will move the APZC by 50 pixels, and dispatch a paint
484 // request.
485 Pan(apzcroot, 100, 50, PanOptions::NoFling);
486
487 // Verify that a touch start doesn't get untransformed
488 ScreenIntPoint touchPoint(50, 50);
489 MultiTouchInput mti =
490 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
491 mti.mTouches.AppendElement(
492 SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
493
494 EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
495 manager->ReceiveInputEvent(mti).GetStatus());
496 EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
497 check.Call("post-first-touch-start");
498
499 // Send a touchend to clear state
500 mti.mType = MultiTouchInput::MULTITOUCH_END;
501 manager->ReceiveInputEvent(mti);
502
503 mcc->AdvanceByMillis(1000);
504
505 // Now do two pans. The first of these will dispatch a repaint request, as
506 // above. The second will get stuck in the paint throttler because the first
507 // one doesn't get marked as "completed", so this will result in a non-empty
508 // LD transform. (Note that any outstanding repaint requests from the first
509 // half of this test don't impact this half because we advance the time by 1
510 // second, which will trigger the max-wait-exceeded codepath in the paint
511 // throttler).
512 Pan(apzcroot, 100, 50, PanOptions::NoFling);
513 check.Call("post-second-fling");
514 Pan(apzcroot, 100, 50, PanOptions::NoFling);
515
516 // Ensure that a touch start again doesn't get untransformed by flushing
517 // a repaint
518 mti.mType = MultiTouchInput::MULTITOUCH_START;
519 EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
520 manager->ReceiveInputEvent(mti).GetStatus());
521 EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
522 check.Call("post-second-touch-start");
523
524 mti.mType = MultiTouchInput::MULTITOUCH_END;
525 EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
526 manager->ReceiveInputEvent(mti).GetStatus());
527 EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
528 }
529
TEST_F(APZHitTestingTester,TestRepaintFlushOnWheelEvents)530 TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
531 // The purpose of this test is to ensure that wheel events trigger a repaint
532 // flush as per bug 1166871, and that the wheel event untransform is a no-op.
533
534 CreateSimpleScrollingLayer();
535 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
536 UpdateHitTestingTree();
537 TestAsyncPanZoomController* apzcroot = ApzcOf(root);
538
539 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
540 ScreenPoint origin(100, 50);
541 for (int i = 0; i < 3; i++) {
542 ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
543 ScrollWheelInput::SCROLLMODE_INSTANT,
544 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 10,
545 false, WheelDeltaAdjustmentStrategy::eNone);
546 EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
547 manager->ReceiveInputEvent(swi).GetStatus());
548 EXPECT_EQ(origin, swi.mOrigin);
549
550 AsyncTransform viewTransform;
551 ParentLayerPoint point;
552 apzcroot->SampleContentTransformForFrame(&viewTransform, point);
553 EXPECT_EQ(0, point.x);
554 EXPECT_EQ((i + 1) * 10, point.y);
555 EXPECT_EQ(0, viewTransform.mTranslation.x);
556 EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
557
558 mcc->AdvanceByMillis(5);
559 }
560 }
561
TEST_F(APZHitTestingTester,TestForceDisableApz)562 TEST_F(APZHitTestingTester, TestForceDisableApz) {
563 CreateSimpleScrollingLayer();
564 DisableApzOn(root);
565 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
566 UpdateHitTestingTree();
567 TestAsyncPanZoomController* apzcroot = ApzcOf(root);
568
569 ScreenPoint origin(100, 50);
570 ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
571 ScrollWheelInput::SCROLLMODE_INSTANT,
572 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 10,
573 false, WheelDeltaAdjustmentStrategy::eNone);
574 EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
575 manager->ReceiveInputEvent(swi).GetStatus());
576 EXPECT_EQ(origin, swi.mOrigin);
577
578 AsyncTransform viewTransform;
579 ParentLayerPoint point;
580 apzcroot->SampleContentTransformForFrame(&viewTransform, point);
581 // Since APZ is force-disabled, we expect to see the async transform via
582 // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode.
583 EXPECT_EQ(0, point.x);
584 EXPECT_EQ(10, point.y);
585 EXPECT_EQ(0, viewTransform.mTranslation.x);
586 EXPECT_EQ(-10, viewTransform.mTranslation.y);
587 viewTransform = apzcroot->GetCurrentAsyncTransform(
588 AsyncPanZoomController::eForCompositing);
589 point = apzcroot->GetCurrentAsyncScrollOffset(
590 AsyncPanZoomController::eForCompositing);
591 EXPECT_EQ(0, point.x);
592 EXPECT_EQ(0, point.y);
593 EXPECT_EQ(0, viewTransform.mTranslation.x);
594 EXPECT_EQ(0, viewTransform.mTranslation.y);
595
596 mcc->AdvanceByMillis(10);
597
598 // With untransforming events we should get normal behaviour (in this case,
599 // no noticeable untransform, because the repaint request already got
600 // flushed).
601 swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
602 ScrollWheelInput::SCROLLMODE_INSTANT,
603 ScrollWheelInput::SCROLLDELTA_PIXEL, origin, 0, 0,
604 false, WheelDeltaAdjustmentStrategy::eNone);
605 EXPECT_EQ(nsEventStatus_eConsumeDoDefault,
606 manager->ReceiveInputEvent(swi).GetStatus());
607 EXPECT_EQ(origin, swi.mOrigin);
608 }
609
TEST_F(APZHitTestingTester,Bug1148350)610 TEST_F(APZHitTestingTester, Bug1148350) {
611 CreateBug1148350LayerTree();
612 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
613 UpdateHitTestingTree();
614
615 MockFunction<void(std::string checkPointName)> check;
616 {
617 InSequence s;
618 EXPECT_CALL(*mcc,
619 HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0,
620 ApzcOf(layers[1])->GetGuid(), _))
621 .Times(1);
622 EXPECT_CALL(check, Call("Tapped without transform"));
623 EXPECT_CALL(*mcc,
624 HandleTap(TapType::eSingleTap, LayoutDevicePoint(100, 100), 0,
625 ApzcOf(layers[1])->GetGuid(), _))
626 .Times(1);
627 EXPECT_CALL(check, Call("Tapped with interleaved transform"));
628 }
629
630 Tap(manager, ScreenIntPoint(100, 100), TimeDuration::FromMilliseconds(100));
631 mcc->RunThroughDelayedTasks();
632 check.Call("Tapped without transform");
633
634 uint64_t blockId =
635 TouchDown(manager, ScreenIntPoint(100, 100), mcc->Time()).mInputBlockId;
636 if (StaticPrefs::layout_css_touch_action_enabled()) {
637 SetDefaultAllowedTouchBehavior(manager, blockId);
638 }
639 mcc->AdvanceByMillis(100);
640
641 layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0, 50, 200, 150)));
642 layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
643 UpdateHitTestingTree();
644
645 TouchUp(manager, ScreenIntPoint(100, 100), mcc->Time());
646 mcc->RunThroughDelayedTasks();
647 check.Call("Tapped with interleaved transform");
648 }
649
TEST_F(APZHitTestingTester,HitTestingRespectsScrollClip_Bug1257288)650 TEST_F(APZHitTestingTester, HitTestingRespectsScrollClip_Bug1257288) {
651 // Create the layer tree.
652 const char* layerTreeSyntax = "c(tt)";
653 // LayerID 0 12
654 nsIntRegion layerVisibleRegion[] = {nsIntRegion(IntRect(0, 0, 200, 200)),
655 nsIntRegion(IntRect(0, 0, 200, 200)),
656 nsIntRegion(IntRect(0, 0, 200, 100))};
657 root =
658 CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
659
660 // Add root scroll metadata to the first painted layer.
661 SetScrollableFrameMetrics(layers[1], ScrollableLayerGuid::START_SCROLL_ID,
662 CSSRect(0, 0, 200, 200));
663
664 // Add root and subframe scroll metadata to the second painted layer.
665 // Give the subframe metadata a scroll clip corresponding to the subframe's
666 // composition bounds.
667 // Importantly, give the layer a layer clip which leaks outside of the
668 // subframe's composition bounds.
669 ScrollMetadata rootMetadata = BuildScrollMetadata(
670 ScrollableLayerGuid::START_SCROLL_ID, CSSRect(0, 0, 200, 200),
671 ParentLayerRect(0, 0, 200, 200));
672 ScrollMetadata subframeMetadata = BuildScrollMetadata(
673 ScrollableLayerGuid::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 200),
674 ParentLayerRect(0, 0, 200, 100));
675 subframeMetadata.SetScrollClip(
676 Some(LayerClip(ParentLayerIntRect(0, 0, 200, 100))));
677 layers[2]->SetScrollMetadata({subframeMetadata, rootMetadata});
678 layers[2]->SetClipRect(Some(ParentLayerIntRect(0, 0, 200, 200)));
679 SetEventRegionsBasedOnBottommostMetrics(layers[2]);
680
681 // Build the hit testing tree.
682 ScopedLayerTreeRegistration registration(LayersId{0}, root, mcc);
683 UpdateHitTestingTree();
684
685 // Pan on a region that's inside layers[2]'s layer clip, but outside
686 // its subframe metadata's scroll clip.
687 Pan(manager, 120, 110);
688
689 // Test that the subframe hasn't scrolled.
690 EXPECT_EQ(CSSPoint(0, 0),
691 ApzcOf(layers[2], 0)->GetFrameMetrics().GetVisualScrollOffset());
692 }
693