Bug 1713050 - P2: Add granularity to a11y caret move events. r=morgan

Differential Revision: https://phabricator.services.mozilla.com/D139746
This commit is contained in:
Eitan Isaacson 2022-03-16 05:56:25 +00:00
Родитель d4418e9378
Коммит a479c8f191
13 изменённых файлов: 167 добавлений и 33 удалений

Просмотреть файл

@ -127,11 +127,13 @@ AccShowEvent::AccShowEvent(LocalAccessible* aTarget)
AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget,
dom::Selection* aSelection,
int32_t aReason)
int32_t aReason,
int32_t aGranularity)
: AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget,
eAutoDetect, eCoalesceTextSelChange),
mSel(aSelection),
mReason(aReason) {}
mReason(aReason),
mGranularity(aGranularity) {}
AccTextSelChangeEvent::~AccTextSelChangeEvent() {}
@ -246,7 +248,8 @@ already_AddRefed<nsIAccessibleEvent> a11y::MakeXPCEvent(AccEvent* aEvent) {
AccCaretMoveEvent* cm = downcast_accEvent(aEvent);
xpEvent = new xpcAccCaretMoveEvent(
type, ToXPC(acc), ToXPCDocument(doc), node, fromUser,
cm->GetCaretOffset(), cm->IsSelectionCollapsed(), cm->IsAtEndOfLine());
cm->GetCaretOffset(), cm->IsSelectionCollapsed(), cm->IsAtEndOfLine(),
cm->GetGranularity());
return xpEvent.forget();
}

Просмотреть файл

@ -348,12 +348,14 @@ class AccCaretMoveEvent : public AccEvent {
public:
AccCaretMoveEvent(LocalAccessible* aAccessible, int32_t aCaretOffset,
bool aIsSelectionCollapsed, bool aIsAtEndOfLine,
int32_t aGranularity,
EIsFromUserInput aIsFromUserInput = eAutoDetect)
: AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible,
aIsFromUserInput),
mCaretOffset(aCaretOffset),
mIsSelectionCollapsed(aIsSelectionCollapsed),
mIsAtEndOfLine(aIsAtEndOfLine) {}
mIsAtEndOfLine(aIsAtEndOfLine),
mGranularity(aGranularity) {}
virtual ~AccCaretMoveEvent() {}
// AccEvent
@ -368,11 +370,14 @@ class AccCaretMoveEvent : public AccEvent {
bool IsSelectionCollapsed() const { return mIsSelectionCollapsed; }
bool IsAtEndOfLine() { return mIsAtEndOfLine; }
int32_t GetGranularity() const { return mGranularity; }
private:
int32_t mCaretOffset;
bool mIsSelectionCollapsed;
bool mIsAtEndOfLine;
int32_t mGranularity;
};
/**
@ -381,7 +386,8 @@ class AccCaretMoveEvent : public AccEvent {
class AccTextSelChangeEvent : public AccEvent {
public:
AccTextSelChangeEvent(HyperTextAccessible* aTarget,
dom::Selection* aSelection, int32_t aReason);
dom::Selection* aSelection, int32_t aReason,
int32_t aGranularity);
virtual ~AccTextSelChangeEvent();
// AccEvent
@ -397,6 +403,8 @@ class AccTextSelChangeEvent : public AccEvent {
*/
bool IsCaretMoveOnly() const;
int32_t GetGranularity() const { return mGranularity; }
/**
* Return selection ranges in document/control.
*/
@ -405,6 +413,7 @@ class AccTextSelChangeEvent : public AccEvent {
private:
RefPtr<dom::Selection> mSel;
int32_t mReason;
int32_t mGranularity;
friend class EventQueue;
friend class SelectionManager;

Просмотреть файл

@ -24,10 +24,12 @@ using namespace mozilla::a11y;
using mozilla::dom::Selection;
struct mozilla::a11y::SelData final {
SelData(Selection* aSel, int32_t aReason) : mSel(aSel), mReason(aReason) {}
SelData(Selection* aSel, int32_t aReason, int32_t aGranularity)
: mSel(aSel), mReason(aReason), mGranularity(aGranularity) {}
RefPtr<Selection> mSel;
int16_t mReason;
int32_t mGranularity;
NS_INLINE_DECL_REFCOUNTING(SelData)
@ -148,9 +150,10 @@ void SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent) {
selection->FocusOffset());
mAccWithCaret = caretCntr;
if (mCaretOffset != -1) {
RefPtr<AccCaretMoveEvent> caretMoveEvent = new AccCaretMoveEvent(
caretCntr, mCaretOffset, selection->IsCollapsed(),
caretCntr->IsCaretAtEndOfLine(), aEvent->FromUserInput());
RefPtr<AccCaretMoveEvent> caretMoveEvent =
new AccCaretMoveEvent(caretCntr, mCaretOffset, selection->IsCollapsed(),
caretCntr->IsCaretAtEndOfLine(),
event->GetGranularity(), aEvent->FromUserInput());
nsEventShell::FireEvent(caretMoveEvent);
}
}
@ -175,7 +178,7 @@ SelectionManager::NotifySelectionChanged(dom::Document* aDocument,
// 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.
RefPtr<SelData> selData = new SelData(aSelection, aReason);
RefPtr<SelData> selData = new SelData(aSelection, aReason, aAmount);
document->HandleNotification<SelectionManager, SelData>(
this, &SelectionManager::ProcessSelectionChanged, selData);
}
@ -211,8 +214,8 @@ void SelectionManager::ProcessSelectionChanged(SelData* aSelData) {
}
if (selection->GetType() == SelectionType::eNormal) {
RefPtr<AccEvent> event =
new AccTextSelChangeEvent(text, selection, aSelData->mReason);
RefPtr<AccEvent> event = new AccTextSelChangeEvent(
text, selection, aSelData->mReason, aSelData->mGranularity);
text->Document()->FireDelayedEvent(event);
} else if (selection->GetType() == SelectionType::eSpellCheck) {

Просмотреть файл

@ -934,9 +934,9 @@ nsresult LocalAccessible::HandleAccEvent(AccEvent* aEvent) {
}
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
AccCaretMoveEvent* event = downcast_accEvent(aEvent);
ipcDoc->SendCaretMoveEvent(id, event->GetCaretOffset(),
event->IsSelectionCollapsed(),
event->IsAtEndOfLine());
ipcDoc->SendCaretMoveEvent(
id, event->GetCaretOffset(), event->IsSelectionCollapsed(),
event->IsAtEndOfLine(), event->GetGranularity());
break;
}
case nsIAccessibleEvent::EVENT_TEXT_INSERTED:

Просмотреть файл

@ -25,4 +25,9 @@ interface nsIAccessibleCaretMoveEvent: nsIAccessibleEvent
* Return true if the caret is at the end of a line.
*/
readonly attribute bool isAtEndOfLine;
/**
* Return caret move granularity.
*/
readonly attribute long granularity;
};

Просмотреть файл

@ -341,7 +341,7 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
const LayoutDeviceIntRect& aCaretRect,
#endif // defined (XP_WIN)
const int32_t& aOffset, const bool& aIsSelectionCollapsed,
const bool& aIsAtEndOfLine) {
const bool& aIsAtEndOfLine, const int32_t& aGranularity) {
if (mShutdown) {
return IPC_OK();
}
@ -378,9 +378,9 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
nsINode* node = nullptr;
bool fromUser = true; // XXX fix me
uint32_t type = nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED;
RefPtr<xpcAccCaretMoveEvent> event =
new xpcAccCaretMoveEvent(type, xpcAcc, doc, node, fromUser, aOffset,
aIsSelectionCollapsed, aIsAtEndOfLine);
RefPtr<xpcAccCaretMoveEvent> event = new xpcAccCaretMoveEvent(
type, xpcAcc, doc, node, fromUser, aOffset, aIsSelectionCollapsed,
aIsAtEndOfLine, aGranularity);
nsCoreUtils::DispatchAccEvent(std::move(event));
return IPC_OK();

Просмотреть файл

@ -108,7 +108,7 @@ class DocAccessibleParent : public RemoteAccessible,
const LayoutDeviceIntRect& aCaretRect,
#endif
const int32_t& aOffset, const bool& aIsSelectionCollapsed,
const bool& aIsAtEndOfLine) final;
const bool& aIsAtEndOfLine, const int32_t& aGranularity) final;
virtual mozilla::ipc::IPCResult RecvTextChangeEvent(
const uint64_t& aID, const nsString& aStr, const int32_t& aStart,

Просмотреть файл

@ -99,7 +99,8 @@ parent:
async HideEvent(uint64_t aRootID, bool aFromUser);
async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
async CaretMoveEvent(uint64_t aID, int32_t aOffset,
bool aIsSelectionCollapsed, bool aIsAtEndOfLine);
bool aIsSelectionCollapsed, bool aIsAtEndOfLine,
int32_t aGranularity);
async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
bool aIsInsert, bool aFromUser);
async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType);

