blob: 95a0dd7a0761e965da7722fb38fa660ff2f89751 [file] [log] [blame]
[email protected]b4df9df2012-11-16 01:58:581// Copyright (c) 2012 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 "content/browser/renderer_host/overscroll_controller.h"
6
Mohsen Izadia0091532017-07-06 04:21:057#include <algorithm>
8
[email protected]3aad43b2013-11-05 20:20:189#include "base/command_line.h"
[email protected]fc4b06d02013-10-23 20:47:1110#include "base/logging.h"
Peter Kastingb5b39872019-09-10 08:44:0411#include "base/numerics/ranges.h"
[email protected]b4df9df2012-11-16 01:58:5812#include "content/browser/renderer_host/overscroll_controller_delegate.h"
[email protected]1cd76c2d82012-11-29 07:36:4713#include "content/public/browser/overscroll_configuration.h"
Sahel Sharifya29ee2112017-12-15 23:22:5414#include "content/public/common/content_features.h"
[email protected]3aad43b2013-11-05 20:20:1815#include "content/public/common/content_switches.h"
[email protected]b4df9df2012-11-16 01:58:5816
Mohsen Izadia0091532017-07-06 04:21:0517namespace content {
18
[email protected]3aad43b2013-11-05 20:20:1819namespace {
20
Daniel Cheng224569ee2018-04-25 05:45:0621// Minimum amount of time after an actual scroll after which a pull-to-refresh
22// can start.
23constexpr base::TimeDelta kPullToRefreshCoolOffDelay =
24 base::TimeDelta::FromMilliseconds(600);
Mohsen Izadi5a6583512018-04-23 18:31:1925
dtapuska19ebfa512016-02-19 22:27:4026bool IsGestureEventFromTouchpad(const blink::WebInputEvent& event) {
Blink Reformat1c4d759e2017-04-09 16:34:5427 DCHECK(blink::WebInputEvent::IsGestureEventType(event.GetType()));
dtapuska19ebfa512016-02-19 22:27:4028 const blink::WebGestureEvent& gesture =
29 static_cast<const blink::WebGestureEvent&>(event);
Daniel Cheng7f9ec902019-04-18 05:07:0030 return gesture.SourceDevice() == blink::WebGestureDevice::kTouchpad;
dtapuska19ebfa512016-02-19 22:27:4031}
32
Sahel Sharifybdaa29e2018-09-17 17:42:2533bool IsGestureEventFromAutoscroll(const blink::WebGestureEvent event) {
Daniel Cheng7f9ec902019-04-18 05:07:0034 return event.SourceDevice() == blink::WebGestureDevice::kSyntheticAutoscroll;
Sahel Sharifybdaa29e2018-09-17 17:42:2535}
36
chaopeng9736c522018-05-08 18:32:2737bool IsGestureScrollUpdateInertialEvent(const blink::WebInputEvent& event) {
38 if (event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
39 return false;
40
41 const blink::WebGestureEvent& gesture =
42 static_cast<const blink::WebGestureEvent&>(event);
43 return gesture.data.scroll_update.inertial_phase ==
Daniel Libby6ce6b95a2019-05-10 17:06:2644 blink::WebGestureEvent::InertialPhaseState::kMomentum;
chaopeng9736c522018-05-08 18:32:2745}
46
Mohsen Izadia0091532017-07-06 04:21:0547float ClampAbsoluteValue(float value, float max_abs) {
48 DCHECK_LT(0.f, max_abs);
Peter Kastingb5b39872019-09-10 08:44:0449 return base::ClampToRange(value, -max_abs, max_abs);
Mohsen Izadia0091532017-07-06 04:21:0550}
[email protected]3aad43b2013-11-05 20:20:1851
Mohsen Izadia0091532017-07-06 04:21:0552} // namespace
[email protected]b4df9df2012-11-16 01:58:5853
Sahel Sharifye6d81f472018-07-11 20:40:2654OverscrollController::OverscrollController() {}
[email protected]b4df9df2012-11-16 01:58:5855
Mohsen Izadi38795d5d2017-06-30 17:10:4856OverscrollController::~OverscrollController() {}
[email protected]b4df9df2012-11-16 01:58:5857
dtapuska19ebfa512016-02-19 22:27:4058bool OverscrollController::ShouldProcessEvent(
59 const blink::WebInputEvent& event) {
Blink Reformat1c4d759e2017-04-09 16:34:5460 switch (event.GetType()) {
Blink Reformat1c4d759e2017-04-09 16:34:5461 case blink::WebInputEvent::kGestureScrollBegin:
62 case blink::WebInputEvent::kGestureScrollUpdate:
63 case blink::WebInputEvent::kGestureScrollEnd: {
dtapuska8c4dae12017-01-13 00:23:0664 const blink::WebGestureEvent& gesture =
65 static_cast<const blink::WebGestureEvent&>(event);
mcnee19fd2492017-06-01 14:42:4366
67 // GestureScrollBegin and GestureScrollEnd events are created to wrap
68 // individual resent GestureScrollUpdates from a plugin. Hence these
69 // should not be used to indicate the beginning/end of the overscroll.
70 // TODO(mcnee): When we remove BrowserPlugin, delete this code.
71 // See crbug.com/533069
72 if (gesture.resending_plugin_id != -1 &&
73 event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
74 return false;
75
Sahel Sharifybdaa29e2018-09-17 17:42:2576 // Gesture events with Autoscroll source don't cause overscrolling.
77 if (IsGestureEventFromAutoscroll(gesture))
78 return false;
79
Daniel Libbye15b80b2019-05-24 17:18:2480 ui::input_types::ScrollGranularity granularity;
Blink Reformat1c4d759e2017-04-09 16:34:5481 switch (event.GetType()) {
82 case blink::WebInputEvent::kGestureScrollBegin:
Daniel Libbya37d447a2019-05-02 20:06:4883 granularity = gesture.data.scroll_begin.delta_hint_units;
dtapuska8c4dae12017-01-13 00:23:0684 break;
Blink Reformat1c4d759e2017-04-09 16:34:5485 case blink::WebInputEvent::kGestureScrollUpdate:
Daniel Libbya37d447a2019-05-02 20:06:4886 granularity = gesture.data.scroll_update.delta_units;
dtapuska8c4dae12017-01-13 00:23:0687 break;
Blink Reformat1c4d759e2017-04-09 16:34:5488 case blink::WebInputEvent::kGestureScrollEnd:
Daniel Libbya37d447a2019-05-02 20:06:4889 granularity = gesture.data.scroll_end.delta_units;
dtapuska8c4dae12017-01-13 00:23:0690 break;
91 default:
Daniel Libbye15b80b2019-05-24 17:18:2492 granularity = ui::input_types::ScrollGranularity::kScrollByPixel;
dtapuska8c4dae12017-01-13 00:23:0693 break;
dtapuska19ebfa512016-02-19 22:27:4094 }
dtapuska8c4dae12017-01-13 00:23:0695
Daniel Libbye15b80b2019-05-24 17:18:2496 return granularity ==
97 ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
dtapuska19ebfa512016-02-19 22:27:4098 }
dtapuska8c4dae12017-01-13 00:23:0699 default:
100 break;
101 }
dtapuska19ebfa512016-02-19 22:27:40102 return true;
103}
104
chaopenga7585d792018-04-10 18:02:38105bool OverscrollController::ShouldIgnoreInertialEvent(
106 const blink::WebInputEvent& event) const {
chaopeng9736c522018-05-08 18:32:27107 return ignore_following_inertial_events_ &&
108 IsGestureScrollUpdateInertialEvent(event);
chaopenga7585d792018-04-10 18:02:38109 }
110
[email protected]277857a2014-06-03 10:38:01111bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
dtapuska19ebfa512016-02-19 22:27:40112 if (!ShouldProcessEvent(event))
113 return false;
114
Mohsen Izadi9830436c2017-12-21 18:02:46115 // TODO(mohsen): Consider filtering mouse-wheel events during overscroll. See
116 // https://crbug.com/772106.
117 if (event.GetType() == blink::WebInputEvent::kMouseWheel)
118 return false;
119
Mohsen Izadi5a6583512018-04-23 18:31:19120 if (event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
121 ignore_following_inertial_events_ = false;
chaopeng9736c522018-05-08 18:32:27122 first_inertial_event_time_.reset();
Mohsen Izadi5a6583512018-04-23 18:31:19123 time_since_last_ignored_scroll_ =
Daniel Cheng224569ee2018-04-25 05:45:06124 event.TimeStamp() - last_ignored_scroll_time_;
Mohsen Izadi5a6583512018-04-23 18:31:19125 // Will handle events when processing ACKs to ensure the correct order.
126 return false;
127 }
128
129 if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd) {
130 if (scroll_state_ == ScrollState::CONTENT_CONSUMING ||
131 overscroll_ignored_) {
Daniel Cheng224569ee2018-04-25 05:45:06132 last_ignored_scroll_time_ = event.TimeStamp();
Mohsen Izadi5a6583512018-04-23 18:31:19133 }
Chong Zhang064c2052017-08-29 16:05:31134 // Will handle events when processing ACKs to ensure the correct order.
135 return false;
136 }
137
chaopeng1d469b02018-04-05 15:41:07138 // Consume the scroll-update events if they are from a inertial scroll (fling)
139 // event that completed an overscroll gesture.
chaopenga7585d792018-04-10 18:02:38140 if (ShouldIgnoreInertialEvent(event))
141 return true;
chaopeng1d469b02018-04-05 15:41:07142
mfomitchev8a785bef2015-04-13 18:35:05143 bool reset_scroll_state = false;
Mohsen Izadi9830436c2017-12-21 18:02:46144 if (scroll_state_ != ScrollState::NONE || overscroll_delta_x_ ||
145 overscroll_delta_y_) {
Blink Reformat1c4d759e2017-04-09 16:34:54146 switch (event.GetType()) {
Blink Reformat1c4d759e2017-04-09 16:34:54147 case blink::WebInputEvent::kGestureFlingStart:
mfomitchev8a785bef2015-04-13 18:35:05148 reset_scroll_state = true;
[email protected]b31beeb2013-06-17 15:54:27149 break;
150
[email protected]b31beeb2013-06-17 15:54:27151 default:
Blink Reformat1c4d759e2017-04-09 16:34:54152 if (blink::WebInputEvent::IsMouseEventType(event.GetType()) ||
153 blink::WebInputEvent::IsKeyboardEventType(event.GetType())) {
mfomitchev8a785bef2015-04-13 18:35:05154 reset_scroll_state = true;
[email protected]b31beeb2013-06-17 15:54:27155 }
156 break;
[email protected]a4141642013-05-23 04:10:58157 }
158 }
159
Mohsen Izadi50180f392017-09-08 19:29:12160 if (reset_scroll_state)
161 ResetScrollState();
mfomitchev8a785bef2015-04-13 18:35:05162
[email protected]b4df9df2012-11-16 01:58:58163 if (DispatchEventCompletesAction(event)) {
164 CompleteAction();
[email protected]9f4f47e2012-11-20 02:21:43165
[email protected]277857a2014-06-03 10:38:01166 // Let the event be dispatched to the renderer.
167 return false;
[email protected]b4df9df2012-11-16 01:58:58168 }
169
170 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
mfomitchev09f0d64a2017-03-02 19:40:07171 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]b4df9df2012-11-16 01:58:58172
173 // Let the event be dispatched to the renderer.
[email protected]277857a2014-06-03 10:38:01174 return false;
[email protected]b4df9df2012-11-16 01:58:58175 }
176
177 if (overscroll_mode_ != OVERSCROLL_NONE) {
[email protected]2422e5c2013-09-13 19:05:02178 // Consume the event only if it updates the overscroll state.
179 if (ProcessEventForOverscroll(event))
[email protected]277857a2014-06-03 10:38:01180 return true;
mfomitchev8a785bef2015-04-13 18:35:05181 } else if (reset_scroll_state) {
182 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]b4df9df2012-11-16 01:58:58183 }
184
Mohsen Izadi50180f392017-09-08 19:29:12185 // In overscrolling state, consume scroll-update and fling-start events when
186 // they do not contribute to overscroll in order to prevent content scroll.
Mohsen Izadi9830436c2017-12-21 18:02:46187 return scroll_state_ == ScrollState::OVERSCROLLING &&
Mohsen Izadi50180f392017-09-08 19:29:12188 (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
189 event.GetType() == blink::WebInputEvent::kGestureFlingStart);
[email protected]b4df9df2012-11-16 01:58:58190}
191
Sandra Suncbd6da42018-01-05 15:29:36192void OverscrollController::OnDidOverscroll(
193 const ui::DidOverscrollParams& params) {
194 // TODO(sunyunjia): We should also decide whether to trigger overscroll,
195 // update scroll_state_ here. See https://crbug.com/799467.
Mohsen Izadi80189412018-03-22 17:10:18196 behavior_ = params.overscroll_behavior;
Sandra Suncbd6da42018-01-05 15:29:36197}
198
[email protected]180ef242013-11-07 06:50:46199void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event,
[email protected]b4df9df2012-11-16 01:58:58200 bool processed) {
dtapuska19ebfa512016-02-19 22:27:40201 if (!ShouldProcessEvent(event))
202 return;
203
chaopenga7585d792018-04-10 18:02:38204 // An inertial scroll (fling) event from a completed overscroll gesture
205 // should not modify states below.
206 if (ShouldIgnoreInertialEvent(event))
207 return;
208
[email protected]a4141642013-05-23 04:10:58209 if (processed) {
210 // If a scroll event is consumed by the page, i.e. some content on the page
211 // has been scrolled, then there is not going to be an overscroll gesture,
212 // until the current scroll ends, and a new scroll gesture starts.
Mohsen Izadi9830436c2017-12-21 18:02:46213 // Similarly, if a mouse-wheel event is consumed, probably the page has
214 // implemented its own scroll-like behavior and no overscroll should happen.
215 if (scroll_state_ == ScrollState::NONE &&
216 (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
217 event.GetType() == blink::WebInputEvent::kMouseWheel)) {
218 scroll_state_ = ScrollState::CONTENT_CONSUMING;
[email protected]a4141642013-05-23 04:10:58219 }
Mohsen Izadi50180f392017-09-08 19:29:12220 // In overscrolling state, only return if we are in an overscroll mode;
221 // otherwise, we would want to ProcessEventForOverscroll to let it start a
222 // new overscroll mode.
Mohsen Izadi9830436c2017-12-21 18:02:46223 if (scroll_state_ != ScrollState::OVERSCROLLING ||
Mohsen Izadi50180f392017-09-08 19:29:12224 overscroll_mode_ != OVERSCROLL_NONE) {
225 return;
226 }
[email protected]a4141642013-05-23 04:10:58227 }
Mohsen Izadi9830436c2017-12-21 18:02:46228
229 if (event.GetType() == blink::WebInputEvent::kMouseWheel)
230 return;
231
[email protected]b4df9df2012-11-16 01:58:58232 ProcessEventForOverscroll(event);
233}
234
[email protected]b4df9df2012-11-16 01:58:58235void OverscrollController::Reset() {
236 overscroll_mode_ = OVERSCROLL_NONE;
Mohsen Izadi38795d5d2017-06-30 17:10:48237 overscroll_source_ = OverscrollSource::NONE;
[email protected]b4df9df2012-11-16 01:58:58238 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadi50180f392017-09-08 19:29:12239 ResetScrollState();
[email protected]b4df9df2012-11-16 01:58:58240}
241
[email protected]63ad73832013-06-17 15:41:04242void OverscrollController::Cancel() {
mfomitchev09f0d64a2017-03-02 19:40:07243 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]63ad73832013-06-17 15:41:04244 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadi50180f392017-09-08 19:29:12245 ResetScrollState();
[email protected]63ad73832013-06-17 15:41:04246}
247
chaopeng1d469b02018-04-05 15:41:07248bool OverscrollController::DispatchEventCompletesAction(
[email protected]180ef242013-11-07 06:50:46249 const blink::WebInputEvent& event) const {
[email protected]b4df9df2012-11-16 01:58:58250 if (overscroll_mode_ == OVERSCROLL_NONE)
251 return false;
Mohsen Izadi4ef8db72017-10-31 00:08:42252 DCHECK_NE(OverscrollSource::NONE, overscroll_source_);
[email protected]b4df9df2012-11-16 01:58:58253
254 // Complete the overscroll gesture if there was a mouse move or a scroll-end
255 // after the threshold.
Blink Reformat1c4d759e2017-04-09 16:34:54256 if (event.GetType() != blink::WebInputEvent::kMouseMove &&
257 event.GetType() != blink::WebInputEvent::kGestureScrollEnd &&
chaopeng1d469b02018-04-05 15:41:07258 event.GetType() != blink::WebInputEvent::kGestureFlingStart &&
259 event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
[email protected]b4df9df2012-11-16 01:58:58260 return false;
261
chaopeng1d469b02018-04-05 15:41:07262 // Complete the overscroll gesture for inertial scroll (fling) event from
263 // touchpad.
264 if (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
265 if (overscroll_source_ != OverscrollSource::TOUCHPAD)
266 return false;
267 DCHECK(IsGestureEventFromTouchpad(event));
268 const blink::WebGestureEvent gesture_event =
269 static_cast<const blink::WebGestureEvent&>(event);
270 if (gesture_event.data.scroll_update.inertial_phase !=
Daniel Libby6ce6b95a2019-05-10 17:06:26271 blink::WebGestureEvent::InertialPhaseState::kMomentum)
chaopeng1d469b02018-04-05 15:41:07272 return false;
273 }
274
Blink Reformat1c4d759e2017-04-09 16:34:54275 if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd &&
Mohsen Izadi4ef8db72017-10-31 00:08:42276 overscroll_source_ == OverscrollSource::TOUCHPAD) {
277 DCHECK(IsGestureEventFromTouchpad(event));
Sahel Sharifya29ee2112017-12-15 23:22:54278 // Complete the action for a GSE with touchpad source only when it is in
279 // momentumPhase.
280 const blink::WebGestureEvent gesture_event =
281 static_cast<const blink::WebGestureEvent&>(event);
282 if (gesture_event.data.scroll_end.inertial_phase !=
Daniel Libby6ce6b95a2019-05-10 17:06:26283 blink::WebGestureEvent::InertialPhaseState::kMomentum)
Sahel Sharifya29ee2112017-12-15 23:22:54284 return false;
Mohsen Izadi38795d5d2017-06-30 17:10:48285 }
dtapuska19ebfa512016-02-19 22:27:40286
[email protected]0da15c2d2013-10-30 16:47:20287 if (!delegate_)
288 return false;
289
Blink Reformat1c4d759e2017-04-09 16:34:54290 if (event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
[email protected]6658c6a2013-01-23 23:42:22291 // Check to see if the fling is in the same direction of the overscroll.
[email protected]180ef242013-11-07 06:50:46292 const blink::WebGestureEvent gesture =
293 static_cast<const blink::WebGestureEvent&>(event);
[email protected]6658c6a2013-01-23 23:42:22294 switch (overscroll_mode_) {
295 case OVERSCROLL_EAST:
Blink Reformat1c4d759e2017-04-09 16:34:54296 if (gesture.data.fling_start.velocity_x < 0)
[email protected]6658c6a2013-01-23 23:42:22297 return false;
298 break;
299 case OVERSCROLL_WEST:
Blink Reformat1c4d759e2017-04-09 16:34:54300 if (gesture.data.fling_start.velocity_x > 0)
[email protected]6658c6a2013-01-23 23:42:22301 return false;
302 break;
303 case OVERSCROLL_NORTH:
Blink Reformat1c4d759e2017-04-09 16:34:54304 if (gesture.data.fling_start.velocity_y > 0)
[email protected]6658c6a2013-01-23 23:42:22305 return false;
306 break;
307 case OVERSCROLL_SOUTH:
Blink Reformat1c4d759e2017-04-09 16:34:54308 if (gesture.data.fling_start.velocity_y < 0)
[email protected]6658c6a2013-01-23 23:42:22309 return false;
310 break;
311 case OVERSCROLL_NONE:
[email protected]6658c6a2013-01-23 23:42:22312 NOTREACHED();
313 }
314 }
315
Mohsen Izadi4bd624cc2017-07-21 19:23:51316 const gfx::Size size = delegate_->GetDisplaySize();
Mohsen Izadi38795d5d2017-06-30 17:10:48317 if (size.IsEmpty())
318 return false;
319
Mohsen Izadi4ef8db72017-10-31 00:08:42320 const float delta =
321 overscroll_mode_ == OVERSCROLL_WEST || overscroll_mode_ == OVERSCROLL_EAST
322 ? overscroll_delta_x_
323 : overscroll_delta_y_;
324 const float ratio = fabs(delta) / std::max(size.width(), size.height());
Elly Fong-Jones47960972019-05-16 18:40:59325 const float threshold =
Mohsen Izadi4ef8db72017-10-31 00:08:42326 overscroll_source_ == OverscrollSource::TOUCHPAD
Elly Fong-Jones47960972019-05-16 18:40:59327 ? OverscrollConfig::kCompleteTouchpadThresholdPercent
328 : OverscrollConfig::kCompleteTouchscreenThresholdPercent;
[email protected]b4df9df2012-11-16 01:58:58329 return ratio >= threshold;
330}
331
332bool OverscrollController::DispatchEventResetsState(
[email protected]180ef242013-11-07 06:50:46333 const blink::WebInputEvent& event) const {
Blink Reformat1c4d759e2017-04-09 16:34:54334 switch (event.GetType()) {
Chong Zhang064c2052017-08-29 16:05:31335 // GestureScrollBegin/End ACK will reset overscroll state when necessary.
Blink Reformat1c4d759e2017-04-09 16:34:54336 case blink::WebInputEvent::kGestureScrollBegin:
337 case blink::WebInputEvent::kGestureScrollEnd:
Blink Reformat1c4d759e2017-04-09 16:34:54338 case blink::WebInputEvent::kGestureScrollUpdate:
339 case blink::WebInputEvent::kGestureFlingCancel:
[email protected]b4df9df2012-11-16 01:58:58340 return false;
341
342 default:
[email protected]c4ddb5c42012-11-17 01:09:57343 // Touch events can arrive during an overscroll gesture initiated by
344 // touch-scrolling. These events should not reset the overscroll state.
Blink Reformat1c4d759e2017-04-09 16:34:54345 return !blink::WebInputEvent::IsTouchEventType(event.GetType());
[email protected]b4df9df2012-11-16 01:58:58346 }
347}
348
[email protected]2422e5c2013-09-13 19:05:02349bool OverscrollController::ProcessEventForOverscroll(
[email protected]180ef242013-11-07 06:50:46350 const blink::WebInputEvent& event) {
[email protected]2422e5c2013-09-13 19:05:02351 bool event_processed = false;
Blink Reformat1c4d759e2017-04-09 16:34:54352 switch (event.GetType()) {
Chong Zhang064c2052017-08-29 16:05:31353 case blink::WebInputEvent::kGestureScrollBegin: {
Sahel Sharifye6d81f472018-07-11 20:40:26354 if (overscroll_mode_ != OVERSCROLL_NONE)
Chong Zhang064c2052017-08-29 16:05:31355 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
Chong Zhang064c2052017-08-29 16:05:31356 break;
357 }
358 case blink::WebInputEvent::kGestureScrollEnd: {
Sahel Sharifya29ee2112017-12-15 23:22:54359 // Only reset the state on GestureScrollEnd generated from the touchpad
360 // when the scrolling is in inertial state.
361 const blink::WebGestureEvent gesture_event =
362 static_cast<const blink::WebGestureEvent&>(event);
Daniel Libby6ce6b95a2019-05-10 17:06:26363 bool reset_scroll_state =
364 !IsGestureEventFromTouchpad(event) ||
365 (gesture_event.data.scroll_end.inertial_phase ==
366 blink::WebGestureEvent::InertialPhaseState::kMomentum);
Chong Zhang064c2052017-08-29 16:05:31367
368 if (reset_scroll_state)
Mohsen Izadi50180f392017-09-08 19:29:12369 ResetScrollState();
Chong Zhang064c2052017-08-29 16:05:31370
371 if (DispatchEventCompletesAction(event)) {
372 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02373 break;
Chong Zhang064c2052017-08-29 16:05:31374 }
375
376 if (!reset_scroll_state)
377 break;
378
379 if (overscroll_mode_ != OVERSCROLL_NONE) {
380 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
381 } else {
382 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
383 }
[email protected]b4df9df2012-11-16 01:58:58384 break;
385 }
Blink Reformat1c4d759e2017-04-09 16:34:54386 case blink::WebInputEvent::kGestureScrollUpdate: {
[email protected]180ef242013-11-07 06:50:46387 const blink::WebGestureEvent& gesture =
388 static_cast<const blink::WebGestureEvent&>(event);
chaopeng9736c522018-05-08 18:32:27389 bool is_gesture_scroll_update_inertial_event =
390 IsGestureScrollUpdateInertialEvent(event);
dtapuska19ebfa512016-02-19 22:27:40391 event_processed = ProcessOverscroll(
Blink Reformat1c4d759e2017-04-09 16:34:54392 gesture.data.scroll_update.delta_x,
393 gesture.data.scroll_update.delta_y,
Daniel Cheng7f9ec902019-04-18 05:07:00394 gesture.SourceDevice() == blink::WebGestureDevice::kTouchpad,
chaopeng9736c522018-05-08 18:32:27395 is_gesture_scroll_update_inertial_event);
396 if (is_gesture_scroll_update_inertial_event) {
397 // Record the timestamp of first inertial event.
398 if (!first_inertial_event_time_) {
399 first_inertial_event_time_ = event.TimeStamp();
400 break;
401 }
402 base::TimeDelta inertial_event_interval =
403 event.TimeStamp() - first_inertial_event_time_.value();
404 if (inertial_event_interval >=
405 OverscrollConfig::MaxInertialEventsBeforeOverscrollCancellation()) {
406 ignore_following_inertial_events_ = true;
407 // Reset overscroll state if fling didn't complete the overscroll
408 // gesture within the first 20 inertial events.
409 Cancel();
410 }
411 }
[email protected]b4df9df2012-11-16 01:58:58412 break;
413 }
Blink Reformat1c4d759e2017-04-09 16:34:54414 case blink::WebInputEvent::kGestureFlingStart: {
[email protected]b4df9df2012-11-16 01:58:58415 const float kFlingVelocityThreshold = 1100.f;
[email protected]180ef242013-11-07 06:50:46416 const blink::WebGestureEvent& gesture =
417 static_cast<const blink::WebGestureEvent&>(event);
Blink Reformat1c4d759e2017-04-09 16:34:54418 float velocity_x = gesture.data.fling_start.velocity_x;
419 float velocity_y = gesture.data.fling_start.velocity_y;
[email protected]b4df9df2012-11-16 01:58:58420 if (fabs(velocity_x) > kFlingVelocityThreshold) {
421 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
422 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
423 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02424 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58425 break;
426 }
427 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
428 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
429 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
430 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02431 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58432 break;
433 }
434 }
435
436 // Reset overscroll state if fling didn't complete the overscroll gesture.
mfomitchev09f0d64a2017-03-02 19:40:07437 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]b4df9df2012-11-16 01:58:58438 break;
439 }
440
441 default:
Blink Reformat1c4d759e2017-04-09 16:34:54442 DCHECK(blink::WebInputEvent::IsGestureEventType(event.GetType()) ||
443 blink::WebInputEvent::IsTouchEventType(event.GetType()))
444 << "Received unexpected event: " << event.GetType();
[email protected]b4df9df2012-11-16 01:58:58445 }
[email protected]2422e5c2013-09-13 19:05:02446 return event_processed;
[email protected]b4df9df2012-11-16 01:58:58447}
448
[email protected]bfab3d02014-08-20 03:16:55449bool OverscrollController::ProcessOverscroll(float delta_x,
[email protected]a7f99f02013-08-29 14:15:19450 float delta_y,
chaopeng9736c522018-05-08 18:32:27451 bool is_touchpad,
452 bool is_inertial) {
Mohsen Izadi9830436c2017-12-21 18:02:46453 if (scroll_state_ == ScrollState::CONTENT_CONSUMING)
454 return false;
455
chaopeng9736c522018-05-08 18:32:27456 // Do not start overscroll for inertial events.
457 if (overscroll_mode_ == OVERSCROLL_NONE && is_inertial)
458 return false;
459
Mohsen Izadi9830436c2017-12-21 18:02:46460 overscroll_delta_x_ += delta_x;
461 overscroll_delta_y_ += delta_y;
[email protected]b4df9df2012-11-16 01:58:58462
Elly Fong-Jones47960972019-05-16 18:40:59463 const float start_threshold =
464 is_touchpad ? OverscrollConfig::kStartTouchpadThresholdDips
465 : OverscrollConfig::kStartTouchscreenThresholdDips;
Mohsen Izadi4ef8db72017-10-31 00:08:42466 if (fabs(overscroll_delta_x_) <= start_threshold &&
467 fabs(overscroll_delta_y_) <= start_threshold) {
mfomitchev09f0d64a2017-03-02 19:40:07468 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]bfab3d02014-08-20 03:16:55469 return true;
[email protected]b4df9df2012-11-16 01:58:58470 }
471
Mohsen Izadia0091532017-07-06 04:21:05472 if (delegate_) {
473 base::Optional<float> cap = delegate_->GetMaxOverscrollDelta();
474 if (cap) {
Mohsen Izadia652de4c2017-07-22 02:30:32475 DCHECK_LE(0.f, cap.value());
Mohsen Izadia0091532017-07-06 04:21:05476 switch (overscroll_mode_) {
477 case OVERSCROLL_WEST:
478 case OVERSCROLL_EAST:
479 overscroll_delta_x_ = ClampAbsoluteValue(
Mohsen Izadi4ef8db72017-10-31 00:08:42480 overscroll_delta_x_, cap.value() + start_threshold);
Mohsen Izadia0091532017-07-06 04:21:05481 break;
482 case OVERSCROLL_NORTH:
483 case OVERSCROLL_SOUTH:
484 overscroll_delta_y_ = ClampAbsoluteValue(
Mohsen Izadi4ef8db72017-10-31 00:08:42485 overscroll_delta_y_, cap.value() + start_threshold);
Mohsen Izadia0091532017-07-06 04:21:05486 break;
487 case OVERSCROLL_NONE:
488 break;
489 }
490 }
491 }
492
[email protected]a23d04af2013-02-19 17:41:10493 // Compute the current overscroll direction. If the direction is different
494 // from the current direction, then always switch to no-overscroll mode first
495 // to make sure that subsequent scroll events go through to the page first.
496 OverscrollMode new_mode = OVERSCROLL_NONE;
[email protected]14749ddb2013-02-23 06:56:10497 const float kMinRatio = 2.5;
Mohsen Izadi4ef8db72017-10-31 00:08:42498 if (fabs(overscroll_delta_x_) > start_threshold &&
[email protected]fa6db252013-07-26 03:38:20499 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10500 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
Mohsen Izadi4ef8db72017-10-31 00:08:42501 else if (fabs(overscroll_delta_y_) > start_threshold &&
[email protected]fa6db252013-07-26 03:38:20502 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10503 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
504
chaopengcd44c7622018-04-13 04:08:21505 // The horizontal overscroll is used for history navigation. Enable it for
506 // touchpad only if TouchpadOverscrollHistoryNavigation is enabled.
507 if ((new_mode == OVERSCROLL_EAST || new_mode == OVERSCROLL_WEST) &&
508 is_touchpad &&
509 !OverscrollConfig::TouchpadOverscrollHistoryNavigationEnabled()) {
510 new_mode = OVERSCROLL_NONE;
511 }
512
Mohsen Izadi93faac12017-07-29 04:45:15513 // The vertical overscroll is used for pull-to-refresh. Enable it only if
514 // pull-to-refresh is enabled.
Mohsen Izadi8c59ba52018-04-12 18:52:01515 if (new_mode == OVERSCROLL_SOUTH || new_mode == OVERSCROLL_NORTH) {
516 auto ptr_mode = OverscrollConfig::GetPullToRefreshMode();
517 if (ptr_mode == OverscrollConfig::PullToRefreshMode::kDisabled ||
518 (ptr_mode ==
519 OverscrollConfig::PullToRefreshMode::kEnabledTouchschreen &&
Mohsen Izadi5a6583512018-04-23 18:31:19520 is_touchpad) ||
Daniel Cheng224569ee2018-04-25 05:45:06521 time_since_last_ignored_scroll_ < kPullToRefreshCoolOffDelay) {
Mohsen Izadi5a6583512018-04-23 18:31:19522 overscroll_ignored_ = true;
Mohsen Izadi8c59ba52018-04-12 18:52:01523 new_mode = OVERSCROLL_NONE;
524 }
525 }
[email protected]3aad43b2013-11-05 20:20:18526
mfomitchev09f0d64a2017-03-02 19:40:07527 if (overscroll_mode_ == OVERSCROLL_NONE) {
528 SetOverscrollMode(new_mode, is_touchpad ? OverscrollSource::TOUCHPAD
529 : OverscrollSource::TOUCHSCREEN);
530 } else if (new_mode != overscroll_mode_) {
531 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
532 }
[email protected]fa6db252013-07-26 03:38:20533
534 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]bfab3d02014-08-20 03:16:55535 return false;
[email protected]b4df9df2012-11-16 01:58:58536
Mohsen Izadi8f43c2c52018-10-09 17:17:11537 overscroll_ignored_ = false;
538
[email protected]b4df9df2012-11-16 01:58:58539 // Tell the delegate about the overscroll update so that it can update
540 // the display accordingly (e.g. show history preview etc.).
[email protected]9f4f47e2012-11-20 02:21:43541 if (delegate_) {
542 // Do not include the threshold amount when sending the deltas to the
543 // delegate.
544 float delegate_delta_x = overscroll_delta_x_;
Mohsen Izadi4ef8db72017-10-31 00:08:42545 if (fabs(delegate_delta_x) > start_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43546 if (delegate_delta_x < 0)
Mohsen Izadi4ef8db72017-10-31 00:08:42547 delegate_delta_x += start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43548 else
Mohsen Izadi4ef8db72017-10-31 00:08:42549 delegate_delta_x -= start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43550 } else {
551 delegate_delta_x = 0.f;
552 }
553
554 float delegate_delta_y = overscroll_delta_y_;
Mohsen Izadi4ef8db72017-10-31 00:08:42555 if (fabs(delegate_delta_y) > start_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43556 if (delegate_delta_y < 0)
Mohsen Izadi4ef8db72017-10-31 00:08:42557 delegate_delta_y += start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43558 else
Mohsen Izadi4ef8db72017-10-31 00:08:42559 delegate_delta_y -= start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43560 } else {
561 delegate_delta_y = 0.f;
562 }
[email protected]bfab3d02014-08-20 03:16:55563 return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
[email protected]9f4f47e2012-11-20 02:21:43564 }
[email protected]bfab3d02014-08-20 03:16:55565 return false;
[email protected]b4df9df2012-11-16 01:58:58566}
567
568void OverscrollController::CompleteAction() {
chaopeng1d469b02018-04-05 15:41:07569 ignore_following_inertial_events_ = true;
[email protected]b4df9df2012-11-16 01:58:58570 if (delegate_)
571 delegate_->OnOverscrollComplete(overscroll_mode_);
Mohsen Izadif589f4b2017-08-24 02:55:48572 Reset();
[email protected]b4df9df2012-11-16 01:58:58573}
574
mfomitchev09f0d64a2017-03-02 19:40:07575void OverscrollController::SetOverscrollMode(OverscrollMode mode,
576 OverscrollSource source) {
[email protected]b4df9df2012-11-16 01:58:58577 if (overscroll_mode_ == mode)
578 return;
mfomitchev09f0d64a2017-03-02 19:40:07579
580 // If the mode changes to NONE, source is also NONE.
581 DCHECK(mode != OVERSCROLL_NONE || source == OverscrollSource::NONE);
582
Mohsen Izadif589f4b2017-08-24 02:55:48583 // When setting to a non-NONE mode and there is a locked mode, don't set the
584 // mode if the new mode is not the same as the locked mode.
585 if (mode != OVERSCROLL_NONE && locked_mode_ != OVERSCROLL_NONE &&
586 mode != locked_mode_) {
587 return;
588 }
589
[email protected]b4df9df2012-11-16 01:58:58590 OverscrollMode old_mode = overscroll_mode_;
591 overscroll_mode_ = mode;
Mohsen Izadi38795d5d2017-06-30 17:10:48592 overscroll_source_ = source;
Mohsen Izadif589f4b2017-08-24 02:55:48593 if (overscroll_mode_ == OVERSCROLL_NONE) {
[email protected]b4df9df2012-11-16 01:58:58594 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadif589f4b2017-08-24 02:55:48595 } else {
Mohsen Izadi9830436c2017-12-21 18:02:46596 scroll_state_ = ScrollState::OVERSCROLLING;
Mohsen Izadif589f4b2017-08-24 02:55:48597 locked_mode_ = overscroll_mode_;
598 }
Mohsen Izadi80189412018-03-22 17:10:18599 if (delegate_) {
600 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_, source,
601 behavior_);
602 }
[email protected]b4df9df2012-11-16 01:58:58603}
604
Mohsen Izadi50180f392017-09-08 19:29:12605void OverscrollController::ResetScrollState() {
Mohsen Izadi9830436c2017-12-21 18:02:46606 scroll_state_ = ScrollState::NONE;
Mohsen Izadi50180f392017-09-08 19:29:12607 locked_mode_ = OVERSCROLL_NONE;
608}
609
[email protected]b4df9df2012-11-16 01:58:58610} // namespace content