blob: 0b5925a13d05c4b29d732f99ae8c22bd125b5e12 [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
7#include "content/browser/renderer_host/gesture_event_filter.h"
8#include "content/browser/renderer_host/overscroll_controller_delegate.h"
9#include "content/browser/renderer_host/render_widget_host_impl.h"
[email protected]1cd76c2d82012-11-29 07:36:4710#include "content/public/browser/overscroll_configuration.h"
[email protected]b4df9df2012-11-16 01:58:5811#include "content/public/browser/render_widget_host_view.h"
12
[email protected]b4df9df2012-11-16 01:58:5813namespace content {
14
15OverscrollController::OverscrollController(
16 RenderWidgetHostImpl* render_widget_host)
17 : render_widget_host_(render_widget_host),
18 overscroll_mode_(OVERSCROLL_NONE),
19 overscroll_delta_x_(0.f),
20 overscroll_delta_y_(0.f),
21 delegate_(NULL) {
22}
23
24OverscrollController::~OverscrollController() {
25}
26
27bool OverscrollController::WillDispatchEvent(
28 const WebKit::WebInputEvent& event) {
29 if (DispatchEventCompletesAction(event)) {
30 CompleteAction();
[email protected]9f4f47e2012-11-20 02:21:4331
32 // If the overscroll was caused by touch-scrolling, then the gesture event
33 // that completes the action needs to be sent to the renderer, because the
34 // touch-scrolls maintain state in the renderer side (in the compositor, for
35 // example), and the event that completes this action needs to be sent to
36 // the renderer so that those states can be updated/reset appropriately.
[email protected]c54ac002012-11-22 20:12:1237 // Send the event through the gesture-event filter when appropriate.
[email protected]23511ad2012-12-15 02:42:0938 if (ShouldForwardToGestureFilter(event)) {
39 const WebKit::WebGestureEvent& gevent =
40 static_cast<const WebKit::WebGestureEvent&>(event);
41 return render_widget_host_->gesture_event_filter()->
42 ShouldForward(gevent);
[email protected]9f4f47e2012-11-20 02:21:4343 }
44
[email protected]b4df9df2012-11-16 01:58:5845 return false;
46 }
47
48 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
49 SetOverscrollMode(OVERSCROLL_NONE);
50 // The overscroll gesture status is being reset. If the event is a
[email protected]23511ad2012-12-15 02:42:0951 // gesture event (from either touchscreen or trackpad), then make sure the
52 // gesture event filter gets the event first (if it didn't already process
53 // it).
54 if (ShouldForwardToGestureFilter(event)) {
[email protected]b4df9df2012-11-16 01:58:5855 const WebKit::WebGestureEvent& gevent =
56 static_cast<const WebKit::WebGestureEvent&>(event);
57 return render_widget_host_->gesture_event_filter()->ShouldForward(gevent);
58 }
59
60 // Let the event be dispatched to the renderer.
61 return true;
62 }
63
64 if (overscroll_mode_ != OVERSCROLL_NONE) {
65 // Consume the event and update overscroll state when in the middle of the
66 // overscroll gesture.
67 ProcessEventForOverscroll(event);
68 return false;
69 }
70
71 return true;
72}
73
74void OverscrollController::ReceivedEventACK(const WebKit::WebInputEvent& event,
75 bool processed) {
76 if (processed)
77 return;
78 ProcessEventForOverscroll(event);
79}
80
81void OverscrollController::Reset() {
82 overscroll_mode_ = OVERSCROLL_NONE;
83 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
84}
85
86bool OverscrollController::DispatchEventCompletesAction (
87 const WebKit::WebInputEvent& event) const {
88 if (overscroll_mode_ == OVERSCROLL_NONE)
89 return false;
90
91 // Complete the overscroll gesture if there was a mouse move or a scroll-end
92 // after the threshold.
93 if (event.type != WebKit::WebInputEvent::MouseMove &&
94 event.type != WebKit::WebInputEvent::GestureScrollEnd &&
95 event.type != WebKit::WebInputEvent::GestureFlingStart)
96 return false;
97
98 RenderWidgetHostView* view = render_widget_host_->GetView();
99 if (!view->IsShowing())
100 return false;
101
102 const gfx::Rect& bounds = view->GetViewBounds();
103 if (bounds.IsEmpty())
104 return false;
105
[email protected]6658c6a2013-01-23 23:42:22106 if (event.type == WebKit::WebInputEvent::GestureFlingStart) {
107 // Check to see if the fling is in the same direction of the overscroll.
108 const WebKit::WebGestureEvent gesture =
109 static_cast<const WebKit::WebGestureEvent&>(event);
110 switch (overscroll_mode_) {
111 case OVERSCROLL_EAST:
112 if (gesture.data.flingStart.velocityX < 0)
113 return false;
114 break;
115 case OVERSCROLL_WEST:
116 if (gesture.data.flingStart.velocityX > 0)
117 return false;
118 break;
119 case OVERSCROLL_NORTH:
120 if (gesture.data.flingStart.velocityY > 0)
121 return false;
122 break;
123 case OVERSCROLL_SOUTH:
124 if (gesture.data.flingStart.velocityY < 0)
125 return false;
126 break;
127 case OVERSCROLL_NONE:
[email protected]ce137a262013-02-11 18:40:07128 case OVERSCROLL_COUNT:
[email protected]6658c6a2013-01-23 23:42:22129 NOTREACHED();
130 }
131 }
132
[email protected]b4df9df2012-11-16 01:58:58133 float ratio, threshold;
134 if (overscroll_mode_ == OVERSCROLL_WEST ||
[email protected]e42b1f82012-11-16 21:18:46135 overscroll_mode_ == OVERSCROLL_EAST) {
136 ratio = fabs(overscroll_delta_x_) / bounds.width();
[email protected]1cd76c2d82012-11-29 07:36:47137 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
[email protected]b4df9df2012-11-16 01:58:58138 } else {
[email protected]e42b1f82012-11-16 21:18:46139 ratio = fabs(overscroll_delta_y_) / bounds.height();
[email protected]1cd76c2d82012-11-29 07:36:47140 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE);
[email protected]b4df9df2012-11-16 01:58:58141 }
[email protected]6658c6a2013-01-23 23:42:22142
[email protected]b4df9df2012-11-16 01:58:58143 return ratio >= threshold;
144}
145
146bool OverscrollController::DispatchEventResetsState(
147 const WebKit::WebInputEvent& event) const {
148 switch (event.type) {
[email protected]b16ac8202013-02-06 15:54:23149 case WebKit::WebInputEvent::MouseWheel: {
150 // Only wheel events with precise deltas (i.e. from trackpad) contribute
151 // to the overscroll gesture.
152 const WebKit::WebMouseWheelEvent& wheel =
153 static_cast<const WebKit::WebMouseWheelEvent&>(event);
154 return !wheel.hasPreciseScrollingDeltas;
155 }
156
[email protected]b4df9df2012-11-16 01:58:58157 case WebKit::WebInputEvent::GestureScrollUpdate:
158 case WebKit::WebInputEvent::GestureFlingCancel:
159 return false;
160
161 default:
[email protected]c4ddb5c42012-11-17 01:09:57162 // Touch events can arrive during an overscroll gesture initiated by
163 // touch-scrolling. These events should not reset the overscroll state.
164 return !WebKit::WebInputEvent::isTouchEventType(event.type);
[email protected]b4df9df2012-11-16 01:58:58165 }
166}
167
168void OverscrollController::ProcessEventForOverscroll(
169 const WebKit::WebInputEvent& event) {
170 switch (event.type) {
171 case WebKit::WebInputEvent::MouseWheel: {
172 const WebKit::WebMouseWheelEvent& wheel =
173 static_cast<const WebKit::WebMouseWheelEvent&>(event);
[email protected]d6d8dc82013-02-14 16:24:20174 if (wheel.hasPreciseScrollingDeltas) {
175 ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
176 wheel.deltaY * wheel.accelerationRatioY);
177 }
[email protected]b4df9df2012-11-16 01:58:58178 break;
179 }
180 case WebKit::WebInputEvent::GestureScrollUpdate: {
181 const WebKit::WebGestureEvent& gesture =
182 static_cast<const WebKit::WebGestureEvent&>(event);
183 ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
184 gesture.data.scrollUpdate.deltaY);
185 break;
186 }
187 case WebKit::WebInputEvent::GestureFlingStart: {
188 const float kFlingVelocityThreshold = 1100.f;
189 const WebKit::WebGestureEvent& gesture =
190 static_cast<const WebKit::WebGestureEvent&>(event);
191 float velocity_x = gesture.data.flingStart.velocityX;
192 float velocity_y = gesture.data.flingStart.velocityY;
193 if (fabs(velocity_x) > kFlingVelocityThreshold) {
194 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
195 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
196 CompleteAction();
197 break;
198 }
199 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
200 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
201 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
202 CompleteAction();
203 break;
204 }
205 }
206
207 // Reset overscroll state if fling didn't complete the overscroll gesture.
208 SetOverscrollMode(OVERSCROLL_NONE);
209 break;
210 }
211
212 default:
[email protected]c4ddb5c42012-11-17 01:09:57213 DCHECK(WebKit::WebInputEvent::isGestureEventType(event.type) ||
214 WebKit::WebInputEvent::isTouchEventType(event.type))
215 << "Received unexpected event: " << event.type;
[email protected]b4df9df2012-11-16 01:58:58216 }
217}
218
219void OverscrollController::ProcessOverscroll(float delta_x, float delta_y) {
220 overscroll_delta_x_ += delta_x;
221 overscroll_delta_y_ += delta_y;
222
[email protected]1cd76c2d82012-11-29 07:36:47223 float threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_MIN_THRESHOLD_START);
224 if (fabs(overscroll_delta_x_) < threshold &&
225 fabs(overscroll_delta_y_) < threshold) {
[email protected]b4df9df2012-11-16 01:58:58226 SetOverscrollMode(OVERSCROLL_NONE);
227 return;
228 }
229
[email protected]a23d04af2013-02-19 17:41:10230 // Compute the current overscroll direction. If the direction is different
231 // from the current direction, then always switch to no-overscroll mode first
232 // to make sure that subsequent scroll events go through to the page first.
233 OverscrollMode new_mode = OVERSCROLL_NONE;
[email protected]14749ddb2013-02-23 06:56:10234 const float kMinRatio = 2.5;
235 if (fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10236 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
[email protected]14749ddb2013-02-23 06:56:10237 else if (fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
[email protected]a23d04af2013-02-19 17:41:10238 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
239
240 if (overscroll_mode_ == OVERSCROLL_NONE) {
241 SetOverscrollMode(new_mode);
242 } else if (new_mode != overscroll_mode_) {
243 SetOverscrollMode(OVERSCROLL_NONE);
244 return;
[email protected]b4df9df2012-11-16 01:58:58245 }
246
247 // Tell the delegate about the overscroll update so that it can update
248 // the display accordingly (e.g. show history preview etc.).
[email protected]9f4f47e2012-11-20 02:21:43249 if (delegate_) {
250 // Do not include the threshold amount when sending the deltas to the
251 // delegate.
252 float delegate_delta_x = overscroll_delta_x_;
[email protected]1cd76c2d82012-11-29 07:36:47253 if (fabs(delegate_delta_x) > threshold) {
[email protected]9f4f47e2012-11-20 02:21:43254 if (delegate_delta_x < 0)
[email protected]1cd76c2d82012-11-29 07:36:47255 delegate_delta_x += threshold;
[email protected]9f4f47e2012-11-20 02:21:43256 else
[email protected]1cd76c2d82012-11-29 07:36:47257 delegate_delta_x -= threshold;
[email protected]9f4f47e2012-11-20 02:21:43258 } else {
259 delegate_delta_x = 0.f;
260 }
261
262 float delegate_delta_y = overscroll_delta_y_;
[email protected]1cd76c2d82012-11-29 07:36:47263 if (fabs(delegate_delta_y) > threshold) {
[email protected]9f4f47e2012-11-20 02:21:43264 if (delegate_delta_y < 0)
[email protected]1cd76c2d82012-11-29 07:36:47265 delegate_delta_y += threshold;
[email protected]9f4f47e2012-11-20 02:21:43266 else
[email protected]1cd76c2d82012-11-29 07:36:47267 delegate_delta_y -= threshold;
[email protected]9f4f47e2012-11-20 02:21:43268 } else {
269 delegate_delta_y = 0.f;
270 }
271 delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
272 }
[email protected]b4df9df2012-11-16 01:58:58273}
274
275void OverscrollController::CompleteAction() {
276 if (delegate_)
277 delegate_->OnOverscrollComplete(overscroll_mode_);
[email protected]e42b1f82012-11-16 21:18:46278 overscroll_mode_ = OVERSCROLL_NONE;
279 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
[email protected]b4df9df2012-11-16 01:58:58280}
281
282void OverscrollController::SetOverscrollMode(OverscrollMode mode) {
283 if (overscroll_mode_ == mode)
284 return;
285 OverscrollMode old_mode = overscroll_mode_;
286 overscroll_mode_ = mode;
287 if (overscroll_mode_ == OVERSCROLL_NONE)
288 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
289 if (delegate_)
290 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_);
291}
292
[email protected]c54ac002012-11-22 20:12:12293bool OverscrollController::ShouldForwardToGestureFilter(
294 const WebKit::WebInputEvent& event) const {
[email protected]23511ad2012-12-15 02:42:09295 if (!WebKit::WebInputEvent::isGestureEventType(event.type))
296 return false;
[email protected]c54ac002012-11-22 20:12:12297
298 // If the GestureEventFilter already processed this event, then the event must
299 // not be sent to the filter again.
300 return !render_widget_host_->gesture_event_filter()->HasQueuedGestureEvents();
301}
302
[email protected]b4df9df2012-11-16 01:58:58303} // namespace content