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