blob: 0ae3d3e25c3c9d5938e85d52c0149ebf4f3b8cb8 [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
[email protected]fc4b06d02013-10-23 20:47:117#include "base/logging.h"
[email protected]b4df9df2012-11-16 01:58:588#include "content/browser/renderer_host/overscroll_controller_delegate.h"
[email protected]1cd76c2d82012-11-29 07:36:479#include "content/public/browser/overscroll_configuration.h"
[email protected]b4df9df2012-11-16 01:58:5810
[email protected]a7f99f02013-08-29 14:15:1911using WebKit::WebInputEvent;
12
[email protected]b4df9df2012-11-16 01:58:5813namespace content {
14
[email protected]fc4b06d02013-10-23 20:47:1115OverscrollController::OverscrollController()
16 : overscroll_mode_(OVERSCROLL_NONE),
[email protected]a4141642013-05-23 04:10:5817 scroll_state_(STATE_UNKNOWN),
[email protected]b4df9df2012-11-16 01:58:5818 overscroll_delta_x_(0.f),
19 overscroll_delta_y_(0.f),
20 delegate_(NULL) {
21}
22
23OverscrollController::~OverscrollController() {
24}
25
[email protected]7680eb1f2013-10-18 11:19:1226OverscrollController::Disposition OverscrollController::DispatchEvent(
[email protected]c2eaa8f2013-05-10 02:41:5527 const WebKit::WebInputEvent& event,
[email protected]4b157662013-05-29 04:05:0528 const ui::LatencyInfo& latency_info) {
[email protected]a4141642013-05-23 04:10:5829 if (scroll_state_ != STATE_UNKNOWN) {
[email protected]b31beeb2013-06-17 15:54:2730 switch (event.type) {
31 case WebKit::WebInputEvent::GestureScrollEnd:
32 case WebKit::WebInputEvent::GestureFlingStart:
[email protected]a4141642013-05-23 04:10:5833 scroll_state_ = STATE_UNKNOWN;
[email protected]b31beeb2013-06-17 15:54:2734 break;
35
36 case WebKit::WebInputEvent::MouseWheel: {
37 const WebKit::WebMouseWheelEvent& wheel =
38 static_cast<const WebKit::WebMouseWheelEvent&>(event);
39 if (!wheel.hasPreciseScrollingDeltas ||
40 wheel.phase == WebKit::WebMouseWheelEvent::PhaseEnded ||
41 wheel.phase == WebKit::WebMouseWheelEvent::PhaseCancelled) {
42 scroll_state_ = STATE_UNKNOWN;
43 }
44 break;
[email protected]a4141642013-05-23 04:10:5845 }
[email protected]b31beeb2013-06-17 15:54:2746
47 default:
48 if (WebKit::WebInputEvent::isMouseEventType(event.type) ||
49 WebKit::WebInputEvent::isKeyboardEventType(event.type)) {
50 scroll_state_ = STATE_UNKNOWN;
51 }
52 break;
[email protected]a4141642013-05-23 04:10:5853 }
54 }
55
[email protected]b4df9df2012-11-16 01:58:5856 if (DispatchEventCompletesAction(event)) {
57 CompleteAction();
[email protected]9f4f47e2012-11-20 02:21:4358
59 // If the overscroll was caused by touch-scrolling, then the gesture event
60 // that completes the action needs to be sent to the renderer, because the
61 // touch-scrolls maintain state in the renderer side (in the compositor, for
62 // example), and the event that completes this action needs to be sent to
63 // the renderer so that those states can be updated/reset appropriately.
[email protected]2422e5c2013-09-13 19:05:0264 if (WebKit::WebInputEvent::isGestureEventType(event.type)) {
65 // A gesture-event isn't sent to the GestureEventFilter when overscroll is
66 // in progress. So dispatch the event through the RenderWidgetHost so that
67 // it can reach the GestureEventFilter.
[email protected]7680eb1f2013-10-18 11:19:1268 return SHOULD_FORWARD_TO_GESTURE_FILTER;
[email protected]9f4f47e2012-11-20 02:21:4369 }
70
[email protected]7680eb1f2013-10-18 11:19:1271 return SHOULD_FORWARD_TO_RENDERER;
[email protected]b4df9df2012-11-16 01:58:5872 }
73
74 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
75 SetOverscrollMode(OVERSCROLL_NONE);
[email protected]2422e5c2013-09-13 19:05:0276 if (WebKit::WebInputEvent::isGestureEventType(event.type)) {
77 // A gesture-event isn't sent to the GestureEventFilter when overscroll is
78 // in progress. So dispatch the event through the RenderWidgetHost so that
79 // it can reach the GestureEventFilter.
[email protected]7680eb1f2013-10-18 11:19:1280 return SHOULD_FORWARD_TO_GESTURE_FILTER;
[email protected]b4df9df2012-11-16 01:58:5881 }
82
83 // Let the event be dispatched to the renderer.
[email protected]7680eb1f2013-10-18 11:19:1284 return SHOULD_FORWARD_TO_RENDERER;
[email protected]b4df9df2012-11-16 01:58:5885 }
86
87 if (overscroll_mode_ != OVERSCROLL_NONE) {
[email protected]2422e5c2013-09-13 19:05:0288 // Consume the event only if it updates the overscroll state.
89 if (ProcessEventForOverscroll(event))
[email protected]7680eb1f2013-10-18 11:19:1290 return CONSUMED;
[email protected]b4df9df2012-11-16 01:58:5891 }
92
[email protected]7680eb1f2013-10-18 11:19:1293 return SHOULD_FORWARD_TO_RENDERER;
[email protected]b4df9df2012-11-16 01:58:5894}
95
96void OverscrollController::ReceivedEventACK(const WebKit::WebInputEvent& event,
97 bool processed) {
[email protected]a4141642013-05-23 04:10:5898 if (processed) {
99 // If a scroll event is consumed by the page, i.e. some content on the page
100 // has been scrolled, then there is not going to be an overscroll gesture,
101 // until the current scroll ends, and a new scroll gesture starts.
102 if (scroll_state_ == STATE_UNKNOWN &&
103 (event.type == WebKit::WebInputEvent::MouseWheel ||
104 event.type == WebKit::WebInputEvent::GestureScrollUpdate)) {
105 scroll_state_ = STATE_CONTENT_SCROLLING;
106 }
[email protected]b4df9df2012-11-16 01:58:58107 return;
[email protected]a4141642013-05-23 04:10:58108 }
[email protected]b4df9df2012-11-16 01:58:58109 ProcessEventForOverscroll(event);
110}
111
[email protected]b31beeb2013-06-17 15:54:27112void OverscrollController::DiscardingGestureEvent(
113 const WebKit::WebGestureEvent& gesture) {
114 if (scroll_state_ != STATE_UNKNOWN &&
115 (gesture.type == WebKit::WebInputEvent::GestureScrollEnd ||
116 gesture.type == WebKit::WebInputEvent::GestureFlingStart)) {
117 scroll_state_ = STATE_UNKNOWN;
118 }
119}
120
[email protected]b4df9df2012-11-16 01:58:58121void OverscrollController::Reset() {
122 overscroll_mode_ = OVERSCROLL_NONE;
123 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]a4141642013-05-23 04:10:58124 scroll_state_ = STATE_UNKNOWN;
[email protected]b4df9df2012-11-16 01:58:58125}
126
[email protected]63ad73832013-06-17 15:41:04127void OverscrollController::Cancel() {
128 SetOverscrollMode(OVERSCROLL_NONE);
129 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
130 scroll_state_ = STATE_UNKNOWN;
131}
132
[email protected]b4df9df2012-11-16 01:58:58133bool OverscrollController::DispatchEventCompletesAction (
134 const WebKit::WebInputEvent& event) const {
135 if (overscroll_mode_ == OVERSCROLL_NONE)
136 return false;
137
138 // Complete the overscroll gesture if there was a mouse move or a scroll-end
139 // after the threshold.
140 if (event.type != WebKit::WebInputEvent::MouseMove &&
141 event.type != WebKit::WebInputEvent::GestureScrollEnd &&
142 event.type != WebKit::WebInputEvent::GestureFlingStart)
143 return false;
144
[email protected]fc4b06d02013-10-23 20:47:11145 DCHECK(delegate_);
146 gfx::Rect bounds = delegate_->GetVisibleBounds();
[email protected]b4df9df2012-11-16 01:58:58147 if (bounds.IsEmpty())
148 return false;
149
[email protected]6658c6a2013-01-23 23:42:22150 if (event.type == WebKit::WebInputEvent::GestureFlingStart) {
151 // Check to see if the fling is in the same direction of the overscroll.
152 const WebKit::WebGestureEvent gesture =
153 static_cast<const WebKit::WebGestureEvent&>(event);
154 switch (overscroll_mode_) {
155 case OVERSCROLL_EAST:
156 if (gesture.data.flingStart.velocityX < 0)
157 return false;
158 break;
159 case OVERSCROLL_WEST:
160 if (gesture.data.flingStart.velocityX > 0)
161 return false;
162 break;
163 case OVERSCROLL_NORTH:
164 if (gesture.data.flingStart.velocityY > 0)
165 return false;
166 break;
167 case OVERSCROLL_SOUTH:
168 if (gesture.data.flingStart.velocityY < 0)
169 return false;
170 break;
171 case OVERSCROLL_NONE:
[email protected]ce137a262013-02-11 18:40:07172 case OVERSCROLL_COUNT:
[email protected]6658c6a2013-01-23 23:42:22173 NOTREACHED();
174 }
175 }
176
[email protected]b4df9df2012-11-16 01:58:58177 float ratio, threshold;
178 if (overscroll_mode_ == OVERSCROLL_WEST ||
[email protected]e42b1f82012-11-16 21:18:46179 overscroll_mode_ == OVERSCROLL_EAST) {
180 ratio = fabs(overscroll_delta_x_) / bounds.width();
[email protected]1cd76c2d82012-11-29 07:36:47181 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
[email protected]b4df9df2012-11-16 01:58:58182 } else {
[email protected]e42b1f82012-11-16 21:18:46183 ratio = fabs(overscroll_delta_y_) / bounds.height();
[email protected]1cd76c2d82012-11-29 07:36:47184 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE);
[email protected]b4df9df2012-11-16 01:58:58185 }
[email protected]6658c6a2013-01-23 23:42:22186
[email protected]b4df9df2012-11-16 01:58:58187 return ratio >= threshold;
188}
189
190bool OverscrollController::DispatchEventResetsState(
191 const WebKit::WebInputEvent& event) const {
192 switch (event.type) {
[email protected]b16ac8202013-02-06 15:54:23193 case WebKit::WebInputEvent::MouseWheel: {
194 // Only wheel events with precise deltas (i.e. from trackpad) contribute
195 // to the overscroll gesture.
196 const WebKit::WebMouseWheelEvent& wheel =
197 static_cast<const WebKit::WebMouseWheelEvent&>(event);
198 return !wheel.hasPreciseScrollingDeltas;
199 }
200
[email protected]b4df9df2012-11-16 01:58:58201 case WebKit::WebInputEvent::GestureScrollUpdate:
202 case WebKit::WebInputEvent::GestureFlingCancel:
203 return false;
204
205 default:
[email protected]c4ddb5c42012-11-17 01:09:57206 // Touch events can arrive during an overscroll gesture initiated by
207 // touch-scrolling. These events should not reset the overscroll state.
208 return !WebKit::WebInputEvent::isTouchEventType(event.type);
[email protected]b4df9df2012-11-16 01:58:58209 }
210}
211
[email protected]2422e5c2013-09-13 19:05:02212bool OverscrollController::ProcessEventForOverscroll(
[email protected]b4df9df2012-11-16 01:58:58213 const WebKit::WebInputEvent& event) {
[email protected]2422e5c2013-09-13 19:05:02214 bool event_processed = false;
[email protected]b4df9df2012-11-16 01:58:58215 switch (event.type) {
216 case WebKit::WebInputEvent::MouseWheel: {
217 const WebKit::WebMouseWheelEvent& wheel =
218 static_cast<const WebKit::WebMouseWheelEvent&>(event);
[email protected]a4141642013-05-23 04:10:58219 if (!wheel.hasPreciseScrollingDeltas)
[email protected]2422e5c2013-09-13 19:05:02220 break;
[email protected]a4141642013-05-23 04:10:58221
222 ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
[email protected]a7f99f02013-08-29 14:15:19223 wheel.deltaY * wheel.accelerationRatioY,
224 wheel.type);
[email protected]2422e5c2013-09-13 19:05:02225 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58226 break;
227 }
228 case WebKit::WebInputEvent::GestureScrollUpdate: {
229 const WebKit::WebGestureEvent& gesture =
230 static_cast<const WebKit::WebGestureEvent&>(event);
231 ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
[email protected]a7f99f02013-08-29 14:15:19232 gesture.data.scrollUpdate.deltaY,
233 gesture.type);
[email protected]2422e5c2013-09-13 19:05:02234 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58235 break;
236 }
237 case WebKit::WebInputEvent::GestureFlingStart: {
238 const float kFlingVelocityThreshold = 1100.f;
239 const WebKit::WebGestureEvent& gesture =
240 static_cast<const WebKit::WebGestureEvent&>(event);
241 float velocity_x = gesture.data.flingStart.velocityX;
242 float velocity_y = gesture.data.flingStart.velocityY;
243 if (fabs(velocity_x) > kFlingVelocityThreshold) {
244 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
245 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
246 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02247 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58248 break;
249 }
250 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
251 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
252 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
253 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02254 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58255 break;
256 }
257 }
258
259 // Reset overscroll state if fling didn't complete the overscroll gesture.
260 SetOverscrollMode(OVERSCROLL_NONE);
261 break;
262 }
263
264 default:
[email protected]c4ddb5c42012-11-17 01:09:57265 DCHECK(WebKit::WebInputEvent::isGestureEventType(event.type) ||
266 WebKit::WebInputEvent::isTouchEventType(event.type))
267 << "Received unexpected event: " << event.type;
[email protected]b4df9df2012-11-16 01:58:58268 }
[email protected]2422e5c2013-09-13 19:05:02269 return event_processed;
[email protected]b4df9df2012-11-16 01:58:58270}
271
[email protected]a7f99f02013-08-29 14:15:19272void OverscrollController::ProcessOverscroll(float delta_x,
273 float delta_y,
274 WebKit::WebInputEvent::Type type) {
[email protected]fa6db252013-07-26 03:38:20275 if (scroll_state_ != STATE_CONTENT_SCROLLING)
276 overscroll_delta_x_ += delta_x;
[email protected]b4df9df2012-11-16 01:58:58277 overscroll_delta_y_ += delta_y;
278
[email protected]fa6db252013-07-26 03:38:20279 float horiz_threshold = GetOverscrollConfig(
[email protected]a7f99f02013-08-29 14:15:19280 WebInputEvent::isGestureEventType(type) ?
281 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN :
282 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD);
[email protected]fa6db252013-07-26 03:38:20283 float vert_threshold = GetOverscrollConfig(
284 OVERSCROLL_CONFIG_VERT_THRESHOLD_START);
285 if (fabs(overscroll_delta_x_) <= horiz_threshold &&
286 fabs(overscroll_delta_y_) <= vert_threshold) {
[email protected]b4df9df2012-11-16 01:58:58287 SetOverscrollMode(OVERSCROLL_NONE);
288 return;
289 }
290
[email protected]a23d04af2013-02-19 17:41:10291 // Compute the current overscroll direction. If the direction is different
292 // from the current direction, then always switch to no-overscroll mode first
293 // to make sure that subsequent scroll events go through to the page first.
294 OverscrollMode new_mode = OVERSCROLL_NONE;
[email protected]14749ddb2013-02-23 06:56:10295 const float kMinRatio = 2.5;
[email protected]fa6db252013-07-26 03:38:20296 if (fabs(overscroll_delta_x_) > horiz_threshold &&
297 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10298 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
[email protected]fa6db252013-07-26 03:38:20299 else if (fabs(overscroll_delta_y_) > vert_threshold &&
300 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10301 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
302
[email protected]fa6db252013-07-26 03:38:20303 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]a23d04af2013-02-19 17:41:10304 SetOverscrollMode(new_mode);
[email protected]fa6db252013-07-26 03:38:20305 else if (new_mode != overscroll_mode_)
[email protected]a23d04af2013-02-19 17:41:10306 SetOverscrollMode(OVERSCROLL_NONE);
[email protected]fa6db252013-07-26 03:38:20307
308 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]a23d04af2013-02-19 17:41:10309 return;
[email protected]b4df9df2012-11-16 01:58:58310
311 // Tell the delegate about the overscroll update so that it can update
312 // the display accordingly (e.g. show history preview etc.).
[email protected]9f4f47e2012-11-20 02:21:43313 if (delegate_) {
314 // Do not include the threshold amount when sending the deltas to the
315 // delegate.
316 float delegate_delta_x = overscroll_delta_x_;
[email protected]fa6db252013-07-26 03:38:20317 if (fabs(delegate_delta_x) > horiz_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43318 if (delegate_delta_x < 0)
[email protected]fa6db252013-07-26 03:38:20319 delegate_delta_x += horiz_threshold;
[email protected]9f4f47e2012-11-20 02:21:43320 else
[email protected]fa6db252013-07-26 03:38:20321 delegate_delta_x -= horiz_threshold;
[email protected]9f4f47e2012-11-20 02:21:43322 } else {
323 delegate_delta_x = 0.f;
324 }
325
326 float delegate_delta_y = overscroll_delta_y_;
[email protected]fa6db252013-07-26 03:38:20327 if (fabs(delegate_delta_y) > vert_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43328 if (delegate_delta_y < 0)
[email protected]fa6db252013-07-26 03:38:20329 delegate_delta_y += vert_threshold;
[email protected]9f4f47e2012-11-20 02:21:43330 else
[email protected]fa6db252013-07-26 03:38:20331 delegate_delta_y -= vert_threshold;
[email protected]9f4f47e2012-11-20 02:21:43332 } else {
333 delegate_delta_y = 0.f;
334 }
335 delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
336 }
[email protected]b4df9df2012-11-16 01:58:58337}
338
339void OverscrollController::CompleteAction() {
340 if (delegate_)
341 delegate_->OnOverscrollComplete(overscroll_mode_);
[email protected]e42b1f82012-11-16 21:18:46342 overscroll_mode_ = OVERSCROLL_NONE;
343 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]b4df9df2012-11-16 01:58:58344}
345
346void OverscrollController::SetOverscrollMode(OverscrollMode mode) {
347 if (overscroll_mode_ == mode)
348 return;
349 OverscrollMode old_mode = overscroll_mode_;
350 overscroll_mode_ = mode;
351 if (overscroll_mode_ == OVERSCROLL_NONE)
352 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]a4141642013-05-23 04:10:58353 else
354 scroll_state_ = STATE_OVERSCROLLING;
[email protected]b4df9df2012-11-16 01:58:58355 if (delegate_)
356 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_);
357}
358
[email protected]b4df9df2012-11-16 01:58:58359} // namespace content