Bug 1097398 Part 2 - Add preferences to make carets always tilt. r=roc
authorTing-Yu Lin <tlin@mozilla.com>
Tue, 16 Feb 2016 16:55:28 +0800
changeset 322582 d0be9a37a515a0e91f099743e2253d580ded88b5
parent 322581 eeebbe5ee8fbe2dbc25d47b3be0adf46178dbb77
child 322583 2fe725e70124255ec26d7be686708932f470d609
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1097398
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1097398 Part 2 - Add preferences to make carets always tilt. r=roc This is to support Firefox Android L style carets assets that the two carets always look like tilt. This patch is derived from a WIP patch by Mark Capella <markcapella@twcny.rr.com> MozReview-Commit-ID: H3nKLz6HcpM
layout/base/AccessibleCaretManager.cpp
layout/base/AccessibleCaretManager.h
layout/base/gtest/TestAccessibleCaretManager.cpp
mobile/android/app/mobile.js
modules/libpref/init/all.js
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -66,16 +66,18 @@ std::ostream& operator<<(std::ostream& a
 
 /*static*/ bool
 AccessibleCaretManager::sSelectionBarEnabled = false;
 /*static*/ bool
 AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = false;
 /*static*/ bool
 AccessibleCaretManager::sCaretsExtendedVisibility = false;
 /*static*/ bool
+AccessibleCaretManager::sCaretsAlwaysTilt = false;
+/*static*/ bool
 AccessibleCaretManager::sCaretsScriptUpdates = false;
 /*static*/ bool
 AccessibleCaretManager::sHapticFeedback = false;
 
 AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell)
   : mPresShell(aPresShell)
 {
   if (!mPresShell) {
@@ -90,16 +92,18 @@ AccessibleCaretManager::AccessibleCaretM
   static bool addedPrefs = false;
   if (!addedPrefs) {
     Preferences::AddBoolVarCache(&sSelectionBarEnabled,
                                  "layout.accessiblecaret.bar.enabled");
     Preferences::AddBoolVarCache(&sCaretShownWhenLongTappingOnEmptyContent,
       "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content");
     Preferences::AddBoolVarCache(&sCaretsExtendedVisibility,
                                  "layout.accessiblecaret.extendedvisibility");
+    Preferences::AddBoolVarCache(&sCaretsAlwaysTilt,
+                                 "layout.accessiblecaret.always_tilt");
     Preferences::AddBoolVarCache(&sCaretsScriptUpdates,
       "layout.accessiblecaret.allow_script_change_updates");
     Preferences::AddBoolVarCache(&sHapticFeedback,
                                  "layout.accessiblecaret.hapticfeedback");
     addedPrefs = true;
   }
 }
 
@@ -389,26 +393,30 @@ AccessibleCaretManager::UpdateCaretsForS
     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();
+    if (sCaretsAlwaysTilt) {
+      UpdateCaretsForAlwaysTilt(startFrame, endFrame);
+    } else {
+      UpdateCaretsForOverlappingTilt();
+    }
   }
 
   if (!mActiveCaret) {
     DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
   }
 }
 
 void
-AccessibleCaretManager::UpdateCaretsForTilt()
+AccessibleCaretManager::UpdateCaretsForOverlappingTilt()
 {
   if (mFirstCaret->IsVisuallyVisible() && mSecondCaret->IsVisuallyVisible()) {
     if (mFirstCaret->Intersects(*mSecondCaret)) {
       if (mFirstCaret->LogicalPosition().x <=
           mSecondCaret->LogicalPosition().x) {
         mFirstCaret->SetAppearance(Appearance::Left);
         mSecondCaret->SetAppearance(Appearance::Right);
       } else {
@@ -418,16 +426,32 @@ AccessibleCaretManager::UpdateCaretsForT
     } else {
       mFirstCaret->SetAppearance(Appearance::Normal);
       mSecondCaret->SetAppearance(Appearance::Normal);
     }
   }
 }
 
 void
+AccessibleCaretManager::UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
+                                                  nsIFrame* aEndFrame)
+{
+  if (mFirstCaret->IsVisuallyVisible()) {
+    auto startFrameWritingMode = aStartFrame->GetWritingMode();
+    mFirstCaret->SetAppearance(startFrameWritingMode.IsBidiLTR() ?
+                               Appearance::Left : Appearance::Right);
+  }
+  if (mSecondCaret->IsVisuallyVisible()) {
+    auto endFrameWritingMode = aEndFrame->GetWritingMode();
+    mSecondCaret->SetAppearance(endFrameWritingMode.IsBidiLTR() ?
+                                Appearance::Right : Appearance::Left);
+  }
+}
+
+void
 AccessibleCaretManager::ProvideHapticFeedback()
 {
   if (sHapticFeedback) {
     nsCOMPtr<nsIHapticFeedback> haptic =
       do_GetService("@mozilla.org/widget/hapticfeedback;1");
     haptic->PerformSimpleAction(haptic->LongPress);
   }
 }
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -199,17 +199,21 @@ protected:
   // 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.
