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 "APZCBasicTester.h"
8 #include "APZTestCommon.h"
9 #include "InputUtils.h"
10 #include "gtest/gtest.h"
11
12 class APZCPanningTester : public APZCBasicTester {
13 protected:
DoPanTest(bool aShouldTriggerScroll,bool aShouldBeConsumed,uint32_t aBehavior)14 void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed,
15 uint32_t aBehavior) {
16 if (aShouldTriggerScroll) {
17 // Three repaint request for each pan.
18 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(6);
19 } else {
20 EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
21 }
22
23 int touchStart = 50;
24 int touchEnd = 10;
25 ParentLayerPoint pointOut;
26 AsyncTransform viewTransformOut;
27
28 nsTArray<uint32_t> allowedTouchBehaviors;
29 allowedTouchBehaviors.AppendElement(aBehavior);
30
31 // Pan down
32 PanAndCheckStatus(apzc, touchStart, touchEnd, aShouldBeConsumed,
33 &allowedTouchBehaviors);
34 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
35
36 if (aShouldTriggerScroll) {
37 EXPECT_EQ(ParentLayerPoint(0, -(touchEnd - touchStart)), pointOut);
38 EXPECT_NE(AsyncTransform(), viewTransformOut);
39 } else {
40 EXPECT_EQ(ParentLayerPoint(), pointOut);
41 EXPECT_EQ(AsyncTransform(), viewTransformOut);
42 }
43
44 // Clear the fling from the previous pan, or stopping it will
45 // consume the next touchstart
46 apzc->CancelAnimation();
47
48 // Pan back
49 PanAndCheckStatus(apzc, touchEnd, touchStart, aShouldBeConsumed,
50 &allowedTouchBehaviors);
51 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
52
53 EXPECT_EQ(ParentLayerPoint(), pointOut);
54 EXPECT_EQ(AsyncTransform(), viewTransformOut);
55 }
56
DoPanWithPreventDefaultTest()57 void DoPanWithPreventDefaultTest() {
58 MakeApzcWaitForMainThread();
59
60 int touchStart = 50;
61 int touchEnd = 10;
62 ParentLayerPoint pointOut;
63 AsyncTransform viewTransformOut;
64 uint64_t blockId = 0;
65
66 // Pan down
67 nsTArray<uint32_t> allowedTouchBehaviors;
68 allowedTouchBehaviors.AppendElement(
69 mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
70 PanAndCheckStatus(apzc, touchStart, touchEnd, true, &allowedTouchBehaviors,
71 &blockId);
72
73 // Send the signal that content has handled and preventDefaulted the touch
74 // events. This flushes the event queue.
75 apzc->ContentReceivedInputBlock(blockId, true);
76
77 apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
78 EXPECT_EQ(ParentLayerPoint(), pointOut);
79 EXPECT_EQ(AsyncTransform(), viewTransformOut);
80
81 apzc->AssertStateIsReset();
82 }
83 };
84
TEST_F(APZCPanningTester,Pan)85 TEST_F(APZCPanningTester, Pan) {
86 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", false);
87 // Velocity bias can cause extra repaint requests.
88 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0);
89 DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
90 }
91
92 // In the each of the following 4 pan tests we are performing two pan gestures:
93 // vertical pan from top to bottom and back - from bottom to top. According to
94 // the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow
95 // vertical scrolling while NONE and PAN_X forbid it. The first parameter of
96 // DoPanTest method specifies this behavior. However, the events will be marked
97 // as consumed even if the behavior in PAN_X, because the user could move their
98 // finger horizontally too - APZ has no way of knowing beforehand and so must
99 // consume the events.
TEST_F(APZCPanningTester,PanWithTouchActionAuto)100 TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
101 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", true);
102 // Velocity bias can cause extra repaint requests.
103 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0);
104 DoPanTest(true, true,
105 mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN |
106 mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
107 }
108
TEST_F(APZCPanningTester,PanWithTouchActionNone)109 TEST_F(APZCPanningTester, PanWithTouchActionNone) {
110 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", true);
111 // Velocity bias can cause extra repaint requests.
112 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0);
113 DoPanTest(false, false, 0);
114 }
115
TEST_F(APZCPanningTester,PanWithTouchActionPanX)116 TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
117 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", true);
118 // Velocity bias can cause extra repaint requests.
119 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0);
120 DoPanTest(false, false,
121 mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
122 }
123
TEST_F(APZCPanningTester,PanWithTouchActionPanY)124 TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
125 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", true);
126 // Velocity bias can cause extra repaint requests.
127 SCOPED_GFX_PREF_FLOAT("apz.velocity_bias", 0.0);
128 DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
129 }
130
TEST_F(APZCPanningTester,PanWithPreventDefaultAndTouchAction)131 TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
132 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", true);
133 DoPanWithPreventDefaultTest();
134 }
135
TEST_F(APZCPanningTester,PanWithPreventDefault)136 TEST_F(APZCPanningTester, PanWithPreventDefault) {
137 SCOPED_GFX_PREF_BOOL("layout.css.touch_action.enabled", false);
138 DoPanWithPreventDefaultTest();
139 }
140
TEST_F(APZCPanningTester,PanWithHistoricalTouchData)141 TEST_F(APZCPanningTester, PanWithHistoricalTouchData) {
142 SCOPED_GFX_PREF_FLOAT("apz.fling_min_velocity_threshold", 0.0);
143
144 // Simulate the same pan gesture, in three different ways.
145 // We start at y=50, with a 50ms resting period at the start of the pan.
146 // Then we accelerate the finger upwards towards y=10, reaching a 10px/10ms
147 // velocity towards the end of the panning motion.
148 //
149 // The first simulation fires touch move events with 10ms gaps.
150 // The second simulation skips two of the touch move events, simulating
151 // "jank". The third simulation also skips those two events, but reports the
152 // missed positions in the following event's historical coordinates.
153 //
154 // Consequently, the first and third simulation should estimate the same
155 // velocities, whereas the second simulation should estimate a different
156 // velocity because it is missing data.
157
158 // First simulation: full data
159
160 APZEventResult result = TouchDown(apzc, ScreenIntPoint(0, 50), mcc->Time());
161 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault &&
162 StaticPrefs::layout_css_touch_action_enabled()) {
163 SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
164 }
165
166 mcc->AdvanceByMillis(50);
167 result = TouchMove(apzc, ScreenIntPoint(0, 45), mcc->Time());
168 mcc->AdvanceByMillis(10);
169 result = TouchMove(apzc, ScreenIntPoint(0, 40), mcc->Time());
170 mcc->AdvanceByMillis(10);
171 result = TouchMove(apzc, ScreenIntPoint(0, 30), mcc->Time());
172 mcc->AdvanceByMillis(10);
173 result = TouchMove(apzc, ScreenIntPoint(0, 20), mcc->Time());
174 result = TouchUp(apzc, ScreenIntPoint(0, 20), mcc->Time());
175 auto velocityFromFullDataAsSeparateEvents = apzc->GetVelocityVector();
176 apzc->CancelAnimation();
177
178 mcc->AdvanceByMillis(100);
179
180 // Second simulation: partial data
181
182 result = TouchDown(apzc, ScreenIntPoint(0, 50), mcc->Time());
183 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault &&
184 StaticPrefs::layout_css_touch_action_enabled()) {
185 SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
186 }
187
188 mcc->AdvanceByMillis(50);
189 result = TouchMove(apzc, ScreenIntPoint(0, 45), mcc->Time());
190 mcc->AdvanceByMillis(30);
191 result = TouchMove(apzc, ScreenIntPoint(0, 20), mcc->Time());
192 result = TouchUp(apzc, ScreenIntPoint(0, 20), mcc->Time());
193 auto velocityFromPartialData = apzc->GetVelocityVector();
194 apzc->CancelAnimation();
195
196 mcc->AdvanceByMillis(100);
197
198 // Third simulation: full data via historical data
199
200 result = TouchDown(apzc, ScreenIntPoint(0, 50), mcc->Time());
201 if (result.GetStatus() != nsEventStatus_eConsumeNoDefault &&
202 StaticPrefs::layout_css_touch_action_enabled()) {
203 SetDefaultAllowedTouchBehavior(apzc, result.mInputBlockId);
204 }
205
206 mcc->AdvanceByMillis(50);
207 result = TouchMove(apzc, ScreenIntPoint(0, 45), mcc->Time());
208 mcc->AdvanceByMillis(30);
209
210 MultiTouchInput mti =
211 CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
212 auto singleTouchData = CreateSingleTouchData(0, ScreenIntPoint(0, 20));
213 singleTouchData.mHistoricalData.AppendElement(
214 SingleTouchData::HistoricalTouchData{
215 mcc->Time() - TimeDuration::FromMilliseconds(20),
216 ScreenIntPoint(0, 40),
217 {},
218 {},
219 0.0f,
220 0.0f});
221 singleTouchData.mHistoricalData.AppendElement(
222 SingleTouchData::HistoricalTouchData{
223 mcc->Time() - TimeDuration::FromMilliseconds(10),
224 ScreenIntPoint(0, 30),
225 {},
226 {},
227 0.0f,
228 0.0f});
229 mti.mTouches.AppendElement(singleTouchData);
230 result = apzc->ReceiveInputEvent(mti);
231
232 result = TouchUp(apzc, ScreenIntPoint(0, 20), mcc->Time());
233 auto velocityFromFullDataViaHistory = apzc->GetVelocityVector();
234 apzc->CancelAnimation();
235
236 EXPECT_EQ(velocityFromFullDataAsSeparateEvents,
237 velocityFromFullDataViaHistory);
238 EXPECT_NE(velocityFromPartialData, velocityFromFullDataViaHistory);
239 }
240