Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 17 Feb 2016 14:37:20 +0100
changeset 331612 428c9570954f13e57f4bf931ed847390499009a8
parent 331611 ee60dc3d06556a23222a8c25a93aaa818ccca9b9 (current diff)
parent 331542 709f559b5406e8555cf84dd09bdc747b076f142c (diff)
child 331613 24253491a3e22a92475775eb0e40291d9d1fa06a
push id11020
push userjolesen@mozilla.com
push dateWed, 17 Feb 2016 18:16:38 +0000
milestone47.0a1
Merge mozilla-central to mozilla-inbound
layout/base/AccessibleCaret.h
--- a/layout/base/AccessibleCaret.h
+++ b/layout/base/AccessibleCaret.h
@@ -194,19 +194,19 @@ protected:
     virtual ~DummyTouchListener() {};
   };
 
   // Member variables
   Appearance mAppearance = Appearance::None;
 
   bool mSelectionBarEnabled = false;
 
-  // AccessibleCaretManager owns us by a UniquePtr. When it's terminated by
+  // AccessibleCaretManager owns us. When it's destroyed by
   // AccessibleCaretEventHub::Terminate() which is called in
-  // PresShell::Destroy(), it frees us automatically. No need to worry if we
+  // PresShell::Destroy(), it frees us automatically. No need to worry we
   // outlive mPresShell.
   nsIPresShell* MOZ_NON_OWNING_REF const mPresShell = nullptr;
 
   RefPtr<dom::AnonymousContent> mCaretElementHolder;
 
   // mImaginaryCaretRect is relative to root frame.
   nsRect mImaginaryCaretRect;
 
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -454,17 +454,17 @@ AccessibleCaretEventHub::Terminate()
   if (mLongTapInjectorTimer) {
     mLongTapInjectorTimer->Cancel();
   }
 
   if (mScrollEndInjectorTimer) {
     mScrollEndInjectorTimer->Cancel();
   }
 
-  mManager->Terminate();
+  mManager = nullptr;
   mPresShell = nullptr;
   mInitialized = false;
 }
 
 nsEventStatus
 AccessibleCaretEventHub::HandleEvent(WidgetEvent* aEvent)
 {
   nsEventStatus status = nsEventStatus_eIgnore;
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -100,27 +100,17 @@ AccessibleCaretManager::AccessibleCaretM
     Preferences::AddBoolVarCache(&sHapticFeedback,
                                  "layout.accessiblecaret.hapticfeedback");
     addedPrefs = true;
   }
 }
 
 AccessibleCaretManager::~AccessibleCaretManager()
 {
-}
-
-void
-AccessibleCaretManager::Terminate()
-{
   CancelCaretTimeoutTimer();
-  mCaretTimeoutTimer = nullptr;
-  mFirstCaret = nullptr;
-  mSecondCaret = nullptr;
-  mActiveCaret = nullptr;
-  mPresShell = nullptr;
 }
 
 nsresult
 AccessibleCaretManager::OnSelectionChanged(nsIDOMDocument* aDoc,
                                            nsISelection* aSel, int16_t aReason)
 {
   Selection* selection = GetSelection();
   AC_LOG("%s: aSel: %p, GetSelection(): %p, aReason: %d", __FUNCTION__,
@@ -137,16 +127,17 @@ AccessibleCaretManager::OnSelectionChang
     return NS_OK;
   }
 
   // Move the cursor by Javascript / or unknown internal.
   if (aReason == nsISelectionListener::NO_REASON) {
     // Update visible carets, if javascript changes are allowed.
     if (sCaretsScriptUpdates &&
         (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible())) {
+        FlushLayout();
         UpdateCarets();
         return NS_OK;
     }
     // Default for NO_REASON is to make hidden.
     HideCarets();
     return NS_OK;
   }
 
@@ -196,32 +187,27 @@ AccessibleCaretManager::DoNotShowCarets(
     DispatchCaretStateChangedEvent(CaretChangedReason::Visibilitychange);
     CancelCaretTimeoutTimer();
   }
 }
 
 void
 AccessibleCaretManager::UpdateCarets(UpdateCaretsHint aHint)
 {
-  FlushLayout();
-  if (IsTerminated()) {
-    return;
-  }
-
   mLastUpdateCaretMode = GetCaretMode();
 
   switch (mLastUpdateCaretMode) {
   case CaretMode::None:
     HideCarets();
     break;
   case CaretMode::Cursor:
     UpdateCaretsForCursorMode(aHint);
     break;
   case CaretMode::Selection:
-    UpdateCaretsForSelectionMode(aHint);
+    UpdateCaretsForSelectionMode();
     break;
   }
 }
 
 bool
 AccessibleCaretManager::IsCaretDisplayableInCursorMode(nsIFrame** aOutFrame,
                                                        int32_t* aOutOffset) const
 {
@@ -325,54 +311,45 @@ AccessibleCaretManager::UpdateCaretsForC
 
   if ((result != PositionChangedResult::NotChanged || oldSecondCaretVisible) &&
       !mActiveCaret) {
     DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
   }
 }
 
 void
