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