Просмотреть файл

@ -200,22 +200,26 @@ bool DocAccessibleChild::SendFocusEvent(const uint64_t& aID,
bool DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
const int32_t& aOffset,
const bool& aIsSelectionCollapsed,
const bool& aIsAtEndOfLine) {
const bool& aIsAtEndOfLine,
const int32_t& aGranularity) {
return SendCaretMoveEvent(aID, GetCaretRectFor(aID), aOffset,
aIsSelectionCollapsed, aIsAtEndOfLine);
aIsSelectionCollapsed, aIsAtEndOfLine,
aGranularity);
}
bool DocAccessibleChild::SendCaretMoveEvent(
const uint64_t& aID, const LayoutDeviceIntRect& aCaretRect,
const int32_t& aOffset, const bool& aIsSelectionCollapsed,
const bool& aIsAtEndOfLine) {
const bool& aIsAtEndOfLine, const int32_t& aGranularity) {
if (IsConstructedInParentProcess()) {
return PDocAccessibleChild::SendCaretMoveEvent(
aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine);
aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine,
aGranularity);
}
PushDeferredEvent(MakeUnique<SerializedCaretMove>(
this, aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine));
this, aID, aCaretRect, aOffset, aIsSelectionCollapsed, aIsAtEndOfLine,
aGranularity));
return true;
}

Просмотреть файл

@ -52,12 +52,14 @@ class DocAccessibleChild : public DocAccessibleChildBase {
const bool& aEnabled);
bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset,
const bool& aIsSelectionCollapsed,
const bool& aIsAtEndOfLine);
const bool& aIsAtEndOfLine,
const int32_t& aGranularity);
bool SendCaretMoveEvent(const uint64_t& aID,
const LayoutDeviceIntRect& aCaretRect,
const int32_t& aOffset,
const bool& aIsSelectionCollapsed,
const bool& aIsAtEndOfLine);
const bool& aIsAtEndOfLine,
const int32_t& aGranularity);
bool SendFocusEvent(const uint64_t& aID);
bool SendFocusEvent(const uint64_t& aID,
const LayoutDeviceIntRect& aCaretRect);
@ -175,17 +177,20 @@ class DocAccessibleChild : public DocAccessibleChildBase {
struct SerializedCaretMove final : public DeferredEvent {
SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID,
const LayoutDeviceIntRect& aCaretRect, int32_t aOffset,
bool aIsSelectionCollapsed, bool aIsAtEndOfLine)
bool aIsSelectionCollapsed, bool aIsAtEndOfLine,
int32_t aGranularity)
: DeferredEvent(aTarget),
mID(aID),
mCaretRect(aCaretRect),
mOffset(aOffset),
mIsSelectionCollapsed(aIsSelectionCollapsed),
mIsAtEndOfLine(aIsAtEndOfLine) {}
mIsAtEndOfLine(aIsAtEndOfLine),
mGranularity(aGranularity) {}
void Dispatch(DocAccessibleChild* aIPCDoc) override {
Unused << aIPCDoc->SendCaretMoveEvent(
mID, mCaretRect, mOffset, mIsSelectionCollapsed, mIsAtEndOfLine);
Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset,
mIsSelectionCollapsed,
mIsAtEndOfLine, mGranularity);
}
uint64_t mID;
@ -193,6 +198,7 @@ class DocAccessibleChild : public DocAccessibleChildBase {
int32_t mOffset;
bool mIsSelectionCollapsed;
bool mIsAtEndOfLine;
int32_t mGranularity;
};
struct SerializedFocus final : public DeferredEvent {

Просмотреть файл

@ -70,7 +70,7 @@ parent:
async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
async CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect,
int32_t aOffset, bool aIsAtEndOfLine,
bool aIsSelectionCollapsed);
bool aIsSelectionCollapsed, int32_t aGranularity);
async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
bool aIsInsert, bool aFromUser);
sync SyncTextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart,