-AccessibleCaretManager::UpdateCaretsForSelectionMode(UpdateCaretsHint aHint)
+AccessibleCaretManager::UpdateCaretsForSelectionMode()
 {
   AC_LOG("%s: selection: %p", __FUNCTION__, GetSelection());
 
   int32_t startOffset = 0;
   nsIFrame* startFrame = FindFirstNodeWithFrame(false, &startOffset);
 
   int32_t endOffset = 0;
   nsIFrame* endFrame = FindFirstNodeWithFrame(true, &endOffset);
 
   if (!CompareTreePosition(startFrame, endFrame)) {
     // XXX: Do we really have to hide carets if this condition isn't satisfied?
     HideCarets();
     return;
   }
 
-  auto updateSingleCaret = [aHint](AccessibleCaret* aCaret, nsIFrame* aFrame,
-                                   int32_t aOffset) -> PositionChangedResult
+  auto updateSingleCaret = [](AccessibleCaret* aCaret, nsIFrame* aFrame,
+                              int32_t aOffset) -> PositionChangedResult
   {
     PositionChangedResult result = aCaret->SetPosition(aFrame, aOffset);
     aCaret->SetSelectionBarEnabled(sSelectionBarEnabled);
 
     switch (result) {
       case PositionChangedResult::NotChanged:
         // Do nothing
         break;
 
       case PositionChangedResult::Changed:
-        switch (aHint) {
-          case UpdateCaretsHint::Default:
-            aCaret->SetAppearance(Appearance::Normal);
-            break;
-
-          case UpdateCaretsHint::RespectOldAppearance:
-            // Do nothing to preserve the appearance of the caret set by the
-            // caller.
-            break;
-        }
+        aCaret->SetAppearance(Appearance::Normal);
         break;
 
       case PositionChangedResult::Invisible:
         aCaret->SetAppearance(Appearance::NormalNotShown);
         break;
     }
     return result;
   };
@@ -381,26 +358,19 @@ AccessibleCaretManager::UpdateCaretsForS
     updateSingleCaret(mFirstCaret.get(), startFrame, startOffset);
   PositionChangedResult secondCaretResult =
     updateSingleCaret(mSecondCaret.get(), endFrame, endOffset);
 
   if (firstCaretResult == PositionChangedResult::Changed ||
       secondCaretResult == PositionChangedResult::Changed) {
     // Flush layout to make the carets intersection correct.
     FlushLayout();
-    if (IsTerminated()) {
-      return;
-    }
   }
 
-  if (aHint == UpdateCaretsHint::Default) {
-    // Only check for tilt carets with default update hint. Otherwise we might
-    // override the appearance set by the caller.
-    UpdateCaretsForTilt();
-  }
+  UpdateCaretsForTilt();
 
   if (!mActiveCaret) {
     DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
   }
 }
 
 void
 AccessibleCaretManager::UpdateCaretsForTilt()
