/* -*- 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 #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(), MakeUnique()}) {} MockAccessibleCaret& FirstCaret() { return static_cast(*mCarets.GetFirst()); } MockAccessibleCaret& SecondCaret() { return static_cast(*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::Set(CaretMode::None); DefaultValue::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 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 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 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 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 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 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 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 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 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