Просмотреть файл

@ -8,6 +8,7 @@ support-files =
environment =
A11YLOG=doclifecycle,events,notifications
[browser_test_caret_move_granularity.js]
[browser_test_docload.js]
skip-if = e10s
[browser_test_scrolling.js]

Просмотреть файл

@ -0,0 +1,102 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const CLUSTER_AMOUNT = Ci.nsISelectionListener.CLUSTER_AMOUNT;
const WORD_AMOUNT = Ci.nsISelectionListener.WORD_AMOUNT;
const LINE_AMOUNT = Ci.nsISelectionListener.LINE_AMOUNT;
const BEGINLINE_AMOUNT = Ci.nsISelectionListener.BEGINLINE_AMOUNT;
const ENDLINE_AMOUNT = Ci.nsISelectionListener.ENDLINE_AMOUNT;
const isMac = AppConstants.platform == "macosx";
function matchCaretMoveEvent(id, caretOffset) {
return evt => {
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
return (
getAccessibleDOMNodeID(evt.accessible) == id &&
evt.caretOffset == caretOffset
);
};
}
addAccessibleTask(
`<textarea id="textarea" style="scrollbar-width: none;" cols="15">` +
`one two three four five six seven eight` +
`</textarea>`,
async function(browser, accDoc) {
const textarea = findAccessibleChildByID(accDoc, "textarea");
let caretMoved = waitForEvent(
EVENT_TEXT_CARET_MOVED,
matchCaretMoveEvent("textarea", 0)
);
textarea.takeFocus();
let evt = await caretMoved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
ok(!evt.isAtEndOfLine, "Caret is not at end of line");
caretMoved = waitForEvent(
EVENT_TEXT_CARET_MOVED,
matchCaretMoveEvent("textarea", 1)
);
EventUtils.synthesizeKey("KEY_ArrowRight");
evt = await caretMoved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
ok(!evt.isAtEndOfLine, "Caret is not at end of line");
is(evt.granularity, CLUSTER_AMOUNT, "Caret moved by cluster");
caretMoved = waitForEvent(
EVENT_TEXT_CARET_MOVED,
matchCaretMoveEvent("textarea", 15)
);
EventUtils.synthesizeKey("KEY_ArrowDown");
evt = await caretMoved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
todo(!evt.isAtEndOfLine, "Caret is not at end of line");
is(evt.granularity, LINE_AMOUNT, "Caret moved by line");
caretMoved = waitForEvent(
EVENT_TEXT_CARET_MOVED,
matchCaretMoveEvent("textarea", 14)
);
if (isMac) {
EventUtils.synthesizeKey("KEY_ArrowLeft", { metaKey: true });
} else {
EventUtils.synthesizeKey("KEY_Home");
}
evt = await caretMoved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
ok(!evt.isAtEndOfLine, "Caret is not at end of line");
is(evt.granularity, BEGINLINE_AMOUNT, "Caret moved to line start");
caretMoved = waitForEvent(
EVENT_TEXT_CARET_MOVED,
matchCaretMoveEvent("textarea", 28)
);
if (isMac) {
EventUtils.synthesizeKey("KEY_ArrowRight", { metaKey: true });
} else {
EventUtils.synthesizeKey("KEY_End");
}
evt = await caretMoved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
ok(evt.isAtEndOfLine, "Caret is at end of line");
is(evt.granularity, ENDLINE_AMOUNT, "Caret moved to line end");
caretMoved = waitForEvent(
EVENT_TEXT_CARET_MOVED,
matchCaretMoveEvent("textarea", 24)
);
if (isMac) {
EventUtils.synthesizeKey("KEY_ArrowLeft", { altKey: true });
} else {
EventUtils.synthesizeKey("KEY_ArrowLeft", { ctrlKey: true });
}
evt = await caretMoved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
ok(!evt.isAtEndOfLine, "Caret is not at end of line");
is(evt.granularity, WORD_AMOUNT, "Caret moved by word");
}
);