@@ -585,41 +555,40 @@ AccessibleCaretManager::SelectWordOrShor
   return rv;
 }
 
 void
 AccessibleCaretManager::OnScrollStart()
 {
   AC_LOG("%s", __FUNCTION__);
 
-  mFirstCaretAppearanceOnScrollStart = mFirstCaret->GetAppearance();
-  mSecondCaretAppearanceOnScrollStart = mSecondCaret->GetAppearance();
+  if (GetCaretMode() == CaretMode::Cursor) {
+    mFirstCaretAppearanceOnScrollStart = mFirstCaret->GetAppearance();
+  }
 
   // Hide the carets. (Extended visibility makes them "NormalNotShown").
   if (sCaretsExtendedVisibility) {
     DoNotShowCarets();
   } else {
     HideCarets();
   }
 }
 
 void
 AccessibleCaretManager::OnScrollEnd()
 {
   if (mLastUpdateCaretMode != GetCaretMode()) {
     return;
   }
 
-  mFirstCaret->SetAppearance(mFirstCaretAppearanceOnScrollStart);
-  mSecondCaret->SetAppearance(mSecondCaretAppearanceOnScrollStart);
-
   if (GetCaretMode() == CaretMode::Cursor) {
+    mFirstCaret->SetAppearance(mFirstCaretAppearanceOnScrollStart);
     if (!mFirstCaret->IsLogicallyVisible()) {
-      // If the caret is hidden (Appearance::None) due to timeout or blur, no
-      // need to update it.
+      // If the caret is hide (Appearance::None) due to timeout or blur, no need
+      // to update it.
       return;
     }
   }
 
   AC_LOG("%s: UpdateCarets()", __FUNCTION__);
   UpdateCarets();
 }
 
