diff --git a/accessible/src/base/AccEvent.cpp b/accessible/src/base/AccEvent.cpp index 661a8d46d14..e28cdfecb62 100644 --- a/accessible/src/base/AccEvent.cpp +++ b/accessible/src/base/AccEvent.cpp @@ -12,6 +12,7 @@ #include "States.h" #include "nsEventStateManager.h" +#include "mozilla/Selection.h" using namespace mozilla; using namespace mozilla::a11y; @@ -126,13 +127,22 @@ AccShowEvent:: //////////////////////////////////////////////////////////////////////////////// AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget, - nsISelection* aSelection) : + Selection* aSelection, + int32_t aReason) : AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget, eAutoDetect, eCoalesceTextSelChange), - mSel(aSelection) {} + mSel(aSelection), mReason(aReason) {} AccTextSelChangeEvent::~AccTextSelChangeEvent() { } +bool +AccTextSelChangeEvent::IsCaretMoveOnly() const +{ + return mSel->GetRangeCount() == 1 && mSel->IsCollapsed() && + ((mReason & (nsISelectionListener::COLLAPSETOSTART_REASON | + nsISelectionListener::COLLAPSETOEND_REASON)) == 0); +} + //////////////////////////////////////////////////////////////////////////////// // AccSelChangeEvent //////////////////////////////////////////////////////////////////////////////// diff --git a/accessible/src/base/AccEvent.h b/accessible/src/base/AccEvent.h index 10a9e0ce9b0..15a48be201d 100644 --- a/accessible/src/base/AccEvent.h +++ b/accessible/src/base/AccEvent.h @@ -10,9 +10,10 @@ #include "mozilla/a11y/Accessible.h" -class nsISelection; - namespace mozilla { + +class Selection; + namespace a11y { class DocAccessible; @@ -366,7 +367,8 @@ private: class AccTextSelChangeEvent : public AccEvent { public: - AccTextSelChangeEvent(HyperTextAccessible* aTarget, nsISelection* aSelection); + AccTextSelChangeEvent(HyperTextAccessible* aTarget, Selection* aSelection, + int32_t aReason); virtual ~AccTextSelChangeEvent(); // AccEvent @@ -376,8 +378,16 @@ public: return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent); } + // AccTextSelChangeEvent + + /** + * Return true if the text selection change wasn't caused by pure caret move. + */ + bool IsCaretMoveOnly() const; + private: - nsCOMPtr mSel; + nsRefPtr mSel; + int32_t mReason; friend class EventQueue; friend class SelectionManager; diff --git a/accessible/src/base/Logging.cpp b/accessible/src/base/Logging.cpp index 435b79f358b..330581b16c7 100644 --- a/accessible/src/base/Logging.cpp +++ b/accessible/src/base/Logging.cpp @@ -582,7 +582,8 @@ logging::FocusDispatched(Accessible* aTarget) } void -logging::SelChange(nsISelection* aSelection, DocAccessible* aDocument) +logging::SelChange(nsISelection* aSelection, DocAccessible* aDocument, + int16_t aReason) { nsCOMPtr privSel(do_QueryInterface(aSelection)); @@ -598,8 +599,10 @@ logging::SelChange(nsISelection* aSelection, DocAccessible* aDocument) strType = "unknown"; bool isIgnored = !aDocument || !aDocument->IsContentLoaded(); - printf("\nSelection changed, selection type: %s, notification %s\n", - strType, (isIgnored ? "ignored" : "pending")); + printf("\nSelection changed, selection type: %s, notification %s, reason: %d\n", + strType, (isIgnored ? "ignored" : "pending"), aReason); + + Stack(); } void diff --git a/accessible/src/base/Logging.h b/accessible/src/base/Logging.h index 35705bd9071..09fc7767ee2 100644 --- a/accessible/src/base/Logging.h +++ b/accessible/src/base/Logging.h @@ -118,7 +118,8 @@ void FocusDispatched(Accessible* aTarget); /** * Log the selection change. */ -void SelChange(nsISelection* aSelection, DocAccessible* aDocument); +void SelChange(nsISelection* aSelection, DocAccessible* aDocument, + int16_t aReason); /** * Log the message ('title: text' format) on new line. Print the start and end diff --git a/accessible/src/base/SelectionManager.cpp b/accessible/src/base/SelectionManager.cpp index 5ea58d4bdc6..afc556b286e 100644 --- a/accessible/src/base/SelectionManager.cpp +++ b/accessible/src/base/SelectionManager.cpp @@ -14,13 +14,23 @@ #include "nsIAccessibleTypes.h" #include "nsIDOMDocument.h" #include "nsIPresShell.h" -#include "nsISelectionPrivate.h" #include "mozilla/Selection.h" #include "mozilla/dom/Element.h" using namespace mozilla; using namespace mozilla::a11y; +struct mozilla::a11y::SelData +{ + SelData(Selection* aSel, int32_t aReason) : + mSel(aSel), mReason(aReason) {} + + nsRefPtr mSel; + int16_t mReason; + + NS_INLINE_DECL_REFCOUNTING(SelData); +}; + void SelectionManager::ClearControlSelectionListener() { @@ -110,17 +120,16 @@ SelectionManager::RemoveDocSelectionListener(nsIPresShell* aPresShell) void SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent) { + // Fire selection change event if it's not pure caret-move selection change, + // i.e. the accessible has or had not collapsed selection. AccTextSelChangeEvent* event = downcast_accEvent(aEvent); - Selection* sel = static_cast(event->mSel.get()); - - // Fire selection change event if it's not pure caret-move selection change. - if (sel->GetRangeCount() != 1 || !sel->IsCollapsed()) + if (!event->IsCaretMoveOnly()) nsEventShell::FireEvent(aEvent); // Fire caret move event if there's a caret in the selection. nsINode* caretCntrNode = - nsCoreUtils::GetDOMNodeFromDOMPoint(sel->GetFocusNode(), - sel->FocusOffset()); + nsCoreUtils::GetDOMNodeFromDOMPoint(event->mSel->GetFocusNode(), + event->mSel->FocusOffset()); if (!caretCntrNode) return; @@ -150,7 +159,7 @@ SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument, #ifdef A11Y_LOG if (logging::IsEnabled(logging::eSelection)) - logging::SelChange(aSelection, document); + logging::SelChange(aSelection, document, aReason); #endif // Don't fire events until document is loaded. @@ -158,17 +167,19 @@ SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument, // Selection manager has longer lifetime than any document accessible, // so that we are guaranteed that the notification is processed before // the selection manager is destroyed. - document->HandleNotification - (this, &SelectionManager::ProcessSelectionChanged, aSelection); + nsRefPtr selData = + new SelData(static_cast(aSelection), aReason); + document->HandleNotification + (this, &SelectionManager::ProcessSelectionChanged, selData); } return NS_OK; } void -SelectionManager::ProcessSelectionChanged(nsISelection* aSelection) +SelectionManager::ProcessSelectionChanged(SelData* aSelData) { - Selection* selection = static_cast(aSelection); + Selection* selection = aSelData->mSel; if (!selection->GetPresShell()) return; @@ -176,11 +187,12 @@ SelectionManager::ProcessSelectionChanged(nsISelection* aSelection) nsINode* cntrNode = nullptr; if (range) cntrNode = range->GetCommonAncestor(); + if (!cntrNode) { cntrNode = selection->GetFrameSelection()->GetAncestorLimiter(); if (!cntrNode) { cntrNode = selection->GetPresShell()->GetDocument(); - NS_ASSERTION(selection->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(), + NS_ASSERTION(aSelData->mSel->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(), "Wrong selection container was used!"); } } @@ -192,7 +204,8 @@ SelectionManager::ProcessSelectionChanged(nsISelection* aSelection) } if (selection->GetType() == nsISelectionController::SELECTION_NORMAL) { - nsRefPtr event = new AccTextSelChangeEvent(text, aSelection); + nsRefPtr event = + new AccTextSelChangeEvent(text, selection, aSelData->mReason); text->Document()->FireDelayedEvent(event); } else if (selection->GetType() == nsISelectionController::SELECTION_SPELLCHECK) { diff --git a/accessible/src/base/SelectionManager.h b/accessible/src/base/SelectionManager.h index 7d2f5c6fff8..1fc9b28b106 100644 --- a/accessible/src/base/SelectionManager.h +++ b/accessible/src/base/SelectionManager.h @@ -38,6 +38,8 @@ class AccEvent; * selection change events. */ +struct SelData; + class SelectionManager : public nsISelectionListener { public: @@ -83,7 +85,7 @@ protected: /** * Process DOM selection change. Fire selection and caret move events. */ - void ProcessSelectionChanged(nsISelection* aSelection); + void ProcessSelectionChanged(SelData* aSelData); private: // Currently focused control. diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index 525d56d6631..9029a760590 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -1212,17 +1212,17 @@ function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs) /** * Left arrow key invoker. */ -function synthLeftKey(aNodeOrID, aCheckerOrEventSeq) +function synthLeftKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { - this.__proto__ = new synthKey(aNodeOrID, "VK_LEFT", null, aCheckerOrEventSeq); + this.__proto__ = new synthKey(aNodeOrID, "VK_LEFT", aArgs, aCheckerOrEventSeq); } /** * Right arrow key invoker. */ -function synthRightKey(aNodeOrID, aCheckerOrEventSeq) +function synthRightKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { - this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", null, aCheckerOrEventSeq); + this.__proto__ = new synthKey(aNodeOrID, "VK_RIGHT", aArgs, aCheckerOrEventSeq); } /** @@ -1764,7 +1764,12 @@ function textSelectionChecker(aID, aStartOffset, aEndOffset) this.check = function textSelectionChecker_check(aEvent) { - testTextGetSelection(aID, aStartOffset, aEndOffset, 0); + if (aStartOffset == aEndOffset) { + is(getAccessible(aID, [nsIAccessibleText]).caretOffset, aStartOffset, + "Wrong collapsed selection!"); + } else { + testTextGetSelection(aID, aStartOffset, aEndOffset, 0); + } } } diff --git a/accessible/tests/mochitest/events/test_textselchange.html b/accessible/tests/mochitest/events/test_textselchange.html index 62b9ec26392..9347f7d3c69 100644 --- a/accessible/tests/mochitest/events/test_textselchange.html +++ b/accessible/tests/mochitest/events/test_textselchange.html @@ -24,19 +24,30 @@ // gA11yEventDumpID = "eventdump"; // debug stuff //gA11yEventDumpToConsole = true; + function getOnclickSeq(aID) + { + return [ + new caretMoveChecker(0, aID), + new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID) + ]; + } + function doTests() { // test caret move events and caret offsets gQueue = new eventQueue(); - var onclickSeq = [ - new caretMoveChecker(0, "c1_p1"), - new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, "c1_p1") - ]; - gQueue.push(new synthClick("c1_p1", onclickSeq)); + gQueue.push(new synthClick("c1_p1", getOnclickSeq("c1_p1"))); gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true })); gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true })); + gQueue.push(new synthClick("ta1", getOnclickSeq("ta1"))); + gQueue.push(new synthRightKey("ta1", + new textSelectionChecker("ta1", 0, 1), + { shiftKey: true })); + gQueue.push(new synthLeftKey("ta1", + new textSelectionChecker("ta1", 0, 0))); + gQueue.invoke(); // Will call SimpleTest.finish(); } @@ -52,6 +63,11 @@ title="Text selection change event has a wrong target when selection is spanned through several objects"> Bug 762934 + + Bug 956032 +

@@ -62,6 +78,8 @@
     

paragraph

+ +
diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index ceea64cdd8e..0bed7a3b4b0 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -787,6 +787,7 @@ nsFrameSelection::MoveCaret(uint32_t aKeycode, { const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); if (anchorFocusRange) { + PostReason(nsISelectionListener::COLLAPSETOSTART_REASON); sel->Collapse(anchorFocusRange->GetStartParent(), anchorFocusRange->StartOffset()); } @@ -802,6 +803,7 @@ nsFrameSelection::MoveCaret(uint32_t aKeycode, { const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); if (anchorFocusRange) { + PostReason(nsISelectionListener::COLLAPSETOEND_REASON); sel->Collapse(anchorFocusRange->GetEndParent(), anchorFocusRange->EndOffset()); }