Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / renderer_host / overscroll_controller.cc
blob86dc692fea28dc06c44b2a30d79b108b70708798
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 if (scroll_state_ != STATE_UNKNOWN) {
39 switch (event.type) {
40 case blink::WebInputEvent::GestureScrollEnd:
41 case blink::WebInputEvent::GestureFlingStart:
42 scroll_state_ = STATE_UNKNOWN;
43 break;
45 case blink::WebInputEvent::MouseWheel: {
46 const blink::WebMouseWheelEvent& wheel =
47 static_cast<const blink::WebMouseWheelEvent&>(event);
48 if (!wheel.hasPreciseScrollingDeltas ||
49 wheel.phase == blink::WebMouseWheelEvent::PhaseEnded ||
50 wheel.phase == blink::WebMouseWheelEvent::PhaseCancelled) {
51 scroll_state_ = STATE_UNKNOWN;
53 break;
56 default:
57 if (blink::WebInputEvent::isMouseEventType(event.type) ||
58 blink::WebInputEvent::isKeyboardEventType(event.type)) {
59 scroll_state_ = STATE_UNKNOWN;
61 break;
65 if (DispatchEventCompletesAction(event)) {
66 CompleteAction();
68 // Let the event be dispatched to the renderer.
69 return false;
72 if (overscroll_mode_ != OVERSCROLL_NONE && DispatchEventResetsState(event)) {
73 SetOverscrollMode(OVERSCROLL_NONE);
75 // Let the event be dispatched to the renderer.
76 return false;
79 if (overscroll_mode_ != OVERSCROLL_NONE) {
80 // Consume the event only if it updates the overscroll state.
81 if (ProcessEventForOverscroll(event))
82 return true;
85 return false;
88 void OverscrollController::ReceivedEventACK(const blink::WebInputEvent& event,
89 bool processed) {
90 if (processed) {
91 // If a scroll event is consumed by the page, i.e. some content on the page
92 // has been scrolled, then there is not going to be an overscroll gesture,
93 // until the current scroll ends, and a new scroll gesture starts.
94 if (scroll_state_ == STATE_UNKNOWN &&
95 (event.type == blink::WebInputEvent::MouseWheel ||
96 event.type == blink::WebInputEvent::GestureScrollUpdate)) {
97 scroll_state_ = STATE_CONTENT_SCROLLING;
99 return;
101 ProcessEventForOverscroll(event);
104 void OverscrollController::DiscardingGestureEvent(
105 const blink::WebGestureEvent& gesture) {
106 if (scroll_state_ != STATE_UNKNOWN &&
107 (gesture.type == blink::WebInputEvent::GestureScrollEnd ||
108 gesture.type == blink::WebInputEvent::GestureFlingStart)) {
109 scroll_state_ = STATE_UNKNOWN;
113 void OverscrollController::Reset() {
114 overscroll_mode_ = OVERSCROLL_NONE;
115 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
116 scroll_state_ = STATE_UNKNOWN;
119 void OverscrollController::Cancel() {
120 SetOverscrollMode(OVERSCROLL_NONE);
121 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
122 scroll_state_ = STATE_UNKNOWN;
125 bool OverscrollController::DispatchEventCompletesAction (
126 const blink::WebInputEvent& event) const {
127 if (overscroll_mode_ == OVERSCROLL_NONE)
128 return false;
130 // Complete the overscroll gesture if there was a mouse move or a scroll-end
131 // after the threshold.
132 if (event.type != blink::WebInputEvent::MouseMove &&
133 event.type != blink::WebInputEvent::GestureScrollEnd &&
134 event.type != blink::WebInputEvent::GestureFlingStart)
135 return false;
137 if (!delegate_)
138 return false;
140 gfx::Rect bounds = delegate_->GetVisibleBounds();
141 if (bounds.IsEmpty())
142 return false;
144 if (event.type == blink::WebInputEvent::GestureFlingStart) {
145 // Check to see if the fling is in the same direction of the overscroll.
146 const blink::WebGestureEvent gesture =
147 static_cast<const blink::WebGestureEvent&>(event);
148 switch (overscroll_mode_) {
149 case OVERSCROLL_EAST:
150 if (gesture.data.flingStart.velocityX < 0)
151 return false;
152 break;
153 case OVERSCROLL_WEST:
154 if (gesture.data.flingStart.velocityX > 0)
155 return false;
156 break;
157 case OVERSCROLL_NORTH:
158 if (gesture.data.flingStart.velocityY > 0)
159 return false;
160 break;
161 case OVERSCROLL_SOUTH:
162 if (gesture.data.flingStart.velocityY < 0)
163 return false;
164 break;
165 case OVERSCROLL_NONE:
166 case OVERSCROLL_COUNT:
167 NOTREACHED();
171 float ratio, threshold;
172 if (overscroll_mode_ == OVERSCROLL_WEST ||
173 overscroll_mode_ == OVERSCROLL_EAST) {
174 ratio = fabs(overscroll_delta_x_) / bounds.width();
175 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
176 } else {
177 ratio = fabs(overscroll_delta_y_) / bounds.height();
178 threshold = GetOverscrollConfig(OVERSCROLL_CONFIG_VERT_THRESHOLD_COMPLETE);
181 return ratio >= threshold;
184 bool OverscrollController::DispatchEventResetsState(
185 const blink::WebInputEvent& event) const {
186 switch (event.type) {
187 case blink::WebInputEvent::MouseWheel: {
188 // Only wheel events with precise deltas (i.e. from trackpad) contribute
189 // to the overscroll gesture.
190 const blink::WebMouseWheelEvent& wheel =
191 static_cast<const blink::WebMouseWheelEvent&>(event);
192 return !wheel.hasPreciseScrollingDeltas;
195 case blink::WebInputEvent::GestureScrollUpdate:
196 case blink::WebInputEvent::GestureFlingCancel:
197 return false;
199 default:
200 // Touch events can arrive during an overscroll gesture initiated by
201 // touch-scrolling. These events should not reset the overscroll state.
202 return !blink::WebInputEvent::isTouchEventType(event.type);
206 bool OverscrollController::ProcessEventForOverscroll(
207 const blink::WebInputEvent& event) {
208 bool event_processed = false;
209 switch (event.type) {
210 case blink::WebInputEvent::MouseWheel: {
211 const blink::WebMouseWheelEvent& wheel =
212 static_cast<const blink::WebMouseWheelEvent&>(event);
213 if (!wheel.hasPreciseScrollingDeltas)
214 break;
215 event_processed =
216 ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX,
217 wheel.deltaY * wheel.accelerationRatioY,
218 wheel.type);
219 break;
221 case blink::WebInputEvent::GestureScrollUpdate: {
222 const blink::WebGestureEvent& gesture =
223 static_cast<const blink::WebGestureEvent&>(event);
224 event_processed = ProcessOverscroll(gesture.data.scrollUpdate.deltaX,
225 gesture.data.scrollUpdate.deltaY,
226 gesture.type);
227 break;
229 case blink::WebInputEvent::GestureFlingStart: {
230 const float kFlingVelocityThreshold = 1100.f;
231 const blink::WebGestureEvent& gesture =
232 static_cast<const blink::WebGestureEvent&>(event);
233 float velocity_x = gesture.data.flingStart.velocityX;
234 float velocity_y = gesture.data.flingStart.velocityY;
235 if (fabs(velocity_x) > kFlingVelocityThreshold) {
236 if ((overscroll_mode_ == OVERSCROLL_WEST && velocity_x < 0) ||
237 (overscroll_mode_ == OVERSCROLL_EAST && velocity_x > 0)) {
238 CompleteAction();
239 event_processed = true;
240 break;
242 } else if (fabs(velocity_y) > kFlingVelocityThreshold) {
243 if ((overscroll_mode_ == OVERSCROLL_NORTH && velocity_y < 0) ||
244 (overscroll_mode_ == OVERSCROLL_SOUTH && velocity_y > 0)) {
245 CompleteAction();
246 event_processed = true;
247 break;
251 // Reset overscroll state if fling didn't complete the overscroll gesture.
252 SetOverscrollMode(OVERSCROLL_NONE);
253 break;
256 default:
257 DCHECK(blink::WebInputEvent::isGestureEventType(event.type) ||
258 blink::WebInputEvent::isTouchEventType(event.type))
259 << "Received unexpected event: " << event.type;
261 return event_processed;
264 bool OverscrollController::ProcessOverscroll(float delta_x,
265 float delta_y,
266 blink::WebInputEvent::Type type) {
267 if (scroll_state_ != STATE_CONTENT_SCROLLING)
268 overscroll_delta_x_ += delta_x;
269 overscroll_delta_y_ += delta_y;
271 float horiz_threshold = GetOverscrollConfig(
272 WebInputEvent::isGestureEventType(type) ?
273 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN :
274 OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD);
275 float vert_threshold = GetOverscrollConfig(
276 OVERSCROLL_CONFIG_VERT_THRESHOLD_START);
277 if (fabs(overscroll_delta_x_) <= horiz_threshold &&
278 fabs(overscroll_delta_y_) <= vert_threshold) {
279 SetOverscrollMode(OVERSCROLL_NONE);
280 return true;
283 // Compute the current overscroll direction. If the direction is different
284 // from the current direction, then always switch to no-overscroll mode first
285 // to make sure that subsequent scroll events go through to the page first.
286 OverscrollMode new_mode = OVERSCROLL_NONE;
287 const float kMinRatio = 2.5;
288 if (fabs(overscroll_delta_x_) > horiz_threshold &&
289 fabs(overscroll_delta_x_) > fabs(overscroll_delta_y_) * kMinRatio)
290 new_mode = overscroll_delta_x_ > 0.f ? OVERSCROLL_EAST : OVERSCROLL_WEST;
291 else if (fabs(overscroll_delta_y_) > vert_threshold &&
292 fabs(overscroll_delta_y_) > fabs(overscroll_delta_x_) * kMinRatio)
293 new_mode = overscroll_delta_y_ > 0.f ? OVERSCROLL_SOUTH : OVERSCROLL_NORTH;
295 // The vertical oversrcoll currently does not have any UX effects other then
296 // for the scroll end effect, so testing if it is enabled.
297 if ((new_mode == OVERSCROLL_SOUTH || new_mode == OVERSCROLL_NORTH) &&
298 !IsScrollEndEffectEnabled())
299 new_mode = OVERSCROLL_NONE;
301 if (overscroll_mode_ == OVERSCROLL_NONE)
302 SetOverscrollMode(new_mode);
303 else if (new_mode != overscroll_mode_)
304 SetOverscrollMode(OVERSCROLL_NONE);
306 if (overscroll_mode_ == OVERSCROLL_NONE)
307 return false;
309 // Tell the delegate about the overscroll update so that it can update
310 // the display accordingly (e.g. show history preview etc.).
311 if (delegate_) {
312 // Do not include the threshold amount when sending the deltas to the
313 // delegate.
314 float delegate_delta_x = overscroll_delta_x_;
315 if (fabs(delegate_delta_x) > horiz_threshold) {
316 if (delegate_delta_x < 0)
317 delegate_delta_x += horiz_threshold;
318 else
319 delegate_delta_x -= horiz_threshold;
320 } else {
321 delegate_delta_x = 0.f;
324 float delegate_delta_y = overscroll_delta_y_;
325 if (fabs(delegate_delta_y) > vert_threshold) {
326 if (delegate_delta_y < 0)
327 delegate_delta_y += vert_threshold;
328 else
329 delegate_delta_y -= vert_threshold;
330 } else {
331 delegate_delta_y = 0.f;
333 return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y);
335 return false;
338 void OverscrollController::CompleteAction() {
339 if (delegate_)
340 delegate_->OnOverscrollComplete(overscroll_mode_);
341 overscroll_mode_ = OVERSCROLL_NONE;
342 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
345 void OverscrollController::SetOverscrollMode(OverscrollMode mode) {
346 if (overscroll_mode_ == mode)
347 return;
348 OverscrollMode old_mode = overscroll_mode_;
349 overscroll_mode_ = mode;
350 if (overscroll_mode_ == OVERSCROLL_NONE)
351 overscroll_delta_x_ = overscroll_delta_y_ = 0.f;
352 else
353 scroll_state_ = STATE_OVERSCROLLING;
354 if (delegate_)
355 delegate_->OnOverscrollModeChange(old_mode, overscroll_mode_);
358 } // namespace content