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 821