/src/mozilla-central/gfx/layers/apz/test/gtest/TestScrollHandoff.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 APZScrollHandoffTester : public APZCTreeManagerTester { |
12 | | protected: |
13 | | UniquePtr<ScopedLayerTreeRegistration> registration; |
14 | | TestAsyncPanZoomController* rootApzc; |
15 | | |
16 | 0 | void CreateScrollHandoffLayerTree1() { |
17 | 0 | const char* layerTreeSyntax = "c(t)"; |
18 | 0 | nsIntRegion layerVisibleRegion[] = { |
19 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
20 | 0 | nsIntRegion(IntRect(0, 50, 100, 50)) |
21 | 0 | }; |
22 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); |
23 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200)); |
24 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100)); |
25 | 0 | SetScrollHandoff(layers[1], root); |
26 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
27 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
28 | 0 | rootApzc = ApzcOf(root); |
29 | 0 | rootApzc->GetFrameMetrics().SetIsRootContent(true); // make root APZC zoomable |
30 | 0 | } |
31 | | |
32 | 0 | void CreateScrollHandoffLayerTree2() { |
33 | 0 | const char* layerTreeSyntax = "c(c(t))"; |
34 | 0 | nsIntRegion layerVisibleRegion[] = { |
35 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
36 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
37 | 0 | nsIntRegion(IntRect(0, 50, 100, 50)) |
38 | 0 | }; |
39 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); |
40 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200)); |
41 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200)); |
42 | 0 | SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100)); |
43 | 0 | SetScrollHandoff(layers[1], root); |
44 | 0 | SetScrollHandoff(layers[2], layers[1]); |
45 | 0 | // No ScopedLayerTreeRegistration as that just needs to be done once per test |
46 | 0 | // and this is the second layer tree for a particular test. |
47 | 0 | MOZ_ASSERT(registration); |
48 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
49 | 0 | rootApzc = ApzcOf(root); |
50 | 0 | } |
51 | | |
52 | 0 | void CreateScrollHandoffLayerTree3() { |
53 | 0 | const char* layerTreeSyntax = "c(c(t)c(t))"; |
54 | 0 | nsIntRegion layerVisibleRegion[] = { |
55 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), // root |
56 | 0 | nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling parent 1 |
57 | 0 | nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling child 1 |
58 | 0 | nsIntRegion(IntRect(0, 50, 100, 50)), // scrolling parent 2 |
59 | 0 | nsIntRegion(IntRect(0, 50, 100, 50)) // scrolling child 2 |
60 | 0 | }; |
61 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); |
62 | 0 | SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 100)); |
63 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100)); |
64 | 0 | SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100)); |
65 | 0 | SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100)); |
66 | 0 | SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 4, CSSRect(0, 50, 100, 100)); |
67 | 0 | SetScrollHandoff(layers[1], layers[0]); |
68 | 0 | SetScrollHandoff(layers[3], layers[0]); |
69 | 0 | SetScrollHandoff(layers[2], layers[1]); |
70 | 0 | SetScrollHandoff(layers[4], layers[3]); |
71 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
72 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
73 | 0 | } |
74 | | |
75 | | // Creates a layer tree with a parent layer that is only scrollable |
76 | | // horizontally, and a child layer that is only scrollable vertically. |
77 | 0 | void CreateScrollHandoffLayerTree4() { |
78 | 0 | const char* layerTreeSyntax = "c(t)"; |
79 | 0 | nsIntRegion layerVisibleRegion[] = { |
80 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), |
81 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)) |
82 | 0 | }; |
83 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); |
84 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 100)); |
85 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200)); |
86 | 0 | SetScrollHandoff(layers[1], root); |
87 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
88 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
89 | 0 | rootApzc = ApzcOf(root); |
90 | 0 | } |
91 | | |
92 | 0 | void CreateScrollgrabLayerTree(bool makeParentScrollable = true) { |
93 | 0 | const char* layerTreeSyntax = "c(t)"; |
94 | 0 | nsIntRegion layerVisibleRegion[] = { |
95 | 0 | nsIntRegion(IntRect(0, 0, 100, 100)), // scroll-grabbing parent |
96 | 0 | nsIntRegion(IntRect(0, 20, 100, 80)) // child |
97 | 0 | }; |
98 | 0 | root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); |
99 | 0 | float parentHeight = makeParentScrollable ? 120 : 100; |
100 | 0 | SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, parentHeight)); |
101 | 0 | SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200)); |
102 | 0 | SetScrollHandoff(layers[1], root); |
103 | 0 | registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); |
104 | 0 | manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); |
105 | 0 | rootApzc = ApzcOf(root); |
106 | 0 | rootApzc->GetScrollMetadata().SetHasScrollgrab(true); |
107 | 0 | } |
108 | | |
109 | 0 | void TestFlingAcceleration() { |
110 | 0 | // Jack up the fling acceleration multiplier so we can easily determine |
111 | 0 | // whether acceleration occured. |
112 | 0 | const float kAcceleration = 100.0f; |
113 | 0 | SCOPED_GFX_PREF(APZFlingAccelBaseMultiplier, float, kAcceleration); |
114 | 0 | SCOPED_GFX_PREF(APZFlingAccelMinVelocity, float, 0.0); |
115 | 0 |
|
116 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
117 | 0 |
|
118 | 0 | // Pan once, enough to fully scroll the scrollgrab parent and then scroll |
119 | 0 | // and fling the child. |
120 | 0 | Pan(manager, 70, 40); |
121 | 0 |
|
122 | 0 | // Give the fling animation a chance to start. |
123 | 0 | SampleAnimationsOnce(); |
124 | 0 |
|
125 | 0 | float childVelocityAfterFling1 = childApzc->GetVelocityVector().y; |
126 | 0 |
|
127 | 0 | // Pan again. |
128 | 0 | Pan(manager, 70, 40); |
129 | 0 |
|
130 | 0 | // Give the fling animation a chance to start. |
131 | 0 | // This time it should be accelerated. |
132 | 0 | SampleAnimationsOnce(); |
133 | 0 |
|
134 | 0 | float childVelocityAfterFling2 = childApzc->GetVelocityVector().y; |
135 | 0 |
|
136 | 0 | // We should have accelerated once. |
137 | 0 | // The division by 2 is to account for friction. |
138 | 0 | EXPECT_GT(childVelocityAfterFling2, |
139 | 0 | childVelocityAfterFling1 * kAcceleration / 2); |
140 | 0 |
|
141 | 0 | // We should not have accelerated twice. |
142 | 0 | // The division by 4 is to account for friction. |
143 | 0 | EXPECT_LE(childVelocityAfterFling2, |
144 | 0 | childVelocityAfterFling1 * kAcceleration * kAcceleration / 4); |
145 | 0 | } |
146 | | |
147 | 0 | void TestCrossApzcAxisLock() { |
148 | 0 | SCOPED_GFX_PREF(APZAxisLockMode, int32_t, 1); |
149 | 0 |
|
150 | 0 | CreateScrollHandoffLayerTree1(); |
151 | 0 |
|
152 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
153 | 0 | Pan(childApzc, ScreenIntPoint(10, 60), ScreenIntPoint(15, 90), |
154 | 0 | PanOptions::KeepFingerDown | PanOptions::ExactCoordinates); |
155 | 0 |
|
156 | 0 | childApzc->AssertAxisLocked(ScrollDirection::eVertical); |
157 | 0 | } |
158 | | }; |
159 | | |
160 | | // Here we test that if the processing of a touch block is deferred while we |
161 | | // wait for content to send a prevent-default message, overscroll is still |
162 | | // handed off correctly when the block is processed. |
163 | 0 | TEST_F(APZScrollHandoffTester, DeferredInputEventProcessing) { |
164 | 0 | // Set up the APZC tree. |
165 | 0 | CreateScrollHandoffLayerTree1(); |
166 | 0 |
|
167 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
168 | 0 |
|
169 | 0 | // Enable touch-listeners so that we can separate the queueing of input |
170 | 0 | // events from them being processed. |
171 | 0 | childApzc->SetWaitForMainThread(); |
172 | 0 |
|
173 | 0 | // Queue input events for a pan. |
174 | 0 | uint64_t blockId = 0; |
175 | 0 | Pan(childApzc, 90, 30, PanOptions::NoFling, nullptr, nullptr, &blockId); |
176 | 0 |
|
177 | 0 | // Allow the pan to be processed. |
178 | 0 | childApzc->ContentReceivedInputBlock(blockId, false); |
179 | 0 | childApzc->ConfirmTarget(blockId); |
180 | 0 |
|
181 | 0 | // Make sure overscroll was handed off correctly. |
182 | 0 | EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y); |
183 | 0 | EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y); |
184 | 0 | } |
185 | | |
186 | | // Here we test that if the layer structure changes in between two input |
187 | | // blocks being queued, and the first block is only processed after the second |
188 | | // one has been queued, overscroll handoff for the first block follows |
189 | | // the original layer structure while overscroll handoff for the second block |
190 | | // follows the new layer structure. |
191 | 0 | TEST_F(APZScrollHandoffTester, LayerStructureChangesWhileEventsArePending) { |
192 | 0 | // Set up an initial APZC tree. |
193 | 0 | CreateScrollHandoffLayerTree1(); |
194 | 0 |
|
195 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
196 | 0 |
|
197 | 0 | // Enable touch-listeners so that we can separate the queueing of input |
198 | 0 | // events from them being processed. |
199 | 0 | childApzc->SetWaitForMainThread(); |
200 | 0 |
|
201 | 0 | // Queue input events for a pan. |
202 | 0 | uint64_t blockId = 0; |
203 | 0 | Pan(childApzc, 90, 30, PanOptions::NoFling, nullptr, nullptr, &blockId); |
204 | 0 |
|
205 | 0 | // Modify the APZC tree to insert a new APZC 'middle' into the handoff chain |
206 | 0 | // between the child and the root. |
207 | 0 | CreateScrollHandoffLayerTree2(); |
208 | 0 | RefPtr<Layer> middle = layers[1]; |
209 | 0 | childApzc->SetWaitForMainThread(); |
210 | 0 | TestAsyncPanZoomController* middleApzc = ApzcOf(middle); |
211 | 0 |
|
212 | 0 | // Queue input events for another pan. |
213 | 0 | uint64_t secondBlockId = 0; |
214 | 0 | Pan(childApzc, 30, 90, PanOptions::NoFling, nullptr, nullptr, &secondBlockId); |
215 | 0 |
|
216 | 0 | // Allow the first pan to be processed. |
217 | 0 | childApzc->ContentReceivedInputBlock(blockId, false); |
218 | 0 | childApzc->ConfirmTarget(blockId); |
219 | 0 |
|
220 | 0 | // Make sure things have scrolled according to the handoff chain in |
221 | 0 | // place at the time the touch-start of the first pan was queued. |
222 | 0 | EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y); |
223 | 0 | EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y); |
224 | 0 | EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y); |
225 | 0 |
|
226 | 0 | // Allow the second pan to be processed. |
227 | 0 | childApzc->ContentReceivedInputBlock(secondBlockId, false); |
228 | 0 | childApzc->ConfirmTarget(secondBlockId); |
229 | 0 |
|
230 | 0 | // Make sure things have scrolled according to the handoff chain in |
231 | 0 | // place at the time the touch-start of the second pan was queued. |
232 | 0 | EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y); |
233 | 0 | EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y); |
234 | 0 | EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y); |
235 | 0 | } |
236 | | |
237 | | // Test that putting a second finger down on an APZC while a down-chain APZC |
238 | | // is overscrolled doesn't result in being stuck in overscroll. |
239 | 0 | TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1073250) { |
240 | 0 | // Enable overscrolling. |
241 | 0 | SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); |
242 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
243 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
244 | 0 |
|
245 | 0 | CreateScrollHandoffLayerTree1(); |
246 | 0 |
|
247 | 0 | TestAsyncPanZoomController* child = ApzcOf(layers[1]); |
248 | 0 |
|
249 | 0 | // Pan, causing the parent APZC to overscroll. |
250 | 0 | Pan(manager, 10, 40, PanOptions::KeepFingerDown); |
251 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
252 | 0 | EXPECT_TRUE(rootApzc->IsOverscrolled()); |
253 | 0 |
|
254 | 0 | // Put a second finger down. |
255 | 0 | MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0); |
256 | 0 | // Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.) |
257 | 0 | secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0)); |
258 | 0 | secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0)); |
259 | 0 | manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr); |
260 | 0 |
|
261 | 0 | // Release the fingers. |
262 | 0 | MultiTouchInput fingersUp = secondFingerDown; |
263 | 0 | fingersUp.mType = MultiTouchInput::MULTITOUCH_END; |
264 | 0 | manager->ReceiveInputEvent(fingersUp, nullptr, nullptr); |
265 | 0 |
|
266 | 0 | // Allow any animations to run their course. |
267 | 0 | child->AdvanceAnimationsUntilEnd(); |
268 | 0 | rootApzc->AdvanceAnimationsUntilEnd(); |
269 | 0 |
|
270 | 0 | // Make sure nothing is overscrolled. |
271 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
272 | 0 | EXPECT_FALSE(rootApzc->IsOverscrolled()); |
273 | 0 | } |
274 | | |
275 | | // This is almost exactly like StuckInOverscroll_Bug1073250, except the |
276 | | // APZC receiving the input events for the first touch block is the child |
277 | | // (and thus not the same APZC that overscrolls, which is the parent). |
278 | 0 | TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1231228) { |
279 | 0 | // Enable overscrolling. |
280 | 0 | SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); |
281 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
282 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
283 | 0 |
|
284 | 0 | CreateScrollHandoffLayerTree1(); |
285 | 0 |
|
286 | 0 | TestAsyncPanZoomController* child = ApzcOf(layers[1]); |
287 | 0 |
|
288 | 0 | // Pan, causing the parent APZC to overscroll. |
289 | 0 | Pan(manager, 60, 90, PanOptions::KeepFingerDown); |
290 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
291 | 0 | EXPECT_TRUE(rootApzc->IsOverscrolled()); |
292 | 0 |
|
293 | 0 | // Put a second finger down. |
294 | 0 | MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0); |
295 | 0 | // Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.) |
296 | 0 | secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0)); |
297 | 0 | secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0)); |
298 | 0 | manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr); |
299 | 0 |
|
300 | 0 | // Release the fingers. |
301 | 0 | MultiTouchInput fingersUp = secondFingerDown; |
302 | 0 | fingersUp.mType = MultiTouchInput::MULTITOUCH_END; |
303 | 0 | manager->ReceiveInputEvent(fingersUp, nullptr, nullptr); |
304 | 0 |
|
305 | 0 | // Allow any animations to run their course. |
306 | 0 | child->AdvanceAnimationsUntilEnd(); |
307 | 0 | rootApzc->AdvanceAnimationsUntilEnd(); |
308 | 0 |
|
309 | 0 | // Make sure nothing is overscrolled. |
310 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
311 | 0 | EXPECT_FALSE(rootApzc->IsOverscrolled()); |
312 | 0 | } |
313 | | |
314 | 0 | TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202a) { |
315 | 0 | // Enable overscrolling. |
316 | 0 | SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); |
317 | 0 |
|
318 | 0 | CreateScrollHandoffLayerTree1(); |
319 | 0 |
|
320 | 0 | TestAsyncPanZoomController* child = ApzcOf(layers[1]); |
321 | 0 |
|
322 | 0 | // Pan, causing the parent APZC to overscroll. |
323 | 0 | Pan(manager, 60, 90, PanOptions::KeepFingerDown); |
324 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
325 | 0 | EXPECT_TRUE(rootApzc->IsOverscrolled()); |
326 | 0 |
|
327 | 0 | // Lift the finger, triggering an overscroll animation |
328 | 0 | // (but don't allow it to run). |
329 | 0 | TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time()); |
330 | 0 |
|
331 | 0 | // Put the finger down again, interrupting the animation |
332 | 0 | // and entering the TOUCHING state. |
333 | 0 | TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time()); |
334 | 0 |
|
335 | 0 | // Lift the finger once again. |
336 | 0 | TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time()); |
337 | 0 |
|
338 | 0 | // Allow any animations to run their course. |
339 | 0 | child->AdvanceAnimationsUntilEnd(); |
340 | 0 | rootApzc->AdvanceAnimationsUntilEnd(); |
341 | 0 |
|
342 | 0 | // Make sure nothing is overscrolled. |
343 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
344 | 0 | EXPECT_FALSE(rootApzc->IsOverscrolled()); |
345 | 0 | } |
346 | | |
347 | 0 | TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202b) { |
348 | 0 | // Enable overscrolling. |
349 | 0 | SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); |
350 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
351 | 0 |
|
352 | 0 | CreateScrollHandoffLayerTree1(); |
353 | 0 |
|
354 | 0 | TestAsyncPanZoomController* child = ApzcOf(layers[1]); |
355 | 0 |
|
356 | 0 | // Pan, causing the parent APZC to overscroll. |
357 | 0 | Pan(manager, 60, 90, PanOptions::KeepFingerDown); |
358 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
359 | 0 | EXPECT_TRUE(rootApzc->IsOverscrolled()); |
360 | 0 |
|
361 | 0 | // Lift the finger, triggering an overscroll animation |
362 | 0 | // (but don't allow it to run). |
363 | 0 | TouchUp(manager, ScreenIntPoint(10, 90), mcc->Time()); |
364 | 0 |
|
365 | 0 | // Put the finger down again, interrupting the animation |
366 | 0 | // and entering the TOUCHING state. |
367 | 0 | TouchDown(manager, ScreenIntPoint(10, 90), mcc->Time()); |
368 | 0 |
|
369 | 0 | // Put a second finger down. Since we're in the TOUCHING state, |
370 | 0 | // the "are we panned into overscroll" check will fail and we |
371 | 0 | // will not ignore the second finger, instead entering the |
372 | 0 | // PINCHING state. |
373 | 0 | MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0); |
374 | 0 | // Use the same touch identifier for the first touch (0) as TouchDown(). (A bit hacky.) |
375 | 0 | secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 90), ScreenSize(0, 0), 0, 0)); |
376 | 0 | secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(10, 80), ScreenSize(0, 0), 0, 0)); |
377 | 0 | manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr); |
378 | 0 |
|
379 | 0 | // Release the fingers. |
380 | 0 | MultiTouchInput fingersUp = secondFingerDown; |
381 | 0 | fingersUp.mType = MultiTouchInput::MULTITOUCH_END; |
382 | 0 | manager->ReceiveInputEvent(fingersUp, nullptr, nullptr); |
383 | 0 |
|
384 | 0 | // Allow any animations to run their course. |
385 | 0 | child->AdvanceAnimationsUntilEnd(); |
386 | 0 | rootApzc->AdvanceAnimationsUntilEnd(); |
387 | 0 |
|
388 | 0 | // Make sure nothing is overscrolled. |
389 | 0 | EXPECT_FALSE(child->IsOverscrolled()); |
390 | 0 | EXPECT_FALSE(rootApzc->IsOverscrolled()); |
391 | 0 | } |
392 | | |
393 | 0 | TEST_F(APZScrollHandoffTester, OpposingConstrainedAxes_Bug1201098) { |
394 | 0 | // Enable overscrolling. |
395 | 0 | SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); |
396 | 0 |
|
397 | 0 | CreateScrollHandoffLayerTree4(); |
398 | 0 |
|
399 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
400 | 0 |
|
401 | 0 | // Pan, causing the child APZC to overscroll. |
402 | 0 | Pan(childApzc, 50, 60); |
403 | 0 |
|
404 | 0 | //Make sure only the child is overscrolled. |
405 | 0 | EXPECT_TRUE(childApzc->IsOverscrolled()); |
406 | 0 | EXPECT_FALSE(rootApzc->IsOverscrolled()); |
407 | 0 | } |
408 | | |
409 | | // Test that flinging in a direction where one component of the fling goes into |
410 | | // overscroll but the other doesn't, results in just the one component being |
411 | | // handed off to the parent, while the original APZC continues flinging in the |
412 | | // other direction. |
413 | 0 | TEST_F(APZScrollHandoffTester, PartialFlingHandoff) { |
414 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
415 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
416 | 0 |
|
417 | 0 | CreateScrollHandoffLayerTree1(); |
418 | 0 |
|
419 | 0 | // Fling up and to the left. The child APZC has room to scroll up, but not |
420 | 0 | // to the left, so the horizontal component of the fling should be handed |
421 | 0 | // off to the parent APZC. |
422 | 0 | Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55)); |
423 | 0 |
|
424 | 0 | RefPtr<TestAsyncPanZoomController> parent = ApzcOf(root); |
425 | 0 | RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]); |
426 | 0 |
|
427 | 0 | // Advance the child's fling animation once to give the partial handoff |
428 | 0 | // a chance to occur. |
429 | 0 | mcc->AdvanceByMillis(10); |
430 | 0 | child->AdvanceAnimations(mcc->Time()); |
431 | 0 |
|
432 | 0 | // Assert that partial handoff has occurred. |
433 | 0 | child->AssertStateIsFling(); |
434 | 0 | parent->AssertStateIsFling(); |
435 | 0 | } |
436 | | |
437 | | // Here we test that if two flings are happening simultaneously, overscroll |
438 | | // is handed off correctly for each. |
439 | 0 | TEST_F(APZScrollHandoffTester, SimultaneousFlings) { |
440 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
441 | 0 |
|
442 | 0 | // Set up an initial APZC tree. |
443 | 0 | CreateScrollHandoffLayerTree3(); |
444 | 0 |
|
445 | 0 | RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]); |
446 | 0 | RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]); |
447 | 0 | RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]); |
448 | 0 | RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]); |
449 | 0 |
|
450 | 0 | // Pan on the lower child. |
451 | 0 | Pan(child2, 45, 5); |
452 | 0 |
|
453 | 0 | // Pan on the upper child. |
454 | 0 | Pan(child1, 95, 55); |
455 | 0 |
|
456 | 0 | // Check that child1 and child2 are in a FLING state. |
457 | 0 | child1->AssertStateIsFling(); |
458 | 0 | child2->AssertStateIsFling(); |
459 | 0 |
|
460 | 0 | // Advance the animations on child1 and child2 until their end. |
461 | 0 | child1->AdvanceAnimationsUntilEnd(); |
462 | 0 | child2->AdvanceAnimationsUntilEnd(); |
463 | 0 |
|
464 | 0 | // Check that the flings have been handed off to the parents. |
465 | 0 | child1->AssertStateIsReset(); |
466 | 0 | parent1->AssertStateIsFling(); |
467 | 0 | child2->AssertStateIsReset(); |
468 | 0 | parent2->AssertStateIsFling(); |
469 | 0 | } |
470 | | |
471 | 0 | TEST_F(APZScrollHandoffTester, Scrollgrab) { |
472 | 0 | // Set up the layer tree |
473 | 0 | CreateScrollgrabLayerTree(); |
474 | 0 |
|
475 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
476 | 0 |
|
477 | 0 | // Pan on the child, enough to fully scroll the scrollgrab parent (20 px) |
478 | 0 | // and leave some more (another 15 px) for the child. |
479 | 0 | Pan(childApzc, 80, 45); |
480 | 0 |
|
481 | 0 | // Check that the parent and child have scrolled as much as we expect. |
482 | 0 | EXPECT_EQ(20, rootApzc->GetFrameMetrics().GetScrollOffset().y); |
483 | 0 | EXPECT_EQ(15, childApzc->GetFrameMetrics().GetScrollOffset().y); |
484 | 0 | } |
485 | | |
486 | 0 | TEST_F(APZScrollHandoffTester, ScrollgrabFling) { |
487 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
488 | 0 | // Set up the layer tree |
489 | 0 | CreateScrollgrabLayerTree(); |
490 | 0 |
|
491 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
492 | 0 |
|
493 | 0 | // Pan on the child, not enough to fully scroll the scrollgrab parent. |
494 | 0 | Pan(childApzc, 80, 70); |
495 | 0 |
|
496 | 0 | // Check that it is the scrollgrab parent that's in a fling, not the child. |
497 | 0 | rootApzc->AssertStateIsFling(); |
498 | 0 | childApzc->AssertStateIsReset(); |
499 | 0 | } |
500 | | |
501 | 0 | TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration1) { |
502 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
503 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
504 | 0 | CreateScrollgrabLayerTree(true /* make parent scrollable */); |
505 | 0 | TestFlingAcceleration(); |
506 | 0 | } |
507 | | |
508 | 0 | TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration2) { |
509 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
510 | 0 | SCOPED_GFX_VAR(UseWebRender, bool, false); |
511 | 0 | CreateScrollgrabLayerTree(false /* do not make parent scrollable */); |
512 | 0 | TestFlingAcceleration(); |
513 | 0 | } |
514 | | |
515 | 0 | TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) { |
516 | 0 | SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false); |
517 | 0 |
|
518 | 0 | CreateScrollHandoffLayerTree1(); |
519 | 0 |
|
520 | 0 | RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root); |
521 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
522 | 0 |
|
523 | 0 | // Pan on the child, enough to scroll it to its end and have scroll |
524 | 0 | // left to hand off. Since immediate handoff is disallowed, we expect |
525 | 0 | // the leftover scroll not to be handed off. |
526 | 0 | Pan(childApzc, 60, 5); |
527 | 0 |
|
528 | 0 | // Verify that the parent has not scrolled. |
529 | 0 | EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y); |
530 | 0 | EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y); |
531 | 0 |
|
532 | 0 | // Pan again on the child. This time, since the child was scrolled to |
533 | 0 | // its end when the gesture began, we expect the scroll to be handed off. |
534 | 0 | Pan(childApzc, 60, 50); |
535 | 0 |
|
536 | 0 | // Verify that the parent scrolled. |
537 | 0 | EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y); |
538 | 0 | } |
539 | | |
540 | 0 | TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Fling) { |
541 | 0 | SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false); |
542 | 0 | SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); |
543 | 0 |
|
544 | 0 | CreateScrollHandoffLayerTree1(); |
545 | 0 |
|
546 | 0 | RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root); |
547 | 0 | RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]); |
548 | 0 |
|
549 | 0 | // Pan on the child, enough to get very close to the end, so that the |
550 | 0 | // subsequent fling reaches the end and has leftover velocity to hand off. |
551 | 0 | Pan(childApzc, 60, 12); |
552 | 0 |
|
553 | 0 | // Allow the fling to run its course. |
554 | 0 | childApzc->AdvanceAnimationsUntilEnd(); |
555 | 0 | parentApzc->AdvanceAnimationsUntilEnd(); |
556 | 0 |
|
557 | 0 | // Verify that the parent has not scrolled. |
558 | 0 | // The first comparison needs to be an ASSERT_NEAR because the fling |
559 | 0 | // computations are such that the final scroll position can be within |
560 | 0 | // COORDINATE_EPSILON of the end rather than right at the end. |
561 | 0 | ASSERT_NEAR(50, childApzc->GetFrameMetrics().GetScrollOffset().y, COORDINATE_EPSILON); |
562 | 0 | EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y); |
563 | 0 |
|
564 | 0 | // Pan again on the child. This time, since the child was scrolled to |
565 | 0 | // its end when the gesture began, we expect the scroll to be handed off. |
566 | 0 | Pan(childApzc, 60, 50); |
567 | 0 |
|
568 | 0 | // Allow the fling to run its course. The fling should also be handed off. |
569 | 0 | childApzc->AdvanceAnimationsUntilEnd(); |
570 | 0 | parentApzc->AdvanceAnimationsUntilEnd(); |
571 | 0 |
|
572 | 0 | // Verify that the parent scrolled from the fling. |
573 | 0 | EXPECT_GT(parentApzc->GetFrameMetrics().GetScrollOffset().y, 10); |
574 | 0 | } |
575 | | |
576 | 0 | TEST_F(APZScrollHandoffTester, CrossApzcAxisLock_NoTouchAction) { |
577 | 0 | SCOPED_GFX_PREF(TouchActionEnabled, bool, false); |
578 | 0 | TestCrossApzcAxisLock(); |
579 | 0 | } |
580 | | |
581 | 0 | TEST_F(APZScrollHandoffTester, CrossApzcAxisLock_TouchAction) { |
582 | 0 | SCOPED_GFX_PREF(TouchActionEnabled, bool, true); |
583 | 0 | TestCrossApzcAxisLock(); |
584 | 0 | } |