diff options
Diffstat (limited to 'layout/base/gtest')
-rw-r--r-- | layout/base/gtest/TestAccessibleCaretEventHub.cpp | 827 | ||||
-rw-r--r-- | layout/base/gtest/TestAccessibleCaretManager.cpp | 810 | ||||
-rw-r--r-- | layout/base/gtest/moz.build | 29 |
3 files changed, 1666 insertions, 0 deletions
diff --git a/layout/base/gtest/TestAccessibleCaretEventHub.cpp b/layout/base/gtest/TestAccessibleCaretEventHub.cpp new file mode 100644 index 000000000..5216a52dc --- /dev/null +++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp @@ -0,0 +1,827 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include <iostream> +#include <string> + +#include "AccessibleCaretEventHub.h" +#include "AccessibleCaretManager.h" +#include "gfxPrefs.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/TouchEvents.h" + +using ::testing::AtLeast; +using ::testing::DefaultValue; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::_; + +// ----------------------------------------------------------------------------- +// This file test the state transitions of AccessibleCaretEventHub under +// various combination of events and callbacks. + +namespace mozilla +{ + +class MockAccessibleCaretManager : public AccessibleCaretManager +{ +public: + MockAccessibleCaretManager() + : AccessibleCaretManager(nullptr) + { + } + + MOCK_METHOD2(PressCaret, + nsresult(const nsPoint& aPoint, EventClassID aEventClass)); + MOCK_METHOD1(DragCaret, nsresult(const nsPoint& aPoint)); + MOCK_METHOD0(ReleaseCaret, nsresult()); + MOCK_METHOD1(TapCaret, nsresult(const nsPoint& aPoint)); + MOCK_METHOD1(SelectWordOrShortcut, nsresult(const nsPoint& aPoint)); + MOCK_METHOD0(OnScrollStart, void()); + MOCK_METHOD0(OnScrollEnd, void()); + MOCK_METHOD0(OnScrollPositionChanged, void()); + MOCK_METHOD0(OnBlur, void()); +}; + +class MockAccessibleCaretEventHub : public AccessibleCaretEventHub +{ +public: + using AccessibleCaretEventHub::NoActionState; + using AccessibleCaretEventHub::PressCaretState; + using AccessibleCaretEventHub::DragCaretState; + using AccessibleCaretEventHub::PressNoCaretState; + using AccessibleCaretEventHub::ScrollState; + using AccessibleCaretEventHub::PostScrollState; + using AccessibleCaretEventHub::LongTapState; + using AccessibleCaretEventHub::FireScrollEnd; + + MockAccessibleCaretEventHub() + : AccessibleCaretEventHub(nullptr) + { + mManager = MakeUnique<MockAccessibleCaretManager>(); + mInitialized = true; + } + + virtual nsPoint GetTouchEventPosition(WidgetTouchEvent* aEvent, + int32_t aIdentifier) const override + { + // Return the device point directly. + LayoutDeviceIntPoint touchIntPoint = aEvent->mTouches[0]->mRefPoint; + return nsPoint(touchIntPoint.x, touchIntPoint.y); + } + + virtual nsPoint GetMouseEventPosition(WidgetMouseEvent* aEvent) const override + { + // Return the device point directly. + LayoutDeviceIntPoint mouseIntPoint = aEvent->AsGUIEvent()->mRefPoint; + return nsPoint(mouseIntPoint.x, mouseIntPoint.y); + } + + MockAccessibleCaretManager* GetMockAccessibleCaretManager() + { + return static_cast<MockAccessibleCaretManager*>(mManager.get()); + } +}; + +// Print the name of the state for debugging. +::std::ostream& operator<<(::std::ostream& aOstream, + const MockAccessibleCaretEventHub::State* aState) +{ + return aOstream << aState->Name(); +} + +class AccessibleCaretEventHubTester : public ::testing::Test +{ +public: + AccessibleCaretEventHubTester() + { + DefaultValue<nsresult>::Set(NS_OK); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState()); + + // AccessibleCaretEventHub requires the caller to hold a ref to it. We just + // add ref here for the sake of convenience. + mHub.get()->AddRef(); + } + + ~AccessibleCaretEventHubTester() + { + // Release the ref added in the constructor. + mHub.get()->Release(); + } + + static UniquePtr<WidgetEvent> CreateMouseEvent(EventMessage aMessage, + nscoord aX, + nscoord aY) + { + auto event = MakeUnique<WidgetMouseEvent>(true, aMessage, nullptr, + WidgetMouseEvent::eReal); + + event->button = WidgetMouseEvent::eLeftButton; + event->mRefPoint = LayoutDeviceIntPoint(aX, aY); + + return Move(event); + } + + static UniquePtr<WidgetEvent> CreateMousePressEvent(nscoord aX, nscoord aY) + { + return CreateMouseEvent(eMouseDown, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateMouseMoveEvent(nscoord aX, nscoord aY) + { + return CreateMouseEvent(eMouseMove, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateMouseReleaseEvent(nscoord aX, nscoord aY) + { + return CreateMouseEvent(eMouseUp, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateLongTapEvent(nscoord aX, nscoord aY) + { + return CreateMouseEvent(eMouseLongTap, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateTouchEvent(EventMessage aMessage, + nscoord aX, + nscoord aY) + { + auto event = MakeUnique<WidgetTouchEvent>(true, aMessage, nullptr); + int32_t identifier = 0; + LayoutDeviceIntPoint point(aX, aY); + LayoutDeviceIntPoint radius(19, 19); + float rotationAngle = 0; + float force = 1; + + RefPtr<dom::Touch> touch( + new dom::Touch(identifier, point, radius, rotationAngle, force)); + event->mTouches.AppendElement(touch); + + return Move(event); + } + + static UniquePtr<WidgetEvent> CreateTouchStartEvent(nscoord aX, nscoord aY) + { + return CreateTouchEvent(eTouchStart, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateTouchMoveEvent(nscoord aX, nscoord aY) + { + return CreateTouchEvent(eTouchMove, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateTouchEndEvent(nscoord aX, nscoord aY) + { + return CreateTouchEvent(eTouchEnd, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateTouchCancelEvent(nscoord aX, nscoord aY) + { + return CreateTouchEvent(eTouchCancel, aX, aY); + } + + static UniquePtr<WidgetEvent> CreateWheelEvent(EventMessage aMessage) + { + auto event = MakeUnique<WidgetWheelEvent>(true, aMessage, nullptr); + + return Move(event); + } + + void HandleEventAndCheckState(UniquePtr<WidgetEvent> aEvent, + MockAccessibleCaretEventHub::State* aExpectedState, + nsEventStatus aExpectedEventStatus) + { + nsEventStatus rv = mHub->HandleEvent(aEvent.get()); + EXPECT_EQ(mHub->GetState(), aExpectedState); + EXPECT_EQ(rv, aExpectedEventStatus); + } + + void CheckState(MockAccessibleCaretEventHub::State* aExpectedState) + { + EXPECT_EQ(mHub->GetState(), aExpectedState); + } + + template <typename PressEventCreator, typename ReleaseEventCreator> + void TestPressReleaseOnNoCaret(PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + template <typename PressEventCreator, typename ReleaseEventCreator> + void TestPressReleaseOnCaret(PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + template <typename PressEventCreator, typename MoveEventCreator, + typename ReleaseEventCreator> + void TestPressMoveReleaseOnNoCaret(PressEventCreator aPressEventCreator, + MoveEventCreator aMoveEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + template <typename PressEventCreator, typename MoveEventCreator, + typename ReleaseEventCreator> + void TestPressMoveReleaseOnCaret(PressEventCreator aPressEventCreator, + MoveEventCreator aMoveEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + template <typename PressEventCreator, typename ReleaseEventCreator> + void TestLongTapWithSelectWordSuccessful( + PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + template <typename PressEventCreator, typename ReleaseEventCreator> + void TestLongTapWithSelectWordFailed( + PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + template <typename PressEventCreator, typename MoveEventCreator, + typename ReleaseEventCreator> + void TestEventDrivenAsyncPanZoomScroll( + PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator, + ReleaseEventCreator aReleaseEventCreator); + + // Member variables + RefPtr<MockAccessibleCaretEventHub> mHub{new MockAccessibleCaretEventHub()}; + +}; // class AccessibleCaretEventHubTester + +TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnNoCaret) +{ + TestPressReleaseOnNoCaret(CreateMousePressEvent, CreateMouseReleaseEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnNoCaret) +{ + TestPressReleaseOnNoCaret(CreateTouchStartEvent, CreateTouchEndEvent); +} + +template <typename PressEventCreator, typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestPressReleaseOnNoCaret( + PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), ReleaseCaret()).Times(0); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), TapCaret(_)).Times(0); + + HandleEventAndCheckState(aPressEventCreator(0, 0), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(aReleaseEventCreator(0, 0), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore); +} + +TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnCaret) +{ + TestPressReleaseOnCaret(CreateMousePressEvent, CreateMouseReleaseEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnCaret) +{ + TestPressReleaseOnCaret(CreateTouchStartEvent, CreateTouchEndEvent); +} + +template <typename PressEventCreator, typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestPressReleaseOnCaret( + PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_OK)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), SelectWordOrShortcut(_)) + .Times(0); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), ReleaseCaret()); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), TapCaret(_)); + } + + HandleEventAndCheckState(aPressEventCreator(0, 0), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(CreateLongTapEvent(0, 0), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(aReleaseEventCreator(0, 0), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eConsumeNoDefault); +} + +TEST_F(AccessibleCaretEventHubTester, TestMousePressMoveReleaseOnNoCaret) +{ + TestPressMoveReleaseOnNoCaret(CreateMousePressEvent, CreateMouseMoveEvent, + CreateMouseReleaseEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnNoCaret) +{ + TestPressMoveReleaseOnNoCaret(CreateTouchStartEvent, CreateTouchMoveEvent, + CreateTouchEndEvent); +} + +template <typename PressEventCreator, typename MoveEventCreator, + typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestPressMoveReleaseOnNoCaret( + PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + nscoord x0 = 0, y0 = 0; + nscoord x1 = 100, y1 = 100; + nscoord x2 = 300, y2 = 300; + nscoord x3 = 400, y3 = 400; + + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), DragCaret(_)).Times(0); + } + + HandleEventAndCheckState(aPressEventCreator(x0, y0), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + // A small move with the distance between (x0, y0) and (x1, y1) below the + // tolerance value. + HandleEventAndCheckState(aMoveEventCreator(x1, y1), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + // A large move to simulate a dragging to select text since the distance + // between (x0, y0) and (x2, y2) is above the tolerance value. + HandleEventAndCheckState(aMoveEventCreator(x2, y2), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(aReleaseEventCreator(x3, y3), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore); +} + +TEST_F(AccessibleCaretEventHubTester, TestMousePressMoveReleaseOnCaret) +{ + TestPressMoveReleaseOnCaret(CreateMousePressEvent, CreateMouseMoveEvent, + CreateMouseReleaseEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnCaret) +{ + TestPressMoveReleaseOnCaret(CreateTouchStartEvent, CreateTouchMoveEvent, + CreateTouchEndEvent); +} + +template <typename PressEventCreator, typename MoveEventCreator, + typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestPressMoveReleaseOnCaret( + PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + nscoord x0 = 0, y0 = 0; + nscoord x1 = 100, y1 = 100; + nscoord x2 = 300, y2 = 300; + nscoord x3 = 400, y3 = 400; + + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_OK)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), DragCaret(_)) + .Times(2) // two valid drag operations + .WillRepeatedly(Return(NS_OK)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), ReleaseCaret()) + .WillOnce(Return(NS_OK)); + } + + HandleEventAndCheckState(aPressEventCreator(x0, y0), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eConsumeNoDefault); + + // A small move with the distance between (x0, y0) and (x1, y1) below the + // tolerance value. + HandleEventAndCheckState(aMoveEventCreator(x1, y1), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eConsumeNoDefault); + + // A large move forms a valid drag since the distance between (x0, y0) and + // (x2, y2) is above the tolerance value. + HandleEventAndCheckState(aMoveEventCreator(x2, y2), + MockAccessibleCaretEventHub::DragCaretState(), + nsEventStatus_eConsumeNoDefault); + + // Also a valid drag since the distance between (x0, y0) and (x3, y3) above + // the tolerance value even if the distance between (x2, y2) and (x3, y3) is + // below the tolerance value. + HandleEventAndCheckState(aMoveEventCreator(x3, y3), + MockAccessibleCaretEventHub::DragCaretState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(aReleaseEventCreator(x3, y3), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eConsumeNoDefault); +} + +TEST_F(AccessibleCaretEventHubTester, + TestTouchStartMoveEndOnCaretWithTouchCancelIgnored) +{ + nscoord x0 = 0, y0 = 0; + nscoord x1 = 100, y1 = 100; + nscoord x2 = 300, y2 = 300; + nscoord x3 = 400, y3 = 400; + + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_OK)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), DragCaret(_)) + .WillOnce(Return(NS_OK)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), ReleaseCaret()) + .WillOnce(Return(NS_OK)); + } + + // All the eTouchCancel events should be ignored in this test. + + HandleEventAndCheckState(CreateTouchStartEvent(x0, y0), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(CreateTouchCancelEvent(x0, y0), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eIgnore); + + // A small move with the distance between (x0, y0) and (x1, y1) below the + // tolerance value. + HandleEventAndCheckState(CreateTouchMoveEvent(x1, y1), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(CreateTouchCancelEvent(x1, y1), + MockAccessibleCaretEventHub::PressCaretState(), + nsEventStatus_eIgnore); + + // A large move forms a valid drag since the distance between (x0, y0) and + // (x2, y2) is above the tolerance value. + HandleEventAndCheckState(CreateTouchMoveEvent(x2, y2), + MockAccessibleCaretEventHub::DragCaretState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(CreateTouchCancelEvent(x2, y2), + MockAccessibleCaretEventHub::DragCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(CreateTouchEndEvent(x3, y3), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eConsumeNoDefault); + + HandleEventAndCheckState(CreateTouchCancelEvent(x3, y3), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore);} + +TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordSuccessful) +{ + TestLongTapWithSelectWordSuccessful(CreateMousePressEvent, + CreateMouseReleaseEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordSuccessful) +{ + TestLongTapWithSelectWordSuccessful(CreateTouchStartEvent, + CreateTouchEndEvent); +} + +template <typename PressEventCreator, typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestLongTapWithSelectWordSuccessful( + PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + MockFunction<void(::std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), SelectWordOrShortcut(_)) + .WillOnce(Return(NS_OK)); + + EXPECT_CALL(check, Call("longtap with scrolling")); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), SelectWordOrShortcut(_)) + .WillOnce(Return(NS_OK)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart()); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()); + } + + // Test long tap without scrolling. + HandleEventAndCheckState(aPressEventCreator(0, 0), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(CreateLongTapEvent(0, 0), + MockAccessibleCaretEventHub::LongTapState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(aReleaseEventCreator(0, 0), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore); + + // On Fennec, after long tap, the script might scroll and zoom the input field + // to the center of the screen to make typing easier before the user lifts the + // finger. + check.Call("longtap with scrolling"); + + HandleEventAndCheckState(aPressEventCreator(1, 1), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(CreateLongTapEvent(1, 1), + MockAccessibleCaretEventHub::LongTapState(), + nsEventStatus_eIgnore); + + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->AsyncPanZoomStopped(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::PostScrollState()); + + // Simulate scroll end fired by timer. + MockAccessibleCaretEventHub::FireScrollEnd(nullptr, mHub); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState()); + + HandleEventAndCheckState(aReleaseEventCreator(1, 1), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore); +} + +TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordFailed) +{ + TestLongTapWithSelectWordFailed(CreateMousePressEvent, + CreateMouseReleaseEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordFailed) +{ + TestLongTapWithSelectWordFailed(CreateTouchStartEvent, + CreateTouchEndEvent); +} + +template <typename PressEventCreator, typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestLongTapWithSelectWordFailed( + PressEventCreator aPressEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), SelectWordOrShortcut(_)) + .WillOnce(Return(NS_ERROR_FAILURE)); + } + + HandleEventAndCheckState(aPressEventCreator(0, 0), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(CreateLongTapEvent(0, 0), + MockAccessibleCaretEventHub::LongTapState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(aReleaseEventCreator(0, 0), + MockAccessibleCaretEventHub::NoActionState(), + nsEventStatus_eIgnore); +} + +TEST_F(AccessibleCaretEventHubTester, TestTouchEventDrivenAsyncPanZoomScroll) +{ + TestEventDrivenAsyncPanZoomScroll(CreateTouchStartEvent, CreateTouchMoveEvent, + CreateTouchEndEvent); +} + +TEST_F(AccessibleCaretEventHubTester, TestMouseEventDrivenAsyncPanZoomScroll) +{ + TestEventDrivenAsyncPanZoomScroll(CreateMousePressEvent, CreateMouseMoveEvent, + CreateMouseReleaseEvent); +} + +template <typename PressEventCreator, typename MoveEventCreator, + typename ReleaseEventCreator> +void +AccessibleCaretEventHubTester::TestEventDrivenAsyncPanZoomScroll( + PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator, + ReleaseEventCreator aReleaseEventCreator) +{ + MockFunction<void(::std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), DragCaret(_)).Times(0); + + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart()); + + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), PressCaret(_, _)) + .WillOnce(Return(NS_ERROR_FAILURE)); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), DragCaret(_)).Times(0); + + EXPECT_CALL(check, Call("3")); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart()); + + EXPECT_CALL(check, Call("4")); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()); + } + + // Receive press event. + HandleEventAndCheckState(aPressEventCreator(0, 0), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(aMoveEventCreator(100, 100), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + check.Call("1"); + + // Event driven scroll started + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + HandleEventAndCheckState(aMoveEventCreator(160, 160), + MockAccessibleCaretEventHub::ScrollState(), + nsEventStatus_eIgnore); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + // Event driven scroll ended + mHub->AsyncPanZoomStopped(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::PostScrollState()); + + HandleEventAndCheckState(aReleaseEventCreator(210, 210), + MockAccessibleCaretEventHub::PostScrollState(), + nsEventStatus_eIgnore); + + check.Call("2"); + + // Receive another press event. + HandleEventAndCheckState(aPressEventCreator(220, 220), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + HandleEventAndCheckState(aMoveEventCreator(230, 230), + MockAccessibleCaretEventHub::PressNoCaretState(), + nsEventStatus_eIgnore); + + check.Call("3"); + + // Another APZ scroll started + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + // Another APZ scroll ended + mHub->AsyncPanZoomStopped(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::PostScrollState()); + + HandleEventAndCheckState(aReleaseEventCreator(310, 310), + MockAccessibleCaretEventHub::PostScrollState(), + nsEventStatus_eIgnore); + + check.Call("4"); + + // Simulate scroll end fired by timer. + MockAccessibleCaretEventHub::FireScrollEnd(nullptr, mHub); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState()); +} + +TEST_F(AccessibleCaretEventHubTester, TestNoEventAsyncPanZoomScroll) +{ + MockFunction<void(::std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(check, Call("1")); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart()); + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), + OnScrollPositionChanged()).Times(0); + + EXPECT_CALL(check, Call("2")); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()); + } + + check.Call("1"); + + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->AsyncPanZoomStopped(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::PostScrollState()); + + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->AsyncPanZoomStopped(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::PostScrollState()); + + check.Call("2"); + + // Simulate scroll end fired by timer. + MockAccessibleCaretEventHub::FireScrollEnd(nullptr, mHub); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState()); +} + +TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollStartedThenBlur) +{ + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart()); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur()); + } + + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->NotifyBlur(true); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState()); +} + +TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollEndedThenBlur) +{ + { + InSequence dummy; + + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart()); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0); + EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur()); + } + + mHub->AsyncPanZoomStarted(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->ScrollPositionChanged(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::ScrollState()); + + mHub->AsyncPanZoomStopped(); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::PostScrollState()); + + mHub->NotifyBlur(true); + EXPECT_EQ(mHub->GetState(), MockAccessibleCaretEventHub::NoActionState()); +} + +} // namespace mozilla diff --git a/layout/base/gtest/TestAccessibleCaretManager.cpp b/layout/base/gtest/TestAccessibleCaretManager.cpp new file mode 100644 index 000000000..78ec6eea9 --- /dev/null +++ b/layout/base/gtest/TestAccessibleCaretManager.cpp @@ -0,0 +1,810 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include <string> + +#include "AccessibleCaret.h" +#include "AccessibleCaretManager.h" +#include "mozilla/AutoRestore.h" + +using ::testing::DefaultValue; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::MockFunction; +using ::testing::Return; +using ::testing::_; + +// ----------------------------------------------------------------------------- +// This file tests CaretStateChanged events and the appearance of the two +// AccessibleCarets manipulated by AccessibleCaretManager. + +namespace mozilla +{ +using dom::CaretChangedReason; + +class AccessibleCaretManagerTester : public ::testing::Test +{ +public: + class MockAccessibleCaret : public AccessibleCaret + { + public: + MockAccessibleCaret() : AccessibleCaret(nullptr) {} + + virtual void SetAppearance(Appearance aAppearance) override + { + // A simplified version without touching CaretElement(). + mAppearance = aAppearance; + } + + virtual void SetSelectionBarEnabled(bool aEnabled) override + { + // A simplified version without touching CaretElement(). + mSelectionBarEnabled = aEnabled; + } + + MOCK_METHOD2(SetPosition, + PositionChangedResult(nsIFrame* aFrame, int32_t aOffset)); + + }; // class MockAccessibleCaret + + class MockAccessibleCaretManager : public AccessibleCaretManager + { + public: + using CaretMode = AccessibleCaretManager::CaretMode; + using AccessibleCaretManager::UpdateCarets; + using AccessibleCaretManager::HideCarets; + using AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent; + using AccessibleCaretManager::sCaretsAlwaysTilt; + using AccessibleCaretManager::sCaretsAlwaysShowWhenScrolling; + + MockAccessibleCaretManager() + : AccessibleCaretManager(nullptr) + { + mFirstCaret = MakeUnique<MockAccessibleCaret>(); + mSecondCaret = MakeUnique<MockAccessibleCaret>(); + } + + MockAccessibleCaret& FirstCaret() + { + return static_cast<MockAccessibleCaret&>(*mFirstCaret); + } + + MockAccessibleCaret& SecondCaret() + { + return static_cast<MockAccessibleCaret&>(*mSecondCaret); + } + + virtual bool CompareTreePosition(nsIFrame* aStartFrame, + nsIFrame* aEndFrame) const override + { + return true; + } + + virtual bool IsCaretDisplayableInCursorMode( + nsIFrame** aOutFrame = nullptr, int32_t* aOutOffset = nullptr) const override + { + return true; + } + + virtual void UpdateCaretsForOverlappingTilt() override {} + + virtual void UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame, + nsIFrame* aEndFrame) + { + if (mFirstCaret->IsVisuallyVisible()) { + mFirstCaret->SetAppearance(Appearance::Left); + } + if (mSecondCaret->IsVisuallyVisible()) { + mSecondCaret->SetAppearance(Appearance::Right); + } + } + + virtual bool IsTerminated() const override { return false; } + + MOCK_CONST_METHOD0(GetCaretMode, CaretMode()); + MOCK_CONST_METHOD1(DispatchCaretStateChangedEvent, + void(CaretChangedReason aReason)); + MOCK_CONST_METHOD1(HasNonEmptyTextContent, bool(nsINode* aNode)); + + }; // class MockAccessibleCaretManager + + using Appearance = AccessibleCaret::Appearance; + using PositionChangedResult = AccessibleCaret::PositionChangedResult; + using CaretMode = MockAccessibleCaretManager::CaretMode; + + AccessibleCaretManagerTester() + { + DefaultValue<CaretMode>::Set(CaretMode::None); + DefaultValue<PositionChangedResult>::Set(PositionChangedResult::NotChanged); + + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillRepeatedly(Return(PositionChangedResult::Changed)); + + EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _)) + .WillRepeatedly(Return(PositionChangedResult::Changed)); + } + + AccessibleCaret::Appearance FirstCaretAppearance() + { + return mManager.FirstCaret().GetAppearance(); + } + + AccessibleCaret::Appearance SecondCaretAppearance() + { + return mManager.SecondCaret().GetAppearance(); + } + + // Member variables + MockAccessibleCaretManager mManager; + +}; // class AccessibleCaretManagerTester + +TEST_F(AccessibleCaretManagerTester, TestUpdatesInSelectionMode) +{ + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Selection)); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(3); + + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal); + + mManager.OnScrollPositionChanged(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal); +} + +TEST_F(AccessibleCaretManagerTester, TestSingleTapOnNonEmptyInput) +{ + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(true)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("update")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)).Times(1); + EXPECT_CALL(check, Call("mouse down")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(check, Call("reflow")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(check, Call("blur")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("mouse up")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("reflow2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + } + + // Simulate a single tap on a non-empty input. + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("update"); + + mManager.OnSelectionChanged(nullptr, nullptr, + nsISelectionListener::DRAG_REASON | + nsISelectionListener::MOUSEDOWN_REASON); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("mouse down"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("reflow"); + + mManager.OnBlur(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("blur"); + + mManager.OnSelectionChanged(nullptr, nullptr, + nsISelectionListener::MOUSEUP_REASON); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("mouse up"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("reflow2"); + + mManager.OnScrollPositionChanged(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); +} + +TEST_F(AccessibleCaretManagerTester, TestSingleTapOnEmptyInput) +{ + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(false)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("update")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)).Times(1); + EXPECT_CALL(check, Call("mouse down")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(check, Call("reflow")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + EXPECT_CALL(check, Call("blur")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("mouse up")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("reflow2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + } + + // Simulate a single tap on an empty input. + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("update"); + + mManager.OnSelectionChanged(nullptr, nullptr, + nsISelectionListener::DRAG_REASON | + nsISelectionListener::MOUSEDOWN_REASON); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("mouse down"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("reflow"); + + mManager.OnBlur(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("blur"); + + mManager.OnSelectionChanged(nullptr, nullptr, + nsISelectionListener::MOUSEUP_REASON); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("mouse up"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("reflow2"); + + mManager.OnScrollPositionChanged(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); +} + +TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput) +{ + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(true)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("update")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)).Times(1); + EXPECT_CALL(check, Call("keyboard")); + + // No CaretStateChanged events should be dispatched since the caret has + // being hidden in cursor mode. + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0); + } + + // Simulate typing the end of the input. + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("update"); + + mManager.OnKeyboardEvent(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("keyboard"); + + mManager.OnSelectionChanged(nullptr, nullptr, + nsISelectionListener::NO_REASON); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + + mManager.OnScrollPositionChanged(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); +} + +TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionMode) +{ + // Simulate B2G preference. + AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Selection)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + // Initially, first caret is out of scrollport, and second caret is visible. + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillOnce(Return(PositionChangedResult::Invisible)); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)); + EXPECT_CALL(check, Call("scrollstart1")); + + // After scroll ended, first caret is visible and second caret is out of + // scroll port. + EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _)) + .WillOnce(Return(PositionChangedResult::Invisible)); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend1")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)); + EXPECT_CALL(check, Call("scrollstart2")); + + // After the scroll ended, both carets are visible. + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend2")); + } + + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal); + check.Call("updatecarets"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + EXPECT_EQ(SecondCaretAppearance(), Appearance::None); + check.Call("scrollstart1"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + EXPECT_EQ(SecondCaretAppearance(), Appearance::None); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollend1"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + EXPECT_EQ(SecondCaretAppearance(), Appearance::None); + check.Call("scrollstart2"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + EXPECT_EQ(SecondCaretAppearance(), Appearance::None); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal); + check.Call("scrollend2"); +} + +TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionModeWithAlwaysTiltPref) +{ + // Simulate Firefox Android preference. + AutoRestore<bool> saveCaretsAlwaysTilt( + MockAccessibleCaretManager::sCaretsAlwaysTilt); + MockAccessibleCaretManager::sCaretsAlwaysTilt = true; + + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Selection)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + // Initially, first caret is out of scrollport, and second caret is visible. + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillOnce(Return(PositionChangedResult::Invisible)); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll)); + EXPECT_CALL(check, Call("scrollstart1")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("reflow1")); + + // After scroll ended, first caret is visible and second caret is out of + // scroll port. + EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _)) + .WillOnce(Return(PositionChangedResult::Invisible)); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend1")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll)); + EXPECT_CALL(check, Call("scrollstart2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("reflow2")); + + // After the scroll ended, both carets are visible. + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend2")); + } + + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); + check.Call("updatecarets"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); + check.Call("scrollstart1"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); + check.Call("reflow1"); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Left); + EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollend1"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Left); + EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollstart2"); + + mManager.OnReflow(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Left); + EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); + check.Call("reflow2"); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Left); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); + check.Call("scrollend2"); +} + +TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible) +{ + // Simulate B2G preference. + AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(true)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)).Times(1); + EXPECT_CALL(check, Call("scrollstart1")); + + // After scroll ended, the caret is out of scroll port. + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillRepeatedly(Return(PositionChangedResult::Invisible)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("scrollend1")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)).Times(1); + EXPECT_CALL(check, Call("scrollstart2")); + + // After scroll ended, the caret is visible again. + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillRepeatedly(Return(PositionChangedResult::Changed)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("scrollend2")); + } + + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("updatecarets"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("scrollstart1"); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollend1"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("scrollstart2"); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("scrollend2"); +} + +TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden) +{ + // Simulate B2G preference. + AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(true)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)).Times(1); + EXPECT_CALL(check, Call("updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)).Times(1); + EXPECT_CALL(check, Call("hidecarets")); + + // After scroll ended, the caret is out of scroll port. + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillRepeatedly(Return(PositionChangedResult::Invisible)); + EXPECT_CALL(check, Call("scrollend1")); + + // After scroll ended, the caret is visible again. + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillRepeatedly(Return(PositionChangedResult::Changed)); + EXPECT_CALL(check, Call("scrollend2")); + } + + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("updatecarets"); + + mManager.HideCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("hidecarets"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("scrollend1"); + + mManager.OnScrollStart(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("scrollend2"); +} + +TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeOnEmptyContent) +{ + // Simulate B2G preference. + AutoRestore<bool> savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(false)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)); + EXPECT_CALL(check, Call("scrollstart1")); + + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillOnce(Return(PositionChangedResult::Invisible)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend1")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)); + EXPECT_CALL(check, Call("scrollstart2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Visibilitychange)); + EXPECT_CALL(check, Call("scrollstart3")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("scrollend3")); + } + + // Simulate a single tap on an empty content. + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("updatecarets"); + + // Scroll the caret to be out of the viewport. + mManager.OnScrollStart(); + check.Call("scrollstart1"); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollend1"); + + // Scroll the caret into the viewport. + mManager.OnScrollStart(); + check.Call("scrollstart2"); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollend2"); + + // Scroll the caret within the viewport. + mManager.OnScrollStart(); + check.Call("scrollstart3"); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("scrollend3"); +} + +TEST_F(AccessibleCaretManagerTester, + TestScrollInCursorModeWithCaretShownWhenLongTappingOnEmptyContentPref) +{ + // Simulate Firefox Android preference. + AutoRestore<bool> savesCaretShownWhenLongTappingOnEmptyContent( + MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent); + MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = true; + + EXPECT_CALL(mManager, GetCaretMode()) + .WillRepeatedly(Return(CaretMode::Cursor)); + + EXPECT_CALL(mManager, HasNonEmptyTextContent(_)) + .WillRepeatedly(Return(false)); + + MockFunction<void(std::string aCheckPointName)> check; + { + InSequence dummy; + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("singletap updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("longtap updatecarets")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll)); + EXPECT_CALL(check, Call("longtap scrollstart1")); + + EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) + .WillOnce(Return(PositionChangedResult::Invisible)); + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("longtap scrollend1")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll)); + EXPECT_CALL(check, Call("longtap scrollstart2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("longtap scrollend2")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Scroll)); + EXPECT_CALL(check, Call("longtap scrollstart3")); + + EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( + CaretChangedReason::Updateposition)); + EXPECT_CALL(check, Call("longtap scrollend3")); + } + + // Simulate a single tap on an empty input. + mManager.FirstCaret().SetAppearance(Appearance::None); + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + check.Call("singletap updatecarets"); + + // Scroll the caret within the viewport. + mManager.OnScrollStart(); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::None); + + // Simulate a long tap on an empty input. + mManager.FirstCaret().SetAppearance(Appearance::Normal); + mManager.UpdateCarets(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("longtap updatecarets"); + + // Scroll the caret to be out of the viewport. + mManager.OnScrollStart(); + check.Call("longtap scrollstart1"); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + check.Call("longtap scrollend1"); + + // Scroll the caret into the viewport. + mManager.OnScrollStart(); + check.Call("longtap scrollstart2"); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("longtap scrollend2"); + + // Scroll the caret within the viewport. + mManager.OnScrollStart(); + check.Call("longtap scrollstart3"); + mManager.OnScrollEnd(); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal); + check.Call("longtap scrollend3"); +} + +} // namespace mozilla diff --git a/layout/base/gtest/moz.build b/layout/base/gtest/moz.build new file mode 100644 index 000000000..2e9100443 --- /dev/null +++ b/layout/base/gtest/moz.build @@ -0,0 +1,29 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'TestAccessibleCaretEventHub.cpp', + 'TestAccessibleCaretManager.cpp', +] + +# THE MOCK_METHOD2 macro from gtest triggers this clang warning and it's hard +# to work around, so we just ignore it. +if CONFIG['CLANG_CXX']: + CXXFLAGS += ['-Wno-inconsistent-missing-override'] + +include('/ipc/chromium/chromium-config.mozbuild') + +LOCAL_INCLUDES += [ + '/docshell/base', + '/layout/base', + '/layout/style', +] + +# Workaround bug 1142396. Suppress the warning from gmock library for clang. +if CONFIG['CLANG_CXX']: + CXXFLAGS += ['-Wno-null-dereference'] + +FINAL_LIBRARY = 'xul-gtest' |