@@ -1139,17 +1108,17 @@ AccessibleCaretManager::CaretTimeoutMs()
   }
 
   return caretTimeoutMs;
 }
 
 void
 AccessibleCaretManager::LaunchCaretTimeoutTimer()
 {
-  if (!mPresShell || !mCaretTimeoutTimer || CaretTimeoutMs() == 0 ||
+  if (!mCaretTimeoutTimer || CaretTimeoutMs() == 0 ||
       GetCaretMode() != CaretMode::Cursor || mActiveCaret) {
     return;
   }
 
   nsTimerCallbackFunc callback = [](nsITimer* aTimer, void* aClosure) {
     auto self = static_cast<AccessibleCaretManager*>(aClosure);
     if (self->GetCaretMode() == CaretMode::Cursor) {
       self->HideCarets();
@@ -1166,22 +1135,21 @@ AccessibleCaretManager::CancelCaretTimeo
   if (mCaretTimeoutTimer) {
     mCaretTimeoutTimer->Cancel();
   }
 }
 
 void
 AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const
 {
-  if (!mPresShell) {
-    return;
-  }
+  // Holding PresShell to prevent AccessibleCaretManager to be destroyed.
+  nsCOMPtr<nsIPresShell> presShell = mPresShell;
 
   FlushLayout();
-  if (IsTerminated()) {
+  if (presShell->IsDestroying()) {
     return;
   }
 
   Selection* sel = GetSelection();
   if (!sel) {
     return;
   }
 
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -45,19 +45,16 @@ class Selection;
 // https://wiki.mozilla.org/Copy_n_Paste
 //
 class AccessibleCaretManager
 {
 public:
   explicit AccessibleCaretManager(nsIPresShell* aPresShell);
   virtual ~AccessibleCaretManager();
 
-  // Called by AccessibleCaretEventHub to inform us that PresShell is destroyed.
-  void Terminate();
-
   // The aPoint in the following public methods should be relative to root
   // frame.
 
   // Press caret on the given point. Return NS_OK if the point is actually on
   // one of the carets.
   virtual nsresult PressCaret(const nsPoint& aPoint);
 
   // Drag caret to the given point. It's required to call PressCaret()
@@ -122,30 +119,28 @@ protected:
     // the caret in cursor mode is hidden due to timeout, do not change its
     // appearance to Normal.
     RespectOldAppearance
   };
 
   friend std::ostream& operator<<(std::ostream& aStream,
                                   const UpdateCaretsHint& aResult);
 
-  // Update carets based on current selection status. This function will flush
-  // layout, so caller must ensure the PresShell is still valid after calling
-  // this method.
+  // Update carets based on current selection status.
   void UpdateCarets(UpdateCaretsHint aHint = UpdateCaretsHint::Default);
 
   // Force hiding all carets regardless of the current selection status.
   void HideCarets();
 
   // Force carets to be "present" logically, but not visible. Allows ActionBar
   // to stay open when carets visibility is supressed during scroll.
   void DoNotShowCarets();
 
   void UpdateCaretsForCursorMode(UpdateCaretsHint aHint);
-  void UpdateCaretsForSelectionMode(UpdateCaretsHint aHint);
+  void UpdateCaretsForSelectionMode();
 
   // Provide haptic / touch feedback, primarily for select on longpress.
   void ProvideHapticFeedback();
 
   // Get the nearest enclosing focusable frame of aFrame.
   // @return focusable frame if there is any; nullptr otherwise.
   nsIFrame* GetFocusableFrame(nsIFrame* aFrame) const;
 
@@ -160,21 +155,17 @@ protected:
   // If aBackward is false, find the first node from the first range in current
   // selection, and return the frame and the offset into that frame. If aBackward
   // is true, find the last node from the last range instead.
   nsIFrame* FindFirstNodeWithFrame(bool aBackward, int32_t* aOutOffset) const;
 
   nsresult DragCaretInternal(const nsPoint& aPoint);
   nsPoint AdjustDragBoundary(const nsPoint& aPoint) const;
   void ClearMaintainedSelection() const;
-
-  // Caller is responsible to use IsTerminated() to check whether PresShell is
-  // still valid.
   void FlushLayout() const;
-
   dom::Element* GetEditingHostForFrame(nsIFrame* aFrame) const;
   dom::Selection* GetSelection() const;
   already_AddRefed<nsFrameSelection> GetFrameSelection() const;
 
   // Get the bounding rectangle for aFrame where the caret under cursor mode can
   // be positioned. The rectangle is relative to the root frame.
   nsRect GetContentBoundaryForFrame(nsIFrame* aFrame) const;
 
@@ -188,19 +179,16 @@ protected:
   // no one touches it.
   uint32_t CaretTimeoutMs() const;
   void LaunchCaretTimeoutTimer();
   void CancelCaretTimeoutTimer();
 
   // ---------------------------------------------------------------------------
   // The following functions are made virtual for stubbing or mocking in gtest.
   //
-  // @return true if Terminate() had been called.
-  virtual bool IsTerminated() const { return !mPresShell; }
-
   // Get caret mode based on current selection.
   virtual CaretMode GetCaretMode() const;
 
   // @return true if aStartFrame comes before aEndFrame.
   virtual bool CompareTreePosition(nsIFrame* aStartFrame,
                                    nsIFrame* aEndFrame) const;
 
   // Check if the two carets is overlapping to become tilt.
@@ -209,31 +197,29 @@ protected:
   // Check whether AccessibleCaret is displayable in cursor mode or not.
   // @param aOutFrame returns frame of the cursor if it's displayable.
   // @param aOutOffset returns frame offset as well.
   virtual bool IsCaretDisplayableInCursorMode(nsIFrame** aOutFrame = nullptr,
                                               int32_t* aOutOffset = nullptr) const;
 
   virtual bool HasNonEmptyTextContent(nsINode* aNode) const;
 
-  // This function will flush layout, so caller must ensure the PresShell is
-  // still valid after calling this method.
+  // This function will call FlushPendingNotifications. So caller must ensure
+  // everything exists after calling this method.
   virtual void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason) const;
 
   // ---------------------------------------------------------------------------
   // Member variables
   //
   nscoord mOffsetYToCaretLogicalPosition = NS_UNCONSTRAINEDSIZE;
 
-  // AccessibleCaretEventHub owns us by a UniquePtr. When it's destroyed, we'll
-  // also be destroyed. No need to worry if we outlive mPresShell.
-  //
-  // mPresShell will be set to nullptr in Terminate(). Therefore mPresShell is
-  // nullptr either we are in gtest or PresShell::IsDestroying() is true.
-  nsIPresShell* MOZ_NON_OWNING_REF mPresShell = nullptr;
+  // AccessibleCaretEventHub owns us. When it's Terminate() called by
+  // PresShell::Destroy(), we will be destroyed. No need to worry we outlive
+  // mPresShell.
+  nsIPresShell* MOZ_NON_OWNING_REF const mPresShell = nullptr;
 
   // First caret is attached to nsCaret in cursor mode, and is attached to
   // selection highlight as the left caret in selection mode.
   UniquePtr<AccessibleCaret> mFirstCaret;
 
   // Second caret is used solely in selection mode, and is attached to selection
   // highlight as the right caret.
   UniquePtr<AccessibleCaret> mSecondCaret;
@@ -243,22 +229,20 @@ protected:
 
   // The timer for hiding the caret in cursor mode after timeout behind the
   // preference "layout.accessiblecaret.timeout_ms".
   nsCOMPtr<nsITimer> mCaretTimeoutTimer;
 
   // The caret mode since last update carets.
   CaretMode mLastUpdateCaretMode = CaretMode::None;
 
-  // Store the appearance of the carets when calling OnScrollStart() so that it
-  // can be restored in OnScrollEnd().
+  // Store the appearance of the first caret when calling OnScrollStart so that
+  // it can be restored in OnScrollEnd.
   AccessibleCaret::Appearance mFirstCaretAppearanceOnScrollStart =
                                  AccessibleCaret::Appearance::None;
-  AccessibleCaret::Appearance mSecondCaretAppearanceOnScrollStart =
-                                 AccessibleCaret::Appearance::None;
 
   static const int32_t kAutoScrollTimerDelay = 30;
 
   // Clicking on the boundary of input or textarea will move the caret to the
   // front or end of the content. To avoid this, we need to deflate the content
   // boundary by 61 app units, which is 1 pixel + 1 app unit as defined in
   // AppUnit.h.
   static const int32_t kBoundaryAppUnits = 61;
@@ -269,17 +253,17 @@ protected:
 
   // Preference to show caret in cursor mode when long tapping on an empty
   // content. This also changes the default update behavior in cursor mode,
   // which is based on the emptiness of the content, into something more
   // heuristic. See UpdateCaretsForCursorMode() for the details.
   static bool sCaretShownWhenLongTappingOnEmptyContent;
 
   // Android specific visibility extensions correct compatibility issues
-  // with ActionBar visibility during page scroll.
+  // with caret-drag and ActionBar visibility during page scroll.
   static bool sCaretsExtendedVisibility;
 
   // By default, javascript content selection changes closes AccessibleCarets and
   // UI interactions. Optionally, we can try to maintain the active UI, keeping
   // carets and ActionBar available.
   static bool sCaretsScriptUpdates;
 
   // AccessibleCaret pref for haptic feedback behaviour on longPress.
--- a/layout/base/gtest/TestAccessibleCaretManager.cpp
+++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
@@ -55,17 +55,16 @@ public:
 
   class MockAccessibleCaretManager : public AccessibleCaretManager
   {
   public:
     using CaretMode = AccessibleCaretManager::CaretMode;
     using AccessibleCaretManager::UpdateCarets;
     using AccessibleCaretManager::HideCarets;
     using AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent;
-    using AccessibleCaretManager::sCaretsExtendedVisibility;
 
     MockAccessibleCaretManager()
       : AccessibleCaretManager(nullptr)
     {
       mFirstCaret = MakeUnique<MockAccessibleCaret>();
       mSecondCaret = MakeUnique<MockAccessibleCaret>();
     }
 
@@ -88,18 +87,16 @@ public:
     virtual bool IsCaretDisplayableInCursorMode(
       nsIFrame** aOutFrame = nullptr, int32_t* aOutOffset = nullptr) const override
     {
       return true;
     }
 
     virtual void UpdateCaretsForTilt() override {}
 
-    virtual bool IsTerminated() const override { return false; }
-
     MOCK_CONST_METHOD0(GetCaretMode, CaretMode());
     MOCK_CONST_METHOD1(DispatchCaretStateChangedEvent,
                        void(CaretChangedReason aReason));
     MOCK_CONST_METHOD1(HasNonEmptyTextContent, bool(nsINode* aNode));
 
   }; // class MockAccessibleCaretManager
 
   using Appearance = AccessibleCaret::Appearance;
@@ -342,166 +339,52 @@ TEST_F(AccessibleCaretManagerTester, Tes
     .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));
+      .WillRepeatedly(Return(PositionChangedResult::Invisible));
+    EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
+      .WillRepeatedly(Return(PositionChangedResult::Changed));
 
     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
+                  CaretChangedReason::Updateposition)).Times(1);
     EXPECT_CALL(check, Call("updatecarets"));
 
     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Visibilitychange));
