Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(308)

Side by Side Diff: content/renderer/input/input_handler_proxy.cc

Issue 1415953004: Move content/renderer input handling for web input events to ui (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 <algorithm>
8
9 #include "base/auto_reset.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/trace_event/trace_event.h"
17 #include "content/common/input/did_overscroll_params.h"
18 #include "content/common/input/web_input_event_traits.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/renderer/input/input_handler_proxy_client.h"
21 #include "content/renderer/input/input_scroll_elasticity_controller.h"
22 #include "third_party/WebKit/public/platform/Platform.h"
23 #include "third_party/WebKit/public/web/WebInputEvent.h"
24 #include "ui/events/latency_info.h"
25 #include "ui/gfx/geometry/point_conversions.h"
26
27 using blink::WebFloatPoint;
28 using blink::WebFloatSize;
29 using blink::WebGestureEvent;
30 using blink::WebInputEvent;
31 using blink::WebMouseEvent;
32 using blink::WebMouseWheelEvent;
33 using blink::WebPoint;
34 using blink::WebTouchEvent;
35 using blink::WebTouchPoint;
36
37 namespace {
38
39 // Maximum time between a fling event's timestamp and the first |Animate| call
40 // for the fling curve to use the fling timestamp as the initial animation time.
41 // Two frames allows a minor delay between event creation and the first animate.
42 const double kMaxSecondsFromFlingTimestampToFirstAnimate = 2. / 60.;
43
44 // Threshold for determining whether a fling scroll delta should have caused the
45 // client to scroll.
46 const float kScrollEpsilon = 0.1f;
47
48 // Minimum fling velocity required for the active fling and new fling for the
49 // two to accumulate.
50 const double kMinBoostFlingSpeedSquare = 350. * 350.;
51
52 // Minimum velocity for the active touch scroll to preserve (boost) an active
53 // fling for which cancellation has been deferred.
54 const double kMinBoostTouchScrollSpeedSquare = 150 * 150.;
55
56 // Timeout window after which the active fling will be cancelled if no animation
57 // ticks, scrolls or flings of sufficient velocity relative to the current fling
58 // are received. The default value on Android native views is 40ms, but we use a
59 // slightly increased value to accomodate small IPC message delays.
60 const double kFlingBoostTimeoutDelaySeconds = 0.05;
61
62 gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
63 return gfx::Vector2dF(-increment.width, -increment.height);
64 }
65
66 double InSecondsF(const base::TimeTicks& time) {
67 return (time - base::TimeTicks()).InSecondsF();
68 }
69
70 bool ShouldSuppressScrollForFlingBoosting(
71 const gfx::Vector2dF& current_fling_velocity,
72 const WebGestureEvent& scroll_update_event,
73 double time_since_last_boost_event,
74 double time_since_last_fling_animate) {
75 DCHECK_EQ(WebInputEvent::GestureScrollUpdate, scroll_update_event.type);
76
77 gfx::Vector2dF dx(scroll_update_event.data.scrollUpdate.deltaX,
78 scroll_update_event.data.scrollUpdate.deltaY);
79 if (gfx::DotProduct(current_fling_velocity, dx) <= 0)
80 return false;
81
82 if (time_since_last_fling_animate > kFlingBoostTimeoutDelaySeconds)
83 return false;
84
85 if (time_since_last_boost_event < 0.001)
86 return true;
87
88 // TODO(jdduke): Use |scroll_update_event.data.scrollUpdate.velocity{X,Y}|.
89 // The scroll must be of sufficient velocity to maintain the active fling.
90 const gfx::Vector2dF scroll_velocity =
91 gfx::ScaleVector2d(dx, 1. / time_since_last_boost_event);
92 if (scroll_velocity.LengthSquared() < kMinBoostTouchScrollSpeedSquare)
93 return false;
94
95 return true;
96 }
97
98 bool ShouldBoostFling(const gfx::Vector2dF& current_fling_velocity,
99 const WebGestureEvent& fling_start_event) {
100 DCHECK_EQ(WebInputEvent::GestureFlingStart, fling_start_event.type);
101
102 gfx::Vector2dF new_fling_velocity(
103 fling_start_event.data.flingStart.velocityX,
104 fling_start_event.data.flingStart.velocityY);
105
106 if (gfx::DotProduct(current_fling_velocity, new_fling_velocity) <= 0)
107 return false;
108
109 if (current_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
110 return false;
111
112 if (new_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare)
113 return false;
114
115 return true;
116 }
117
118 WebGestureEvent ObtainGestureScrollBegin(const WebGestureEvent& event) {
119 WebGestureEvent scroll_begin_event = event;
120 scroll_begin_event.type = WebInputEvent::GestureScrollBegin;
121 scroll_begin_event.data.scrollBegin.deltaXHint = 0;
122 scroll_begin_event.data.scrollBegin.deltaYHint = 0;
123 return scroll_begin_event;
124 }
125
126 void ReportInputEventLatencyUma(const WebInputEvent& event,
127 const ui::LatencyInfo& latency_info) {
128 if (!(event.type == WebInputEvent::GestureScrollBegin ||
129 event.type == WebInputEvent::GestureScrollUpdate ||
130 event.type == WebInputEvent::GesturePinchBegin ||
131 event.type == WebInputEvent::GesturePinchUpdate ||
132 event.type == WebInputEvent::GestureFlingStart)) {
133 return;
134 }
135
136 ui::LatencyInfo::LatencyMap::const_iterator it =
137 latency_info.latency_components().find(std::make_pair(
138 ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0));
139
140 if (it == latency_info.latency_components().end())
141 return;
142
143 base::TimeDelta delta = base::TimeTicks::Now() - it->second.event_time;
144 for (size_t i = 0; i < it->second.event_count; ++i) {
145 switch (event.type) {
146 case blink::WebInputEvent::GestureScrollBegin:
147 UMA_HISTOGRAM_CUSTOM_COUNTS(
148 "Event.Latency.RendererImpl.GestureScrollBegin",
149 delta.InMicroseconds(), 1, 1000000, 100);
150 break;
151 case blink::WebInputEvent::GestureScrollUpdate:
152 UMA_HISTOGRAM_CUSTOM_COUNTS(
153 // So named for historical reasons.
154 "Event.Latency.RendererImpl.GestureScroll2",
155 delta.InMicroseconds(), 1, 1000000, 100);
156 break;
157 case blink::WebInputEvent::GesturePinchBegin:
158 UMA_HISTOGRAM_CUSTOM_COUNTS(
159 "Event.Latency.RendererImpl.GesturePinchBegin",
160 delta.InMicroseconds(), 1, 1000000, 100);
161 break;
162 case blink::WebInputEvent::GesturePinchUpdate:
163 UMA_HISTOGRAM_CUSTOM_COUNTS(
164 "Event.Latency.RendererImpl.GesturePinchUpdate",
165 delta.InMicroseconds(), 1, 1000000, 100);
166 break;
167 case blink::WebInputEvent::GestureFlingStart:
168 UMA_HISTOGRAM_CUSTOM_COUNTS(
169 "Event.Latency.RendererImpl.GestureFlingStart",
170 delta.InMicroseconds(), 1, 1000000, 100);
171 break;
172 default:
173 NOTREACHED();
174 break;
175 }
176 }
177 }
178
179 } // namespace
180
181 namespace content {
182
183 InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler,
184 InputHandlerProxyClient* client)
185 : client_(client),
186 input_handler_(input_handler),
187 deferred_fling_cancel_time_seconds_(0),
188 synchronous_input_handler_(nullptr),
189 allow_root_animate_(true),
190 #ifndef NDEBUG
191 expect_scroll_update_end_(false),
192 #endif
193 gesture_scroll_on_impl_thread_(false),
194 gesture_pinch_on_impl_thread_(false),
195 fling_may_be_active_on_main_thread_(false),
196 disallow_horizontal_fling_scroll_(false),
197 disallow_vertical_fling_scroll_(false),
198 has_fling_animation_started_(false),
199 uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()) {
200 DCHECK(client);
201 input_handler_->BindToClient(this);
202 smooth_scroll_enabled_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
203 switches::kEnableSmoothScrolling);
204 cc::ScrollElasticityHelper* scroll_elasticity_helper =
205 input_handler_->CreateScrollElasticityHelper();
206 if (scroll_elasticity_helper) {
207 scroll_elasticity_controller_.reset(
208 new InputScrollElasticityController(scroll_elasticity_helper));
209 }
210 }
211
212 InputHandlerProxy::~InputHandlerProxy() {}
213
214 void InputHandlerProxy::WillShutdown() {
215 scroll_elasticity_controller_.reset();
216 input_handler_ = NULL;
217 client_->WillShutdown();
218 }
219
220 InputHandlerProxy::EventDisposition
221 InputHandlerProxy::HandleInputEventWithLatencyInfo(
222 const WebInputEvent& event,
223 ui::LatencyInfo* latency_info) {
224 DCHECK(input_handler_);
225
226 if (uma_latency_reporting_enabled_)
227 ReportInputEventLatencyUma(event, *latency_info);
228
229 TRACE_EVENT_WITH_FLOW1("input,benchmark",
230 "LatencyInfo.Flow",
231 TRACE_ID_DONT_MANGLE(latency_info->trace_id()),
232 TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
233 "step", "HandleInputEventImpl");
234
235 scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
236 input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info);
237 InputHandlerProxy::EventDisposition disposition = HandleInputEvent(event);
238 return disposition;
239 }
240
241 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent(
242 const WebInputEvent& event) {
243 DCHECK(input_handler_);
244 TRACE_EVENT1("input,benchmark", "InputHandlerProxy::HandleInputEvent",
245 "type", WebInputEventTraits::GetName(event.type));
246
247 if (FilterInputEventForFlingBoosting(event))
248 return DID_HANDLE;
249
250 switch (event.type) {
251 case WebInputEvent::MouseWheel:
252 return HandleMouseWheel(static_cast<const WebMouseWheelEvent&>(event));
253
254 case WebInputEvent::GestureScrollBegin:
255 return HandleGestureScrollBegin(
256 static_cast<const WebGestureEvent&>(event));
257
258 case WebInputEvent::GestureScrollUpdate:
259 return HandleGestureScrollUpdate(
260 static_cast<const WebGestureEvent&>(event));
261
262 case WebInputEvent::GestureScrollEnd:
263 return HandleGestureScrollEnd(static_cast<const WebGestureEvent&>(event));
264
265 case WebInputEvent::GesturePinchBegin: {
266 DCHECK(!gesture_pinch_on_impl_thread_);
267 const WebGestureEvent& gesture_event =
268 static_cast<const WebGestureEvent&>(event);
269 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad &&
270 input_handler_->HaveWheelEventHandlersAt(
271 gfx::Point(gesture_event.x, gesture_event.y))) {
272 return DID_NOT_HANDLE;
273 } else {
274 input_handler_->PinchGestureBegin();
275 gesture_pinch_on_impl_thread_ = true;
276 return DID_HANDLE;
277 }
278 }
279
280 case WebInputEvent::GesturePinchEnd:
281 if (gesture_pinch_on_impl_thread_) {
282 gesture_pinch_on_impl_thread_ = false;
283 input_handler_->PinchGestureEnd();
284 return DID_HANDLE;
285 } else {
286 return DID_NOT_HANDLE;
287 }
288
289 case WebInputEvent::GesturePinchUpdate: {
290 if (gesture_pinch_on_impl_thread_) {
291 const WebGestureEvent& gesture_event =
292 static_cast<const WebGestureEvent&>(event);
293 if (gesture_event.data.pinchUpdate.zoomDisabled)
294 return DROP_EVENT;
295 input_handler_->PinchGestureUpdate(
296 gesture_event.data.pinchUpdate.scale,
297 gfx::Point(gesture_event.x, gesture_event.y));
298 return DID_HANDLE;
299 } else {
300 return DID_NOT_HANDLE;
301 }
302 }
303
304 case WebInputEvent::GestureFlingStart:
305 return HandleGestureFlingStart(
306 *static_cast<const WebGestureEvent*>(&event));
307
308 case WebInputEvent::GestureFlingCancel:
309 if (CancelCurrentFling())
310 return DID_HANDLE;
311 else if (!fling_may_be_active_on_main_thread_)
312 return DROP_EVENT;
313 return DID_NOT_HANDLE;
314
315 case WebInputEvent::TouchStart:
316 return HandleTouchStart(static_cast<const WebTouchEvent&>(event));
317
318 case WebInputEvent::MouseMove: {
319 const WebMouseEvent& mouse_event =
320 static_cast<const WebMouseEvent&>(event);
321 // TODO(tony): Ignore when mouse buttons are down?
322 // TODO(davemoore): This should never happen, but bug #326635 showed some
323 // surprising crashes.
324 CHECK(input_handler_);
325 input_handler_->MouseMoveAt(gfx::Point(mouse_event.x, mouse_event.y));
326 return DID_NOT_HANDLE;
327 }
328
329 default:
330 if (WebInputEvent::isKeyboardEventType(event.type)) {
331 // Only call |CancelCurrentFling()| if a fling was active, as it will
332 // otherwise disrupt an in-progress touch scroll.
333 if (fling_curve_)
334 CancelCurrentFling();
335 }
336 break;
337 }
338
339 return DID_NOT_HANDLE;
340 }
341
342 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel(
343 const WebMouseWheelEvent& wheel_event) {
344 InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE;
345 cc::InputHandlerScrollResult scroll_result;
346
347 // TODO(ccameron): The rail information should be pushed down into
348 // InputHandler.
349 gfx::Vector2dF scroll_delta(
350 wheel_event.railsMode != WebInputEvent::RailsModeVertical
351 ? -wheel_event.deltaX
352 : 0,
353 wheel_event.railsMode != WebInputEvent::RailsModeHorizontal
354 ? -wheel_event.deltaY
355 : 0);
356
357 if (wheel_event.scrollByPage) {
358 // TODO(jamesr): We don't properly handle scroll by page in the compositor
359 // thread, so punt it to the main thread. http://crbug.com/236639
360 result = DID_NOT_HANDLE;
361 } else if (!wheel_event.canScroll) {
362 // Wheel events with |canScroll| == false will not trigger scrolling,
363 // only event handlers. Forward to the main thread.
364 result = DID_NOT_HANDLE;
365 } else if (smooth_scroll_enabled_ && !wheel_event.hasPreciseScrollingDeltas) {
366 cc::InputHandler::ScrollStatus scroll_status =
367 input_handler_->ScrollAnimated(gfx::Point(wheel_event.x, wheel_event.y),
368 scroll_delta);
369 switch (scroll_status) {
370 case cc::InputHandler::SCROLL_STARTED:
371 result = DID_HANDLE;
372 break;
373 case cc::InputHandler::SCROLL_IGNORED:
374 result = DROP_EVENT;
375 break;
376 default:
377 result = DID_NOT_HANDLE;
378 break;
379 }
380 } else {
381 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
382 gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::WHEEL);
383 switch (scroll_status) {
384 case cc::InputHandler::SCROLL_STARTED: {
385 TRACE_EVENT_INSTANT2("input",
386 "InputHandlerProxy::handle_input wheel scroll",
387 TRACE_EVENT_SCOPE_THREAD, "deltaX",
388 scroll_delta.x(), "deltaY", scroll_delta.y());
389 gfx::Point scroll_point(wheel_event.x, wheel_event.y);
390 scroll_result = input_handler_->ScrollBy(scroll_point, scroll_delta);
391 HandleOverscroll(scroll_point, scroll_result);
392 input_handler_->ScrollEnd();
393 result = scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
394 break;
395 }
396 case cc::InputHandler::SCROLL_IGNORED:
397 // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
398 // to properly sync scrollability it's safer to send the event to the
399 // main thread. Change back to DROP_EVENT once we have synchronization
400 // bugs sorted out.
401 result = DID_NOT_HANDLE;
402 break;
403 case cc::InputHandler::SCROLL_UNKNOWN:
404 case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
405 result = DID_NOT_HANDLE;
406 break;
407 case cc::InputHandler::ScrollStatusCount:
408 NOTREACHED();
409 break;
410 }
411 }
412
413 // Send the event and its disposition to the elasticity controller to update
414 // the over-scroll animation. If the event is to be handled on the main
415 // thread, the event and its disposition will be sent to the elasticity
416 // controller after being handled on the main thread.
417 if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE) {
418 // Note that the call to the elasticity controller is made asynchronously,
419 // to minimize divergence between main thread and impl thread event
420 // handling paths.
421 base::ThreadTaskRunnerHandle::Get()->PostTask(
422 FROM_HERE,
423 base::Bind(&InputScrollElasticityController::ObserveWheelEventAndResult,
424 scroll_elasticity_controller_->GetWeakPtr(), wheel_event,
425 scroll_result));
426 }
427 return result;
428 }
429
430 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
431 const WebGestureEvent& gesture_event) {
432 if (gesture_scroll_on_impl_thread_)
433 CancelCurrentFling();
434
435 #ifndef NDEBUG
436 DCHECK(!expect_scroll_update_end_);
437 expect_scroll_update_end_ = true;
438 #endif
439 cc::InputHandler::ScrollStatus scroll_status;
440 if (gesture_event.data.scrollBegin.targetViewport) {
441 scroll_status = input_handler_->RootScrollBegin(cc::InputHandler::GESTURE);
442 } else {
443 scroll_status = input_handler_->ScrollBegin(
444 gfx::Point(gesture_event.x, gesture_event.y),
445 cc::InputHandler::GESTURE);
446 }
447 UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult",
448 scroll_status,
449 cc::InputHandler::ScrollStatusCount);
450 switch (scroll_status) {
451 case cc::InputHandler::SCROLL_STARTED:
452 TRACE_EVENT_INSTANT0("input",
453 "InputHandlerProxy::handle_input gesture scroll",
454 TRACE_EVENT_SCOPE_THREAD);
455 gesture_scroll_on_impl_thread_ = true;
456 return DID_HANDLE;
457 case cc::InputHandler::SCROLL_UNKNOWN:
458 case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
459 return DID_NOT_HANDLE;
460 case cc::InputHandler::SCROLL_IGNORED:
461 return DROP_EVENT;
462 case cc::InputHandler::ScrollStatusCount:
463 NOTREACHED();
464 break;
465 }
466 return DID_NOT_HANDLE;
467 }
468
469 InputHandlerProxy::EventDisposition
470 InputHandlerProxy::HandleGestureScrollUpdate(
471 const WebGestureEvent& gesture_event) {
472 #ifndef NDEBUG
473 DCHECK(expect_scroll_update_end_);
474 #endif
475
476 if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
477 return DID_NOT_HANDLE;
478
479 gfx::Point scroll_point(gesture_event.x, gesture_event.y);
480 gfx::Vector2dF scroll_delta(-gesture_event.data.scrollUpdate.deltaX,
481 -gesture_event.data.scrollUpdate.deltaY);
482 cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(
483 scroll_point, scroll_delta);
484 HandleOverscroll(scroll_point, scroll_result);
485 return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
486 }
487
488 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
489 const WebGestureEvent& gesture_event) {
490 #ifndef NDEBUG
491 DCHECK(expect_scroll_update_end_);
492 expect_scroll_update_end_ = false;
493 #endif
494 input_handler_->ScrollEnd();
495 if (!gesture_scroll_on_impl_thread_)
496 return DID_NOT_HANDLE;
497 gesture_scroll_on_impl_thread_ = false;
498 return DID_HANDLE;
499 }
500
501 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart(
502 const WebGestureEvent& gesture_event) {
503 cc::InputHandler::ScrollStatus scroll_status =
504 cc::InputHandler::SCROLL_ON_MAIN_THREAD;
505 switch (gesture_event.sourceDevice) {
506 case blink::WebGestureDeviceTouchpad:
507 if (gesture_event.data.flingStart.targetViewport) {
508 scroll_status = input_handler_->RootScrollBegin(
509 cc::InputHandler::NON_BUBBLING_GESTURE);
510 } else {
511 scroll_status = input_handler_->ScrollBegin(
512 gfx::Point(gesture_event.x, gesture_event.y),
513 cc::InputHandler::NON_BUBBLING_GESTURE);
514 }
515 break;
516 case blink::WebGestureDeviceTouchscreen:
517 if (!gesture_scroll_on_impl_thread_)
518 scroll_status = cc::InputHandler::SCROLL_ON_MAIN_THREAD;
519 else
520 scroll_status = input_handler_->FlingScrollBegin();
521 break;
522 case blink::WebGestureDeviceUninitialized:
523 NOTREACHED();
524 return DID_NOT_HANDLE;
525 }
526
527 #ifndef NDEBUG
528 expect_scroll_update_end_ = false;
529 #endif
530
531 switch (scroll_status) {
532 case cc::InputHandler::SCROLL_STARTED: {
533 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad)
534 input_handler_->ScrollEnd();
535
536 const float vx = gesture_event.data.flingStart.velocityX;
537 const float vy = gesture_event.data.flingStart.velocityY;
538 current_fling_velocity_ = gfx::Vector2dF(vx, vy);
539 DCHECK(!current_fling_velocity_.IsZero());
540 fling_curve_.reset(client_->CreateFlingAnimationCurve(
541 gesture_event.sourceDevice,
542 WebFloatPoint(vx, vy),
543 blink::WebSize()));
544 disallow_horizontal_fling_scroll_ = !vx;
545 disallow_vertical_fling_scroll_ = !vy;
546 TRACE_EVENT_ASYNC_BEGIN2("input,benchmark",
547 "InputHandlerProxy::HandleGestureFling::started",
548 this, "vx", vx, "vy", vy);
549 // Note that the timestamp will only be used to kickstart the animation if
550 // its sufficiently close to the timestamp of the first call |Animate()|.
551 has_fling_animation_started_ = false;
552 fling_parameters_.startTime = gesture_event.timeStampSeconds;
553 fling_parameters_.delta = WebFloatPoint(vx, vy);
554 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
555 fling_parameters_.globalPoint =
556 WebPoint(gesture_event.globalX, gesture_event.globalY);
557 fling_parameters_.modifiers = gesture_event.modifiers;
558 fling_parameters_.sourceDevice = gesture_event.sourceDevice;
559 RequestAnimation();
560 return DID_HANDLE;
561 }
562 case cc::InputHandler::SCROLL_UNKNOWN:
563 case cc::InputHandler::SCROLL_ON_MAIN_THREAD: {
564 TRACE_EVENT_INSTANT0("input",
565 "InputHandlerProxy::HandleGestureFling::"
566 "scroll_on_main_thread",
567 TRACE_EVENT_SCOPE_THREAD);
568 gesture_scroll_on_impl_thread_ = false;
569 fling_may_be_active_on_main_thread_ = true;
570 return DID_NOT_HANDLE;
571 }
572 case cc::InputHandler::SCROLL_IGNORED: {
573 TRACE_EVENT_INSTANT0(
574 "input",
575 "InputHandlerProxy::HandleGestureFling::ignored",
576 TRACE_EVENT_SCOPE_THREAD);
577 gesture_scroll_on_impl_thread_ = false;
578 if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
579 // We still pass the curve to the main thread if there's nothing
580 // scrollable, in case something
581 // registers a handler before the curve is over.
582 return DID_NOT_HANDLE;
583 }
584 return DROP_EVENT;
585 }
586 case cc::InputHandler::ScrollStatusCount:
587 NOTREACHED();
588 break;
589 }
590 return DID_NOT_HANDLE;
591 }
592
593 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart(
594 const blink::WebTouchEvent& touch_event) {
595 for (size_t i = 0; i < touch_event.touchesLength; ++i) {
596 if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
597 continue;
598 if (input_handler_->DoTouchEventsBlockScrollAt(
599 gfx::Point(touch_event.touches[i].position.x,
600 touch_event.touches[i].position.y))) {
601 // TODO(rbyers): We should consider still sending the touch events to
602 // main asynchronously (crbug.com/455539).
603 return DID_NOT_HANDLE;
604 }
605 }
606 return DROP_EVENT;
607 }
608
609 bool InputHandlerProxy::FilterInputEventForFlingBoosting(
610 const WebInputEvent& event) {
611 if (!WebInputEvent::isGestureEventType(event.type))
612 return false;
613
614 if (!fling_curve_) {
615 DCHECK(!deferred_fling_cancel_time_seconds_);
616 return false;
617 }
618
619 const WebGestureEvent& gesture_event =
620 static_cast<const WebGestureEvent&>(event);
621 if (gesture_event.type == WebInputEvent::GestureFlingCancel) {
622 if (gesture_event.data.flingCancel.preventBoosting)
623 return false;
624
625 if (current_fling_velocity_.LengthSquared() < kMinBoostFlingSpeedSquare)
626 return false;
627
628 TRACE_EVENT_INSTANT0("input",
629 "InputHandlerProxy::FlingBoostStart",
630 TRACE_EVENT_SCOPE_THREAD);
631 deferred_fling_cancel_time_seconds_ =
632 event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
633 return true;
634 }
635
636 // A fling is either inactive or is "free spinning", i.e., has yet to be
637 // interrupted by a touch gesture, in which case there is nothing to filter.
638 if (!deferred_fling_cancel_time_seconds_)
639 return false;
640
641 // Gestures from a different source should immediately interrupt the fling.
642 if (gesture_event.sourceDevice != fling_parameters_.sourceDevice) {
643 CancelCurrentFling();
644 return false;
645 }
646
647 switch (gesture_event.type) {
648 case WebInputEvent::GestureTapCancel:
649 case WebInputEvent::GestureTapDown:
650 return false;
651
652 case WebInputEvent::GestureScrollBegin:
653 if (!input_handler_->IsCurrentlyScrollingLayerAt(
654 gfx::Point(gesture_event.x, gesture_event.y),
655 fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad
656 ? cc::InputHandler::NON_BUBBLING_GESTURE
657 : cc::InputHandler::GESTURE)) {
658 CancelCurrentFling();
659 return false;
660 }
661
662 // TODO(jdduke): Use |gesture_event.data.scrollBegin.delta{X,Y}Hint| to
663 // determine if the ScrollBegin should immediately cancel the fling.
664 ExtendBoostedFlingTimeout(gesture_event);
665 return true;
666
667 case WebInputEvent::GestureScrollUpdate: {
668 const double time_since_last_boost_event =
669 event.timeStampSeconds - last_fling_boost_event_.timeStampSeconds;
670 const double time_since_last_fling_animate = std::max(
671 0.0, event.timeStampSeconds - InSecondsF(last_fling_animate_time_));
672 if (ShouldSuppressScrollForFlingBoosting(current_fling_velocity_,
673 gesture_event,
674 time_since_last_boost_event,
675 time_since_last_fling_animate)) {
676 ExtendBoostedFlingTimeout(gesture_event);
677 return true;
678 }
679
680 CancelCurrentFling();
681 return false;
682 }
683
684 case WebInputEvent::GestureScrollEnd:
685 // Clear the last fling boost event *prior* to fling cancellation,
686 // preventing insertion of a synthetic GestureScrollBegin.
687 last_fling_boost_event_ = WebGestureEvent();
688 CancelCurrentFling();
689 return true;
690
691 case WebInputEvent::GestureFlingStart: {
692 DCHECK_EQ(fling_parameters_.sourceDevice, gesture_event.sourceDevice);
693
694 bool fling_boosted =
695 fling_parameters_.modifiers == gesture_event.modifiers &&
696 ShouldBoostFling(current_fling_velocity_, gesture_event);
697
698 gfx::Vector2dF new_fling_velocity(
699 gesture_event.data.flingStart.velocityX,
700 gesture_event.data.flingStart.velocityY);
701 DCHECK(!new_fling_velocity.IsZero());
702
703 if (fling_boosted)
704 current_fling_velocity_ += new_fling_velocity;
705 else
706 current_fling_velocity_ = new_fling_velocity;
707
708 WebFloatPoint velocity(current_fling_velocity_.x(),
709 current_fling_velocity_.y());
710 deferred_fling_cancel_time_seconds_ = 0;
711 disallow_horizontal_fling_scroll_ = !velocity.x;
712 disallow_vertical_fling_scroll_ = !velocity.y;
713 last_fling_boost_event_ = WebGestureEvent();
714 fling_curve_.reset(client_->CreateFlingAnimationCurve(
715 gesture_event.sourceDevice,
716 velocity,
717 blink::WebSize()));
718 fling_parameters_.startTime = gesture_event.timeStampSeconds;
719 fling_parameters_.delta = velocity;
720 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
721 fling_parameters_.globalPoint =
722 WebPoint(gesture_event.globalX, gesture_event.globalY);
723
724 TRACE_EVENT_INSTANT2("input",
725 fling_boosted ? "InputHandlerProxy::FlingBoosted"
726 : "InputHandlerProxy::FlingReplaced",
727 TRACE_EVENT_SCOPE_THREAD,
728 "vx",
729 current_fling_velocity_.x(),
730 "vy",
731 current_fling_velocity_.y());
732
733 // The client expects balanced calls between a consumed GestureFlingStart
734 // and |DidStopFlinging()|.
735 client_->DidStopFlinging();
736 return true;
737 }
738
739 default:
740 // All other types of gestures (taps, presses, etc...) will complete the
741 // deferred fling cancellation.
742 CancelCurrentFling();
743 return false;
744 }
745 }
746
747 void InputHandlerProxy::ExtendBoostedFlingTimeout(
748 const blink::WebGestureEvent& event) {
749 TRACE_EVENT_INSTANT0("input",
750 "InputHandlerProxy::ExtendBoostedFlingTimeout",
751 TRACE_EVENT_SCOPE_THREAD);
752 deferred_fling_cancel_time_seconds_ =
753 event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds;
754 last_fling_boost_event_ = event;
755 }
756
757 void InputHandlerProxy::Animate(base::TimeTicks time) {
758 // If using synchronous animate, then only expect Animate attempts started by
759 // the synchronous system. Don't let the InputHandler try to Animate also.
760 DCHECK(!input_handler_->IsCurrentlyScrollingInnerViewport() ||
761 allow_root_animate_);
762
763 if (scroll_elasticity_controller_)
764 scroll_elasticity_controller_->Animate(time);
765
766 if (!fling_curve_)
767 return;
768
769 last_fling_animate_time_ = time;
770 double monotonic_time_sec = InSecondsF(time);
771
772 if (deferred_fling_cancel_time_seconds_ &&
773 monotonic_time_sec > deferred_fling_cancel_time_seconds_) {
774 CancelCurrentFling();
775 return;
776 }
777
778 client_->DidAnimateForInput();
779
780 if (!has_fling_animation_started_) {
781 has_fling_animation_started_ = true;
782 // Guard against invalid, future or sufficiently stale start times, as there
783 // are no guarantees fling event and animation timestamps are compatible.
784 if (!fling_parameters_.startTime ||
785 monotonic_time_sec <= fling_parameters_.startTime ||
786 monotonic_time_sec >= fling_parameters_.startTime +
787 kMaxSecondsFromFlingTimestampToFirstAnimate) {
788 fling_parameters_.startTime = monotonic_time_sec;
789 RequestAnimation();
790 return;
791 }
792 }
793
794 bool fling_is_active =
795 fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime,
796 this);
797
798 if (disallow_vertical_fling_scroll_ && disallow_horizontal_fling_scroll_)
799 fling_is_active = false;
800
801 if (fling_is_active) {
802 RequestAnimation();
803 } else {
804 TRACE_EVENT_INSTANT0("input",
805 "InputHandlerProxy::animate::flingOver",
806 TRACE_EVENT_SCOPE_THREAD);
807 CancelCurrentFling();
808 }
809 }
810
811 void InputHandlerProxy::MainThreadHasStoppedFlinging() {
812 fling_may_be_active_on_main_thread_ = false;
813 client_->DidStopFlinging();
814 }
815
816 void InputHandlerProxy::ReconcileElasticOverscrollAndRootScroll() {
817 if (scroll_elasticity_controller_)
818 scroll_elasticity_controller_->ReconcileStretchAndScroll();
819 }
820
821 void InputHandlerProxy::UpdateRootLayerStateForSynchronousInputHandler(
822 const gfx::ScrollOffset& total_scroll_offset,
823 const gfx::ScrollOffset& max_scroll_offset,
824 const gfx::SizeF& scrollable_size,
825 float page_scale_factor,
826 float min_page_scale_factor,
827 float max_page_scale_factor) {
828 if (synchronous_input_handler_) {
829 synchronous_input_handler_->UpdateRootLayerState(
830 total_scroll_offset, max_scroll_offset, scrollable_size,
831 page_scale_factor, min_page_scale_factor, max_page_scale_factor);
832 }
833 }
834
835 void InputHandlerProxy::SetOnlySynchronouslyAnimateRootFlings(
836 SynchronousInputHandler* synchronous_input_handler) {
837 allow_root_animate_ = !synchronous_input_handler;
838 synchronous_input_handler_ = synchronous_input_handler;
839 if (synchronous_input_handler_)
840 input_handler_->RequestUpdateForSynchronousInputHandler();
841 }
842
843 void InputHandlerProxy::SynchronouslyAnimate(base::TimeTicks time) {
844 // When this function is used, SetOnlySynchronouslyAnimate() should have been
845 // previously called. IOW you should either be entirely in synchronous mode or
846 // not.
847 DCHECK(synchronous_input_handler_);
848 DCHECK(!allow_root_animate_);
849 base::AutoReset<bool> reset(&allow_root_animate_, true);
850 Animate(time);
851 }
852
853 void InputHandlerProxy::SynchronouslySetRootScrollOffset(
854 const gfx::ScrollOffset& root_offset) {
855 DCHECK(synchronous_input_handler_);
856 input_handler_->SetSynchronousInputHandlerRootScrollOffset(root_offset);
857 }
858
859 void InputHandlerProxy::HandleOverscroll(
860 const gfx::Point& causal_event_viewport_point,
861 const cc::InputHandlerScrollResult& scroll_result) {
862 DCHECK(client_);
863 if (!scroll_result.did_overscroll_root)
864 return;
865
866 TRACE_EVENT2("input",
867 "InputHandlerProxy::DidOverscroll",
868 "dx",
869 scroll_result.unused_scroll_delta.x(),
870 "dy",
871 scroll_result.unused_scroll_delta.y());
872
873 DidOverscrollParams params;
874 params.accumulated_overscroll = scroll_result.accumulated_root_overscroll;
875 params.latest_overscroll_delta = scroll_result.unused_scroll_delta;
876 params.current_fling_velocity =
877 ToClientScrollIncrement(current_fling_velocity_);
878 params.causal_event_viewport_point = gfx::PointF(causal_event_viewport_point);
879
880 if (fling_curve_) {
881 static const int kFlingOverscrollThreshold = 1;
882 disallow_horizontal_fling_scroll_ |=
883 std::abs(params.accumulated_overscroll.x()) >=
884 kFlingOverscrollThreshold;
885 disallow_vertical_fling_scroll_ |=
886 std::abs(params.accumulated_overscroll.y()) >=
887 kFlingOverscrollThreshold;
888 }
889
890 client_->DidOverscroll(params);
891 }
892
893 bool InputHandlerProxy::CancelCurrentFling() {
894 if (CancelCurrentFlingWithoutNotifyingClient()) {
895 client_->DidStopFlinging();
896 return true;
897 }
898 return false;
899 }
900
901 bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() {
902 bool had_fling_animation = fling_curve_;
903 if (had_fling_animation &&
904 fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) {
905 input_handler_->ScrollEnd();
906 TRACE_EVENT_ASYNC_END0(
907 "input",
908 "InputHandlerProxy::HandleGestureFling::started",
909 this);
910 }
911
912 TRACE_EVENT_INSTANT1("input",
913 "InputHandlerProxy::CancelCurrentFling",
914 TRACE_EVENT_SCOPE_THREAD,
915 "had_fling_animation",
916 had_fling_animation);
917 fling_curve_.reset();
918 has_fling_animation_started_ = false;
919 gesture_scroll_on_impl_thread_ = false;
920 current_fling_velocity_ = gfx::Vector2dF();
921 fling_parameters_ = blink::WebActiveWheelFlingParameters();
922
923 if (deferred_fling_cancel_time_seconds_) {
924 deferred_fling_cancel_time_seconds_ = 0;
925
926 WebGestureEvent last_fling_boost_event = last_fling_boost_event_;
927 last_fling_boost_event_ = WebGestureEvent();
928 if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin ||
929 last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) {
930 // Synthesize a GestureScrollBegin, as the original was suppressed.
931 HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event));
932 }
933 }
934
935 return had_fling_animation;
936 }
937
938 void InputHandlerProxy::RequestAnimation() {
939 // When a SynchronousInputHandler is present, root flings should go through
940 // it to allow it to control when or if the root fling is animated. Non-root
941 // flings always go through the normal InputHandler.
942 if (synchronous_input_handler_ &&
943 input_handler_->IsCurrentlyScrollingInnerViewport())
944 synchronous_input_handler_->SetNeedsSynchronousAnimateInput();
945 else
946 input_handler_->SetNeedsAnimateInput();
947 }
948
949 bool InputHandlerProxy::TouchpadFlingScroll(
950 const WebFloatSize& increment) {
951 WebMouseWheelEvent synthetic_wheel;
952 synthetic_wheel.type = WebInputEvent::MouseWheel;
953 synthetic_wheel.deltaX = increment.width;
954 synthetic_wheel.deltaY = increment.height;
955 synthetic_wheel.hasPreciseScrollingDeltas = true;
956 synthetic_wheel.x = fling_parameters_.point.x;
957 synthetic_wheel.y = fling_parameters_.point.y;
958 synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
959 synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
960 synthetic_wheel.modifiers = fling_parameters_.modifiers;
961
962 InputHandlerProxy::EventDisposition disposition =
963 HandleInputEvent(synthetic_wheel);
964 switch (disposition) {
965 case DID_HANDLE:
966 return true;
967 case DROP_EVENT:
968 break;
969 case DID_NOT_HANDLE:
970 TRACE_EVENT_INSTANT0("input",
971 "InputHandlerProxy::scrollBy::AbortFling",
972 TRACE_EVENT_SCOPE_THREAD);
973 // If we got a DID_NOT_HANDLE, that means we need to deliver wheels on the
974 // main thread. In this case we need to schedule a commit and transfer the
975 // fling curve over to the main thread and run the rest of the wheels from
976 // there. This can happen when flinging a page that contains a scrollable
977 // subarea that we can't scroll on the thread if the fling starts outside
978 // the subarea but then is flung "under" the pointer.
979 client_->TransferActiveWheelFlingAnimation(fling_parameters_);
980 fling_may_be_active_on_main_thread_ = true;
981 CancelCurrentFlingWithoutNotifyingClient();
982 break;
983 }
984
985 return false;
986 }
987
988 bool InputHandlerProxy::scrollBy(const WebFloatSize& increment,
989 const WebFloatSize& velocity) {
990 WebFloatSize clipped_increment;
991 WebFloatSize clipped_velocity;
992 if (!disallow_horizontal_fling_scroll_) {
993 clipped_increment.width = increment.width;
994 clipped_velocity.width = velocity.width;
995 }
996 if (!disallow_vertical_fling_scroll_) {
997 clipped_increment.height = increment.height;
998 clipped_velocity.height = velocity.height;
999 }
1000
1001 current_fling_velocity_ = clipped_velocity;
1002
1003 // Early out if the increment is zero, but avoid early terimination if the
1004 // velocity is still non-zero.
1005 if (clipped_increment == WebFloatSize())
1006 return clipped_velocity != WebFloatSize();
1007
1008 TRACE_EVENT2("input",
1009 "InputHandlerProxy::scrollBy",
1010 "x",
1011 clipped_increment.width,
1012 "y",
1013 clipped_increment.height);
1014
1015 bool did_scroll = false;
1016
1017 switch (fling_parameters_.sourceDevice) {
1018 case blink::WebGestureDeviceTouchpad:
1019 did_scroll = TouchpadFlingScroll(clipped_increment);
1020 break;
1021 case blink::WebGestureDeviceTouchscreen: {
1022 clipped_increment = ToClientScrollIncrement(clipped_increment);
1023 cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(
1024 fling_parameters_.point, clipped_increment);
1025 HandleOverscroll(fling_parameters_.point, scroll_result);
1026 did_scroll = scroll_result.did_scroll;
1027 } break;
1028 case blink::WebGestureDeviceUninitialized:
1029 NOTREACHED();
1030 return false;
1031 }
1032
1033 if (did_scroll) {
1034 fling_parameters_.cumulativeScroll.width += clipped_increment.width;
1035 fling_parameters_.cumulativeScroll.height += clipped_increment.height;
1036 }
1037
1038 // It's possible the provided |increment| is sufficiently small as to not
1039 // trigger a scroll, e.g., with a trivial time delta between fling updates.
1040 // Return true in this case to prevent early fling termination.
1041 if (std::abs(clipped_increment.width) < kScrollEpsilon &&
1042 std::abs(clipped_increment.height) < kScrollEpsilon)
1043 return true;
1044
1045 return did_scroll;
1046 }
1047
1048 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/input/input_handler_proxy.h ('k') | content/renderer/input/input_handler_proxy_client.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698