mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 956032 - text selection change event is missed when selected text becomes unselected, r=tbsaunde
This commit is contained in:
parent
8150931bfa
commit
af12f68960
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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<nsISelection> mSel;
|
||||
nsRefPtr<Selection> mSel;
|
||||
int32_t mReason;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class SelectionManager;
|
||||
|
@ -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<nsISelectionPrivate> 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
|
||||
|
@ -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
|
||||
|
@ -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<Selection> 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<Selection*>(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<SelectionManager, nsISelection>
|
||||
(this, &SelectionManager::ProcessSelectionChanged, aSelection);
|
||||
nsRefPtr<SelData> selData =
|
||||
new SelData(static_cast<Selection*>(aSelection), aReason);
|
||||
document->HandleNotification<SelectionManager, SelData>
|
||||
(this, &SelectionManager::ProcessSelectionChanged, selData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::ProcessSelectionChanged(nsISelection* aSelection)
|
||||
SelectionManager::ProcessSelectionChanged(SelData* aSelData)
|
||||
{
|
||||
Selection* selection = static_cast<Selection*>(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<AccEvent> event = new AccTextSelChangeEvent(text, aSelection);
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccTextSelChangeEvent(text, selection, aSelData->mReason);
|
||||
text->Document()->FireDelayedEvent(event);
|
||||
|
||||
} else if (selection->GetType() == nsISelectionController::SELECTION_SPELLCHECK) {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=956032"
|
||||
title="Text selection change event missed when selected text becomes unselected">
|
||||
Bug 956032
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
@ -62,6 +78,8 @@
|
||||
<p id="c1_p2">paragraph</p>
|
||||
</div>
|
||||
|
||||
<textarea id="ta1">Hello world</textarea>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user