blob: f8b5aaeb7c5c373987b355394f44a16334ed3383 [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"
[email protected]b4df9df2012-11-16 01:58:5811#include "content/browser/renderer_host/overscroll_controller_delegate.h"
[email protected]1cd76c2d82012-11-29 07:36:4712#include "content/public/browser/overscroll_configuration.h"
Sahel Sharifya29ee2112017-12-15 23:22:5413#include "content/public/common/content_features.h"
[email protected]3aad43b2013-11-05 20:20:1814#include "content/public/common/content_switches.h"
[email protected]b4df9df2012-11-16 01:58:5815
Mohsen Izadia0091532017-07-06 04:21:0516namespace content {
17
[email protected]3aad43b2013-11-05 20:20:1818namespace {
19
Daniel Cheng224569ee2018-04-25 05:45:0620// Minimum amount of time after an actual scroll after which a pull-to-refresh
21// can start.
22constexpr base::TimeDelta kPullToRefreshCoolOffDelay =
23 base::TimeDelta::FromMilliseconds(600);
Mohsen Izadi5a6583512018-04-23 18:31:1924
dtapuska19ebfa512016-02-19 22:27:4025bool IsGestureEventFromTouchpad(const blink::WebInputEvent& event) {
Blink Reformat1c4d759e2017-04-09 16:34:5426 DCHECK(blink::WebInputEvent::IsGestureEventType(event.GetType()));
dtapuska19ebfa512016-02-19 22:27:4027 const blink::WebGestureEvent& gesture =
28 static_cast<const blink::WebGestureEvent&>(event);
Daniel Cheng7f9ec902019-04-18 05:07:0029 return gesture.SourceDevice() == blink::WebGestureDevice::kTouchpad;
dtapuska19ebfa512016-02-19 22:27:4030}
31
Sahel Sharifybdaa29e2018-09-17 17:42:2532bool IsGestureEventFromAutoscroll(const blink::WebGestureEvent event) {
Daniel Cheng7f9ec902019-04-18 05:07:0033 return event.SourceDevice() == blink::WebGestureDevice::kSyntheticAutoscroll;
Sahel Sharifybdaa29e2018-09-17 17:42:2534}
35
chaopeng9736c522018-05-08 18:32:2736bool IsGestureScrollUpdateInertialEvent(const blink::WebInputEvent& event) {
37 if (event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
38 return false;
39
40 const blink::WebGestureEvent& gesture =
41 static_cast<const blink::WebGestureEvent&>(event);
42 return gesture.data.scroll_update.inertial_phase ==
Daniel Libby6ce6b95a2019-05-10 17:06:2643 blink::WebGestureEvent::InertialPhaseState::kMomentum;
chaopeng9736c522018-05-08 18:32:2744}
45
Mohsen Izadia0091532017-07-06 04:21:0546float ClampAbsoluteValue(float value, float max_abs) {
47 DCHECK_LT(0.f, max_abs);
48 return std::max(-max_abs, std::min(value, max_abs));
49}
[email protected]3aad43b2013-11-05 20:20:1850
Mohsen Izadia0091532017-07-06 04:21:0551} // namespace
[email protected]b4df9df2012-11-16 01:58:5852
Sahel Sharifye6d81f472018-07-11 20:40:2653OverscrollController::OverscrollController() {}
[email protected]b4df9df2012-11-16 01:58:5854
Mohsen Izadi38795d5d2017-06-30 17:10:4855OverscrollController::~OverscrollController() {}
[email protected]b4df9df2012-11-16 01:58:5856
dtapuska19ebfa512016-02-19 22:27:4057bool OverscrollController::ShouldProcessEvent(
58 const blink::WebInputEvent& event) {
Blink Reformat1c4d759e2017-04-09 16:34:5459 switch (event.GetType()) {
Blink Reformat1c4d759e2017-04-09 16:34:5460 case blink::WebInputEvent::kGestureScrollBegin:
61 case blink::WebInputEvent::kGestureScrollUpdate:
62 case blink::WebInputEvent::kGestureScrollEnd: {
dtapuska8c4dae12017-01-13 00:23:0663 const blink::WebGestureEvent& gesture =
64 static_cast<const blink::WebGestureEvent&>(event);
mcnee19fd2492017-06-01 14:42:4365
66 // GestureScrollBegin and GestureScrollEnd events are created to wrap
67 // individual resent GestureScrollUpdates from a plugin. Hence these
68 // should not be used to indicate the beginning/end of the overscroll.
69 // TODO(mcnee): When we remove BrowserPlugin, delete this code.
70 // See crbug.com/533069
71 if (gesture.resending_plugin_id != -1 &&
72 event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
73 return false;
74
Sahel Sharifybdaa29e2018-09-17 17:42:2575 // Gesture events with Autoscroll source don't cause overscrolling.
76 if (IsGestureEventFromAutoscroll(gesture))
77 return false;
78
Daniel Libbye15b80b2019-05-24 17:18:2479 ui::input_types::ScrollGranularity granularity;
Blink Reformat1c4d759e2017-04-09 16:34:5480 switch (event.GetType()) {
81 case blink::WebInputEvent::kGestureScrollBegin:
Daniel Libbya37d447a2019-05-02 20:06:4882 granularity = gesture.data.scroll_begin.delta_hint_units;
dtapuska8c4dae12017-01-13 00:23:0683 break;
Blink Reformat1c4d759e2017-04-09 16:34:5484 case blink::WebInputEvent::kGestureScrollUpdate:
Daniel Libbya37d447a2019-05-02 20:06:4885 granularity = gesture.data.scroll_update.delta_units;
dtapuska8c4dae12017-01-13 00:23:0686 break;
Blink Reformat1c4d759e2017-04-09 16:34:5487 case blink::WebInputEvent::kGestureScrollEnd:
Daniel Libbya37d447a2019-05-02 20:06:4888 granularity = gesture.data.scroll_end.delta_units;
dtapuska8c4dae12017-01-13 00:23:0689 break;
90 default:
Daniel Libbye15b80b2019-05-24 17:18:2491 granularity = ui::input_types::ScrollGranularity::kScrollByPixel;
dtapuska8c4dae12017-01-13 00:23:0692 break;
dtapuska19ebfa512016-02-19 22:27:4093 }
dtapuska8c4dae12017-01-13 00:23:0694
Daniel Libbye15b80b2019-05-24 17:18:2495 return granularity ==
96 ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
dtapuska19ebfa512016-02-19 22:27:4097 }
dtapuska8c4dae12017-01-13 00:23:0698 default:
99 break;
100 }
dtapuska19ebfa512016-02-19 22:27:40101 return true;
102}
103
chaopenga7585d792018-04-10 18:02:38104bool OverscrollController::ShouldIgnoreInertialEvent(
105 const blink::WebInputEvent& event) const {
chaopeng9736c522018-05-08 18:32:27106 return ignore_following_inertial_events_ &&
107 IsGestureScrollUpdateInertialEvent(event);
chaopenga7585d792018-04-10 18:02:38108 }
109
[email protected]277857a2014-06-03 10:38:01110bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
dtapuska19ebfa512016-02-19 22:27:40111 if (!ShouldProcessEvent(event))
112 return false;
113
Mohsen Izadi9830436c2017-12-21 18:02:46114 // TODO(mohsen): Consider filtering mouse-wheel events during overscroll. See
115 // https://crbug.com/772106.
116 if (event.GetType() == blink::WebInputEvent::kMouseWheel)
117 return false;
118
Mohsen Izadi5a6583512018-04-23 18:31:19119 if (event.GetType() == blink::WebInputEvent::kGestureScrollBegin) {
120 ignore_following_inertial_events_ = false;
chaopeng9736c522018-05-08 18:32:27121 first_inertial_event_time_.reset();
Mohsen Izadi5a6583512018-04-23 18:31:19122 time_since_last_ignored_scroll_ =
Daniel Cheng224569ee2018-04-25 05:45:06123 event.TimeStamp() - last_ignored_scroll_time_;
Mohsen Izadi5a6583512018-04-23 18:31:19124 // Will handle events when processing ACKs to ensure the correct order.
125 return false;
126 }
127
128 if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd) {
129 if (scroll_state_ == ScrollState::CONTENT_CONSUMING ||
130 overscroll_ignored_) {
Daniel Cheng224569ee2018-04-25 05:45:06131 last_ignored_scroll_time_ = event.TimeStamp();
Mohsen Izadi5a6583512018-04-23 18:31:19132 }
Chong Zhang064c2052017-08-29 16:05:31133 // Will handle events when processing ACKs to ensure the correct order.
134 return false;
135 }
136
chaopeng1d469b02018-04-05 15:41:07137 // Consume the scroll-update events if they are from a inertial scroll (fling)
138 // event that completed an overscroll gesture.
chaopenga7585d792018-04-10 18:02:38139 if (ShouldIgnoreInertialEvent(event))
140 return true;
chaopeng1d469b02018-04-05 15:41:07141
mfomitchev8a785bef2015-04-13 18:35:05142 bool reset_scroll_state = false;
Mohsen Izadi9830436c2017-12-21 18:02:46143 if (scroll_state_ != ScrollState::NONE || overscroll_delta_x_ ||
144 overscroll_delta_y_) {
Blink Reformat1c4d759e2017-04-09 16:34:54145 switch (event.GetType()) {
Blink Reformat1c4d759e2017-04-09 16:34:54146 case blink::WebInputEvent::kGestureFlingStart:
mfomitchev8a785bef2015-04-13 18:35:05147 reset_scroll_state = true;
[email protected]b31beeb2013-06-17 15:54:27148 break;
149
[email protected]b31beeb2013-06-17 15:54:27150 default:
Blink Reformat1c4d759e2017-04-09 16:34:54151 if (blink::WebInputEvent::IsMouseEventType(event.GetType()) ||
152 blink::WebInputEvent::IsKeyboardEventType(event.GetType())) {
mfomitchev8a785bef2015-04-13 18:35:05153 reset_scroll_state = true;
[email protected]b31beeb2013-06-17 15:54:27154 }
155 break;
[email protected]a4141642013-05-23 04:10:58156 }
157 }
158
Mohsen Izadi50180f392017-09-08 19:29:12159 if (reset_scroll_state)
160 ResetScrollState();
mfomitchev8a785bef2015-04-13 18:35:05161
[email protected]b4df9df2012-11-16 01:58:58162 if (DispatchEventCompletesAction(event)) {
163 CompleteAction();
[email protected]9f4f47e2012-11-20 02:21:43164
[email protected]277857a2014-06-03 10:38:01165 // Let the event be dispatched to the renderer.
166 return false;
[email protected]b4df9df2012-11-16 01:58:58167 }
168
169 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
mfomitchev09f0d64a2017-03-02 19:40:07170 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]b4df9df2012-11-16 01:58:58171
172 // Let the event be dispatched to the renderer.
[email protected]277857a2014-06-03 10:38:01173 return false;
[email protected]b4df9df2012-11-16 01:58:58174 }
175
176 if (overscroll_mode_ != OVERSCROLL_NONE) {
[email protected]2422e5c2013-09-13 19:05:02177 // Consume the event only if it updates the overscroll state.
178 if (ProcessEventForOverscroll(event))
[email protected]277857a2014-06-03 10:38:01179 return true;
mfomitchev8a785bef2015-04-13 18:35:05180 } else if (reset_scroll_state) {
181 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]b4df9df2012-11-16 01:58:58182 }
183
Mohsen Izadi50180f392017-09-08 19:29:12184 // In overscrolling state, consume scroll-update and fling-start events when
185 // they do not contribute to overscroll in order to prevent content scroll.
Mohsen Izadi9830436c2017-12-21 18:02:46186 return scroll_state_ == ScrollState::OVERSCROLLING &&
Mohsen Izadi50180f392017-09-08 19:29:12187 (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
188 event.GetType() == blink::WebInputEvent::kGestureFlingStart);
[email protected]b4df9df2012-11-16 01:58:58189}
190
Sandra Suncbd6da42018-01-05 15:29:36191void OverscrollController::OnDidOverscroll(
192 const ui::DidOverscrollParams& params) {
193 // TODO(sunyunjia): We should also decide whether to trigger overscroll,
194 // update scroll_state_ here. See https://crbug.com/799467.
Mohsen Izadi80189412018-03-22 17:10:18195 behavior_ = params.overscroll_behavior;
Sandra Suncbd6da42018-01-05 15:29:36196}
197
[email protected]180ef242013-11-07 06:50:46198void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event,
[email protected]b4df9df2012-11-16 01:58:58199 bool processed) {
dtapuska19ebfa512016-02-19 22:27:40200 if (!ShouldProcessEvent(event))
201 return;
202
chaopenga7585d792018-04-10 18:02:38203 // An inertial scroll (fling) event from a completed overscroll gesture
204 // should not modify states below.
205 if (ShouldIgnoreInertialEvent(event))
206 return;
207
[email protected]a4141642013-05-23 04:10:58208 if (processed) {
209 // If a scroll event is consumed by the page, i.e. some content on the page
210 // has been scrolled, then there is not going to be an overscroll gesture,
211 // until the current scroll ends, and a new scroll gesture starts.
Mohsen Izadi9830436c2017-12-21 18:02:46212 // Similarly, if a mouse-wheel event is consumed, probably the page has
213 // implemented its own scroll-like behavior and no overscroll should happen.
214 if (scroll_state_ == ScrollState::NONE &&
215 (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate ||
216 event.GetType() == blink::WebInputEvent::kMouseWheel)) {
217 scroll_state_ = ScrollState::CONTENT_CONSUMING;
[email protected]a4141642013-05-23 04:10:58218 }
Mohsen Izadi50180f392017-09-08 19:29:12219 // In overscrolling state, only return if we are in an overscroll mode;
220 // otherwise, we would want to ProcessEventForOverscroll to let it start a
221 // new overscroll mode.
Mohsen Izadi9830436c2017-12-21 18:02:46222 if (scroll_state_ != ScrollState::OVERSCROLLING ||
Mohsen Izadi50180f392017-09-08 19:29:12223 overscroll_mode_ != OVERSCROLL_NONE) {
224 return;
225 }
[email protected]a4141642013-05-23 04:10:58226 }
Mohsen Izadi9830436c2017-12-21 18:02:46227
228 if (event.GetType() == blink::WebInputEvent::kMouseWheel)
229 return;
230
[email protected]b4df9df2012-11-16 01:58:58231 ProcessEventForOverscroll(event);
232}
233
[email protected]b4df9df2012-11-16 01:58:58234void OverscrollController::Reset() {
235 overscroll_mode_ = OVERSCROLL_NONE;
Mohsen Izadi38795d5d2017-06-30 17:10:48236 overscroll_source_ = OverscrollSource::NONE;
[email protected]b4df9df2012-11-16 01:58:58237 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadi50180f392017-09-08 19:29:12238 ResetScrollState();
[email protected]b4df9df2012-11-16 01:58:58239}
240
[email protected]63ad73832013-06-17 15:41:04241void OverscrollController::Cancel() {
mfomitchev09f0d64a2017-03-02 19:40:07242 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]63ad73832013-06-17 15:41:04243 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadi50180f392017-09-08 19:29:12244 ResetScrollState();
[email protected]63ad73832013-06-17 15:41:04245}
246
chaopeng1d469b02018-04-05 15:41:07247bool OverscrollController::DispatchEventCompletesAction(
[email protected]180ef242013-11-07 06:50:46248 const blink::WebInputEvent& event) const {
[email protected]b4df9df2012-11-16 01:58:58249 if (overscroll_mode_ == OVERSCROLL_NONE)
250 return false;
Mohsen Izadi4ef8db72017-10-31 00:08:42251 DCHECK_NE(OverscrollSource::NONE, overscroll_source_);
[email protected]b4df9df2012-11-16 01:58:58252
253 // Complete the overscroll gesture if there was a mouse move or a scroll-end
254 // after the threshold.
Blink Reformat1c4d759e2017-04-09 16:34:54255 if (event.GetType() != blink::WebInputEvent::kMouseMove &&
256 event.GetType() != blink::WebInputEvent::kGestureScrollEnd &&
chaopeng1d469b02018-04-05 15:41:07257 event.GetType() != blink::WebInputEvent::kGestureFlingStart &&
258 event.GetType() != blink::WebInputEvent::kGestureScrollUpdate)
[email protected]b4df9df2012-11-16 01:58:58259 return false;
260
chaopeng1d469b02018-04-05 15:41:07261 // Complete the overscroll gesture for inertial scroll (fling) event from
262 // touchpad.
263 if (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
264 if (overscroll_source_ != OverscrollSource::TOUCHPAD)
265 return false;
266 DCHECK(IsGestureEventFromTouchpad(event));
267 const blink::WebGestureEvent gesture_event =
268 static_cast<const blink::WebGestureEvent&>(event);
269 if (gesture_event.data.scroll_update.inertial_phase !=
Daniel Libby6ce6b95a2019-05-10 17:06:26270 blink::WebGestureEvent::InertialPhaseState::kMomentum)
chaopeng1d469b02018-04-05 15:41:07271 return false;
272 }
273
Blink Reformat1c4d759e2017-04-09 16:34:54274 if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd &&
Mohsen Izadi4ef8db72017-10-31 00:08:42275 overscroll_source_ == OverscrollSource::TOUCHPAD) {
276 DCHECK(IsGestureEventFromTouchpad(event));
Sahel Sharifya29ee2112017-12-15 23:22:54277 // Complete the action for a GSE with touchpad source only when it is in
278 // momentumPhase.
279 const blink::WebGestureEvent gesture_event =
280 static_cast<const blink::WebGestureEvent&>(event);
281 if (gesture_event.data.scroll_end.inertial_phase !=
Daniel Libby6ce6b95a2019-05-10 17:06:26282 blink::WebGestureEvent::InertialPhaseState::kMomentum)
Sahel Sharifya29ee2112017-12-15 23:22:54283 return false;
Mohsen Izadi38795d5d2017-06-30 17:10:48284 }
dtapuska19ebfa512016-02-19 22:27:40285
[email protected]0da15c2d2013-10-30 16:47:20286 if (!delegate_)
287 return false;
288
Blink Reformat1c4d759e2017-04-09 16:34:54289 if (event.GetType() == blink::WebInputEvent::kGestureFlingStart) {
[email protected]6658c6a2013-01-23 23:42:22290 // Check to see if the fling is in the same direction of the overscroll.
[email protected]180ef242013-11-07 06:50:46291 const blink::WebGestureEvent gesture =
292 static_cast<const blink::WebGestureEvent&>(event);
[email protected]6658c6a2013-01-23 23:42:22293 switch (overscroll_mode_) {
294 case OVERSCROLL_EAST:
Blink Reformat1c4d759e2017-04-09 16:34:54295 if (gesture.data.fling_start.velocity_x < 0)
[email protected]6658c6a2013-01-23 23:42:22296 return false;
297 break;
298 case OVERSCROLL_WEST:
Blink Reformat1c4d759e2017-04-09 16:34:54299 if (gesture.data.fling_start.velocity_x > 0)
[email protected]6658c6a2013-01-23 23:42:22300 return false;
301 break;
302 case OVERSCROLL_NORTH:
Blink Reformat1c4d759e2017-04-09 16:34:54303 if (gesture.data.fling_start.velocity_y > 0)
[email protected]6658c6a2013-01-23 23:42:22304 return false;
305 break;
306 case OVERSCROLL_SOUTH:
Blink Reformat1c4d759e2017-04-09 16:34:54307 if (gesture.data.fling_start.velocity_y < 0)
[email protected]6658c6a2013-01-23 23:42:22308 return false;
309 break;
310 case OVERSCROLL_NONE:
[email protected]6658c6a2013-01-23 23:42:22311 NOTREACHED();
312 }
313 }
314
Mohsen Izadi4bd624cc2017-07-21 19:23:51315 const gfx::Size size = delegate_->GetDisplaySize();
Mohsen Izadi38795d5d2017-06-30 17:10:48316 if (size.IsEmpty())
317 return false;
318
Mohsen Izadi4ef8db72017-10-31 00:08:42319 const float delta =
320 overscroll_mode_ == OVERSCROLL_WEST || overscroll_mode_ == OVERSCROLL_EAST
321 ? overscroll_delta_x_
322 : overscroll_delta_y_;
323 const float ratio = fabs(delta) / std::max(size.width(), size.height());
Elly Fong-Jones47960972019-05-16 18:40:59324 const float threshold =
Mohsen Izadi4ef8db72017-10-31 00:08:42325 overscroll_source_ == OverscrollSource::TOUCHPAD
Elly Fong-Jones47960972019-05-16 18:40:59326 ? OverscrollConfig::kCompleteTouchpadThresholdPercent
327 : OverscrollConfig::kCompleteTouchscreenThresholdPercent;
[email protected]b4df9df2012-11-16 01:58:58328 return ratio >= threshold;
329}
330
331bool OverscrollController::DispatchEventResetsState(
[email protected]180ef242013-11-07 06:50:46332 const blink::WebInputEvent& event) const {
Blink Reformat1c4d759e2017-04-09 16:34:54333 switch (event.GetType()) {
Chong Zhang064c2052017-08-29 16:05:31334 // GestureScrollBegin/End ACK will reset overscroll state when necessary.
Blink Reformat1c4d759e2017-04-09 16:34:54335 case blink::WebInputEvent::kGestureScrollBegin:
336 case blink::WebInputEvent::kGestureScrollEnd:
Blink Reformat1c4d759e2017-04-09 16:34:54337 case blink::WebInputEvent::kGestureScrollUpdate:
338 case blink::WebInputEvent::kGestureFlingCancel:
[email protected]b4df9df2012-11-16 01:58:58339 return false;
340
341 default:
[email protected]c4ddb5c42012-11-17 01:09:57342 // Touch events can arrive during an overscroll gesture initiated by
343 // touch-scrolling. These events should not reset the overscroll state.
Blink Reformat1c4d759e2017-04-09 16:34:54344 return !blink::WebInputEvent::IsTouchEventType(event.GetType());
[email protected]b4df9df2012-11-16 01:58:58345 }
346}
347
[email protected]2422e5c2013-09-13 19:05:02348bool OverscrollController::ProcessEventForOverscroll(
[email protected]180ef242013-11-07 06:50:46349 const blink::WebInputEvent& event) {
[email protected]2422e5c2013-09-13 19:05:02350 bool event_processed = false;
Blink Reformat1c4d759e2017-04-09 16:34:54351 switch (event.GetType()) {
Chong Zhang064c2052017-08-29 16:05:31352 case blink::WebInputEvent::kGestureScrollBegin: {
Sahel Sharifye6d81f472018-07-11 20:40:26353 if (overscroll_mode_ != OVERSCROLL_NONE)
Chong Zhang064c2052017-08-29 16:05:31354 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
Chong Zhang064c2052017-08-29 16:05:31355 break;
356 }
357 case blink::WebInputEvent::kGestureScrollEnd: {
Sahel Sharifya29ee2112017-12-15 23:22:54358 // Only reset the state on GestureScrollEnd generated from the touchpad
359 // when the scrolling is in inertial state.
360 const blink::WebGestureEvent gesture_event =
361 static_cast<const blink::WebGestureEvent&>(event);
Daniel Libby6ce6b95a2019-05-10 17:06:26362 bool reset_scroll_state =
363 !IsGestureEventFromTouchpad(event) ||
364 (gesture_event.data.scroll_end.inertial_phase ==
365 blink::WebGestureEvent::InertialPhaseState::kMomentum);
Chong Zhang064c2052017-08-29 16:05:31366
367 if (reset_scroll_state)
Mohsen Izadi50180f392017-09-08 19:29:12368 ResetScrollState();
Chong Zhang064c2052017-08-29 16:05:31369
370 if (DispatchEventCompletesAction(event)) {
371 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02372 break;
Chong Zhang064c2052017-08-29 16:05:31373 }
374
375 if (!reset_scroll_state)
376 break;
377
378 if (overscroll_mode_ != OVERSCROLL_NONE) {
379 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
380 } else {
381 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
382 }
[email protected]b4df9df2012-11-16 01:58:58383 break;
384 }
Blink Reformat1c4d759e2017-04-09 16:34:54385 case blink::WebInputEvent::kGestureScrollUpdate: {
[email protected]180ef242013-11-07 06:50:46386 const blink::WebGestureEvent& gesture =
387 static_cast<const blink::WebGestureEvent&>(event);
chaopeng9736c522018-05-08 18:32:27388 bool is_gesture_scroll_update_inertial_event =
389 IsGestureScrollUpdateInertialEvent(event);
dtapuska19ebfa512016-02-19 22:27:40390 event_processed = ProcessOverscroll(
Blink Reformat1c4d759e2017-04-09 16:34:54391 gesture.data.scroll_update.delta_x,
392 gesture.data.scroll_update.delta_y,
Daniel Cheng7f9ec902019-04-18 05:07:00393 gesture.SourceDevice() == blink::WebGestureDevice::kTouchpad,
chaopeng9736c522018-05-08 18:32:27394 is_gesture_scroll_update_inertial_event);
395 if (is_gesture_scroll_update_inertial_event) {
396 // Record the timestamp of first inertial event.
397 if (!first_inertial_event_time_) {
398 first_inertial_event_time_ = event.TimeStamp();
399 break;
400 }
401 base::TimeDelta inertial_event_interval =
402 event.TimeStamp() - first_inertial_event_time_.value();
403 if (inertial_event_interval >=
404 OverscrollConfig::MaxInertialEventsBeforeOverscrollCancellation()) {
405 ignore_following_inertial_events_ = true;
406 // Reset overscroll state if fling didn't complete the overscroll
407 // gesture within the first 20 inertial events.
408 Cancel();
409 }
410 }
[email protected]b4df9df2012-11-16 01:58:58411 break;
412 }
Blink Reformat1c4d759e2017-04-09 16:34:54413 case blink::WebInputEvent::kGestureFlingStart: {
[email protected]b4df9df2012-11-16 01:58:58414 const float kFlingVelocityThreshold = 1100.f;
[email protected]180ef242013-11-07 06:50:46415 const blink::WebGestureEvent& gesture =
416 static_cast<const blink::WebGestureEvent&>(event);
Blink Reformat1c4d759e2017-04-09 16:34:54417 float velocity_x = gesture.data.fling_start.velocity_x;
418 float velocity_y = gesture.data.fling_start.velocity_y;
[email protected]b4df9df2012-11-16 01:58:58419 if (fabs(velocity_x) > kFlingVelocityThreshold) {
420 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
421 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
422 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02423 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58424 break;
425 }
426 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
427 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
428 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
429 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02430 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58431 break;
432 }
433 }
434
435 // Reset overscroll state if fling didn't complete the overscroll gesture.
mfomitchev09f0d64a2017-03-02 19:40:07436 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]b4df9df2012-11-16 01:58:58437 break;
438 }
439
440 default:
Blink Reformat1c4d759e2017-04-09 16:34:54441 DCHECK(blink::WebInputEvent::IsGestureEventType(event.GetType()) ||
442 blink::WebInputEvent::IsTouchEventType(event.GetType()))
443 << "Received unexpected event: " << event.GetType();
[email protected]b4df9df2012-11-16 01:58:58444 }
[email protected]2422e5c2013-09-13 19:05:02445 return event_processed;
[email protected]b4df9df2012-11-16 01:58:58446}
447
[email protected]bfab3d02014-08-20 03:16:55448bool OverscrollController::ProcessOverscroll(float delta_x,
[email protected]a7f99f02013-08-29 14:15:19449 float delta_y,
chaopeng9736c522018-05-08 18:32:27450 bool is_touchpad,
451 bool is_inertial) {
Mohsen Izadi9830436c2017-12-21 18:02:46452 if (scroll_state_ == ScrollState::CONTENT_CONSUMING)
453 return false;
454
chaopeng9736c522018-05-08 18:32:27455 // Do not start overscroll for inertial events.
456 if (overscroll_mode_ == OVERSCROLL_NONE && is_inertial)
457 return false;
458
Mohsen Izadi9830436c2017-12-21 18:02:46459 overscroll_delta_x_ += delta_x;
460 overscroll_delta_y_ += delta_y;
[email protected]b4df9df2012-11-16 01:58:58461
Elly Fong-Jones47960972019-05-16 18:40:59462 const float start_threshold =
463 is_touchpad ? OverscrollConfig::kStartTouchpadThresholdDips
464 : OverscrollConfig::kStartTouchscreenThresholdDips;
Mohsen Izadi4ef8db72017-10-31 00:08:42465 if (fabs(overscroll_delta_x_) <= start_threshold &&
466 fabs(overscroll_delta_y_) <= start_threshold) {
mfomitchev09f0d64a2017-03-02 19:40:07467 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
[email protected]bfab3d02014-08-20 03:16:55468 return true;
[email protected]b4df9df2012-11-16 01:58:58469 }
470
Mohsen Izadia0091532017-07-06 04:21:05471 if (delegate_) {
472 base::Optional<float> cap = delegate_->GetMaxOverscrollDelta();
473 if (cap) {
Mohsen Izadia652de4c2017-07-22 02:30:32474 DCHECK_LE(0.f, cap.value());
Mohsen Izadia0091532017-07-06 04:21:05475 switch (overscroll_mode_) {
476 case OVERSCROLL_WEST:
477 case OVERSCROLL_EAST:
478 overscroll_delta_x_ = ClampAbsoluteValue(
Mohsen Izadi4ef8db72017-10-31 00:08:42479 overscroll_delta_x_, cap.value() + start_threshold);
Mohsen Izadia0091532017-07-06 04:21:05480 break;
481 case OVERSCROLL_NORTH:
482 case OVERSCROLL_SOUTH:
483 overscroll_delta_y_ = ClampAbsoluteValue(
Mohsen Izadi4ef8db72017-10-31 00:08:42484 overscroll_delta_y_, cap.value() + start_threshold);
Mohsen Izadia0091532017-07-06 04:21:05485 break;
486 case OVERSCROLL_NONE:
487 break;
488 }
489 }
490 }
491
[email protected]a23d04af2013-02-19 17:41:10492 // Compute the current overscroll direction. If the direction is different
493 // from the current direction, then always switch to no-overscroll mode first
494 // to make sure that subsequent scroll events go through to the page first.
495 OverscrollMode new_mode = OVERSCROLL_NONE;
[email protected]14749ddb2013-02-23 06:56:10496 const float kMinRatio = 2.5;
Mohsen Izadi4ef8db72017-10-31 00:08:42497 if (fabs(overscroll_delta_x_) > start_threshold &&
[email protected]fa6db252013-07-26 03:38:20498 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10499 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
Mohsen Izadi4ef8db72017-10-31 00:08:42500 else if (fabs(overscroll_delta_y_) > start_threshold &&
[email protected]fa6db252013-07-26 03:38:20501 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10502 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
503
chaopengcd44c7622018-04-13 04:08:21504 // The horizontal overscroll is used for history navigation. Enable it for
505 // touchpad only if TouchpadOverscrollHistoryNavigation is enabled.
506 if ((new_mode == OVERSCROLL_EAST || new_mode == OVERSCROLL_WEST) &&
507 is_touchpad &&
508 !OverscrollConfig::TouchpadOverscrollHistoryNavigationEnabled()) {
509 new_mode = OVERSCROLL_NONE;
510 }
511
Mohsen Izadi93faac12017-07-29 04:45:15512 // The vertical overscroll is used for pull-to-refresh. Enable it only if
513 // pull-to-refresh is enabled.
Mohsen Izadi8c59ba52018-04-12 18:52:01514 if (new_mode == OVERSCROLL_SOUTH || new_mode == OVERSCROLL_NORTH) {
515 auto ptr_mode = OverscrollConfig::GetPullToRefreshMode();
516 if (ptr_mode == OverscrollConfig::PullToRefreshMode::kDisabled ||
517 (ptr_mode ==
518 OverscrollConfig::PullToRefreshMode::kEnabledTouchschreen &&
Mohsen Izadi5a6583512018-04-23 18:31:19519 is_touchpad) ||
Daniel Cheng224569ee2018-04-25 05:45:06520 time_since_last_ignored_scroll_ < kPullToRefreshCoolOffDelay) {
Mohsen Izadi5a6583512018-04-23 18:31:19521 overscroll_ignored_ = true;
Mohsen Izadi8c59ba52018-04-12 18:52:01522 new_mode = OVERSCROLL_NONE;
523 }
524 }
[email protected]3aad43b2013-11-05 20:20:18525
mfomitchev09f0d64a2017-03-02 19:40:07526 if (overscroll_mode_ == OVERSCROLL_NONE) {
527 SetOverscrollMode(new_mode, is_touchpad ? OverscrollSource::TOUCHPAD
528 : OverscrollSource::TOUCHSCREEN);
529 } else if (new_mode != overscroll_mode_) {
530 SetOverscrollMode(OVERSCROLL_NONE, OverscrollSource::NONE);
531 }
[email protected]fa6db252013-07-26 03:38:20532
533 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]bfab3d02014-08-20 03:16:55534 return false;
[email protected]b4df9df2012-11-16 01:58:58535
Mohsen Izadi8f43c2c52018-10-09 17:17:11536 overscroll_ignored_ = false;
537
[email protected]b4df9df2012-11-16 01:58:58538 // Tell the delegate about the overscroll update so that it can update
539 // the display accordingly (e.g. show history preview etc.).
[email protected]9f4f47e2012-11-20 02:21:43540 if (delegate_) {
541 // Do not include the threshold amount when sending the deltas to the
542 // delegate.
543 float delegate_delta_x = overscroll_delta_x_;
Mohsen Izadi4ef8db72017-10-31 00:08:42544 if (fabs(delegate_delta_x) > start_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43545 if (delegate_delta_x < 0)
Mohsen Izadi4ef8db72017-10-31 00:08:42546 delegate_delta_x += start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43547 else
Mohsen Izadi4ef8db72017-10-31 00:08:42548 delegate_delta_x -= start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43549 } else {
550 delegate_delta_x = 0.f;
551 }
552
553 float delegate_delta_y = overscroll_delta_y_;
Mohsen Izadi4ef8db72017-10-31 00:08:42554 if (fabs(delegate_delta_y) > start_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43555 if (delegate_delta_y < 0)
Mohsen Izadi4ef8db72017-10-31 00:08:42556 delegate_delta_y += start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43557 else
Mohsen Izadi4ef8db72017-10-31 00:08:42558 delegate_delta_y -= start_threshold;
[email protected]9f4f47e2012-11-20 02:21:43559 } else {
560 delegate_delta_y = 0.f;
561 }
[email protected]bfab3d02014-08-20 03:16:55562 return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
[email protected]9f4f47e2012-11-20 02:21:43563 }
[email protected]bfab3d02014-08-20 03:16:55564 return false;
[email protected]b4df9df2012-11-16 01:58:58565}
566
567void OverscrollController::CompleteAction() {
chaopeng1d469b02018-04-05 15:41:07568 ignore_following_inertial_events_ = true;
[email protected]b4df9df2012-11-16 01:58:58569 if (delegate_)
570 delegate_->OnOverscrollComplete(overscroll_mode_);
Mohsen Izadif589f4b2017-08-24 02:55:48571 Reset();
[email protected]b4df9df2012-11-16 01:58:58572}
573
mfomitchev09f0d64a2017-03-02 19:40:07574void OverscrollController::SetOverscrollMode(OverscrollMode mode,
575 OverscrollSource source) {
[email protected]b4df9df2012-11-16 01:58:58576 if (overscroll_mode_ == mode)
577 return;
mfomitchev09f0d64a2017-03-02 19:40:07578
579 // If the mode changes to NONE, source is also NONE.
580 DCHECK(mode != OVERSCROLL_NONE || source == OverscrollSource::NONE);
581
Mohsen Izadif589f4b2017-08-24 02:55:48582 // When setting to a non-NONE mode and there is a locked mode, don't set the
583 // mode if the new mode is not the same as the locked mode.
584 if (mode != OVERSCROLL_NONE && locked_mode_ != OVERSCROLL_NONE &&
585 mode != locked_mode_) {
586 return;
587 }
588
[email protected]b4df9df2012-11-16 01:58:58589 OverscrollMode old_mode = overscroll_mode_;
590 overscroll_mode_ = mode;
Mohsen Izadi38795d5d2017-06-30 17:10:48591 overscroll_source_ = source;
Mohsen Izadif589f4b2017-08-24 02:55:48592 if (overscroll_mode_ == OVERSCROLL_NONE) {
[email protected]b4df9df2012-11-16 01:58:58593 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
Mohsen Izadif589f4b2017-08-24 02:55:48594 } else {
Mohsen Izadi9830436c2017-12-21 18:02:46595 scroll_state_ = ScrollState::OVERSCROLLING;
Mohsen Izadif589f4b2017-08-24 02:55:48596 locked_mode_ = overscroll_mode_;
597 }
Mohsen Izadi80189412018-03-22 17:10:18598 if (delegate_) {
599 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_, source,
600 behavior_);
601 }
[email protected]b4df9df2012-11-16 01:58:58602}
603
Mohsen Izadi50180f392017-09-08 19:29:12604void OverscrollController::ResetScrollState() {
Mohsen Izadi9830436c2017-12-21 18:02:46605 scroll_state_ = ScrollState::NONE;
Mohsen Izadi50180f392017-09-08 19:29:12606 locked_mode_ = OVERSCROLL_NONE;
607}
608
[email protected]b4df9df2012-11-16 01:58:58609} // namespace content