IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / renderer / input / input_handler_proxy.cc
blobfa95881c2f1528239cb7f92f94a60e52d056d045
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/debug/trace_event.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "content/renderer/input/input_handler_proxy_client.h"
11 #include "third_party/WebKit/public/platform/Platform.h"
12 #include "third_party/WebKit/public/web/WebInputEvent.h"
13 #include "ui/events/latency_info.h"
14 #include "ui/gfx/frame_time.h"
16 using blink::WebFloatPoint;
17 using blink::WebFloatSize;
18 using blink::WebGestureEvent;
19 using blink::WebInputEvent;
20 using blink::WebMouseEvent;
21 using blink::WebMouseWheelEvent;
22 using blink::WebPoint;
23 using blink::WebTouchEvent;
24 using blink::WebTouchPoint;
26 namespace {
28 // Validate provided event timestamps that interact with animation timestamps.
29 const double kBadTimestampDeltaFromNowInS = 60. * 60. * 24. * 7.;
31 double InSecondsF(const base::TimeTicks& time) {
32 return (time - base::TimeTicks()).InSecondsF();
35 void SendScrollLatencyUma(const WebInputEvent& event,
36 const ui::LatencyInfo& latency_info) {
37 if (!(event.type == WebInputEvent::GestureScrollBegin ||
38 event.type == WebInputEvent::GestureScrollUpdate ||
39 event.type == WebInputEvent::GestureScrollUpdateWithoutPropagation))
40 return;
42 ui::LatencyInfo::LatencyMap::const_iterator it =
43 latency_info.latency_components.find(std::make_pair(
44 ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0));
46 if (it == latency_info.latency_components.end())
47 return;
49 base::TimeDelta delta = base::TimeTicks::HighResNow() - it->second.event_time;
50 for (size_t i = 0; i < it->second.event_count; ++i) {
51 UMA_HISTOGRAM_CUSTOM_COUNTS(
52 "Event.Latency.RendererImpl.GestureScroll2",
53 delta.InMicroseconds(),
55 1000000,
56 100);
58 } // namespace
62 namespace content {
64 InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler)
65 : client_(NULL),
66 input_handler_(input_handler),
67 #ifndef NDEBUG
68 expect_scroll_update_end_(false),
69 #endif
70 gesture_scroll_on_impl_thread_(false),
71 gesture_pinch_on_impl_thread_(false),
72 fling_may_be_active_on_main_thread_(false),
73 disallow_horizontal_fling_scroll_(false),
74 disallow_vertical_fling_scroll_(false) {
75 input_handler_->BindToClient(this);
78 InputHandlerProxy::~InputHandlerProxy() {}
80 void InputHandlerProxy::WillShutdown() {
81 input_handler_ = NULL;
82 DCHECK(client_);
83 client_->WillShutdown();
86 void InputHandlerProxy::SetClient(InputHandlerProxyClient* client) {
87 DCHECK(!client_ || !client);
88 client_ = client;
91 InputHandlerProxy::EventDisposition
92 InputHandlerProxy::HandleInputEventWithLatencyInfo(
93 const WebInputEvent& event,
94 ui::LatencyInfo* latency_info) {
95 DCHECK(input_handler_);
97 SendScrollLatencyUma(event, *latency_info);
99 scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
100 input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info);
101 InputHandlerProxy::EventDisposition disposition = HandleInputEvent(event);
102 return disposition;
105 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent(
106 const WebInputEvent& event) {
107 DCHECK(client_);
108 DCHECK(input_handler_);
110 if (event.type == WebInputEvent::MouseWheel) {
111 const WebMouseWheelEvent& wheel_event =
112 *static_cast<const WebMouseWheelEvent*>(&event);
113 if (wheel_event.scrollByPage) {
114 // TODO(jamesr): We don't properly handle scroll by page in the compositor
115 // thread, so punt it to the main thread. http://crbug.com/236639
116 return DID_NOT_HANDLE;
118 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
119 gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
120 switch (scroll_status) {
121 case cc::InputHandler::ScrollStarted: {
122 TRACE_EVENT_INSTANT2(
123 "renderer",
124 "InputHandlerProxy::handle_input wheel scroll",
125 TRACE_EVENT_SCOPE_THREAD,
126 "deltaX",
127 -wheel_event.deltaX,
128 "deltaY",
129 -wheel_event.deltaY);
130 bool did_scroll = input_handler_->ScrollBy(
131 gfx::Point(wheel_event.x, wheel_event.y),
132 gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
133 input_handler_->ScrollEnd();
134 return did_scroll ? DID_HANDLE : DROP_EVENT;
136 case cc::InputHandler::ScrollIgnored:
137 // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
138 // to properly sync scrollability it's safer to send the event to the
139 // main thread. Change back to DROP_EVENT once we have synchronization
140 // bugs sorted out.
141 return DID_NOT_HANDLE;
142 case cc::InputHandler::ScrollOnMainThread:
143 return DID_NOT_HANDLE;
145 } else if (event.type == WebInputEvent::GestureScrollBegin) {
146 DCHECK(!gesture_scroll_on_impl_thread_);
147 #ifndef NDEBUG
148 DCHECK(!expect_scroll_update_end_);
149 expect_scroll_update_end_ = true;
150 #endif
151 const WebGestureEvent& gesture_event =
152 *static_cast<const WebGestureEvent*>(&event);
153 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
154 gfx::Point(gesture_event.x, gesture_event.y),
155 cc::InputHandler::Gesture);
156 switch (scroll_status) {
157 case cc::InputHandler::ScrollStarted:
158 gesture_scroll_on_impl_thread_ = true;
159 return DID_HANDLE;
160 case cc::InputHandler::ScrollOnMainThread:
161 return DID_NOT_HANDLE;
162 case cc::InputHandler::ScrollIgnored:
163 return DROP_EVENT;
165 } else if (event.type == WebInputEvent::GestureScrollUpdate) {
166 #ifndef NDEBUG
167 DCHECK(expect_scroll_update_end_);
168 #endif
170 if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
171 return DID_NOT_HANDLE;
173 const WebGestureEvent& gesture_event =
174 *static_cast<const WebGestureEvent*>(&event);
175 bool did_scroll = input_handler_->ScrollBy(
176 gfx::Point(gesture_event.x, gesture_event.y),
177 gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX,
178 -gesture_event.data.scrollUpdate.deltaY));
179 return did_scroll ? DID_HANDLE : DROP_EVENT;
180 } else if (event.type == WebInputEvent::GestureScrollEnd) {
181 #ifndef NDEBUG
182 DCHECK(expect_scroll_update_end_);
183 expect_scroll_update_end_ = false;
184 #endif
185 input_handler_->ScrollEnd();
187 if (!gesture_scroll_on_impl_thread_)
188 return DID_NOT_HANDLE;
190 gesture_scroll_on_impl_thread_ = false;
191 return DID_HANDLE;
192 } else if (event.type == WebInputEvent::GesturePinchBegin) {
193 input_handler_->PinchGestureBegin();
194 DCHECK(!gesture_pinch_on_impl_thread_);
195 gesture_pinch_on_impl_thread_ = true;
196 return DID_HANDLE;
197 } else if (event.type == WebInputEvent::GesturePinchEnd) {
198 DCHECK(gesture_pinch_on_impl_thread_);
199 gesture_pinch_on_impl_thread_ = false;
200 input_handler_->PinchGestureEnd();
201 return DID_HANDLE;
202 } else if (event.type == WebInputEvent::GesturePinchUpdate) {
203 DCHECK(gesture_pinch_on_impl_thread_);
204 const WebGestureEvent& gesture_event =
205 *static_cast<const WebGestureEvent*>(&event);
206 input_handler_->PinchGestureUpdate(
207 gesture_event.data.pinchUpdate.scale,
208 gfx::Point(gesture_event.x, gesture_event.y));
209 return DID_HANDLE;
210 } else if (event.type == WebInputEvent::GestureFlingStart) {
211 const WebGestureEvent& gesture_event =
212 *static_cast<const WebGestureEvent*>(&event);
213 return HandleGestureFling(gesture_event);
214 } else if (event.type == WebInputEvent::GestureFlingCancel) {
215 if (CancelCurrentFling())
216 return DID_HANDLE;
217 else if (!fling_may_be_active_on_main_thread_)
218 return DROP_EVENT;
219 } else if (event.type == WebInputEvent::TouchStart) {
220 const WebTouchEvent& touch_event =
221 *static_cast<const WebTouchEvent*>(&event);
222 for (size_t i = 0; i < touch_event.touchesLength; ++i) {
223 if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
224 continue;
225 if (input_handler_->HaveTouchEventHandlersAt(touch_event.touches[i]
226 .position))
227 return DID_NOT_HANDLE;
229 return DROP_EVENT;
230 } else if (WebInputEvent::isKeyboardEventType(event.type)) {
231 CancelCurrentFling();
232 } else if (event.type == WebInputEvent::MouseMove) {
233 const WebMouseEvent& mouse_event =
234 *static_cast<const WebMouseEvent*>(&event);
235 // TODO(tony): Ignore when mouse buttons are down?
236 // TODO(davemoore): This should never happen, but bug #326635 showed some
237 // surprising crashes.
238 CHECK(input_handler_);
239 input_handler_->MouseMoveAt(gfx::Point(mouse_event.x, mouse_event.y));
242 return DID_NOT_HANDLE;
245 InputHandlerProxy::EventDisposition
246 InputHandlerProxy::HandleGestureFling(
247 const WebGestureEvent& gesture_event) {
248 cc::InputHandler::ScrollStatus scroll_status;
250 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) {
251 scroll_status = input_handler_->ScrollBegin(
252 gfx::Point(gesture_event.x, gesture_event.y),
253 cc::InputHandler::NonBubblingGesture);
254 } else {
255 if (!gesture_scroll_on_impl_thread_)
256 scroll_status = cc::InputHandler::ScrollOnMainThread;
257 else
258 scroll_status = input_handler_->FlingScrollBegin();
261 #ifndef NDEBUG
262 expect_scroll_update_end_ = false;
263 #endif
265 switch (scroll_status) {
266 case cc::InputHandler::ScrollStarted: {
267 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad)
268 input_handler_->ScrollEnd();
270 fling_curve_.reset(client_->CreateFlingAnimationCurve(
271 gesture_event.sourceDevice,
272 WebFloatPoint(gesture_event.data.flingStart.velocityX,
273 gesture_event.data.flingStart.velocityY),
274 blink::WebSize()));
275 disallow_horizontal_fling_scroll_ =
276 !gesture_event.data.flingStart.velocityX;
277 disallow_vertical_fling_scroll_ =
278 !gesture_event.data.flingStart.velocityY;
279 TRACE_EVENT_ASYNC_BEGIN0(
280 "renderer",
281 "InputHandlerProxy::HandleGestureFling::started",
282 this);
283 if (gesture_event.timeStampSeconds) {
284 fling_parameters_.startTime = gesture_event.timeStampSeconds;
285 DCHECK_LT(fling_parameters_.startTime -
286 InSecondsF(gfx::FrameTime::Now()),
287 kBadTimestampDeltaFromNowInS);
289 fling_parameters_.delta =
290 WebFloatPoint(gesture_event.data.flingStart.velocityX,
291 gesture_event.data.flingStart.velocityY);
292 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
293 fling_parameters_.globalPoint =
294 WebPoint(gesture_event.globalX, gesture_event.globalY);
295 fling_parameters_.modifiers = gesture_event.modifiers;
296 fling_parameters_.sourceDevice = gesture_event.sourceDevice;
297 input_handler_->ScheduleAnimation();
298 return DID_HANDLE;
300 case cc::InputHandler::ScrollOnMainThread: {
301 TRACE_EVENT_INSTANT0("renderer",
302 "InputHandlerProxy::HandleGestureFling::"
303 "scroll_on_main_thread",
304 TRACE_EVENT_SCOPE_THREAD);
305 fling_may_be_active_on_main_thread_ = true;
306 return DID_NOT_HANDLE;
308 case cc::InputHandler::ScrollIgnored: {
309 TRACE_EVENT_INSTANT0(
310 "renderer",
311 "InputHandlerProxy::HandleGestureFling::ignored",
312 TRACE_EVENT_SCOPE_THREAD);
313 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) {
314 // We still pass the curve to the main thread if there's nothing
315 // scrollable, in case something
316 // registers a handler before the curve is over.
317 return DID_NOT_HANDLE;
319 return DROP_EVENT;
322 return DID_NOT_HANDLE;
325 void InputHandlerProxy::Animate(base::TimeTicks time) {
326 if (!fling_curve_)
327 return;
329 double monotonic_time_sec = InSecondsF(time);
330 if (!fling_parameters_.startTime) {
331 fling_parameters_.startTime = monotonic_time_sec;
332 input_handler_->ScheduleAnimation();
333 return;
336 bool fling_is_active =
337 fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime,
338 this);
340 if (disallow_vertical_fling_scroll_ && disallow_horizontal_fling_scroll_)
341 fling_is_active = false;
343 if (fling_is_active) {
344 input_handler_->ScheduleAnimation();
345 } else {
346 TRACE_EVENT_INSTANT0("renderer",
347 "InputHandlerProxy::animate::flingOver",
348 TRACE_EVENT_SCOPE_THREAD);
349 CancelCurrentFling();
353 void InputHandlerProxy::MainThreadHasStoppedFlinging() {
354 fling_may_be_active_on_main_thread_ = false;
357 void InputHandlerProxy::DidOverscroll(const cc::DidOverscrollParams& params) {
358 DCHECK(client_);
359 if (fling_curve_) {
360 static const int kFlingOverscrollThreshold = 1;
361 disallow_horizontal_fling_scroll_ |=
362 std::abs(params.accumulated_overscroll.x()) >=
363 kFlingOverscrollThreshold;
364 disallow_vertical_fling_scroll_ |=
365 std::abs(params.accumulated_overscroll.y()) >=
366 kFlingOverscrollThreshold;
369 client_->DidOverscroll(params);
372 bool InputHandlerProxy::CancelCurrentFling() {
373 bool had_fling_animation = fling_curve_;
374 if (had_fling_animation &&
375 fling_parameters_.sourceDevice == WebGestureEvent::Touchscreen) {
376 input_handler_->ScrollEnd();
377 TRACE_EVENT_ASYNC_END0(
378 "renderer",
379 "InputHandlerProxy::HandleGestureFling::started",
380 this);
383 TRACE_EVENT_INSTANT1("renderer",
384 "InputHandlerProxy::CancelCurrentFling",
385 TRACE_EVENT_SCOPE_THREAD,
386 "had_fling_animation",
387 had_fling_animation);
388 fling_curve_.reset();
389 gesture_scroll_on_impl_thread_ = false;
390 fling_parameters_ = blink::WebActiveWheelFlingParameters();
391 return had_fling_animation;
394 bool InputHandlerProxy::TouchpadFlingScroll(
395 const WebFloatSize& increment) {
396 WebMouseWheelEvent synthetic_wheel;
397 synthetic_wheel.type = WebInputEvent::MouseWheel;
398 synthetic_wheel.deltaX = increment.width;
399 synthetic_wheel.deltaY = increment.height;
400 synthetic_wheel.hasPreciseScrollingDeltas = true;
401 synthetic_wheel.x = fling_parameters_.point.x;
402 synthetic_wheel.y = fling_parameters_.point.y;
403 synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
404 synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
405 synthetic_wheel.modifiers = fling_parameters_.modifiers;
407 InputHandlerProxy::EventDisposition disposition =
408 HandleInputEvent(synthetic_wheel);
409 switch (disposition) {
410 case DID_HANDLE:
411 return true;
412 case DROP_EVENT:
413 break;
414 case DID_NOT_HANDLE:
415 TRACE_EVENT_INSTANT0("renderer",
416 "InputHandlerProxy::scrollBy::AbortFling",
417 TRACE_EVENT_SCOPE_THREAD);
418 // If we got a DID_NOT_HANDLE, that means we need to deliver wheels on the
419 // main thread. In this case we need to schedule a commit and transfer the
420 // fling curve over to the main thread and run the rest of the wheels from
421 // there. This can happen when flinging a page that contains a scrollable
422 // subarea that we can't scroll on the thread if the fling starts outside
423 // the subarea but then is flung "under" the pointer.
424 client_->TransferActiveWheelFlingAnimation(fling_parameters_);
425 fling_may_be_active_on_main_thread_ = true;
426 CancelCurrentFling();
427 break;
430 return false;
433 static gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
434 return gfx::Vector2dF(-increment.width, -increment.height);
437 void InputHandlerProxy::scrollBy(const WebFloatSize& increment) {
438 WebFloatSize clipped_increment;
439 if (!disallow_horizontal_fling_scroll_)
440 clipped_increment.width = increment.width;
441 if (!disallow_vertical_fling_scroll_)
442 clipped_increment.height = increment.height;
444 if (clipped_increment == WebFloatSize())
445 return;
447 TRACE_EVENT2("renderer",
448 "InputHandlerProxy::scrollBy",
449 "x",
450 clipped_increment.width,
451 "y",
452 clipped_increment.height);
454 bool did_scroll = false;
456 switch (fling_parameters_.sourceDevice) {
457 case WebGestureEvent::Touchpad:
458 did_scroll = TouchpadFlingScroll(clipped_increment);
459 break;
460 case WebGestureEvent::Touchscreen:
461 clipped_increment = ToClientScrollIncrement(clipped_increment);
462 did_scroll = input_handler_->ScrollBy(fling_parameters_.point,
463 clipped_increment);
464 break;
467 if (did_scroll) {
468 fling_parameters_.cumulativeScroll.width += clipped_increment.width;
469 fling_parameters_.cumulativeScroll.height += clipped_increment.height;
473 void InputHandlerProxy::notifyCurrentFlingVelocity(
474 const WebFloatSize& velocity) {
475 TRACE_EVENT2("renderer",
476 "InputHandlerProxy::notifyCurrentFlingVelocity",
477 "vx",
478 velocity.width,
479 "vy",
480 velocity.height);
481 input_handler_->NotifyCurrentFlingVelocity(ToClientScrollIncrement(velocity));
484 } // namespace content