Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / renderer / input / input_handler_proxy.cc
blob6970d22003e9b589003fc7eb27bbdeb48ee372c3
1 // Copyright 2013 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/renderer/input/input_handler_proxy.h"
7 #include "base/auto_reset.h"
8 #include "base/command_line.h"
9 #include "base/debug/trace_event.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "content/common/input/did_overscroll_params.h"
13 #include "content/common/input/web_input_event_traits.h"
14 #include "content/public/common/content_switches.h"
15 #include "content/renderer/input/input_handler_proxy_client.h"
16 #include "third_party/WebKit/public/platform/Platform.h"
17 #include "third_party/WebKit/public/web/WebInputEvent.h"
18 #include "ui/events/latency_info.h"
19 #include "ui/gfx/frame_time.h"
20 #include "ui/gfx/geometry/point_conversions.h"
22 using blink::WebFloatPoint;
23 using blink::WebFloatSize;
24 using blink::WebGestureEvent;
25 using blink::WebInputEvent;
26 using blink::WebMouseEvent;
27 using blink::WebMouseWheelEvent;
28 using blink::WebPoint;
29 using blink::WebTouchEvent;
30 using blink::WebTouchPoint;
32 namespace {
34 // Maximum time between a fling event's timestamp and the first |Animate| call
35 // for the fling curve to use the fling timestamp as the initial animation time.
36 // Two frames allows a minor delay between event creation and the first animate.
37 const double kMaxSecondsFromFlingTimestampToFirstAnimate = 2. / 60.;
39 // Threshold for determining whether a fling scroll delta should have caused the
40 // client to scroll.
41 const float kScrollEpsilon = 0.1f;
43 // Minimum fling velocity required for the active fling and new fling for the
44 // two to accumulate.
45 const double kMinBoostFlingSpeedSquare = 350. * 350.;
47 // Minimum velocity for the active touch scroll to preserve (boost) an active
48 // fling for which cancellation has been deferred.
49 const double kMinBoostTouchScrollSpeedSquare = 150 * 150.;
51 // Timeout window after which the active fling will be cancelled if no scrolls
52 // or flings of sufficient velocity relative to the current fling are received.
53 // The default value on Android native views is 40ms, but we use a slightly
54 // increased value to accomodate small IPC message delays.
55 const double kFlingBoostTimeoutDelaySeconds = 0.045;
57 gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
58 return gfx::Vector2dF(-increment.width, -increment.height);
61 double InSecondsF(const base::TimeTicks& time) {
62 return (time - base::TimeTicks()).InSecondsF();
65 bool ShouldSuppressScrollForFlingBoosting(
66 const gfx::Vector2dF& current_fling_velocity,
67 const WebGestureEvent& scroll_update_event,
68 double time_since_last_boost_event) {
69 DCHECK_EQ(WebInputEvent::GestureScrollUpdate, scroll_update_event.type);
71 gfx::Vector2dF dx(scroll_update_event.data.scrollUpdate.deltaX,
72 scroll_update_event.data.scrollUpdate.deltaY);
73 if (gfx::DotProduct(current_fling_velocity, dx) < 0)
74 return false;
76 if (time_since_last_boost_event < 0.001)
77 return true;
79 // TODO(jdduke): Use |scroll_update_event.data.scrollUpdate.velocity{X,Y}|.
80 // The scroll must be of sufficient velocity to maintain the active fling.
81 const gfx::Vector2dF scroll_velocity =
82 gfx::ScaleVector2d(dx, 1. / time_since_last_boost_event);
83 if (scroll_velocity.LengthSquared() < kMinBoostTouchScrollSpeedSquare)
84 return false;
86 return true;
89 bool ShouldBoostFling(const gfx::Vector2dF& current_fling_velocity,
90 const WebGestureEvent& fling_start_event) {
91 DCHECK_EQ(WebInputEvent::GestureFlingStart, fling_start_event.type);
93 gfx::Vector2dF new_fling_velocity(
94 fling_start_event.data.flingStart.velocityX,
95 fling_start_event.data.flingStart.velocityY);
97 if (gfx::DotProduct(current_fling_velocity, new_fling_velocity) < 0)
98 return false;
100 if (current_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
101 return false;
103 if (new_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
104 return false;
106 return true;
109 WebGestureEvent ObtainGestureScrollBegin(const WebGestureEvent& event) {
110 WebGestureEvent scroll_begin_event = event;
111 scroll_begin_event.type = WebInputEvent::GestureScrollBegin;
112 scroll_begin_event.data.scrollBegin.deltaXHint = 0;
113 scroll_begin_event.data.scrollBegin.deltaYHint = 0;
114 return scroll_begin_event;
117 void SendScrollLatencyUma(const WebInputEvent& event,
118 const ui::LatencyInfo& latency_info) {
119 if (!(event.type == WebInputEvent::GestureScrollBegin ||
120 event.type == WebInputEvent::GestureScrollUpdate ||
121 event.type == WebInputEvent::GestureScrollUpdateWithoutPropagation))
122 return;
124 ui::LatencyInfo::LatencyMap::const_iterator it =
125 latency_info.latency_components.find(std::make_pair(
126 ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0));
128 if (it == latency_info.latency_components.end())
129 return;
131 base::TimeDelta delta = base::TimeTicks::HighResNow() - it->second.event_time;
132 for (size_t i = 0; i < it->second.event_count; ++i) {
133 UMA_HISTOGRAM_CUSTOM_COUNTS(
134 "Event.Latency.RendererImpl.GestureScroll2",
135 delta.InMicroseconds(),
137 1000000,
138 100);
140 } // namespace
144 namespace content {
146 InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler,
147 InputHandlerProxyClient* client)
148 : client_(client),
149 input_handler_(input_handler),
150 deferred_fling_cancel_time_seconds_(0),
151 #ifndef NDEBUG
152 expect_scroll_update_end_(false),
153 #endif
154 gesture_scroll_on_impl_thread_(false),
155 gesture_pinch_on_impl_thread_(false),
156 fling_may_be_active_on_main_thread_(false),
157 disallow_horizontal_fling_scroll_(false),
158 disallow_vertical_fling_scroll_(false),
159 has_fling_animation_started_(false) {
160 DCHECK(client);
161 input_handler_->BindToClient(this);
162 smooth_scroll_enabled_ = CommandLine::ForCurrentProcess()->HasSwitch(
163 switches::kEnableSmoothScrolling);
166 InputHandlerProxy::~InputHandlerProxy() {}
168 void InputHandlerProxy::WillShutdown() {
169 input_handler_ = NULL;
170 client_->WillShutdown();
173 InputHandlerProxy::EventDisposition
174 InputHandlerProxy::HandleInputEventWithLatencyInfo(
175 const WebInputEvent& event,
176 ui::LatencyInfo* latency_info) {
177 DCHECK(input_handler_);
179 SendScrollLatencyUma(event, *latency_info);
181 TRACE_EVENT_FLOW_STEP0("input",
182 "LatencyInfo.Flow",
183 TRACE_ID_DONT_MANGLE(latency_info->trace_id),
184 "HandleInputEventImpl");
186 scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
187 input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info);
188 InputHandlerProxy::EventDisposition disposition = HandleInputEvent(event);
189 return disposition;
192 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent(
193 const WebInputEvent& event) {
194 DCHECK(input_handler_);
195 TRACE_EVENT1("input", "InputHandlerProxy::HandleInputEvent",
196 "type", WebInputEventTraits::GetName(event.type));
198 if (FilterInputEventForFlingBoosting(event))
199 return DID_HANDLE;
201 if (event.type == WebInputEvent::MouseWheel) {
202 const WebMouseWheelEvent& wheel_event =
203 *static_cast<const WebMouseWheelEvent*>(&event);
204 if (wheel_event.scrollByPage) {
205 // TODO(jamesr): We don't properly handle scroll by page in the compositor
206 // thread, so punt it to the main thread. http://crbug.com/236639
207 return DID_NOT_HANDLE;
209 if (wheel_event.modifiers & WebInputEvent::ControlKey) {
210 // Wheel events involving the control key never trigger scrolling, only
211 // event handlers. Forward to the main thread.
212 return DID_NOT_HANDLE;
214 if (smooth_scroll_enabled_) {
215 cc::InputHandler::ScrollStatus scroll_status =
216 input_handler_->ScrollAnimated(
217 gfx::Point(wheel_event.x, wheel_event.y),
218 gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
219 switch (scroll_status) {
220 case cc::InputHandler::ScrollStarted:
221 return DID_HANDLE;
222 case cc::InputHandler::ScrollIgnored:
223 return DROP_EVENT;
224 default:
225 return DID_NOT_HANDLE;
228 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
229 gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
230 switch (scroll_status) {
231 case cc::InputHandler::ScrollStarted: {
232 TRACE_EVENT_INSTANT2(
233 "input",
234 "InputHandlerProxy::handle_input wheel scroll",
235 TRACE_EVENT_SCOPE_THREAD,
236 "deltaX",
237 -wheel_event.deltaX,
238 "deltaY",
239 -wheel_event.deltaY);
240 bool did_scroll = input_handler_->ScrollBy(
241 gfx::Point(wheel_event.x, wheel_event.y),
242 gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
243 input_handler_->ScrollEnd();
244 return did_scroll ? DID_HANDLE : DROP_EVENT;
246 case cc::InputHandler::ScrollIgnored:
247 // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
248 // to properly sync scrollability it's safer to send the event to the
249 // main thread. Change back to DROP_EVENT once we have synchronization
250 // bugs sorted out.
251 return DID_NOT_HANDLE;
252 case cc::InputHandler::ScrollUnknown:
253 case cc::InputHandler::ScrollOnMainThread:
254 return DID_NOT_HANDLE;
255 case cc::InputHandler::ScrollStatusCount:
256 NOTREACHED();
257 break;
259 } else if (event.type == WebInputEvent::GestureScrollBegin) {
260 DCHECK(!gesture_scroll_on_impl_thread_);
261 #ifndef NDEBUG
262 DCHECK(!expect_scroll_update_end_);
263 expect_scroll_update_end_ = true;
264 #endif
265 const WebGestureEvent& gesture_event =
266 *static_cast<const WebGestureEvent*>(&event);
267 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
268 gfx::Point(gesture_event.x, gesture_event.y),
269 cc::InputHandler::Gesture);
270 UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult",
271 scroll_status,
272 cc::InputHandler::ScrollStatusCount);
273 switch (scroll_status) {
274 case cc::InputHandler::ScrollStarted:
275 TRACE_EVENT_INSTANT0("input",
276 "InputHandlerProxy::handle_input gesture scroll",
277 TRACE_EVENT_SCOPE_THREAD);
278 gesture_scroll_on_impl_thread_ = true;
279 return DID_HANDLE;
280 case cc::InputHandler::ScrollUnknown:
281 case cc::InputHandler::ScrollOnMainThread:
282 return DID_NOT_HANDLE;
283 case cc::InputHandler::ScrollIgnored:
284 return DROP_EVENT;
285 case cc::InputHandler::ScrollStatusCount:
286 NOTREACHED();
287 break;
289 } else if (event.type == WebInputEvent::GestureScrollUpdate) {
290 #ifndef NDEBUG
291 DCHECK(expect_scroll_update_end_);
292 #endif
294 if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
295 return DID_NOT_HANDLE;
297 const WebGestureEvent& gesture_event =
298 *static_cast<const WebGestureEvent*>(&event);
299 bool did_scroll = input_handler_->ScrollBy(
300 gfx::Point(gesture_event.x, gesture_event.y),
301 gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX,
302 -gesture_event.data.scrollUpdate.deltaY));
303 return did_scroll ? DID_HANDLE : DROP_EVENT;
304 } else if (event.type == WebInputEvent::GestureScrollEnd) {
305 #ifndef NDEBUG
306 DCHECK(expect_scroll_update_end_);
307 expect_scroll_update_end_ = false;
308 #endif
309 input_handler_->ScrollEnd();
311 if (!gesture_scroll_on_impl_thread_)
312 return DID_NOT_HANDLE;
314 gesture_scroll_on_impl_thread_ = false;
315 return DID_HANDLE;
316 } else if (event.type == WebInputEvent::GesturePinchBegin) {
317 input_handler_->PinchGestureBegin();
318 DCHECK(!gesture_pinch_on_impl_thread_);
319 gesture_pinch_on_impl_thread_ = true;
320 return DID_HANDLE;
321 } else if (event.type == WebInputEvent::GesturePinchEnd) {
322 DCHECK(gesture_pinch_on_impl_thread_);
323 gesture_pinch_on_impl_thread_ = false;
324 input_handler_->PinchGestureEnd();
325 return DID_HANDLE;
326 } else if (event.type == WebInputEvent::GesturePinchUpdate) {
327 DCHECK(gesture_pinch_on_impl_thread_);
328 const WebGestureEvent& gesture_event =
329 *static_cast<const WebGestureEvent*>(&event);
330 input_handler_->PinchGestureUpdate(
331 gesture_event.data.pinchUpdate.scale,
332 gfx::Point(gesture_event.x, gesture_event.y));
333 return DID_HANDLE;
334 } else if (event.type == WebInputEvent::GestureFlingStart) {
335 const WebGestureEvent& gesture_event =
336 *static_cast<const WebGestureEvent*>(&event);
337 return HandleGestureFling(gesture_event);
338 } else if (event.type == WebInputEvent::GestureFlingCancel) {
339 if (CancelCurrentFling())
340 return DID_HANDLE;
341 else if (!fling_may_be_active_on_main_thread_)
342 return DROP_EVENT;
343 } else if (event.type == WebInputEvent::TouchStart) {
344 const WebTouchEvent& touch_event =
345 *static_cast<const WebTouchEvent*>(&event);
346 for (size_t i = 0; i < touch_event.touchesLength; ++i) {
347 if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
348 continue;
349 if (input_handler_->HaveTouchEventHandlersAt(
350 gfx::Point(touch_event.touches[i].position.x,
351 touch_event.touches[i].position.y))) {
352 return DID_NOT_HANDLE;
355 return DROP_EVENT;
356 } else if (WebInputEvent::isKeyboardEventType(event.type)) {
357 // Only call |CancelCurrentFling()| if a fling was active, as it will
358 // otherwise disrupt an in-progress touch scroll.
359 if (fling_curve_)
360 CancelCurrentFling();
361 } else if (event.type == WebInputEvent::MouseMove) {
362 const WebMouseEvent& mouse_event =
363 *static_cast<const WebMouseEvent*>(&event);
364 // TODO(tony): Ignore when mouse buttons are down?
365 // TODO(davemoore): This should never happen, but bug #326635 showed some
366 // surprising crashes.
367 CHECK(input_handler_);
368 input_handler_->MouseMoveAt(gfx::Point(mouse_event.x, mouse_event.y));
371 return DID_NOT_HANDLE;
374 InputHandlerProxy::EventDisposition
375 InputHandlerProxy::HandleGestureFling(
376 const WebGestureEvent& gesture_event) {
377 cc::InputHandler::ScrollStatus scroll_status;
379 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
380 scroll_status = input_handler_->ScrollBegin(
381 gfx::Point(gesture_event.x, gesture_event.y),
382 cc::InputHandler::NonBubblingGesture);
383 } else {
384 if (!gesture_scroll_on_impl_thread_)
385 scroll_status = cc::InputHandler::ScrollOnMainThread;
386 else
387 scroll_status = input_handler_->FlingScrollBegin();
390 #ifndef NDEBUG
391 expect_scroll_update_end_ = false;
392 #endif
394 switch (scroll_status) {
395 case cc::InputHandler::ScrollStarted: {
396 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad)
397 input_handler_->ScrollEnd();
399 const float vx = gesture_event.data.flingStart.velocityX;
400 const float vy = gesture_event.data.flingStart.velocityY;
401 current_fling_velocity_ = gfx::Vector2dF(vx, vy);
402 fling_curve_.reset(client_->CreateFlingAnimationCurve(
403 gesture_event.sourceDevice,
404 WebFloatPoint(vx, vy),
405 blink::WebSize()));
406 disallow_horizontal_fling_scroll_ = !vx;
407 disallow_vertical_fling_scroll_ = !vy;
408 TRACE_EVENT_ASYNC_BEGIN2("input",
409 "InputHandlerProxy::HandleGestureFling::started",
410 this,
411 "vx",
413 "vy",
414 vy);
415 // Note that the timestamp will only be used to kickstart the animation if
416 // its sufficiently close to the timestamp of the first call |Animate()|.
417 has_fling_animation_started_ = false;
418 fling_parameters_.startTime = gesture_event.timeStampSeconds;
419 fling_parameters_.delta = WebFloatPoint(vx, vy);
420 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
421 fling_parameters_.globalPoint =
422 WebPoint(gesture_event.globalX, gesture_event.globalY);
423 fling_parameters_.modifiers = gesture_event.modifiers;
424 fling_parameters_.sourceDevice = gesture_event.sourceDevice;
425 input_handler_->SetNeedsAnimate();
426 return DID_HANDLE;
428 case cc::InputHandler::ScrollUnknown:
429 case cc::InputHandler::ScrollOnMainThread: {
430 TRACE_EVENT_INSTANT0("input",
431 "InputHandlerProxy::HandleGestureFling::"
432 "scroll_on_main_thread",
433 TRACE_EVENT_SCOPE_THREAD);
434 fling_may_be_active_on_main_thread_ = true;
435 return DID_NOT_HANDLE;
437 case cc::InputHandler::ScrollIgnored: {
438 TRACE_EVENT_INSTANT0(
439 "input",
440 "InputHandlerProxy::HandleGestureFling::ignored",
441 TRACE_EVENT_SCOPE_THREAD);
442 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
443 // We still pass the curve to the main thread if there's nothing
444 // scrollable, in case something
445 // registers a handler before the curve is over.
446 return DID_NOT_HANDLE;
448 return DROP_EVENT;
450 case cc::InputHandler::ScrollStatusCount:
451 NOTREACHED();
452 break;
454 return DID_NOT_HANDLE;
457 bool InputHandlerProxy::FilterInputEventForFlingBoosting(
458 const WebInputEvent& event) {
459 if (!WebInputEvent::isGestureEventType(event.type))
460 return false;
462 if (!fling_curve_) {
463 DCHECK(!deferred_fling_cancel_time_seconds_);
464 return false;
467 const WebGestureEvent& gesture_event =
468 static_cast<const WebGestureEvent&>(event);
469 if (gesture_event.type == WebInputEvent::GestureFlingCancel) {
470 if (current_fling_velocity_.LengthSquared() < kMinBoostFlingSpeedSquare)
471 return false;
473 TRACE_EVENT_INSTANT0("input",
474 "InputHandlerProxy::FlingBoostStart",
475 TRACE_EVENT_SCOPE_THREAD);
476 deferred_fling_cancel_time_seconds_ =
477 event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
478 return true;
481 // A fling is either inactive or is "free spinning", i.e., has yet to be
482 // interrupted by a touch gesture, in which case there is nothing to filter.
483 if (!deferred_fling_cancel_time_seconds_)
484 return false;
486 // Gestures from a different source should immediately interrupt the fling.
487 if (gesture_event.sourceDevice != fling_parameters_.sourceDevice) {
488 CancelCurrentFling();
489 return false;
492 switch (gesture_event.type) {
493 case WebInputEvent::GestureTapCancel:
494 case WebInputEvent::GestureTapDown:
495 return false;
497 case WebInputEvent::GestureScrollBegin:
498 if (!input_handler_->IsCurrentlyScrollingLayerAt(
499 gfx::Point(gesture_event.x, gesture_event.y),
500 fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad
501 ? cc::InputHandler::NonBubblingGesture
502 : cc::InputHandler::Gesture)) {
503 CancelCurrentFling();
504 return false;
507 // TODO(jdduke): Use |gesture_event.data.scrollBegin.delta{X,Y}Hint| to
508 // determine if the ScrollBegin should immediately cancel the fling.
509 ExtendBoostedFlingTimeout(gesture_event);
510 return true;
512 case WebInputEvent::GestureScrollUpdate: {
513 const double time_since_last_boost_event =
514 event.timeStampSeconds - last_fling_boost_event_.timeStampSeconds;
515 if (ShouldSuppressScrollForFlingBoosting(current_fling_velocity_,
516 gesture_event,
517 time_since_last_boost_event)) {
518 ExtendBoostedFlingTimeout(gesture_event);
519 return true;
522 CancelCurrentFling();
523 return false;
526 case WebInputEvent::GestureScrollEnd:
527 // Clear the last fling boost event *prior* to fling cancellation,
528 // preventing insertion of a synthetic GestureScrollBegin.
529 last_fling_boost_event_ = WebGestureEvent();
530 CancelCurrentFling();
531 return true;
533 case WebInputEvent::GestureFlingStart: {
534 DCHECK_EQ(fling_parameters_.sourceDevice, gesture_event.sourceDevice);
536 bool fling_boosted =
537 fling_parameters_.modifiers == gesture_event.modifiers &&
538 ShouldBoostFling(current_fling_velocity_, gesture_event);
540 gfx::Vector2dF new_fling_velocity(
541 gesture_event.data.flingStart.velocityX,
542 gesture_event.data.flingStart.velocityY);
544 if (fling_boosted)
545 current_fling_velocity_ += new_fling_velocity;
546 else
547 current_fling_velocity_ = new_fling_velocity;
549 WebFloatPoint velocity(current_fling_velocity_.x(),
550 current_fling_velocity_.y());
551 deferred_fling_cancel_time_seconds_ = 0;
552 last_fling_boost_event_ = WebGestureEvent();
553 fling_curve_.reset(client_->CreateFlingAnimationCurve(
554 gesture_event.sourceDevice,
555 velocity,
556 blink::WebSize()));
557 fling_parameters_.startTime = gesture_event.timeStampSeconds;
558 fling_parameters_.delta = velocity;
559 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
560 fling_parameters_.globalPoint =
561 WebPoint(gesture_event.globalX, gesture_event.globalY);
563 TRACE_EVENT_INSTANT2("input",
564 fling_boosted ? "InputHandlerProxy::FlingBoosted"
565 : "InputHandlerProxy::FlingReplaced",
566 TRACE_EVENT_SCOPE_THREAD,
567 "vx",
568 current_fling_velocity_.x(),
569 "vy",
570 current_fling_velocity_.y());
572 // The client expects balanced calls between a consumed GestureFlingStart
573 // and |DidStopFlinging()|. TODO(jdduke): Provide a count parameter to
574 // |DidStopFlinging()| and only send after the accumulated fling ends.
575 client_->DidStopFlinging();
576 return true;
579 default:
580 // All other types of gestures (taps, presses, etc...) will complete the
581 // deferred fling cancellation.
582 CancelCurrentFling();
583 return false;
587 void InputHandlerProxy::ExtendBoostedFlingTimeout(
588 const blink::WebGestureEvent& event) {
589 TRACE_EVENT_INSTANT0("input",
590 "InputHandlerProxy::ExtendBoostedFlingTimeout",
591 TRACE_EVENT_SCOPE_THREAD);
592 deferred_fling_cancel_time_seconds_ =
593 event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
594 last_fling_boost_event_ = event;
597 void InputHandlerProxy::Animate(base::TimeTicks time) {
598 if (!fling_curve_)
599 return;
601 double monotonic_time_sec = InSecondsF(time);
603 if (deferred_fling_cancel_time_seconds_ &&
604 monotonic_time_sec > deferred_fling_cancel_time_seconds_) {
605 CancelCurrentFling();
606 return;
609 if (!has_fling_animation_started_) {
610 has_fling_animation_started_ = true;
611 // Guard against invalid, future or sufficiently stale start times, as there
612 // are no guarantees fling event and animation timestamps are compatible.
613 if (!fling_parameters_.startTime ||
614 monotonic_time_sec <= fling_parameters_.startTime ||
615 monotonic_time_sec >= fling_parameters_.startTime +
616 kMaxSecondsFromFlingTimestampToFirstAnimate) {
617 fling_parameters_.startTime = monotonic_time_sec;
618 input_handler_->SetNeedsAnimate();
619 return;
623 bool fling_is_active =
624 fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime,
625 this);
627 if (disallow_vertical_fling_scroll_ && disallow_horizontal_fling_scroll_)
628 fling_is_active = false;
630 if (fling_is_active) {
631 input_handler_->SetNeedsAnimate();
632 } else {
633 TRACE_EVENT_INSTANT0("input",
634 "InputHandlerProxy::animate::flingOver",
635 TRACE_EVENT_SCOPE_THREAD);
636 CancelCurrentFling();
640 void InputHandlerProxy::MainThreadHasStoppedFlinging() {
641 fling_may_be_active_on_main_thread_ = false;
642 client_->DidStopFlinging();
645 void InputHandlerProxy::DidOverscroll(
646 const gfx::PointF& causal_event_viewport_point,
647 const gfx::Vector2dF& accumulated_overscroll,
648 const gfx::Vector2dF& latest_overscroll_delta) {
649 DCHECK(client_);
651 TRACE_EVENT2("input",
652 "InputHandlerProxy::DidOverscroll",
653 "dx",
654 latest_overscroll_delta.x(),
655 "dy",
656 latest_overscroll_delta.y());
658 DidOverscrollParams params;
659 params.accumulated_overscroll = accumulated_overscroll;
660 params.latest_overscroll_delta = latest_overscroll_delta;
661 params.current_fling_velocity =
662 ToClientScrollIncrement(current_fling_velocity_);
663 params.causal_event_viewport_point = causal_event_viewport_point;
665 if (fling_curve_) {
666 static const int kFlingOverscrollThreshold = 1;
667 disallow_horizontal_fling_scroll_ |=
668 std::abs(params.accumulated_overscroll.x()) >=
669 kFlingOverscrollThreshold;
670 disallow_vertical_fling_scroll_ |=
671 std::abs(params.accumulated_overscroll.y()) >=
672 kFlingOverscrollThreshold;
675 client_->DidOverscroll(params);
678 bool InputHandlerProxy::CancelCurrentFling() {
679 if (CancelCurrentFlingWithoutNotifyingClient()) {
680 client_->DidStopFlinging();
681 return true;
683 return false;
686 bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() {
687 bool had_fling_animation = fling_curve_;
688 if (had_fling_animation &&
689 fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) {
690 input_handler_->ScrollEnd();
691 TRACE_EVENT_ASYNC_END0(
692 "input",
693 "InputHandlerProxy::HandleGestureFling::started",
694 this);
697 TRACE_EVENT_INSTANT1("input",
698 "InputHandlerProxy::CancelCurrentFling",
699 TRACE_EVENT_SCOPE_THREAD,
700 "had_fling_animation",
701 had_fling_animation);
702 fling_curve_.reset();
703 has_fling_animation_started_ = false;
704 gesture_scroll_on_impl_thread_ = false;
705 current_fling_velocity_ = gfx::Vector2dF();
706 fling_parameters_ = blink::WebActiveWheelFlingParameters();
708 if (deferred_fling_cancel_time_seconds_) {
709 deferred_fling_cancel_time_seconds_ = 0;
711 WebGestureEvent last_fling_boost_event = last_fling_boost_event_;
712 last_fling_boost_event_ = WebGestureEvent();
713 if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin ||
714 last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) {
715 // Synthesize a GestureScrollBegin, as the original was suppressed.
716 HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event));
720 return had_fling_animation;
723 bool InputHandlerProxy::TouchpadFlingScroll(
724 const WebFloatSize& increment) {
725 WebMouseWheelEvent synthetic_wheel;
726 synthetic_wheel.type = WebInputEvent::MouseWheel;
727 synthetic_wheel.deltaX = increment.width;
728 synthetic_wheel.deltaY = increment.height;
729 synthetic_wheel.hasPreciseScrollingDeltas = true;
730 synthetic_wheel.x = fling_parameters_.point.x;
731 synthetic_wheel.y = fling_parameters_.point.y;
732 synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
733 synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
734 synthetic_wheel.modifiers = fling_parameters_.modifiers;
736 InputHandlerProxy::EventDisposition disposition =
737 HandleInputEvent(synthetic_wheel);
738 switch (disposition) {
739 case DID_HANDLE:
740 return true;
741 case DROP_EVENT:
742 break;
743 case DID_NOT_HANDLE:
744 TRACE_EVENT_INSTANT0("input",
745 "InputHandlerProxy::scrollBy::AbortFling",
746 TRACE_EVENT_SCOPE_THREAD);
747 // If we got a DID_NOT_HANDLE, that means we need to deliver wheels on the
748 // main thread. In this case we need to schedule a commit and transfer the
749 // fling curve over to the main thread and run the rest of the wheels from
750 // there. This can happen when flinging a page that contains a scrollable
751 // subarea that we can't scroll on the thread if the fling starts outside
752 // the subarea but then is flung "under" the pointer.
753 client_->TransferActiveWheelFlingAnimation(fling_parameters_);
754 fling_may_be_active_on_main_thread_ = true;
755 CancelCurrentFlingWithoutNotifyingClient();
756 break;
759 return false;
762 bool InputHandlerProxy::scrollBy(const WebFloatSize& increment,
763 const WebFloatSize& velocity) {
764 WebFloatSize clipped_increment;
765 WebFloatSize clipped_velocity;
766 if (!disallow_horizontal_fling_scroll_) {
767 clipped_increment.width = increment.width;
768 clipped_velocity.width = velocity.width;
770 if (!disallow_vertical_fling_scroll_) {
771 clipped_increment.height = increment.height;
772 clipped_velocity.height = velocity.height;
775 current_fling_velocity_ = clipped_velocity;
777 // Early out if the increment is zero, but avoid early terimination if the
778 // velocity is still non-zero.
779 if (clipped_increment == WebFloatSize())
780 return clipped_velocity != WebFloatSize();
782 TRACE_EVENT2("input",
783 "InputHandlerProxy::scrollBy",
784 "x",
785 clipped_increment.width,
786 "y",
787 clipped_increment.height);
789 bool did_scroll = false;
791 switch (fling_parameters_.sourceDevice) {
792 case blink::WebGestureDeviceTouchpad:
793 did_scroll = TouchpadFlingScroll(clipped_increment);
794 break;
795 case blink::WebGestureDeviceTouchscreen:
796 clipped_increment = ToClientScrollIncrement(clipped_increment);
797 did_scroll = input_handler_->ScrollBy(fling_parameters_.point,
798 clipped_increment);
799 break;
802 if (did_scroll) {
803 fling_parameters_.cumulativeScroll.width += clipped_increment.width;
804 fling_parameters_.cumulativeScroll.height += clipped_increment.height;
807 // It's possible the provided |increment| is sufficiently small as to not
808 // trigger a scroll, e.g., with a trivial time delta between fling updates.
809 // Return true in this case to prevent early fling termination.
810 if (std::abs(clipped_increment.width) < kScrollEpsilon &&
811 std::abs(clipped_increment.height) < kScrollEpsilon)
812 return true;
814 return did_scroll;
817 } // namespace content