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 <initializer_list>
8 #include "APZCTreeManagerTester.h"
9 #include "APZTestCommon.h"
10 #include "InputUtils.h"
11 
12 class APZCFlingAccelerationTester : public APZCTreeManagerTester {
13  protected:
SetUp()14   void SetUp() {
15     APZCTreeManagerTester::SetUp();
16     const char* layerTreeSyntax = "c";
17     nsIntRegion layerVisibleRegion[] = {
18         nsIntRegion(IntRect(0, 0, 800, 1000)),
19     };
20     root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm,
21                            layers);
22     SetScrollableFrameMetrics(root, ScrollableLayerGuid::START_SCROLL_ID,
23                               CSSRect(0, 0, 800, 50000));
24     // Scroll somewhere into the middle of the scroll range, so that we have
25     // lots of space to scroll in both directions.
26     ModifyFrameMetrics(root, [](ScrollMetadata& aSm, FrameMetrics& aMetrics) {
27       aMetrics.SetVisualScrollUpdateType(
28           FrameMetrics::ScrollOffsetUpdateType::eMainThread);
29       aMetrics.SetVisualDestination(CSSPoint(0, 25000));
30     });
31 
32     registration =
33         MakeUnique<ScopedLayerTreeRegistration>(LayersId{0}, root, mcc);
34     UpdateHitTestingTree();
35 
36     apzc = ApzcOf(root);
37   }
38 
ExecutePanGesture100Hz(const ScreenIntPoint & aStartPoint,std::initializer_list<int32_t> aYDeltas)39   void ExecutePanGesture100Hz(const ScreenIntPoint& aStartPoint,
40                               std::initializer_list<int32_t> aYDeltas) {
41     APZEventResult result = TouchDown(apzc, aStartPoint, mcc->Time());
42 
43     // Allowed touch behaviours must be set after sending touch-start.
44     if (result.GetStatus() != nsEventStatus_eConsumeNoDefault &&
45         StaticPrefs::layout_css_touch_action_enabled()) {
46       SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
47     }
48 
49     const TimeDuration kTouchTimeDelta100Hz =
50         TimeDuration::FromMilliseconds(10);
51 
52     ScreenIntPoint currentLocation = aStartPoint;
53     for (int32_t delta : aYDeltas) {
54       mcc->AdvanceBy(kTouchTimeDelta100Hz);
55       if (delta != 0) {
56         currentLocation.y += delta;
57         Unused << TouchMove(apzc, currentLocation, mcc->Time());
58       }
59     }
60 
61     Unused << TouchUp(apzc, currentLocation, mcc->Time());
62   }
63 
ExecuteWait(const TimeDuration & aDuration)64   void ExecuteWait(const TimeDuration& aDuration) {
65     TimeDuration remaining = aDuration;
66     const TimeDuration TIME_BETWEEN_FRAMES =
67         TimeDuration::FromSeconds(1) / int64_t(60);
68     while (remaining.ToMilliseconds() > 0) {
69       mcc->AdvanceBy(TIME_BETWEEN_FRAMES);
70       apzc->AdvanceAnimations(mcc->GetSampleTime());
71       remaining -= TIME_BETWEEN_FRAMES;
72     }
73   }
74 
75   RefPtr<TestAsyncPanZoomController> apzc;
76   UniquePtr<ScopedLayerTreeRegistration> registration;
77 };
78 
79 enum class UpOrDown : uint8_t { Up, Down };
80 
81 // This is a macro so that the assertions print useful line numbers.
82 #define CHECK_VELOCITY(aUpOrDown, aLowerBound, aUpperBound) \
83   do {                                                      \
84     auto vel = apzc->GetVelocityVector();                   \
85     if (UpOrDown::aUpOrDown == UpOrDown::Up) {              \
86       EXPECT_LT(vel.y, 0.0);                                \
87     } else {                                                \
88       EXPECT_GT(vel.y, 0.0);                                \
89     }                                                       \
90     EXPECT_GE(vel.Length(), aLowerBound);                   \
91     EXPECT_LE(vel.Length(), aUpperBound);                   \
92   } while (0)
93 
94 // These tests have the following pattern: Two flings are executed, with a bit
95 // of wait time in between. The deltas in each pan gesture have been captured
96 // from a real phone, from touch events triggered by real fingers.
97 // We check the velocity at the end to detect whether the fling was accelerated
98 // or not. As an additional safety precaution, we also check the velocities for
99 // the first fling, so that changes in behavior are easier to analyze.
100 // One added challenge of this test is the fact that it has to work with on
101 // multiple platforms, and we use different velocity estimation strategies and
102 // different fling physics depending on the platform.
103 // The upper and lower bounds for the velocities were chosen in such a way that
104 // the test passes on all platforms. At the time of writing, we usually end up
105 // with higher velocities on Android than on Desktop, so the observed velocities
106 // on Android became the upper bounds and the observed velocities on Desktop
107 // becaume the lower bounds, each rounded out to a multiple of 0.1.
108 
TEST_F(APZCFlingAccelerationTester,TwoNormalFlingsShouldAccelerate)109 TEST_F(APZCFlingAccelerationTester, TwoNormalFlingsShouldAccelerate) {
110   ExecutePanGesture100Hz(ScreenIntPoint{665, 1244},
111                          {0, 0, -21, -44, -52, -55, -53, -49, -46, -47});
112   CHECK_VELOCITY(Down, 4.5, 6.8);
113 
114   ExecuteWait(TimeDuration::FromMilliseconds(375));
115   CHECK_VELOCITY(Down, 2.2, 5.1);
116 
117   ExecutePanGesture100Hz(ScreenIntPoint{623, 1211},
118                          {-6, -51, -55, 0, -53, -57, -60, -60, -56});
119   CHECK_VELOCITY(Down, 9.0, 14.0);
120 }
121 
TEST_F(APZCFlingAccelerationTester,TwoFastFlingsShouldAccelerate)122 TEST_F(APZCFlingAccelerationTester, TwoFastFlingsShouldAccelerate) {
123   ExecutePanGesture100Hz(ScreenIntPoint{764, 714},
124                          {9, 30, 49, 60, 64, 64, 62, 59, 51});
125   CHECK_VELOCITY(Up, 5.0, 7.5);
126 
127   ExecuteWait(TimeDuration::FromMilliseconds(447));
128   CHECK_VELOCITY(Up, 2.3, 5.2);
129 
130   ExecutePanGesture100Hz(ScreenIntPoint{743, 739},
131                          {7, 0, 38, 66, 75, 146, 0, 119});
132   CHECK_VELOCITY(Up, 13.0, 20.0);
133 }
134 
TEST_F(APZCFlingAccelerationTester,FlingsInOppositeDirectionShouldNotAccelerate)135 TEST_F(APZCFlingAccelerationTester,
136        FlingsInOppositeDirectionShouldNotAccelerate) {
137   ExecutePanGesture100Hz(ScreenIntPoint{728, 1381},
138                          {0, 0, 0, -12, -24, -32, -43, -46, 0});
139   CHECK_VELOCITY(Down, 2.9, 5.3);
140 
141   ExecuteWait(TimeDuration::FromMilliseconds(153));
142   CHECK_VELOCITY(Down, 2.1, 4.8);
143 
144   ExecutePanGesture100Hz(ScreenIntPoint{698, 1059},
145                          {0, 0, 14, 61, 41, 0, 45, 35});
146   CHECK_VELOCITY(Up, 3.2, 4.2);
147 }
148 
TEST_F(APZCFlingAccelerationTester,ShouldNotAccelerateWhenPreviousFlingHasSlowedDown)149 TEST_F(APZCFlingAccelerationTester,
150        ShouldNotAccelerateWhenPreviousFlingHasSlowedDown) {
151   ExecutePanGesture100Hz(ScreenIntPoint{748, 1046},
152                          {0, 9, 15, 23, 31, 30, 0, 34, 31, 29, 28, 24, 24, 11});
153   CHECK_VELOCITY(Up, 2.2, 3.0);
154   ExecuteWait(TimeDuration::FromMilliseconds(498));
155   CHECK_VELOCITY(Up, 0.5, 1.0);
156   ExecutePanGesture100Hz(ScreenIntPoint{745, 1056},
157                          {0, 10, 17, 29, 29, 33, 33, 0, 31, 27, 13});
158   CHECK_VELOCITY(Up, 1.8, 2.7);
159 }
160 
TEST_F(APZCFlingAccelerationTester,ShouldNotAccelerateWhenPausedAtStartOfPan)161 TEST_F(APZCFlingAccelerationTester, ShouldNotAccelerateWhenPausedAtStartOfPan) {
162   ExecutePanGesture100Hz(
163       ScreenIntPoint{711, 1468},
164       {0, 0, 0, 0, -8, 0, -18, -32, -50, -57, -66, -68, -63, -60});
165   CHECK_VELOCITY(Down, 6.2, 8.5);
166 
167   ExecuteWait(TimeDuration::FromMilliseconds(285));
168   CHECK_VELOCITY(Down, 3.4, 7.3);
169 
170   ExecutePanGesture100Hz(
171       ScreenIntPoint{658, 1352},
172       {0, 0, 0, 0, 0, 0,  0,   0,   0,   0,   0,   0,   0,
173        0, 0, 0, 0, 0, -8, -18, -34, -53, -70, -75, -75, -64});
174   CHECK_VELOCITY(Down, 6.7, 9.1);
175 }
176 
TEST_F(APZCFlingAccelerationTester,ShouldNotAccelerateWhenPausedDuringPan)177 TEST_F(APZCFlingAccelerationTester, ShouldNotAccelerateWhenPausedDuringPan) {
178   ExecutePanGesture100Hz(
179       ScreenIntPoint{732, 1423},
180       {0, 0, 0, -5, 0, -15, -41, -71, -90, -93, -85, -64, -44});
181   CHECK_VELOCITY(Down, 7.5, 10.0);
182 
183   ExecuteWait(TimeDuration::FromMilliseconds(204));
184   CHECK_VELOCITY(Down, 4.8, 9.3);
185 
186   ExecutePanGesture100Hz(
187       ScreenIntPoint{651, 1372},
188       {0,   0,   0,  -6, 0,  -16, -26, -41, -49, -65, -66, -61, -50, -35, -24,
189        -17, -11, -8, -6, -5, -4,  -3,  -2,  -2,  -2,  -2,  -2,  -2,  -2,  -2,
190        -3,  -4,  -5, -7, -9, -10, -10, -12, -18, -25, -23, -28, -30, -24});
191   CHECK_VELOCITY(Down, 2.5, 3.4);
192 }
193 
TEST_F(APZCFlingAccelerationTester,ShouldNotAccelerateWhenOppositeDirectionDuringPan)194 TEST_F(APZCFlingAccelerationTester,
195        ShouldNotAccelerateWhenOppositeDirectionDuringPan) {
196   ExecutePanGesture100Hz(ScreenIntPoint{663, 1371},
197                          {0, 0, 0, -5, -18, -31, -49, -56, -61, -54, -55});
198   CHECK_VELOCITY(Down, 5.4, 7.0);
199 
200   ExecuteWait(TimeDuration::FromMilliseconds(255));
201   CHECK_VELOCITY(Down, 3.1, 6.0);
202 
203   ExecutePanGesture100Hz(
204       ScreenIntPoint{726, 930},
205       {0,  0,   0,   0,   30,  0,   19,  24,  32,  30, 37, 33,
206        33, 32,  25,  23,  23,  18,  13,  9,   5,   3,  1,  0,
207        -7, -19, -38, -53, -68, -79, -85, -73, -64, -54});
208   CHECK_VELOCITY(Down, 7.0, 10.0);
209 }
210 
TEST_F(APZCFlingAccelerationTester,ShouldAccelerateAfterLongWaitIfVelocityStillHigh)211 TEST_F(APZCFlingAccelerationTester,
212        ShouldAccelerateAfterLongWaitIfVelocityStillHigh) {
213   // Reduce friction with the "Desktop" fling physics a little, so that it
214   // behaves more similarly to the Android fling physics, and has enough
215   // velocity after the wait time to allow for acceleration.
216   SCOPED_GFX_PREF_FLOAT("apz.fling_friction", 0.0012);
217 
218   ExecutePanGesture100Hz(ScreenIntPoint{739, 1424},
219                          {0, 0, -5, -10, -20, 0, -110, -86, 0, -102, -105});
220   CHECK_VELOCITY(Down, 6.3, 9.4);
221 
222   ExecuteWait(TimeDuration::FromMilliseconds(1117));
223   CHECK_VELOCITY(Down, 1.6, 3.3);
224 
225   ExecutePanGesture100Hz(ScreenIntPoint{726, 1380},
226                          {0, -8, 0, -30, -60, -87, -104, -111});
227   CHECK_VELOCITY(Down, 13.0, 23.0);
228 }
229 
TEST_F(APZCFlingAccelerationTester,ShouldNotAccelerateAfterCanceledWithTap)230 TEST_F(APZCFlingAccelerationTester, ShouldNotAccelerateAfterCanceledWithTap) {
231   // First, build up a lot of speed.
232   ExecutePanGesture100Hz(ScreenIntPoint{569, 710},
233                          {11, 2, 107, 18, 148, 57, 133, 159, 21});
234   ExecuteWait(TimeDuration::FromMilliseconds(154));
235   ExecutePanGesture100Hz(ScreenIntPoint{581, 650},
236                          {12, 68, 0, 162, 78, 140, 167});
237   ExecuteWait(TimeDuration::FromMilliseconds(123));
238   ExecutePanGesture100Hz(ScreenIntPoint{568, 723}, {11, 0, 79, 91, 131, 171});
239   ExecuteWait(TimeDuration::FromMilliseconds(123));
240   ExecutePanGesture100Hz(ScreenIntPoint{598, 678},
241                          {8, 55, 22, 87, 117, 220, 54});
242   ExecuteWait(TimeDuration::FromMilliseconds(134));
243   ExecutePanGesture100Hz(ScreenIntPoint{585, 854}, {45, 137, 107, 102, 79});
244   ExecuteWait(TimeDuration::FromMilliseconds(246));
245 
246   // Then, interrupt with a tap.
247   ExecutePanGesture100Hz(ScreenIntPoint{566, 812}, {0, 0, 0, 0});
248   ExecuteWait(TimeDuration::FromMilliseconds(869));
249 
250   // Then do a regular fling.
251   ExecutePanGesture100Hz(ScreenIntPoint{599, 819},
252                          {0, 0, 8, 35, 8, 38, 29, 37});
253 
254   CHECK_VELOCITY(Up, 2.8, 4.2);
255 }
256