OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "cc/input/input_handler.h" | |
6 #include "content/renderer/input/input_scroll_elasticity_controller.h" | |
7 #include "testing/gtest/include/gtest/gtest.h" | |
8 #include "third_party/WebKit/public/web/WebInputEvent.h" | |
9 | |
10 namespace content { | |
11 namespace { | |
12 | |
13 enum Phase { | |
14 PhaseNone = blink::WebMouseWheelEvent::PhaseNone, | |
15 PhaseBegan = blink::WebMouseWheelEvent::PhaseBegan, | |
16 PhaseStationary = blink::WebMouseWheelEvent::PhaseStationary, | |
17 PhaseChanged = blink::WebMouseWheelEvent::PhaseChanged, | |
18 PhaseEnded = blink::WebMouseWheelEvent::PhaseEnded, | |
19 PhaseCancelled = blink::WebMouseWheelEvent::PhaseCancelled, | |
20 PhaseMayBegin = blink::WebMouseWheelEvent::PhaseMayBegin, | |
21 }; | |
22 | |
23 class MockScrollElasticityHelper : public cc::ScrollElasticityHelper { | |
24 public: | |
25 MockScrollElasticityHelper() | |
26 : is_user_scrollable_(true), | |
27 set_stretch_amount_count_(0), | |
28 request_animate_count_(0) {} | |
29 ~MockScrollElasticityHelper() override {} | |
30 | |
31 // cc::ScrollElasticityHelper implementation: | |
32 bool IsUserScrollable() const override { return is_user_scrollable_; } | |
33 gfx::Vector2dF StretchAmount() const override { return stretch_amount_; } | |
34 void SetStretchAmount(const gfx::Vector2dF& stretch_amount) override { | |
35 set_stretch_amount_count_ += 1; | |
36 stretch_amount_ = stretch_amount; | |
37 } | |
38 gfx::ScrollOffset ScrollOffset() const override { return scroll_offset_; } | |
39 gfx::ScrollOffset MaxScrollOffset() const override { | |
40 return max_scroll_offset_; | |
41 } | |
42 void ScrollBy(const gfx::Vector2dF& delta) override { | |
43 scroll_offset_ += gfx::ScrollOffset(delta); | |
44 } | |
45 void RequestAnimate() override { request_animate_count_ += 1; } | |
46 | |
47 // Counters for number of times functions were called. | |
48 int request_animate_count() const { return request_animate_count_; } | |
49 int set_stretch_amount_count() const { return set_stretch_amount_count_; } | |
50 | |
51 void SetScrollOffsetAndMaxScrollOffset( | |
52 const gfx::ScrollOffset& scroll_offset, | |
53 const gfx::ScrollOffset& max_scroll_offset) { | |
54 scroll_offset_ = scroll_offset; | |
55 max_scroll_offset_ = max_scroll_offset; | |
56 } | |
57 void SetUserScrollable(bool is_user_scrollable) { | |
58 is_user_scrollable_ = is_user_scrollable; | |
59 } | |
60 | |
61 private: | |
62 bool is_user_scrollable_; | |
63 gfx::Vector2dF stretch_amount_; | |
64 int set_stretch_amount_count_; | |
65 int request_animate_count_; | |
66 | |
67 gfx::ScrollOffset scroll_offset_; | |
68 gfx::ScrollOffset max_scroll_offset_; | |
69 }; | |
70 | |
71 class ScrollElasticityControllerTest : public testing::Test { | |
72 public: | |
73 ScrollElasticityControllerTest() | |
74 : controller_(&helper_), | |
75 input_event_count_(0), | |
76 current_time_(base::TimeTicks::FromInternalValue(100000000ull)) {} | |
77 ~ScrollElasticityControllerTest() override {} | |
78 | |
79 void SendMouseWheelEvent( | |
80 Phase phase, | |
81 Phase momentum_phase, | |
82 const gfx::Vector2dF& event_delta = gfx::Vector2dF(), | |
83 const gfx::Vector2dF& overscroll_delta = gfx::Vector2dF()) { | |
84 blink::WebMouseWheelEvent event; | |
85 event.phase = static_cast<blink::WebMouseWheelEvent::Phase>(phase); | |
86 event.momentumPhase = | |
87 static_cast<blink::WebMouseWheelEvent::Phase>(momentum_phase); | |
88 event.deltaX = -event_delta.x(); | |
89 event.deltaY = -event_delta.y(); | |
90 TickCurrentTime(); | |
91 event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF(); | |
92 | |
93 cc::InputHandlerScrollResult scroll_result; | |
94 scroll_result.did_overscroll_root = !overscroll_delta.IsZero(); | |
95 scroll_result.unused_scroll_delta = overscroll_delta; | |
96 | |
97 controller_.ObserveWheelEventAndResult(event, scroll_result); | |
98 input_event_count_ += 1; | |
99 } | |
100 | |
101 const base::TimeTicks& TickCurrentTime() { | |
102 current_time_ += base::TimeDelta::FromSecondsD(1 / 60.f); | |
103 return current_time_; | |
104 } | |
105 void TickCurrentTimeAndAnimate() { | |
106 TickCurrentTime(); | |
107 controller_.Animate(current_time_); | |
108 } | |
109 | |
110 MockScrollElasticityHelper helper_; | |
111 InputScrollElasticityController controller_; | |
112 int input_event_count_; | |
113 base::TimeTicks current_time_; | |
114 }; | |
115 | |
116 // Verify that stretching only occurs in one axis at a time, and that it | |
117 // is biased to the Y axis. | |
118 TEST_F(ScrollElasticityControllerTest, Axis) { | |
119 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0), | |
120 gfx::ScrollOffset(0, 0)); | |
121 | |
122 // If we push equally in the X and Y directions, we should see a stretch only | |
123 // in the Y direction. | |
124 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
125 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(10, 10), | |
126 gfx::Vector2dF(10, 10)); | |
127 EXPECT_EQ(1, helper_.set_stretch_amount_count()); | |
128 EXPECT_EQ(0.f, helper_.StretchAmount().x()); | |
129 EXPECT_LT(0.f, helper_.StretchAmount().y()); | |
130 helper_.SetStretchAmount(gfx::Vector2dF()); | |
131 EXPECT_EQ(2, helper_.set_stretch_amount_count()); | |
132 SendMouseWheelEvent(PhaseEnded, PhaseNone); | |
133 EXPECT_EQ(0, helper_.request_animate_count()); | |
134 | |
135 // If we push more in the X direction than the Y direction, we should see a | |
136 // stretch only in the X direction. This decision should be based on the | |
137 // input delta, not the actual overscroll delta. | |
138 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
139 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(-25, 10), | |
140 gfx::Vector2dF(-25, 40)); | |
141 EXPECT_EQ(3, helper_.set_stretch_amount_count()); | |
142 EXPECT_GT(0.f, helper_.StretchAmount().x()); | |
143 EXPECT_EQ(0.f, helper_.StretchAmount().y()); | |
144 helper_.SetStretchAmount(gfx::Vector2dF()); | |
145 EXPECT_EQ(4, helper_.set_stretch_amount_count()); | |
146 SendMouseWheelEvent(PhaseEnded, PhaseNone); | |
147 EXPECT_EQ(0, helper_.request_animate_count()); | |
148 } | |
149 | |
150 // Verify that we need a total overscroll delta of at least 10 in a pinned | |
151 // direction before we start stretching. | |
152 TEST_F(ScrollElasticityControllerTest, MinimumDeltaBeforeStretch) { | |
153 // We should not start stretching while we are not pinned in the direction | |
154 // of the scroll (even if there is an overscroll delta). We have to wait for | |
155 // the regular scroll to eat all of the events. | |
156 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), | |
157 gfx::ScrollOffset(10, 10)); | |
158 SendMouseWheelEvent(PhaseMayBegin, PhaseNone); | |
159 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
160 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, 10), | |
161 gfx::Vector2dF(0, 10)); | |
162 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, 10), | |
163 gfx::Vector2dF(0, 10)); | |
164 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
165 | |
166 // Now pin the -X and +Y direction. The first event will not generate a | |
167 // stretch | |
168 // because it is below the delta threshold of 10. | |
169 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 10), | |
170 gfx::ScrollOffset(10, 10)); | |
171 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, 10), | |
172 gfx::Vector2dF(0, 8)); | |
173 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
174 | |
175 // Make the next scroll be in the -X direction more than the +Y direction, | |
176 // which will erase the memory of the previous unused delta of 8. | |
177 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(-10, 5), | |
178 gfx::Vector2dF(-8, 10)); | |
179 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
180 | |
181 // Now push against the pinned +Y direction again by 8. We reset the | |
182 // previous delta, so this will not generate a stretch. | |
183 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, 10), | |
184 gfx::Vector2dF(0, 8)); | |
185 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
186 | |
187 // Push against +Y by another 8. This gets us above the delta threshold of | |
188 // 10, so we should now have had the stretch set, and it should be in the | |
189 // +Y direction. The scroll in the -X direction should have been forgotten. | |
190 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, 10), | |
191 gfx::Vector2dF(0, 8)); | |
192 EXPECT_EQ(1, helper_.set_stretch_amount_count()); | |
193 EXPECT_EQ(0.f, helper_.StretchAmount().x()); | |
194 EXPECT_LT(0.f, helper_.StretchAmount().y()); | |
195 | |
196 // End the gesture. Because there is a non-zero stretch, we should be in the | |
197 // animated state, and should have had a frame requested. | |
198 EXPECT_EQ(0, helper_.request_animate_count()); | |
199 SendMouseWheelEvent(PhaseEnded, PhaseNone); | |
200 EXPECT_EQ(1, helper_.request_animate_count()); | |
201 } | |
202 | |
203 // Verify that an stretch caused by a momentum scroll will switch to the | |
204 // animating mode, where input events are ignored, and the stretch is updated | |
205 // while animating. | |
206 TEST_F(ScrollElasticityControllerTest, MomentumAnimate) { | |
207 // Do an active scroll, then switch to the momentum phase and scroll for a | |
208 // bit. | |
209 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), | |
210 gfx::ScrollOffset(10, 10)); | |
211 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
212 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, -80), | |
213 gfx::Vector2dF(0, 0)); | |
214 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, -80), | |
215 gfx::Vector2dF(0, 0)); | |
216 SendMouseWheelEvent(PhaseChanged, PhaseNone, gfx::Vector2dF(0, -80), | |
217 gfx::Vector2dF(0, 0)); | |
218 SendMouseWheelEvent(PhaseEnded, PhaseNone); | |
219 SendMouseWheelEvent(PhaseNone, PhaseBegan); | |
220 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
221 gfx::Vector2dF(0, 0)); | |
222 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
223 gfx::Vector2dF(0, 0)); | |
224 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
225 gfx::Vector2dF(0, 0)); | |
226 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
227 | |
228 // Hit the -Y edge and overscroll slightly, but not enough to go over the | |
229 // threshold to cause a stretch. | |
230 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 0), | |
231 gfx::ScrollOffset(10, 10)); | |
232 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
233 gfx::Vector2dF(0, -8)); | |
234 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
235 EXPECT_EQ(0, helper_.request_animate_count()); | |
236 | |
237 // Take another step, this time going over the threshold. This should update | |
238 // the stretch amount, and then switch to the animating mode. | |
239 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
240 gfx::Vector2dF(0, -80)); | |
241 EXPECT_EQ(1, helper_.set_stretch_amount_count()); | |
242 EXPECT_EQ(1, helper_.request_animate_count()); | |
243 EXPECT_GT(-1.f, helper_.StretchAmount().y()); | |
244 | |
245 // Subsequent momentum events should do nothing. | |
246 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
247 gfx::Vector2dF(0, -80)); | |
248 SendMouseWheelEvent(PhaseNone, PhaseChanged, gfx::Vector2dF(0, -80), | |
249 gfx::Vector2dF(0, -80)); | |
250 SendMouseWheelEvent(PhaseNone, PhaseEnded, gfx::Vector2dF(0, -80), | |
251 gfx::Vector2dF(0, -80)); | |
252 EXPECT_EQ(1, helper_.set_stretch_amount_count()); | |
253 EXPECT_EQ(1, helper_.request_animate_count()); | |
254 | |
255 // Subsequent animate events should update the stretch amount and request | |
256 // another frame. | |
257 TickCurrentTimeAndAnimate(); | |
258 EXPECT_EQ(2, helper_.set_stretch_amount_count()); | |
259 EXPECT_EQ(2, helper_.request_animate_count()); | |
260 EXPECT_GT(-1.f, helper_.StretchAmount().y()); | |
261 | |
262 // Touching the trackpad (a PhaseMayBegin event) should disable animation. | |
263 SendMouseWheelEvent(PhaseMayBegin, PhaseNone); | |
264 TickCurrentTimeAndAnimate(); | |
265 EXPECT_EQ(2, helper_.set_stretch_amount_count()); | |
266 EXPECT_EQ(2, helper_.request_animate_count()); | |
267 | |
268 // Releasing the trackpad should re-enable animation. | |
269 SendMouseWheelEvent(PhaseCancelled, PhaseNone); | |
270 EXPECT_EQ(2, helper_.set_stretch_amount_count()); | |
271 EXPECT_EQ(3, helper_.request_animate_count()); | |
272 TickCurrentTimeAndAnimate(); | |
273 EXPECT_EQ(3, helper_.set_stretch_amount_count()); | |
274 EXPECT_EQ(4, helper_.request_animate_count()); | |
275 | |
276 // Keep animating frames until the stretch returns to rest. | |
277 int stretch_count = 3; | |
278 int animate_count = 4; | |
279 while (1) { | |
280 TickCurrentTimeAndAnimate(); | |
281 if (helper_.StretchAmount().IsZero()) { | |
282 stretch_count += 1; | |
283 EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count()); | |
284 EXPECT_EQ(animate_count, helper_.request_animate_count()); | |
285 break; | |
286 } | |
287 stretch_count += 1; | |
288 animate_count += 1; | |
289 EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count()); | |
290 EXPECT_EQ(animate_count, helper_.request_animate_count()); | |
291 } | |
292 | |
293 // After coming to rest, no subsequent animate calls change anything. | |
294 TickCurrentTimeAndAnimate(); | |
295 EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count()); | |
296 EXPECT_EQ(animate_count, helper_.request_animate_count()); | |
297 } | |
298 | |
299 // Verify that an stretch opposing a scroll is correctly resolved. | |
300 TEST_F(ScrollElasticityControllerTest, ReconcileStretchAndScroll) { | |
301 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
302 | |
303 // Verify completely knocking out the scroll in the -Y direction. | |
304 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), | |
305 gfx::ScrollOffset(10, 10)); | |
306 helper_.SetStretchAmount(gfx::Vector2dF(0, -10)); | |
307 controller_.ReconcileStretchAndScroll(); | |
308 EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, -5)); | |
309 EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(5, 0)); | |
310 | |
311 // Verify partially knocking out the scroll in the -Y direction. | |
312 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 8), | |
313 gfx::ScrollOffset(10, 10)); | |
314 helper_.SetStretchAmount(gfx::Vector2dF(0, -5)); | |
315 controller_.ReconcileStretchAndScroll(); | |
316 EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); | |
317 EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(5, 3)); | |
318 | |
319 // Verify completely knocking out the scroll in the +X direction. | |
320 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5), | |
321 gfx::ScrollOffset(10, 10)); | |
322 helper_.SetStretchAmount(gfx::Vector2dF(10, 0)); | |
323 controller_.ReconcileStretchAndScroll(); | |
324 EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(5, 0)); | |
325 EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(10, 5)); | |
326 | |
327 // Verify partially knocking out the scroll in the +X and +Y directions. | |
328 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(2, 3), | |
329 gfx::ScrollOffset(10, 10)); | |
330 helper_.SetStretchAmount(gfx::Vector2dF(5, 5)); | |
331 controller_.ReconcileStretchAndScroll(); | |
332 EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); | |
333 EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(7, 8)); | |
334 } | |
335 | |
336 // Verify that stretching only happens when the area is user scrollable. | |
337 TEST_F(ScrollElasticityControllerTest, UserScrollableRequiredForStretch) { | |
338 helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0), | |
339 gfx::ScrollOffset(10, 10)); | |
340 gfx::Vector2dF delta(0, -15); | |
341 | |
342 // Do an active scroll, and ensure that the stretch amount doesn't change, | |
343 // and also that the stretch amount isn't even ever changed. | |
344 helper_.SetUserScrollable(false); | |
345 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
346 SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta); | |
347 SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta); | |
348 SendMouseWheelEvent(PhaseEnded, PhaseNone); | |
349 EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); | |
350 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
351 SendMouseWheelEvent(PhaseNone, PhaseBegan); | |
352 SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta); | |
353 SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta); | |
354 SendMouseWheelEvent(PhaseNone, PhaseEnded); | |
355 EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); | |
356 EXPECT_EQ(0, helper_.set_stretch_amount_count()); | |
357 | |
358 // Re-enable user scrolling and ensure that stretching is re-enabled. | |
359 helper_.SetUserScrollable(true); | |
360 SendMouseWheelEvent(PhaseBegan, PhaseNone); | |
361 SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta); | |
362 SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta); | |
363 SendMouseWheelEvent(PhaseEnded, PhaseNone); | |
364 EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); | |
365 EXPECT_GT(helper_.set_stretch_amount_count(), 0); | |
366 SendMouseWheelEvent(PhaseNone, PhaseBegan); | |
367 SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta); | |
368 SendMouseWheelEvent(PhaseNone, PhaseChanged, delta, delta); | |
369 SendMouseWheelEvent(PhaseNone, PhaseEnded); | |
370 EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0)); | |
371 EXPECT_GT(helper_.set_stretch_amount_count(), 0); | |
372 | |
373 // Disable user scrolling and tick the timer until the stretch goes back | |
374 // to zero. Ensure that the return to zero doesn't happen immediately. | |
375 helper_.SetUserScrollable(false); | |
376 int ticks_to_zero = 0; | |
377 while (1) { | |
378 TickCurrentTimeAndAnimate(); | |
379 if (helper_.StretchAmount().IsZero()) | |
380 break; | |
381 ticks_to_zero += 1; | |
382 } | |
383 EXPECT_GT(ticks_to_zero, 3); | |
384 } | |
385 | |
386 } // namespace | |
387 } // namespace content | |
OLD | NEW |