/src/mozilla-central/gfx/layers/apz/test/gtest/TestEventRegions.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
16 | 0 | void CreateEventRegionsLayerTree1() { |
17 | 0 | const char* layerTreeSyntax = "c(tt)"; |
18 | 0 | nsIntRegion layerVisibleRegions[] = { |
19 | 0 | nsIntRegion(IntRect(0, 0, 200, 200)), // root |
20 | 0 | nsIntRegion(IntRect(0, 0, 100, 200)), // left half |
21 | 0 | nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half |
22 | 0 | }; |
23 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); |
24 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); |
25 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1); |
26 | 0 | SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2); |
27 | 0 | SetScrollHandoff(layers[1], root); |
28 | 0 | SetScrollHandoff(layers[2], root); |
29 | 0 |
|
30 | 0 | // Set up the event regions over a 200x200 area. The root layer has the |
31 | 0 | // whole 200x200 as the hit region; layers[1] has the left half and |
32 | 0 | // layers[2] has the bottom half. The bottom-left 100x100 area is also |
33 | 0 | // in the d-t-c region for both layers[1] and layers[2] (but layers[2] is |
34 | 0 | // on top so it gets the events by default if the main thread doesn't |
35 | 0 | // respond). |
36 | 0 | EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200))); |
37 | 0 | root->SetEventRegions(regions); |
38 | 0 | regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100)); |
39 | 0 | regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200)); |
40 | 0 | layers[1]->SetEventRegions(regions); |
41 | 0 | regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100)); |
42 | 0 | layers[2]->SetEventRegions(regions); |
43 | 0 |
|
44 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
45 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
46 | 0 | rootApzc = ApzcOf(root); |
47 | 0 | } |
48 | | |
49 | 0 | void CreateEventRegionsLayerTree2() { |
50 | 0 | const char* layerTreeSyntax = "c(t)"; |
51 | 0 | nsIntRegion layerVisibleRegions[] = { |
52 | 0 | nsIntRegion(IntRect(0, 0, 100, 500)), |
53 | 0 | nsIntRegion(IntRect(0, 150, 100, 100)), |
54 | 0 | }; |
55 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); |
56 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); |
57 | 0 |
|
58 | 0 | // Set up the event regions so that the child thebes layer is positioned far |
59 | 0 | // away from the scrolling container layer. |
60 | 0 | EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100))); |
61 | 0 | root->SetEventRegions(regions); |
62 | 0 | regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100)); |
63 | 0 | layers[1]->SetEventRegions(regions); |
64 | 0 |
|
65 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
66 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
67 | 0 | rootApzc = ApzcOf(root); |
68 | 0 | } |
69 | | |
70 | 0 | void CreateObscuringLayerTree() { |
71 | 0 | const char* layerTreeSyntax = "c(c(t)t)"; |
72 | 0 | // LayerID 0 1 2 3 |
73 | 0 | // 0 is the root. |
74 | 0 | // 1 is a parent scrollable layer. |
75 | 0 | // 2 is a child scrollable layer. |
76 | 0 | // 3 is the Obscurer, who ruins everything. |
77 | 0 | nsIntRegion layerVisibleRegions[] = { |
78 | 0 | // x coordinates are uninteresting |
79 | 0 | nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200] |
80 | 0 | nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200] |
81 | 0 | nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150] |
82 | 0 | nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200] |
83 | 0 | }; |
84 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); |
85 | 0 |
|
86 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200)); |
87 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 300)); |
88 | 0 | SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 200, 100)); |
89 | 0 | SetScrollHandoff(layers[2], layers[1]); |
90 | 0 | SetScrollHandoff(layers[1], root); |
91 | 0 |
|
92 | 0 | EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200))); |
93 | 0 | root->SetEventRegions(regions); |
94 | 0 | regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300)); |
95 | 0 | layers[1]->SetEventRegions(regions); |
96 | 0 | regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100)); |
97 | 0 | layers[2]->SetEventRegions(regions); |
98 | 0 |
|
99 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
100 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
101 | 0 | rootApzc = ApzcOf(root); |
102 | 0 | } |
103 | | |
104 | 0 | void CreateBug1119497LayerTree() { |
105 | 0 | const char* layerTreeSyntax = "c(tt)"; |
106 | 0 | // LayerID 0 12 |
107 | 0 | // 0 is the root and has an APZC |
108 | 0 | // 1 is behind 2 and has an APZC |
109 | 0 | // 2 entirely covers 1 and should take all the input events, but has no APZC |
110 | 0 | // so hits to 2 should go to to the root APZC |
111 | 0 | nsIntRegion layerVisibleRegions[] = { |
112 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
113 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
114 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
115 | 0 | }; |
116 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers); |
117 | 0 |
|
118 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID); |
119 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1); |
120 | 0 |
|
121 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
122 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
123 | 0 | } |
124 | | |
125 | 0 | void CreateBug1117712LayerTree() { |
126 | 0 | const char* layerTreeSyntax = "c(c(t)t)"; |
127 | 0 | // LayerID 0 1 2 3 |
128 | 0 | // 0 is the root |
129 | 0 | // 1 is a container layer whose sole purpose to make a non-empty ancestor |
130 | 0 | // transform for 2, so that 2's screen-to-apzc and apzc-to-gecko |
131 | 0 | // transforms are different from 3's. |
132 | 0 | // 2 is a small layer that is the actual target |
133 | 0 | // 3 is a big layer obscuring 2 with a dispatch-to-content region |
134 | 0 | nsIntRegion layerVisibleRegions[] = { |
135 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
136 | 0 | nsIntRegion(IntRect(0, 0, 0, 0)), |
137 | 0 | nsIntRegion(IntRect(0, 0, 10, 10)), |
138 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
139 | 0 | }; |
140 | 0 | Matrix4x4 layerTransforms[] = { |
141 | 0 | Matrix4x4(), |
142 | 0 | Matrix4x4::Translation(50, 0, 0), |
143 | 0 | Matrix4x4(), |
144 | 0 | Matrix4x4(), |
145 | 0 | }; |
146 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, layerTransforms, lm, layers); |
147 | 0 |
|
148 | 0 | SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 10, 10)); |
149 | 0 | SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100)); |
150 | 0 | SetScrollHandoff(layers[3], layers[2]); |
151 | 0 |
|
152 | 0 | EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10))); |
153 | 0 | layers[2]->SetEventRegions(regions); |
154 | 0 | regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100)); |
155 | 0 | regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100)); |
156 | 0 | layers[3]->SetEventRegions(regions); |
157 | 0 |
|
158 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
159 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
160 | 0 | } |
161 | | }; |
162 | | |
163 | 0 | TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) { |
164 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
165 | 0 |
|
166 | 0 | CreateEventRegionsLayerTree1(); |
167 | 0 |
|
168 | 0 | TestAsyncPanZoomController* root = ApzcOf(layers[0]); |
169 | 0 | TestAsyncPanZoomController* left = ApzcOf(layers[1]); |
170 | 0 | TestAsyncPanZoomController* bottom = ApzcOf(layers[2]); |
171 | 0 |
|
172 | 0 | MockFunction<void(std::string checkPointName)> check; |
173 | 0 | { |
174 | 0 | InSequence s; |
175 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1); |
176 | 0 | EXPECT_CALL(check, Call("Tapped on left")); |
177 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1); |
178 | 0 | EXPECT_CALL(check, Call("Tapped on bottom")); |
179 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, root->GetGuid(), _)).Times(1); |
180 | 0 | EXPECT_CALL(check, Call("Tapped on root")); |
181 | 0 | EXPECT_CALL(check, Call("Tap pending on d-t-c region")); |
182 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, bottom->GetGuid(), _)).Times(1); |
183 | 0 | EXPECT_CALL(check, Call("Tapped on bottom again")); |
184 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, left->GetGuid(), _)).Times(1); |
185 | 0 | EXPECT_CALL(check, Call("Tapped on left this time")); |
186 | 0 | } |
187 | 0 |
|
188 | 0 | TimeDuration tapDuration = TimeDuration::FromMilliseconds(100); |
189 | 0 |
|
190 | 0 | // Tap in the exposed hit regions of each of the layers once and ensure |
191 | 0 | // the clicks are dispatched right away |
192 | 0 | Tap(manager, ScreenIntPoint(10, 10), tapDuration); |
193 | 0 | mcc->RunThroughDelayedTasks(); // this runs the tap event |
194 | 0 | check.Call("Tapped on left"); |
195 | 0 | Tap(manager, ScreenIntPoint(110, 110), tapDuration); |
196 | 0 | mcc->RunThroughDelayedTasks(); // this runs the tap event |
197 | 0 | check.Call("Tapped on bottom"); |
198 | 0 | Tap(manager, ScreenIntPoint(110, 10), tapDuration); |
199 | 0 | mcc->RunThroughDelayedTasks(); // this runs the tap event |
200 | 0 | check.Call("Tapped on root"); |
201 | 0 |
|
202 | 0 | // Now tap on the dispatch-to-content region where the layers overlap |
203 | 0 | Tap(manager, ScreenIntPoint(10, 110), tapDuration); |
204 | 0 | mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout |
205 | 0 | check.Call("Tap pending on d-t-c region"); |
206 | 0 | mcc->RunThroughDelayedTasks(); // this runs the tap event |
207 | 0 | check.Call("Tapped on bottom again"); |
208 | 0 |
|
209 | 0 | // Now let's do that again, but simulate a main-thread response |
210 | 0 | uint64_t inputBlockId = 0; |
211 | 0 | Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId); |
212 | 0 | nsTArray<ScrollableLayerGuid> targets; |
213 | 0 | targets.AppendElement(left->GetGuid()); |
214 | 0 | manager->SetTargetAPZC(inputBlockId, targets); |
215 | 0 | while (mcc->RunThroughDelayedTasks()); // this runs the tap event |
216 | 0 | check.Call("Tapped on left this time"); |
217 | 0 | } |
218 | | |
219 | 0 | TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) { |
220 | 0 | CreateEventRegionsLayerTree2(); |
221 | 0 |
|
222 | 0 | // Tap in the area of the child layer that's not directly included in the |
223 | 0 | // parent layer's hit region. Verify that it comes out of the APZC's |
224 | 0 | // content controller, which indicates the input events got routed correctly |
225 | 0 | // to the APZC. |
226 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1); |
227 | 0 | Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100)); |
228 | 0 | } |
229 | | |
230 | 0 | TEST_F(APZEventRegionsTester, Obscuration) { |
231 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
232 | 0 |
|
233 | 0 | CreateObscuringLayerTree(); |
234 | 0 | ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc); |
235 | 0 |
|
236 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
237 | 0 |
|
238 | 0 | RefPtr<TestAsyncPanZoomController> parent = ApzcOf(layers[1]); |
239 | 0 | TestAsyncPanZoomController* child = ApzcOf(layers[2]); |
240 | 0 |
|
241 | 0 | Pan(parent, 75, 25, PanOptions::NoFling); |
242 | 0 |
|
243 | 0 | gfx::CompositorHitTestInfo result; |
244 | 0 | RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result); |
245 | 0 | EXPECT_EQ(child, hit.get()); |
246 | 0 | EXPECT_EQ(CompositorHitTestInfo::eVisibleToHitTest, result); |
247 | 0 | } |
248 | | |
249 | 0 | TEST_F(APZEventRegionsTester, Bug1119497) { |
250 | 0 | CreateBug1119497LayerTree(); |
251 | 0 |
|
252 | 0 | gfx::CompositorHitTestInfo result; |
253 | 0 | RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 50), &result); |
254 | 0 | // We should hit layers[2], so |result| will be eVisibleToHitTest but there's no |
255 | 0 | // actual APZC on layers[2], so it will be the APZC of the root layer. |
256 | 0 | EXPECT_EQ(ApzcOf(layers[0]), hit.get()); |
257 | 0 | EXPECT_EQ(CompositorHitTestInfo::eVisibleToHitTest, result); |
258 | 0 | } |
259 | | |
260 | 0 | TEST_F(APZEventRegionsTester, Bug1117712) { |
261 | 0 | CreateBug1117712LayerTree(); |
262 | 0 |
|
263 | 0 | TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]); |
264 | 0 |
|
265 | 0 | // These touch events should hit the dispatch-to-content region of layers[3] |
266 | 0 | // and so get queued with that APZC as the tentative target. |
267 | 0 | uint64_t inputBlockId = 0; |
268 | 0 | Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId); |
269 | 0 | // But now we tell the APZ that really it hit layers[2], and expect the tap |
270 | 0 | // to be delivered at the correct coordinates. |
271 | 0 | EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0, apzc2->GetGuid(), _)).Times(1); |
272 | 0 |
|
273 | 0 | nsTArray<ScrollableLayerGuid> targets; |
274 | 0 | targets.AppendElement(apzc2->GetGuid()); |
275 | 0 | manager->SetTargetAPZC(inputBlockId, targets); |
276 | 0 | } |