Remove aura enum from DesktopMediaID to fix desktop mirroring audio (CrOS).
[chromium-blink-merge.git] / content / renderer / input / input_handler_proxy.cc
blob232d8b7baf0528718f311b80e80f1d6a53620035
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/location.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/trace_event/trace_event.h"
15 #include "content/common/input/did_overscroll_params.h"
16 #include "content/common/input/web_input_event_traits.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/renderer/input/input_handler_proxy_client.h"
19 #include "content/renderer/input/input_scroll_elasticity_controller.h"
20 #include "third_party/WebKit/public/platform/Platform.h"
21 #include "third_party/WebKit/public/web/WebInputEvent.h"
22 #include "ui/events/latency_info.h"
23 #include "ui/gfx/geometry/point_conversions.h"
25 using blink::WebFloatPoint;
26 using blink::WebFloatSize;
27 using blink::WebGestureEvent;
28 using blink::WebInputEvent;
29 using blink::WebMouseEvent;
30 using blink::WebMouseWheelEvent;
31 using blink::WebPoint;
32 using blink::WebTouchEvent;
33 using blink::WebTouchPoint;
35 namespace {
37 // Maximum time between a fling event's timestamp and the first |Animate| call
38 // for the fling curve to use the fling timestamp as the initial animation time.
39 // Two frames allows a minor delay between event creation and the first animate.
40 const double kMaxSecondsFromFlingTimestampToFirstAnimate = 2. / 60.;
42 // Threshold for determining whether a fling scroll delta should have caused the
43 // client to scroll.
44 const float kScrollEpsilon = 0.1f;
46 // Minimum fling velocity required for the active fling and new fling for the
47 // two to accumulate.
48 const double kMinBoostFlingSpeedSquare = 350. * 350.;
50 // Minimum velocity for the active touch scroll to preserve (boost) an active
51 // fling for which cancellation has been deferred.
52 const double kMinBoostTouchScrollSpeedSquare = 150 * 150.;
54 // Timeout window after which the active fling will be cancelled if no scrolls
55 // or flings of sufficient velocity relative to the current fling are received.
56 // The default value on Android native views is 40ms, but we use a slightly
57 // increased value to accomodate small IPC message delays.
58 const double kFlingBoostTimeoutDelaySeconds = 0.045;
60 gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
61 return gfx::Vector2dF(-increment.width, -increment.height);
64 double InSecondsF(const base::TimeTicks& time) {
65 return (time - base::TimeTicks()).InSecondsF();
68 bool ShouldSuppressScrollForFlingBoosting(
69 const gfx::Vector2dF& current_fling_velocity,
70 const WebGestureEvent& scroll_update_event,
71 double time_since_last_boost_event) {
72 DCHECK_EQ(WebInputEvent::GestureScrollUpdate, scroll_update_event.type);
74 gfx::Vector2dF dx(scroll_update_event.data.scrollUpdate.deltaX,
75 scroll_update_event.data.scrollUpdate.deltaY);
76 if (gfx::DotProduct(current_fling_velocity, dx) <= 0)
77 return false;
79 if (time_since_last_boost_event < 0.001)
80 return true;
82 // TODO(jdduke): Use |scroll_update_event.data.scrollUpdate.velocity{X,Y}|.
83 // The scroll must be of sufficient velocity to maintain the active fling.
84 const gfx::Vector2dF scroll_velocity =
85 gfx::ScaleVector2d(dx, 1. / time_since_last_boost_event);
86 if (scroll_velocity.LengthSquared() < kMinBoostTouchScrollSpeedSquare)
87 return false;
89 return true;
92 bool ShouldBoostFling(const gfx::Vector2dF& current_fling_velocity,
93 const WebGestureEvent& fling_start_event) {
94 DCHECK_EQ(WebInputEvent::GestureFlingStart, fling_start_event.type);
96 gfx::Vector2dF new_fling_velocity(
97 fling_start_event.data.flingStart.velocityX,
98 fling_start_event.data.flingStart.velocityY);
100 if (gfx::DotProduct(current_fling_velocity, new_fling_velocity) <= 0)
101 return false;
103 if (current_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
104 return false;
106 if (new_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
107 return false;
109 return true;
112 WebGestureEvent ObtainGestureScrollBegin(const WebGestureEvent& event) {
113 WebGestureEvent scroll_begin_event = event;
114 scroll_begin_event.type = WebInputEvent::GestureScrollBegin;
115 scroll_begin_event.data.scrollBegin.deltaXHint = 0;
116 scroll_begin_event.data.scrollBegin.deltaYHint = 0;
117 return scroll_begin_event;
120 void ReportInputEventLatencyUma(const WebInputEvent& event,
121 const ui::LatencyInfo& latency_info) {
122 if (!(event.type == WebInputEvent::GestureScrollBegin ||
123 event.type == WebInputEvent::GestureScrollUpdate ||
124 event.type == WebInputEvent::GesturePinchBegin ||
125 event.type == WebInputEvent::GesturePinchUpdate ||
126 event.type == WebInputEvent::GestureFlingStart)) {
127 return;
130 ui::LatencyInfo::LatencyMap::const_iterator it =
131 latency_info.latency_components.find(std::make_pair(
132 ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0));
134 if (it == latency_info.latency_components.end())
135 return;
137 base::TimeDelta delta = base::TimeTicks::Now() - it->second.event_time;
138 for (size_t i = 0; i < it->second.event_count; ++i) {
139 switch (event.type) {
140 case blink::WebInputEvent::GestureScrollBegin:
141 UMA_HISTOGRAM_CUSTOM_COUNTS(
142 "Event.Latency.RendererImpl.GestureScrollBegin",
143 delta.InMicroseconds(), 1, 1000000, 100);
144 break;
145 case blink::WebInputEvent::GestureScrollUpdate:
146 UMA_HISTOGRAM_CUSTOM_COUNTS(
147 // So named for historical reasons.
148 "Event.Latency.RendererImpl.GestureScroll2",
149 delta.InMicroseconds(), 1, 1000000, 100);
150 break;
151 case blink::WebInputEvent::GesturePinchBegin:
152 UMA_HISTOGRAM_CUSTOM_COUNTS(
153 "Event.Latency.RendererImpl.GesturePinchBegin",
154 delta.InMicroseconds(), 1, 1000000, 100);
155 break;
156 case blink::WebInputEvent::GesturePinchUpdate:
157 UMA_HISTOGRAM_CUSTOM_COUNTS(
158 "Event.Latency.RendererImpl.GesturePinchUpdate",
159 delta.InMicroseconds(), 1, 1000000, 100);
160 break;
161 case blink::WebInputEvent::GestureFlingStart:
162 UMA_HISTOGRAM_CUSTOM_COUNTS(
163 "Event.Latency.RendererImpl.GestureFlingStart",
164 delta.InMicroseconds(), 1, 1000000, 100);
165 break;
166 default:
167 NOTREACHED();
168 break;
173 } // namespace
175 namespace content {
177 InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler,
178 InputHandlerProxyClient* client)
179 : client_(client),
180 input_handler_(input_handler),
181 deferred_fling_cancel_time_seconds_(0),
182 #ifndef NDEBUG
183 expect_scroll_update_end_(false),
184 #endif
185 gesture_scroll_on_impl_thread_(false),
186 gesture_pinch_on_impl_thread_(false),
187 fling_may_be_active_on_main_thread_(false),
188 disallow_horizontal_fling_scroll_(false),
189 disallow_vertical_fling_scroll_(false),
190 has_fling_animation_started_(false),
191 uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()) {
192 DCHECK(client);
193 input_handler_->BindToClient(this);
194 smooth_scroll_enabled_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
195 switches::kEnableSmoothScrolling);
196 cc::ScrollElasticityHelper* scroll_elasticity_helper =
197 input_handler_->CreateScrollElasticityHelper();
198 if (scroll_elasticity_helper) {
199 scroll_elasticity_controller_.reset(
200 new InputScrollElasticityController(scroll_elasticity_helper));
204 InputHandlerProxy::~InputHandlerProxy() {}
206 void InputHandlerProxy::WillShutdown() {
207 scroll_elasticity_controller_.reset();
208 input_handler_ = NULL;
209 client_->WillShutdown();
212 InputHandlerProxy::EventDisposition
213 InputHandlerProxy::HandleInputEventWithLatencyInfo(
214 const WebInputEvent& event,
215 ui::LatencyInfo* latency_info) {
216 DCHECK(input_handler_);
218 if (uma_latency_reporting_enabled_)
219 ReportInputEventLatencyUma(event, *latency_info);
221 TRACE_EVENT_FLOW_STEP0("input,benchmark",
222 "LatencyInfo.Flow",
223 TRACE_ID_DONT_MANGLE(latency_info->trace_id),
224 "HandleInputEventImpl");
226 scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
227 input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info);
228 InputHandlerProxy::EventDisposition disposition = HandleInputEvent(event);
229 return disposition;
232 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent(
233 const WebInputEvent& event) {
234 DCHECK(input_handler_);
235 TRACE_EVENT1("input,benchmark", "InputHandlerProxy::HandleInputEvent",
236 "type", WebInputEventTraits::GetName(event.type));
238 if (FilterInputEventForFlingBoosting(event))
239 return DID_HANDLE;
241 switch (event.type) {
242 case WebInputEvent::MouseWheel:
243 return HandleMouseWheel(static_cast<const WebMouseWheelEvent&>(event));
245 case WebInputEvent::GestureScrollBegin:
246 return HandleGestureScrollBegin(
247 static_cast<const WebGestureEvent&>(event));
249 case WebInputEvent::GestureScrollUpdate:
250 return HandleGestureScrollUpdate(
251 static_cast<const WebGestureEvent&>(event));
253 case WebInputEvent::GestureScrollEnd:
254 return HandleGestureScrollEnd(static_cast<const WebGestureEvent&>(event));
256 case WebInputEvent::GesturePinchBegin: {
257 DCHECK(!gesture_pinch_on_impl_thread_);
258 const WebGestureEvent& gesture_event =
259 static_cast<const WebGestureEvent&>(event);
260 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad &&
261 input_handler_->HaveWheelEventHandlersAt(
262 gfx::Point(gesture_event.x, gesture_event.y))) {
263 return DID_NOT_HANDLE;
264 } else {
265 input_handler_->PinchGestureBegin();
266 gesture_pinch_on_impl_thread_ = true;
267 return DID_HANDLE;
271 case WebInputEvent::GesturePinchEnd:
272 if (gesture_pinch_on_impl_thread_) {
273 gesture_pinch_on_impl_thread_ = false;
274 input_handler_->PinchGestureEnd();
275 return DID_HANDLE;
276 } else {
277 return DID_NOT_HANDLE;
280 case WebInputEvent::GesturePinchUpdate: {
281 if (gesture_pinch_on_impl_thread_) {
282 const WebGestureEvent& gesture_event =
283 static_cast<const WebGestureEvent&>(event);
284 if (gesture_event.data.pinchUpdate.zoomDisabled)
285 return DROP_EVENT;
286 input_handler_->PinchGestureUpdate(
287 gesture_event.data.pinchUpdate.scale,
288 gfx::Point(gesture_event.x, gesture_event.y));
289 return DID_HANDLE;
290 } else {
291 return DID_NOT_HANDLE;
295 case WebInputEvent::GestureFlingStart:
296 return HandleGestureFlingStart(
297 *static_cast<const WebGestureEvent*>(&event));
299 case WebInputEvent::GestureFlingCancel:
300 if (CancelCurrentFling())
301 return DID_HANDLE;
302 else if (!fling_may_be_active_on_main_thread_)
303 return DROP_EVENT;
304 return DID_NOT_HANDLE;
306 case WebInputEvent::TouchStart:
307 return HandleTouchStart(static_cast<const WebTouchEvent&>(event));
309 case WebInputEvent::MouseMove: {
310 const WebMouseEvent& mouse_event =
311 static_cast<const WebMouseEvent&>(event);
312 // TODO(tony): Ignore when mouse buttons are down?
313 // TODO(davemoore): This should never happen, but bug #326635 showed some
314 // surprising crashes.
315 CHECK(input_handler_);
316 input_handler_->MouseMoveAt(gfx::Point(mouse_event.x, mouse_event.y));
317 return DID_NOT_HANDLE;
320 default:
321 if (WebInputEvent::isKeyboardEventType(event.type)) {
322 // Only call |CancelCurrentFling()| if a fling was active, as it will
323 // otherwise disrupt an in-progress touch scroll.
324 if (fling_curve_)
325 CancelCurrentFling();
327 break;
330 return DID_NOT_HANDLE;
333 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel(
334 const WebMouseWheelEvent& wheel_event) {
335 InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE;
336 cc::InputHandlerScrollResult scroll_result;
338 // TODO(ccameron): The rail information should be pushed down into
339 // InputHandler.
340 gfx::Vector2dF scroll_delta(
341 wheel_event.railsMode != WebInputEvent::RailsModeVertical
342 ? -wheel_event.deltaX
343 : 0,
344 wheel_event.railsMode != WebInputEvent::RailsModeHorizontal
345 ? -wheel_event.deltaY
346 : 0);
348 if (wheel_event.scrollByPage) {
349 // TODO(jamesr): We don't properly handle scroll by page in the compositor
350 // thread, so punt it to the main thread. http://crbug.com/236639
351 result = DID_NOT_HANDLE;
352 } else if (!wheel_event.canScroll) {
353 // Wheel events with |canScroll| == false will not trigger scrolling,
354 // only event handlers. Forward to the main thread.
355 result = DID_NOT_HANDLE;
356 } else if (smooth_scroll_enabled_) {
357 cc::InputHandler::ScrollStatus scroll_status =
358 input_handler_->ScrollAnimated(gfx::Point(wheel_event.x, wheel_event.y),
359 scroll_delta);
360 switch (scroll_status) {
361 case cc::InputHandler::SCROLL_STARTED:
362 result = DID_HANDLE;
363 break;
364 case cc::InputHandler::SCROLL_IGNORED:
365 result = DROP_EVENT;
366 default:
367 result = DID_NOT_HANDLE;
368 break;
370 } else {
371 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
372 gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::WHEEL);
373 switch (scroll_status) {
374 case cc::InputHandler::SCROLL_STARTED: {
375 TRACE_EVENT_INSTANT2("input",
376 "InputHandlerProxy::handle_input wheel scroll",
377 TRACE_EVENT_SCOPE_THREAD, "deltaX",
378 scroll_delta.x(), "deltaY", scroll_delta.y());
379 gfx::Point scroll_point(wheel_event.x, wheel_event.y);
380 scroll_result = input_handler_->ScrollBy(scroll_point, scroll_delta);
381 HandleOverscroll(scroll_point, scroll_result);
382 input_handler_->ScrollEnd();
383 result = scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
384 break;
386 case cc::InputHandler::SCROLL_IGNORED:
387 // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
388 // to properly sync scrollability it's safer to send the event to the
389 // main thread. Change back to DROP_EVENT once we have synchronization
390 // bugs sorted out.
391 result = DID_NOT_HANDLE;
392 break;
393 case cc::InputHandler::SCROLL_UNKNOWN:
394 case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
395 result = DID_NOT_HANDLE;
396 break;
397 case cc::InputHandler::ScrollStatusCount:
398 NOTREACHED();
399 break;
403 // Send the event and its disposition to the elasticity controller to update
404 // the over-scroll animation. If the event is to be handled on the main
405 // thread, the event and its disposition will be sent to the elasticity
406 // controller after being handled on the main thread.
407 if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE) {
408 // Note that the call to the elasticity controller is made asynchronously,
409 // to minimize divergence between main thread and impl thread event
410 // handling paths.
411 base::ThreadTaskRunnerHandle::Get()->PostTask(
412 FROM_HERE,
413 base::Bind(&InputScrollElasticityController::ObserveWheelEventAndResult,
414 scroll_elasticity_controller_->GetWeakPtr(), wheel_event,
415 scroll_result));
417 return result;
420 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
421 const WebGestureEvent& gesture_event) {
422 if (gesture_scroll_on_impl_thread_)
423 CancelCurrentFling();
425 #ifndef NDEBUG
426 DCHECK(!expect_scroll_update_end_);
427 expect_scroll_update_end_ = true;
428 #endif
429 cc::InputHandler::ScrollStatus scroll_status;
430 if (gesture_event.data.scrollBegin.targetViewport) {
431 scroll_status = input_handler_->RootScrollBegin(cc::InputHandler::GESTURE);
432 } else {
433 scroll_status = input_handler_->ScrollBegin(
434 gfx::Point(gesture_event.x, gesture_event.y),
435 cc::InputHandler::GESTURE);
437 UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult",
438 scroll_status,
439 cc::InputHandler::ScrollStatusCount);
440 switch (scroll_status) {
441 case cc::InputHandler::SCROLL_STARTED:
442 TRACE_EVENT_INSTANT0("input",
443 "InputHandlerProxy::handle_input gesture scroll",
444 TRACE_EVENT_SCOPE_THREAD);
445 gesture_scroll_on_impl_thread_ = true;
446 return DID_HANDLE;
447 case cc::InputHandler::SCROLL_UNKNOWN:
448 case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
449 return DID_NOT_HANDLE;
450 case cc::InputHandler::SCROLL_IGNORED:
451 return DROP_EVENT;
452 case cc::InputHandler::ScrollStatusCount:
453 NOTREACHED();
454 break;
456 return DID_NOT_HANDLE;
459 InputHandlerProxy::EventDisposition
460 InputHandlerProxy::HandleGestureScrollUpdate(
461 const WebGestureEvent& gesture_event) {
462 #ifndef NDEBUG
463 DCHECK(expect_scroll_update_end_);
464 #endif
466 if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
467 return DID_NOT_HANDLE;
469 gfx::Point scroll_point(gesture_event.x, gesture_event.y);
470 gfx::Vector2dF scroll_delta(-gesture_event.data.scrollUpdate.deltaX,
471 -gesture_event.data.scrollUpdate.deltaY);
472 cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(
473 scroll_point, scroll_delta);
474 HandleOverscroll(scroll_point, scroll_result);
475 return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
478 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
479 const WebGestureEvent& gesture_event) {
480 #ifndef NDEBUG
481 DCHECK(expect_scroll_update_end_);
482 expect_scroll_update_end_ = false;
483 #endif
484 input_handler_->ScrollEnd();
485 if (!gesture_scroll_on_impl_thread_)
486 return DID_NOT_HANDLE;
487 gesture_scroll_on_impl_thread_ = false;
488 return DID_HANDLE;
491 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart(
492 const WebGestureEvent& gesture_event) {
493 cc::InputHandler::ScrollStatus scroll_status;
495 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
496 if (gesture_event.data.flingStart.targetViewport) {
497 scroll_status = input_handler_->RootScrollBegin(
498 cc::InputHandler::NON_BUBBLING_GESTURE);
499 } else {
500 scroll_status = input_handler_->ScrollBegin(
501 gfx::Point(gesture_event.x, gesture_event.y),
502 cc::InputHandler::NON_BUBBLING_GESTURE);
504 } else {
505 if (!gesture_scroll_on_impl_thread_)
506 scroll_status = cc::InputHandler::SCROLL_ON_MAIN_THREAD;
507 else
508 scroll_status = input_handler_->FlingScrollBegin();
511 #ifndef NDEBUG
512 expect_scroll_update_end_ = false;
513 #endif
515 switch (scroll_status) {
516 case cc::InputHandler::SCROLL_STARTED: {
517 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad)
518 input_handler_->ScrollEnd();
520 const float vx = gesture_event.data.flingStart.velocityX;
521 const float vy = gesture_event.data.flingStart.velocityY;
522 current_fling_velocity_ = gfx::Vector2dF(vx, vy);
523 DCHECK(!current_fling_velocity_.IsZero());
524 fling_curve_.reset(client_->CreateFlingAnimationCurve(
525 gesture_event.sourceDevice,
526 WebFloatPoint(vx, vy),
527 blink::WebSize()));
528 disallow_horizontal_fling_scroll_ = !vx;
529 disallow_vertical_fling_scroll_ = !vy;
530 TRACE_EVENT_ASYNC_BEGIN2("input",
531 "InputHandlerProxy::HandleGestureFling::started",
532 this,
533 "vx",
535 "vy",
536 vy);
537 // Note that the timestamp will only be used to kickstart the animation if
538 // its sufficiently close to the timestamp of the first call |Animate()|.
539 has_fling_animation_started_ = false;
540 fling_parameters_.startTime = gesture_event.timeStampSeconds;
541 fling_parameters_.delta = WebFloatPoint(vx, vy);
542 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
543 fling_parameters_.globalPoint =
544 WebPoint(gesture_event.globalX, gesture_event.globalY);
545 fling_parameters_.modifiers = gesture_event.modifiers;
546 fling_parameters_.sourceDevice = gesture_event.sourceDevice;
547 input_handler_->SetNeedsAnimateInput();
548 return DID_HANDLE;
550 case cc::InputHandler::SCROLL_UNKNOWN:
551 case cc::InputHandler::SCROLL_ON_MAIN_THREAD: {
552 TRACE_EVENT_INSTANT0("input",
553 "InputHandlerProxy::HandleGestureFling::"
554 "scroll_on_main_thread",
555 TRACE_EVENT_SCOPE_THREAD);
556 gesture_scroll_on_impl_thread_ = false;
557 fling_may_be_active_on_main_thread_ = true;
558 return DID_NOT_HANDLE;
560 case cc::InputHandler::SCROLL_IGNORED: {
561 TRACE_EVENT_INSTANT0(
562 "input",
563 "InputHandlerProxy::HandleGestureFling::ignored",
564 TRACE_EVENT_SCOPE_THREAD);
565 gesture_scroll_on_impl_thread_ = false;
566 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
567 // We still pass the curve to the main thread if there's nothing
568 // scrollable, in case something
569 // registers a handler before the curve is over.
570 return DID_NOT_HANDLE;
572 return DROP_EVENT;
574 case cc::InputHandler::ScrollStatusCount:
575 NOTREACHED();
576 break;
578 return DID_NOT_HANDLE;
581 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart(
582 const blink::WebTouchEvent& touch_event) {
583 for (size_t i = 0; i < touch_event.touchesLength; ++i) {
584 if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
585 continue;
586 if (input_handler_->DoTouchEventsBlockScrollAt(
587 gfx::Point(touch_event.touches[i].position.x,
588 touch_event.touches[i].position.y))) {
589 // TODO(rbyers): We should consider still sending the touch events to
590 // main asynchronously (crbug.com/455539).
591 return DID_NOT_HANDLE;
594 return DROP_EVENT;
597 bool InputHandlerProxy::FilterInputEventForFlingBoosting(
598 const WebInputEvent& event) {
599 if (!WebInputEvent::isGestureEventType(event.type))
600 return false;
602 if (!fling_curve_) {
603 DCHECK(!deferred_fling_cancel_time_seconds_);
604 return false;
607 const WebGestureEvent& gesture_event =
608 static_cast<const WebGestureEvent&>(event);
609 if (gesture_event.type == WebInputEvent::GestureFlingCancel) {
610 if (gesture_event.data.flingCancel.preventBoosting)
611 return false;
613 if (current_fling_velocity_.LengthSquared() < kMinBoostFlingSpeedSquare)
614 return false;
616 TRACE_EVENT_INSTANT0("input",
617 "InputHandlerProxy::FlingBoostStart",
618 TRACE_EVENT_SCOPE_THREAD);
619 deferred_fling_cancel_time_seconds_ =
620 event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
621 return true;
624 // A fling is either inactive or is "free spinning", i.e., has yet to be
625 // interrupted by a touch gesture, in which case there is nothing to filter.
626 if (!deferred_fling_cancel_time_seconds_)
627 return false;
629 // Gestures from a different source should immediately interrupt the fling.
630 if (gesture_event.sourceDevice != fling_parameters_.sourceDevice) {
631 CancelCurrentFling();
632 return false;
635 switch (gesture_event.type) {
636 case WebInputEvent::GestureTapCancel:
637 case WebInputEvent::GestureTapDown:
638 return false;
640 case WebInputEvent::GestureScrollBegin:
641 if (!input_handler_->IsCurrentlyScrollingLayerAt(
642 gfx::Point(gesture_event.x, gesture_event.y),
643 fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad
644 ? cc::InputHandler::NON_BUBBLING_GESTURE
645 : cc::InputHandler::GESTURE)) {
646 CancelCurrentFling();
647 return false;
650 // TODO(jdduke): Use |gesture_event.data.scrollBegin.delta{X,Y}Hint| to
651 // determine if the ScrollBegin should immediately cancel the fling.
652 ExtendBoostedFlingTimeout(gesture_event);
653 return true;
655 case WebInputEvent::GestureScrollUpdate: {
656 const double time_since_last_boost_event =
657 event.timeStampSeconds - last_fling_boost_event_.timeStampSeconds;
658 if (ShouldSuppressScrollForFlingBoosting(current_fling_velocity_,
659 gesture_event,
660 time_since_last_boost_event)) {
661 ExtendBoostedFlingTimeout(gesture_event);
662 return true;
665 CancelCurrentFling();
666 return false;
669 case WebInputEvent::GestureScrollEnd:
670 // Clear the last fling boost event *prior* to fling cancellation,
671 // preventing insertion of a synthetic GestureScrollBegin.
672 last_fling_boost_event_ = WebGestureEvent();
673 CancelCurrentFling();
674 return true;
676 case WebInputEvent::GestureFlingStart: {
677 DCHECK_EQ(fling_parameters_.sourceDevice, gesture_event.sourceDevice);
679 bool fling_boosted =
680 fling_parameters_.modifiers == gesture_event.modifiers &&
681 ShouldBoostFling(current_fling_velocity_, gesture_event);
683 gfx::Vector2dF new_fling_velocity(
684 gesture_event.data.flingStart.velocityX,
685 gesture_event.data.flingStart.velocityY);
686 DCHECK(!new_fling_velocity.IsZero());
688 if (fling_boosted)
689 current_fling_velocity_ += new_fling_velocity;
690 else
691 current_fling_velocity_ = new_fling_velocity;
693 WebFloatPoint velocity(current_fling_velocity_.x(),
694 current_fling_velocity_.y());
695 deferred_fling_cancel_time_seconds_ = 0;
696 disallow_horizontal_fling_scroll_ = !velocity.x;
697 disallow_vertical_fling_scroll_ = !velocity.y;
698 last_fling_boost_event_ = WebGestureEvent();
699 fling_curve_.reset(client_->CreateFlingAnimationCurve(
700 gesture_event.sourceDevice,
701 velocity,
702 blink::WebSize()));
703 fling_parameters_.startTime = gesture_event.timeStampSeconds;
704 fling_parameters_.delta = velocity;
705 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
706 fling_parameters_.globalPoint =
707 WebPoint(gesture_event.globalX, gesture_event.globalY);
709 TRACE_EVENT_INSTANT2("input",
710 fling_boosted ? "InputHandlerProxy::FlingBoosted"
711 : "InputHandlerProxy::FlingReplaced",
712 TRACE_EVENT_SCOPE_THREAD,
713 "vx",
714 current_fling_velocity_.x(),
715 "vy",
716 current_fling_velocity_.y());
718 // The client expects balanced calls between a consumed GestureFlingStart
719 // and |DidStopFlinging()|. TODO(jdduke): Provide a count parameter to
720 // |DidStopFlinging()| and only send after the accumulated fling ends.
721 client_->DidStopFlinging();
722 return true;
725 default:
726 // All other types of gestures (taps, presses, etc...) will complete the
727 // deferred fling cancellation.
728 CancelCurrentFling();
729 return false;
733 void InputHandlerProxy::ExtendBoostedFlingTimeout(
734 const blink::WebGestureEvent& event) {
735 TRACE_EVENT_INSTANT0("input",
736 "InputHandlerProxy::ExtendBoostedFlingTimeout",
737 TRACE_EVENT_SCOPE_THREAD);
738 deferred_fling_cancel_time_seconds_ =
739 event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
740 last_fling_boost_event_ = event;
743 void InputHandlerProxy::Animate(base::TimeTicks time) {
744 if (scroll_elasticity_controller_)
745 scroll_elasticity_controller_->Animate(time);
747 if (!fling_curve_)
748 return;
750 double monotonic_time_sec = InSecondsF(time);
752 if (deferred_fling_cancel_time_seconds_ &&
753 monotonic_time_sec > deferred_fling_cancel_time_seconds_) {
754 CancelCurrentFling();
755 return;
758 client_->DidAnimateForInput();
760 if (!has_fling_animation_started_) {
761 has_fling_animation_started_ = true;
762 // Guard against invalid, future or sufficiently stale start times, as there
763 // are no guarantees fling event and animation timestamps are compatible.
764 if (!fling_parameters_.startTime ||
765 monotonic_time_sec <= fling_parameters_.startTime ||
766 monotonic_time_sec >= fling_parameters_.startTime +
767 kMaxSecondsFromFlingTimestampToFirstAnimate) {
768 fling_parameters_.startTime = monotonic_time_sec;
769 input_handler_->SetNeedsAnimateInput();
770 return;
774 bool fling_is_active =
775 fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime,
776 this);
778 if (disallow_vertical_fling_scroll_ && disallow_horizontal_fling_scroll_)
779 fling_is_active = false;
781 if (fling_is_active) {
782 input_handler_->SetNeedsAnimateInput();
783 } else {
784 TRACE_EVENT_INSTANT0("input",
785 "InputHandlerProxy::animate::flingOver",
786 TRACE_EVENT_SCOPE_THREAD);
787 CancelCurrentFling();
791 void InputHandlerProxy::MainThreadHasStoppedFlinging() {
792 fling_may_be_active_on_main_thread_ = false;
793 client_->DidStopFlinging();
796 void InputHandlerProxy::ReconcileElasticOverscrollAndRootScroll() {
797 if (scroll_elasticity_controller_)
798 scroll_elasticity_controller_->ReconcileStretchAndScroll();
801 void InputHandlerProxy::HandleOverscroll(
802 const gfx::Point& causal_event_viewport_point,
803 const cc::InputHandlerScrollResult& scroll_result) {
804 DCHECK(client_);
805 if (!scroll_result.did_overscroll_root)
806 return;
808 TRACE_EVENT2("input",
809 "InputHandlerProxy::DidOverscroll",
810 "dx",
811 scroll_result.unused_scroll_delta.x(),
812 "dy",
813 scroll_result.unused_scroll_delta.y());
815 DidOverscrollParams params;
816 params.accumulated_overscroll = scroll_result.accumulated_root_overscroll;
817 params.latest_overscroll_delta = scroll_result.unused_scroll_delta;
818 params.current_fling_velocity =
819 ToClientScrollIncrement(current_fling_velocity_);
820 params.causal_event_viewport_point = causal_event_viewport_point;
822 if (fling_curve_) {
823 static const int kFlingOverscrollThreshold = 1;
824 disallow_horizontal_fling_scroll_ |=
825 std::abs(params.accumulated_overscroll.x()) >=
826 kFlingOverscrollThreshold;
827 disallow_vertical_fling_scroll_ |=
828 std::abs(params.accumulated_overscroll.y()) >=
829 kFlingOverscrollThreshold;
832 client_->DidOverscroll(params);
835 bool InputHandlerProxy::CancelCurrentFling() {
836 if (CancelCurrentFlingWithoutNotifyingClient()) {
837 client_->DidStopFlinging();
838 return true;
840 return false;
843 bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() {
844 bool had_fling_animation = fling_curve_;
845 if (had_fling_animation &&
846 fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) {
847 input_handler_->ScrollEnd();
848 TRACE_EVENT_ASYNC_END0(
849 "input",
850 "InputHandlerProxy::HandleGestureFling::started",
851 this);
854 TRACE_EVENT_INSTANT1("input",
855 "InputHandlerProxy::CancelCurrentFling",
856 TRACE_EVENT_SCOPE_THREAD,
857 "had_fling_animation",
858 had_fling_animation);
859 fling_curve_.reset();
860 has_fling_animation_started_ = false;
861 gesture_scroll_on_impl_thread_ = false;
862 current_fling_velocity_ = gfx::Vector2dF();
863 fling_parameters_ = blink::WebActiveWheelFlingParameters();
865 if (deferred_fling_cancel_time_seconds_) {
866 deferred_fling_cancel_time_seconds_ = 0;
868 WebGestureEvent last_fling_boost_event = last_fling_boost_event_;
869 last_fling_boost_event_ = WebGestureEvent();
870 if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin ||
871 last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) {
872 // Synthesize a GestureScrollBegin, as the original was suppressed.
873 HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event));
877 return had_fling_animation;
880 bool InputHandlerProxy::TouchpadFlingScroll(
881 const WebFloatSize& increment) {
882 WebMouseWheelEvent synthetic_wheel;
883 synthetic_wheel.type = WebInputEvent::MouseWheel;
884 synthetic_wheel.deltaX = increment.width;
885 synthetic_wheel.deltaY = increment.height;
886 synthetic_wheel.hasPreciseScrollingDeltas = true;
887 synthetic_wheel.x = fling_parameters_.point.x;
888 synthetic_wheel.y = fling_parameters_.point.y;
889 synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
890 synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
891 synthetic_wheel.modifiers = fling_parameters_.modifiers;
893 InputHandlerProxy::EventDisposition disposition =
894 HandleInputEvent(synthetic_wheel);
895 switch (disposition) {
896 case DID_HANDLE:
897 return true;
898 case DROP_EVENT:
899 break;
900 case DID_NOT_HANDLE:
901 TRACE_EVENT_INSTANT0("input",
902 "InputHandlerProxy::scrollBy::AbortFling",
903 TRACE_EVENT_SCOPE_THREAD);
904 // If we got a DID_NOT_HANDLE, that means we need to deliver wheels on the
905 // main thread. In this case we need to schedule a commit and transfer the
906 // fling curve over to the main thread and run the rest of the wheels from
907 // there. This can happen when flinging a page that contains a scrollable
908 // subarea that we can't scroll on the thread if the fling starts outside
909 // the subarea but then is flung "under" the pointer.
910 client_->TransferActiveWheelFlingAnimation(fling_parameters_);
911 fling_may_be_active_on_main_thread_ = true;
912 CancelCurrentFlingWithoutNotifyingClient();
913 break;
916 return false;
919 bool InputHandlerProxy::scrollBy(const WebFloatSize& increment,
920 const WebFloatSize& velocity) {
921 WebFloatSize clipped_increment;
922 WebFloatSize clipped_velocity;
923 if (!disallow_horizontal_fling_scroll_) {
924 clipped_increment.width = increment.width;
925 clipped_velocity.width = velocity.width;
927 if (!disallow_vertical_fling_scroll_) {
928 clipped_increment.height = increment.height;
929 clipped_velocity.height = velocity.height;
932 current_fling_velocity_ = clipped_velocity;
934 // Early out if the increment is zero, but avoid early terimination if the
935 // velocity is still non-zero.
936 if (clipped_increment == WebFloatSize())
937 return clipped_velocity != WebFloatSize();
939 TRACE_EVENT2("input",
940 "InputHandlerProxy::scrollBy",
941 "x",
942 clipped_increment.width,
943 "y",
944 clipped_increment.height);
946 bool did_scroll = false;
948 switch (fling_parameters_.sourceDevice) {
949 case blink::WebGestureDeviceTouchpad:
950 did_scroll = TouchpadFlingScroll(clipped_increment);
951 break;
952 case blink::WebGestureDeviceTouchscreen: {
953 clipped_increment = ToClientScrollIncrement(clipped_increment);
954 cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(
955 fling_parameters_.point, clipped_increment);
956 HandleOverscroll(fling_parameters_.point, scroll_result);
957 did_scroll = scroll_result.did_scroll;
958 } break;
961 if (did_scroll) {
962 fling_parameters_.cumulativeScroll.width += clipped_increment.width;
963 fling_parameters_.cumulativeScroll.height += clipped_increment.height;
966 // It's possible the provided |increment| is sufficiently small as to not
967 // trigger a scroll, e.g., with a trivial time delta between fling updates.
968 // Return true in this case to prevent early fling termination.
969 if (std::abs(clipped_increment.width) < kScrollEpsilon &&
970 std::abs(clipped_increment.height) < kScrollEpsilon)
971 return true;
973 return did_scroll;
976 } // namespace content