blob: 01ed3e103e7c9edb7fa3ff5b61d93f0b4e4dbc33 [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) {
Dave Tapuska347d60a2020-04-21 23:55:4738 if (event.GetType() != blink::WebInputEvent::Type::kGestureScrollUpdate)
chaopeng9736c522018-05-08 18:32:2739 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()) {
Dave Tapuska347d60a2020-04-21 23:55:4761 case blink::WebInputEvent::Type::kGestureScrollBegin:
62 case blink::WebInputEvent::Type::kGestureScrollUpdate:
63 case blink::WebInputEvent::Type::kGestureScrollEnd: {
dtapuska8c4dae12017-01-13 00:23:0664 const blink::WebGestureEvent& gesture =
65 static_cast<const blink::WebGestureEvent&>(event);
mcnee19fd2492017-06-01 14:42:4366
Sahel Sharifybdaa29e2018-09-17 17:42:2567 // Gesture events with Autoscroll source don't cause overscrolling.
68 if (IsGestureEventFromAutoscroll(gesture))
69 return false;
70
Mohsen Izadiffcbc61f12020-02-09 06:31:2771 ui::ScrollGranularity granularity;
Blink Reformat1c4d759e2017-04-09 16:34:5472 switch (event.GetType()) {
Dave Tapuska347d60a2020-04-21 23:55:4773 case blink::WebInputEvent::Type::kGestureScrollBegin:
Daniel Libbya37d447a2019-05-02 20:06:4874 granularity = gesture.data.scroll_begin.delta_hint_units;
dtapuska8c4dae12017-01-13 00:23:0675 break;
Dave Tapuska347d60a2020-04-21 23:55:4776 case blink::WebInputEvent::Type::kGestureScrollUpdate:
Daniel Libbya37d447a2019-05-02 20:06:4877 granularity = gesture.data.scroll_update.delta_units;
dtapuska8c4dae12017-01-13 00:23:0678 break;
Dave Tapuska347d60a2020-04-21 23:55:4779 case blink::WebInputEvent::Type::kGestureScrollEnd:
Daniel Libbya37d447a2019-05-02 20:06:4880 granularity = gesture.data.scroll_end.delta_units;
dtapuska8c4dae12017-01-13 00:23:0681 break;
82 default:
Mohsen Izadiffcbc61f12020-02-09 06:31:2783 granularity = ui::ScrollGranularity::kScrollByPixel;
dtapuska8c4dae12017-01-13 00:23:0684 break;
dtapuska19ebfa512016-02-19 22:27:4085 }
dtapuska8c4dae12017-01-13 00:23:0686
Mohsen Izadiffcbc61f12020-02-09 06:31:2787 return granularity == ui::ScrollGranularity::kScrollByPrecisePixel;
dtapuska19ebfa512016-02-19 22:27:4088 }
dtapuska8c4dae12017-01-13 00:23:0689 default:
90 break;
91 }
dtapuska19ebfa512016-02-19 22:27:4092 return true;
93}
94
chaopenga7585d792018-04-10 18:02:3895bool OverscrollController::ShouldIgnoreInertialEvent(
96 const blink::WebInputEvent& event) const {
chaopeng9736c522018-05-08 18:32:2797 return ignore_following_inertial_events_ &&
98 IsGestureScrollUpdateInertialEvent(event);
chaopenga7585d792018-04-10 18:02:3899 }
100
[email protected]277857a2014-06-03 10:38:01101bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
dtapuska19ebfa512016-02-19 22:27:40102 if (!ShouldProcessEvent(event))
103 return false;
104
Mohsen Izadi9830436c2017-12-21 18:02:46105 // TODO(mohsen): Consider filtering mouse-wheel events during overscroll. See
106 // https://crbug.com/772106.
Dave Tapuska347d60a2020-04-21 23:55:47107 if (event.GetType() == blink::WebInputEvent::Type::kMouseWheel)
Mohsen Izadi9830436c2017-12-21 18:02:46108 return false;
109
Dave Tapuska347d60a2020-04-21 23:55:47110 if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollBegin) {
Mohsen Izadi5a6583512018-04-23 18:31:19111 ignore_following_inertial_events_ = false;
chaopeng9736c522018-05-08 18:32:27112 first_inertial_event_time_.reset();
Mohsen Izadi5a6583512018-04-23 18:31:19113 time_since_last_ignored_scroll_ =
Daniel Cheng224569ee2018-04-25 05:45:06114 event.TimeStamp() - last_ignored_scroll_time_;
Mohsen Izadi5a6583512018-04-23 18:31:19115 // Will handle events when processing ACKs to ensure the correct order.
116 return false;
117 }
118
Dave Tapuska347d60a2020-04-21 23:55:47119 if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollEnd) {
Mohsen Izadi5a6583512018-04-23 18:31:19120 if (scroll_state_ == ScrollState::CONTENT_CONSUMING ||
121 overscroll_ignored_) {
Daniel Cheng224569ee2018-04-25 05:45:06122 last_ignored_scroll_time_ = event.TimeStamp();
Mohsen Izadi5a6583512018-04-23 18:31:19123 }
Chong Zhang064c2052017-08-29 16:05:31124 // Will handle events when processing ACKs to ensure the correct order.
125 return false;
126 }
127
chaopeng1d469b02018-04-05 15:41:07128 // Consume the scroll-update events if they are from a inertial scroll (fling)
129 // event that completed an overscroll gesture.
chaopenga7585d792018-04-10 18:02:38130 if (ShouldIgnoreInertialEvent(event))
131 return true;
chaopeng1d469b02018-04-05 15:41:07132
mfomitchev8a785bef2015-04-13 18:35:05133 bool reset_scroll_state = false;
Mohsen Izadi9830436c2017-12-21 18:02:46134 if (scroll_state_ != ScrollState::NONE || overscroll_delta_x_ ||
135 overscroll_delta_y_) {
Blink Reformat1c4d759e2017-04-09 16:34:54136 switch (event.GetType()) {
Dave Tapuska347d60a2020-04-21 23:55:47137 case blink::WebInputEvent::Type::kGestureFlingStart:
mfomitchev8a785bef2015-04-13 18:35:05138 reset_scroll_state = true;
[email protected]b31beeb2013-06-17 15:54:27139 break;
140
[email protected]b31beeb2013-06-17 15:54:27141 default:
Blink Reformat1c4d759e2017-04-09 16:34:54142 if (blink::WebInputEvent::IsMouseEventType(event.GetType()) ||
143 blink::WebInputEvent::IsKeyboardEventType(event.GetType())) {
mfomitchev8a785bef2015-04-13 18:35:05144 reset_scroll_state = true;
[email protected]b31beeb2013-06-17 15:54:27145 }
146 break;
[email protected]a4141642013-05-23 04:10:58147 }
148 }
149
Mohsen Izadi50180f392017-09-08 19:29:12150 if (reset_scroll_state)
151 ResetScrollState();
mfomitchev8a785bef2015-04-13 18:35:05152
[email protected]b4df9df2012-11-16 01:58:58153 if (DispatchEventCompletesAction(event)) {
154 CompleteAction();
[email protected]9f4f47e2012-11-20 02:21:43155
[email protected]277857a2014-06-03 10:38:01156 // Let the event be dispatched to the renderer.
157 return false;
[email protected]b4df9df2012-11-16 01:58:58158 }
159
160 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
mfomitchev09f0d64a2017-03-02 19:40:07161 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]b4df9df2012-11-16 01:58:58162
163 // Let the event be dispatched to the renderer.
[email protected]277857a2014-06-03 10:38:01164 return false;
[email protected]b4df9df2012-11-16 01:58:58165 }
166
167 if (overscroll_mode_ != OVERSCROLL_NONE) {
[email protected]2422e5c2013-09-13 19:05:02168 // Consume the event only if it updates the overscroll state.
169 if (ProcessEventForOverscroll(event))
[email protected]277857a2014-06-03 10:38:01170 return true;
mfomitchev8a785bef2015-04-13 18:35:05171 } else if (reset_scroll_state) {
172 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]b4df9df2012-11-16 01:58:58173 }
174
Mohsen Izadi50180f392017-09-08 19:29:12175 // In overscrolling state, consume scroll-update and fling-start events when
176 // they do not contribute to overscroll in order to prevent content scroll.
Mohsen Izadi9830436c2017-12-21 18:02:46177 return scroll_state_ == ScrollState::OVERSCROLLING &&
Dave Tapuska347d60a2020-04-21 23:55:47178 (event.GetType() == blink::WebInputEvent::Type::kGestureScrollUpdate ||
179 event.GetType() == blink::WebInputEvent::Type::kGestureFlingStart);
[email protected]b4df9df2012-11-16 01:58:58180}
181
Sandra Suncbd6da42018-01-05 15:29:36182void OverscrollController::OnDidOverscroll(
183 const ui::DidOverscrollParams& params) {
184 // TODO(sunyunjia): We should also decide whether to trigger overscroll,
185 // update scroll_state_ here. See https://crbug.com/799467.
Mohsen Izadi80189412018-03-22 17:10:18186 behavior_ = params.overscroll_behavior;
Sandra Suncbd6da42018-01-05 15:29:36187}
188
[email protected]180ef242013-11-07 06:50:46189void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event,
[email protected]b4df9df2012-11-16 01:58:58190 bool processed) {
dtapuska19ebfa512016-02-19 22:27:40191 if (!ShouldProcessEvent(event))
192 return;
193
chaopenga7585d792018-04-10 18:02:38194 // An inertial scroll (fling) event from a completed overscroll gesture
195 // should not modify states below.
196 if (ShouldIgnoreInertialEvent(event))
197 return;
198
[email protected]a4141642013-05-23 04:10:58199 if (processed) {
200 // If a scroll event is consumed by the page, i.e. some content on the page
201 // has been scrolled, then there is not going to be an overscroll gesture,
202 // until the current scroll ends, and a new scroll gesture starts.
Mohsen Izadi9830436c2017-12-21 18:02:46203 // Similarly, if a mouse-wheel event is consumed, probably the page has
204 // implemented its own scroll-like behavior and no overscroll should happen.
205 if (scroll_state_ == ScrollState::NONE &&
Dave Tapuska347d60a2020-04-21 23:55:47206 (event.GetType() == blink::WebInputEvent::Type::kGestureScrollUpdate ||
207 event.GetType() == blink::WebInputEvent::Type::kMouseWheel)) {
Mohsen Izadi9830436c2017-12-21 18:02:46208 scroll_state_ = ScrollState::CONTENT_CONSUMING;
[email protected]a4141642013-05-23 04:10:58209 }
Mohsen Izadi50180f392017-09-08 19:29:12210 // In overscrolling state, only return if we are in an overscroll mode;
211 // otherwise, we would want to ProcessEventForOverscroll to let it start a
212 // new overscroll mode.
Mohsen Izadi9830436c2017-12-21 18:02:46213 if (scroll_state_ != ScrollState::OVERSCROLLING ||
Mohsen Izadi50180f392017-09-08 19:29:12214 overscroll_mode_ != OVERSCROLL_NONE) {
215 return;
216 }
[email protected]a4141642013-05-23 04:10:58217 }
Mohsen Izadi9830436c2017-12-21 18:02:46218
Dave Tapuska347d60a2020-04-21 23:55:47219 if (event.GetType() == blink::WebInputEvent::Type::kMouseWheel)
Mohsen Izadi9830436c2017-12-21 18:02:46220 return;
221
[email protected]b4df9df2012-11-16 01:58:58222 ProcessEventForOverscroll(event);
223}
224
[email protected]b4df9df2012-11-16 01:58:58225void OverscrollController::Reset() {
226 overscroll_mode_ = OVERSCROLL_NONE;
Mohsen Izadi38795d5d2017-06-30 17:10:48227 overscroll_source_ = OverscrollSource::NONE;
[email protected]b4df9df2012-11-16 01:58:58228 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadi50180f392017-09-08 19:29:12229 ResetScrollState();
[email protected]b4df9df2012-11-16 01:58:58230}
231
[email protected]63ad73832013-06-17 15:41:04232void OverscrollController::Cancel() {
mfomitchev09f0d64a2017-03-02 19:40:07233 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]63ad73832013-06-17 15:41:04234 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadi50180f392017-09-08 19:29:12235 ResetScrollState();
[email protected]63ad73832013-06-17 15:41:04236}
237
chaopeng1d469b02018-04-05 15:41:07238bool OverscrollController::DispatchEventCompletesAction(
[email protected]180ef242013-11-07 06:50:46239 const blink::WebInputEvent& event) const {
[email protected]b4df9df2012-11-16 01:58:58240 if (overscroll_mode_ == OVERSCROLL_NONE)
241 return false;
Mohsen Izadi4ef8db72017-10-31 00:08:42242 DCHECK_NE(OverscrollSource::NONE, overscroll_source_);
[email protected]b4df9df2012-11-16 01:58:58243
244 // Complete the overscroll gesture if there was a mouse move or a scroll-end
245 // after the threshold.
Dave Tapuska347d60a2020-04-21 23:55:47246 if (event.GetType() != blink::WebInputEvent::Type::kMouseMove &&
247 event.GetType() != blink::WebInputEvent::Type::kGestureScrollEnd &&
248 event.GetType() != blink::WebInputEvent::Type::kGestureFlingStart &&
249 event.GetType() != blink::WebInputEvent::Type::kGestureScrollUpdate)
[email protected]b4df9df2012-11-16 01:58:58250 return false;
251
chaopeng1d469b02018-04-05 15:41:07252 // Complete the overscroll gesture for inertial scroll (fling) event from
253 // touchpad.
Dave Tapuska347d60a2020-04-21 23:55:47254 if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollUpdate) {
chaopeng1d469b02018-04-05 15:41:07255 if (overscroll_source_ != OverscrollSource::TOUCHPAD)
256 return false;
257 DCHECK(IsGestureEventFromTouchpad(event));
258 const blink::WebGestureEvent gesture_event =
259 static_cast<const blink::WebGestureEvent&>(event);
260 if (gesture_event.data.scroll_update.inertial_phase !=
Daniel Libby6ce6b95a2019-05-10 17:06:26261 blink::WebGestureEvent::InertialPhaseState::kMomentum)
chaopeng1d469b02018-04-05 15:41:07262 return false;
263 }
264
Dave Tapuska347d60a2020-04-21 23:55:47265 if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollEnd &&
Mohsen Izadi4ef8db72017-10-31 00:08:42266 overscroll_source_ == OverscrollSource::TOUCHPAD) {
267 DCHECK(IsGestureEventFromTouchpad(event));
Sahel Sharifya29ee2112017-12-15 23:22:54268 // Complete the action for a GSE with touchpad source only when it is in
269 // momentumPhase.
270 const blink::WebGestureEvent gesture_event =
271 static_cast<const blink::WebGestureEvent&>(event);
272 if (gesture_event.data.scroll_end.inertial_phase !=
Daniel Libby6ce6b95a2019-05-10 17:06:26273 blink::WebGestureEvent::InertialPhaseState::kMomentum)
Sahel Sharifya29ee2112017-12-15 23:22:54274 return false;
Mohsen Izadi38795d5d2017-06-30 17:10:48275 }
dtapuska19ebfa512016-02-19 22:27:40276
[email protected]0da15c2d2013-10-30 16:47:20277 if (!delegate_)
278 return false;
279
Dave Tapuska347d60a2020-04-21 23:55:47280 if (event.GetType() == blink::WebInputEvent::Type::kGestureFlingStart) {
[email protected]6658c6a2013-01-23 23:42:22281 // Check to see if the fling is in the same direction of the overscroll.
[email protected]180ef242013-11-07 06:50:46282 const blink::WebGestureEvent gesture =
283 static_cast<const blink::WebGestureEvent&>(event);
[email protected]6658c6a2013-01-23 23:42:22284 switch (overscroll_mode_) {
285 case OVERSCROLL_EAST:
Blink Reformat1c4d759e2017-04-09 16:34:54286 if (gesture.data.fling_start.velocity_x < 0)
[email protected]6658c6a2013-01-23 23:42:22287 return false;
288 break;
289 case OVERSCROLL_WEST:
Blink Reformat1c4d759e2017-04-09 16:34:54290 if (gesture.data.fling_start.velocity_x > 0)
[email protected]6658c6a2013-01-23 23:42:22291 return false;
292 break;
293 case OVERSCROLL_NORTH:
Blink Reformat1c4d759e2017-04-09 16:34:54294 if (gesture.data.fling_start.velocity_y > 0)
[email protected]6658c6a2013-01-23 23:42:22295 return false;
296 break;
297 case OVERSCROLL_SOUTH:
Blink Reformat1c4d759e2017-04-09 16:34:54298 if (gesture.data.fling_start.velocity_y < 0)
[email protected]6658c6a2013-01-23 23:42:22299 return false;
300 break;
301 case OVERSCROLL_NONE:
[email protected]6658c6a2013-01-23 23:42:22302 NOTREACHED();
303 }
304 }
305
Mohsen Izadi4bd624cc2017-07-21 19:23:51306 const gfx::Size size = delegate_->GetDisplaySize();
Mohsen Izadi38795d5d2017-06-30 17:10:48307 if (size.IsEmpty())
308 return false;
309
Mohsen Izadi4ef8db72017-10-31 00:08:42310 const float delta =
311 overscroll_mode_ == OVERSCROLL_WEST || overscroll_mode_ == OVERSCROLL_EAST
312 ? overscroll_delta_x_
313 : overscroll_delta_y_;
314 const float ratio = fabs(delta) / std::max(size.width(), size.height());
Elly Fong-Jones47960972019-05-16 18:40:59315 const float threshold =
Mohsen Izadi4ef8db72017-10-31 00:08:42316 overscroll_source_ == OverscrollSource::TOUCHPAD
Elly Fong-Jones47960972019-05-16 18:40:59317 ? OverscrollConfig::kCompleteTouchpadThresholdPercent
318 : OverscrollConfig::kCompleteTouchscreenThresholdPercent;
[email protected]b4df9df2012-11-16 01:58:58319 return ratio >= threshold;
320}
321
322bool OverscrollController::DispatchEventResetsState(
[email protected]180ef242013-11-07 06:50:46323 const blink::WebInputEvent& event) const {
Blink Reformat1c4d759e2017-04-09 16:34:54324 switch (event.GetType()) {
Chong Zhang064c2052017-08-29 16:05:31325 // GestureScrollBegin/End ACK will reset overscroll state when necessary.
Dave Tapuska347d60a2020-04-21 23:55:47326 case blink::WebInputEvent::Type::kGestureScrollBegin:
327 case blink::WebInputEvent::Type::kGestureScrollEnd:
328 case blink::WebInputEvent::Type::kGestureScrollUpdate:
329 case blink::WebInputEvent::Type::kGestureFlingCancel:
[email protected]b4df9df2012-11-16 01:58:58330 return false;
331
332 default:
[email protected]c4ddb5c42012-11-17 01:09:57333 // Touch events can arrive during an overscroll gesture initiated by
334 // touch-scrolling. These events should not reset the overscroll state.
Blink Reformat1c4d759e2017-04-09 16:34:54335 return !blink::WebInputEvent::IsTouchEventType(event.GetType());
[email protected]b4df9df2012-11-16 01:58:58336 }
337}
338
[email protected]2422e5c2013-09-13 19:05:02339bool OverscrollController::ProcessEventForOverscroll(
[email protected]180ef242013-11-07 06:50:46340 const blink::WebInputEvent& event) {
[email protected]2422e5c2013-09-13 19:05:02341 bool event_processed = false;
Blink Reformat1c4d759e2017-04-09 16:34:54342 switch (event.GetType()) {
Dave Tapuska347d60a2020-04-21 23:55:47343 case blink::WebInputEvent::Type::kGestureScrollBegin: {
Sahel Sharifye6d81f472018-07-11 20:40:26344 if (overscroll_mode_ != OVERSCROLL_NONE)
Chong Zhang064c2052017-08-29 16:05:31345 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
Chong Zhang064c2052017-08-29 16:05:31346 break;
347 }
Dave Tapuska347d60a2020-04-21 23:55:47348 case blink::WebInputEvent::Type::kGestureScrollEnd: {
Sahel Sharifya29ee2112017-12-15 23:22:54349 // Only reset the state on GestureScrollEnd generated from the touchpad
350 // when the scrolling is in inertial state.
351 const blink::WebGestureEvent gesture_event =
352 static_cast<const blink::WebGestureEvent&>(event);
Daniel Libby6ce6b95a2019-05-10 17:06:26353 bool reset_scroll_state =
354 !IsGestureEventFromTouchpad(event) ||
355 (gesture_event.data.scroll_end.inertial_phase ==
356 blink::WebGestureEvent::InertialPhaseState::kMomentum);
Chong Zhang064c2052017-08-29 16:05:31357
358 if (reset_scroll_state)
Mohsen Izadi50180f392017-09-08 19:29:12359 ResetScrollState();
Chong Zhang064c2052017-08-29 16:05:31360
361 if (DispatchEventCompletesAction(event)) {
362 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02363 break;
Chong Zhang064c2052017-08-29 16:05:31364 }
365
366 if (!reset_scroll_state)
367 break;
368
369 if (overscroll_mode_ != OVERSCROLL_NONE) {
370 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
371 } else {
372 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
373 }
[email protected]b4df9df2012-11-16 01:58:58374 break;
375 }
Dave Tapuska347d60a2020-04-21 23:55:47376 case blink::WebInputEvent::Type::kGestureScrollUpdate: {
[email protected]180ef242013-11-07 06:50:46377 const blink::WebGestureEvent& gesture =
378 static_cast<const blink::WebGestureEvent&>(event);
chaopeng9736c522018-05-08 18:32:27379 bool is_gesture_scroll_update_inertial_event =
380 IsGestureScrollUpdateInertialEvent(event);
dtapuska19ebfa512016-02-19 22:27:40381 event_processed = ProcessOverscroll(
Blink Reformat1c4d759e2017-04-09 16:34:54382 gesture.data.scroll_update.delta_x,
383 gesture.data.scroll_update.delta_y,
Daniel Cheng7f9ec902019-04-18 05:07:00384 gesture.SourceDevice() == blink::WebGestureDevice::kTouchpad,
chaopeng9736c522018-05-08 18:32:27385 is_gesture_scroll_update_inertial_event);
386 if (is_gesture_scroll_update_inertial_event) {
387 // Record the timestamp of first inertial event.
388 if (!first_inertial_event_time_) {
389 first_inertial_event_time_ = event.TimeStamp();
390 break;
391 }
392 base::TimeDelta inertial_event_interval =
393 event.TimeStamp() - first_inertial_event_time_.value();
394 if (inertial_event_interval >=
395 OverscrollConfig::MaxInertialEventsBeforeOverscrollCancellation()) {
396 ignore_following_inertial_events_ = true;
397 // Reset overscroll state if fling didn't complete the overscroll
398 // gesture within the first 20 inertial events.
399 Cancel();
400 }
401 }
[email protected]b4df9df2012-11-16 01:58:58402 break;
403 }
Dave Tapuska347d60a2020-04-21 23:55:47404 case blink::WebInputEvent::Type::kGestureFlingStart: {
[email protected]b4df9df2012-11-16 01:58:58405 const float kFlingVelocityThreshold = 1100.f;
[email protected]180ef242013-11-07 06:50:46406 const blink::WebGestureEvent& gesture =
407 static_cast<const blink::WebGestureEvent&>(event);
Blink Reformat1c4d759e2017-04-09 16:34:54408 float velocity_x = gesture.data.fling_start.velocity_x;
409 float velocity_y = gesture.data.fling_start.velocity_y;
[email protected]b4df9df2012-11-16 01:58:58410 if (fabs(velocity_x) > kFlingVelocityThreshold) {
411 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
412 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
413 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02414 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58415 break;
416 }
417 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
418 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
419 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
420 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02421 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58422 break;
423 }
424 }
425
426 // Reset overscroll state if fling didn't complete the overscroll gesture.
mfomitchev09f0d64a2017-03-02 19:40:07427 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]b4df9df2012-11-16 01:58:58428 break;
429 }
430
431 default:
Blink Reformat1c4d759e2017-04-09 16:34:54432 DCHECK(blink::WebInputEvent::IsGestureEventType(event.GetType()) ||
433 blink::WebInputEvent::IsTouchEventType(event.GetType()))
434 << "Received unexpected event: " << event.GetType();
[email protected]b4df9df2012-11-16 01:58:58435 }
[email protected]2422e5c2013-09-13 19:05:02436 return event_processed;
[email protected]b4df9df2012-11-16 01:58:58437}
438
[email protected]bfab3d02014-08-20 03:16:55439bool OverscrollController::ProcessOverscroll(float delta_x,
[email protected]a7f99f02013-08-29 14:15:19440 float delta_y,
chaopeng9736c522018-05-08 18:32:27441 bool is_touchpad,
442 bool is_inertial) {
Mohsen Izadi9830436c2017-12-21 18:02:46443 if (scroll_state_ == ScrollState::CONTENT_CONSUMING)
444 return false;
445
chaopeng9736c522018-05-08 18:32:27446 // Do not start overscroll for inertial events.
447 if (overscroll_mode_ == OVERSCROLL_NONE && is_inertial)
448 return false;
449
Mohsen Izadi9830436c2017-12-21 18:02:46450 overscroll_delta_x_ += delta_x;
451 overscroll_delta_y_ += delta_y;
[email protected]b4df9df2012-11-16 01:58:58452
Elly Fong-Jones47960972019-05-16 18:40:59453 const float start_threshold =
454 is_touchpad ? OverscrollConfig::kStartTouchpadThresholdDips
455 : OverscrollConfig::kStartTouchscreenThresholdDips;
Mohsen Izadi4ef8db72017-10-31 00:08:42456 if (fabs(overscroll_delta_x_) <= start_threshold &&
457 fabs(overscroll_delta_y_) <= start_threshold) {
mfomitchev09f0d64a2017-03-02 19:40:07458 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]bfab3d02014-08-20 03:16:55459 return true;
[email protected]b4df9df2012-11-16 01:58:58460 }
461
Mohsen Izadia0091532017-07-06 04:21:05462 if (delegate_) {
463 base::Optional<float> cap = delegate_->GetMaxOverscrollDelta();
464 if (cap) {
Mohsen Izadia652de4c2017-07-22 02:30:32465 DCHECK_LE(0.f, cap.value());
Mohsen Izadia0091532017-07-06 04:21:05466 switch (overscroll_mode_) {
467 case OVERSCROLL_WEST:
468 case OVERSCROLL_EAST:
469 overscroll_delta_x_ = ClampAbsoluteValue(
Mohsen Izadi4ef8db72017-10-31 00:08:42470 overscroll_delta_x_, cap.value() + start_threshold);
Mohsen Izadia0091532017-07-06 04:21:05471 break;
472 case OVERSCROLL_NORTH:
473 case OVERSCROLL_SOUTH:
474 overscroll_delta_y_ = ClampAbsoluteValue(
Mohsen Izadi4ef8db72017-10-31 00:08:42475 overscroll_delta_y_, cap.value() + start_threshold);
Mohsen Izadia0091532017-07-06 04:21:05476 break;
477 case OVERSCROLL_NONE:
478 break;
479 }
480 }
481 }
482
[email protected]a23d04af2013-02-19 17:41:10483 // Compute the current overscroll direction. If the direction is different
484 // from the current direction, then always switch to no-overscroll mode first
485 // to make sure that subsequent scroll events go through to the page first.
486 OverscrollMode new_mode = OVERSCROLL_NONE;
[email protected]14749ddb2013-02-23 06:56:10487 const float kMinRatio = 2.5;
Mohsen Izadi4ef8db72017-10-31 00:08:42488 if (fabs(overscroll_delta_x_) > start_threshold &&
[email protected]fa6db252013-07-26 03:38:20489 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10490 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
Mohsen Izadi4ef8db72017-10-31 00:08:42491 else if (fabs(overscroll_delta_y_) > start_threshold &&
[email protected]fa6db252013-07-26 03:38:20492 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10493 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
494
chaopengcd44c7622018-04-13 04:08:21495 // The horizontal overscroll is used for history navigation. Enable it for
496 // touchpad only if TouchpadOverscrollHistoryNavigation is enabled.
497 if ((new_mode == OVERSCROLL_EAST || new_mode == OVERSCROLL_WEST) &&
498 is_touchpad &&
499 !OverscrollConfig::TouchpadOverscrollHistoryNavigationEnabled()) {
500 new_mode = OVERSCROLL_NONE;
501 }
502
Mohsen Izadi93faac12017-07-29 04:45:15503 // The vertical overscroll is used for pull-to-refresh. Enable it only if
504 // pull-to-refresh is enabled.
Mohsen Izadi8c59ba52018-04-12 18:52:01505 if (new_mode == OVERSCROLL_SOUTH || new_mode == OVERSCROLL_NORTH) {
506 auto ptr_mode = OverscrollConfig::GetPullToRefreshMode();
507 if (ptr_mode == OverscrollConfig::PullToRefreshMode::kDisabled ||
508 (ptr_mode ==
509 OverscrollConfig::PullToRefreshMode::kEnabledTouchschreen &&
Mohsen Izadi5a6583512018-04-23 18:31:19510 is_touchpad) ||
Daniel Cheng224569ee2018-04-25 05:45:06511 time_since_last_ignored_scroll_ < kPullToRefreshCoolOffDelay) {
Mohsen Izadi5a6583512018-04-23 18:31:19512 overscroll_ignored_ = true;
Mohsen Izadi8c59ba52018-04-12 18:52:01513 new_mode = OVERSCROLL_NONE;
514 }
515 }
[email protected]3aad43b2013-11-05 20:20:18516
mfomitchev09f0d64a2017-03-02 19:40:07517 if (overscroll_mode_ == OVERSCROLL_NONE) {
518 SetOverscrollMode(new_mode, is_touchpad ? OverscrollSource::TOUCHPAD
519 : OverscrollSource::TOUCHSCREEN);
520 } else if (new_mode != overscroll_mode_) {
521 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
522 }
[email protected]fa6db252013-07-26 03:38:20523
524 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]bfab3d02014-08-20 03:16:55525 return false;
[email protected]b4df9df2012-11-16 01:58:58526
Mohsen Izadi8f43c2c52018-10-09 17:17:11527 overscroll_ignored_ = false;
528
[email protected]b4df9df2012-11-16 01:58:58529 // Tell the delegate about the overscroll update so that it can update
530 // the display accordingly (e.g. show history preview etc.).
[email protected]9f4f47e2012-11-20 02:21:43531 if (delegate_) {
532 // Do not include the threshold amount when sending the deltas to the
533 // delegate.
534 float delegate_delta_x = overscroll_delta_x_;
Mohsen Izadi4ef8db72017-10-31 00:08:42535 if (fabs(delegate_delta_x) > start_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43536 if (delegate_delta_x < 0)
Mohsen Izadi4ef8db72017-10-31 00:08:42537 delegate_delta_x += start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43538 else
Mohsen Izadi4ef8db72017-10-31 00:08:42539 delegate_delta_x -= start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43540 } else {
541 delegate_delta_x = 0.f;
542 }
543
544 float delegate_delta_y = overscroll_delta_y_;
Mohsen Izadi4ef8db72017-10-31 00:08:42545 if (fabs(delegate_delta_y) > start_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43546 if (delegate_delta_y < 0)
Mohsen Izadi4ef8db72017-10-31 00:08:42547 delegate_delta_y += start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43548 else
Mohsen Izadi4ef8db72017-10-31 00:08:42549 delegate_delta_y -= start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43550 } else {
551 delegate_delta_y = 0.f;
552 }
[email protected]bfab3d02014-08-20 03:16:55553 return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
[email protected]9f4f47e2012-11-20 02:21:43554 }
[email protected]bfab3d02014-08-20 03:16:55555 return false;
[email protected]b4df9df2012-11-16 01:58:58556}
557
558void OverscrollController::CompleteAction() {
chaopeng1d469b02018-04-05 15:41:07559 ignore_following_inertial_events_ = true;
[email protected]b4df9df2012-11-16 01:58:58560 if (delegate_)
561 delegate_->OnOverscrollComplete(overscroll_mode_);
Mohsen Izadif589f4b2017-08-24 02:55:48562 Reset();
[email protected]b4df9df2012-11-16 01:58:58563}
564
mfomitchev09f0d64a2017-03-02 19:40:07565void OverscrollController::SetOverscrollMode(OverscrollMode mode,
566 OverscrollSource source) {
[email protected]b4df9df2012-11-16 01:58:58567 if (overscroll_mode_ == mode)
568 return;
mfomitchev09f0d64a2017-03-02 19:40:07569
570 // If the mode changes to NONE, source is also NONE.
571 DCHECK(mode != OVERSCROLL_NONE || source == OverscrollSource::NONE);
572
Mohsen Izadif589f4b2017-08-24 02:55:48573 // When setting to a non-NONE mode and there is a locked mode, don't set the
574 // mode if the new mode is not the same as the locked mode.
575 if (mode != OVERSCROLL_NONE && locked_mode_ != OVERSCROLL_NONE &&
576 mode != locked_mode_) {
577 return;
578 }
579
[email protected]b4df9df2012-11-16 01:58:58580 OverscrollMode old_mode = overscroll_mode_;
581 overscroll_mode_ = mode;
Mohsen Izadi38795d5d2017-06-30 17:10:48582 overscroll_source_ = source;
Mohsen Izadif589f4b2017-08-24 02:55:48583 if (overscroll_mode_ == OVERSCROLL_NONE) {
[email protected]b4df9df2012-11-16 01:58:58584 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadif589f4b2017-08-24 02:55:48585 } else {
Mohsen Izadi9830436c2017-12-21 18:02:46586 scroll_state_ = ScrollState::OVERSCROLLING;
Mohsen Izadif589f4b2017-08-24 02:55:48587 locked_mode_ = overscroll_mode_;
588 }
Mohsen Izadi80189412018-03-22 17:10:18589 if (delegate_) {
590 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_, source,
591 behavior_);
592 }
[email protected]b4df9df2012-11-16 01:58:58593}
594
Mohsen Izadi50180f392017-09-08 19:29:12595void OverscrollController::ResetScrollState() {
Mohsen Izadi9830436c2017-12-21 18:02:46596 scroll_state_ = ScrollState::NONE;
Mohsen Izadi50180f392017-09-08 19:29:12597 locked_mode_ = OVERSCROLL_NONE;
598}
599
[email protected]b4df9df2012-11-16 01:58:58600} // namespace content