/* * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WebCompositorInputHandlerImpl.h" #include "WebCompositorInitializer.h" #include "WebCompositorInputHandlerClient.h" #include "WebInputEvent.h" #include #include #include #include #include #include #include #include using namespace WebKit; namespace { class MockWebInputHandlerClient : public WebInputHandlerClient { WTF_MAKE_NONCOPYABLE(MockWebInputHandlerClient); public: MockWebInputHandlerClient() { } virtual ~MockWebInputHandlerClient() { } MOCK_METHOD0(pinchGestureBegin, void()); MOCK_METHOD2(pinchGestureUpdate, void(float magnifyDelta, WebPoint anchor)); MOCK_METHOD0(pinchGestureEnd, void()); MOCK_METHOD0(scheduleAnimation, void()); MOCK_METHOD2(scrollBegin, ScrollStatus(WebPoint, WebInputHandlerClient::ScrollInputType)); MOCK_METHOD2(scrollByIfPossible, bool(WebPoint, WebSize)); MOCK_METHOD0(scrollEnd, void()); private: virtual void startPageScaleAnimation(WebSize targetPosition, bool anchorPoint, float pageScale, double startTimeMs, double durationMs) OVERRIDE { } }; class MockWebCompositorInputHandlerClient : public WebCompositorInputHandlerClient { WTF_MAKE_NONCOPYABLE(MockWebCompositorInputHandlerClient); public: MockWebCompositorInputHandlerClient() : WebCompositorInputHandlerClient() { } virtual ~MockWebCompositorInputHandlerClient() { } MOCK_METHOD0(willShutdown, void()); MOCK_METHOD0(didHandleInputEvent, void()); MOCK_METHOD1(didNotHandleInputEvent, void(bool sendToWidget)); MOCK_METHOD1(transferActiveWheelFlingAnimation, void(const WebActiveWheelFlingParameters&)); }; class WebCompositorInputHandlerImplTest : public testing::Test { public: WebCompositorInputHandlerImplTest() : m_initializer(0) , m_expectedDisposition(DidHandle) { m_inputHandler = adoptPtr(new WebCompositorInputHandlerImpl); m_inputHandler->bindToClient(&m_mockInputHandlerClient); m_inputHandler->setClient(&m_mockClient); } ~WebCompositorInputHandlerImplTest() { m_inputHandler->setClient(0); m_inputHandler.clear(); } // 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(&m_mockInputHandlerClient); \ testing::Mock::VerifyAndClearExpectations(&m_mockClient); \ switch (m_expectedDisposition) { \ case DidHandle: \ /* If we expect to handle events, we shouldn't get any didNotHandleInputEvent() calls with any parameter. */ \ EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0); \ EXPECT_CALL(m_mockClient, didHandleInputEvent()); \ break; \ case DidNotHandle: \ /* If we aren't expecting to handle events, we shouldn't call didHandleInputEvent(). */ \ EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0); \ EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false)).Times(0); \ EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)); \ break; \ case DropEvent: \ /* If we're expecting to drop, we shouldn't get any didHandle..() or didNotHandleInputEvent(true) calls. */ \ EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0); \ EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)).Times(0); \ EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false)); \ break; \ } \ } while (0) protected: testing::StrictMock m_mockInputHandlerClient; OwnPtr m_inputHandler; testing::StrictMock m_mockClient; WebGestureEvent gesture; WebKitTests::WebCompositorInitializer m_initializer; enum ExpectedDisposition { DidHandle, DidNotHandle, DropEvent }; ExpectedDisposition m_expectedDisposition; }; TEST_F(WebCompositorInputHandlerImplTest, gestureScrollStarted) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); gesture.type = WebInputEvent::GestureScrollBegin; m_inputHandler->handleInputEvent(gesture); // The event should not be marked as handled if scrolling is not possible. m_expectedDisposition = DropEvent; VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollUpdate; gesture.data.scrollUpdate.deltaY = -40; // -Y means scroll down - i.e. in the +Y direction. EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::height, testing::Gt(0)))) .WillOnce(testing::Return(false)); m_inputHandler->handleInputEvent(gesture); // Mark the event as handled if scroll happens. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollUpdate; gesture.data.scrollUpdate.deltaY = -40; // -Y means scroll down - i.e. in the +Y direction. EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::height, testing::Gt(0)))) .WillOnce(testing::Return(true)); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollEnd; gesture.data.scrollUpdate.deltaY = 0; EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureScrollOnMainThread) { // We should send all events to the widget for this gesture. m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(::testing::_, ::testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); gesture.type = WebInputEvent::GestureScrollBegin; m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollUpdate; gesture.data.scrollUpdate.deltaY = 40; m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollEnd; gesture.data.scrollUpdate.deltaY = 0; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureScrollIgnored) { // We shouldn't handle the GestureScrollBegin. // Instead, we should get one didNotHandleInputEvent(false) call per handleInputEvent(), // 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. m_expectedDisposition = DropEvent; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusIgnored)); gesture.type = WebInputEvent::GestureScrollBegin; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gesturePinch) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchBegin; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureBegin()); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchUpdate; gesture.data.pinchUpdate.scale = 1.5; gesture.x = 7; gesture.y = 13; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureUpdate(1.5, WebPoint(7, 13))); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchUpdate; gesture.data.pinchUpdate.scale = 0.5; gesture.x = 9; gesture.y = 6; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureUpdate(.5, WebPoint(9, 6))); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchEnd; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureEnd()); m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gesturePinchAfterScrollOnMainThread) { // Scrolls will start by being sent to the main thread. m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(::testing::_, ::testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); gesture.type = WebInputEvent::GestureScrollBegin; m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollUpdate; gesture.data.scrollUpdate.deltaY = 40; m_inputHandler->handleInputEvent(gesture); // However, after the pinch gesture starts, they should go to the impl // thread. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchBegin; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureBegin()); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchUpdate; gesture.data.pinchUpdate.scale = 1.5; gesture.x = 7; gesture.y = 13; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureUpdate(1.5, WebPoint(7, 13))); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollUpdate; gesture.data.scrollUpdate.deltaY = -40; // -Y means scroll down - i.e. in the +Y direction. EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::height, testing::Gt(0)))) .WillOnce(testing::Return(true)); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchUpdate; gesture.data.pinchUpdate.scale = 0.5; gesture.x = 9; gesture.y = 6; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureUpdate(.5, WebPoint(9, 6))); m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GesturePinchEnd; EXPECT_CALL(m_mockInputHandlerClient, pinchGestureEnd()); m_inputHandler->handleInputEvent(gesture); // After the pinch gesture ends, they should go to back to the main // thread. m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); gesture.type = WebInputEvent::GestureScrollEnd; gesture.data.scrollUpdate.deltaY = 0; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingStartedTouchpad) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.velocityX = 10; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); // Verify that a GestureFlingCancel during an animation cancels it. gesture.type = WebInputEvent::GestureFlingCancel; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingOnMainThreadTouchpad) { // We should send all events to the widget for this gesture. m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; m_inputHandler->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.type = WebInputEvent::GestureFlingCancel; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingIgnoredTouchpad) { m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusIgnored)); gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; m_inputHandler->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.type = WebInputEvent::GestureFlingCancel; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimatesTouchpad) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); // On the fling start, we should schedule an animation but not actually start // scrolling. gesture.type = WebInputEvent::GestureFlingStart; WebFloatPoint flingDelta = WebFloatPoint(1000, 0); WebPoint flingPoint = WebPoint(7, 13); WebPoint flingGlobalPoint = WebPoint(17, 23); int modifiers = 7; gesture.data.flingStart.velocityX = flingDelta.x; gesture.data.flingStart.velocityY = flingDelta.y; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; gesture.x = flingPoint.x; gesture.y = flingPoint.y; gesture.globalX = flingGlobalPoint.x; gesture.globalY = flingGlobalPoint.y; gesture.modifiers = modifiers; EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->handleInputEvent(gesture); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // 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_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); m_inputHandler->animate(10); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // The second call should start scrolling in the -X direction. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::width, testing::Lt(0)))) .WillOnce(testing::Return(true)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->animate(10.1); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // 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(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::_)).Times(0); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()).Times(0); // Expected wheel fling animation parameters: // *) flingDelta and flingPoint 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(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf( testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)), testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)), testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)), testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)), testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(10)), testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, testing::Field(&WebSize::width, testing::Gt(0)))))); m_inputHandler->animate(10.2); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); testing::Mock::VerifyAndClearExpectations(&m_mockClient); // Since we've aborted the fling, the next animation should be a no-op and should not result in another // frame being requested. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()).Times(0); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); m_inputHandler->animate(10.3); // Since we've transferred the fling to the main thread, we need to pass the next GestureFlingCancel to the main // thread as well. EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)); gesture.type = WebInputEvent::GestureFlingCancel; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingTransferResetsTouchpad) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); // Start a gesture fling in the -X direction with zero Y movement. gesture.type = WebInputEvent::GestureFlingStart; WebFloatPoint flingDelta = WebFloatPoint(1000, 0); WebPoint flingPoint = WebPoint(7, 13); WebPoint flingGlobalPoint = WebPoint(17, 23); int modifiers = 1; gesture.data.flingStart.velocityX = flingDelta.x; gesture.data.flingStart.velocityY = flingDelta.y; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; gesture.x = flingPoint.x; gesture.y = flingPoint.y; gesture.globalX = flingGlobalPoint.x; gesture.globalY = flingGlobalPoint.y; gesture.modifiers = modifiers; EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->handleInputEvent(gesture); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // Start the fling animation at time 10. This shouldn't actually scroll, just establish a start time. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); m_inputHandler->animate(10); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // The second call should start scrolling in the -X direction. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::width, testing::Lt(0)))) .WillOnce(testing::Return(true)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->animate(10.1); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // 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(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::_)).Times(0); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()).Times(0); // Expected wheel fling animation parameters: // *) flingDelta and flingPoint 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(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf( testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)), testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)), testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)), testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)), testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(10)), testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, testing::Field(&WebSize::width, testing::Gt(0)))))); m_inputHandler->animate(10.2); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); testing::Mock::VerifyAndClearExpectations(&m_mockClient); // Since we've aborted the fling, the next animation should be a no-op and should not result in another // frame being requested. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()).Times(0); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); m_inputHandler->animate(10.3); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // Since we've transferred the fling to the main thread, we need to pass the next GestureFlingCancel to the main // thread as well. EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)); gesture.type = WebInputEvent::GestureFlingCancel; m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); // Start a second gesture fling, this time in the +Y direction with no X. gesture.type = WebInputEvent::GestureFlingStart; flingDelta = WebFloatPoint(0, -1000); flingPoint = WebPoint(95, 87); flingGlobalPoint = WebPoint(32, 71); modifiers = 2; gesture.data.flingStart.velocityX = flingDelta.x; gesture.data.flingStart.velocityY = flingDelta.y; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchpad; gesture.x = flingPoint.x; gesture.y = flingPoint.y; gesture.globalX = flingGlobalPoint.x; gesture.globalY = flingGlobalPoint.y; gesture.modifiers = modifiers; EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->handleInputEvent(gesture); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // Start the second fling animation at time 30. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); m_inputHandler->animate(30); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // Tick the second fling once normally. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::height, testing::Gt(0)))) .WillOnce(testing::Return(true)); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); m_inputHandler->animate(30.1); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // Then abort the second fling. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::_)).Times(0); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()).Times(0); // We should get parameters from the second fling, nothing from the first fling should "leak". EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf( testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)), testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)), testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)), testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)), testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(30)), testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, testing::Field(&WebSize::height, testing::Lt(0)))))); m_inputHandler->animate(30.2); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingStartedTouchscreen) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.velocityX = 10; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; m_inputHandler->handleInputEvent(gesture); VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); // Verify that a GestureFlingCancel during an animation cancels it. gesture.type = WebInputEvent::GestureFlingCancel; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingOnMainThreadTouchscreen) { // We should send all events to the widget for this gesture. m_expectedDisposition = DidNotHandle; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusOnMainThread)); gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; m_inputHandler->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.type = WebInputEvent::GestureFlingCancel; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingIgnoredTouchscreen) { m_expectedDisposition = DropEvent; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusIgnored)); gesture.type = WebInputEvent::GestureFlingStart; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; m_inputHandler->handleInputEvent(gesture); m_expectedDisposition = DidNotHandle; 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.type = WebInputEvent::GestureFlingCancel; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; m_inputHandler->handleInputEvent(gesture); } TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimatesTouchscreen) { // We shouldn't send any events to the widget for this gesture. m_expectedDisposition = DidHandle; VERIFY_AND_RESET_MOCKS(); // On the fling start, we should schedule an animation but not actually start // scrolling. gesture.type = WebInputEvent::GestureFlingStart; WebFloatPoint flingDelta = WebFloatPoint(1000, 0); WebPoint flingPoint = WebPoint(7, 13); WebPoint flingGlobalPoint = WebPoint(17, 23); int modifiers = 7; gesture.data.flingStart.velocityX = flingDelta.x; gesture.data.flingStart.velocityY = flingDelta.y; gesture.data.flingStart.sourceDevice = WebGestureEvent::Touchscreen; gesture.x = flingPoint.x; gesture.y = flingPoint.y; gesture.globalX = flingGlobalPoint.x; gesture.globalY = flingGlobalPoint.y; gesture.modifiers = modifiers; EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(WebInputHandlerClient::ScrollStatusStarted)); m_inputHandler->handleInputEvent(gesture); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // 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_CALL(m_mockInputHandlerClient, scheduleAnimation()); m_inputHandler->animate(10); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); // The second call should start scrolling in the -X direction. EXPECT_CALL(m_mockInputHandlerClient, scheduleAnimation()); EXPECT_CALL(m_mockInputHandlerClient, scrollByIfPossible(testing::_, testing::Field(&WebSize::width, testing::Lt(0)))) .WillOnce(testing::Return(true)); m_inputHandler->animate(10.1); testing::Mock::VerifyAndClearExpectations(&m_mockInputHandlerClient); EXPECT_CALL(m_mockClient, didHandleInputEvent()); EXPECT_CALL(m_mockInputHandlerClient, scrollEnd()); gesture.type = WebInputEvent::GestureFlingCancel; m_inputHandler->handleInputEvent(gesture); } }