OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 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/gpu/input_handler_proxy.h" |
| 6 |
| 7 #include "base/debug/trace_event.h" |
| 8 #include "base/logging.h" |
| 9 #include "content/renderer/gpu/input_handler_proxy_client.h" |
| 10 #include "third_party/WebKit/Source/Platform/chromium/public/Platform.h" |
| 11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" |
| 12 |
| 13 using WebKit::WebFloatPoint; |
| 14 using WebKit::WebFloatSize; |
| 15 using WebKit::WebGestureEvent; |
| 16 using WebKit::WebInputEvent; |
| 17 using WebKit::WebMouseWheelEvent; |
| 18 using WebKit::WebPoint; |
| 19 using WebKit::WebScrollbar; |
| 20 using WebKit::WebTouchEvent; |
| 21 |
| 22 namespace content { |
| 23 |
| 24 InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler) |
| 25 : client_(NULL), |
| 26 input_handler_(input_handler), |
| 27 #ifndef NDEBUG |
| 28 expect_scroll_update_end_(false), |
| 29 expect_pinch_update_end_(false), |
| 30 #endif |
| 31 gesture_scroll_on_impl_thread_(false), |
| 32 gesture_pinch_on_impl_thread_(false), |
| 33 fling_may_be_active_on_main_thread_(false) { |
| 34 input_handler_->BindToClient(this); |
| 35 } |
| 36 |
| 37 InputHandlerProxy::~InputHandlerProxy() {} |
| 38 |
| 39 void InputHandlerProxy::WillShutdown() { |
| 40 input_handler_ = NULL; |
| 41 DCHECK(client_); |
| 42 client_->WillShutdown(); |
| 43 } |
| 44 |
| 45 void InputHandlerProxy::SetClient(InputHandlerProxyClient* client) { |
| 46 DCHECK(!client_ || !client); |
| 47 client_ = client; |
| 48 } |
| 49 |
| 50 void InputHandlerProxy::HandleInputEvent(const WebInputEvent& event) { |
| 51 DCHECK(client_); |
| 52 DCHECK(input_handler_); |
| 53 |
| 54 InputHandlerProxy::EventDisposition disposition = |
| 55 HandleInputEventInternal(event); |
| 56 switch (disposition) { |
| 57 case DidHandle: |
| 58 client_->DidHandleInputEvent(); |
| 59 break; |
| 60 case DidNotHandle: |
| 61 client_->DidNotHandleInputEvent(true /* send_to_widget */); |
| 62 break; |
| 63 case DropEvent: |
| 64 client_->DidNotHandleInputEvent(false /* send_to_widget */); |
| 65 break; |
| 66 } |
| 67 if (event.modifiers & WebInputEvent::IsLastInputEventForCurrentVSync) { |
| 68 input_handler_->DidReceiveLastInputEventForVSync( |
| 69 base::TimeTicks::FromInternalValue(event.timeStampSeconds * |
| 70 base::Time::kMicrosecondsPerSecond)); |
| 71 } |
| 72 } |
| 73 |
| 74 InputHandlerProxy::EventDisposition |
| 75 InputHandlerProxy::HandleInputEventInternal(const WebInputEvent& event) { |
| 76 if (event.type == WebInputEvent::MouseWheel) { |
| 77 const WebMouseWheelEvent& wheel_event = |
| 78 *static_cast<const WebMouseWheelEvent*>(&event); |
| 79 if (wheel_event.scrollByPage) { |
| 80 // TODO(jamesr): We don't properly handle scroll by page in the compositor |
| 81 // thread, so punt it to the main thread. http://crbug.com/236639 |
| 82 return DidNotHandle; |
| 83 } |
| 84 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( |
| 85 gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel); |
| 86 switch (scroll_status) { |
| 87 case cc::InputHandler::ScrollStarted: { |
| 88 TRACE_EVENT_INSTANT2( |
| 89 "renderer", |
| 90 "InputHandlerProxy::handle_input wheel scroll", |
| 91 TRACE_EVENT_SCOPE_THREAD, |
| 92 "deltaX", |
| 93 -wheel_event.deltaX, |
| 94 "deltaY", |
| 95 -wheel_event.deltaY); |
| 96 bool did_scroll = input_handler_->ScrollBy( |
| 97 gfx::Point(wheel_event.x, wheel_event.y), |
| 98 gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY)); |
| 99 input_handler_->ScrollEnd(); |
| 100 return did_scroll ? DidHandle : DropEvent; |
| 101 } |
| 102 case cc::InputHandler::ScrollIgnored: |
| 103 // TODO(jamesr): This should be DropEvent, but in cases where we fail to |
| 104 // properly sync scrollability it's safer to send the |
| 105 // event to the main thread. Change back to DropEvent once we have |
| 106 // synchronization bugs sorted out. |
| 107 return DidNotHandle; |
| 108 case cc::InputHandler::ScrollOnMainThread: |
| 109 return DidNotHandle; |
| 110 } |
| 111 } else if (event.type == WebInputEvent::GestureScrollBegin) { |
| 112 DCHECK(!gesture_scroll_on_impl_thread_); |
| 113 #ifndef NDEBUG |
| 114 DCHECK(!expect_scroll_update_end_); |
| 115 expect_scroll_update_end_ = true; |
| 116 #endif |
| 117 const WebGestureEvent& gesture_event = |
| 118 *static_cast<const WebGestureEvent*>(&event); |
| 119 cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( |
| 120 gfx::Point(gesture_event.x, gesture_event.y), |
| 121 cc::InputHandler::Gesture); |
| 122 switch (scroll_status) { |
| 123 case cc::InputHandler::ScrollStarted: |
| 124 gesture_scroll_on_impl_thread_ = true; |
| 125 return DidHandle; |
| 126 case cc::InputHandler::ScrollOnMainThread: |
| 127 return DidNotHandle; |
| 128 case cc::InputHandler::ScrollIgnored: |
| 129 return DropEvent; |
| 130 } |
| 131 } else if (event.type == WebInputEvent::GestureScrollUpdate) { |
| 132 #ifndef NDEBUG |
| 133 DCHECK(expect_scroll_update_end_); |
| 134 #endif |
| 135 |
| 136 if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_) |
| 137 return DidNotHandle; |
| 138 |
| 139 const WebGestureEvent& gesture_event = |
| 140 *static_cast<const WebGestureEvent*>(&event); |
| 141 bool did_scroll = input_handler_->ScrollBy( |
| 142 gfx::Point(gesture_event.x, gesture_event.y), |
| 143 gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX, |
| 144 -gesture_event.data.scrollUpdate.deltaY)); |
| 145 return did_scroll ? DidHandle : DropEvent; |
| 146 } else if (event.type == WebInputEvent::GestureScrollEnd) { |
| 147 #ifndef NDEBUG |
| 148 DCHECK(expect_scroll_update_end_); |
| 149 expect_scroll_update_end_ = false; |
| 150 #endif |
| 151 if (!gesture_scroll_on_impl_thread_) |
| 152 return DidNotHandle; |
| 153 |
| 154 input_handler_->ScrollEnd(); |
| 155 gesture_scroll_on_impl_thread_ = false; |
| 156 return DidHandle; |
| 157 } else if (event.type == WebInputEvent::GesturePinchBegin) { |
| 158 #ifndef NDEBUG |
| 159 DCHECK(!expect_pinch_update_end_); |
| 160 expect_pinch_update_end_ = true; |
| 161 #endif |
| 162 input_handler_->PinchGestureBegin(); |
| 163 gesture_pinch_on_impl_thread_ = true; |
| 164 return DidHandle; |
| 165 } else if (event.type == WebInputEvent::GesturePinchEnd) { |
| 166 #ifndef NDEBUG |
| 167 DCHECK(expect_pinch_update_end_); |
| 168 expect_pinch_update_end_ = false; |
| 169 #endif |
| 170 gesture_pinch_on_impl_thread_ = false; |
| 171 input_handler_->PinchGestureEnd(); |
| 172 return DidHandle; |
| 173 } else if (event.type == WebInputEvent::GesturePinchUpdate) { |
| 174 #ifndef NDEBUG |
| 175 DCHECK(expect_pinch_update_end_); |
| 176 #endif |
| 177 const WebGestureEvent& gesture_event = |
| 178 *static_cast<const WebGestureEvent*>(&event); |
| 179 input_handler_->PinchGestureUpdate( |
| 180 gesture_event.data.pinchUpdate.scale, |
| 181 gfx::Point(gesture_event.x, gesture_event.y)); |
| 182 return DidHandle; |
| 183 } else if (event.type == WebInputEvent::GestureFlingStart) { |
| 184 const WebGestureEvent& gesture_event = |
| 185 *static_cast<const WebGestureEvent*>(&event); |
| 186 return HandleGestureFling(gesture_event); |
| 187 } else if (event.type == WebInputEvent::GestureFlingCancel) { |
| 188 if (CancelCurrentFling()) |
| 189 return DidHandle; |
| 190 else if (!fling_may_be_active_on_main_thread_) |
| 191 return DropEvent; |
| 192 } else if (event.type == WebInputEvent::TouchStart) { |
| 193 const WebTouchEvent& touch_event = |
| 194 *static_cast<const WebTouchEvent*>(&event); |
| 195 if (!input_handler_->HaveTouchEventHandlersAt(touch_event.touches[0] |
| 196 .position)) |
| 197 return DropEvent; |
| 198 } else if (WebInputEvent::isKeyboardEventType(event.type)) { |
| 199 CancelCurrentFling(); |
| 200 } |
| 201 |
| 202 return DidNotHandle; |
| 203 } |
| 204 |
| 205 InputHandlerProxy::EventDisposition |
| 206 InputHandlerProxy::HandleGestureFling( |
| 207 const WebGestureEvent& gesture_event) { |
| 208 cc::InputHandler::ScrollStatus scroll_status; |
| 209 |
| 210 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) { |
| 211 scroll_status = input_handler_->ScrollBegin( |
| 212 gfx::Point(gesture_event.x, gesture_event.y), |
| 213 cc::InputHandler::NonBubblingGesture); |
| 214 } else { |
| 215 if (!gesture_scroll_on_impl_thread_) |
| 216 scroll_status = cc::InputHandler::ScrollOnMainThread; |
| 217 else |
| 218 scroll_status = input_handler_->FlingScrollBegin(); |
| 219 } |
| 220 |
| 221 #ifndef NDEBUG |
| 222 expect_scroll_update_end_ = false; |
| 223 #endif |
| 224 |
| 225 switch (scroll_status) { |
| 226 case cc::InputHandler::ScrollStarted: { |
| 227 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) |
| 228 input_handler_->ScrollEnd(); |
| 229 |
| 230 fling_curve_.reset(client_->CreateFlingAnimationCurve( |
| 231 gesture_event.sourceDevice, |
| 232 WebFloatPoint(gesture_event.data.flingStart.velocityX, |
| 233 gesture_event.data.flingStart.velocityY), |
| 234 WebKit::WebSize())); |
| 235 TRACE_EVENT_ASYNC_BEGIN0( |
| 236 "renderer", |
| 237 "InputHandlerProxy::HandleGestureFling::started", |
| 238 this); |
| 239 fling_parameters_.delta = |
| 240 WebFloatPoint(gesture_event.data.flingStart.velocityX, |
| 241 gesture_event.data.flingStart.velocityY); |
| 242 fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y); |
| 243 fling_parameters_.globalPoint = |
| 244 WebPoint(gesture_event.globalX, gesture_event.globalY); |
| 245 fling_parameters_.modifiers = gesture_event.modifiers; |
| 246 fling_parameters_.sourceDevice = gesture_event.sourceDevice; |
| 247 input_handler_->ScheduleAnimation(); |
| 248 return DidHandle; |
| 249 } |
| 250 case cc::InputHandler::ScrollOnMainThread: { |
| 251 TRACE_EVENT_INSTANT0("renderer", |
| 252 "InputHandlerProxy::HandleGestureFling::" |
| 253 "scroll_on_main_thread", |
| 254 TRACE_EVENT_SCOPE_THREAD); |
| 255 fling_may_be_active_on_main_thread_ = true; |
| 256 return DidNotHandle; |
| 257 } |
| 258 case cc::InputHandler::ScrollIgnored: { |
| 259 TRACE_EVENT_INSTANT0( |
| 260 "renderer", |
| 261 "InputHandlerProxy::HandleGestureFling::ignored", |
| 262 TRACE_EVENT_SCOPE_THREAD); |
| 263 if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) { |
| 264 // We still pass the curve to the main thread if there's nothing |
| 265 // scrollable, in case something |
| 266 // registers a handler before the curve is over. |
| 267 return DidNotHandle; |
| 268 } |
| 269 return DropEvent; |
| 270 } |
| 271 } |
| 272 return DidNotHandle; |
| 273 } |
| 274 |
| 275 void InputHandlerProxy::Animate(base::TimeTicks time) { |
| 276 if (!fling_curve_) |
| 277 return; |
| 278 |
| 279 double monotonic_time_sec = (time - base::TimeTicks()).InSecondsF(); |
| 280 if (!fling_parameters_.startTime) { |
| 281 fling_parameters_.startTime = monotonic_time_sec; |
| 282 input_handler_->ScheduleAnimation(); |
| 283 return; |
| 284 } |
| 285 |
| 286 if (fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime, |
| 287 this)) { |
| 288 input_handler_->ScheduleAnimation(); |
| 289 } else { |
| 290 TRACE_EVENT_INSTANT0("renderer", |
| 291 "InputHandlerProxy::animate::flingOver", |
| 292 TRACE_EVENT_SCOPE_THREAD); |
| 293 CancelCurrentFling(); |
| 294 } |
| 295 } |
| 296 |
| 297 void InputHandlerProxy::MainThreadHasStoppedFlinging() { |
| 298 fling_may_be_active_on_main_thread_ = false; |
| 299 } |
| 300 |
| 301 bool InputHandlerProxy::CancelCurrentFling() { |
| 302 bool had_fling_animation = fling_curve_; |
| 303 if (had_fling_animation && |
| 304 fling_parameters_.sourceDevice == WebGestureEvent::Touchscreen) { |
| 305 input_handler_->ScrollEnd(); |
| 306 TRACE_EVENT_ASYNC_END0( |
| 307 "renderer", |
| 308 "InputHandlerProxy::HandleGestureFling::started", |
| 309 this); |
| 310 } |
| 311 |
| 312 TRACE_EVENT_INSTANT1("renderer", |
| 313 "InputHandlerProxy::CancelCurrentFling", |
| 314 TRACE_EVENT_SCOPE_THREAD, |
| 315 "had_fling_animation", |
| 316 had_fling_animation); |
| 317 fling_curve_.reset(); |
| 318 gesture_scroll_on_impl_thread_ = false; |
| 319 fling_parameters_ = WebKit::WebActiveWheelFlingParameters(); |
| 320 return had_fling_animation; |
| 321 } |
| 322 |
| 323 bool InputHandlerProxy::TouchpadFlingScroll( |
| 324 const WebFloatSize& increment) { |
| 325 WebMouseWheelEvent synthetic_wheel; |
| 326 synthetic_wheel.type = WebInputEvent::MouseWheel; |
| 327 synthetic_wheel.deltaX = increment.width; |
| 328 synthetic_wheel.deltaY = increment.height; |
| 329 synthetic_wheel.hasPreciseScrollingDeltas = true; |
| 330 synthetic_wheel.x = fling_parameters_.point.x; |
| 331 synthetic_wheel.y = fling_parameters_.point.y; |
| 332 synthetic_wheel.globalX = fling_parameters_.globalPoint.x; |
| 333 synthetic_wheel.globalY = fling_parameters_.globalPoint.y; |
| 334 synthetic_wheel.modifiers = fling_parameters_.modifiers; |
| 335 |
| 336 InputHandlerProxy::EventDisposition disposition = |
| 337 HandleInputEventInternal(synthetic_wheel); |
| 338 switch (disposition) { |
| 339 case DidHandle: |
| 340 return true; |
| 341 case DropEvent: |
| 342 break; |
| 343 case DidNotHandle: |
| 344 TRACE_EVENT_INSTANT0("renderer", |
| 345 "InputHandlerProxy::scrollBy::AbortFling", |
| 346 TRACE_EVENT_SCOPE_THREAD); |
| 347 // If we got a DidNotHandle, that means we need to deliver wheels on the |
| 348 // main thread. In this case we need to schedule a commit and transfer the |
| 349 // fling curve over to the main thread and run the rest of the wheels from |
| 350 // there. This can happen when flinging a page that contains a scrollable |
| 351 // subarea that we can't scroll on the thread if the fling starts outside |
| 352 // the subarea but then is flung "under" the pointer. |
| 353 client_->TransferActiveWheelFlingAnimation(fling_parameters_); |
| 354 fling_may_be_active_on_main_thread_ = true; |
| 355 CancelCurrentFling(); |
| 356 break; |
| 357 } |
| 358 |
| 359 return false; |
| 360 } |
| 361 |
| 362 static gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) { |
| 363 return gfx::Vector2dF(-increment.width, -increment.height); |
| 364 } |
| 365 |
| 366 void InputHandlerProxy::scrollBy(const WebFloatSize& increment) { |
| 367 if (increment == WebFloatSize()) |
| 368 return; |
| 369 |
| 370 TRACE_EVENT2("renderer", |
| 371 "InputHandlerProxy::scrollBy", |
| 372 "x", |
| 373 increment.width, |
| 374 "y", |
| 375 increment.height); |
| 376 |
| 377 bool did_scroll = false; |
| 378 |
| 379 switch (fling_parameters_.sourceDevice) { |
| 380 case WebGestureEvent::Touchpad: |
| 381 did_scroll = TouchpadFlingScroll(increment); |
| 382 break; |
| 383 case WebGestureEvent::Touchscreen: |
| 384 did_scroll = input_handler_->ScrollBy(fling_parameters_.point, |
| 385 ToClientScrollIncrement(increment)); |
| 386 break; |
| 387 } |
| 388 |
| 389 if (did_scroll) { |
| 390 fling_parameters_.cumulativeScroll.width += increment.width; |
| 391 fling_parameters_.cumulativeScroll.height += increment.height; |
| 392 } |
| 393 } |
| 394 |
| 395 void InputHandlerProxy::notifyCurrentFlingVelocity( |
| 396 const WebFloatSize& velocity) { |
| 397 TRACE_EVENT2("renderer", |
| 398 "InputHandlerProxy::notifyCurrentFlingVelocity", |
| 399 "vx", |
| 400 velocity.width, |
| 401 "vy", |
| 402 velocity.height); |
| 403 input_handler_->NotifyCurrentFlingVelocity(ToClientScrollIncrement(velocity)); |
| 404 } |
| 405 |
| 406 } // namespace content |
OLD | NEW |