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