Index: content/renderer/gpu/input_handler_client_impl.cc |
diff --git a/content/renderer/gpu/input_handler_client_impl.cc b/content/renderer/gpu/input_handler_client_impl.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2a203d153d918f476b467c8e837252374e41c035 |
--- /dev/null |
+++ b/content/renderer/gpu/input_handler_client_impl.cc |
@@ -0,0 +1,400 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/renderer/gpu/input_handler_client_impl.h" |
+ |
+#include "base/debug/trace_event.h" |
+#include "base/logging.h" |
+#include "content/renderer/gpu/input_handler_client_impl_client.h" |
+#include "third_party/WebKit/Source/Platform/chromium/public/Platform.h" |
+#include "third_party/WebKit/Source/Platform/chromium/public/WebInputHandlerClient.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" |
+ |
+using WebKit::WebFloatPoint; |
+using WebKit::WebFloatSize; |
+using WebKit::WebGestureEvent; |
+using WebKit::WebInputEvent; |
+using WebKit::WebInputHandlerClient; |
+using WebKit::WebMouseWheelEvent; |
+using WebKit::WebPoint; |
+using WebKit::WebScrollbar; |
+using WebKit::WebTouchEvent; |
+ |
+namespace content { |
+ |
+InputHandlerClientImpl::InputHandlerClientImpl(cc::InputHandler* input_handler) |
+ : client_(NULL), |
+ input_handler_(input_handler), |
+#ifndef NDEBUG |
+ expect_scroll_update_end_(false), |
+ expect_pinch_update_end_(false), |
+#endif |
+ gesture_scroll_on_impl_thread_(false), |
+ gesture_pinch_on_impl_thread_(false), |
+ fling_active_on_main_thread_(false) { |
+ input_handler_->BindToClient(this); |
+} |
+ |
+InputHandlerClientImpl::~InputHandlerClientImpl() { |
+ if (client_) |
+ client_->willShutdown(); |
+} |
+ |
+void InputHandlerClientImpl::SetClient(InputHandlerClientImplClient* client) { |
+ // It's valid to set a new client if we've never had one or to clear the |
+ // client, but it's not valid to change from having one client to a different |
+ // one. |
+ DCHECK(!client_ || !client); |
+ client_ = client; |
+} |
+ |
+void InputHandlerClientImpl::HandleInputEvent(const WebInputEvent& event) { |
+ DCHECK(client_); |
+ |
+ InputHandlerClientImpl::EventDisposition disposition = |
+ HandleInputEventInternal(event); |
+ switch (disposition) { |
+ case DidHandle: |
+ client_->didHandleInputEvent(); |
+ break; |
+ case DidNotHandle: |
+ client_->didNotHandleInputEvent(true /* send_to_widget */); |
+ break; |
+ case DropEvent: |
+ client_->didNotHandleInputEvent(false /* send_to_widget */); |
+ break; |
+ } |
+ if (event.modifiers & WebInputEvent::IsLastInputEventForCurrentVSync) |
danakj
2013/05/01 19:20:43
nit: {}
|
+ input_handler_->DidReceiveLastInputEventForVSync( |
+ base::TimeTicks::FromInternalValue(event.timeStampSeconds * |
+ base::Time::kMicrosecondsPerSecond)); |
+} |
+ |
+InputHandlerClientImpl::EventDisposition |
+InputHandlerClientImpl::HandleInputEventInternal(const WebInputEvent& event) { |
+ if (event.type == WebInputEvent::MouseWheel) { |
+ const WebMouseWheelEvent& wheel_event = |
+ *static_cast<const WebMouseWheelEvent*>(&event); |
+ cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( |
+ gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel); |
+ switch (scroll_status) { |
+ case cc::InputHandler::ScrollStarted: { |
+ TRACE_EVENT_INSTANT2( |
+ "renderer", |
+ "InputHandlerClientImpl::handle_input wheel scroll", |
+ TRACE_EVENT_SCOPE_THREAD, |
+ "deltaX", |
+ -wheel_event.deltaX, |
+ "deltaY", |
+ -wheel_event.deltaY); |
+ bool did_scroll = false; |
+ if (wheel_event.scrollByPage) { |
+ DCHECK(!wheel_event.deltaX); |
+ WebScrollbar::ScrollDirection direction = |
+ (wheel_event.deltaY < 0) ? WebScrollbar::ScrollForward |
+ : WebScrollbar::ScrollBackward; |
+ did_scroll = input_handler_->ScrollVerticallyByPage( |
+ gfx::Point(wheel_event.x, wheel_event.y), direction); |
+ } else { |
+ did_scroll = input_handler_->ScrollBy( |
+ gfx::Point(wheel_event.x, wheel_event.y), |
+ gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY)); |
+ } |
+ input_handler_->ScrollEnd(); |
+ return did_scroll ? DidHandle : DropEvent; |
+ } |
+ case cc::InputHandler::ScrollIgnored: |
+ // FIXME: This should be DropEvent, but in cases where we fail to |
+ // properly sync scrollability it's safer to send the |
+ // event to the main thread. Change back to DropEvent once we have |
+ // synchronization bugs sorted out. |
+ return DidNotHandle; |
+ case cc::InputHandler::ScrollOnMainThread: |
+ return DidNotHandle; |
+ } |
+ } else if (event.type == WebInputEvent::GestureScrollBegin) { |
+ DCHECK(!gesture_scroll_on_impl_thread_); |
+#ifndef NDEBUG |
+ DCHECK(!expect_scroll_update_end_); |
+ expect_scroll_update_end_ = true; |
+#endif |
+ const WebGestureEvent& gesture_event = |
+ *static_cast<const WebGestureEvent*>(&event); |
+ cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin( |
+ gfx::Point(gesture_event.x, gesture_event.y), |
+ cc::InputHandler::Gesture); |
+ switch (scroll_status) { |
+ case cc::InputHandler::ScrollStarted: |
+ gesture_scroll_on_impl_thread_ = true; |
+ return DidHandle; |
+ case cc::InputHandler::ScrollOnMainThread: |
+ return DidNotHandle; |
+ case cc::InputHandler::ScrollIgnored: |
+ return DropEvent; |
+ } |
+ } else if (event.type == WebInputEvent::GestureScrollUpdate) { |
+#ifndef NDEBUG |
+ DCHECK(expect_scroll_update_end_); |
+#endif |
+ |
+ if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_) |
+ return DidNotHandle; |
+ |
+ const WebGestureEvent& gesture_event = |
+ *static_cast<const WebGestureEvent*>(&event); |
+ bool did_scroll = input_handler_->ScrollBy( |
+ gfx::Point(gesture_event.x, gesture_event.y), |
+ gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX, |
+ -gesture_event.data.scrollUpdate.deltaY)); |
+ return did_scroll ? DidHandle : DropEvent; |
+ } else if (event.type == WebInputEvent::GestureScrollEnd) { |
+#ifndef NDEBUG |
+ DCHECK(expect_scroll_update_end_); |
+ expect_scroll_update_end_ = false; |
+#endif |
+ if (!gesture_scroll_on_impl_thread_) |
+ return DidNotHandle; |
+ |
+ input_handler_->ScrollEnd(); |
+ gesture_scroll_on_impl_thread_ = false; |
+ return DidHandle; |
+ } else if (event.type == WebInputEvent::GesturePinchBegin) { |
+#ifndef NDEBUG |
+ DCHECK(!expect_pinch_update_end_); |
+ expect_pinch_update_end_ = true; |
+#endif |
+ input_handler_->PinchGestureBegin(); |
+ gesture_pinch_on_impl_thread_ = true; |
+ return DidHandle; |
+ } else if (event.type == WebInputEvent::GesturePinchEnd) { |
+#ifndef NDEBUG |
+ DCHECK(expect_pinch_update_end_); |
+ expect_pinch_update_end_ = false; |
+#endif |
+ gesture_pinch_on_impl_thread_ = false; |
+ input_handler_->PinchGestureEnd(); |
+ return DidHandle; |
+ } else if (event.type == WebInputEvent::GesturePinchUpdate) { |
+#ifndef NDEBUG |
+ DCHECK(expect_pinch_update_end_); |
+#endif |
+ const WebGestureEvent& gesture_event = |
+ *static_cast<const WebGestureEvent*>(&event); |
+ input_handler_->PinchGestureUpdate( |
+ gesture_event.data.pinchUpdate.scale, |
+ gfx::Point(gesture_event.x, gesture_event.y)); |
+ return DidHandle; |
+ } else if (event.type == WebInputEvent::GestureFlingStart) { |
+ const WebGestureEvent& gesture_event = |
+ *static_cast<const WebGestureEvent*>(&event); |
+ return HandleGestureFling(gesture_event); |
+ } else if (event.type == WebInputEvent::GestureFlingCancel) { |
+ if (CancelCurrentFling()) |
+ return DidHandle; |
+ else if (!fling_active_on_main_thread_) |
+ return DropEvent; |
+ } else if (event.type == WebInputEvent::TouchStart) { |
+ const WebTouchEvent& touch_event = |
+ *static_cast<const WebTouchEvent*>(&event); |
+ if (!input_handler_->HaveTouchEventHandlersAt(touch_event.touches[0] |
+ .position)) |
+ return DropEvent; |
+ } else if (WebInputEvent::isKeyboardEventType(event.type)) { |
+ CancelCurrentFling(); |
+ } |
+ |
+ return DidNotHandle; |
+} |
+ |
+InputHandlerClientImpl::EventDisposition |
+InputHandlerClientImpl::HandleGestureFling( |
+ const WebGestureEvent& gesture_event) { |
+ cc::InputHandler::ScrollStatus scroll_status = |
+ input_handler_->ScrollBegin(gfx::Point(gesture_event.x, gesture_event.y), |
+ cc::InputHandler::NonBubblingGesture); |
+ switch (scroll_status) { |
+ case cc::InputHandler::ScrollStarted: { |
+ if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) |
+ input_handler_->ScrollEnd(); |
+ fling_curve_.reset(client_->createFlingAnimationCurve( |
+ gesture_event.sourceDevice, |
+ WebFloatPoint(gesture_event.data.flingStart.velocityX, |
+ gesture_event.data.flingStart.velocityY), |
+ WebKit::WebSize())); |
+ TRACE_EVENT_ASYNC_BEGIN0( |
+ "renderer", |
+ "InputHandlerClientImpl::HandleGestureFling::started", |
+ this); |
+ fling_parameters_.delta = |
+ WebFloatPoint(gesture_event.data.flingStart.velocityX, |
+ gesture_event.data.flingStart.velocityY); |
+ fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y); |
+ fling_parameters_.globalPoint = |
+ WebPoint(gesture_event.globalX, gesture_event.globalY); |
+ fling_parameters_.modifiers = gesture_event.modifiers; |
+ fling_parameters_.sourceDevice = gesture_event.sourceDevice; |
+ input_handler_->ScheduleAnimation(); |
+ return DidHandle; |
+ } |
+ case cc::InputHandler::ScrollOnMainThread: { |
+ TRACE_EVENT_INSTANT0("renderer", |
+ "InputHandlerClientImpl::HandleGestureFling::" |
+ "scroll_on_main_thread", |
+ TRACE_EVENT_SCOPE_THREAD); |
+ fling_active_on_main_thread_ = true; |
+ return DidNotHandle; |
+ } |
+ case cc::InputHandler::ScrollIgnored: { |
+ TRACE_EVENT_INSTANT0( |
+ "renderer", |
+ "InputHandlerClientImpl::HandleGestureFling::ignored", |
+ TRACE_EVENT_SCOPE_THREAD); |
+ if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) { |
+ // We still pass the curve to the main thread if there's nothing |
+ // scrollable, in case something |
+ // registers a handler before the curve is over. |
+ return DidNotHandle; |
+ } |
+ return DropEvent; |
+ } |
+ } |
+ return DidNotHandle; |
+} |
+ |
+void InputHandlerClientImpl::Animate(base::TimeTicks time) { |
+ if (!fling_curve_) |
+ return; |
+ |
+ double monotonic_time_sec = (time - base::TimeTicks()).InSecondsF(); |
+ if (!fling_parameters_.startTime) { |
+ fling_parameters_.startTime = monotonic_time_sec; |
+ input_handler_->ScheduleAnimation(); |
+ return; |
+ } |
+ |
+ if (fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime, |
+ this)) { |
+ input_handler_->ScheduleAnimation(); |
+ } else { |
+ TRACE_EVENT_INSTANT0("renderer", |
+ "InputHandlerClientImpl::animate::flingOver", |
+ TRACE_EVENT_SCOPE_THREAD); |
+ CancelCurrentFling(); |
+ } |
+} |
+ |
+void InputHandlerClientImpl::MainThreadHasStoppedFlinging() { |
+ fling_active_on_main_thread_ = false; |
+} |
+ |
+bool InputHandlerClientImpl::CancelCurrentFling() { |
+ bool had_fling_animation = fling_curve_; |
+ if (had_fling_animation && |
+ fling_parameters_.sourceDevice == WebGestureEvent::Touchscreen) { |
+ input_handler_->ScrollEnd(); |
+ TRACE_EVENT_ASYNC_END0( |
+ "renderer", |
+ "InputHandlerClientImpl::HandleGestureFling::started", |
+ this); |
+ } |
+ |
+ TRACE_EVENT_INSTANT1("renderer", |
+ "InputHandlerClientImpl::CancelCurrentFling", |
+ TRACE_EVENT_SCOPE_THREAD, |
+ "had_fling_animation", |
+ had_fling_animation); |
+ fling_curve_.reset(); |
+ gesture_scroll_on_impl_thread_ = false; |
+ fling_parameters_ = WebKit::WebActiveWheelFlingParameters(); |
+ return had_fling_animation; |
+} |
+ |
+bool InputHandlerClientImpl::touchpadFlingScroll( |
+ const WebFloatSize& increment) { |
+ WebMouseWheelEvent synthetic_wheel; |
+ synthetic_wheel.type = WebInputEvent::MouseWheel; |
+ synthetic_wheel.deltaX = increment.width; |
+ synthetic_wheel.deltaY = increment.height; |
+ synthetic_wheel.hasPreciseScrollingDeltas = true; |
+ synthetic_wheel.x = fling_parameters_.point.x; |
+ synthetic_wheel.y = fling_parameters_.point.y; |
+ synthetic_wheel.globalX = fling_parameters_.globalPoint.x; |
+ synthetic_wheel.globalY = fling_parameters_.globalPoint.y; |
+ synthetic_wheel.modifiers = fling_parameters_.modifiers; |
+ |
+ InputHandlerClientImpl::EventDisposition disposition = |
+ HandleInputEventInternal(synthetic_wheel); |
+ switch (disposition) { |
+ case DidHandle: |
+ return true; |
+ case DropEvent: |
+ break; |
+ case DidNotHandle: |
+ TRACE_EVENT_INSTANT0("renderer", |
+ "InputHandlerClientImpl::scrollBy::AbortFling", |
+ TRACE_EVENT_SCOPE_THREAD); |
+ // If we got a DidNotHandle, that means we need to deliver wheels on the |
+ // main thread. |
+ // In this case we need to schedule a commit and transfer the fling curve |
+ // over to the main |
+ // thread and run the rest of the wheels from there. |
+ // This can happen when flinging a page that contains a scrollable subarea |
+ // that we can't |
+ // scroll on the thread if the fling starts outside the subarea but then |
+ // is flung "under" the |
+ // pointer. |
+ client_->transferActiveWheelFlingAnimation(fling_parameters_); |
+ fling_active_on_main_thread_ = true; |
+ CancelCurrentFling(); |
+ break; |
+ } |
+ |
+ return false; |
+} |
+ |
+static gfx::Vector2dF toClientScrollIncrement(const WebFloatSize& increment) { |
danakj
2013/05/01 19:20:43
webkit style non-virtual
|
+ return gfx::Vector2dF(-increment.width, -increment.height); |
+} |
+ |
+void InputHandlerClientImpl::scrollBy(const WebFloatSize& increment) { |
+ if (increment == WebFloatSize()) |
+ return; |
+ |
+ TRACE_EVENT2("renderer", |
+ "InputHandlerClientImpl::scrollBy", |
+ "x", |
+ increment.width, |
+ "y", |
+ increment.height); |
+ |
+ bool did_scroll = false; |
+ |
+ switch (fling_parameters_.sourceDevice) { |
+ case WebGestureEvent::Touchpad: |
+ did_scroll = touchpadFlingScroll(increment); |
+ break; |
+ case WebGestureEvent::Touchscreen: |
+ did_scroll = input_handler_->ScrollBy(fling_parameters_.point, |
+ toClientScrollIncrement(increment)); |
+ break; |
+ } |
+ |
+ if (did_scroll) { |
+ fling_parameters_.cumulativeScroll.width += increment.width; |
+ fling_parameters_.cumulativeScroll.height += increment.height; |
+ } |
+} |
+ |
+void InputHandlerClientImpl::notifyCurrentFlingVelocity( |
+ const WebFloatSize& velocity) { |
+ TRACE_EVENT2("renderer", |
+ "InputHandlerClientImpl::notifyCurrentFlingVelocity", |
+ "vx", |
+ velocity.width, |
+ "vy", |
+ velocity.height); |
+ input_handler_->NotifyCurrentFlingVelocity(toClientScrollIncrement(velocity)); |
+} |
+ |
+} // namespace content |