Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / renderer / input / input_handler_proxy.cc
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.
4
5 #include "content/renderer/input/input_handler_proxy.h"
6
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"
21
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;
31
32 namespace {
33
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.;
38
39 // Threshold for determining whether a fling scroll delta should have caused the
40 // client to scroll.
41 const float kScrollEpsilon = 0.1f;
42
43 // Minimum fling velocity required for the active fling and new fling for the
44 // two to accumulate.
45 const double kMinBoostFlingSpeedSquare = 350. * 350.;
46
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.;
50
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;
56
57 gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
58   return gfx::Vector2dF(-increment.width, -increment.height);
59 }
60
61 double InSecondsF(const base::TimeTicks& time) {
62   return (time - base::TimeTicks()).InSecondsF();
63 }
64
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);
70
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;
75
76   if (time_since_last_boost_event < 0.001)
77     return true;
78
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;
85
86   return true;
87 }
88
89 bool ShouldBoostFling(const gfx::Vector2dF& current_fling_velocity,
90                       const WebGestureEvent& fling_start_event) {
91   DCHECK_EQ(WebInputEvent::GestureFlingStart, fling_start_event.type);
92
93   gfx::Vector2dF new_fling_velocity(
94       fling_start_event.data.flingStart.velocityX,
95       fling_start_event.data.flingStart.velocityY);
96
97   if (gfx::DotProduct(current_fling_velocity, new_fling_velocity) <= 0)
98     return false;
99
100   if (current_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
101     return false;
102
103   if (new_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
104     return false;
105
106   return true;
107 }
108
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;
115 }
116
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;
123
124   ui::LatencyInfo::LatencyMap::const_iterator it =
125       latency_info.latency_components.find(std::make_pair(
126           ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0));
127
128   if (it == latency_info.latency_components.end())
129     return;
130
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(),
136         1,
137         1000000,
138         100);
139   }
140 }  // namespace
141
142 }
143
144 namespace content {
145
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);
164 }
165
166 InputHandlerProxy::~InputHandlerProxy() {}
167
168 void InputHandlerProxy::WillShutdown() {
169   input_handler_ = NULL;
170   client_->WillShutdown();
171 }
172
173 InputHandlerProxy::EventDisposition
174 InputHandlerProxy::HandleInputEventWithLatencyInfo(
175     const WebInputEvent& event,
176     ui::LatencyInfo* latency_info) {
177   DCHECK(input_handler_);
178
179   SendScrollLatencyUma(event, *latency_info);
180
181   TRACE_EVENT_FLOW_STEP0("input",
182                          "LatencyInfo.Flow",
183                          TRACE_ID_DONT_MANGLE(latency_info->trace_id),
184                          "HandleInputEventImpl");
185
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;
190 }
191
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));
197
198   client_->DidReceiveInputEvent();
199   if (FilterInputEventForFlingBoosting(event))
200     return DID_HANDLE;
201
202   if (event.type == WebInputEvent::MouseWheel) {
203     const WebMouseWheelEvent& wheel_event =
204         *static_cast<const WebMouseWheelEvent*>(&event);
205     if (wheel_event.scrollByPage) {
206       // TODO(jamesr): We don't properly handle scroll by page in the compositor
207       // thread, so punt it to the main thread. http://crbug.com/236639
208       return DID_NOT_HANDLE;
209     }
210     if (wheel_event.modifiers & WebInputEvent::ControlKey) {
211       // Wheel events involving the control key never trigger scrolling, only
212       // event handlers.  Forward to the main thread.
213       return DID_NOT_HANDLE;
214     }
215     if (smooth_scroll_enabled_) {
216       cc::InputHandler::ScrollStatus scroll_status =
217           input_handler_->ScrollAnimated(
218               gfx::Point(wheel_event.x, wheel_event.y),
219               gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
220       switch (scroll_status) {
221         case cc::InputHandler::ScrollStarted:
222           return DID_HANDLE;
223         case cc::InputHandler::ScrollIgnored:
224           return DROP_EVENT;
225         default:
226           return DID_NOT_HANDLE;
227       }
228     }
229     cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
230         gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
231     switch (scroll_status) {
232       case cc::InputHandler::ScrollStarted: {
233         TRACE_EVENT_INSTANT2(
234             "input",
235             "InputHandlerProxy::handle_input wheel scroll",
236             TRACE_EVENT_SCOPE_THREAD,
237             "deltaX",
238             -wheel_event.deltaX,
239             "deltaY",
240             -wheel_event.deltaY);
241         bool did_scroll = input_handler_->ScrollBy(
242             gfx::Point(wheel_event.x, wheel_event.y),
243             gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
244         input_handler_->ScrollEnd();
245         return did_scroll ? DID_HANDLE : DROP_EVENT;
246       }
247       case cc::InputHandler::ScrollIgnored:
248         // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
249         // to properly sync scrollability it's safer to send the event to the
250         // main thread. Change back to DROP_EVENT once we have synchronization
251         // bugs sorted out.
252         return DID_NOT_HANDLE;
253       case cc::InputHandler::ScrollUnknown:
254       case cc::InputHandler::ScrollOnMainThread:
255         return DID_NOT_HANDLE;
256       case cc::InputHandler::ScrollStatusCount:
257         NOTREACHED();
258         break;
259     }
260   } else if (event.type == WebInputEvent::GestureScrollBegin) {
261     DCHECK(!gesture_scroll_on_impl_thread_);
262 #ifndef NDEBUG
263     DCHECK(!expect_scroll_update_end_);
264     expect_scroll_update_end_ = true;
265 #endif
266     const WebGestureEvent& gesture_event =
267         *static_cast<const WebGestureEvent*>(&event);
268     cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
269         gfx::Point(gesture_event.x, gesture_event.y),
270         cc::InputHandler::Gesture);
271     UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult",
272                               scroll_status,
273                               cc::InputHandler::ScrollStatusCount);
274     switch (scroll_status) {
275       case cc::InputHandler::ScrollStarted:
276         TRACE_EVENT_INSTANT0("input",
277                              "InputHandlerProxy::handle_input gesture scroll",
278                              TRACE_EVENT_SCOPE_THREAD);
279         gesture_scroll_on_impl_thread_ = true;
280         return DID_HANDLE;
281       case cc::InputHandler::ScrollUnknown:
282       case cc::InputHandler::ScrollOnMainThread:
283         return DID_NOT_HANDLE;
284       case cc::InputHandler::ScrollIgnored:
285         return DROP_EVENT;
286       case cc::InputHandler::ScrollStatusCount:
287         NOTREACHED();
288         break;
289     }
290   } else if (event.type == WebInputEvent::GestureScrollUpdate) {
291 #ifndef NDEBUG
292     DCHECK(expect_scroll_update_end_);
293 #endif
294
295     if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
296       return DID_NOT_HANDLE;
297
298     const WebGestureEvent& gesture_event =
299         *static_cast<const WebGestureEvent*>(&event);
300     bool did_scroll = input_handler_->ScrollBy(
301         gfx::Point(gesture_event.x, gesture_event.y),
302         gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX,
303                        -gesture_event.data.scrollUpdate.deltaY));
304     return did_scroll ? DID_HANDLE : DROP_EVENT;
305   } else if (event.type == WebInputEvent::GestureScrollEnd) {
306 #ifndef NDEBUG
307     DCHECK(expect_scroll_update_end_);
308     expect_scroll_update_end_ = false;
309 #endif
310     input_handler_->ScrollEnd();
311
312     if (!gesture_scroll_on_impl_thread_)
313       return DID_NOT_HANDLE;
314
315     gesture_scroll_on_impl_thread_ = false;
316     return DID_HANDLE;
317   } else if (event.type == WebInputEvent::GesturePinchBegin) {
318     input_handler_->PinchGestureBegin();
319     DCHECK(!gesture_pinch_on_impl_thread_);
320     gesture_pinch_on_impl_thread_ = true;
321     return DID_HANDLE;
322   } else if (event.type == WebInputEvent::GesturePinchEnd) {
323     DCHECK(gesture_pinch_on_impl_thread_);
324     gesture_pinch_on_impl_thread_ = false;
325     input_handler_->PinchGestureEnd();
326     return DID_HANDLE;
327   } else if (event.type == WebInputEvent::GesturePinchUpdate) {
328     DCHECK(gesture_pinch_on_impl_thread_);
329     const WebGestureEvent& gesture_event =
330         *static_cast<const WebGestureEvent*>(&event);
331     input_handler_->PinchGestureUpdate(
332         gesture_event.data.pinchUpdate.scale,
333         gfx::Point(gesture_event.x, gesture_event.y));
334     return DID_HANDLE;
335   } else if (event.type == WebInputEvent::GestureFlingStart) {
336     const WebGestureEvent& gesture_event =
337         *static_cast<const WebGestureEvent*>(&event);
338     return HandleGestureFling(gesture_event);
339   } else if (event.type == WebInputEvent::GestureFlingCancel) {
340     if (CancelCurrentFling())
341       return DID_HANDLE;
342     else if (!fling_may_be_active_on_main_thread_)
343       return DROP_EVENT;
344   } else if (event.type == WebInputEvent::TouchStart) {
345     const WebTouchEvent& touch_event =
346         *static_cast<const WebTouchEvent*>(&event);
347     for (size_t i = 0; i < touch_event.touchesLength; ++i) {
348       if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
349         continue;
350       if (input_handler_->HaveTouchEventHandlersAt(
351               gfx::Point(touch_event.touches[i].position.x,
352                          touch_event.touches[i].position.y))) {
353         return DID_NOT_HANDLE;
354       }
355     }
356     return DROP_EVENT;
357   } else if (WebInputEvent::isKeyboardEventType(event.type)) {
358     // Only call |CancelCurrentFling()| if a fling was active, as it will
359     // otherwise disrupt an in-progress touch scroll.
360     if (fling_curve_)
361       CancelCurrentFling();
362   } else if (event.type == WebInputEvent::MouseMove) {
363     const WebMouseEvent& mouse_event =
364         *static_cast<const WebMouseEvent*>(&event);
365     // TODO(tony): Ignore when mouse buttons are down?
366     // TODO(davemoore): This should never happen, but bug #326635 showed some
367     // surprising crashes.
368     CHECK(input_handler_);
369     input_handler_->MouseMoveAt(gfx::Point(mouse_event.x, mouse_event.y));
370   }
371
372   return DID_NOT_HANDLE;
373 }
374
375 InputHandlerProxy::EventDisposition
376 InputHandlerProxy::HandleGestureFling(
377     const WebGestureEvent& gesture_event) {
378   cc::InputHandler::ScrollStatus scroll_status;
379
380   if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
381     scroll_status = input_handler_->ScrollBegin(
382         gfx::Point(gesture_event.x, gesture_event.y),
383         cc::InputHandler::NonBubblingGesture);
384   } else {
385     if (!gesture_scroll_on_impl_thread_)
386       scroll_status = cc::InputHandler::ScrollOnMainThread;
387     else
388       scroll_status = input_handler_->FlingScrollBegin();
389   }
390
391 #ifndef NDEBUG
392   expect_scroll_update_end_ = false;
393 #endif
394
395   switch (scroll_status) {
396     case cc::InputHandler::ScrollStarted: {
397       if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad)
398         input_handler_->ScrollEnd();
399
400       const float vx = gesture_event.data.flingStart.velocityX;
401       const float vy = gesture_event.data.flingStart.velocityY;
402       current_fling_velocity_ = gfx::Vector2dF(vx, vy);
403       fling_curve_.reset(client_->CreateFlingAnimationCurve(
404           gesture_event.sourceDevice,
405           WebFloatPoint(vx, vy),
406           blink::WebSize()));
407       disallow_horizontal_fling_scroll_ = !vx;
408       disallow_vertical_fling_scroll_ = !vy;
409       TRACE_EVENT_ASYNC_BEGIN2("input",
410                                "InputHandlerProxy::HandleGestureFling::started",
411                                this,
412                                "vx",
413                                vx,
414                                "vy",
415                                vy);
416       // Note that the timestamp will only be used to kickstart the animation if
417       // its sufficiently close to the timestamp of the first call |Animate()|.
418       has_fling_animation_started_ = false;
419       fling_parameters_.startTime = gesture_event.timeStampSeconds;
420       fling_parameters_.delta = WebFloatPoint(vx, vy);
421       fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
422       fling_parameters_.globalPoint =
423           WebPoint(gesture_event.globalX, gesture_event.globalY);
424       fling_parameters_.modifiers = gesture_event.modifiers;
425       fling_parameters_.sourceDevice = gesture_event.sourceDevice;
426       input_handler_->SetNeedsAnimate();
427       return DID_HANDLE;
428     }
429     case cc::InputHandler::ScrollUnknown:
430     case cc::InputHandler::ScrollOnMainThread: {
431       TRACE_EVENT_INSTANT0("input",
432                            "InputHandlerProxy::HandleGestureFling::"
433                            "scroll_on_main_thread",
434                            TRACE_EVENT_SCOPE_THREAD);
435       fling_may_be_active_on_main_thread_ = true;
436       return DID_NOT_HANDLE;
437     }
438     case cc::InputHandler::ScrollIgnored: {
439       TRACE_EVENT_INSTANT0(
440           "input",
441           "InputHandlerProxy::HandleGestureFling::ignored",
442           TRACE_EVENT_SCOPE_THREAD);
443       if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
444         // We still pass the curve to the main thread if there's nothing
445         // scrollable, in case something
446         // registers a handler before the curve is over.
447         return DID_NOT_HANDLE;
448       }
449       return DROP_EVENT;
450     }
451     case cc::InputHandler::ScrollStatusCount:
452       NOTREACHED();
453       break;
454   }
455   return DID_NOT_HANDLE;
456 }
457
458 bool InputHandlerProxy::FilterInputEventForFlingBoosting(
459     const WebInputEvent& event) {
460   if (!WebInputEvent::isGestureEventType(event.type))
461     return false;
462
463   if (!fling_curve_) {
464     DCHECK(!deferred_fling_cancel_time_seconds_);
465     return false;
466   }
467
468   const WebGestureEvent& gesture_event =
469       static_cast<const WebGestureEvent&>(event);
470   if (gesture_event.type == WebInputEvent::GestureFlingCancel) {
471     if (current_fling_velocity_.LengthSquared() < kMinBoostFlingSpeedSquare)
472       return false;
473
474     TRACE_EVENT_INSTANT0("input",
475                          "InputHandlerProxy::FlingBoostStart",
476                          TRACE_EVENT_SCOPE_THREAD);
477     deferred_fling_cancel_time_seconds_ =
478         event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
479     return true;
480   }
481
482   // A fling is either inactive or is "free spinning", i.e., has yet to be
483   // interrupted by a touch gesture, in which case there is nothing to filter.
484   if (!deferred_fling_cancel_time_seconds_)
485     return false;
486
487   // Gestures from a different source should immediately interrupt the fling.
488   if (gesture_event.sourceDevice != fling_parameters_.sourceDevice) {
489     CancelCurrentFling();
490     return false;
491   }
492
493   switch (gesture_event.type) {
494     case WebInputEvent::GestureTapCancel:
495     case WebInputEvent::GestureTapDown:
496       return false;
497
498     case WebInputEvent::GestureScrollBegin:
499       if (!input_handler_->IsCurrentlyScrollingLayerAt(
500               gfx::Point(gesture_event.x, gesture_event.y),
501               fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad
502                   ? cc::InputHandler::NonBubblingGesture
503                   : cc::InputHandler::Gesture)) {
504         CancelCurrentFling();
505         return false;
506       }
507
508       // TODO(jdduke): Use |gesture_event.data.scrollBegin.delta{X,Y}Hint| to
509       // determine if the ScrollBegin should immediately cancel the fling.
510       ExtendBoostedFlingTimeout(gesture_event);
511       return true;
512
513     case WebInputEvent::GestureScrollUpdate: {
514       const double time_since_last_boost_event =
515           event.timeStampSeconds - last_fling_boost_event_.timeStampSeconds;
516       if (ShouldSuppressScrollForFlingBoosting(current_fling_velocity_,
517                                                gesture_event,
518                                                time_since_last_boost_event)) {
519         ExtendBoostedFlingTimeout(gesture_event);
520         return true;
521       }
522
523       CancelCurrentFling();
524       return false;
525     }
526
527     case WebInputEvent::GestureScrollEnd:
528       // Clear the last fling boost event *prior* to fling cancellation,
529       // preventing insertion of a synthetic GestureScrollBegin.
530       last_fling_boost_event_ = WebGestureEvent();
531       CancelCurrentFling();
532       return true;
533
534     case WebInputEvent::GestureFlingStart: {
535       DCHECK_EQ(fling_parameters_.sourceDevice, gesture_event.sourceDevice);
536
537       bool fling_boosted =
538           fling_parameters_.modifiers == gesture_event.modifiers &&
539           ShouldBoostFling(current_fling_velocity_, gesture_event);
540
541       gfx::Vector2dF new_fling_velocity(
542           gesture_event.data.flingStart.velocityX,
543           gesture_event.data.flingStart.velocityY);
544
545       if (fling_boosted)
546         current_fling_velocity_ += new_fling_velocity;
547       else
548         current_fling_velocity_ = new_fling_velocity;
549
550       WebFloatPoint velocity(current_fling_velocity_.x(),
551                              current_fling_velocity_.y());
552       deferred_fling_cancel_time_seconds_ = 0;
553       disallow_horizontal_fling_scroll_ = !velocity.x;
554       disallow_vertical_fling_scroll_ = !velocity.y;
555       last_fling_boost_event_ = WebGestureEvent();
556       fling_curve_.reset(client_->CreateFlingAnimationCurve(
557           gesture_event.sourceDevice,
558           velocity,
559           blink::WebSize()));
560       fling_parameters_.startTime = gesture_event.timeStampSeconds;
561       fling_parameters_.delta = velocity;
562       fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
563       fling_parameters_.globalPoint =
564           WebPoint(gesture_event.globalX, gesture_event.globalY);
565
566       TRACE_EVENT_INSTANT2("input",
567                            fling_boosted ? "InputHandlerProxy::FlingBoosted"
568                                          : "InputHandlerProxy::FlingReplaced",
569                            TRACE_EVENT_SCOPE_THREAD,
570                            "vx",
571                            current_fling_velocity_.x(),
572                            "vy",
573                            current_fling_velocity_.y());
574
575       // The client expects balanced calls between a consumed GestureFlingStart
576       // and |DidStopFlinging()|. TODO(jdduke): Provide a count parameter to
577       // |DidStopFlinging()| and only send after the accumulated fling ends.
578       client_->DidStopFlinging();
579       return true;
580     }
581
582     default:
583       // All other types of gestures (taps, presses, etc...) will complete the
584       // deferred fling cancellation.
585       CancelCurrentFling();
586       return false;
587   }
588 }
589
590 void InputHandlerProxy::ExtendBoostedFlingTimeout(
591     const blink::WebGestureEvent& event) {
592   TRACE_EVENT_INSTANT0("input",
593                        "InputHandlerProxy::ExtendBoostedFlingTimeout",
594                        TRACE_EVENT_SCOPE_THREAD);
595   deferred_fling_cancel_time_seconds_ =
596       event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
597   last_fling_boost_event_ = event;
598 }
599
600 void InputHandlerProxy::Animate(base::TimeTicks time) {
601   if (!fling_curve_)
602     return;
603
604   double monotonic_time_sec = InSecondsF(time);
605
606   if (deferred_fling_cancel_time_seconds_ &&
607       monotonic_time_sec > deferred_fling_cancel_time_seconds_) {
608     CancelCurrentFling();
609     return;
610   }
611
612   if (!has_fling_animation_started_) {
613     has_fling_animation_started_ = true;
614     // Guard against invalid, future or sufficiently stale start times, as there
615     // are no guarantees fling event and animation timestamps are compatible.
616     if (!fling_parameters_.startTime ||
617         monotonic_time_sec <= fling_parameters_.startTime ||
618         monotonic_time_sec >= fling_parameters_.startTime +
619                                   kMaxSecondsFromFlingTimestampToFirstAnimate) {
620       fling_parameters_.startTime = monotonic_time_sec;
621       input_handler_->SetNeedsAnimate();
622       return;
623     }
624   }
625
626   bool fling_is_active =
627       fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime,
628                           this);
629
630   if (disallow_vertical_fling_scroll_ && disallow_horizontal_fling_scroll_)
631     fling_is_active = false;
632
633   if (fling_is_active) {
634     input_handler_->SetNeedsAnimate();
635   } else {
636     TRACE_EVENT_INSTANT0("input",
637                          "InputHandlerProxy::animate::flingOver",
638                          TRACE_EVENT_SCOPE_THREAD);
639     CancelCurrentFling();
640   }
641 }
642
643 void InputHandlerProxy::MainThreadHasStoppedFlinging() {
644   fling_may_be_active_on_main_thread_ = false;
645   client_->DidStopFlinging();
646 }
647
648 void InputHandlerProxy::DidOverscroll(
649     const gfx::PointF& causal_event_viewport_point,
650     const gfx::Vector2dF& accumulated_overscroll,
651     const gfx::Vector2dF& latest_overscroll_delta) {
652   DCHECK(client_);
653
654   TRACE_EVENT2("input",
655                "InputHandlerProxy::DidOverscroll",
656                "dx",
657                latest_overscroll_delta.x(),
658                "dy",
659                latest_overscroll_delta.y());
660
661   DidOverscrollParams params;
662   params.accumulated_overscroll = accumulated_overscroll;
663   params.latest_overscroll_delta = latest_overscroll_delta;
664   params.current_fling_velocity =
665       ToClientScrollIncrement(current_fling_velocity_);
666   params.causal_event_viewport_point = causal_event_viewport_point;
667
668   if (fling_curve_) {
669     static const int kFlingOverscrollThreshold = 1;
670     disallow_horizontal_fling_scroll_ |=
671         std::abs(params.accumulated_overscroll.x()) >=
672         kFlingOverscrollThreshold;
673     disallow_vertical_fling_scroll_ |=
674         std::abs(params.accumulated_overscroll.y()) >=
675         kFlingOverscrollThreshold;
676   }
677
678   client_->DidOverscroll(params);
679 }
680
681 bool InputHandlerProxy::CancelCurrentFling() {
682   if (CancelCurrentFlingWithoutNotifyingClient()) {
683     client_->DidStopFlinging();
684     return true;
685   }
686   return false;
687 }
688
689 bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() {
690   bool had_fling_animation = fling_curve_;
691   if (had_fling_animation &&
692       fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) {
693     input_handler_->ScrollEnd();
694     TRACE_EVENT_ASYNC_END0(
695         "input",
696         "InputHandlerProxy::HandleGestureFling::started",
697         this);
698   }
699
700   TRACE_EVENT_INSTANT1("input",
701                        "InputHandlerProxy::CancelCurrentFling",
702                        TRACE_EVENT_SCOPE_THREAD,
703                        "had_fling_animation",
704                        had_fling_animation);
705   fling_curve_.reset();
706   has_fling_animation_started_ = false;
707   gesture_scroll_on_impl_thread_ = false;
708   current_fling_velocity_ = gfx::Vector2dF();
709   fling_parameters_ = blink::WebActiveWheelFlingParameters();
710
711   if (deferred_fling_cancel_time_seconds_) {
712     deferred_fling_cancel_time_seconds_ = 0;
713
714     WebGestureEvent last_fling_boost_event = last_fling_boost_event_;
715     last_fling_boost_event_ = WebGestureEvent();
716     if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin ||
717         last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) {
718       // Synthesize a GestureScrollBegin, as the original was suppressed.
719       HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event));
720     }
721   }
722
723   return had_fling_animation;
724 }
725
726 bool InputHandlerProxy::TouchpadFlingScroll(
727     const WebFloatSize& increment) {
728   WebMouseWheelEvent synthetic_wheel;
729   synthetic_wheel.type = WebInputEvent::MouseWheel;
730   synthetic_wheel.deltaX = increment.width;
731   synthetic_wheel.deltaY = increment.height;
732   synthetic_wheel.hasPreciseScrollingDeltas = true;
733   synthetic_wheel.x = fling_parameters_.point.x;
734   synthetic_wheel.y = fling_parameters_.point.y;
735   synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
736   synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
737   synthetic_wheel.modifiers = fling_parameters_.modifiers;
738
739   InputHandlerProxy::EventDisposition disposition =
740       HandleInputEvent(synthetic_wheel);
741   switch (disposition) {
742     case DID_HANDLE:
743       return true;
744     case DROP_EVENT:
745       break;
746     case DID_NOT_HANDLE:
747       TRACE_EVENT_INSTANT0("input",
748                            "InputHandlerProxy::scrollBy::AbortFling",
749                            TRACE_EVENT_SCOPE_THREAD);
750       // If we got a DID_NOT_HANDLE, that means we need to deliver wheels on the
751       // main thread. In this case we need to schedule a commit and transfer the
752       // fling curve over to the main thread and run the rest of the wheels from
753       // there. This can happen when flinging a page that contains a scrollable
754       // subarea that we can't scroll on the thread if the fling starts outside
755       // the subarea but then is flung "under" the pointer.
756       client_->TransferActiveWheelFlingAnimation(fling_parameters_);
757       fling_may_be_active_on_main_thread_ = true;
758       CancelCurrentFlingWithoutNotifyingClient();
759       break;
760   }
761
762   return false;
763 }
764
765 bool InputHandlerProxy::scrollBy(const WebFloatSize& increment,
766                                  const WebFloatSize& velocity) {
767   WebFloatSize clipped_increment;
768   WebFloatSize clipped_velocity;
769   if (!disallow_horizontal_fling_scroll_) {
770     clipped_increment.width = increment.width;
771     clipped_velocity.width = velocity.width;
772   }
773   if (!disallow_vertical_fling_scroll_) {
774     clipped_increment.height = increment.height;
775     clipped_velocity.height = velocity.height;
776   }
777
778   current_fling_velocity_ = clipped_velocity;
779
780   // Early out if the increment is zero, but avoid early terimination if the
781   // velocity is still non-zero.
782   if (clipped_increment == WebFloatSize())
783     return clipped_velocity != WebFloatSize();
784
785   TRACE_EVENT2("input",
786                "InputHandlerProxy::scrollBy",
787                "x",
788                clipped_increment.width,
789                "y",
790                clipped_increment.height);
791
792   bool did_scroll = false;
793
794   switch (fling_parameters_.sourceDevice) {
795     case blink::WebGestureDeviceTouchpad:
796       did_scroll = TouchpadFlingScroll(clipped_increment);
797       break;
798     case blink::WebGestureDeviceTouchscreen:
799       clipped_increment = ToClientScrollIncrement(clipped_increment);
800       did_scroll = input_handler_->ScrollBy(fling_parameters_.point,
801                                             clipped_increment);
802       break;
803   }
804
805   if (did_scroll) {
806     fling_parameters_.cumulativeScroll.width += clipped_increment.width;
807     fling_parameters_.cumulativeScroll.height += clipped_increment.height;
808   }
809
810   // It's possible the provided |increment| is sufficiently small as to not
811   // trigger a scroll, e.g., with a trivial time delta between fling updates.
812   // Return true in this case to prevent early fling termination.
813   if (std::abs(clipped_increment.width) < kScrollEpsilon &&
814       std::abs(clipped_increment.height) < kScrollEpsilon)
815     return true;
816
817   return did_scroll;
818 }
819
820 }  // namespace content