-  virtual void UpdateCaretsForTilt();
+  virtual void UpdateCaretsForOverlappingTilt();
+
+  // Make the two carets always tilt.
+  virtual void UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
+                                         nsIFrame* aEndFrame);
 
   // 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;
@@ -272,16 +276,20 @@ protected:
   // 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.
   static bool sCaretsExtendedVisibility;
 
+  // Preference to make carets always tilt in selection mode. By default, the
+  // carets become tilt only when they are overlapping.
+  static bool sCaretsAlwaysTilt;
+
   // 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.
   static bool sHapticFeedback;
 };
--- a/layout/base/gtest/TestAccessibleCaretManager.cpp
+++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
@@ -56,16 +56,17 @@ public:
   class MockAccessibleCaretManager : public AccessibleCaretManager
   {
   public:
     using CaretMode = AccessibleCaretManager::CaretMode;
     using AccessibleCaretManager::UpdateCarets;
     using AccessibleCaretManager::HideCarets;
     using AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent;
     using AccessibleCaretManager::sCaretsExtendedVisibility;
+    using AccessibleCaretManager::sCaretsAlwaysTilt;
 
     MockAccessibleCaretManager()
       : AccessibleCaretManager(nullptr)
     {
       mFirstCaret = MakeUnique<MockAccessibleCaret>();
       mSecondCaret = MakeUnique<MockAccessibleCaret>();
     }
 
@@ -86,17 +87,28 @@ public:
     }
 
     virtual bool IsCaretDisplayableInCursorMode(
       nsIFrame** aOutFrame = nullptr, int32_t* aOutOffset = nullptr) const override
     {
       return true;
     }
 
-    virtual void UpdateCaretsForTilt() override {}
+    virtual void UpdateCaretsForOverlappingTilt() override {}
+
+    virtual void UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
+                                           nsIFrame* aEndFrame)
+    {
+      if (mFirstCaret->IsVisuallyVisible()) {
+        mFirstCaret->SetAppearance(Appearance::Left);
+      }
+      if (mSecondCaret->IsVisuallyVisible()) {
+        mSecondCaret->SetAppearance(Appearance::Right);
+      }
+    }
 
     virtual bool IsTerminated() const override { return false; }
 
     MOCK_CONST_METHOD0(GetCaretMode, CaretMode());
     MOCK_CONST_METHOD1(DispatchCaretStateChangedEvent,
                        void(CaretChangedReason aReason));
     MOCK_CONST_METHOD1(HasNonEmptyTextContent, bool(nsINode* aNode));
 
@@ -406,17 +418,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
 
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester,
-       TestScrollInSelectionModeWithExtendedVisibility)
+       TestScrollInSelectionModeWithExtendedVisibilityAndAlwaysTilt)
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Selection));
 
   MockFunction<void(std::string aCheckPointName)> check;
   {
     InSequence dummy;
 
@@ -454,53 +466,57 @@ TEST_F(AccessibleCaretManagerTester,
     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(
+  // Simulate Firefox Android preferences.
+  AutoRestore<bool> saveCaretsExtendedVisibility(
     MockAccessibleCaretManager::sCaretsExtendedVisibility);
   MockAccessibleCaretManager::sCaretsExtendedVisibility = true;
+  AutoRestore<bool> saveCaretsAlwaysTilt(
+    MockAccessibleCaretManager::sCaretsAlwaysTilt);
+  MockAccessibleCaretManager::sCaretsAlwaysTilt = true;
 
   mManager.UpdateCarets();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+  EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
   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(FirstCaretAppearance(), Appearance::Left);
   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);
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
+  EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible)
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -942,16 +942,19 @@ pref("layout.accessiblecaret.timeout_ms"
 // 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.
 pref("layout.accessiblecaret.extendedvisibility", true);
 
+// Androids carets are always tilt to match the text selection guideline.
+pref("layout.accessiblecaret.always_tilt", 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);
 
 // Disable sending console to logcat on release builds.
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4975,16 +4975,19 @@ pref("layout.accessiblecaret.timeout_ms"
 
 // Simulate long tap to select words on the platforms where APZ is not enabled
 // or long tap events does not fired by APZ.
 pref("layout.accessiblecaret.use_long_tap_injector", true);
 
 // Use AccessibleCaret default behaviours.
 pref("layout.accessiblecaret.extendedvisibility", false);
 
+// By default, carets become tilt only when they are overlapping.
+pref("layout.accessiblecaret.always_tilt", false);
+
 // Selection change notifications generated by Javascript hide
 // AccessibleCarets and close UI interaction by default.
 pref("layout.accessiblecaret.allow_script_change_updates", false);
 
 // Optionally provide haptic feedback on longPress selection events.
 pref("layout.accessiblecaret.hapticfeedback", false);
 
 // Wakelock is disabled by default.