blob: f07efab61f4fe1504186974481be50128de69345 [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]b4df9df2012-11-16 01:58:587#include "content/browser/renderer_host/overscroll_controller_delegate.h"
8#include "content/browser/renderer_host/render_widget_host_impl.h"
[email protected]1cd76c2d82012-11-29 07:36:479#include "content/public/browser/overscroll_configuration.h"
[email protected]b4df9df2012-11-16 01:58:5810#include "content/public/browser/render_widget_host_view.h"
11
[email protected]a7f99f02013-08-29 14:15:1912using WebKit::WebInputEvent;
13
[email protected]b4df9df2012-11-16 01:58:5814namespace content {
15
16OverscrollController::OverscrollController(
17 RenderWidgetHostImpl* render_widget_host)
18 : render_widget_host_(render_widget_host),
19 overscroll_mode_(OVERSCROLL_NONE),
[email protected]a4141642013-05-23 04:10:5820 scroll_state_(STATE_UNKNOWN),
[email protected]b4df9df2012-11-16 01:58:5821 overscroll_delta_x_(0.f),
22 overscroll_delta_y_(0.f),
23 delegate_(NULL) {
24}
25
26OverscrollController::~OverscrollController() {
27}
28
29bool OverscrollController::WillDispatchEvent(
[email protected]c2eaa8f2013-05-10 02:41:5530 const WebKit::WebInputEvent& event,
[email protected]4b157662013-05-29 04:05:0531 const ui::LatencyInfo& latency_info) {
[email protected]a4141642013-05-23 04:10:5832 if (scroll_state_ != STATE_UNKNOWN) {
[email protected]b31beeb2013-06-17 15:54:2733 switch (event.type) {
34 case WebKit::WebInputEvent::GestureScrollEnd:
35 case WebKit::WebInputEvent::GestureFlingStart:
[email protected]a4141642013-05-23 04:10:5836 scroll_state_ = STATE_UNKNOWN;
[email protected]b31beeb2013-06-17 15:54:2737 break;
38
39 case WebKit::WebInputEvent::MouseWheel: {
40 const WebKit::WebMouseWheelEvent& wheel =
41 static_cast<const WebKit::WebMouseWheelEvent&>(event);
42 if (!wheel.hasPreciseScrollingDeltas ||
43 wheel.phase == WebKit::WebMouseWheelEvent::PhaseEnded ||
44 wheel.phase == WebKit::WebMouseWheelEvent::PhaseCancelled) {
45 scroll_state_ = STATE_UNKNOWN;
46 }
47 break;
[email protected]a4141642013-05-23 04:10:5848 }
[email protected]b31beeb2013-06-17 15:54:2749
50 default:
51 if (WebKit::WebInputEvent::isMouseEventType(event.type) ||
52 WebKit::WebInputEvent::isKeyboardEventType(event.type)) {
53 scroll_state_ = STATE_UNKNOWN;
54 }
55 break;
[email protected]a4141642013-05-23 04:10:5856 }
57 }
58
[email protected]b4df9df2012-11-16 01:58:5859 if (DispatchEventCompletesAction(event)) {
60 CompleteAction();
[email protected]9f4f47e2012-11-20 02:21:4361
62 // If the overscroll was caused by touch-scrolling, then the gesture event
63 // that completes the action needs to be sent to the renderer, because the
64 // touch-scrolls maintain state in the renderer side (in the compositor, for
65 // example), and the event that completes this action needs to be sent to
66 // the renderer so that those states can be updated/reset appropriately.
[email protected]2422e5c2013-09-13 19:05:0267 if (WebKit::WebInputEvent::isGestureEventType(event.type)) {
68 // A gesture-event isn't sent to the GestureEventFilter when overscroll is
69 // in progress. So dispatch the event through the RenderWidgetHost so that
70 // it can reach the GestureEventFilter.
[email protected]23511ad2012-12-15 02:42:0971 const WebKit::WebGestureEvent& gevent =
72 static_cast<const WebKit::WebGestureEvent&>(event);
[email protected]82efb412013-07-20 11:41:1473 return render_widget_host_->ShouldForwardGestureEvent(
74 GestureEventWithLatencyInfo(gevent, latency_info));
[email protected]9f4f47e2012-11-20 02:21:4375 }
76
[email protected]2422e5c2013-09-13 19:05:0277 return true;
[email protected]b4df9df2012-11-16 01:58:5878 }
79
80 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
81 SetOverscrollMode(OVERSCROLL_NONE);
[email protected]2422e5c2013-09-13 19:05:0282 if (WebKit::WebInputEvent::isGestureEventType(event.type)) {
83 // A gesture-event isn't sent to the GestureEventFilter when overscroll is
84 // in progress. So dispatch the event through the RenderWidgetHost so that
85 // it can reach the GestureEventFilter.
[email protected]b4df9df2012-11-16 01:58:5886 const WebKit::WebGestureEvent& gevent =
87 static_cast<const WebKit::WebGestureEvent&>(event);
[email protected]82efb412013-07-20 11:41:1488 return render_widget_host_->ShouldForwardGestureEvent(
[email protected]c2eaa8f2013-05-10 02:41:5589 GestureEventWithLatencyInfo(gevent, latency_info));
[email protected]b4df9df2012-11-16 01:58:5890 }
91
92 // Let the event be dispatched to the renderer.
93 return true;
94 }
95
96 if (overscroll_mode_ != OVERSCROLL_NONE) {
[email protected]2422e5c2013-09-13 19:05:0297 // Consume the event only if it updates the overscroll state.
98 if (ProcessEventForOverscroll(event))
99 return false;
[email protected]b4df9df2012-11-16 01:58:58100 }
101
102 return true;
103}
104
105void OverscrollController::ReceivedEventACK(const WebKit::WebInputEvent& event,
106 bool processed) {
[email protected]a4141642013-05-23 04:10:58107 if (processed) {
108 // If a scroll event is consumed by the page, i.e. some content on the page
109 // has been scrolled, then there is not going to be an overscroll gesture,
110 // until the current scroll ends, and a new scroll gesture starts.
111 if (scroll_state_ == STATE_UNKNOWN &&
112 (event.type == WebKit::WebInputEvent::MouseWheel ||
113 event.type == WebKit::WebInputEvent::GestureScrollUpdate)) {
114 scroll_state_ = STATE_CONTENT_SCROLLING;
115 }
[email protected]b4df9df2012-11-16 01:58:58116 return;
[email protected]a4141642013-05-23 04:10:58117 }
[email protected]b4df9df2012-11-16 01:58:58118 ProcessEventForOverscroll(event);
119}
120
[email protected]b31beeb2013-06-17 15:54:27121void OverscrollController::DiscardingGestureEvent(
122 const WebKit::WebGestureEvent& gesture) {
123 if (scroll_state_ != STATE_UNKNOWN &&
124 (gesture.type == WebKit::WebInputEvent::GestureScrollEnd ||
125 gesture.type == WebKit::WebInputEvent::GestureFlingStart)) {
126 scroll_state_ = STATE_UNKNOWN;
127 }
128}
129
[email protected]b4df9df2012-11-16 01:58:58130void OverscrollController::Reset() {
131 overscroll_mode_ = OVERSCROLL_NONE;
132 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]a4141642013-05-23 04:10:58133 scroll_state_ = STATE_UNKNOWN;
[email protected]b4df9df2012-11-16 01:58:58134}
135
[email protected]63ad73832013-06-17 15:41:04136void OverscrollController::Cancel() {
137 SetOverscrollMode(OVERSCROLL_NONE);
138 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
139 scroll_state_ = STATE_UNKNOWN;
140}
141
[email protected]b4df9df2012-11-16 01:58:58142bool OverscrollController::DispatchEventCompletesAction (
143 const WebKit::WebInputEvent& event) const {
144 if (overscroll_mode_ == OVERSCROLL_NONE)
145 return false;
146
147 // Complete the overscroll gesture if there was a mouse move or a scroll-end
148 // after the threshold.
149 if (event.type != WebKit::WebInputEvent::MouseMove &&
150 event.type != WebKit::WebInputEvent::GestureScrollEnd &&
151 event.type != WebKit::WebInputEvent::GestureFlingStart)
152 return false;
153
154 RenderWidgetHostView* view = render_widget_host_->GetView();
155 if (!view->IsShowing())
156 return false;
157
158 const gfx::Rect& bounds = view->GetViewBounds();
159 if (bounds.IsEmpty())
160 return false;
161
[email protected]6658c6a2013-01-23 23:42:22162 if (event.type == WebKit::WebInputEvent::GestureFlingStart) {
163 // Check to see if the fling is in the same direction of the overscroll.
164 const WebKit::WebGestureEvent gesture =
165 static_cast<const WebKit::WebGestureEvent&>(event);
166 switch (overscroll_mode_) {
167 case OVERSCROLL_EAST:
168 if (gesture.data.flingStart.velocityX < 0)
169 return false;
170 break;
171 case OVERSCROLL_WEST:
172 if (gesture.data.flingStart.velocityX > 0)
173 return false;
174 break;
175 case OVERSCROLL_NORTH:
176 if (gesture.data.flingStart.velocityY > 0)
177 return false;
178 break;
179 case OVERSCROLL_SOUTH:
180 if (gesture.data.flingStart.velocityY < 0)
181 return false;
182 break;
183 case OVERSCROLL_NONE:
[email protected]ce137a262013-02-11 18:40:07184 case OVERSCROLL_COUNT:
[email protected]6658c6a2013-01-23 23:42:22185 NOTREACHED();
186 }
187 }
188
[email protected]b4df9df2012-11-16 01:58:58189 float ratio, threshold;
190 if (overscroll_mode_ == OVERSCROLL_WEST ||
[email protected]e42b1f82012-11-16 21:18:46191 overscroll_mode_ == OVERSCROLL_EAST) {
192 ratio = fabs(overscroll_delta_x_) / bounds.width();
[email protected]1cd76c2d82012-11-29 07:36:47193 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
[email protected]b4df9df2012-11-16 01:58:58194 } else {
[email protected]e42b1f82012-11-16 21:18:46195 ratio = fabs(overscroll_delta_y_) / bounds.height();
[email protected]1cd76c2d82012-11-29 07:36:47196 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE);
[email protected]b4df9df2012-11-16 01:58:58197 }
[email protected]6658c6a2013-01-23 23:42:22198
[email protected]b4df9df2012-11-16 01:58:58199 return ratio >= threshold;
200}
201
202bool OverscrollController::DispatchEventResetsState(
203 const WebKit::WebInputEvent& event) const {
204 switch (event.type) {
[email protected]b16ac8202013-02-06 15:54:23205 case WebKit::WebInputEvent::MouseWheel: {
206 // Only wheel events with precise deltas (i.e. from trackpad) contribute
207 // to the overscroll gesture.
208 const WebKit::WebMouseWheelEvent& wheel =
209 static_cast<const WebKit::WebMouseWheelEvent&>(event);
210 return !wheel.hasPreciseScrollingDeltas;
211 }
212
[email protected]b4df9df2012-11-16 01:58:58213 case WebKit::WebInputEvent::GestureScrollUpdate:
214 case WebKit::WebInputEvent::GestureFlingCancel:
215 return false;
216
217 default:
[email protected]c4ddb5c42012-11-17 01:09:57218 // Touch events can arrive during an overscroll gesture initiated by
219 // touch-scrolling. These events should not reset the overscroll state.
220 return !WebKit::WebInputEvent::isTouchEventType(event.type);
[email protected]b4df9df2012-11-16 01:58:58221 }
222}
223
[email protected]2422e5c2013-09-13 19:05:02224bool OverscrollController::ProcessEventForOverscroll(
[email protected]b4df9df2012-11-16 01:58:58225 const WebKit::WebInputEvent& event) {
[email protected]2422e5c2013-09-13 19:05:02226 bool event_processed = false;
[email protected]b4df9df2012-11-16 01:58:58227 switch (event.type) {
228 case WebKit::WebInputEvent::MouseWheel: {
229 const WebKit::WebMouseWheelEvent& wheel =
230 static_cast<const WebKit::WebMouseWheelEvent&>(event);
[email protected]a4141642013-05-23 04:10:58231 if (!wheel.hasPreciseScrollingDeltas)
[email protected]2422e5c2013-09-13 19:05:02232 break;
[email protected]a4141642013-05-23 04:10:58233
234 ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
[email protected]a7f99f02013-08-29 14:15:19235 wheel.deltaY * wheel.accelerationRatioY,
236 wheel.type);
[email protected]2422e5c2013-09-13 19:05:02237 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58238 break;
239 }
240 case WebKit::WebInputEvent::GestureScrollUpdate: {
241 const WebKit::WebGestureEvent& gesture =
242 static_cast<const WebKit::WebGestureEvent&>(event);
243 ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
[email protected]a7f99f02013-08-29 14:15:19244 gesture.data.scrollUpdate.deltaY,
245 gesture.type);
[email protected]2422e5c2013-09-13 19:05:02246 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58247 break;
248 }
249 case WebKit::WebInputEvent::GestureFlingStart: {
250 const float kFlingVelocityThreshold = 1100.f;
251 const WebKit::WebGestureEvent& gesture =
252 static_cast<const WebKit::WebGestureEvent&>(event);
253 float velocity_x = gesture.data.flingStart.velocityX;
254 float velocity_y = gesture.data.flingStart.velocityY;
255 if (fabs(velocity_x) > kFlingVelocityThreshold) {
256 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
257 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
258 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02259 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58260 break;
261 }
262 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
263 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
264 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
265 CompleteAction();
[email protected]2422e5c2013-09-13 19:05:02266 event_processed = true;
[email protected]b4df9df2012-11-16 01:58:58267 break;
268 }
269 }
270
271 // Reset overscroll state if fling didn't complete the overscroll gesture.
272 SetOverscrollMode(OVERSCROLL_NONE);
273 break;
274 }
275
276 default:
[email protected]c4ddb5c42012-11-17 01:09:57277 DCHECK(WebKit::WebInputEvent::isGestureEventType(event.type) ||
278 WebKit::WebInputEvent::isTouchEventType(event.type))
279 << "Received unexpected event: " << event.type;
[email protected]b4df9df2012-11-16 01:58:58280 }
[email protected]2422e5c2013-09-13 19:05:02281 return event_processed;
[email protected]b4df9df2012-11-16 01:58:58282}
283
[email protected]a7f99f02013-08-29 14:15:19284void OverscrollController::ProcessOverscroll(float delta_x,
285 float delta_y,
286 WebKit::WebInputEvent::Type type) {
[email protected]fa6db252013-07-26 03:38:20287 if (scroll_state_ != STATE_CONTENT_SCROLLING)
288 overscroll_delta_x_ += delta_x;
[email protected]b4df9df2012-11-16 01:58:58289 overscroll_delta_y_ += delta_y;
290
[email protected]fa6db252013-07-26 03:38:20291 float horiz_threshold = GetOverscrollConfig(
[email protected]a7f99f02013-08-29 14:15:19292 WebInputEvent::isGestureEventType(type) ?
293 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN :
294 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD);
[email protected]fa6db252013-07-26 03:38:20295 float vert_threshold = GetOverscrollConfig(
296 OVERSCROLL_CONFIG_VERT_THRESHOLD_START);
297 if (fabs(overscroll_delta_x_) <= horiz_threshold &&
298 fabs(overscroll_delta_y_) <= vert_threshold) {
[email protected]b4df9df2012-11-16 01:58:58299 SetOverscrollMode(OVERSCROLL_NONE);
300 return;
301 }
302
[email protected]a23d04af2013-02-19 17:41:10303 // Compute the current overscroll direction. If the direction is different
304 // from the current direction, then always switch to no-overscroll mode first
305 // to make sure that subsequent scroll events go through to the page first.
306 OverscrollMode new_mode = OVERSCROLL_NONE;
[email protected]14749ddb2013-02-23 06:56:10307 const float kMinRatio = 2.5;
[email protected]fa6db252013-07-26 03:38:20308 if (fabs(overscroll_delta_x_) > horiz_threshold &&
309 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10310 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
[email protected]fa6db252013-07-26 03:38:20311 else if (fabs(overscroll_delta_y_) > vert_threshold &&
312 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10313 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
314
[email protected]fa6db252013-07-26 03:38:20315 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]a23d04af2013-02-19 17:41:10316 SetOverscrollMode(new_mode);
[email protected]fa6db252013-07-26 03:38:20317 else if (new_mode != overscroll_mode_)
[email protected]a23d04af2013-02-19 17:41:10318 SetOverscrollMode(OVERSCROLL_NONE);
[email protected]fa6db252013-07-26 03:38:20319
320 if (overscroll_mode_ == OVERSCROLL_NONE)
[email protected]a23d04af2013-02-19 17:41:10321 return;
[email protected]b4df9df2012-11-16 01:58:58322
323 // Tell the delegate about the overscroll update so that it can update
324 // the display accordingly (e.g. show history preview etc.).
[email protected]9f4f47e2012-11-20 02:21:43325 if (delegate_) {
326 // Do not include the threshold amount when sending the deltas to the
327 // delegate.
328 float delegate_delta_x = overscroll_delta_x_;
[email protected]fa6db252013-07-26 03:38:20329 if (fabs(delegate_delta_x) > horiz_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43330 if (delegate_delta_x < 0)
[email protected]fa6db252013-07-26 03:38:20331 delegate_delta_x += horiz_threshold;
[email protected]9f4f47e2012-11-20 02:21:43332 else
[email protected]fa6db252013-07-26 03:38:20333 delegate_delta_x -= horiz_threshold;
[email protected]9f4f47e2012-11-20 02:21:43334 } else {
335 delegate_delta_x = 0.f;
336 }
337
338 float delegate_delta_y = overscroll_delta_y_;
[email protected]fa6db252013-07-26 03:38:20339 if (fabs(delegate_delta_y) > vert_threshold) {
[email protected]9f4f47e2012-11-20 02:21:43340 if (delegate_delta_y < 0)
[email protected]fa6db252013-07-26 03:38:20341 delegate_delta_y += vert_threshold;
[email protected]9f4f47e2012-11-20 02:21:43342 else
[email protected]fa6db252013-07-26 03:38:20343 delegate_delta_y -= vert_threshold;
[email protected]9f4f47e2012-11-20 02:21:43344 } else {
345 delegate_delta_y = 0.f;
346 }
347 delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
348 }
[email protected]b4df9df2012-11-16 01:58:58349}
350
351void OverscrollController::CompleteAction() {
352 if (delegate_)
353 delegate_->OnOverscrollComplete(overscroll_mode_);
[email protected]e42b1f82012-11-16 21:18:46354 overscroll_mode_ = OVERSCROLL_NONE;
355 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]b4df9df2012-11-16 01:58:58356}
357
358void OverscrollController::SetOverscrollMode(OverscrollMode mode) {
359 if (overscroll_mode_ == mode)
360 return;
361 OverscrollMode old_mode = overscroll_mode_;
362 overscroll_mode_ = mode;
363 if (overscroll_mode_ == OVERSCROLL_NONE)
364 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]a4141642013-05-23 04:10:58365 else
366 scroll_state_ = STATE_OVERSCROLLING;
[email protected]b4df9df2012-11-16 01:58:58367 if (delegate_)
368 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_);
369}
370
[email protected]b4df9df2012-11-16 01:58:58371} // namespace content