Index: content/renderer/input/input_handler_proxy.cc |
diff --git a/content/renderer/input/input_handler_proxy.cc b/content/renderer/input/input_handler_proxy.cc |
deleted file mode 100644 |
index 255492f0d4d2556ead2a4e7d15058fe7689ea0fe..0000000000000000000000000000000000000000 |
--- a/content/renderer/input/input_handler_proxy.cc |
+++ /dev/null |
@@ -1,1048 +0,0 @@ |
-// Copyright 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/input/input_handler_proxy.h" |
- |
-#include <algorithm> |
- |
-#include "base/auto_reset.h" |
-#include "base/command_line.h" |
-#include "base/location.h" |
-#include "base/logging.h" |
-#include "base/metrics/histogram.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/thread_task_runner_handle.h" |
-#include "base/trace_event/trace_event.h" |
-#include "content/common/input/did_overscroll_params.h" |
-#include "content/common/input/web_input_event_traits.h" |
-#include "content/public/common/content_switches.h" |
-#include "content/renderer/input/input_handler_proxy_client.h" |
-#include "content/renderer/input/input_scroll_elasticity_controller.h" |
-#include "third_party/WebKit/public/platform/Platform.h" |
-#include "third_party/WebKit/public/web/WebInputEvent.h" |
-#include "ui/events/latency_info.h" |
-#include "ui/gfx/geometry/point_conversions.h" |
- |
-using blink::WebFloatPoint; |
-using blink::WebFloatSize; |
-using blink::WebGestureEvent; |
-using blink::WebInputEvent; |
-using blink::WebMouseEvent; |
-using blink::WebMouseWheelEvent; |
-using blink::WebPoint; |
-using blink::WebTouchEvent; |
-using blink::WebTouchPoint; |
- |
-namespace { |
- |
-// Maximum time between a fling event's timestamp and the first |Animate| call |
-// for the fling curve to use the fling timestamp as the initial animation time. |
-// Two frames allows a minor delay between event creation and the first animate. |
-const double kMaxSecondsFromFlingTimestampToFirstAnimate = 2. / 60.; |
- |
-// Threshold for determining whether a fling scroll delta should have caused the |
-// client to scroll. |
-const float kScrollEpsilon = 0.1f; |
- |
-// Minimum fling velocity required for the active fling and new fling for the |
-// two to accumulate. |
-const double kMinBoostFlingSpeedSquare = 350. * 350.; |
- |
-// Minimum velocity for the active touch scroll to preserve (boost) an active |
-// fling for which cancellation has been deferred. |
-const double kMinBoostTouchScrollSpeedSquare = 150 * 150.; |
- |
-// Timeout window after which the active fling will be cancelled if no animation |
-// ticks, scrolls or flings of sufficient velocity relative to the current fling |
-// are received. The default value on Android native views is 40ms, but we use a |
-// slightly increased value to accomodate small IPC message delays. |
-const double kFlingBoostTimeoutDelaySeconds = 0.05; |
- |
-gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) { |
- return gfx::Vector2dF(-increment.width, -increment.height); |
-} |
- |
-double InSecondsF(const base::TimeTicks& time) { |
- return (time - base::TimeTicks()).InSecondsF(); |
-} |
- |
-bool ShouldSuppressScrollForFlingBoosting( |
- const gfx::Vector2dF& current_fling_velocity, |
- const WebGestureEvent& scroll_update_event, |
- double time_since_last_boost_event, |
- double time_since_last_fling_animate) { |
- DCHECK_EQ(WebInputEvent::GestureScrollUpdate, scroll_update_event.type); |
- |
- gfx::Vector2dF dx(scroll_update_event.data.scrollUpdate.deltaX, |
- scroll_update_event.data.scrollUpdate.deltaY); |
- if (gfx::DotProduct(current_fling_velocity, dx) <= 0) |
- return false; |
- |
- if (time_since_last_fling_animate > kFlingBoostTimeoutDelaySeconds) |
- return false; |
- |
- if (time_since_last_boost_event < 0.001) |
- return true; |
- |
- // TODO(jdduke): Use |scroll_update_event.data.scrollUpdate.velocity{X,Y}|. |
- // The scroll must be of sufficient velocity to maintain the active fling. |
- const gfx::Vector2dF scroll_velocity = |
- gfx::ScaleVector2d(dx, 1. / time_since_last_boost_event); |
- if (scroll_velocity.LengthSquared() < kMinBoostTouchScrollSpeedSquare) |
- return false; |
- |
- return true; |
-} |
- |
-bool ShouldBoostFling(const gfx::Vector2dF& current_fling_velocity, |
- const WebGestureEvent& fling_start_event) { |
- DCHECK_EQ(WebInputEvent::GestureFlingStart, fling_start_event.type); |
- |
- gfx::Vector2dF new_fling_velocity( |
- fling_start_event.data.flingStart.velocityX, |
- fling_start_event.data.flingStart.velocityY); |
- |
- if (gfx::DotProduct(current_fling_velocity, new_fling_velocity) <= 0) |
- return false; |
- |
- if (current_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare) |
- return false; |
- |
- if (new_fling_velocity.LengthSquared() < kMinBoostFlingSpeedSquare) |
- return false; |
- |
- return true; |
-} |
- |
-WebGestureEvent ObtainGestureScrollBegin(const WebGestureEvent& event) { |
- WebGestureEvent scroll_begin_event = event; |
- scroll_begin_event.type = WebInputEvent::GestureScrollBegin; |
- scroll_begin_event.data.scrollBegin.deltaXHint = 0; |
- scroll_begin_event.data.scrollBegin.deltaYHint = 0; |
- return scroll_begin_event; |
-} |
- |
-void ReportInputEventLatencyUma(const WebInputEvent& event, |
- const ui::LatencyInfo& latency_info) { |
- if (!(event.type == WebInputEvent::GestureScrollBegin || |
- event.type == WebInputEvent::GestureScrollUpdate || |
- event.type == WebInputEvent::GesturePinchBegin || |
- event.type == WebInputEvent::GesturePinchUpdate || |
- event.type == WebInputEvent::GestureFlingStart)) { |
- return; |
- } |
- |
- ui::LatencyInfo::LatencyMap::const_iterator it = |
- latency_info.latency_components().find(std::make_pair( |
- ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0)); |
- |
- if (it == latency_info.latency_components().end()) |
- return; |
- |
- base::TimeDelta delta = base::TimeTicks::Now() - it->second.event_time; |
- for (size_t i = 0; i < it->second.event_count; ++i) { |
- switch (event.type) { |
- case blink::WebInputEvent::GestureScrollBegin: |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "Event.Latency.RendererImpl.GestureScrollBegin", |
- delta.InMicroseconds(), 1, 1000000, 100); |
- break; |
- case blink::WebInputEvent::GestureScrollUpdate: |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- // So named for historical reasons. |
- "Event.Latency.RendererImpl.GestureScroll2", |
- delta.InMicroseconds(), 1, 1000000, 100); |
- break; |
- case blink::WebInputEvent::GesturePinchBegin: |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "Event.Latency.RendererImpl.GesturePinchBegin", |
- delta.InMicroseconds(), 1, 1000000, 100); |
- break; |
- case blink::WebInputEvent::GesturePinchUpdate: |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "Event.Latency.RendererImpl.GesturePinchUpdate", |
- delta.InMicroseconds(), 1, 1000000, 100); |
- break; |
- case blink::WebInputEvent::GestureFlingStart: |
- UMA_HISTOGRAM_CUSTOM_COUNTS( |
- "Event.Latency.RendererImpl.GestureFlingStart", |
- delta.InMicroseconds(), 1, 1000000, 100); |
- break; |
- default: |
- NOTREACHED(); |
- break; |
- } |
- } |
-} |
- |
-} // namespace |
- |
-namespace content { |
- |
-InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler, |
- InputHandlerProxyClient* client) |
- : client_(client), |
- input_handler_(input_handler), |
- deferred_fling_cancel_time_seconds_(0), |
- synchronous_input_handler_(nullptr), |
- allow_root_animate_(true), |
-#ifndef NDEBUG |
- expect_scroll_update_end_(false), |
-#endif |
- gesture_scroll_on_impl_thread_(false), |
- gesture_pinch_on_impl_thread_(false), |
- fling_may_be_active_on_main_thread_(false), |
- disallow_horizontal_fling_scroll_(false), |
- disallow_vertical_fling_scroll_(false), |
- has_fling_animation_started_(false), |
- uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()) { |
- DCHECK(client); |
- input_handler_->BindToClient(this); |
- smooth_scroll_enabled_ = base::CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kEnableSmoothScrolling); |
- cc::ScrollElasticityHelper* scroll_elasticity_helper = |
- input_handler_->CreateScrollElasticityHelper(); |
- if (scroll_elasticity_helper) { |
- scroll_elasticity_controller_.reset( |
- new InputScrollElasticityController(scroll_elasticity_helper)); |
- } |
-} |
- |
-InputHandlerProxy::~InputHandlerProxy() {} |
- |
-void InputHandlerProxy::WillShutdown() { |
- scroll_elasticity_controller_.reset(); |
- input_handler_ = NULL; |
- client_->WillShutdown(); |
-} |
- |
-InputHandlerProxy::EventDisposition |
-InputHandlerProxy::HandleInputEventWithLatencyInfo( |
- const WebInputEvent& event, |
- ui::LatencyInfo* latency_info) { |
- DCHECK(input_handler_); |
- |
- if (uma_latency_reporting_enabled_) |
- ReportInputEventLatencyUma(event, *latency_info); |
- |
- TRACE_EVENT_WITH_FLOW1("input,benchmark", |
- "LatencyInfo.Flow", |
- TRACE_ID_DONT_MANGLE(latency_info->trace_id()), |
- TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, |
- "step", "HandleInputEventImpl"); |
- |
- scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor = |
- input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info); |
- InputHandlerProxy::EventDisposition disposition = HandleInputEvent(event); |
- return disposition; |
-} |
- |
-InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( |
- const WebInputEvent& event) { |
- DCHECK(input_handler_); |
- TRACE_EVENT1("input,benchmark", "InputHandlerProxy::HandleInputEvent", |
- "type", WebInputEventTraits::GetName(event.type)); |
- |
- if (FilterInputEventForFlingBoosting(event)) |
- return DID_HANDLE; |
- |
- switch (event.type) { |
- case WebInputEvent::MouseWheel: |
- return HandleMouseWheel(static_cast<const WebMouseWheelEvent&>(event)); |
- |
- case WebInputEvent::GestureScrollBegin: |
- return HandleGestureScrollBegin( |
- static_cast<const WebGestureEvent&>(event)); |
- |
- case WebInputEvent::GestureScrollUpdate: |
- return HandleGestureScrollUpdate( |
- static_cast<const WebGestureEvent&>(event)); |
- |
- case WebInputEvent::GestureScrollEnd: |
- return HandleGestureScrollEnd(static_cast<const WebGestureEvent&>(event)); |
- |
- case WebInputEvent::GesturePinchBegin: { |
- DCHECK(!gesture_pinch_on_impl_thread_); |
- const WebGestureEvent& gesture_event = |
- static_cast<const WebGestureEvent&>(event); |
- if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad && |
- input_handler_->HaveWheelEventHandlersAt( |
- gfx::Point(gesture_event.x, gesture_event.y))) { |
- return DID_NOT_HANDLE; |
- } else { |
- input_handler_->PinchGestureBegin(); |
- gesture_pinch_on_impl_thread_ = true; |
- return DID_HANDLE; |
- } |
- } |
- |
- case WebInputEvent::GesturePinchEnd: |
- if (gesture_pinch_on_impl_thread_) { |
- gesture_pinch_on_impl_thread_ = false; |
- input_handler_->PinchGestureEnd(); |
- return DID_HANDLE; |
- } else { |
- return DID_NOT_HANDLE; |
- } |
- |
- case WebInputEvent::GesturePinchUpdate: { |
- if (gesture_pinch_on_impl_thread_) { |
- const WebGestureEvent& gesture_event = |
- static_cast<const WebGestureEvent&>(event); |
- if (gesture_event.data.pinchUpdate.zoomDisabled) |
- return DROP_EVENT; |
- input_handler_->PinchGestureUpdate( |
- gesture_event.data.pinchUpdate.scale, |
- gfx::Point(gesture_event.x, gesture_event.y)); |
- return DID_HANDLE; |
- } else { |
- return DID_NOT_HANDLE; |
- } |
- } |
- |
- case WebInputEvent::GestureFlingStart: |
- return HandleGestureFlingStart( |
- *static_cast<const WebGestureEvent*>(&event)); |
- |
- case WebInputEvent::GestureFlingCancel: |
- if (CancelCurrentFling()) |
- return DID_HANDLE; |
- else if (!fling_may_be_active_on_main_thread_) |
- return DROP_EVENT; |
- return DID_NOT_HANDLE; |
- |
- case WebInputEvent::TouchStart: |
- return HandleTouchStart(static_cast<const WebTouchEvent&>(event)); |
- |
- case WebInputEvent::MouseMove: { |
- const WebMouseEvent& mouse_event = |
- static_cast<const WebMouseEvent&>(event); |
- // TODO(tony): Ignore when mouse buttons are down? |
- // TODO(davemoore): This should never happen, but bug #326635 showed some |
- // surprising crashes. |
- CHECK(input_handler_); |
- input_handler_->MouseMoveAt(gfx::Point(mouse_event.x, mouse_event.y)); |
- return DID_NOT_HANDLE; |
- } |
- |
- default: |
- if (WebInputEvent::isKeyboardEventType(event.type)) { |
- // Only call |CancelCurrentFling()| if a fling was active, as it will |
- // otherwise disrupt an in-progress touch scroll. |
- if (fling_curve_) |
- CancelCurrentFling(); |
- } |
- break; |
- } |
- |
- return DID_NOT_HANDLE; |
-} |
- |
-InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel( |
- const WebMouseWheelEvent& wheel_event) { |
- InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; |
- cc::InputHandlerScrollResult scroll_result; |
- |
- // TODO(ccameron): The rail information should be pushed down into |
- // InputHandler. |
- gfx::Vector2dF scroll_delta( |
- wheel_event.railsMode != WebInputEvent::RailsModeVertical |
- ? -wheel_event.deltaX |
- : 0, |
- wheel_event.railsMode != WebInputEvent::RailsModeHorizontal |
- ? -wheel_event.deltaY |
- : 0); |
- |
- if (wheel_event.scrollByPage) { |
- // TODO(jamesr): We don't properly handle scroll by page in the compositor |
- // thread, so punt it to the main thread. http://crbug.com/236639 |
- result = DID_NOT_HANDLE; |
- } else if (!wheel_event.canScroll) { |
- // Wheel events with |canScroll| == false will not trigger scrolling, |
- // only event handlers. Forward to the main thread. |
- result = DID_NOT_HANDLE; |
- } else if (smooth_scroll_enabled_ && !wheel_event.hasPreciseScrollingDeltas) { |
- cc::InputHandler::ScrollStatus scroll_status = |
- input_handler_->ScrollAnimated(gfx::Point(wheel_event.x, wheel_event.y), |
- scroll_delta); |
- switch (scroll_status) { |
- case cc::InputHandler::SCROLL_STARTED: |
- result = DID_HANDLE; |
- break; |
- case cc::InputHandler::SCROLL_IGNORED: |
- result = DROP_EVENT; |
- break; |
- default: |
- result = DID_NOT_HANDLE; |
- break; |
- } |
- } else { |
- 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::SCROLL_STARTED: { |
- TRACE_EVENT_INSTANT2("input", |
- "InputHandlerProxy::handle_input wheel scroll", |
- TRACE_EVENT_SCOPE_THREAD, "deltaX", |
- scroll_delta.x(), "deltaY", scroll_delta.y()); |
- gfx::Point scroll_point(wheel_event.x, wheel_event.y); |
- scroll_result = input_handler_->ScrollBy(scroll_point, scroll_delta); |
- HandleOverscroll(scroll_point, scroll_result); |
- input_handler_->ScrollEnd(); |
- result = scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT; |
- break; |
- } |
- case cc::InputHandler::SCROLL_IGNORED: |
- // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail |
- // to properly sync scrollability it's safer to send the event to the |
- // main thread. Change back to DROP_EVENT once we have synchronization |
- // bugs sorted out. |
- result = DID_NOT_HANDLE; |
- break; |
- case cc::InputHandler::SCROLL_UNKNOWN: |
- case cc::InputHandler::SCROLL_ON_MAIN_THREAD: |
- result = DID_NOT_HANDLE; |
- break; |
- case cc::InputHandler::ScrollStatusCount: |
- NOTREACHED(); |
- break; |
- } |
- } |
- |
- // Send the event and its disposition to the elasticity controller to update |
- // the over-scroll animation. If the event is to be handled on the main |
- // thread, the event and its disposition will be sent to the elasticity |
- // controller after being handled on the main thread. |
- if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE) { |
- // Note that the call to the elasticity controller is made asynchronously, |
- // to minimize divergence between main thread and impl thread event |
- // handling paths. |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, |
- base::Bind(&InputScrollElasticityController::ObserveWheelEventAndResult, |
- scroll_elasticity_controller_->GetWeakPtr(), wheel_event, |
- scroll_result)); |
- } |
- return result; |
-} |
- |
-InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( |
- const WebGestureEvent& gesture_event) { |
- if (gesture_scroll_on_impl_thread_) |
- CancelCurrentFling(); |
- |
-#ifndef NDEBUG |
- DCHECK(!expect_scroll_update_end_); |
- expect_scroll_update_end_ = true; |
-#endif |
- cc::InputHandler::ScrollStatus scroll_status; |
- if (gesture_event.data.scrollBegin.targetViewport) { |
- scroll_status = input_handler_->RootScrollBegin(cc::InputHandler::GESTURE); |
- } else { |
- scroll_status = input_handler_->ScrollBegin( |
- gfx::Point(gesture_event.x, gesture_event.y), |
- cc::InputHandler::GESTURE); |
- } |
- UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult", |
- scroll_status, |
- cc::InputHandler::ScrollStatusCount); |
- switch (scroll_status) { |
- case cc::InputHandler::SCROLL_STARTED: |
- TRACE_EVENT_INSTANT0("input", |
- "InputHandlerProxy::handle_input gesture scroll", |
- TRACE_EVENT_SCOPE_THREAD); |
- gesture_scroll_on_impl_thread_ = true; |
- return DID_HANDLE; |
- case cc::InputHandler::SCROLL_UNKNOWN: |
- case cc::InputHandler::SCROLL_ON_MAIN_THREAD: |
- return DID_NOT_HANDLE; |
- case cc::InputHandler::SCROLL_IGNORED: |
- return DROP_EVENT; |
- case cc::InputHandler::ScrollStatusCount: |
- NOTREACHED(); |
- break; |
- } |
- return DID_NOT_HANDLE; |
-} |
- |
-InputHandlerProxy::EventDisposition |
-InputHandlerProxy::HandleGestureScrollUpdate( |
- const WebGestureEvent& gesture_event) { |
-#ifndef NDEBUG |
- DCHECK(expect_scroll_update_end_); |
-#endif |
- |
- if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_) |
- return DID_NOT_HANDLE; |
- |
- gfx::Point scroll_point(gesture_event.x, gesture_event.y); |
- gfx::Vector2dF scroll_delta(-gesture_event.data.scrollUpdate.deltaX, |
- -gesture_event.data.scrollUpdate.deltaY); |
- cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy( |
- scroll_point, scroll_delta); |
- HandleOverscroll(scroll_point, scroll_result); |
- return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT; |
-} |
- |
-InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd( |
- const WebGestureEvent& gesture_event) { |
-#ifndef NDEBUG |
- DCHECK(expect_scroll_update_end_); |
- expect_scroll_update_end_ = false; |
-#endif |
- input_handler_->ScrollEnd(); |
- if (!gesture_scroll_on_impl_thread_) |
- return DID_NOT_HANDLE; |
- gesture_scroll_on_impl_thread_ = false; |
- return DID_HANDLE; |
-} |
- |
-InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( |
- const WebGestureEvent& gesture_event) { |
- cc::InputHandler::ScrollStatus scroll_status = |
- cc::InputHandler::SCROLL_ON_MAIN_THREAD; |
- switch (gesture_event.sourceDevice) { |
- case blink::WebGestureDeviceTouchpad: |
- if (gesture_event.data.flingStart.targetViewport) { |
- scroll_status = input_handler_->RootScrollBegin( |
- cc::InputHandler::NON_BUBBLING_GESTURE); |
- } else { |
- scroll_status = input_handler_->ScrollBegin( |
- gfx::Point(gesture_event.x, gesture_event.y), |
- cc::InputHandler::NON_BUBBLING_GESTURE); |
- } |
- break; |
- case blink::WebGestureDeviceTouchscreen: |
- if (!gesture_scroll_on_impl_thread_) |
- scroll_status = cc::InputHandler::SCROLL_ON_MAIN_THREAD; |
- else |
- scroll_status = input_handler_->FlingScrollBegin(); |
- break; |
- case blink::WebGestureDeviceUninitialized: |
- NOTREACHED(); |
- return DID_NOT_HANDLE; |
- } |
- |
-#ifndef NDEBUG |
- expect_scroll_update_end_ = false; |
-#endif |
- |
- switch (scroll_status) { |
- case cc::InputHandler::SCROLL_STARTED: { |
- if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) |
- input_handler_->ScrollEnd(); |
- |
- const float vx = gesture_event.data.flingStart.velocityX; |
- const float vy = gesture_event.data.flingStart.velocityY; |
- current_fling_velocity_ = gfx::Vector2dF(vx, vy); |
- DCHECK(!current_fling_velocity_.IsZero()); |
- fling_curve_.reset(client_->CreateFlingAnimationCurve( |
- gesture_event.sourceDevice, |
- WebFloatPoint(vx, vy), |
- blink::WebSize())); |
- disallow_horizontal_fling_scroll_ = !vx; |
- disallow_vertical_fling_scroll_ = !vy; |
- TRACE_EVENT_ASYNC_BEGIN2("input,benchmark", |
- "InputHandlerProxy::HandleGestureFling::started", |
- this, "vx", vx, "vy", vy); |
- // Note that the timestamp will only be used to kickstart the animation if |
- // its sufficiently close to the timestamp of the first call |Animate()|. |
- has_fling_animation_started_ = false; |
- fling_parameters_.startTime = gesture_event.timeStampSeconds; |
- fling_parameters_.delta = WebFloatPoint(vx, vy); |
- 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; |
- RequestAnimation(); |
- return DID_HANDLE; |
- } |
- case cc::InputHandler::SCROLL_UNKNOWN: |
- case cc::InputHandler::SCROLL_ON_MAIN_THREAD: { |
- TRACE_EVENT_INSTANT0("input", |
- "InputHandlerProxy::HandleGestureFling::" |
- "scroll_on_main_thread", |
- TRACE_EVENT_SCOPE_THREAD); |
- gesture_scroll_on_impl_thread_ = false; |
- fling_may_be_active_on_main_thread_ = true; |
- return DID_NOT_HANDLE; |
- } |
- case cc::InputHandler::SCROLL_IGNORED: { |
- TRACE_EVENT_INSTANT0( |
- "input", |
- "InputHandlerProxy::HandleGestureFling::ignored", |
- TRACE_EVENT_SCOPE_THREAD); |
- gesture_scroll_on_impl_thread_ = false; |
- if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) { |
- // 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 DID_NOT_HANDLE; |
- } |
- return DROP_EVENT; |
- } |
- case cc::InputHandler::ScrollStatusCount: |
- NOTREACHED(); |
- break; |
- } |
- return DID_NOT_HANDLE; |
-} |
- |
-InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart( |
- const blink::WebTouchEvent& touch_event) { |
- for (size_t i = 0; i < touch_event.touchesLength; ++i) { |
- if (touch_event.touches[i].state != WebTouchPoint::StatePressed) |
- continue; |
- if (input_handler_->DoTouchEventsBlockScrollAt( |
- gfx::Point(touch_event.touches[i].position.x, |
- touch_event.touches[i].position.y))) { |
- // TODO(rbyers): We should consider still sending the touch events to |
- // main asynchronously (crbug.com/455539). |
- return DID_NOT_HANDLE; |
- } |
- } |
- return DROP_EVENT; |
-} |
- |
-bool InputHandlerProxy::FilterInputEventForFlingBoosting( |
- const WebInputEvent& event) { |
- if (!WebInputEvent::isGestureEventType(event.type)) |
- return false; |
- |
- if (!fling_curve_) { |
- DCHECK(!deferred_fling_cancel_time_seconds_); |
- return false; |
- } |
- |
- const WebGestureEvent& gesture_event = |
- static_cast<const WebGestureEvent&>(event); |
- if (gesture_event.type == WebInputEvent::GestureFlingCancel) { |
- if (gesture_event.data.flingCancel.preventBoosting) |
- return false; |
- |
- if (current_fling_velocity_.LengthSquared() < kMinBoostFlingSpeedSquare) |
- return false; |
- |
- TRACE_EVENT_INSTANT0("input", |
- "InputHandlerProxy::FlingBoostStart", |
- TRACE_EVENT_SCOPE_THREAD); |
- deferred_fling_cancel_time_seconds_ = |
- event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds; |
- return true; |
- } |
- |
- // A fling is either inactive or is "free spinning", i.e., has yet to be |
- // interrupted by a touch gesture, in which case there is nothing to filter. |
- if (!deferred_fling_cancel_time_seconds_) |
- return false; |
- |
- // Gestures from a different source should immediately interrupt the fling. |
- if (gesture_event.sourceDevice != fling_parameters_.sourceDevice) { |
- CancelCurrentFling(); |
- return false; |
- } |
- |
- switch (gesture_event.type) { |
- case WebInputEvent::GestureTapCancel: |
- case WebInputEvent::GestureTapDown: |
- return false; |
- |
- case WebInputEvent::GestureScrollBegin: |
- if (!input_handler_->IsCurrentlyScrollingLayerAt( |
- gfx::Point(gesture_event.x, gesture_event.y), |
- fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad |
- ? cc::InputHandler::NON_BUBBLING_GESTURE |
- : cc::InputHandler::GESTURE)) { |
- CancelCurrentFling(); |
- return false; |
- } |
- |
- // TODO(jdduke): Use |gesture_event.data.scrollBegin.delta{X,Y}Hint| to |
- // determine if the ScrollBegin should immediately cancel the fling. |
- ExtendBoostedFlingTimeout(gesture_event); |
- return true; |
- |
- case WebInputEvent::GestureScrollUpdate: { |
- const double time_since_last_boost_event = |
- event.timeStampSeconds - last_fling_boost_event_.timeStampSeconds; |
- const double time_since_last_fling_animate = std::max( |
- 0.0, event.timeStampSeconds - InSecondsF(last_fling_animate_time_)); |
- if (ShouldSuppressScrollForFlingBoosting(current_fling_velocity_, |
- gesture_event, |
- time_since_last_boost_event, |
- time_since_last_fling_animate)) { |
- ExtendBoostedFlingTimeout(gesture_event); |
- return true; |
- } |
- |
- CancelCurrentFling(); |
- return false; |
- } |
- |
- case WebInputEvent::GestureScrollEnd: |
- // Clear the last fling boost event *prior* to fling cancellation, |
- // preventing insertion of a synthetic GestureScrollBegin. |
- last_fling_boost_event_ = WebGestureEvent(); |
- CancelCurrentFling(); |
- return true; |
- |
- case WebInputEvent::GestureFlingStart: { |
- DCHECK_EQ(fling_parameters_.sourceDevice, gesture_event.sourceDevice); |
- |
- bool fling_boosted = |
- fling_parameters_.modifiers == gesture_event.modifiers && |
- ShouldBoostFling(current_fling_velocity_, gesture_event); |
- |
- gfx::Vector2dF new_fling_velocity( |
- gesture_event.data.flingStart.velocityX, |
- gesture_event.data.flingStart.velocityY); |
- DCHECK(!new_fling_velocity.IsZero()); |
- |
- if (fling_boosted) |
- current_fling_velocity_ += new_fling_velocity; |
- else |
- current_fling_velocity_ = new_fling_velocity; |
- |
- WebFloatPoint velocity(current_fling_velocity_.x(), |
- current_fling_velocity_.y()); |
- deferred_fling_cancel_time_seconds_ = 0; |
- disallow_horizontal_fling_scroll_ = !velocity.x; |
- disallow_vertical_fling_scroll_ = !velocity.y; |
- last_fling_boost_event_ = WebGestureEvent(); |
- fling_curve_.reset(client_->CreateFlingAnimationCurve( |
- gesture_event.sourceDevice, |
- velocity, |
- blink::WebSize())); |
- fling_parameters_.startTime = gesture_event.timeStampSeconds; |
- fling_parameters_.delta = velocity; |
- fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y); |
- fling_parameters_.globalPoint = |
- WebPoint(gesture_event.globalX, gesture_event.globalY); |
- |
- TRACE_EVENT_INSTANT2("input", |
- fling_boosted ? "InputHandlerProxy::FlingBoosted" |
- : "InputHandlerProxy::FlingReplaced", |
- TRACE_EVENT_SCOPE_THREAD, |
- "vx", |
- current_fling_velocity_.x(), |
- "vy", |
- current_fling_velocity_.y()); |
- |
- // The client expects balanced calls between a consumed GestureFlingStart |
- // and |DidStopFlinging()|. |
- client_->DidStopFlinging(); |
- return true; |
- } |
- |
- default: |
- // All other types of gestures (taps, presses, etc...) will complete the |
- // deferred fling cancellation. |
- CancelCurrentFling(); |
- return false; |
- } |
-} |
- |
-void InputHandlerProxy::ExtendBoostedFlingTimeout( |
- const blink::WebGestureEvent& event) { |
- TRACE_EVENT_INSTANT0("input", |
- "InputHandlerProxy::ExtendBoostedFlingTimeout", |
- TRACE_EVENT_SCOPE_THREAD); |
- deferred_fling_cancel_time_seconds_ = |
- event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds; |
- last_fling_boost_event_ = event; |
-} |
- |
-void InputHandlerProxy::Animate(base::TimeTicks time) { |
- // If using synchronous animate, then only expect Animate attempts started by |
- // the synchronous system. Don't let the InputHandler try to Animate also. |
- DCHECK(!input_handler_->IsCurrentlyScrollingInnerViewport() || |
- allow_root_animate_); |
- |
- if (scroll_elasticity_controller_) |
- scroll_elasticity_controller_->Animate(time); |
- |
- if (!fling_curve_) |
- return; |
- |
- last_fling_animate_time_ = time; |
- double monotonic_time_sec = InSecondsF(time); |
- |
- if (deferred_fling_cancel_time_seconds_ && |
- monotonic_time_sec > deferred_fling_cancel_time_seconds_) { |
- CancelCurrentFling(); |
- return; |
- } |
- |
- client_->DidAnimateForInput(); |
- |
- if (!has_fling_animation_started_) { |
- has_fling_animation_started_ = true; |
- // Guard against invalid, future or sufficiently stale start times, as there |
- // are no guarantees fling event and animation timestamps are compatible. |
- if (!fling_parameters_.startTime || |
- monotonic_time_sec <= fling_parameters_.startTime || |
- monotonic_time_sec >= fling_parameters_.startTime + |
- kMaxSecondsFromFlingTimestampToFirstAnimate) { |
- fling_parameters_.startTime = monotonic_time_sec; |
- RequestAnimation(); |
- return; |
- } |
- } |
- |
- bool fling_is_active = |
- fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime, |
- this); |
- |
- if (disallow_vertical_fling_scroll_ && disallow_horizontal_fling_scroll_) |
- fling_is_active = false; |
- |
- if (fling_is_active) { |
- RequestAnimation(); |
- } else { |
- TRACE_EVENT_INSTANT0("input", |
- "InputHandlerProxy::animate::flingOver", |
- TRACE_EVENT_SCOPE_THREAD); |
- CancelCurrentFling(); |
- } |
-} |
- |
-void InputHandlerProxy::MainThreadHasStoppedFlinging() { |
- fling_may_be_active_on_main_thread_ = false; |
- client_->DidStopFlinging(); |
-} |
- |
-void InputHandlerProxy::ReconcileElasticOverscrollAndRootScroll() { |
- if (scroll_elasticity_controller_) |
- scroll_elasticity_controller_->ReconcileStretchAndScroll(); |
-} |
- |
-void InputHandlerProxy::UpdateRootLayerStateForSynchronousInputHandler( |
- const gfx::ScrollOffset& total_scroll_offset, |
- const gfx::ScrollOffset& max_scroll_offset, |
- const gfx::SizeF& scrollable_size, |
- float page_scale_factor, |
- float min_page_scale_factor, |
- float max_page_scale_factor) { |
- if (synchronous_input_handler_) { |
- synchronous_input_handler_->UpdateRootLayerState( |
- total_scroll_offset, max_scroll_offset, scrollable_size, |
- page_scale_factor, min_page_scale_factor, max_page_scale_factor); |
- } |
-} |
- |
-void InputHandlerProxy::SetOnlySynchronouslyAnimateRootFlings( |
- SynchronousInputHandler* synchronous_input_handler) { |
- allow_root_animate_ = !synchronous_input_handler; |
- synchronous_input_handler_ = synchronous_input_handler; |
- if (synchronous_input_handler_) |
- input_handler_->RequestUpdateForSynchronousInputHandler(); |
-} |
- |
-void InputHandlerProxy::SynchronouslyAnimate(base::TimeTicks time) { |
- // When this function is used, SetOnlySynchronouslyAnimate() should have been |
- // previously called. IOW you should either be entirely in synchronous mode or |
- // not. |
- DCHECK(synchronous_input_handler_); |
- DCHECK(!allow_root_animate_); |
- base::AutoReset<bool> reset(&allow_root_animate_, true); |
- Animate(time); |
-} |
- |
-void InputHandlerProxy::SynchronouslySetRootScrollOffset( |
- const gfx::ScrollOffset& root_offset) { |
- DCHECK(synchronous_input_handler_); |
- input_handler_->SetSynchronousInputHandlerRootScrollOffset(root_offset); |
-} |
- |
-void InputHandlerProxy::HandleOverscroll( |
- const gfx::Point& causal_event_viewport_point, |
- const cc::InputHandlerScrollResult& scroll_result) { |
- DCHECK(client_); |
- if (!scroll_result.did_overscroll_root) |
- return; |
- |
- TRACE_EVENT2("input", |
- "InputHandlerProxy::DidOverscroll", |
- "dx", |
- scroll_result.unused_scroll_delta.x(), |
- "dy", |
- scroll_result.unused_scroll_delta.y()); |
- |
- DidOverscrollParams params; |
- params.accumulated_overscroll = scroll_result.accumulated_root_overscroll; |
- params.latest_overscroll_delta = scroll_result.unused_scroll_delta; |
- params.current_fling_velocity = |
- ToClientScrollIncrement(current_fling_velocity_); |
- params.causal_event_viewport_point = gfx::PointF(causal_event_viewport_point); |
- |
- if (fling_curve_) { |
- static const int kFlingOverscrollThreshold = 1; |
- disallow_horizontal_fling_scroll_ |= |
- std::abs(params.accumulated_overscroll.x()) >= |
- kFlingOverscrollThreshold; |
- disallow_vertical_fling_scroll_ |= |
- std::abs(params.accumulated_overscroll.y()) >= |
- kFlingOverscrollThreshold; |
- } |
- |
- client_->DidOverscroll(params); |
-} |
- |
-bool InputHandlerProxy::CancelCurrentFling() { |
- if (CancelCurrentFlingWithoutNotifyingClient()) { |
- client_->DidStopFlinging(); |
- return true; |
- } |
- return false; |
-} |
- |
-bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() { |
- bool had_fling_animation = fling_curve_; |
- if (had_fling_animation && |
- fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) { |
- input_handler_->ScrollEnd(); |
- TRACE_EVENT_ASYNC_END0( |
- "input", |
- "InputHandlerProxy::HandleGestureFling::started", |
- this); |
- } |
- |
- TRACE_EVENT_INSTANT1("input", |
- "InputHandlerProxy::CancelCurrentFling", |
- TRACE_EVENT_SCOPE_THREAD, |
- "had_fling_animation", |
- had_fling_animation); |
- fling_curve_.reset(); |
- has_fling_animation_started_ = false; |
- gesture_scroll_on_impl_thread_ = false; |
- current_fling_velocity_ = gfx::Vector2dF(); |
- fling_parameters_ = blink::WebActiveWheelFlingParameters(); |
- |
- if (deferred_fling_cancel_time_seconds_) { |
- deferred_fling_cancel_time_seconds_ = 0; |
- |
- WebGestureEvent last_fling_boost_event = last_fling_boost_event_; |
- last_fling_boost_event_ = WebGestureEvent(); |
- if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin || |
- last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) { |
- // Synthesize a GestureScrollBegin, as the original was suppressed. |
- HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event)); |
- } |
- } |
- |
- return had_fling_animation; |
-} |
- |
-void InputHandlerProxy::RequestAnimation() { |
- // When a SynchronousInputHandler is present, root flings should go through |
- // it to allow it to control when or if the root fling is animated. Non-root |
- // flings always go through the normal InputHandler. |
- if (synchronous_input_handler_ && |
- input_handler_->IsCurrentlyScrollingInnerViewport()) |
- synchronous_input_handler_->SetNeedsSynchronousAnimateInput(); |
- else |
- input_handler_->SetNeedsAnimateInput(); |
-} |
- |
-bool InputHandlerProxy::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; |
- |
- InputHandlerProxy::EventDisposition disposition = |
- HandleInputEvent(synthetic_wheel); |
- switch (disposition) { |
- case DID_HANDLE: |
- return true; |
- case DROP_EVENT: |
- break; |
- case DID_NOT_HANDLE: |
- TRACE_EVENT_INSTANT0("input", |
- "InputHandlerProxy::scrollBy::AbortFling", |
- TRACE_EVENT_SCOPE_THREAD); |
- // If we got a DID_NOT_HANDLE, 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_may_be_active_on_main_thread_ = true; |
- CancelCurrentFlingWithoutNotifyingClient(); |
- break; |
- } |
- |
- return false; |
-} |
- |
-bool InputHandlerProxy::scrollBy(const WebFloatSize& increment, |
- const WebFloatSize& velocity) { |
- WebFloatSize clipped_increment; |
- WebFloatSize clipped_velocity; |
- if (!disallow_horizontal_fling_scroll_) { |
- clipped_increment.width = increment.width; |
- clipped_velocity.width = velocity.width; |
- } |
- if (!disallow_vertical_fling_scroll_) { |
- clipped_increment.height = increment.height; |
- clipped_velocity.height = velocity.height; |
- } |
- |
- current_fling_velocity_ = clipped_velocity; |
- |
- // Early out if the increment is zero, but avoid early terimination if the |
- // velocity is still non-zero. |
- if (clipped_increment == WebFloatSize()) |
- return clipped_velocity != WebFloatSize(); |
- |
- TRACE_EVENT2("input", |
- "InputHandlerProxy::scrollBy", |
- "x", |
- clipped_increment.width, |
- "y", |
- clipped_increment.height); |
- |
- bool did_scroll = false; |
- |
- switch (fling_parameters_.sourceDevice) { |
- case blink::WebGestureDeviceTouchpad: |
- did_scroll = TouchpadFlingScroll(clipped_increment); |
- break; |
- case blink::WebGestureDeviceTouchscreen: { |
- clipped_increment = ToClientScrollIncrement(clipped_increment); |
- cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy( |
- fling_parameters_.point, clipped_increment); |
- HandleOverscroll(fling_parameters_.point, scroll_result); |
- did_scroll = scroll_result.did_scroll; |
- } break; |
- case blink::WebGestureDeviceUninitialized: |
- NOTREACHED(); |
- return false; |
- } |
- |
- if (did_scroll) { |
- fling_parameters_.cumulativeScroll.width += clipped_increment.width; |
- fling_parameters_.cumulativeScroll.height += clipped_increment.height; |
- } |
- |
- // It's possible the provided |increment| is sufficiently small as to not |
- // trigger a scroll, e.g., with a trivial time delta between fling updates. |
- // Return true in this case to prevent early fling termination. |
- if (std::abs(clipped_increment.width) < kScrollEpsilon && |
- std::abs(clipped_increment.height) < kScrollEpsilon) |
- return true; |
- |
- return did_scroll; |
-} |
- |
-} // namespace content |