-    EXPECT_CALL(check, Call("scrollstart1"));
+                  CaretChangedReason::Visibilitychange)).Times(1);
+    EXPECT_CALL(check, Call("scrollstart"));
 
     // After scroll ended, first caret is visible and second caret is out of
     // scroll port.
+    EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
+      .WillRepeatedly(Return(PositionChangedResult::Changed));
     EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
-      .WillOnce(Return(PositionChangedResult::Invisible));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("scrollend1"));
+      .WillRepeatedly(Return(PositionChangedResult::Invisible));
 
     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Visibilitychange));
-    EXPECT_CALL(check, Call("scrollstart2"));
-
-    // After the scroll ended, both carets are visible.
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("scrollend2"));
+                  CaretChangedReason::Updateposition)).Times(1);
   }
 
   mManager.UpdateCarets();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
   check.Call("updatecarets");
 
   mManager.OnScrollStart();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::None);
-  check.Call("scrollstart1");
-
-  mManager.OnReflow();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::None);
+  check.Call("scrollstart");
 
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
-  check.Call("scrollend1");
-
-  mManager.OnScrollStart();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::None);
-  check.Call("scrollstart2");
-
-  mManager.OnReflow();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::None);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::None);
-
-  mManager.OnScrollEnd();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
-  check.Call("scrollend2");
-}
-
-TEST_F(AccessibleCaretManagerTester,
-       TestScrollInSelectionModeWithExtendedVisibility)
-{
-  EXPECT_CALL(mManager, GetCaretMode())
-    .WillRepeatedly(Return(CaretMode::Selection));
-
-  MockFunction<void(std::string aCheckPointName)> check;
-  {
-    InSequence dummy;
-
-    // Initially, first caret is out of scrollport, and second caret is visible.
-    EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _))
-      .WillOnce(Return(PositionChangedResult::Invisible));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("updatecarets"));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Visibilitychange));
-    EXPECT_CALL(check, Call("scrollstart1"));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("reflow1"));
-
-    // After scroll ended, first caret is visible and second caret is out of
-    // scroll port.
-    EXPECT_CALL(mManager.SecondCaret(), SetPosition(_, _))
-      .WillOnce(Return(PositionChangedResult::Invisible));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("scrollend1"));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Visibilitychange));
-    EXPECT_CALL(check, Call("scrollstart2"));
-
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("reflow2"));
-
-    // After the scroll ended, both carets are visible.
-    EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
-                  CaretChangedReason::Updateposition));
-    EXPECT_CALL(check, Call("scrollend2"));
-  }
-
-  AutoRestore<bool> savePref(
-    MockAccessibleCaretManager::sCaretsExtendedVisibility);
-  MockAccessibleCaretManager::sCaretsExtendedVisibility = true;
-
-  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::NormalNotShown);
-  check.Call("scrollstart1");
-
-  mManager.OnReflow();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
-  check.Call("reflow1");
-
-  mManager.OnScrollEnd();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
-  check.Call("scrollend1");
-
-  mManager.OnScrollStart();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
-  check.Call("scrollstart2");
-
-  mManager.OnReflow();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
-  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, TestScrollInCursorModeWhenLogicallyVisible)
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
@@ -649,17 +532,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
 
     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
                    CaretChangedReason::Visibilitychange));
     EXPECT_CALL(check, Call("scrollstart3"));
 
     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
                    CaretChangedReason::Updateposition));
     EXPECT_CALL(check, Call("scrollend3"));
