diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index cd2fcbc2746..9de51212ee9 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -8,6 +8,7 @@ #include "nsFocusManager.h" +#include "AccessibleCaretEventHub.h" #include "nsIInterfaceRequestorUtils.h" #include "nsGkAtoms.h" #include "nsContentUtils.h" @@ -1694,6 +1695,11 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear, selectionCarets->NotifyBlur(aIsLeavingDocument || !mActiveWindow); } + nsRefPtr eventHub = presShell->GetAccessibleCaretEventHub(); + if (eventHub) { + eventHub->NotifyBlur(aIsLeavingDocument || !mActiveWindow); + } + // at this point, it is expected that this window will be still be // focused, but the focused content will be null, as it was cleared before // the event. If this isn't the case, then something else was focused during diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 562cd9fb5ce..d5938ec1379 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -59,6 +59,7 @@ class nsCanvasFrame; class nsAString; class nsCaret; namespace mozilla { +class AccessibleCaretEventHub; class TouchCaret; class SelectionCarets; } // namespace mozilla @@ -808,6 +809,11 @@ public: */ virtual mozilla::dom::Element* GetSelectionCaretsEndElement() const = 0; + /** + * Get the AccessibleCaretEventHub, if it exists. AddRefs it. + */ + virtual already_AddRefed GetAccessibleCaretEventHub() const = 0; + /** * Get the caret, if it exists. AddRefs it. */ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index a977de6e400..85cbe96a32b 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -76,6 +76,7 @@ #include "nsIPermissionManager.h" #include "nsIMozBrowserFrame.h" #include "nsCaret.h" +#include "AccessibleCaretEventHub.h" #include "TouchCaret.h" #include "SelectionCarets.h" #include "nsIDOMHTMLDocument.h" @@ -706,6 +707,7 @@ static uint32_t sNextPresShellId; static bool sPointerEventEnabled = true; static bool sTouchCaretEnabled = false; static bool sSelectionCaretEnabled = false; +static bool sAccessibleCaretEnabled = false; static bool sBeforeAfterKeyboardEventEnabled = false; /* static */ bool @@ -730,6 +732,17 @@ PresShell::SelectionCaretPrefEnabled() return sSelectionCaretEnabled; } +/* static */ bool +PresShell::AccessibleCaretEnabled() +{ + static bool initialized = false; + if (!initialized) { + Preferences::AddBoolVarCache(&sAccessibleCaretEnabled, "layout.accessiblecaret.enabled"); + initialized = true; + } + return sAccessibleCaretEnabled; +} + /* static */ bool PresShell::BeforeAfterKeyboardEventEnabled() { @@ -883,17 +896,21 @@ PresShell::Init(nsIDocument* aDocument, // before creating any frames. SetPreferenceStyleRules(false); - if (TouchCaretPrefEnabled()) { + if (TouchCaretPrefEnabled() && !AccessibleCaretEnabled()) { // Create touch caret handle mTouchCaret = new TouchCaret(this); } - if (SelectionCaretPrefEnabled()) { + if (SelectionCaretPrefEnabled() && !AccessibleCaretEnabled()) { // Create selection caret handle mSelectionCarets = new SelectionCarets(this); mSelectionCarets->Init(); } + if (AccessibleCaretEnabled()) { + // Need to happen before nsFrameSelection has been set up. + mAccessibleCaretEventHub = new AccessibleCaretEventHub(); + } mSelection = new nsFrameSelection(); @@ -1163,6 +1180,11 @@ PresShell::Destroy() mSelectionCarets = nullptr; } + if (mAccessibleCaretEventHub) { + mAccessibleCaretEventHub->Terminate(); + mAccessibleCaretEventHub = nullptr; + } + // release our pref style sheet, if we have one still ClearPreferenceStyleRules(); @@ -1907,6 +1929,11 @@ PresShell::Initialize(nscoord aWidth, nscoord aHeight) mFrameConstructor->EndUpdate(); } + // Initialize after nsCanvasFrame is created. + if (mAccessibleCaretEventHub) { + mAccessibleCaretEventHub->Init(this); + } + // nsAutoScriptBlocker going out of scope may have killed us too NS_ENSURE_STATE(!mHaveShutDown); @@ -2214,6 +2241,12 @@ already_AddRefed PresShell::GetSelectionCarets() const return selectionCaret.forget(); } +already_AddRefed PresShell::GetAccessibleCaretEventHub() const +{ + nsRefPtr eventHub = mAccessibleCaretEventHub; + return eventHub.forget(); +} + void PresShell::SetCaret(nsCaret *aNewCaret) { mCaret = aNewCaret; @@ -7253,6 +7286,28 @@ PresShell::HandleEvent(nsIFrame* aFrame, } } + if (AccessibleCaretEnabled()) { + // We have to target the focus window because regardless of where the + // touch goes, we want to access the copy paste manager. + nsCOMPtr window = GetFocusedDOMWindowInOurWindow(); + nsCOMPtr retargetEventDoc = + window ? window->GetExtantDoc() : nullptr; + nsCOMPtr presShell = + retargetEventDoc ? retargetEventDoc->GetShell() : nullptr; + + nsRefPtr eventHub = + presShell ? presShell->GetAccessibleCaretEventHub() : nullptr; + if (eventHub) { + *aEventStatus = eventHub->HandleEvent(aEvent); + if (*aEventStatus == nsEventStatus_eConsumeNoDefault) { + // If the event is consumed, cancel APZC panning by setting + // mMultipleActionsPrevented. + aEvent->mFlags.mMultipleActionsPrevented = true; + return NS_OK; + } + } + } + if (sPointerEventEnabled) { UpdateActivePointerState(aEvent); } diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 8e52692c28e..30f781a20d2 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -77,6 +77,8 @@ public: // Selection caret preference static bool SelectionCaretPrefEnabled(); + static bool AccessibleCaretEnabled(); + // BeforeAfterKeyboardEvent preference static bool BeforeAfterKeyboardEventEnabled(); @@ -240,6 +242,9 @@ public: virtual already_AddRefed GetSelectionCarets() const override; virtual mozilla::dom::Element* GetSelectionCaretsStartElement() const override; virtual mozilla::dom::Element* GetSelectionCaretsEndElement() const override; + + virtual already_AddRefed GetAccessibleCaretEventHub() const override; + // caret handling virtual already_AddRefed GetCaret() const override; NS_IMETHOD SetCaretEnabled(bool aInEnable) override; @@ -811,6 +816,7 @@ protected: // TouchCaret nsRefPtr mTouchCaret; nsRefPtr mSelectionCarets; + nsRefPtr mAccessibleCaretEventHub; // This timer controls painting suppression. Until it fires // or all frames are constructed, we won't paint anything but diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 26ec482479c..38bcfe77774 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -54,6 +54,9 @@ static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID); #include "TouchCaret.h" #include "SelectionCarets.h" +#include "AccessibleCaretEventHub.h" +#include "AccessibleCaretManager.h" + #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" @@ -819,6 +822,14 @@ nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter) mDomSelections[index]->AddSelectionListener(selectionCarets); } } + + nsRefPtr eventHub = mShell->GetAccessibleCaretEventHub(); + if (eventHub) { + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + if (mDomSelections[index]) { + mDomSelections[index]->AddSelectionListener(eventHub); + } + } } nsresult @@ -3225,6 +3236,12 @@ nsFrameSelection::DisconnectFromPresShell() mDomSelections[index]->RemoveSelectionListener(selectionCarets); } + nsRefPtr eventHub = mShell->GetAccessibleCaretEventHub(); + if (eventHub) { + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + mDomSelections[index]->RemoveSelectionListener(eventHub); + } + StopAutoScrollTimer(); for (int32_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) { mDomSelections[i]->Clear(nullptr); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index ea8a3ca5ca7..c9ef06a5450 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4553,6 +4553,13 @@ pref("selectioncaret.inflatesize.threshold", 40); // Selection carets will fall-back to internal LongTap detector. pref("selectioncaret.detects.longtap", true); +// New implementation to unify touch-caret and selection-carets. +pref("layout.accessiblecaret.enabled", false); + +// Timeout in milliseconds to hide the accessiblecaret under cursor mode while +// no one touches it. Set the value to 0 to disable this feature. +pref("layout.accessiblecaret.timeout_ms", 3000); + // Wakelock is disabled by default. pref("dom.wakelock.enabled", false);