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* treeShape = "x(xx)";
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     CreateScrollData(treeShape, layerVisibleRegions);
25     SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID);
26     SetScrollableFrameMetrics(layers[1],
27                               ScrollableLayerGuid::START_SCROLL_ID + 1);
28     SetScrollableFrameMetrics(layers[2],
29                               ScrollableLayerGuid::START_SCROLL_ID + 2);
30     SetScrollHandoff(layers[1], root);
31     SetScrollHandoff(layers[2], root);
32 
33     registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
34     UpdateHitTestingTree();
35     rootApzc = ApzcOf(root);
36   }
37 
CreateEventRegionsLayerTree2()38   void CreateEventRegionsLayerTree2() {
39     const char* treeShape = "x(x)";
40     nsIntRegion layerVisibleRegions[] = {
41         nsIntRegion(IntRect(0, 0, 100, 500)),
42         nsIntRegion(IntRect(0, 150, 100, 100)),
43     };
44     CreateScrollData(treeShape, layerVisibleRegions);
45     SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID);
46 
47     registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
48     UpdateHitTestingTree();
49     rootApzc = ApzcOf(root);
50   }
51 
CreateBug1117712LayerTree()52   void CreateBug1117712LayerTree() {
53     const char* treeShape = "x(x(x)x)";
54     // LayerID               0 1 2 3
55     // 0 is the root
56     // 1 is a container layer whose sole purpose to make a non-empty ancestor
57     //   transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
58     //   transforms are different from 3's.
59     // 2 is a small layer that is the actual target
60     // 3 is a big layer obscuring 2 with a dispatch-to-content region
61     nsIntRegion layerVisibleRegions[] = {
62         nsIntRegion(IntRect(0, 0, 100, 100)),
63         nsIntRegion(IntRect(0, 0, 0, 0)),
64         nsIntRegion(IntRect(0, 0, 10, 10)),
65         nsIntRegion(IntRect(0, 0, 100, 100)),
66     };
67     Matrix4x4 layerTransforms[] = {
68         Matrix4x4(),
69         Matrix4x4::Translation(50, 0, 0),
70         Matrix4x4(),
71         Matrix4x4(),
72     };
73     CreateScrollData(treeShape, layerVisibleRegions, layerTransforms);
74 
75     SetScrollableFrameMetrics(layers[2], ScrollableLayerGuid::START_SCROLL_ID,
76                               CSSRect(0, 0, 10, 10));
77     SetScrollableFrameMetrics(layers[3],
78                               ScrollableLayerGuid::START_SCROLL_ID + 1,
79                               CSSRect(0, 0, 100, 100));
80     SetScrollHandoff(layers[3], layers[2]);
81 
82     registration = MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, mcc);
83     UpdateHitTestingTree();
84   }
85 };
86 
87 class APZEventRegionsTesterMock : public APZEventRegionsTester {
88  public:
APZEventRegionsTesterMock()89   APZEventRegionsTesterMock() { CreateMockHitTester(); }
90 };
91 
TEST_F(APZEventRegionsTesterMock,HitRegionImmediateResponse)92 TEST_F(APZEventRegionsTesterMock, HitRegionImmediateResponse) {
93   CreateEventRegionsLayerTree1();
94 
95   TestAsyncPanZoomController* root = ApzcOf(layers[0]);
96   TestAsyncPanZoomController* left = ApzcOf(layers[1]);
97   TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
98 
99   MockFunction<void(std::string checkPointName)> check;
100   {
101     InSequence s;
102     EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _))
103         .Times(1);
104     EXPECT_CALL(check, Call("Tapped on left"));
105     EXPECT_CALL(*mcc,
106                 HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _))
107         .Times(1);
108     EXPECT_CALL(check, Call("Tapped on bottom"));
109     EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _))
110         .Times(1);
111     EXPECT_CALL(check, Call("Tapped on root"));
112     EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
113     EXPECT_CALL(*mcc,
114                 HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _))
115         .Times(1);
116     EXPECT_CALL(check, Call("Tapped on bottom again"));
117     EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _))
118         .Times(1);
119     EXPECT_CALL(check, Call("Tapped on left this time"));
120   }
121 
122   TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
123 
124   // Tap in the exposed hit regions of each of the layers once and ensure
125   // the clicks are dispatched right away
126   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1);
127   Tap(manager, ScreenIntPoint(10, 10), tapDuration);
128   mcc->RunThroughDelayedTasks();  // this runs the tap event
129   check.Call("Tapped on left");
130   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 2);
131   Tap(manager, ScreenIntPoint(110, 110), tapDuration);
132   mcc->RunThroughDelayedTasks();  // this runs the tap event
133   check.Call("Tapped on bottom");
134   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
135   Tap(manager, ScreenIntPoint(110, 10), tapDuration);
136   mcc->RunThroughDelayedTasks();  // this runs the tap event
137   check.Call("Tapped on root");
138 
139   // Now tap on the dispatch-to-content region where the layers overlap
140   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 2,
141                      {CompositorHitTestFlags::eVisibleToHitTest,
142                       CompositorHitTestFlags::eIrregularArea});
143   Tap(manager, ScreenIntPoint(10, 110), tapDuration);
144   mcc->RunThroughDelayedTasks();  // this runs the main-thread timeout
145   check.Call("Tap pending on d-t-c region");
146   mcc->RunThroughDelayedTasks();  // this runs the tap event
147   check.Call("Tapped on bottom again");
148 
149   // Now let's do that again, but simulate a main-thread response
150   uint64_t inputBlockId = 0;
151   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 2,
152                      {CompositorHitTestFlags::eVisibleToHitTest,
153                       CompositorHitTestFlags::eIrregularArea});
154   Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId);
155   nsTArray<ScrollableLayerGuid> targets;
156   targets.AppendElement(left->GetGuid());
157   manager->SetTargetAPZC(inputBlockId, targets);
158   while (mcc->RunThroughDelayedTasks())
159     ;  // this runs the tap event
160   check.Call("Tapped on left this time");
161 }
162 
TEST_F(APZEventRegionsTesterMock,HitRegionAccumulatesChildren)163 TEST_F(APZEventRegionsTesterMock, HitRegionAccumulatesChildren) {
164   CreateEventRegionsLayerTree2();
165 
166   // Tap in the area of the child layer that's not directly included in the
167   // parent layer's hit region. Verify that it comes out of the APZC's
168   // content controller, which indicates the input events got routed correctly
169   // to the APZC.
170   EXPECT_CALL(*mcc,
171               HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _))
172       .Times(1);
173   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID);
174   Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100));
175 }
176 
TEST_F(APZEventRegionsTesterMock,Bug1117712)177 TEST_F(APZEventRegionsTesterMock, Bug1117712) {
178   CreateBug1117712LayerTree();
179 
180   TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
181 
182   // These touch events should hit the dispatch-to-content region of layers[3]
183   // and so get queued with that APZC as the tentative target.
184   uint64_t inputBlockId = 0;
185   QueueMockHitResult(ScrollableLayerGuid::START_SCROLL_ID + 1,
186                      {CompositorHitTestFlags::eVisibleToHitTest,
187                       CompositorHitTestFlags::eIrregularArea});
188   Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100),
189       nullptr, &inputBlockId);
190   // But now we tell the APZ that really it hit layers[2], and expect the tap
191   // to be delivered at the correct coordinates.
192   EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0,
193                               apzc2->GetGuid(), _))
194       .Times(1);
195 
196   nsTArray<ScrollableLayerGuid> targets;
197   targets.AppendElement(apzc2->GetGuid());
198   manager->SetTargetAPZC(inputBlockId, targets);
199 }
200