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