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