summaryrefslogtreecommitdiffstats
path: root/layout/base/gtest
diff options
context:
space:
mode:
Diffstat (limited to 'layout/base/gtest')
-rw-r--r--layout/base/gtest/TestAccessibleCaretEventHub.cpp785
-rw-r--r--layout/base/gtest/TestAccessibleCaretManager.cpp848
-rw-r--r--layout/base/gtest/moz.build26
3 files changed, 1659 insertions, 0 deletions
diff --git a/layout/base/gtest/TestAccessibleCaretEventHub.cpp b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
new file mode 100644
index 0000000000..6a3c9b61ac
--- /dev/null
+++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp
@@ -0,0 +1,785 @@
+/* -*- 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 "AccessibleCaretManager.h"
+
+#include "mozilla/AccessibleCaretEventHub.h"
+#include "mozilla/BasicEvents.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/TouchEvents.h"
+
+using ::testing::_;
+using ::testing::AtLeast;
+using ::testing::DefaultValue;
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::MockFunction;
+using ::testing::Return;
+
+// -----------------------------------------------------------------------------
+// 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::DragCaretState;
+ using AccessibleCaretEventHub::LongTapState;
+ using AccessibleCaretEventHub::NoActionState;
+ using AccessibleCaretEventHub::PressCaretState;
+ using AccessibleCaretEventHub::PressNoCaretState;
+ using AccessibleCaretEventHub::ScrollState;
+
+ MockAccessibleCaretEventHub() : AccessibleCaretEventHub(nullptr) {
+ mManager = MakeUnique<MockAccessibleCaretManager>();
+ mInitialized = true;
+ }
+
+ 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);
+ }
+
+ 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.
+static ::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() override {
+ // 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->mButton = MouseButton::ePrimary;
+ event->mRefPoint = LayoutDeviceIntPoint(aX, aY);
+
+ return std::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 std::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 std::move(event);
+ }
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestAsyncPanZoomScroll();
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ void HandleEventAndCheckState(
+ UniquePtr<WidgetEvent> aEvent,
+ MockAccessibleCaretEventHub::State* aExpectedState,
+ nsEventStatus aExpectedEventStatus) {
+ RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+ nsEventStatus rv = hub->HandleEvent(aEvent.get());
+ EXPECT_EQ(hub->GetState(), aExpectedState);
+ EXPECT_EQ(rv, aExpectedEventStatus);
+ }
+
+ void CheckState(MockAccessibleCaretEventHub::State* aExpectedState) {
+ EXPECT_EQ(mHub->GetState(), aExpectedState);
+ }
+
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressReleaseOnNoCaret(
+ PressEventCreator aPressEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressReleaseOnCaret(
+ PressEventCreator aPressEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ template <typename PressEventCreator, typename MoveEventCreator,
+ typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressMoveReleaseOnNoCaret(
+ PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ template <typename PressEventCreator, typename MoveEventCreator,
+ typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestPressMoveReleaseOnCaret(
+ PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestLongTapWithSelectWordSuccessful(
+ PressEventCreator aPressEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ template <typename PressEventCreator, typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestLongTapWithSelectWordFailed(
+ PressEventCreator aPressEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ template <typename PressEventCreator, typename MoveEventCreator,
+ typename ReleaseEventCreator>
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void TestEventDrivenAsyncPanZoomScroll(
+ PressEventCreator aPressEventCreator, MoveEventCreator aMoveEventCreator,
+ ReleaseEventCreator aReleaseEventCreator);
+
+ // Member variables
+ RefPtr<MockAccessibleCaretEventHub> mHub{new MockAccessibleCaretEventHub()};
+
+}; // class AccessibleCaretEventHubTester
+
+TEST_F(AccessibleCaretEventHubTester, TestMousePressReleaseOnNoCaret)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestPressReleaseOnNoCaret(CreateMousePressEvent, CreateMouseReleaseEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnNoCaret)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestPressReleaseOnCaret(CreateMousePressEvent, CreateMouseReleaseEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestTouchPressReleaseOnCaret)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestPressMoveReleaseOnNoCaret(CreateMousePressEvent, CreateMouseMoveEvent,
+ CreateMouseReleaseEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnNoCaret)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestPressMoveReleaseOnCaret(CreateMousePressEvent, CreateMouseMoveEvent,
+ CreateMouseReleaseEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestTouchPressMoveReleaseOnCaret)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestLongTapWithSelectWordSuccessful(CreateMousePressEvent,
+ CreateMouseReleaseEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordSuccessful)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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);
+
+ RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->AsyncPanZoomStopped();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+
+ HandleEventAndCheckState(aReleaseEventCreator(1, 1),
+ MockAccessibleCaretEventHub::NoActionState(),
+ nsEventStatus_eIgnore);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestMouseLongTapWithSelectWordFailed)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestLongTapWithSelectWordFailed(CreateMousePressEvent,
+ CreateMouseReleaseEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestTouchLongTapWithSelectWordFailed)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ TestEventDrivenAsyncPanZoomScroll(CreateTouchStartEvent, CreateTouchMoveEvent,
+ CreateTouchEndEvent);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestMouseEventDrivenAsyncPanZoomScroll)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
+
+ EXPECT_CALL(check, Call("2"));
+ 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(*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
+ RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ HandleEventAndCheckState(aMoveEventCreator(160, 160),
+ MockAccessibleCaretEventHub::ScrollState(),
+ nsEventStatus_eIgnore);
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ // Event driven scroll ended
+ hub->AsyncPanZoomStopped();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+
+ HandleEventAndCheckState(aReleaseEventCreator(210, 210),
+ MockAccessibleCaretEventHub::NoActionState(),
+ 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
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ // Another APZ scroll ended
+ hub->AsyncPanZoomStopped();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+
+ HandleEventAndCheckState(aReleaseEventCreator(310, 310),
+ MockAccessibleCaretEventHub::NoActionState(),
+ nsEventStatus_eIgnore);
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScroll)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION { TestAsyncPanZoomScroll(); }
+
+void AccessibleCaretEventHubTester::TestAsyncPanZoomScroll() {
+ MockFunction<void(::std::string aCheckPointName)> check;
+ {
+ InSequence dummy;
+
+ EXPECT_CALL(check, Call("1"));
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(),
+ OnScrollPositionChanged());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
+
+ EXPECT_CALL(check, Call("2"));
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(),
+ OnScrollPositionChanged());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
+ }
+
+ // First APZ scrolling.
+ check.Call("1");
+
+ RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->AsyncPanZoomStopped();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+
+ // Second APZ scrolling.
+ check.Call("2");
+
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->AsyncPanZoomStopped();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollStartedThenBlur)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ {
+ InSequence dummy;
+
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd()).Times(0);
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
+ }
+
+ RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->NotifyBlur(true);
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+}
+
+TEST_F(AccessibleCaretEventHubTester, TestAsyncPanZoomScrollEndedThenBlur)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ {
+ InSequence dummy;
+
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollStart());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnScrollEnd());
+ EXPECT_CALL(*mHub->GetMockAccessibleCaretManager(), OnBlur());
+ }
+
+ RefPtr<MockAccessibleCaretEventHub> hub(mHub);
+ hub->AsyncPanZoomStarted();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->ScrollPositionChanged();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::ScrollState());
+
+ hub->AsyncPanZoomStopped();
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+
+ hub->NotifyBlur(true);
+ EXPECT_EQ(hub->GetState(), MockAccessibleCaretEventHub::NoActionState());
+}
+
+} // namespace mozilla
diff --git a/layout/base/gtest/TestAccessibleCaretManager.cpp b/layout/base/gtest/TestAccessibleCaretManager.cpp
new file mode 100644
index 0000000000..464ad68fb0
--- /dev/null
+++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
@@ -0,0 +1,848 @@
+/* -*- 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/Preferences.h"
+
+using ::testing::_;
+using ::testing::DefaultValue;
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::MockFunction;
+using ::testing::Return;
+
+// -----------------------------------------------------------------------------
+// This file tests CaretStateChanged events and the appearance of the two
+// AccessibleCarets manipulated by AccessibleCaretManager.
+
+namespace mozilla {
+using dom::CaretChangedReason;
+
+class MOZ_RAII AutoRestoreBoolPref final {
+ public:
+ AutoRestoreBoolPref(const char* aPref, bool aValue) : mPref(aPref) {
+ Preferences::GetBool(mPref, &mOldValue);
+ Preferences::SetBool(mPref, aValue);
+ }
+
+ ~AutoRestoreBoolPref() { Preferences::SetBool(mPref, mOldValue); }
+
+ private:
+ const char* mPref = nullptr;
+ bool mOldValue = false;
+};
+
+class AccessibleCaretManagerTester : public ::testing::Test {
+ public:
+ class MockAccessibleCaret : public AccessibleCaret {
+ public:
+ MockAccessibleCaret() : AccessibleCaret(nullptr) {}
+
+ void SetAppearance(Appearance aAppearance) override {
+ // A simplified version without touching CaretElement().
+ mAppearance = aAppearance;
+ }
+
+ MOCK_METHOD2(SetPosition,
+ PositionChangedResult(nsIFrame* aFrame, int32_t aOffset));
+
+ }; // class MockAccessibleCaret
+
+ class MockAccessibleCaretManager : public AccessibleCaretManager {
+ public:
+ using CaretMode = AccessibleCaretManager::CaretMode;
+ using AccessibleCaretManager::HideCaretsAndDispatchCaretStateChangedEvent;
+ using AccessibleCaretManager::UpdateCarets;
+
+ MockAccessibleCaretManager()
+ : AccessibleCaretManager(nullptr,
+ Carets{MakeUnique<MockAccessibleCaret>(),
+ MakeUnique<MockAccessibleCaret>()}) {}
+
+ MockAccessibleCaret& FirstCaret() {
+ return static_cast<MockAccessibleCaret&>(*mCarets.GetFirst());
+ }
+
+ MockAccessibleCaret& SecondCaret() {
+ return static_cast<MockAccessibleCaret&>(*mCarets.GetSecond());
+ }
+
+ bool CompareTreePosition(nsIFrame* aStartFrame,
+ nsIFrame* aEndFrame) const override {
+ return true;
+ }
+
+ bool IsCaretDisplayableInCursorMode(
+ nsIFrame** aOutFrame = nullptr,
+ int32_t* aOutOffset = nullptr) const override {
+ return true;
+ }
+
+ bool UpdateCaretsForOverlappingTilt() override { return true; }
+
+ void UpdateCaretsForAlwaysTilt(const nsIFrame* aStartFrame,
+ const nsIFrame* aEndFrame) override {
+ if (mCarets.GetFirst()->IsVisuallyVisible()) {
+ mCarets.GetFirst()->SetAppearance(Appearance::Left);
+ }
+ if (mCarets.GetSecond()->IsVisuallyVisible()) {
+ mCarets.GetSecond()->SetAppearance(Appearance::Right);
+ }
+ }
+
+ Terminated IsTerminated() const override { return Terminated::No; }
+ bool IsScrollStarted() const { return mIsScrollStarted; }
+
+ Terminated MaybeFlushLayout() override { return Terminated::No; }
+
+ MOCK_CONST_METHOD0(GetCaretMode, CaretMode());
+ MOCK_METHOD2(DispatchCaretStateChangedEvent,
+ void(CaretChangedReason aReason, const nsPoint* aPoint));
+ 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::Position));
+
+ EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
+ .WillRepeatedly(Return(PositionChangedResult::Position));
+ }
+
+ AccessibleCaret::Appearance FirstCaretAppearance() {
+ return mManager.FirstCaret().GetAppearance();
+ }
+
+ AccessibleCaret::Appearance SecondCaretAppearance() {
+ return mManager.SecondCaret().GetAppearance();
+ }
+
+ // Member variables
+ MockAccessibleCaretManager mManager;
+
+}; // class AccessibleCaretManagerTester
+
+TEST_F(AccessibleCaretManagerTester, TestUpdatesInSelectionMode)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ // Set default preference.
+ AutoRestoreBoolPref savedPref("layout.accessiblecaret.always_tilt", false);
+
+ EXPECT_CALL(mManager, GetCaretMode())
+ .WillRepeatedly(Return(CaretMode::Selection));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("update"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Visibilitychange, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("mouse down"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0);
+ EXPECT_CALL(check, Call("reflow"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0);
+ EXPECT_CALL(check, Call("blur"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("mouse up"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("reflow2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ // Set default preference.
+ AutoRestoreBoolPref savedPref(
+ "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content",
+ 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, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("update"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Visibilitychange, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("mouse down"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0);
+ EXPECT_CALL(check, Call("reflow"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0);
+ EXPECT_CALL(check, Call("blur"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("mouse up"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("reflow2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("update"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Visibilitychange, nullptr))
+ .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(_, nullptr)).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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ // Set default preference.
+ AutoRestoreBoolPref savedPref("layout.accessiblecaret.always_tilt", 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, nullptr));
+ EXPECT_CALL(check, Call("updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ 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, nullptr));
+ EXPECT_CALL(check, Call("scrollend1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("reflow2"));
+
+ // After the scroll ended, both carets are visible.
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ 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::NormalNotShown);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+ check.Call("scrollstart1");
+
+ mManager.OnReflow();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+ check.Call("reflow1");
+
+ mManager.OnScrollEnd();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
+ check.Call("scrollend1");
+
+ mManager.OnScrollStart();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
+ check.Call("scrollstart2");
+
+ mManager.OnReflow();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
+ check.Call("reflow2");
+
+ mManager.OnScrollEnd();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+ check.Call("scrollend2");
+}
+
+TEST_F(AccessibleCaretManagerTester,
+ TestScrollInSelectionModeWithAlwaysTiltPref)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ // Simulate Firefox Android preference.
+ AutoRestoreBoolPref savedPref("layout.accessiblecaret.always_tilt", 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, nullptr));
+ EXPECT_CALL(check, Call("updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0);
+ EXPECT_CALL(check, Call("scrollPositionChanged1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ 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, nullptr));
+ EXPECT_CALL(check, Call("scrollend1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_, nullptr)).Times(0);
+ EXPECT_CALL(check, Call("scrollPositionChanged2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("reflow2"));
+
+ // After the scroll ended, both carets are visible.
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ 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.OnScrollPositionChanged();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
+ check.Call("scrollPositionChanged1");
+
+ 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.OnScrollPositionChanged();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
+ EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
+ check.Call("scrollPositionChanged2");
+
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr))
+ .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, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("scrollend1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("scrollstart2"));
+
+ // After scroll ended, the caret is visible again.
+ EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+ .WillRepeatedly(Return(PositionChangedResult::Position));
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("scrollend2"));
+ }
+
+ mManager.UpdateCarets();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ check.Call("updatecarets");
+
+ mManager.OnScrollStart();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ check.Call("scrollstart1");
+
+ mManager.OnScrollEnd();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+ check.Call("scrollend1");
+
+ mManager.OnScrollStart();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
+ check.Call("scrollstart2");
+
+ mManager.OnScrollEnd();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ check.Call("scrollend2");
+}
+
+TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ 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, nullptr))
+ .Times(1);
+ EXPECT_CALL(check, Call("updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Visibilitychange, nullptr))
+ .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::Position));
+ EXPECT_CALL(check, Call("scrollend2"));
+ }
+
+ mManager.UpdateCarets();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ check.Call("updatecarets");
+
+ mManager.HideCaretsAndDispatchCaretStateChangedEvent();
+ 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ // Set default preference.
+ AutoRestoreBoolPref savedPref(
+ "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content",
+ 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, nullptr));
+ EXPECT_CALL(check, Call("updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart1"));
+
+ EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+ .WillOnce(Return(PositionChangedResult::Invisible));
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("scrollend1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("scrollend2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("scrollstart3"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("scrollend3"));
+ }
+
+ // Simulate a pinch-zoom operation before tapping on an empty content.
+ mManager.OnScrollStart();
+ mManager.OnScrollEnd();
+ EXPECT_EQ(mManager.IsScrollStarted(), false);
+
+ // 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)
+MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+ // Simulate Firefox Android preference.
+ AutoRestoreBoolPref savedPref(
+ "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content",
+ 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, nullptr));
+ EXPECT_CALL(check, Call("singletap updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("longtap updatecarets"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("longtap scrollstart1"));
+
+ EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+ .WillOnce(Return(PositionChangedResult::Invisible));
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("longtap scrollend1"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("longtap scrollstart2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ EXPECT_CALL(check, Call("longtap scrollend2"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Scroll, nullptr));
+ EXPECT_CALL(check, Call("longtap scrollstart3"));
+
+ EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
+ CaretChangedReason::Updateposition, nullptr));
+ 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.OnScrollPositionChanged();
+ mManager.OnScrollEnd();
+ EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+ check.Call("longtap scrollend2");
+
+ // Scroll the caret within the viewport.
+ mManager.OnScrollStart();
+ check.Call("longtap scrollstart3");
+ mManager.OnScrollPositionChanged();
+ 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 0000000000..b082855295
--- /dev/null
+++ b/layout/base/gtest/moz.build
@@ -0,0 +1,26 @@
+# -*- 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["CC_TYPE"] == "clang":
+ CXXFLAGS += ["-Wno-inconsistent-missing-override"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ "/docshell/base",
+ "/layout/base",
+ "/layout/style",
+]
+
+FINAL_LIBRARY = "xul-gtest"