| OLD | NEW |
| (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 | |
| OLD | NEW |