Add ICU message format support
[chromium-blink-merge.git] / content / browser / renderer_host / overscroll_controller.cc
blobb622dc8533807e3a2d82635c6dde454e25cc4232
1 // 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.
5 #include "content/browser/renderer_host/overscroll_controller.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "content/browser/renderer_host/overscroll_controller_delegate.h"
10 #include "content/public/browser/overscroll_configuration.h"
11 #include "content/public/common/content_switches.h"
13 using blink::WebInputEvent;
15 namespace {
17 bool IsScrollEndEffectEnabled() {
18 return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
19 switches::kScrollEndEffect) == "1";
22 } // namespace
24 namespace content {
26 OverscrollController::OverscrollController()
27 : overscroll_mode_(OVERSCROLL_NONE),
28 scroll_state_(STATE_UNKNOWN),
29 overscroll_delta_x_(0.f),
30 overscroll_delta_y_(0.f),
31 delegate_(NULL) {
34 OverscrollController::~OverscrollController() {
37 bool OverscrollController::WillHandleEvent(const blink::WebInputEvent& event) {
38 bool reset_scroll_state = false;
39 if (scroll_state_ != STATE_UNKNOWN ||
40 overscroll_delta_x_ || overscroll_delta_y_) {
41 switch (event.type) {
42 case blink::WebInputEvent::GestureScrollEnd:
43 case blink::WebInputEvent::GestureFlingStart:
44 reset_scroll_state = true;
45 break;
47 case blink::WebInputEvent::MouseWheel: {
48 const blink::WebMouseWheelEvent& wheel =
49 static_cast<const blink::WebMouseWheelEvent&>(event);
50 if (!wheel.hasPreciseScrollingDeltas ||
51 wheel.phase == blink::WebMouseWheelEvent::PhaseEnded ||
52 wheel.phase == blink::WebMouseWheelEvent::PhaseCancelled) {
53 reset_scroll_state = true;
55 break;
58 default:
59 if (blink::WebInputEvent::isMouseEventType(event.type) ||
60 blink::WebInputEvent::isKeyboardEventType(event.type)) {
61 reset_scroll_state = true;
63 break;
67 if (reset_scroll_state)
68 scroll_state_ = STATE_UNKNOWN;
70 if (DispatchEventCompletesAction(event)) {
71 CompleteAction();
73 // Let the event be dispatched to the renderer.
74 return false;
77 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
78 SetOverscrollMode(OVERSCROLL_NONE);
80 // Let the event be dispatched to the renderer.
81 return false;
84 if (overscroll_mode_ != OVERSCROLL_NONE) {
85 // Consume the event only if it updates the overscroll state.
86 if (ProcessEventForOverscroll(event))
87 return true;
88 } else if (reset_scroll_state) {
89 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
93 return false;
96 void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event,
97 bool processed) {
98 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 == blink::WebInputEvent::MouseWheel ||
104 event.type == blink::WebInputEvent::GestureScrollUpdate)) {
105 scroll_state_ = STATE_CONTENT_SCROLLING;
107 return;
109 ProcessEventForOverscroll(event);
112 void OverscrollController::DiscardingGestureEvent(
113 const blink::WebGestureEvent& gesture) {
114 if (scroll_state_ != STATE_UNKNOWN &&
115 (gesture.type == blink::WebInputEvent::GestureScrollEnd ||
116 gesture.type == blink::WebInputEvent::GestureFlingStart)) {
117 scroll_state_ = STATE_UNKNOWN;
121 void OverscrollController::Reset() {
122 overscroll_mode_ = OVERSCROLL_NONE;
123 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
124 scroll_state_ = STATE_UNKNOWN;
127 void OverscrollController::Cancel() {
128 SetOverscrollMode(OVERSCROLL_NONE);
129 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
130 scroll_state_ = STATE_UNKNOWN;
133 bool OverscrollController::DispatchEventCompletesAction (
134 const blink::WebInputEvent& event) const {
135 if (overscroll_mode_ == OVERSCROLL_NONE)
136 return false;
138 // Complete the overscroll gesture if there was a mouse move or a scroll-end
139 // after the threshold.
140 if (event.type != blink::WebInputEvent::MouseMove &&
141 event.type != blink::WebInputEvent::GestureScrollEnd &&
142 event.type != blink::WebInputEvent::GestureFlingStart)
143 return false;
145 if (!delegate_)
146 return false;
148 gfx::Rect bounds = delegate_->GetVisibleBounds();
149 if (bounds.IsEmpty())
150 return false;
152 if (event.type == blink::WebInputEvent::GestureFlingStart) {
153 // Check to see if the fling is in the same direction of the overscroll.
154 const blink::WebGestureEvent gesture =
155 static_cast<const blink::WebGestureEvent&>(event);
156 switch (overscroll_mode_) {
157 case OVERSCROLL_EAST:
158 if (gesture.data.flingStart.velocityX < 0)
159 return false;
160 break;
161 case OVERSCROLL_WEST:
162 if (gesture.data.flingStart.velocityX > 0)
163 return false;
164 break;
165 case OVERSCROLL_NORTH:
166 if (gesture.data.flingStart.velocityY > 0)
167 return false;
168 break;
169 case OVERSCROLL_SOUTH:
170 if (gesture.data.flingStart.velocityY < 0)
171 return false;
172 break;
173 case OVERSCROLL_NONE:
174 NOTREACHED();
178 float ratio, threshold;
179 if (overscroll_mode_ == OVERSCROLL_WEST ||
180 overscroll_mode_ == OVERSCROLL_EAST) {
181 ratio = fabs(overscroll_delta_x_) / bounds.width();
182 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
183 } else {
184 ratio = fabs(overscroll_delta_y_) / bounds.height();
185 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE);
188 return ratio >= threshold;
191 bool OverscrollController::DispatchEventResetsState(
192 const blink::WebInputEvent& event) const {
193 switch (event.type) {
194 case blink::WebInputEvent::MouseWheel: {
195 // Only wheel events with precise deltas (i.e. from trackpad) contribute
196 // to the overscroll gesture.
197 const blink::WebMouseWheelEvent& wheel =
198 static_cast<const blink::WebMouseWheelEvent&>(event);
199 return !wheel.hasPreciseScrollingDeltas;
202 case blink::WebInputEvent::GestureScrollUpdate:
203 case blink::WebInputEvent::GestureFlingCancel:
204 return false;
206 default:
207 // Touch events can arrive during an overscroll gesture initiated by
208 // touch-scrolling. These events should not reset the overscroll state.
209 return !blink::WebInputEvent::isTouchEventType(event.type);
213 bool OverscrollController::ProcessEventForOverscroll(
214 const blink::WebInputEvent& event) {
215 bool event_processed = false;
216 switch (event.type) {
217 case blink::WebInputEvent::MouseWheel: {
218 const blink::WebMouseWheelEvent& wheel =
219 static_cast<const blink::WebMouseWheelEvent&>(event);
220 if (!wheel.hasPreciseScrollingDeltas)
221 break;
222 event_processed =
223 ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
224 wheel.deltaY * wheel.accelerationRatioY,
225 wheel.type);
226 break;
228 case blink::WebInputEvent::GestureScrollUpdate: {
229 const blink::WebGestureEvent& gesture =
230 static_cast<const blink::WebGestureEvent&>(event);
231 event_processed = ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
232 gesture.data.scrollUpdate.deltaY,
233 gesture.type);
234 break;
236 case blink::WebInputEvent::GestureFlingStart: {
237 const float kFlingVelocityThreshold = 1100.f;
238 const blink::WebGestureEvent& gesture =
239 static_cast<const blink::WebGestureEvent&>(event);
240 float velocity_x = gesture.data.flingStart.velocityX;
241 float velocity_y = gesture.data.flingStart.velocityY;
242 if (fabs(velocity_x) > kFlingVelocityThreshold) {
243 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
244 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
245 CompleteAction();
246 event_processed = true;
247 break;
249 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
250 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
251 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
252 CompleteAction();
253 event_processed = true;
254 break;
258 // Reset overscroll state if fling didn't complete the overscroll gesture.
259 SetOverscrollMode(OVERSCROLL_NONE);
260 break;
263 default:
264 DCHECK(blink::WebInputEvent::isGestureEventType(event.type) ||
265 blink::WebInputEvent::isTouchEventType(event.type))
266 << "Received unexpected event: " << event.type;
268 return event_processed;
271 bool OverscrollController::ProcessOverscroll(float delta_x,
272 float delta_y,
273 blink::WebInputEvent::Type type) {
274 if (scroll_state_ != STATE_CONTENT_SCROLLING)
275 overscroll_delta_x_ += delta_x;
276 overscroll_delta_y_ += delta_y;
278 float horiz_threshold = GetOverscrollConfig(
279 WebInputEvent::isGestureEventType(type) ?
280 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN :
281 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD);
282 float vert_threshold = GetOverscrollConfig(
283 OVERSCROLL_CONFIG_VERT_THRESHOLD_START);
284 if (fabs(overscroll_delta_x_) <= horiz_threshold &&
285 fabs(overscroll_delta_y_) <= vert_threshold) {
286 SetOverscrollMode(OVERSCROLL_NONE);
287 return true;
290 // Compute the current overscroll direction. If the direction is different
291 // from the current direction, then always switch to no-overscroll mode first
292 // to make sure that subsequent scroll events go through to the page first.
293 OverscrollMode new_mode = OVERSCROLL_NONE;
294 const float kMinRatio = 2.5;
295 if (fabs(overscroll_delta_x_) > horiz_threshold &&
296 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
297 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
298 else if (fabs(overscroll_delta_y_) > vert_threshold &&
299 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
300 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
302 // The vertical oversrcoll currently does not have any UX effects other then
303 // for the scroll end effect, so testing if it is enabled.
304 if ((new_mode == OVERSCROLL_SOUTH || new_mode == OVERSCROLL_NORTH) &&
305 !IsScrollEndEffectEnabled())
306 new_mode = OVERSCROLL_NONE;
308 if (overscroll_mode_ == OVERSCROLL_NONE)
309 SetOverscrollMode(new_mode);
310 else if (new_mode != overscroll_mode_)
311 SetOverscrollMode(OVERSCROLL_NONE);
313 if (overscroll_mode_ == OVERSCROLL_NONE)
314 return false;
316 // Tell the delegate about the overscroll update so that it can update
317 // the display accordingly (e.g. show history preview etc.).
318 if (delegate_) {
319 // Do not include the threshold amount when sending the deltas to the
320 // delegate.
321 float delegate_delta_x = overscroll_delta_x_;
322 if (fabs(delegate_delta_x) > horiz_threshold) {
323 if (delegate_delta_x < 0)
324 delegate_delta_x += horiz_threshold;
325 else
326 delegate_delta_x -= horiz_threshold;
327 } else {
328 delegate_delta_x = 0.f;
331 float delegate_delta_y = overscroll_delta_y_;
332 if (fabs(delegate_delta_y) > vert_threshold) {
333 if (delegate_delta_y < 0)
334 delegate_delta_y += vert_threshold;
335 else
336 delegate_delta_y -= vert_threshold;
337 } else {
338 delegate_delta_y = 0.f;
340 return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
342 return false;
345 void OverscrollController::CompleteAction() {
346 if (delegate_)
347 delegate_->OnOverscrollComplete(overscroll_mode_);
348 overscroll_mode_ = OVERSCROLL_NONE;
349 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
352 void OverscrollController::SetOverscrollMode(OverscrollMode mode) {
353 if (overscroll_mode_ == mode)
354 return;
355 OverscrollMode old_mode = overscroll_mode_;
356 overscroll_mode_ = mode;
357 if (overscroll_mode_ == OVERSCROLL_NONE)
358 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
359 else
360 scroll_state_ = STATE_OVERSCROLLING;
361 if (delegate_)
362 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_);
365 } // namespace content