| // 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 "ui/events/blink/input_handler_proxy.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/containers/circular_deque.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/test/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/test/trace_event_analyzer.h" |
| #include "base/trace_event/trace_buffer.h" |
| #include "cc/input/main_thread_scrolling_reason.h" |
| #include "cc/trees/swap_promise_monitor.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/platform/WebFloatPoint.h" |
| #include "third_party/WebKit/public/platform/WebFloatSize.h" |
| #include "third_party/WebKit/public/platform/WebGestureCurve.h" |
| #include "third_party/WebKit/public/platform/WebInputEvent.h" |
| #include "third_party/WebKit/public/platform/WebKeyboardEvent.h" |
| #include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" |
| #include "third_party/WebKit/public/platform/WebPoint.h" |
| #include "third_party/WebKit/public/platform/WebTouchEvent.h" |
| #include "ui/events/blink/blink_event_util.h" |
| #include "ui/events/blink/compositor_thread_event_queue.h" |
| #include "ui/events/blink/did_overscroll_params.h" |
| #include "ui/events/blink/event_with_callback.h" |
| #include "ui/events/blink/input_handler_proxy.h" |
| #include "ui/events/blink/input_handler_proxy_client.h" |
| #include "ui/events/blink/web_input_event_traits.h" |
| #include "ui/gfx/geometry/scroll_offset.h" |
| #include "ui/gfx/geometry/size_f.h" |
| #include "ui/latency/latency_info.h" |
| |
| using blink::WebActiveWheelFlingParameters; |
| using blink::WebFloatPoint; |
| using blink::WebFloatSize; |
| using blink::WebGestureDevice; |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| using blink::WebKeyboardEvent; |
| using blink::WebMouseWheelEvent; |
| using blink::WebPoint; |
| using blink::WebSize; |
| using blink::WebTouchEvent; |
| using blink::WebTouchPoint; |
| using testing::Field; |
| |
| namespace ui { |
| namespace test { |
| |
| namespace { |
| |
| const char* kCoalescedCountHistogram = |
| "Event.CompositorThreadEventQueue.CoalescedCount"; |
| const char* kContinuousHeadQueueingTimeHistogram = |
| "Event.CompositorThreadEventQueue.Continuous.HeadQueueingTime"; |
| const char* kContinuousTailQueueingTimeHistogram = |
| "Event.CompositorThreadEventQueue.Continuous.TailQueueingTime"; |
| const char* kNonContinuousQueueingTimeHistogram = |
| "Event.CompositorThreadEventQueue.NonContinuous.QueueingTime"; |
| |
| enum InputHandlerProxyTestType { |
| ROOT_SCROLL_NORMAL_HANDLER, |
| ROOT_SCROLL_SYNCHRONOUS_HANDLER, |
| CHILD_SCROLL_NORMAL_HANDLER, |
| CHILD_SCROLL_SYNCHRONOUS_HANDLER, |
| }; |
| static const InputHandlerProxyTestType test_types[] = { |
| ROOT_SCROLL_NORMAL_HANDLER, ROOT_SCROLL_SYNCHRONOUS_HANDLER, |
| CHILD_SCROLL_NORMAL_HANDLER, CHILD_SCROLL_SYNCHRONOUS_HANDLER}; |
| |
| double InSecondsF(const base::TimeTicks& time) { |
| return (time - base::TimeTicks()).InSecondsF(); |
| } |
| |
| bool WheelEventsMatch(const WebInputEvent& lhs, const WebInputEvent& rhs) { |
| if (lhs.size() == rhs.size() && lhs.GetType() == rhs.GetType() && |
| lhs.GetType() == WebInputEvent::kMouseWheel) { |
| WebMouseWheelEvent rhs_timestamped = |
| static_cast<const WebMouseWheelEvent&>(rhs); |
| rhs_timestamped.SetTimeStampSeconds(lhs.TimeStampSeconds()); |
| return memcmp(&rhs_timestamped, &lhs, rhs.size()) == 0; |
| } |
| return false; |
| } |
| |
| MATCHER_P(WheelEventsMatch, expected, "") { |
| return WheelEventsMatch(arg, expected); |
| } |
| |
| WebGestureEvent CreateFling(base::TimeTicks timestamp, |
| WebGestureDevice source_device, |
| WebFloatPoint velocity, |
| WebPoint point, |
| WebPoint global_point, |
| int modifiers) { |
| WebGestureEvent fling(WebInputEvent::kGestureFlingStart, modifiers, |
| (timestamp - base::TimeTicks()).InSecondsF()); |
| fling.source_device = source_device; |
| fling.data.fling_start.velocity_x = velocity.x; |
| fling.data.fling_start.velocity_y = velocity.y; |
| fling.x = point.x; |
| fling.y = point.y; |
| fling.global_x = global_point.x; |
| fling.global_y = global_point.y; |
| return fling; |
| } |
| |
| WebGestureEvent CreateFling(WebGestureDevice source_device, |
| WebFloatPoint velocity, |
| WebPoint point, |
| WebPoint global_point, |
| int modifiers) { |
| return CreateFling(base::TimeTicks(), |
| source_device, |
| velocity, |
| point, |
| global_point, |
| modifiers); |
| } |
| |
| WebScopedInputEvent CreateGestureScrollFlingPinch( |
| WebInputEvent::Type type, |
| WebGestureDevice source_device, |
| float delta_y_or_scale = 0, |
| int x = 0, |
| int y = 0) { |
| WebGestureEvent gesture(type, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| gesture.source_device = source_device; |
| if (type == WebInputEvent::kGestureScrollUpdate) { |
| gesture.data.scroll_update.delta_y = delta_y_or_scale; |
| } else if (type == WebInputEvent::kGestureFlingStart) { |
| gesture.data.fling_start.velocity_y = delta_y_or_scale; |
| } else if (type == WebInputEvent::kGesturePinchUpdate) { |
| gesture.data.pinch_update.scale = delta_y_or_scale; |
| gesture.x = x; |
| gesture.y = y; |
| } |
| return WebInputEventTraits::Clone(gesture); |
| } |
| |
| void OnTraceDataCollected(base::Closure quit_closure, |
| base::trace_event::TraceResultBuffer* buffer, |
| const scoped_refptr<base::RefCountedString>& json, |
| bool has_more_events) { |
| buffer->AddFragment(json->data()); |
| if (!has_more_events) |
| quit_closure.Run(); |
| } |
| |
| class MockInputHandler : public cc::InputHandler { |
| public: |
| MockInputHandler() {} |
| ~MockInputHandler() override {} |
| |
| MOCK_METHOD0(PinchGestureBegin, void()); |
| MOCK_METHOD2(PinchGestureUpdate, |
| void(float magnify_delta, const gfx::Point& anchor)); |
| MOCK_METHOD0(PinchGestureEnd, void()); |
| |
| MOCK_METHOD0(SetNeedsAnimateInput, void()); |
| |
| MOCK_METHOD2(ScrollBegin, |
| ScrollStatus(cc::ScrollState*, |
| cc::InputHandler::ScrollInputType type)); |
| MOCK_METHOD2(RootScrollBegin, |
| ScrollStatus(cc::ScrollState*, |
| cc::InputHandler::ScrollInputType type)); |
| MOCK_METHOD1(ScrollAnimatedBegin, ScrollStatus(cc::ScrollState*)); |
| MOCK_METHOD3(ScrollAnimated, |
| ScrollStatus(const gfx::Point& viewport_point, |
| const gfx::Vector2dF& scroll_delta, |
| base::TimeDelta)); |
| MOCK_METHOD1(ScrollBy, cc::InputHandlerScrollResult(cc::ScrollState*)); |
| MOCK_METHOD1(ScrollEnd, void(cc::ScrollState*)); |
| MOCK_METHOD0(FlingScrollBegin, cc::InputHandler::ScrollStatus()); |
| MOCK_METHOD0(ScrollingShouldSwitchtoMainThread, bool()); |
| |
| std::unique_ptr<cc::SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor( |
| ui::LatencyInfo* latency) override { |
| return nullptr; |
| } |
| |
| cc::ScrollElasticityHelper* CreateScrollElasticityHelper() override { |
| return NULL; |
| } |
| bool GetScrollOffsetForLayer(int layer_id, |
| gfx::ScrollOffset* offset) override { |
| return false; |
| } |
| bool ScrollLayerTo(int layer_id, const gfx::ScrollOffset& offset) override { |
| return false; |
| } |
| |
| void BindToClient(cc::InputHandlerClient* client, |
| bool touchpad_and_wheel_scroll_latching_enabled) override {} |
| |
| void MouseDown() override {} |
| void MouseUp() override {} |
| void MouseLeave() override {} |
| |
| void MouseMoveAt(const gfx::Point& mouse_position) override {} |
| |
| MOCK_CONST_METHOD2(IsCurrentlyScrollingLayerAt, |
| bool(const gfx::Point& point, |
| cc::InputHandler::ScrollInputType type)); |
| |
| MOCK_CONST_METHOD1( |
| GetEventListenerProperties, |
| cc::EventListenerProperties(cc::EventListenerClass event_class)); |
| MOCK_METHOD2(EventListenerTypeForTouchStartOrMoveAt, |
| cc::InputHandler::TouchStartOrMoveEventListenerType( |
| const gfx::Point& point, |
| cc::TouchAction* touch_action)); |
| |
| MOCK_METHOD0(RequestUpdateForSynchronousInputHandler, void()); |
| MOCK_METHOD1(SetSynchronousInputHandlerRootScrollOffset, |
| void(const gfx::ScrollOffset& root_offset)); |
| |
| bool IsCurrentlyScrollingViewport() const override { |
| return is_scrolling_root_; |
| } |
| void set_is_scrolling_root(bool is) { is_scrolling_root_ = is; } |
| |
| private: |
| bool is_scrolling_root_ = true; |
| DISALLOW_COPY_AND_ASSIGN(MockInputHandler); |
| }; |
| |
| class MockSynchronousInputHandler : public SynchronousInputHandler { |
| public: |
| MOCK_METHOD0(SetNeedsSynchronousAnimateInput, void()); |
| MOCK_METHOD6(UpdateRootLayerState, |
| void(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)); |
| }; |
| |
| // A simple WebGestureCurve implementation that flings at a constant velocity |
| // indefinitely. |
| class FakeWebGestureCurve : public blink::WebGestureCurve { |
| public: |
| FakeWebGestureCurve(const blink::WebFloatSize& velocity, |
| const blink::WebFloatSize& cumulative_scroll) |
| : velocity_(velocity), cumulative_scroll_(cumulative_scroll) {} |
| |
| ~FakeWebGestureCurve() override {} |
| |
| // Returns false if curve has finished and can no longer be applied. |
| bool Apply(double time, blink::WebGestureCurveTarget* target) override { |
| blink::WebFloatSize displacement(velocity_.width * time, |
| velocity_.height * time); |
| blink::WebFloatSize increment( |
| displacement.width - cumulative_scroll_.width, |
| displacement.height - cumulative_scroll_.height); |
| cumulative_scroll_ = displacement; |
| // scrollBy() could delete this curve if the animation is over, so don't |
| // touch any member variables after making that call. |
| return target->ScrollBy(increment, velocity_); |
| } |
| |
| private: |
| blink::WebFloatSize velocity_; |
| blink::WebFloatSize cumulative_scroll_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeWebGestureCurve); |
| }; |
| |
| class MockInputHandlerProxyClient |
| : public InputHandlerProxyClient { |
| public: |
| MockInputHandlerProxyClient() {} |
| ~MockInputHandlerProxyClient() override {} |
| |
| void WillShutdown() override {} |
| |
| MOCK_METHOD1(TransferActiveWheelFlingAnimation, |
| void(const WebActiveWheelFlingParameters&)); |
| |
| MOCK_METHOD1(DispatchNonBlockingEventToMainThread_, |
| void(const WebInputEvent&)); |
| |
| MOCK_METHOD1(GenerateScrollBeginAndSendToMainThread, |
| void(const blink::WebGestureEvent& update_event)); |
| |
| void DispatchNonBlockingEventToMainThread( |
| WebScopedInputEvent event, |
| const ui::LatencyInfo& latency_info) override { |
| CHECK(event.get()); |
| DispatchNonBlockingEventToMainThread_(*event.get()); |
| } |
| |
| std::unique_ptr<blink::WebGestureCurve> CreateFlingAnimationCurve( |
| WebGestureDevice deviceSource, |
| const WebFloatPoint& velocity, |
| const WebSize& cumulative_scroll) override { |
| return std::make_unique<FakeWebGestureCurve>( |
| blink::WebFloatSize(velocity.x, velocity.y), |
| blink::WebFloatSize(cumulative_scroll.width, cumulative_scroll.height)); |
| } |
| |
| MOCK_METHOD5( |
| DidOverscroll, |
| void(const gfx::Vector2dF& accumulated_overscroll, |
| const gfx::Vector2dF& latest_overscroll_delta, |
| const gfx::Vector2dF& current_fling_velocity, |
| const gfx::PointF& causal_event_viewport_point, |
| const cc::ScrollBoundaryBehavior& scroll_boundary_behavior)); |
| void DidStopFlinging() override {} |
| void DidAnimateForInput() override {} |
| MOCK_METHOD3(SetWhiteListedTouchAction, |
| void(cc::TouchAction touch_action, |
| uint32_t unique_touch_event_id, |
| InputHandlerProxy::EventDisposition event_disposition)); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockInputHandlerProxyClient); |
| }; |
| |
| class MockInputHandlerProxyClientWithDidAnimateForInput |
| : public MockInputHandlerProxyClient { |
| public: |
| MockInputHandlerProxyClientWithDidAnimateForInput() {} |
| ~MockInputHandlerProxyClientWithDidAnimateForInput() override {} |
| |
| MOCK_METHOD0(DidAnimateForInput, void()); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MockInputHandlerProxyClientWithDidAnimateForInput); |
| }; |
| |
| WebTouchPoint CreateWebTouchPoint(WebTouchPoint::State state, float x, |
| float y) { |
| WebTouchPoint point; |
| point.state = state; |
| point.SetPositionInScreen(x, y); |
| point.SetPositionInWidget(x, y); |
| return point; |
| } |
| |
| const cc::InputHandler::ScrollStatus kImplThreadScrollState( |
| cc::InputHandler::SCROLL_ON_IMPL_THREAD, |
| cc::MainThreadScrollingReason::kNotScrollingOnMain); |
| |
| const cc::InputHandler::ScrollStatus kMainThreadScrollState( |
| cc::InputHandler::SCROLL_ON_MAIN_THREAD, |
| cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); |
| |
| const cc::InputHandler::ScrollStatus kScrollIgnoredScrollState( |
| cc::InputHandler::SCROLL_IGNORED, |
| cc::MainThreadScrollingReason::kNotScrollable); |
| |
| } // namespace |
| |
| class TestInputHandlerProxy : public InputHandlerProxy { |
| public: |
| TestInputHandlerProxy(cc::InputHandler* input_handler, |
| InputHandlerProxyClient* client, |
| bool touchpad_and_wheel_scroll_latching_enabled) |
| : InputHandlerProxy(input_handler, |
| client, |
| touchpad_and_wheel_scroll_latching_enabled) {} |
| void RecordMainThreadScrollingReasonsForTest(blink::WebGestureDevice device, |
| uint32_t reasons) { |
| RecordMainThreadScrollingReasons(device, reasons); |
| } |
| |
| EventDisposition HitTestTouchEventForTest( |
| const blink::WebTouchEvent& touch_event, |
| bool* is_touching_scrolling_layer, |
| cc::TouchAction* white_listed_touch_action) { |
| return HitTestTouchEvent(touch_event, is_touching_scrolling_layer, |
| white_listed_touch_action); |
| } |
| }; |
| |
| class InputHandlerProxyTest |
| : public testing::Test, |
| public testing::WithParamInterface<InputHandlerProxyTestType> { |
| public: |
| InputHandlerProxyTest(bool touchpad_and_wheel_scroll_latching_enabled = true) |
| : synchronous_root_scroll_(GetParam() == ROOT_SCROLL_SYNCHRONOUS_HANDLER), |
| install_synchronous_handler_( |
| GetParam() == ROOT_SCROLL_SYNCHRONOUS_HANDLER || |
| GetParam() == CHILD_SCROLL_SYNCHRONOUS_HANDLER), |
| expected_disposition_(InputHandlerProxy::DID_HANDLE), |
| touchpad_and_wheel_scroll_latching_enabled_( |
| touchpad_and_wheel_scroll_latching_enabled) { |
| input_handler_.reset( |
| new TestInputHandlerProxy(&mock_input_handler_, &mock_client_, |
| touchpad_and_wheel_scroll_latching_enabled_)); |
| scroll_result_did_scroll_.did_scroll = true; |
| scroll_result_did_not_scroll_.did_scroll = false; |
| |
| if (install_synchronous_handler_) { |
| EXPECT_CALL(mock_input_handler_, |
| RequestUpdateForSynchronousInputHandler()) |
| .Times(1); |
| input_handler_->SetOnlySynchronouslyAnimateRootFlings( |
| &mock_synchronous_input_handler_); |
| } |
| |
| mock_input_handler_.set_is_scrolling_root(synchronous_root_scroll_); |
| |
| // Set a default device so tests don't always have to set this. |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| } |
| |
| virtual ~InputHandlerProxyTest() { input_handler_.reset(); } |
| |
| // This is defined as a macro so the line numbers can be traced back to the |
| // correct spot when it fails. |
| #define EXPECT_SET_NEEDS_ANIMATE_INPUT(times) \ |
| do { \ |
| if (synchronous_root_scroll_) { \ |
| EXPECT_CALL(mock_synchronous_input_handler_, \ |
| SetNeedsSynchronousAnimateInput()) \ |
| .Times(times); \ |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(0); \ |
| } else { \ |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(times); \ |
| EXPECT_CALL(mock_synchronous_input_handler_, \ |
| SetNeedsSynchronousAnimateInput()) \ |
| .Times(0); \ |
| } \ |
| } while (false) |
| |
| // This is defined as a macro because when an expectation is not satisfied the |
| // only output you get out of gmock is the line number that set the expectation. |
| #define VERIFY_AND_RESET_MOCKS() \ |
| do { \ |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); \ |
| testing::Mock::VerifyAndClearExpectations( \ |
| &mock_synchronous_input_handler_); \ |
| testing::Mock::VerifyAndClearExpectations(&mock_client_); \ |
| } while (false) |
| |
| void Animate(base::TimeTicks time) { |
| if (synchronous_root_scroll_) { |
| input_handler_->SynchronouslyAnimate(time); |
| } else { |
| input_handler_->Animate(time); |
| } |
| } |
| |
| void StartFling(base::TimeTicks timestamp, |
| WebGestureDevice source_device, |
| WebFloatPoint velocity, |
| WebPoint position) { |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = source_device; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| |
| gesture_ = |
| CreateFling(timestamp, source_device, velocity, position, position, 0); |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| void CancelFling(base::TimeTicks timestamp) { |
| gesture_.SetTimeStampSeconds(InSecondsF(timestamp)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| void SetSmoothScrollEnabled(bool value) { |
| input_handler_->smooth_scroll_enabled_ = value; |
| } |
| |
| base::HistogramTester& histogram_tester() { |
| return histogram_tester_; |
| } |
| |
| protected: |
| void GestureFlingAnimatesTouchpad(); |
| void DidReceiveInputEvent_ForFling(); |
| void GestureScrollStarted(); |
| void GestureFlingPassiveListener(); |
| void GestureFlingStartedTouchpad(); |
| void GestureFlingStopsAtContentEdge(); |
| void GestureFlingTransferResetsTouchpad(); |
| void ScrollHandlingSwitchedToMainThread(); |
| |
| const bool synchronous_root_scroll_; |
| const bool install_synchronous_handler_; |
| testing::StrictMock<MockInputHandler> mock_input_handler_; |
| testing::StrictMock<MockSynchronousInputHandler> |
| mock_synchronous_input_handler_; |
| std::unique_ptr<TestInputHandlerProxy> input_handler_; |
| testing::StrictMock<MockInputHandlerProxyClient> mock_client_; |
| WebGestureEvent gesture_; |
| InputHandlerProxy::EventDisposition expected_disposition_; |
| base::HistogramTester histogram_tester_; |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| cc::InputHandlerScrollResult scroll_result_did_not_scroll_; |
| bool touchpad_and_wheel_scroll_latching_enabled_; |
| }; |
| |
| class InputHandlerProxyWithoutWheelScrollLatchingTest |
| : public InputHandlerProxyTest { |
| public: |
| InputHandlerProxyWithoutWheelScrollLatchingTest() |
| : InputHandlerProxyTest(false) {} |
| }; |
| |
| class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> { |
| public: |
| InputHandlerProxyEventQueueTest() : weak_ptr_factory_(this) { |
| feature_list_.InitAndEnableFeature(features::kVsyncAlignedInputEvents); |
| } |
| |
| ~InputHandlerProxyEventQueueTest() { input_handler_proxy_.reset(); } |
| |
| void SetUp() override { |
| bool wheel_scroll_latching_enabled = GetParam(); |
| event_disposition_recorder_.clear(); |
| latency_info_recorder_.clear(); |
| input_handler_proxy_ = std::make_unique<TestInputHandlerProxy>( |
| &mock_input_handler_, &mock_client_, wheel_scroll_latching_enabled); |
| if (input_handler_proxy_->compositor_event_queue_) |
| input_handler_proxy_->compositor_event_queue_ = |
| std::make_unique<CompositorThreadEventQueue>(); |
| } |
| |
| void StartTracing() { |
| base::trace_event::TraceLog::GetInstance()->SetEnabled( |
| base::trace_event::TraceConfig("*"), |
| base::trace_event::TraceLog::RECORDING_MODE); |
| } |
| |
| void StopTracing() { |
| base::trace_event::TraceLog::GetInstance()->SetDisabled(); |
| } |
| |
| std::unique_ptr<trace_analyzer::TraceAnalyzer> CreateTraceAnalyzer() { |
| base::trace_event::TraceResultBuffer buffer; |
| base::trace_event::TraceResultBuffer::SimpleOutput trace_output; |
| buffer.SetOutputCallback(trace_output.GetCallback()); |
| base::RunLoop run_loop; |
| buffer.Start(); |
| base::trace_event::TraceLog::GetInstance()->Flush( |
| base::Bind(&OnTraceDataCollected, run_loop.QuitClosure(), |
| base::Unretained(&buffer))); |
| run_loop.Run(); |
| buffer.Finish(); |
| |
| return base::WrapUnique( |
| trace_analyzer::TraceAnalyzer::Create(trace_output.json_output)); |
| } |
| |
| void HandleGestureEvent(WebInputEvent::Type type, |
| float delta_y_or_scale = 0, |
| int x = 0, |
| int y = 0) { |
| HandleGestureEventWithSourceDevice( |
| type, blink::kWebGestureDeviceTouchscreen, delta_y_or_scale, x, y); |
| } |
| |
| void HandleGestureEventWithSourceDevice(WebInputEvent::Type type, |
| WebGestureDevice source_device, |
| float delta_y_or_scale = 0, |
| int x = 0, |
| int y = 0) { |
| LatencyInfo latency; |
| input_handler_proxy_->HandleInputEventWithLatencyInfo( |
| CreateGestureScrollFlingPinch(type, source_device, delta_y_or_scale, x, |
| y), |
| latency, |
| base::Bind( |
| &InputHandlerProxyEventQueueTest::DidHandleInputEventAndOverscroll, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DidHandleInputEventAndOverscroll( |
| InputHandlerProxy::EventDisposition event_disposition, |
| WebScopedInputEvent input_event, |
| const ui::LatencyInfo& latency_info, |
| std::unique_ptr<ui::DidOverscrollParams> overscroll_params) { |
| event_disposition_recorder_.push_back(event_disposition); |
| latency_info_recorder_.push_back(latency_info); |
| } |
| |
| base::circular_deque<std::unique_ptr<EventWithCallback>>& event_queue() { |
| return input_handler_proxy_->compositor_event_queue_->queue_; |
| } |
| |
| void SetInputHandlerProxyTickClockForTesting( |
| std::unique_ptr<base::TickClock> tick_clock) { |
| input_handler_proxy_->SetTickClockForTesting(std::move(tick_clock)); |
| } |
| |
| protected: |
| base::test::ScopedFeatureList feature_list_; |
| testing::StrictMock<MockInputHandler> mock_input_handler_; |
| std::unique_ptr<TestInputHandlerProxy> input_handler_proxy_; |
| testing::StrictMock<MockInputHandlerProxyClient> mock_client_; |
| std::vector<InputHandlerProxy::EventDisposition> event_disposition_recorder_; |
| std::vector<ui::LatencyInfo> latency_info_recorder_; |
| |
| base::MessageLoop loop_; |
| base::WeakPtrFactory<InputHandlerProxyEventQueueTest> weak_ptr_factory_; |
| }; |
| |
| TEST_P(InputHandlerProxyTest, MouseWheelNoListener) { |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| |
| WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel, |
| WebInputEvent::kControlKey, |
| WebInputEvent::kTimeStampForTesting); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MouseWheelPassiveListener) { |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); |
| |
| WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel, |
| WebInputEvent::kControlKey, |
| WebInputEvent::kTimeStampForTesting); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MouseWheelBlockingListener) { |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); |
| |
| WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel, |
| WebInputEvent::kControlKey, |
| WebInputEvent::kTimeStampForTesting); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MouseWheelBlockingAndPassiveListener) { |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce( |
| testing::Return(cc::EventListenerProperties::kBlockingAndPassive)); |
| |
| WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel, |
| WebInputEvent::kControlKey, |
| WebInputEvent::kTimeStampForTesting); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| void InputHandlerProxyTest::GestureScrollStarted() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_,input_handler_->HandleInputEvent(gesture_)); |
| |
| // The event should not be marked as handled if scrolling is not possible. |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = |
| -40; // -Y means scroll down - i.e. in the +Y direction. |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread()) |
| .WillOnce(testing::Return(false)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // Mark the event as handled if scroll happens. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = |
| -40; // -Y means scroll down - i.e. in the +Y direction. |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| gesture_.data.scroll_update.delta_y = 0; |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, GestureScrollStarted) { |
| GestureScrollStarted(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, GestureScrollStarted) { |
| GestureScrollStarted(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureScrollOnMainThread) { |
| // We should send all events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = 40; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| gesture_.data.scroll_update.delta_y = 0; |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .WillOnce(testing::Return()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureScrollIgnored) { |
| // We shouldn't handle the GestureScrollBegin. |
| // Instead, we should get a DROP_EVENT result, indicating |
| // that we could determine that there's nothing that could scroll or otherwise |
| // react to this gesture sequence and thus we should drop the whole gesture |
| // sequence on the floor, except for the ScrollEnd. |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kScrollIgnoredScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // GSB is dropped and not sent to the main thread, GSE shouldn't get sent to |
| // the main thread, either. |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .WillOnce(testing::Return()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureScrollByPage) { |
| // We should send all events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.data.scroll_begin.delta_hint_units = |
| WebGestureEvent::ScrollUnits::kPage; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = 1; |
| gesture_.data.scroll_update.delta_units = WebGestureEvent::ScrollUnits::kPage; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| gesture_.data.scroll_update.delta_y = 0; |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .WillOnce(testing::Return()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| // Mac does not smooth scroll wheel events (crbug.com/574283). |
| #if !defined(OS_MACOSX) |
| TEST_P(InputHandlerProxyTest, GestureScrollByCoarsePixels) { |
| #else |
| TEST_P(InputHandlerProxyTest, DISABLED_GestureScrollByCoarsePixels) { |
| #endif |
| SetSmoothScrollEnabled(true); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.data.scroll_begin.delta_hint_units = |
| WebGestureEvent::ScrollUnits::kPixels; |
| EXPECT_CALL(mock_input_handler_, ScrollAnimatedBegin(::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_units = |
| WebGestureEvent::ScrollUnits::kPixels; |
| |
| EXPECT_CALL(mock_input_handler_, |
| ScrollAnimated(::testing::_, ::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureScrollBeginThatTargetViewport) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, RootScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.data.scroll_begin.target_viewport = true; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GesturePinch) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchBegin); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| EXPECT_CALL(mock_input_handler_, PinchGestureBegin()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 1.5; |
| gesture_.x = 7; |
| gesture_.y = 13; |
| EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.5, gfx::Point(7, 13))); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 0.5; |
| gesture_.data.pinch_update.zoom_disabled = true; |
| gesture_.x = 9; |
| gesture_.y = 6; |
| EXPECT_EQ(InputHandlerProxy::DROP_EVENT, |
| input_handler_->HandleInputEvent(gesture_)); |
| gesture_.data.pinch_update.zoom_disabled = false; |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 0.5; |
| gesture_.x = 9; |
| gesture_.y = 6; |
| EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(.5, gfx::Point(9, 6))); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchEnd); |
| EXPECT_CALL(mock_input_handler_, PinchGestureEnd()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GesturePinchWithWheelHandler) { |
| // We will send the synthetic wheel event to the widget. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchBegin); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 1.5; |
| gesture_.x = 7; |
| gesture_.y = 13; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 0.5; |
| gesture_.x = 9; |
| gesture_.y = 6; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchEnd); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) { |
| // Scrolls will start by being sent to the main thread. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = 40; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // However, after the pinch gesture starts, they should go to the impl |
| // thread. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchBegin); |
| ; |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| EXPECT_CALL(mock_input_handler_, PinchGestureBegin()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 1.5; |
| gesture_.x = 7; |
| gesture_.y = 13; |
| EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.5, gfx::Point(7, 13))); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = |
| -40; // -Y means scroll down - i.e. in the +Y direction. |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchUpdate); |
| gesture_.data.pinch_update.scale = 0.5; |
| gesture_.x = 9; |
| gesture_.y = 6; |
| EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(.5, gfx::Point(9, 6))); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGesturePinchEnd); |
| EXPECT_CALL(mock_input_handler_, PinchGestureEnd()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // After the pinch gesture ends, they should go to back to the main |
| // thread. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| gesture_.data.scroll_update.delta_y = 0; |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .WillOnce(testing::Return()); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| void InputHandlerProxyTest::GestureFlingStartedTouchpad() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| gesture_.data.fling_start.velocity_x = 10; |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| if (touchpad_and_wheel_scroll_latching_enabled_) { |
| // The fling cancellation shouldn't get deferred because velocityX is less |
| // than minimum. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(1); |
| } |
| // Verify that a GestureFlingCancel during an animation cancels it. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchpad) { |
| GestureFlingStartedTouchpad(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, |
| GestureFlingStartedTouchpad) { |
| GestureFlingStartedTouchpad(); |
| } |
| |
| void InputHandlerProxyTest::ScrollHandlingSwitchedToMainThread() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = -40; |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| // The scroll handling switches to the main thread, a GSB is sent to the main |
| // thread to initiate the hit testing and compute the scroll chain. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread()) |
| .WillOnce(testing::Return(true)); |
| EXPECT_CALL(mock_client_, |
| GenerateScrollBeginAndSendToMainThread(::testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, WheelScrollHandlingSwitchedToMainThread) { |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| ScrollHandlingSwitchedToMainThread(); |
| } |
| TEST_P(InputHandlerProxyTest, TouchScrollHandlingSwitchedToMainThread) { |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| ScrollHandlingSwitchedToMainThread(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingTouchpadScrollLatchingEnabled) { |
| // Reset the input_handler_ with wheel scroll latching enabled. |
| input_handler_.reset( |
| new TestInputHandlerProxy(&mock_input_handler_, &mock_client_, true)); |
| if (install_synchronous_handler_) { |
| EXPECT_CALL(mock_input_handler_, RequestUpdateForSynchronousInputHandler()) |
| .Times(1); |
| input_handler_->SetOnlySynchronouslyAnimateRootFlings( |
| &mock_synchronous_input_handler_); |
| } |
| |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| // Note that for trackpad, wheel events with the Control modifier are |
| // special (reserved for zoom), so don't set that here. |
| int modifiers = WebInputEvent::kShiftKey | WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchpad, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // When scroll latching is enabled, ScrollEnd shouldn't get called while |
| // handling GestureFlingStart. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The first animate call won't start scrolling. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second call should start scrolling in the -X direction. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| |
| // When scroll latching is enabled, ScrollBegin shouldn't get called for |
| // every tick. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| |
| // When scroll latching is enabled, ScrollEnd shouldn't get called for every |
| // tick. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The last call should stop scrolling. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| |
| // When scroll latching is enabled, ScrollBegin shouldn't get called for |
| // every tick. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread()) |
| .WillOnce(testing::Return(false)); |
| |
| // When scroll latching is enabled, ScrollEnd gets called when the last |
| // ScrollBy did not scroll. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Fling has ended, the last Animate won't cause any more wheel ticks. |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingOnMainThreadTouchpad) { |
| // We should send all events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // Since we returned ScrollStatusOnMainThread from scrollBegin, ensure the |
| // input handler knows it's scrolling off the impl thread |
| ASSERT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Even if we didn't start a fling ourselves, we still need to send the cancel |
| // event to the widget. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchpad) { |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kScrollIgnoredScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Since the previous fling was ignored, we should also be dropping the next |
| // fling_cancel. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| gesture_.source_device = blink::kWebGestureDeviceTouchpad; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| } |
| |
| void InputHandlerProxyTest::GestureFlingAnimatesTouchpad() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| // Note that for trackpad, wheel events with the Control modifier are |
| // special (reserved for zoom), so don't set that here. |
| int modifiers = WebInputEvent::kShiftKey | WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchpad, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The first animate call should let us pick up an animation start time, but |
| // we shouldn't actually move anywhere just yet. The first frame after the |
| // fling start will typically include the last scroll from the gesture that |
| // lead to the scroll (either wheel or gesture scroll), so there should be no |
| // visible hitch. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second call should start scrolling in the -X direction. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Let's say on the third call we hit a non-scrollable region. We should abort |
| // the fling and not scroll. |
| // We also should pass the current fling parameters out to the client so the |
| // rest of the fling can be |
| // transferred to the main thread. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); |
| } else { |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread()) |
| .WillOnce(testing::Return(true)); |
| // A GSB is sent to the main thread before transfering the fling. |
| EXPECT_CALL(mock_client_, DispatchNonBlockingEventToMainThread_(testing::_)) |
| .Times(1); |
| } |
| // Expected wheel fling animation parameters: |
| // *) fling_delta and fling_point should match the original GestureFlingStart |
| // event |
| // *) startTime should be 10 to match the time parameter of the first |
| // Animate() call after the GestureFlingStart |
| // *) cumulativeScroll depends on the curve, but since we've animated in the |
| // -X direction the X value should be < 0 |
| EXPECT_CALL( |
| mock_client_, |
| TransferActiveWheelFlingAnimation(testing::AllOf( |
| testing::Field(&WebActiveWheelFlingParameters::delta, |
| testing::Eq(fling_delta)), |
| testing::Field(&WebActiveWheelFlingParameters::point, |
| testing::Eq(fling_point)), |
| testing::Field(&WebActiveWheelFlingParameters::global_point, |
| testing::Eq(fling_global_point)), |
| testing::Field(&WebActiveWheelFlingParameters::modifiers, |
| testing::Eq(modifiers)), |
| testing::Field(&WebActiveWheelFlingParameters::start_time, |
| testing::Eq(10)), |
| testing::Field(&WebActiveWheelFlingParameters::cumulative_scroll, |
| testing::Field(&WebSize::width, testing::Gt(0)))))); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Since we've aborted the fling, the next animation should be a no-op and |
| // should not result in another |
| // frame being requested. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(0); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| // Since we've transferred the fling to the main thread, we need to pass the |
| // next GestureFlingCancel to the main |
| // thread as well. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) { |
| GestureFlingAnimatesTouchpad(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, |
| GestureFlingAnimatesTouchpad) { |
| GestureFlingAnimatesTouchpad(); |
| } |
| |
| void InputHandlerProxyTest::GestureFlingPassiveListener() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| // Note that for trackpad, wheel events with the Control modifier are |
| // special (reserved for zoom), so don't set that here. |
| int modifiers = WebInputEvent::kShiftKey | WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchpad, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The first animate call should let us pick up an animation start time, but |
| // we shouldn't actually move anywhere just yet. The first frame after the |
| // fling start will typically include the last scroll from the gesture that |
| // lead to the scroll (either wheel or gesture scroll), so there should be no |
| // visible hitch. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second call should punt activate the fling and call the method |
| // dispatching the events for the passive event listeners. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| WebMouseWheelEvent expected_wheel(WebInputEvent::kMouseWheel, modifiers, |
| WebInputEvent::kTimeStampForTesting); |
| expected_wheel.SetPositionInWidget(fling_point.x, fling_point.y); |
| expected_wheel.SetPositionInScreen(fling_global_point.x, |
| fling_global_point.y); |
| expected_wheel.delta_x = fling_delta.x / 10; |
| expected_wheel.has_precise_scrolling_deltas = true; |
| expected_wheel.phase = WebMouseWheelEvent::kPhaseChanged; |
| |
| EXPECT_CALL(mock_client_, DispatchNonBlockingEventToMainThread_( |
| WheelEventsMatch(expected_wheel))) |
| .Times(1); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(1); |
| |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Ensure we can cancel the gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| if (touchpad_and_wheel_scroll_latching_enabled_) { |
| // The fling cancellation should be deferred. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); |
| } |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, GestureFlingPassiveListener) { |
| GestureFlingPassiveListener(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, |
| GestureFlingPassiveListener) { |
| GestureFlingPassiveListener(); |
| } |
| |
| void InputHandlerProxyTest::GestureFlingTransferResetsTouchpad() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Start a gesture fling in the -X direction with zero Y movement. |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| // Note that for trackpad, wheel events with the Control modifier are |
| // special (reserved for zoom), so don't set that here. |
| int modifiers = WebInputEvent::kShiftKey | WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchpad, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Start the fling animation at time 10. This shouldn't actually scroll, just |
| // establish a start time. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second call should start scrolling in the -X direction. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Let's say on the third call we hit a non-scrollable region. We should abort |
| // the fling and not scroll. |
| // We also should pass the current fling parameters out to the client so the |
| // rest of the fling can be |
| // transferred to the main thread. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); |
| } else { |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread()) |
| .WillOnce(testing::Return(true)); |
| // A GSB is sent to the main thread before transfering the fling. |
| EXPECT_CALL(mock_client_, DispatchNonBlockingEventToMainThread_(testing::_)) |
| .Times(1); |
| } |
| |
| // Expected wheel fling animation parameters: |
| // *) fling_delta and fling_point should match the original GestureFlingStart |
| // event |
| // *) startTime should be 10 to match the time parameter of the first |
| // Animate() call after the GestureFlingStart |
| // *) cumulativeScroll depends on the curve, but since we've animated in the |
| // -X direction the X value should be < 0 |
| EXPECT_CALL( |
| mock_client_, |
| TransferActiveWheelFlingAnimation(testing::AllOf( |
| testing::Field(&WebActiveWheelFlingParameters::delta, |
| testing::Eq(fling_delta)), |
| testing::Field(&WebActiveWheelFlingParameters::point, |
| testing::Eq(fling_point)), |
| testing::Field(&WebActiveWheelFlingParameters::global_point, |
| testing::Eq(fling_global_point)), |
| testing::Field(&WebActiveWheelFlingParameters::modifiers, |
| testing::Eq(modifiers)), |
| testing::Field(&WebActiveWheelFlingParameters::start_time, |
| testing::Eq(10)), |
| testing::Field(&WebActiveWheelFlingParameters::cumulative_scroll, |
| testing::Field(&WebSize::width, testing::Gt(0)))))); |
| if (touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Since we've aborted the fling, the next animation should be a no-op and |
| // should not result in another |
| // frame being requested. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(0); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Since we've transferred the fling to the main thread, we need to pass the |
| // next GestureFlingCancel to the main |
| // thread as well. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| input_handler_->MainThreadHasStoppedFlinging(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Start a second gesture fling, this time in the +Y direction with no X. |
| fling_delta = WebFloatPoint(0, -1000); |
| fling_point = WebPoint(95, 87); |
| fling_global_point = WebPoint(32, 71); |
| modifiers = WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchpad, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Start the second fling animation at time 30. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .Times(0); |
| time = base::TimeTicks() + base::TimeDelta::FromSeconds(30); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Tick the second fling once normally. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Then abort the second fling. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0); |
| } else { |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread()) |
| .WillOnce(testing::Return(true)); |
| // A GSB is sent to the main thread before transfering the fling. |
| EXPECT_CALL(mock_client_, DispatchNonBlockingEventToMainThread_(testing::_)) |
| .Times(1); |
| } |
| |
| // We should get parameters from the second fling, nothing from the first |
| // fling should "leak". |
| EXPECT_CALL( |
| mock_client_, |
| TransferActiveWheelFlingAnimation(testing::AllOf( |
| testing::Field(&WebActiveWheelFlingParameters::delta, |
| testing::Eq(fling_delta)), |
| testing::Field(&WebActiveWheelFlingParameters::point, |
| testing::Eq(fling_point)), |
| testing::Field(&WebActiveWheelFlingParameters::global_point, |
| testing::Eq(fling_global_point)), |
| testing::Field(&WebActiveWheelFlingParameters::modifiers, |
| testing::Eq(modifiers)), |
| testing::Field(&WebActiveWheelFlingParameters::start_time, |
| testing::Eq(30)), |
| testing::Field(&WebActiveWheelFlingParameters::cumulative_scroll, |
| testing::Field(&WebSize::height, testing::Lt(0)))))); |
| |
| if (touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) { |
| GestureFlingTransferResetsTouchpad(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, |
| GestureFlingTransferResetsTouchpad) { |
| GestureFlingTransferResetsTouchpad(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchscreen) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| gesture_.data.fling_start.velocity_x = 10; |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| // Verify that a GestureFlingCancel during an animation cancels it. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingOnMainThreadTouchscreen) { |
| // We should send all events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()).Times(0); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Even if we didn't start a fling ourselves, we still need to send the cancel |
| // event to the widget. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) { |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Flings ignored by the InputHandler should be dropped, signalling the end |
| // of the touch scroll sequence. |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kScrollIgnoredScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Subsequent scrolls should behave normally, even without an intervening |
| // GestureFlingCancel, as the original GestureFlingStart was dropped. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| // Note that for touchscreen the control modifier is not special. |
| int modifiers = WebInputEvent::kControlKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| // The first animate call should let us pick up an animation start time, but |
| // we shouldn't actually move anywhere just yet. The first frame after the |
| // fling start will typically include the last scroll from the gesture that |
| // lead to the scroll (either wheel or gesture scroll), so there should be no |
| // visible hitch. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second call should start scrolling in the -X direction. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingWithValidTimestamp) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| int modifiers = WebInputEvent::kControlKey; |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| // With a valid time stamp, the first animate call should skip start time |
| // initialization and immediately begin scroll update production. This reduces |
| // the likelihood of a hitch between the scroll preceding the fling and |
| // the first scroll generated by the fling. |
| // Scrolling should start in the -X direction. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| time += dt; |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingWithInvalidTimestamp) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| base::TimeDelta start_time_offset = base::TimeDelta::FromMilliseconds(10); |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| int modifiers = WebInputEvent::kControlKey; |
| gesture_.SetTimeStampSeconds(start_time_offset.InSecondsF()); |
| gesture_.data.fling_start.velocity_x = fling_delta.x; |
| gesture_.data.fling_start.velocity_y = fling_delta.y; |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| gesture_.x = fling_point.x; |
| gesture_.y = fling_point.y; |
| gesture_.global_x = fling_global_point.x; |
| gesture_.global_y = fling_global_point.y; |
| gesture_.SetModifiers(modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| // Event though a time stamp was provided for the fling event, it will be |
| // ignored as its too far in the past relative to the first animate call's |
| // timestamp. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| base::TimeTicks time = |
| base::TimeTicks() + start_time_offset + base::TimeDelta::FromSeconds(1); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Further animation ticks should update the fling as usual. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| time += base::TimeDelta::FromMilliseconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureScrollOnImplThreadFlagClearedAfterFling) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // After sending a GestureScrollBegin, the member variable |
| // |gesture_scroll_on_impl_thread_| should be true. |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| int modifiers = WebInputEvent::kControlKey | WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // |gesture_scroll_on_impl_thread_| should still be true after |
| // a GestureFlingStart is sent. |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| // The first animate call should let us pick up an animation start time, but |
| // we shouldn't actually move anywhere just yet. The first frame after the |
| // fling start will typically include the last scroll from the gesture that |
| // lead to the scroll (either wheel or gesture scroll), so there should be no |
| // visible hitch. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second call should start scrolling in the -X direction. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // |gesture_scroll_on_impl_thread_| should be false once |
| // the fling has finished (note no GestureScrollEnd has been sent). |
| EXPECT_TRUE(!input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, |
| BeginScrollWhenGestureScrollOnImplThreadFlagIsSet) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // After sending a GestureScrollBegin, the member variable |
| // |gesture_scroll_on_impl_thread_| should be true. |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| int modifiers = WebInputEvent::kControlKey | WebInputEvent::kAltKey; |
| gesture_ = CreateFling(blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // |gesture_scroll_on_impl_thread_| should still be true after |
| // a GestureFlingStart is sent. |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // gesture_scroll_on_impl_thread_ is still true when this scroll begins. As a |
| // result, this scroll begin will cancel the previous fling. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| // After sending a GestureScrollBegin, the member variable |
| // |gesture_scroll_on_impl_thread_| should be true. |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| void InputHandlerProxyTest::GestureFlingStopsAtContentEdge() { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(100, 100); |
| gesture_.data.fling_start.velocity_x = fling_delta.x; |
| gesture_.data.fling_start.velocity_y = fling_delta.y; |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The first animate doesn't cause any scrolling. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second animate starts scrolling in the positive X and Y directions. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The third animate overscrolls in the positive Y direction but scrolls |
| // somewhat. |
| cc::InputHandlerScrollResult overscroll; |
| overscroll.did_scroll = true; |
| overscroll.did_overscroll_root = true; |
| overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100); |
| overscroll.unused_scroll_delta = gfx::Vector2dF(0, 10); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) |
| .WillOnce(testing::Return(overscroll)); |
| EXPECT_CALL( |
| mock_client_, |
| DidOverscroll(overscroll.accumulated_root_overscroll, |
| overscroll.unused_scroll_delta, |
| testing::Property(&gfx::Vector2dF::y, testing::Lt(0)), |
| testing::_, overscroll.scroll_boundary_behavior)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The next call to animate will no longer scroll vertically. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) { |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| } |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Eq(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) { |
| GestureFlingStopsAtContentEdge(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, |
| GestureFlingStopsAtContentEdge) { |
| GestureFlingStopsAtContentEdge(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingNotCancelledBySmallTimeDelta) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| int modifiers = WebInputEvent::kControlKey; |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| // With an animation timestamp equivalent to the starting timestamp, the |
| // animation will simply be rescheduled. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| // A small time delta should not stop the fling, even if the client |
| // reports no scrolling. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| time += base::TimeDelta::FromMicroseconds(5); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| // A time delta of zero should not stop the fling, and neither should it |
| // trigger scrolling on the client. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| // Lack of movement on the client, with a non-trivial scroll delta, should |
| // terminate the fling. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(1)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| time += base::TimeDelta::FromMilliseconds(100); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { |
| cc::InputHandlerScrollResult overscroll; |
| overscroll.did_scroll = true; |
| overscroll.did_overscroll_root = true; |
| |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(100, 100); |
| gesture_.data.fling_start.velocity_x = fling_delta.x; |
| gesture_.data.fling_start.velocity_y = fling_delta.y; |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The first animate doesn't cause any scrolling. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The second animate starts scrolling in the positive X and Y directions. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| time += base::TimeDelta::FromMilliseconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The third animate hits the bottom content edge. |
| overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100); |
| overscroll.unused_scroll_delta = gfx::Vector2dF(0, 100); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) |
| .WillOnce(testing::Return(overscroll)); |
| EXPECT_CALL( |
| mock_client_, |
| DidOverscroll(overscroll.accumulated_root_overscroll, |
| overscroll.unused_scroll_delta, |
| testing::Property(&gfx::Vector2dF::y, testing::Lt(0)), |
| testing::_, overscroll.scroll_boundary_behavior)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| time += base::TimeDelta::FromMilliseconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The next call to animate will no longer scroll vertically. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Eq(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| time += base::TimeDelta::FromMilliseconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The next call will hit the right edge. |
| overscroll.accumulated_root_overscroll = gfx::Vector2dF(100, 100); |
| overscroll.unused_scroll_delta = gfx::Vector2dF(100, 0); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(overscroll)); |
| EXPECT_CALL( |
| mock_client_, |
| DidOverscroll(overscroll.accumulated_root_overscroll, |
| overscroll.unused_scroll_delta, |
| testing::Property(&gfx::Vector2dF::x, testing::Lt(0)), |
| testing::_, overscroll.scroll_boundary_behavior)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| time += base::TimeDelta::FromMilliseconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The next call to animate will no longer scroll horizontally or vertically, |
| // and the fling should be cancelled. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(0); |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); |
| time += base::TimeDelta::FromMilliseconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| } |
| |
| TEST_P(InputHandlerProxyTest, HitTestTouchEventNonNullTouchAction) { |
| // One of the touch points is on a touch-region. So the event should be sent |
| // to the main thread. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Eq(0)), testing::_)) |
| .WillOnce(testing::Invoke([](const gfx::Point&, |
| cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionMax; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; |
| })); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) |
| .WillOnce( |
| testing::Invoke([](const gfx::Point&, cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionPanUp; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType:: |
| HANDLER_ON_SCROLLING_LAYER; |
| })); |
| // Since the second touch point hits a touch-region, there should be no |
| // hit-testing for the third touch point. |
| |
| WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| |
| touch.touches_length = 3; |
| touch.touch_start_or_first_touch_move = true; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); |
| touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, -10, 10); |
| |
| bool is_touching_scrolling_layer; |
| cc::TouchAction white_listed_touch_action = cc::kTouchActionAuto; |
| EXPECT_EQ(expected_disposition_, input_handler_->HitTestTouchEventForTest( |
| touch, &is_touching_scrolling_layer, |
| &white_listed_touch_action)); |
| EXPECT_TRUE(is_touching_scrolling_layer); |
| EXPECT_EQ(white_listed_touch_action, cc::kTouchActionPanUp); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, HitTestTouchEventNullTouchAction) { |
| // One of the touch points is on a touch-region. So the event should be sent |
| // to the main thread. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Eq(0)), testing::_)) |
| .WillOnce(testing::Return( |
| cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) |
| .WillOnce( |
| testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType:: |
| HANDLER_ON_SCROLLING_LAYER)); |
| // Since the second touch point hits a touch-region, there should be no |
| // hit-testing for the third touch point. |
| |
| WebTouchEvent touch(WebInputEvent::kTouchMove, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| |
| touch.touches_length = 3; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); |
| touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, -10, 10); |
| |
| bool is_touching_scrolling_layer; |
| cc::TouchAction* white_listed_touch_action = nullptr; |
| EXPECT_EQ(expected_disposition_, input_handler_->HitTestTouchEventForTest( |
| touch, &is_touching_scrolling_layer, |
| white_listed_touch_action)); |
| EXPECT_TRUE(is_touching_scrolling_layer); |
| EXPECT_TRUE(!white_listed_touch_action); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestNegative) { |
| // None of the three touch points fall in the touch region. So the event |
| // should be dropped. |
| expected_disposition_ = InputHandlerProxy::DROP_EVENT; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchEndOrCancel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| EXPECT_CALL(mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) |
| .Times(2) |
| .WillRepeatedly(testing::Invoke([](const gfx::Point&, |
| cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionPanUp; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; |
| })); |
| EXPECT_CALL(mock_client_, |
| SetWhiteListedTouchAction(cc::kTouchActionPanUp, 1, |
| InputHandlerProxy::DROP_EVENT)) |
| .WillOnce(testing::Return()); |
| |
| WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| |
| touch.unique_touch_event_id = 1; |
| touch.touches_length = 3; |
| touch.touch_start_or_first_touch_move = true; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStateStationary, 0, 0); |
| touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, -10, 10); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPositive) { |
| // One of the touch points is on a touch-region. So the event should be sent |
| // to the main thread. |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Eq(0)), testing::_)) |
| .WillOnce(testing::Invoke([](const gfx::Point&, |
| cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionAuto; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; |
| })); |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) |
| .WillOnce( |
| testing::Invoke([](const gfx::Point&, cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionPanY; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType:: |
| HANDLER_ON_SCROLLING_LAYER; |
| })); |
| EXPECT_CALL(mock_client_, |
| SetWhiteListedTouchAction(cc::kTouchActionPanY, 1, |
| InputHandlerProxy::DID_NOT_HANDLE)) |
| .WillOnce(testing::Return()); |
| // Since the second touch point hits a touch-region, there should be no |
| // hit-testing for the third touch point. |
| |
| WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| |
| touch.unique_touch_event_id = 1; |
| touch.touches_length = 3; |
| touch.touch_start_or_first_touch_move = true; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); |
| touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, -10, 10); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPassivePositive) { |
| // One of the touch points is not on a touch-region. So the event should be |
| // sent to the impl thread. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) |
| .WillRepeatedly(testing::Return(cc::EventListenerProperties::kPassive)); |
| EXPECT_CALL(mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) |
| .Times(3) |
| .WillOnce(testing::Invoke([](const gfx::Point&, |
| cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionPanRight; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; |
| })) |
| .WillRepeatedly(testing::Invoke([](const gfx::Point&, |
| cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionPanX; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; |
| })); |
| EXPECT_CALL(mock_client_, SetWhiteListedTouchAction( |
| cc::kTouchActionPanRight, 1, |
| InputHandlerProxy::DID_HANDLE_NON_BLOCKING)) |
| .WillOnce(testing::Return()); |
| |
| WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| |
| touch.unique_touch_event_id = 1; |
| touch.touches_length = 3; |
| touch.touch_start_or_first_touch_move = true; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); |
| touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, -10, 10); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, TouchStartPassiveAndTouchEndBlocking) { |
| // The touch start is not in a touch-region but there is a touch end handler |
| // so to maintain targeting we need to dispatch the touch start as |
| // non-blocking but drop all touch moves. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchEndOrCancel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); |
| EXPECT_CALL(mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) |
| .WillOnce(testing::Invoke([](const gfx::Point&, |
| cc::TouchAction* touch_action) { |
| *touch_action = cc::kTouchActionNone; |
| return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; |
| })); |
| EXPECT_CALL(mock_client_, SetWhiteListedTouchAction( |
| cc::kTouchActionNone, 1, |
| InputHandlerProxy::DID_HANDLE_NON_BLOCKING)) |
| .WillOnce(testing::Return()); |
| |
| WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| touch.unique_touch_event_id = 1; |
| touch.touches_length = 1; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); |
| touch.touch_start_or_first_touch_move = true; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch)); |
| |
| touch.SetType(WebInputEvent::kTouchMove); |
| touch.touches_length = 1; |
| touch.touch_start_or_first_touch_move = false; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| EXPECT_EQ(InputHandlerProxy::DROP_EVENT, |
| input_handler_->HandleInputEvent(touch)); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) { |
| // The touch start is not in a touch-region but there is a touch end handler |
| // so to maintain targeting we need to dispatch the touch start as |
| // non-blocking but drop all touch moves. |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); |
| EXPECT_CALL(mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) |
| .WillOnce(testing::Return( |
| cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); |
| EXPECT_CALL(mock_client_, |
| SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) |
| .WillOnce(testing::Return()); |
| |
| WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| touch.touches_length = 1; |
| touch.touch_start_or_first_touch_move = true; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE_NON_BLOCKING, |
| input_handler_->HandleInputEvent(touch)); |
| |
| EXPECT_CALL(mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) |
| .WillOnce(testing::Return( |
| cc::InputHandler::TouchStartOrMoveEventListenerType::HANDLER)); |
| EXPECT_CALL(mock_client_, |
| SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) |
| .WillOnce(testing::Return()); |
| |
| touch.SetType(WebInputEvent::kTouchMove); |
| touch.touches_length = 1; |
| touch.touch_start_or_first_touch_move = true; |
| touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStateMoved, 10, 10); |
| EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE, |
| input_handler_->HandleInputEvent(touch)); |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) { |
| // We shouldn't send any events to the widget for this gesture. |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE, |
| input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Keyboard events received during a scroll should have no effect. |
| WebKeyboardEvent key_event(WebInputEvent::kKeyDown, |
| WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE, |
| input_handler_->HandleInputEvent(key_event)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, animation should be scheduled, but no scrolling occurs. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(100, 100); |
| gesture_.data.fling_start.velocity_x = fling_delta.x; |
| gesture_.data.fling_start.velocity_y = fling_delta.y; |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Keyboard events received during a fling should cancel the active fling. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE, |
| input_handler_->HandleInputEvent(key_event)); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The call to animate should have no effect, as the fling was cancelled. |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // A fling cancel should be dropped, as there is nothing to cancel. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(InputHandlerProxy::DROP_EVENT, |
| input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingCancelledByWheelEvent) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Wheel events received during a scroll shouldn't cancel the fling, but will |
| // cause scrolling. |
| cc::InputHandlerScrollResult result; |
| |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); |
| |
| WebMouseWheelEvent wheel_event(WebInputEvent::kMouseWheel, |
| WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| input_handler_->HandleInputEvent(wheel_event); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, animation should be scheduled, but no scrolling occurs. |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(100, 100); |
| gesture_.data.fling_start.velocity_x = fling_delta.x; |
| gesture_.data.fling_start.velocity_y = fling_delta.y; |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Wheel events received during a fling should cancel the active fling, and |
| // cause a scroll. |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); |
| |
| |
| input_handler_->HandleInputEvent(wheel_event); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The call to animate should have no effect, as the fling was cancelled. |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // A fling cancel should be dropped, as there is nothing to cancel. |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(InputHandlerProxy::DROP_EVENT, |
| input_handler_->HandleInputEvent(gesture_)); |
| EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureFlingWithNegativeTimeDelta) { |
| // We shouldn't send any events to the widget for this gesture. |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_.source_device = blink::kWebGestureDeviceTouchscreen; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // On the fling start, we should schedule an animation but not actually start |
| // scrolling. |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(100, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| WebPoint fling_global_point = WebPoint(17, 23); |
| int modifiers = WebInputEvent::kControlKey; |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_global_point, modifiers); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // If we get a negative time delta, that is, the Animation tick time happens |
| // before the fling's start time then we should *not* try scrolling and |
| // instead reset the fling start time. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); |
| time -= base::TimeDelta::FromMilliseconds(5); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // The first call should have reset the start time so subsequent calls should |
| // generate scroll events. |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| |
| Animate(time + base::TimeDelta::FromMilliseconds(1)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, FlingBoost) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| base::TimeTicks last_animate_time = time; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Now cancel the fling. The fling cancellation should be deferred to allow |
| // fling boosting events to arrive. |
| time += dt; |
| CancelFling(time); |
| |
| // The GestureScrollBegin should be swallowed by the fling if a fling cancel |
| // is deferred. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Animate calls within the deferred cancellation window should continue. |
| time += dt; |
| float expected_delta = |
| (time - last_animate_time).InSecondsF() * -fling_delta.x; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| Animate(time); |
| last_animate_time = time; |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // GestureScrollUpdates in the same direction and at sufficient speed should |
| // be swallowed by the fling. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_x = fling_delta.x; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Animate calls within the deferred cancellation window should continue. |
| time += dt; |
| expected_delta = (time - last_animate_time).InSecondsF() * -fling_delta.x; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| Animate(time); |
| last_animate_time = time; |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // GestureFlingStart in the same direction and at sufficient speed should |
| // boost the active fling. |
| |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_point, 0); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| time += dt; |
| // Note we get *2x* as much delta because 2 flings have combined. |
| expected_delta = 2 * (time - last_animate_time).InSecondsF() * -fling_delta.x; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| Animate(time); |
| last_animate_time = time; |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Repeated GestureFlingStarts should accumulate. |
| |
| CancelFling(time); |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point, fling_point, 0); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| time += dt; |
| // Note we get *3x* as much delta because 3 flings have combined. |
| expected_delta = 3 * (time - last_animate_time).InSecondsF() * -fling_delta.x; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| Animate(time); |
| last_animate_time = time; |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // GestureFlingCancel should terminate the fling if no boosting gestures are |
| // received within the timeout window. |
| |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| time += base::TimeDelta::FromMilliseconds(100); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollDelayed) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Cancel the fling. The fling cancellation should be deferred to allow |
| // fling boosting events to arrive. |
| time += dt; |
| CancelFling(time); |
| |
| // The GestureScrollBegin should be swallowed by the fling if a fling cancel |
| // is deferred. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // If no GestureScrollUpdate or GestureFlingStart is received within the |
| // timeout window, the fling should be cancelled and scrolling should resume. |
| time += base::TimeDelta::FromMilliseconds(100); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, NoFlingBoostIfNotAnimated) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Animate fling once. |
| time += dt; |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| Animate(time); |
| |
| // Cancel the fling after long delay of no animate. The fling cancellation |
| // should be deferred to allow fling boosting events to arrive. |
| time += base::TimeDelta::FromMilliseconds(100); |
| CancelFling(time); |
| |
| // The GestureScrollBegin should be swallowed by the fling if a fling cancel |
| // is deferred. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Should exit scroll bosting on GestureScrollUpdate due to long delay |
| // since last animate. Cancel old fling and start new scroll. |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_y = -40; |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, NoFlingBoostIfFlingInDifferentDirection) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Cancel the fling. The fling cancellation should be deferred to allow |
| // fling boosting events to arrive. |
| time += dt; |
| CancelFling(time); |
| |
| // If the new fling is orthogonal to the existing fling, no boosting should |
| // take place, with the new fling replacing the old. |
| WebFloatPoint orthogonal_fling_delta = |
| WebFloatPoint(fling_delta.y, -fling_delta.x); |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, |
| orthogonal_fling_delta, fling_point, fling_point, 0); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Note that the new fling delta uses the orthogonal, unboosted fling |
| // velocity. |
| time += dt; |
| float expected_delta = dt.InSecondsF() * -orthogonal_fling_delta.y; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollInDifferentDirection) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Cancel the fling. The fling cancellation should be deferred to allow |
| // fling boosting events to arrive. |
| time += dt; |
| CancelFling(time); |
| |
| // The GestureScrollBegin should be swallowed by the fling if a fling cancel |
| // is deferred. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // If the GestureScrollUpdate is in a different direction than the fling, |
| // the fling should be cancelled and scrolling should resume. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_x = -fling_delta.x; |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(fling_delta.x)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, NoFlingBoostIfFlingTooSlow) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Cancel the fling. The fling cancellation should be deferred to allow |
| // fling boosting events to arrive. |
| time += dt; |
| CancelFling(time); |
| |
| // If the new fling is too slow, no boosting should take place, with the new |
| // fling replacing the old. |
| WebFloatPoint small_fling_delta = WebFloatPoint(100, 0); |
| gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, |
| small_fling_delta, fling_point, fling_point, 0); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Note that the new fling delta uses the *slow*, unboosted fling velocity. |
| time += dt; |
| float expected_delta = dt.InSecondsF() * -small_fling_delta.x; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, NoFlingBoostIfPreventBoostingFlagIsSet) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| // Cancel the fling. The fling cancellation should not be deferred because of |
| // prevent boosting flag set. |
| gesture_.data.fling_cancel.prevent_boosting = true; |
| time += dt; |
| CancelFling(time); |
| |
| // VERIFY_AND_RESET_MOCKS already called by CancelFling |
| } |
| |
| TEST_P(InputHandlerProxyTest, FlingBoostTerminatedDuringScrollSequence) { |
| base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); |
| base::TimeTicks time = base::TimeTicks() + dt; |
| base::TimeTicks last_animate_time = time; |
| WebFloatPoint fling_delta = WebFloatPoint(1000, 0); |
| WebPoint fling_point = WebPoint(7, 13); |
| StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, |
| fling_point); |
| |
| // Now cancel the fling. The fling cancellation should be deferred to allow |
| // fling boosting events to arrive. |
| time += dt; |
| CancelFling(time); |
| |
| // The GestureScrollBegin should be swallowed by the fling. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Now animate the fling to completion (in this case, the fling should |
| // terminate because the input handler reports a failed scroll). As the fling |
| // was cancelled during an active scroll sequence, a synthetic |
| // GestureScrollBegin should be processed, resuming the scroll. |
| time += dt; |
| float expected_delta = |
| (time - last_animate_time).InSecondsF() * -fling_delta.x; |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_not_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Subsequent GestureScrollUpdates after the cancelled, boosted fling should |
| // cause scrolling as usual. |
| time += dt; |
| expected_delta = 7.3f; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollUpdate); |
| gesture_.data.scroll_update.delta_x = -expected_delta; |
| EXPECT_CALL(mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_x, |
| testing::Eq(expected_delta)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // GestureScrollEnd should terminate the resumed scroll properly. |
| time += dt; |
| gesture_.SetTimeStampSeconds(InSecondsF(time)); |
| gesture_.SetType(WebInputEvent::kGestureScrollEnd); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| void InputHandlerProxyTest::DidReceiveInputEvent_ForFling() { |
| testing::StrictMock<MockInputHandlerProxyClientWithDidAnimateForInput> |
| mock_client; |
| input_handler_.reset( |
| new TestInputHandlerProxy(&mock_input_handler_, &mock_client, |
| touchpad_and_wheel_scroll_latching_enabled_)); |
| if (install_synchronous_handler_) { |
| EXPECT_CALL(mock_input_handler_, RequestUpdateForSynchronousInputHandler()) |
| .Times(1); |
| input_handler_->SetOnlySynchronouslyAnimateRootFlings( |
| &mock_synchronous_input_handler_); |
| } |
| mock_input_handler_.set_is_scrolling_root(synchronous_root_scroll_); |
| |
| gesture_.SetType(WebInputEvent::kGestureFlingStart); |
| WebFloatPoint fling_delta = WebFloatPoint(100, 100); |
| gesture_.data.fling_start.velocity_x = fling_delta.x; |
| gesture_.data.fling_start.velocity_y = fling_delta.y; |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| if (!touchpad_and_wheel_scroll_latching_enabled_) |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE, |
| input_handler_->HandleInputEvent(gesture_)); |
| VERIFY_AND_RESET_MOCKS(); |
| |
| EXPECT_SET_NEEDS_ANIMATE_INPUT(1); |
| EXPECT_CALL(mock_client, DidAnimateForInput()); |
| base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); |
| Animate(time); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| TEST_P(InputHandlerProxyTest, DidReceiveInputEvent_ForFling) { |
| DidReceiveInputEvent_ForFling(); |
| } |
| TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, |
| DidReceiveInputEvent_ForFling) { |
| DidReceiveInputEvent_ForFling(); |
| } |
| |
| TEST(SynchronousInputHandlerProxyTest, StartupShutdown) { |
| testing::StrictMock<MockInputHandler> mock_input_handler; |
| testing::StrictMock<MockInputHandlerProxyClient> mock_client; |
| testing::StrictMock<MockSynchronousInputHandler> |
| mock_synchronous_input_handler; |
| ui::InputHandlerProxy proxy(&mock_input_handler, &mock_client, false); |
| |
| // When adding a SynchronousInputHandler, immediately request an |
| // UpdateRootLayerStateForSynchronousInputHandler() call. |
| EXPECT_CALL(mock_input_handler, RequestUpdateForSynchronousInputHandler()) |
| .Times(1); |
| proxy.SetOnlySynchronouslyAnimateRootFlings(&mock_synchronous_input_handler); |
| |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler); |
| testing::Mock::VerifyAndClearExpectations(&mock_client); |
| testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler); |
| |
| EXPECT_CALL(mock_input_handler, RequestUpdateForSynchronousInputHandler()) |
| .Times(0); |
| proxy.SetOnlySynchronouslyAnimateRootFlings(nullptr); |
| |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler); |
| testing::Mock::VerifyAndClearExpectations(&mock_client); |
| testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler); |
| } |
| |
| TEST(SynchronousInputHandlerProxyTest, UpdateRootLayerState) { |
| testing::NiceMock<MockInputHandler> mock_input_handler; |
| testing::StrictMock<MockInputHandlerProxyClient> mock_client; |
| testing::StrictMock<MockSynchronousInputHandler> |
| mock_synchronous_input_handler; |
| ui::InputHandlerProxy proxy(&mock_input_handler, &mock_client, false); |
| |
| proxy.SetOnlySynchronouslyAnimateRootFlings(&mock_synchronous_input_handler); |
| |
| // When adding a SynchronousInputHandler, immediately request an |
| // UpdateRootLayerStateForSynchronousInputHandler() call. |
| EXPECT_CALL( |
| mock_synchronous_input_handler, |
| UpdateRootLayerState(gfx::ScrollOffset(1, 2), gfx::ScrollOffset(3, 4), |
| gfx::SizeF(5, 6), 7, 8, 9)) |
| .Times(1); |
| proxy.UpdateRootLayerStateForSynchronousInputHandler( |
| gfx::ScrollOffset(1, 2), gfx::ScrollOffset(3, 4), gfx::SizeF(5, 6), 7, 8, |
| 9); |
| |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler); |
| testing::Mock::VerifyAndClearExpectations(&mock_client); |
| testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler); |
| } |
| |
| TEST(SynchronousInputHandlerProxyTest, SetOffset) { |
| testing::NiceMock<MockInputHandler> mock_input_handler; |
| testing::StrictMock<MockInputHandlerProxyClient> mock_client; |
| testing::StrictMock<MockSynchronousInputHandler> |
| mock_synchronous_input_handler; |
| ui::InputHandlerProxy proxy(&mock_input_handler, &mock_client, false); |
| |
| proxy.SetOnlySynchronouslyAnimateRootFlings(&mock_synchronous_input_handler); |
| |
| EXPECT_CALL(mock_input_handler, SetSynchronousInputHandlerRootScrollOffset( |
| gfx::ScrollOffset(5, 6))); |
| proxy.SynchronouslySetRootScrollOffset(gfx::ScrollOffset(5, 6)); |
| |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler); |
| testing::Mock::VerifyAndClearExpectations(&mock_client); |
| testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler); |
| } |
| |
| TEST_P(InputHandlerProxyTest, MainThreadScrollingMouseWheelHistograms) { |
| input_handler_->RecordMainThreadScrollingReasonsForTest( |
| blink::kWebGestureDeviceTouchpad, |
| cc::MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects | |
| cc::MainThreadScrollingReason::kThreadedScrollingDisabled | |
| cc::MainThreadScrollingReason::kPageOverlay | |
| cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); |
| |
| EXPECT_THAT( |
| histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), |
| testing::ElementsAre(base::Bucket(1, 1), base::Bucket(3, 1), |
| base::Bucket(5, 1))); |
| |
| // We only want to record "Handling scroll from main thread" reason if it's |
| // the only reason. If it's not the only reason, the "real" reason for |
| // scrolling on main is something else, and we only want to pay attention to |
| // that reason. So we should only include this reason in the histogram when |
| // its on its own. |
| input_handler_->RecordMainThreadScrollingReasonsForTest( |
| blink::kWebGestureDeviceTouchpad, |
| cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); |
| |
| EXPECT_THAT( |
| histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), |
| testing::ElementsAre(base::Bucket(1, 1), base::Bucket(3, 1), |
| base::Bucket(5, 1), base::Bucket(14, 1))); |
| } |
| |
| TEST_P(InputHandlerProxyTest, GestureScrollingThreadStatusHistogram) { |
| VERIFY_AND_RESET_MOCKS(); |
| |
| WebTouchEvent touch_start(WebInputEvent::kTouchStart, |
| WebInputEvent::kNoModifiers, |
| WebInputEvent::kTimeStampForTesting); |
| touch_start.touches_length = 1; |
| touch_start.touch_start_or_first_touch_move = true; |
| touch_start.touches[0] = |
| CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); |
| |
| WebGestureEvent gesture_scroll_begin; |
| gesture_scroll_begin.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_scroll_begin.source_device = blink::kWebGestureDeviceTouchscreen; |
| |
| WebGestureEvent gesture_scroll_end; |
| gesture_scroll_end.SetType(WebInputEvent::kGestureScrollEnd); |
| gesture_scroll_end.source_device = blink::kWebGestureDeviceTouchscreen; |
| |
| // Touch start with passive event listener. |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) |
| .WillOnce(testing::Return( |
| cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); |
| EXPECT_CALL(mock_client_, |
| SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) |
| .WillOnce(testing::Return()); |
| |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(touch_start)); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_begin)); |
| |
| EXPECT_THAT(histogram_tester().GetAllSamples( |
| "Renderer4.GestureScrollingThreadStatus"), |
| testing::ElementsAre(base::Bucket(0, 1))); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_end)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Touch event with HANDLER_ON_SCROLLING_LAYER event listener. |
| EXPECT_CALL( |
| mock_input_handler_, |
| EventListenerTypeForTouchStartOrMoveAt( |
| testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) |
| .WillOnce( |
| testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType:: |
| HANDLER_ON_SCROLLING_LAYER)); |
| EXPECT_CALL(mock_client_, |
| SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) |
| .WillOnce(testing::Return()); |
| |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(touch_start)); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_begin)); |
| |
| EXPECT_THAT(histogram_tester().GetAllSamples( |
| "Renderer4.GestureScrollingThreadStatus"), |
| testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1))); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_end)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Gesture scrolling on main thread. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_begin)); |
| |
| EXPECT_THAT(histogram_tester().GetAllSamples( |
| "Renderer4.GestureScrollingThreadStatus"), |
| testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1), |
| base::Bucket(2, 1))); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_end)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyTest, WheelScrollingThreadStatusHistogram) { |
| VERIFY_AND_RESET_MOCKS(); |
| |
| WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel, |
| WebInputEvent::kControlKey, |
| WebInputEvent::kTimeStampForTesting); |
| |
| WebGestureEvent gesture_scroll_begin; |
| gesture_scroll_begin.SetType(WebInputEvent::kGestureScrollBegin); |
| gesture_scroll_begin.source_device = blink::kWebGestureDeviceTouchpad; |
| |
| WebGestureEvent gesture_scroll_end; |
| gesture_scroll_end.SetType(WebInputEvent::kGestureScrollEnd); |
| gesture_scroll_end.source_device = blink::kWebGestureDeviceTouchpad; |
| |
| // Wheel event with passive event listener. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_begin)); |
| |
| EXPECT_THAT( |
| histogram_tester().GetAllSamples("Renderer4.WheelScrollingThreadStatus"), |
| testing::ElementsAre(base::Bucket(0, 1))); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_end)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Wheel event with blocking event listener. |
| EXPECT_CALL(mock_input_handler_, |
| GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) |
| .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_begin)); |
| |
| EXPECT_THAT( |
| histogram_tester().GetAllSamples("Renderer4.WheelScrollingThreadStatus"), |
| testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1))); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_end)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| |
| // Wheel scrolling on main thread. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kMainThreadScrollState)); |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_begin)); |
| |
| EXPECT_THAT( |
| histogram_tester().GetAllSamples("Renderer4.WheelScrollingThreadStatus"), |
| testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1), |
| base::Bucket(2, 1))); |
| |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; |
| EXPECT_EQ(expected_disposition_, |
| input_handler_->HandleInputEvent(gesture_scroll_end)); |
| |
| VERIFY_AND_RESET_MOCKS(); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScroll) { |
| base::HistogramTester histogram_tester; |
| |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| |
| // GestureScrollBegin will be processed immediately. |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE, event_disposition_recorder_[0]); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| |
| // GestureScrollUpdate will be queued. |
| EXPECT_EQ(1ul, event_queue().size()); |
| EXPECT_EQ(-20, static_cast<const blink::WebGestureEvent&>( |
| event_queue().front()->event()) |
| .data.scroll_update.delta_y); |
| EXPECT_EQ(1ul, event_queue().front()->coalesced_count()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -40); |
| |
| // GestureScrollUpdate will be coalesced. |
| EXPECT_EQ(1ul, event_queue().size()); |
| EXPECT_EQ(-60, static_cast<const blink::WebGestureEvent&>( |
| event_queue().front()->event()) |
| .data.scroll_update.delta_y); |
| EXPECT_EQ(2ul, event_queue().front()->coalesced_count()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| |
| // GestureScrollEnd will be queued. |
| EXPECT_EQ(2ul, event_queue().size()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| // Dispatch all queued events. |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| EXPECT_EQ(0ul, event_queue().size()); |
| // Should run callbacks for every original events. |
| EXPECT_EQ(4ul, event_disposition_recorder_.size()); |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE, event_disposition_recorder_[1]); |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE, event_disposition_recorder_[2]); |
| EXPECT_EQ(InputHandlerProxy::DID_HANDLE, event_disposition_recorder_[3]); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| histogram_tester.ExpectUniqueSample(kCoalescedCountHistogram, 2, 1); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScrollPinchScroll) { |
| base::HistogramTester histogram_tester; |
| |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| // Start scroll in the first frame. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| |
| EXPECT_EQ(1ul, event_queue().size()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(2ul, event_disposition_recorder_.size()); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| |
| // Continue scroll in the second frame, pinch, then start another scroll. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(2); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); |
| EXPECT_CALL(mock_input_handler_, PinchGestureBegin()); |
| // Two |GesturePinchUpdate| will be coalesced. |
| EXPECT_CALL(mock_input_handler_, |
| PinchGestureUpdate(0.7f, gfx::Point(13, 17))); |
| EXPECT_CALL(mock_input_handler_, PinchGestureEnd()); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| HandleGestureEvent(WebInputEvent::kGesturePinchBegin); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 1.4f, 13, 17); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.5f, 13, 17); |
| HandleGestureEvent(WebInputEvent::kGesturePinchEnd); |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -70); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -5); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| |
| EXPECT_EQ(8ul, event_queue().size()); |
| EXPECT_EQ(2ul, event_disposition_recorder_.size()); |
| |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(12ul, event_disposition_recorder_.size()); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| histogram_tester.ExpectBucketCount(kCoalescedCountHistogram, 1, 2); |
| histogram_tester.ExpectBucketCount(kCoalescedCountHistogram, 2, 2); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedQueueingTime) { |
| base::HistogramTester histogram_tester; |
| std::unique_ptr<base::SimpleTestTickClock> tick_clock = |
| std::make_unique<base::SimpleTestTickClock>(); |
| base::SimpleTestTickClock* tick_clock_ptr = tick_clock.get(); |
| tick_clock_ptr->SetNowTicks(base::TimeTicks::Now()); |
| SetInputHandlerProxyTickClockForTesting(std::move(tick_clock)); |
| |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| tick_clock_ptr->Advance(base::TimeDelta::FromMicroseconds(10)); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| tick_clock_ptr->Advance(base::TimeDelta::FromMicroseconds(40)); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -40); |
| tick_clock_ptr->Advance(base::TimeDelta::FromMicroseconds(20)); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); |
| tick_clock_ptr->Advance(base::TimeDelta::FromMicroseconds(10)); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| |
| // Dispatch all queued events. |
| tick_clock_ptr->Advance(base::TimeDelta::FromMicroseconds(70)); |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(5ul, event_disposition_recorder_.size()); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| histogram_tester.ExpectUniqueSample(kContinuousHeadQueueingTimeHistogram, 140, |
| 1); |
| histogram_tester.ExpectUniqueSample(kContinuousTailQueueingTimeHistogram, 80, |
| 1); |
| histogram_tester.ExpectBucketCount(kNonContinuousQueueingTimeHistogram, 0, 1); |
| histogram_tester.ExpectBucketCount(kNonContinuousQueueingTimeHistogram, 70, |
| 1); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedCoalesceScrollAndPinch) { |
| // Start scroll in the first frame. |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); |
| |
| // GSUs and GPUs in one sequence should be coalesced into 1 GSU and 1 GPU. |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -7); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 2.0f, 13, 10); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -6); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| HandleGestureEvent(WebInputEvent::kGesturePinchBegin); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.2f, 2, 20); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 10.0f, 1, 10); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.25f, 3, 30); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); |
| HandleGestureEvent(WebInputEvent::kGesturePinchEnd); |
| |
| // Only the first GSB was dispatched. |
| EXPECT_EQ(7ul, event_queue().size()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| event_queue()[0]->event().GetType()); |
| EXPECT_EQ( |
| -35, |
| ToWebGestureEvent(event_queue()[0]->event()).data.scroll_update.delta_y); |
| EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, |
| event_queue()[1]->event().GetType()); |
| EXPECT_EQ( |
| 2.0f, |
| ToWebGestureEvent(event_queue()[1]->event()).data.pinch_update.scale); |
| EXPECT_EQ(WebInputEvent::kGestureScrollEnd, |
| event_queue()[2]->event().GetType()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchBegin, |
| event_queue()[3]->event().GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| event_queue()[4]->event().GetType()); |
| EXPECT_EQ( |
| -85, |
| ToWebGestureEvent(event_queue()[4]->event()).data.scroll_update.delta_y); |
| EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, |
| event_queue()[5]->event().GetType()); |
| EXPECT_EQ( |
| 0.5f, |
| ToWebGestureEvent(event_queue()[5]->event()).data.pinch_update.scale); |
| EXPECT_EQ(WebInputEvent::kGesturePinchEnd, |
| event_queue()[6]->event().GetType()); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, OriginalEventsTracing) { |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillRepeatedly(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()) |
| .Times(::testing::AtLeast(1)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .Times(::testing::AtLeast(1)); |
| |
| StartTracing(); |
| // Simulate scroll. |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -40); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| |
| // Simulate scroll and pinch. |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 10.0f, 1, 10); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); |
| HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 2.0f, 1, 10); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| |
| // Dispatch all events. |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| StopTracing(); |
| |
| // Retrieve tracing data. |
| std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer = |
| CreateTraceAnalyzer(); |
| trace_analyzer::TraceEventVector begin_events; |
| trace_analyzer::Query begin_query = trace_analyzer::Query::EventPhaseIs( |
| TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN); |
| analyzer->FindEvents(begin_query, &begin_events); |
| |
| trace_analyzer::TraceEventVector end_events; |
| trace_analyzer::Query end_query = |
| trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END); |
| analyzer->FindEvents(end_query, &end_events); |
| |
| EXPECT_EQ(5ul, begin_events.size()); |
| EXPECT_EQ(5ul, end_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| end_events[0]->GetKnownArgAsInt("type")); |
| EXPECT_EQ(3, end_events[0]->GetKnownArgAsInt("coalesced_count")); |
| EXPECT_EQ(WebInputEvent::kGestureScrollEnd, |
| end_events[1]->GetKnownArgAsInt("type")); |
| |
| EXPECT_EQ(WebInputEvent::kGestureScrollBegin, |
| end_events[2]->GetKnownArgAsInt("type")); |
| // Original scroll and pinch updates will be stored in the coalesced |
| // PinchUpdate of the <ScrollUpdate, PinchUpdate> pair. |
| // The ScrollUpdate of the pair doesn't carry original events and won't be |
| // traced. |
| EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, |
| end_events[3]->GetKnownArgAsInt("type")); |
| EXPECT_EQ(4, end_events[3]->GetKnownArgAsInt("coalesced_count")); |
| EXPECT_EQ(WebInputEvent::kGestureScrollEnd, |
| end_events[4]->GetKnownArgAsInt("type")); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, GestureScrollFlingOrder) { |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillRepeatedly(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()) |
| .Times(::testing::AtLeast(1)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .Times(::testing::AtLeast(1)); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // Simulate scroll. |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); |
| HandleGestureEvent(WebInputEvent::kGestureFlingStart, -10); |
| |
| // ScrollUpdate and FlingStart should be queued. |
| EXPECT_EQ(2ul, event_queue().size()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| event_queue()[0]->event().GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureFlingStart, |
| event_queue()[1]->event().GetType()); |
| |
| // Dispatch events. |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(4ul, event_disposition_recorder_.size()); |
| EXPECT_TRUE( |
| input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| // Send FlingCancel to stop scrolling. |
| HandleGestureEvent(WebInputEvent::kGestureFlingCancel); |
| EXPECT_EQ(1ul, event_queue().size()); |
| EXPECT_EQ(WebInputEvent::kGestureFlingCancel, |
| event_queue()[0]->event().GetType()); |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(5ul, event_disposition_recorder_.size()); |
| // Should stop scrolling. Note that no ScrollEnd was sent. |
| EXPECT_TRUE( |
| !input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, GestureScrollAfterFling) { |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillRepeatedly(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()) |
| .Times(::testing::AtLeast(1)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .Times(::testing::AtLeast(1)); |
| EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| |
| // Simulate fling. |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| HandleGestureEvent(WebInputEvent::kGestureFlingStart, -10); |
| HandleGestureEvent(WebInputEvent::kGestureFlingCancel); |
| |
| // Dispatch events. |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(4ul, event_disposition_recorder_.size()); |
| EXPECT_FALSE( |
| input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); |
| |
| // New ScrollBegin should be dispatched immediately as there is no on-going |
| // scroll, fling or pinch. |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| EXPECT_EQ(0ul, event_queue().size()); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, TouchpadGestureScrollEndFlushQueue) { |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillRepeatedly(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()) |
| .Times(::testing::AtLeast(1)); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)) |
| .Times(::testing::AtLeast(1)); |
| |
| // Simulate scroll. |
| HandleGestureEventWithSourceDevice(WebInputEvent::kGestureScrollBegin, |
| blink::kWebGestureDeviceTouchpad); |
| HandleGestureEventWithSourceDevice(WebInputEvent::kGestureScrollUpdate, |
| blink::kWebGestureDeviceTouchpad, -20); |
| |
| // GSB will be dispatched immediately, GSU will be queued. |
| EXPECT_EQ(1ul, event_queue().size()); |
| EXPECT_EQ(1ul, event_disposition_recorder_.size()); |
| |
| // Touchpad GSE will flush the queue. |
| HandleGestureEventWithSourceDevice(WebInputEvent::kGestureScrollEnd, |
| blink::kWebGestureDeviceTouchpad); |
| |
| EXPECT_EQ(0ul, event_queue().size()); |
| EXPECT_EQ(3ul, event_disposition_recorder_.size()); |
| EXPECT_FALSE( |
| input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); |
| } |
| |
| TEST_P(InputHandlerProxyEventQueueTest, CoalescedLatencyInfo) { |
| // Handle scroll on compositor. |
| cc::InputHandlerScrollResult scroll_result_did_scroll_; |
| scroll_result_did_scroll_.did_scroll = true; |
| |
| EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) |
| .WillOnce(testing::Return(kImplThreadScrollState)); |
| EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); |
| EXPECT_CALL( |
| mock_input_handler_, |
| ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) |
| .WillOnce(testing::Return(scroll_result_did_scroll_)); |
| EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); |
| |
| HandleGestureEvent(WebInputEvent::kGestureScrollBegin); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -40); |
| HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); |
| HandleGestureEvent(WebInputEvent::kGestureScrollEnd); |
| input_handler_proxy_->DeliverInputForBeginFrame(); |
| |
| EXPECT_EQ(0ul, event_queue().size()); |
| // Should run callbacks for every original events. |
| EXPECT_EQ(5ul, event_disposition_recorder_.size()); |
| EXPECT_EQ(5ul, latency_info_recorder_.size()); |
| EXPECT_EQ(false, latency_info_recorder_[1].coalesced()); |
| // Coalesced events should have latency set to coalesced. |
| EXPECT_EQ(true, latency_info_recorder_[2].coalesced()); |
| EXPECT_EQ(true, latency_info_recorder_[3].coalesced()); |
| testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(AnimateInput, |
| InputHandlerProxyTest, |
| testing::ValuesIn(test_types)); |
| |
| INSTANTIATE_TEST_CASE_P(AnimateInput, |
| InputHandlerProxyWithoutWheelScrollLatchingTest, |
| testing::ValuesIn(test_types)); |
| |
| INSTANTIATE_TEST_CASE_P(InputHandlerProxyEventQueueTests, |
| InputHandlerProxyEventQueueTest, |
| testing::Bool()); |
| |
| } // namespace test |
| } // namespace ui |