1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "gtest/gtest.h"
8 #include "gmock/gmock.h"
9 
10 #include <string>
11 
12 #include "AccessibleCaret.h"
13 #include "AccessibleCaretManager.h"
14 #include "mozilla/Preferences.h"
15 
16 using ::testing::_;
17 using ::testing::DefaultValue;
18 using ::testing::Eq;
19 using ::testing::InSequence;
20 using ::testing::MockFunction;
21 using ::testing::Return;
22 
23 // -----------------------------------------------------------------------------
24 // This file tests CaretStateChanged events and the appearance of the two
25 // AccessibleCarets manipulated by AccessibleCaretManager.
26 
27 namespace mozilla {
28 using dom::CaretChangedReason;
29 
30 class MOZ_RAII AutoRestoreBoolPref final {
31  public:
AutoRestoreBoolPref(const char * aPref,bool aValue)32   AutoRestoreBoolPref(const char* aPref, bool aValue) : mPref(aPref) {
33     Preferences::GetBool(mPref, &mOldValue);
34     Preferences::SetBool(mPref, aValue);
35   }
36 
~AutoRestoreBoolPref()37   ~AutoRestoreBoolPref() { Preferences::SetBool(mPref, mOldValue); }
38 
39  private:
40   const char* mPref = nullptr;
41   bool mOldValue = false;
42 };
43 
44 class AccessibleCaretManagerTester : public ::testing::Test {
45  public:
46   class MockAccessibleCaret : public AccessibleCaret {
47    public:
MockAccessibleCaret()48     MockAccessibleCaret() : AccessibleCaret(nullptr) {}
49 
SetAppearance(Appearance aAppearance)50     void SetAppearance(Appearance aAppearance) override {
51       // A simplified version without touching CaretElement().
52       mAppearance = aAppearance;
53     }
54 
55     MOCK_METHOD2(SetPosition,
56                  PositionChangedResult(nsIFrame* aFrame, int32_t aOffset));
57 
58   };  // class MockAccessibleCaret
59 
60   class MockAccessibleCaretManager : public AccessibleCaretManager {
61    public:
62     using CaretMode = AccessibleCaretManager::CaretMode;
63     using AccessibleCaretManager::HideCarets;
64     using AccessibleCaretManager::UpdateCarets;
65 
MockAccessibleCaretManager()66     MockAccessibleCaretManager() : AccessibleCaretManager(nullptr) {
67       mFirstCaret = MakeUnique<MockAccessibleCaret>();
68       mSecondCaret = MakeUnique<MockAccessibleCaret>();
69     }
70 
FirstCaret()71     MockAccessibleCaret& FirstCaret() {
72       return static_cast<MockAccessibleCaret&>(*mFirstCaret);
73     }
74 
SecondCaret()75     MockAccessibleCaret& SecondCaret() {
76       return static_cast<MockAccessibleCaret&>(*mSecondCaret);
77     }
78 
CompareTreePosition(nsIFrame * aStartFrame,nsIFrame * aEndFrame) const79     bool CompareTreePosition(nsIFrame* aStartFrame,
80                              nsIFrame* aEndFrame) const override {
81       return true;
82     }
83 
IsCaretDisplayableInCursorMode(nsIFrame ** aOutFrame=nullptr,int32_t * aOutOffset=nullptr) const84     bool IsCaretDisplayableInCursorMode(
85         nsIFrame** aOutFrame = nullptr,
86         int32_t* aOutOffset = nullptr) const override {
87       return true;
88     }
89 
UpdateCaretsForOverlappingTilt()90     bool UpdateCaretsForOverlappingTilt() override { return true; }
91 
UpdateCaretsForAlwaysTilt(nsIFrame * aStartFrame,nsIFrame * aEndFrame)92     void UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
93                                    nsIFrame* aEndFrame) override {
94       if (mFirstCaret->IsVisuallyVisible()) {
95         mFirstCaret->SetAppearance(Appearance::Left);
96       }
97       if (mSecondCaret->IsVisuallyVisible()) {
98         mSecondCaret->SetAppearance(Appearance::Right);
99       }
100     }
101 
IsTerminated() const102     bool IsTerminated() const override { return false; }
103 
104     MOCK_CONST_METHOD0(GetCaretMode, CaretMode());
105     MOCK_METHOD1(DispatchCaretStateChangedEvent,
106                  void(CaretChangedReason aReason));
107     MOCK_CONST_METHOD1(HasNonEmptyTextContent, bool(nsINode* aNode));
108 
109   };  // class MockAccessibleCaretManager
110 
111   using Appearance = AccessibleCaret::Appearance;
112   using PositionChangedResult = AccessibleCaret::PositionChangedResult;
113   using CaretMode = MockAccessibleCaretManager::CaretMode;
114 
AccessibleCaretManagerTester()115   AccessibleCaretManagerTester() {
116     DefaultValue<CaretMode>::Set(CaretMode::None);
117     DefaultValue<PositionChangedResult>::Set(PositionChangedResult::NotChanged);
118 
119     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
120         .WillRepeatedly(Return(PositionChangedResult::Position));
121 
122     EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
123         .WillRepeatedly(Return(PositionChangedResult::Position));
124   }
125 
FirstCaretAppearance()126   AccessibleCaret::Appearance FirstCaretAppearance() {
127     return mManager.FirstCaret().GetAppearance();
128   }
129 
SecondCaretAppearance()130   AccessibleCaret::Appearance SecondCaretAppearance() {
131     return mManager.SecondCaret().GetAppearance();
132   }
133 
134   // Member variables
135   MockAccessibleCaretManager mManager;
136 
137 };  // class AccessibleCaretManagerTester
138 
TEST_F(AccessibleCaretManagerTester,TestUpdatesInSelectionMode)139 TEST_F(AccessibleCaretManagerTester, TestUpdatesInSelectionMode)
140 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
141   // Set default preference.
142   AutoRestoreBoolPref savedPref("layout.accessiblecaret.always_tilt", false);
143 
144   EXPECT_CALL(mManager, GetCaretMode())
145       .WillRepeatedly(Return(CaretMode::Selection));
146 
147   EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
148                             CaretChangedReason::Updateposition))
149       .Times(3);
150 
151   mManager.UpdateCarets();
152   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
153   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
154 
155   mManager.OnReflow();
156   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
157   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
158 
159   mManager.OnScrollPositionChanged();
160   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
161   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
162 }
163 
TEST_F(AccessibleCaretManagerTester,TestSingleTapOnNonEmptyInput)164 TEST_F(AccessibleCaretManagerTester, TestSingleTapOnNonEmptyInput)
165 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
166   EXPECT_CALL(mManager, GetCaretMode())
167       .WillRepeatedly(Return(CaretMode::Cursor));
168 
169   EXPECT_CALL(mManager, HasNonEmptyTextContent(_)).WillRepeatedly(Return(true));
170 
171   MockFunction<void(std::string aCheckPointName)> check;
172   {
173     InSequence dummy;
174 
175     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
176                               CaretChangedReason::Updateposition))
177         .Times(1);
178     EXPECT_CALL(check, Call("update"));
179 
180     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
181                               CaretChangedReason::Visibilitychange))
182         .Times(1);
183     EXPECT_CALL(check, Call("mouse down"));
184 
185     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
186     EXPECT_CALL(check, Call("reflow"));
187 
188     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
189     EXPECT_CALL(check, Call("blur"));
190 
191     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
192                               CaretChangedReason::Updateposition))
193         .Times(1);
194     EXPECT_CALL(check, Call("mouse up"));
195 
196     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
197                               CaretChangedReason::Updateposition))
198         .Times(1);
199     EXPECT_CALL(check, Call("reflow2"));
200 
201     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
202                               CaretChangedReason::Updateposition))
203         .Times(1);
204   }
205 
206   // Simulate a single tap on a non-empty input.
207   mManager.UpdateCarets();
208   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
209   check.Call("update");
210 
211   mManager.OnSelectionChanged(nullptr, nullptr,
212                               nsISelectionListener::DRAG_REASON |
213                                   nsISelectionListener::MOUSEDOWN_REASON);
214   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
215   check.Call("mouse down");
216 
217   mManager.OnReflow();
218   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
219   check.Call("reflow");
220 
221   mManager.OnBlur();
222   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
223   check.Call("blur");
224 
225   mManager.OnSelectionChanged(nullptr, nullptr,
226                               nsISelectionListener::MOUSEUP_REASON);
227   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
228   check.Call("mouse up");
229 
230   mManager.OnReflow();
231   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
232   check.Call("reflow2");
233 
234   mManager.OnScrollPositionChanged();
235   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
236 }
237 
TEST_F(AccessibleCaretManagerTester,TestSingleTapOnEmptyInput)238 TEST_F(AccessibleCaretManagerTester, TestSingleTapOnEmptyInput)
239 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
240   // Set default preference.
241   AutoRestoreBoolPref savedPref(
242       "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content",
243       false);
244 
245   EXPECT_CALL(mManager, GetCaretMode())
246       .WillRepeatedly(Return(CaretMode::Cursor));
247 
248   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
249       .WillRepeatedly(Return(false));
250 
251   MockFunction<void(std::string aCheckPointName)> check;
252   {
253     InSequence dummy;
254 
255     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
256                               CaretChangedReason::Updateposition))
257         .Times(1);
258     EXPECT_CALL(check, Call("update"));
259 
260     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
261                               CaretChangedReason::Visibilitychange))
262         .Times(1);
263     EXPECT_CALL(check, Call("mouse down"));
264 
265     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
266     EXPECT_CALL(check, Call("reflow"));
267 
268     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
269     EXPECT_CALL(check, Call("blur"));
270 
271     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
272                               CaretChangedReason::Updateposition))
273         .Times(1);
274     EXPECT_CALL(check, Call("mouse up"));
275 
276     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
277                               CaretChangedReason::Updateposition))
278         .Times(1);
279     EXPECT_CALL(check, Call("reflow2"));
280 
281     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
282                               CaretChangedReason::Updateposition))
283         .Times(1);
284   }
285 
286   // Simulate a single tap on an empty input.
287   mManager.UpdateCarets();
288   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
289   check.Call("update");
290 
291   mManager.OnSelectionChanged(nullptr, nullptr,
292                               nsISelectionListener::DRAG_REASON |
293                                   nsISelectionListener::MOUSEDOWN_REASON);
294   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
295   check.Call("mouse down");
296 
297   mManager.OnReflow();
298   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
299   check.Call("reflow");
300 
301   mManager.OnBlur();
302   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
303   check.Call("blur");
304 
305   mManager.OnSelectionChanged(nullptr, nullptr,
306                               nsISelectionListener::MOUSEUP_REASON);
307   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
308   check.Call("mouse up");
309 
310   mManager.OnReflow();
311   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
312   check.Call("reflow2");
313 
314   mManager.OnScrollPositionChanged();
315   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
316 }
317 
TEST_F(AccessibleCaretManagerTester,TestTypingAtEndOfInput)318 TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput)
319 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
320   EXPECT_CALL(mManager, GetCaretMode())
321       .WillRepeatedly(Return(CaretMode::Cursor));
322 
323   EXPECT_CALL(mManager, HasNonEmptyTextContent(_)).WillRepeatedly(Return(true));
324 
325   MockFunction<void(std::string aCheckPointName)> check;
326   {
327     InSequence dummy;
328 
329     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
330                               CaretChangedReason::Updateposition))
331         .Times(1);
332     EXPECT_CALL(check, Call("update"));
333 
334     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
335                               CaretChangedReason::Visibilitychange))
336         .Times(1);
337     EXPECT_CALL(check, Call("keyboard"));
338 
339     // No CaretStateChanged events should be dispatched since the caret has
340     // being hidden in cursor mode.
341     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
342   }
343 
344   // Simulate typing the end of the input.
345   mManager.UpdateCarets();
346   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
347   check.Call("update");
348 
349   mManager.OnKeyboardEvent();
350   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
351   check.Call("keyboard");
352 
353   mManager.OnSelectionChanged(nullptr, nullptr,
354                               nsISelectionListener::NO_REASON);
355   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
356 
357   mManager.OnScrollPositionChanged();
358   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
359 }
360 
TEST_F(AccessibleCaretManagerTester,TestScrollInSelectionMode)361 TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionMode)
362 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
363   // Set default preference.
364   AutoRestoreBoolPref savedPref("layout.accessiblecaret.always_tilt", false);
365 
366   EXPECT_CALL(mManager, GetCaretMode())
367       .WillRepeatedly(Return(CaretMode::Selection));
368 
369   MockFunction<void(std::string aCheckPointName)> check;
370   {
371     InSequence dummy;
372 
373     // Initially, first caret is out of scrollport, and second caret is visible.
374     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
375         .WillOnce(Return(PositionChangedResult::Invisible));
376 
377     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
378                               CaretChangedReason::Updateposition));
379     EXPECT_CALL(check, Call("updatecarets"));
380 
381     EXPECT_CALL(mManager,
382                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
383     EXPECT_CALL(check, Call("scrollstart1"));
384 
385     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
386                               CaretChangedReason::Updateposition));
387     EXPECT_CALL(check, Call("reflow1"));
388 
389     // After scroll ended, first caret is visible and second caret is out of
390     // scroll port.
391     EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
392         .WillOnce(Return(PositionChangedResult::Invisible));
393 
394     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
395                               CaretChangedReason::Updateposition));
396     EXPECT_CALL(check, Call("scrollend1"));
397 
398     EXPECT_CALL(mManager,
399                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
400     EXPECT_CALL(check, Call("scrollstart2"));
401 
402     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
403                               CaretChangedReason::Updateposition));
404     EXPECT_CALL(check, Call("reflow2"));
405 
406     // After the scroll ended, both carets are visible.
407     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
408                               CaretChangedReason::Updateposition));
409     EXPECT_CALL(check, Call("scrollend2"));
410   }
411 
412   mManager.UpdateCarets();
413   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
414   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
415   check.Call("updatecarets");
416 
417   mManager.OnScrollStart();
418   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
419   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
420   check.Call("scrollstart1");
421 
422   mManager.OnReflow();
423   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
424   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
425   check.Call("reflow1");
426 
427   mManager.OnScrollEnd();
428   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
429   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
430   check.Call("scrollend1");
431 
432   mManager.OnScrollStart();
433   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
434   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
435   check.Call("scrollstart2");
436 
437   mManager.OnReflow();
438   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
439   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
440   check.Call("reflow2");
441 
442   mManager.OnScrollEnd();
443   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
444   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
445   check.Call("scrollend2");
446 }
447 
TEST_F(AccessibleCaretManagerTester,TestScrollInSelectionModeWithAlwaysTiltPref)448 TEST_F(AccessibleCaretManagerTester,
449        TestScrollInSelectionModeWithAlwaysTiltPref)
450 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
451   // Simulate Firefox Android preference.
452   AutoRestoreBoolPref savedPref("layout.accessiblecaret.always_tilt", true);
453 
454   EXPECT_CALL(mManager, GetCaretMode())
455       .WillRepeatedly(Return(CaretMode::Selection));
456 
457   MockFunction<void(std::string aCheckPointName)> check;
458   {
459     InSequence dummy;
460 
461     // Initially, first caret is out of scrollport, and second caret is visible.
462     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
463         .WillOnce(Return(PositionChangedResult::Invisible));
464 
465     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
466                               CaretChangedReason::Updateposition));
467     EXPECT_CALL(check, Call("updatecarets"));
468 
469     EXPECT_CALL(mManager,
470                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
471     EXPECT_CALL(check, Call("scrollstart1"));
472 
473     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
474     EXPECT_CALL(check, Call("scrollPositionChanged1"));
475 
476     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
477                               CaretChangedReason::Updateposition));
478     EXPECT_CALL(check, Call("reflow1"));
479 
480     // After scroll ended, first caret is visible and second caret is out of
481     // scroll port.
482     EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
483         .WillOnce(Return(PositionChangedResult::Invisible));
484 
485     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
486                               CaretChangedReason::Updateposition));
487     EXPECT_CALL(check, Call("scrollend1"));
488 
489     EXPECT_CALL(mManager,
490                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
491     EXPECT_CALL(check, Call("scrollstart2"));
492 
493     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(_)).Times(0);
494     EXPECT_CALL(check, Call("scrollPositionChanged2"));
495 
496     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
497                               CaretChangedReason::Updateposition));
498     EXPECT_CALL(check, Call("reflow2"));
499 
500     // After the scroll ended, both carets are visible.
501     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
502                               CaretChangedReason::Updateposition));
503     EXPECT_CALL(check, Call("scrollend2"));
504   }
505 
506   mManager.UpdateCarets();
507   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
508   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
509   check.Call("updatecarets");
510 
511   mManager.OnScrollStart();
512   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
513   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
514   check.Call("scrollstart1");
515 
516   mManager.OnScrollPositionChanged();
517   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
518   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
519   check.Call("scrollPositionChanged1");
520 
521   mManager.OnReflow();
522   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
523   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
524   check.Call("reflow1");
525 
526   mManager.OnScrollEnd();
527   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
528   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
529   check.Call("scrollend1");
530 
531   mManager.OnScrollStart();
532   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
533   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
534   check.Call("scrollstart2");
535 
536   mManager.OnScrollPositionChanged();
537   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
538   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
539   check.Call("scrollPositionChanged2");
540 
541   mManager.OnReflow();
542   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
543   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
544   check.Call("reflow2");
545 
546   mManager.OnScrollEnd();
547   EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
548   EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
549   check.Call("scrollend2");
550 }
551 
TEST_F(AccessibleCaretManagerTester,TestScrollInCursorModeWhenLogicallyVisible)552 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible)
553 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
554   EXPECT_CALL(mManager, GetCaretMode())
555       .WillRepeatedly(Return(CaretMode::Cursor));
556 
557   EXPECT_CALL(mManager, HasNonEmptyTextContent(_)).WillRepeatedly(Return(true));
558 
559   MockFunction<void(std::string aCheckPointName)> check;
560   {
561     InSequence dummy;
562 
563     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
564                               CaretChangedReason::Updateposition))
565         .Times(1);
566     EXPECT_CALL(check, Call("updatecarets"));
567 
568     EXPECT_CALL(mManager,
569                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll))
570         .Times(1);
571     EXPECT_CALL(check, Call("scrollstart1"));
572 
573     // After scroll ended, the caret is out of scroll port.
574     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
575         .WillRepeatedly(Return(PositionChangedResult::Invisible));
576     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
577                               CaretChangedReason::Updateposition))
578         .Times(1);
579     EXPECT_CALL(check, Call("scrollend1"));
580 
581     EXPECT_CALL(mManager,
582                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll))
583         .Times(1);
584     EXPECT_CALL(check, Call("scrollstart2"));
585 
586     // After scroll ended, the caret is visible again.
587     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
588         .WillRepeatedly(Return(PositionChangedResult::Position));
589     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
590                               CaretChangedReason::Updateposition))
591         .Times(1);
592     EXPECT_CALL(check, Call("scrollend2"));
593   }
594 
595   mManager.UpdateCarets();
596   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
597   check.Call("updatecarets");
598 
599   mManager.OnScrollStart();
600   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
601   check.Call("scrollstart1");
602 
603   mManager.OnScrollEnd();
604   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
605   check.Call("scrollend1");
606 
607   mManager.OnScrollStart();
608   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
609   check.Call("scrollstart2");
610 
611   mManager.OnScrollEnd();
612   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
613   check.Call("scrollend2");
614 }
615 
TEST_F(AccessibleCaretManagerTester,TestScrollInCursorModeWhenHidden)616 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden)
617 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
618   EXPECT_CALL(mManager, GetCaretMode())
619       .WillRepeatedly(Return(CaretMode::Cursor));
620 
621   EXPECT_CALL(mManager, HasNonEmptyTextContent(_)).WillRepeatedly(Return(true));
622 
623   MockFunction<void(std::string aCheckPointName)> check;
624   {
625     InSequence dummy;
626 
627     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
628                               CaretChangedReason::Updateposition))
629         .Times(1);
630     EXPECT_CALL(check, Call("updatecarets"));
631 
632     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
633                               CaretChangedReason::Visibilitychange))
634         .Times(1);
635     EXPECT_CALL(check, Call("hidecarets"));
636 
637     // After scroll ended, the caret is out of scroll port.
638     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
639         .WillRepeatedly(Return(PositionChangedResult::Invisible));
640     EXPECT_CALL(check, Call("scrollend1"));
641 
642     // After scroll ended, the caret is visible again.
643     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
644         .WillRepeatedly(Return(PositionChangedResult::Position));
645     EXPECT_CALL(check, Call("scrollend2"));
646   }
647 
648   mManager.UpdateCarets();
649   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
650   check.Call("updatecarets");
651 
652   mManager.HideCarets();
653   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
654   check.Call("hidecarets");
655 
656   mManager.OnScrollStart();
657   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
658 
659   mManager.OnScrollEnd();
660   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
661   check.Call("scrollend1");
662 
663   mManager.OnScrollStart();
664   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
665 
666   mManager.OnScrollEnd();
667   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
668   check.Call("scrollend2");
669 }
670 
TEST_F(AccessibleCaretManagerTester,TestScrollInCursorModeOnEmptyContent)671 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeOnEmptyContent)
672 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
673   // Set default preference.
674   AutoRestoreBoolPref savedPref(
675       "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content",
676       false);
677 
678   EXPECT_CALL(mManager, GetCaretMode())
679       .WillRepeatedly(Return(CaretMode::Cursor));
680 
681   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
682       .WillRepeatedly(Return(false));
683 
684   MockFunction<void(std::string aCheckPointName)> check;
685   {
686     InSequence dummy;
687 
688     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
689                               CaretChangedReason::Updateposition));
690     EXPECT_CALL(check, Call("updatecarets"));
691 
692     EXPECT_CALL(mManager,
693                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
694     EXPECT_CALL(check, Call("scrollstart1"));
695 
696     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
697         .WillOnce(Return(PositionChangedResult::Invisible));
698     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
699                               CaretChangedReason::Updateposition));
700     EXPECT_CALL(check, Call("scrollend1"));
701 
702     EXPECT_CALL(mManager,
703                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
704     EXPECT_CALL(check, Call("scrollstart2"));
705 
706     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
707                               CaretChangedReason::Updateposition));
708     EXPECT_CALL(check, Call("scrollend2"));
709 
710     EXPECT_CALL(mManager,
711                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
712     EXPECT_CALL(check, Call("scrollstart3"));
713 
714     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
715                               CaretChangedReason::Updateposition));
716     EXPECT_CALL(check, Call("scrollend3"));
717   }
718 
719   // Simulate a single tap on an empty content.
720   mManager.UpdateCarets();
721   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
722   check.Call("updatecarets");
723 
724   // Scroll the caret to be out of the viewport.
725   mManager.OnScrollStart();
726   check.Call("scrollstart1");
727   mManager.OnScrollEnd();
728   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
729   check.Call("scrollend1");
730 
731   // Scroll the caret into the viewport.
732   mManager.OnScrollStart();
733   check.Call("scrollstart2");
734   mManager.OnScrollEnd();
735   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
736   check.Call("scrollend2");
737 
738   // Scroll the caret within the viewport.
739   mManager.OnScrollStart();
740   check.Call("scrollstart3");
741   mManager.OnScrollEnd();
742   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
743   check.Call("scrollend3");
744 }
745 
TEST_F(AccessibleCaretManagerTester,TestScrollInCursorModeWithCaretShownWhenLongTappingOnEmptyContentPref)746 TEST_F(AccessibleCaretManagerTester,
747        TestScrollInCursorModeWithCaretShownWhenLongTappingOnEmptyContentPref)
748 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
749   // Simulate Firefox Android preference.
750   AutoRestoreBoolPref savedPref(
751       "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content",
752       true);
753 
754   EXPECT_CALL(mManager, GetCaretMode())
755       .WillRepeatedly(Return(CaretMode::Cursor));
756 
757   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
758       .WillRepeatedly(Return(false));
759 
760   MockFunction<void(std::string aCheckPointName)> check;
761   {
762     InSequence dummy;
763 
764     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
765                               CaretChangedReason::Updateposition));
766     EXPECT_CALL(check, Call("singletap updatecarets"));
767 
768     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
769                               CaretChangedReason::Updateposition));
770     EXPECT_CALL(check, Call("longtap updatecarets"));
771 
772     EXPECT_CALL(mManager,
773                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
774     EXPECT_CALL(check, Call("longtap scrollstart1"));
775 
776     EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
777         .WillOnce(Return(PositionChangedResult::Invisible));
778     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
779                               CaretChangedReason::Updateposition));
780     EXPECT_CALL(check, Call("longtap scrollend1"));
781 
782     EXPECT_CALL(mManager,
783                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
784     EXPECT_CALL(check, Call("longtap scrollstart2"));
785 
786     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
787                               CaretChangedReason::Updateposition));
788     EXPECT_CALL(check, Call("longtap scrollend2"));
789 
790     EXPECT_CALL(mManager,
791                 DispatchCaretStateChangedEvent(CaretChangedReason::Scroll));
792     EXPECT_CALL(check, Call("longtap scrollstart3"));
793 
794     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
795                               CaretChangedReason::Updateposition));
796     EXPECT_CALL(check, Call("longtap scrollend3"));
797   }
798 
799   // Simulate a single tap on an empty input.
800   mManager.FirstCaret().SetAppearance(Appearance::None);
801   mManager.UpdateCarets();
802   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
803   check.Call("singletap updatecarets");
804 
805   // Scroll the caret within the viewport.
806   mManager.OnScrollStart();
807   mManager.OnScrollEnd();
808   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
809 
810   // Simulate a long tap on an empty input.
811   mManager.FirstCaret().SetAppearance(Appearance::Normal);
812   mManager.UpdateCarets();
813   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
814   check.Call("longtap updatecarets");
815 
816   // Scroll the caret to be out of the viewport.
817   mManager.OnScrollStart();
818   check.Call("longtap scrollstart1");
819   mManager.OnScrollEnd();
820   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
821   check.Call("longtap scrollend1");
822 
823   // Scroll the caret into the viewport.
824   mManager.OnScrollStart();
825   check.Call("longtap scrollstart2");
826   mManager.OnScrollPositionChanged();
827   mManager.OnScrollEnd();
828   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
829   check.Call("longtap scrollend2");
830 
831   // Scroll the caret within the viewport.
832   mManager.OnScrollStart();
833   check.Call("longtap scrollstart3");
834   mManager.OnScrollPositionChanged();
835   mManager.OnScrollEnd();
836   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
837   check.Call("longtap scrollend3");
838 }
839 
840 }  // namespace mozilla
841