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