-  }
+}
 
   // Simulate a single tap on an empty content.
   mManager.UpdateCarets();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   check.Call("updatecarets");
 
   // Scroll the caret to be out of the viewport.
   mManager.OnScrollStart();
@@ -678,16 +561,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
   // Scroll the caret within the viewport.
   mManager.OnScrollStart();
   check.Call("scrollstart3");
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   check.Call("scrollend3");
 }
 
+
 TEST_F(AccessibleCaretManagerTester,
        TestScrollInCursorModeOnEmptyContentWithSpecialPreference)
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
   EXPECT_CALL(mManager, HasNonEmptyTextContent(_))
     .WillRepeatedly(Return(false));
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -950,18 +950,18 @@ pref("layout.accessiblecaret.caret_shown
 
 // Android needs persistent carets and actionbar. Turn off the caret timeout.
 pref("layout.accessiblecaret.timeout_ms", 0);
 
 // Android generates long tap (mouse) events.
 pref("layout.accessiblecaret.use_long_tap_injector", false);
 
 // AccessibleCarets behaviour is extended to support Android specific
-// requirements to hide carets while maintaining ActionBar visiblity during page
-// scroll.
+// requirements during caret-drag, tapping into empty inputs, and to
+// hide carets while maintaining ActionBar visiblity during page scroll.
 pref("layout.accessiblecaret.extendedvisibility", true);
 
 // Selection change notifications generated by Javascript changes
 // update active AccessibleCarets / UI interactions.
 pref("layout.accessiblecaret.allow_script_change_updates", true);
 
 // Optionally provide haptic feedback on longPress selection events.
 pref("layout.accessiblecaret.hapticfeedback", true);