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