Bug 1097398 Part 2 - Add preferences to make carets always tilt
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
--- 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
@@ -954,16 +954,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
@@ -4969,16 +4969,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.