mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1713050 - P3: Add more granularities to AXSelectedTextChanged events. r=morgan
Differential Revision: https://phabricator.services.mozilla.com/D139747
This commit is contained in:
parent
a479c8f191
commit
ce4771430d
@ -111,7 +111,8 @@ void a11y::ProxyStateChangeEvent(RemoteAccessible* aTarget, uint64_t aState,
|
||||
}
|
||||
|
||||
void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset,
|
||||
bool aIsSelectionCollapsed) {
|
||||
bool aIsSelectionCollapsed,
|
||||
int32_t aGranularity) {
|
||||
RefPtr<SessionAccessibility> sessionAcc =
|
||||
SessionAccessibility::GetInstanceFor(aTarget);
|
||||
|
||||
|
@ -1372,7 +1372,8 @@ void a11y::ProxyStateChangeEvent(RemoteAccessible* aTarget, uint64_t aState,
|
||||
}
|
||||
|
||||
void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset,
|
||||
bool aIsSelectionCollapsed) {
|
||||
bool aIsSelectionCollapsed,
|
||||
int32_t aGranularity) {
|
||||
AtkObject* wrapper = GetWrapperFor(aTarget);
|
||||
g_signal_emit_by_name(wrapper, "text_caret_moved", aOffset);
|
||||
}
|
||||
|
@ -104,10 +104,11 @@ void ProxyStateChangeEvent(RemoteAccessible* aTarget, uint64_t aState,
|
||||
void ProxyFocusEvent(RemoteAccessible* aTarget,
|
||||
const LayoutDeviceIntRect& aCaretRect);
|
||||
void ProxyCaretMoveEvent(RemoteAccessible* aTarget,
|
||||
const LayoutDeviceIntRect& aCaretRect);
|
||||
const LayoutDeviceIntRect& aCaretRect,
|
||||
int32_t aGranularity);
|
||||
#else
|
||||
void ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset,
|
||||
bool aIsSelectionCollapsed);
|
||||
bool aIsSelectionCollapsed, int32_t aGranularity);
|
||||
#endif
|
||||
void ProxyTextChangeEvent(RemoteAccessible* aTarget, const nsString& aStr,
|
||||
int32_t aStart, uint32_t aLen, bool aIsInsert,
|
||||
|
@ -364,9 +364,9 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvCaretMoveEvent(
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
ProxyCaretMoveEvent(proxy, aCaretRect);
|
||||
ProxyCaretMoveEvent(proxy, aCaretRect, aGranularity);
|
||||
#else
|
||||
ProxyCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed);
|
||||
ProxyCaretMoveEvent(proxy, aOffset, aIsSelectionCollapsed, aGranularity);
|
||||
#endif
|
||||
|
||||
if (!nsCoreUtils::AccEventObserversExist()) {
|
||||
|
@ -226,7 +226,9 @@ nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
|
||||
int32_t caretOffset = event->GetCaretOffset();
|
||||
MOXTextMarkerDelegate* delegate =
|
||||
[MOXTextMarkerDelegate getOrCreateForDoc:aEvent->Document()];
|
||||
[delegate setCaretOffset:eventTarget at:caretOffset];
|
||||
[delegate setCaretOffset:eventTarget
|
||||
at:caretOffset
|
||||
moveGranularity:event->GetGranularity()];
|
||||
if (event->IsSelectionCollapsed()) {
|
||||
// If the selection is collapsed, invalidate our text selection cache.
|
||||
[delegate setSelectionFrom:eventTarget
|
||||
|
@ -15,6 +15,7 @@
|
||||
AXTextMarkerRangeRef mSelection;
|
||||
AXTextMarkerRef mCaret;
|
||||
AXTextMarkerRef mPrevCaret;
|
||||
int32_t mCaretMoveGranularity;
|
||||
}
|
||||
|
||||
+ (id)getOrCreateForDoc:(mozilla::a11y::Accessible*)aDoc;
|
||||
@ -30,7 +31,9 @@
|
||||
to:(mozilla::a11y::Accessible*)endContainer
|
||||
at:(int32_t)endOffset;
|
||||
|
||||
- (void)setCaretOffset:(mozilla::a11y::Accessible*)container at:(int32_t)offset;
|
||||
- (void)setCaretOffset:(mozilla::a11y::Accessible*)container
|
||||
at:(int32_t)offset
|
||||
moveGranularity:(int32_t)granularity;
|
||||
|
||||
- (NSDictionary*)selectionChangeInfo;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "mozAccessible.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsISelectionListener.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@ -52,6 +53,8 @@ static nsTHashMap<nsPtrHashKey<mozilla::a11y::Accessible>,
|
||||
mGeckoDocAccessible = aDoc;
|
||||
}
|
||||
|
||||
mCaretMoveGranularity = nsISelectionListener::NO_AMOUNT;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -76,11 +79,14 @@ static nsTHashMap<nsPtrHashKey<mozilla::a11y::Accessible>,
|
||||
}
|
||||
|
||||
- (void)setCaretOffset:(mozilla::a11y::Accessible*)container
|
||||
at:(int32_t)offset {
|
||||
at:(int32_t)offset
|
||||
moveGranularity:(int32_t)granularity {
|
||||
GeckoTextMarker caretMarker(container, offset);
|
||||
|
||||
mPrevCaret = mCaret;
|
||||
mCaret = caretMarker.CreateAXTextMarker();
|
||||
mCaretMoveGranularity = granularity;
|
||||
|
||||
CFRetain(mCaret);
|
||||
}
|
||||
|
||||
@ -146,21 +152,43 @@ static nsTHashMap<nsPtrHashKey<mozilla::a11y::Accessible>,
|
||||
}
|
||||
|
||||
bool isForward = prevCaretMarker < caretMarker;
|
||||
uint32_t deltaLength =
|
||||
GeckoTextMarkerRange(isForward ? prevCaretMarker : caretMarker,
|
||||
isForward ? caretMarker : prevCaretMarker)
|
||||
.Length();
|
||||
int direction = isForward ? AXTextSelectionDirectionNext
|
||||
: AXTextSelectionDirectionPrevious;
|
||||
|
||||
int32_t granularity = AXTextSelectionGranularityUnknown;
|
||||
switch (mCaretMoveGranularity) {
|
||||
case nsISelectionListener::CHARACTER_AMOUNT:
|
||||
case nsISelectionListener::CLUSTER_AMOUNT:
|
||||
granularity = AXTextSelectionGranularityCharacter;
|
||||
break;
|
||||
case nsISelectionListener::WORD_AMOUNT:
|
||||
case nsISelectionListener::WORDNOSPACE_AMOUNT:
|
||||
granularity = AXTextSelectionGranularityWord;
|
||||
break;
|
||||
case nsISelectionListener::LINE_AMOUNT:
|
||||
granularity = AXTextSelectionGranularityLine;
|
||||
break;
|
||||
case nsISelectionListener::BEGINLINE_AMOUNT:
|
||||
direction = AXTextSelectionDirectionBeginning;
|
||||
granularity = AXTextSelectionGranularityLine;
|
||||
break;
|
||||
case nsISelectionListener::ENDLINE_AMOUNT:
|
||||
direction = AXTextSelectionDirectionEnd;
|
||||
granularity = AXTextSelectionGranularityLine;
|
||||
break;
|
||||
case nsISelectionListener::PARAGRAPH_AMOUNT:
|
||||
granularity = AXTextSelectionGranularityParagraph;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine selection direction with marker comparison.
|
||||
// If the delta between the two markers is more than one, consider it
|
||||
// a word. Not accurate, but good enough for VO.
|
||||
[info addEntriesFromDictionary:@{
|
||||
@"AXTextSelectionDirection" : isForward
|
||||
? @(AXTextSelectionDirectionNext)
|
||||
: @(AXTextSelectionDirectionPrevious),
|
||||
@"AXTextSelectionGranularity" : deltaLength == 1
|
||||
? @(AXTextSelectionGranularityCharacter)
|
||||
: @(AXTextSelectionGranularityWord)
|
||||
@"AXTextSelectionDirection" : @(direction),
|
||||
@"AXTextSelectionGranularity" : @(granularity)
|
||||
}];
|
||||
|
||||
return info;
|
||||
|
@ -111,11 +111,11 @@ void ProxyStateChangeEvent(RemoteAccessible* aProxy, uint64_t aState,
|
||||
}
|
||||
|
||||
void ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset,
|
||||
bool aIsSelectionCollapsed) {
|
||||
bool aIsSelectionCollapsed, int32_t aGranularity) {
|
||||
mozAccessible* wrapper = GetNativeFromGeckoAccessible(aTarget);
|
||||
MOXTextMarkerDelegate* delegate =
|
||||
[MOXTextMarkerDelegate getOrCreateForDoc:aTarget->Document()];
|
||||
[delegate setCaretOffset:aTarget at:aOffset];
|
||||
[delegate setCaretOffset:aTarget at:aOffset moveGranularity:aGranularity];
|
||||
if (aIsSelectionCollapsed) {
|
||||
// If selection is collapsed, invalidate selection.
|
||||
[delegate setSelectionFrom:aTarget at:aOffset to:aTarget at:aOffset];
|
||||
|
@ -22,7 +22,8 @@ void a11y::ProxyEvent(RemoteAccessible*, uint32_t) {}
|
||||
void a11y::ProxyStateChangeEvent(RemoteAccessible*, uint64_t, bool) {}
|
||||
|
||||
void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget, int32_t aOffset,
|
||||
bool aIsSelectionCollapsed) {}
|
||||
bool aIsSelectionCollapsed,
|
||||
int32_t aGranularity) {}
|
||||
|
||||
void a11y::ProxyTextChangeEvent(RemoteAccessible*, const nsString&, int32_t,
|
||||
uint32_t, bool, bool) {}
|
||||
|
@ -224,7 +224,7 @@ async function synthKeyAndTestValueChanged(
|
||||
);
|
||||
}
|
||||
|
||||
async function focusIntoInputAndType(accDoc, inputId, innerContainerId) {
|
||||
async function focusIntoInput(accDoc, inputId, innerContainerId) {
|
||||
let selectionId = innerContainerId ? innerContainerId : inputId;
|
||||
let input = getNativeInterface(accDoc, inputId);
|
||||
ok(!input.getAttributeValue("AXFocused"), "input is not focused");
|
||||
@ -249,6 +249,11 @@ async function focusIntoInputAndType(accDoc, inputId, innerContainerId) {
|
||||
]);
|
||||
input.setAttributeValue("AXFocused", true);
|
||||
await events;
|
||||
}
|
||||
|
||||
async function focusIntoInputAndType(accDoc, inputId, innerContainerId) {
|
||||
let selectionId = innerContainerId ? innerContainerId : inputId;
|
||||
await focusIntoInput(accDoc, inputId, innerContainerId);
|
||||
|
||||
async function testTextInput(
|
||||
synthKey,
|
||||
@ -337,14 +342,14 @@ async function focusIntoInputAndType(accDoc, inputId, innerContainerId) {
|
||||
{ AXTextStateChangeType: AXTextStateChangeTypeSelectionMove }
|
||||
);
|
||||
await synthKeyAndTestSelectionChanged(
|
||||
"KEY_Home",
|
||||
{ shiftKey: true },
|
||||
"KEY_ArrowLeft",
|
||||
{ shiftKey: true, metaKey: true },
|
||||
selectionId,
|
||||
"hello ",
|
||||
{
|
||||
AXTextStateChangeType: AXTextStateChangeTypeSelectionExtend,
|
||||
AXTextSelectionDirection: AXTextSelectionDirectionPrevious,
|
||||
AXTextSelectionGranularity: AXTextSelectionGranularityWord,
|
||||
AXTextSelectionDirection: AXTextSelectionDirectionBeginning,
|
||||
AXTextSelectionGranularity: AXTextSelectionGranularityLine,
|
||||
}
|
||||
);
|
||||
await synthKeyAndTestSelectionChanged(
|
||||
@ -372,7 +377,8 @@ addAccessibleTask(
|
||||
`<a href="#">link</a> <input id="input">`,
|
||||
async (browser, accDoc) => {
|
||||
await focusIntoInputAndType(accDoc, "input");
|
||||
}
|
||||
},
|
||||
{ topLevel: true, iframe: true, remoteIframe: true }
|
||||
);
|
||||
|
||||
// Test content editable
|
||||
@ -390,15 +396,6 @@ addAccessibleTask(
|
||||
}
|
||||
);
|
||||
|
||||
// Test text input in iframe
|
||||
addAccessibleTask(
|
||||
`<a href="#">link</a> <input id="input">`,
|
||||
async (browser, accDoc) => {
|
||||
await focusIntoInputAndType(accDoc, "input");
|
||||
},
|
||||
{ iframe: true }
|
||||
);
|
||||
|
||||
// Test input that gets role::EDITCOMBOBOX
|
||||
addAccessibleTask(`<input type="text" id="box">`, async (browser, accDoc) => {
|
||||
const box = getNativeInterface(accDoc, "box");
|
||||
@ -410,3 +407,48 @@ addAccessibleTask(`<input type="text" id="box">`, async (browser, accDoc) => {
|
||||
);
|
||||
await focusIntoInputAndType(accDoc, "box");
|
||||
});
|
||||
|
||||
// Test multiline caret control in a text area
|
||||
addAccessibleTask(
|
||||
`<textarea id="input" cols="15">one two three four five six seven eight</textarea>`,
|
||||
async (browser, accDoc) => {
|
||||
await focusIntoInput(accDoc, "input");
|
||||
|
||||
await synthKeyAndTestSelectionChanged("KEY_ArrowRight", null, "input", "", {
|
||||
AXTextStateChangeType: AXTextStateChangeTypeSelectionMove,
|
||||
AXTextSelectionDirection: AXTextSelectionDirectionNext,
|
||||
AXTextSelectionGranularity: AXTextSelectionGranularityCharacter,
|
||||
});
|
||||
|
||||
await synthKeyAndTestSelectionChanged("KEY_ArrowDown", null, "input", "", {
|
||||
AXTextStateChangeType: AXTextStateChangeTypeSelectionMove,
|
||||
AXTextSelectionDirection: AXTextSelectionDirectionNext,
|
||||
AXTextSelectionGranularity: AXTextSelectionGranularityLine,
|
||||
});
|
||||
|
||||
await synthKeyAndTestSelectionChanged(
|
||||
"KEY_ArrowLeft",
|
||||
{ metaKey: true },
|
||||
"input",
|
||||
"",
|
||||
{
|
||||
AXTextStateChangeType: AXTextStateChangeTypeSelectionMove,
|
||||
AXTextSelectionDirection: AXTextSelectionDirectionBeginning,
|
||||
AXTextSelectionGranularity: AXTextSelectionGranularityLine,
|
||||
}
|
||||
);
|
||||
|
||||
await synthKeyAndTestSelectionChanged(
|
||||
"KEY_ArrowRight",
|
||||
{ metaKey: true },
|
||||
"input",
|
||||
"",
|
||||
{
|
||||
AXTextStateChangeType: AXTextStateChangeTypeSelectionMove,
|
||||
AXTextSelectionDirection: AXTextSelectionDirectionEnd,
|
||||
AXTextSelectionGranularity: AXTextSelectionGranularityLine,
|
||||
}
|
||||
);
|
||||
},
|
||||
{ topLevel: true, iframe: true, remoteIframe: true }
|
||||
);
|
||||
|
@ -10,7 +10,9 @@
|
||||
AXTextStateChangeTypeSelectionExtend, AXTextSelectionDirectionUnknown,
|
||||
AXTextSelectionDirectionPrevious, AXTextSelectionDirectionNext,
|
||||
AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown,
|
||||
AXTextSelectionGranularityCharacter, AXTextSelectionGranularityWord */
|
||||
AXTextSelectionDirectionBeginning, AXTextSelectionDirectionEnd,
|
||||
AXTextSelectionGranularityCharacter, AXTextSelectionGranularityWord,
|
||||
AXTextSelectionGranularityLine */
|
||||
|
||||
// Load the shared-head file first.
|
||||
/* import-globals-from ../shared-head.js */
|
||||
@ -37,6 +39,8 @@ const AXTextEditTypeTyping = 3;
|
||||
|
||||
// AXTextSelectionDirection enum values
|
||||
const AXTextSelectionDirectionUnknown = 0;
|
||||
const AXTextSelectionDirectionBeginning = 1;
|
||||
const AXTextSelectionDirectionEnd = 2;
|
||||
const AXTextSelectionDirectionPrevious = 3;
|
||||
const AXTextSelectionDirectionNext = 4;
|
||||
const AXTextSelectionDirectionDiscontiguous = 5;
|
||||
@ -45,6 +49,7 @@ const AXTextSelectionDirectionDiscontiguous = 5;
|
||||
const AXTextSelectionGranularityUnknown = 0;
|
||||
const AXTextSelectionGranularityCharacter = 1;
|
||||
const AXTextSelectionGranularityWord = 2;
|
||||
const AXTextSelectionGranularityLine = 3;
|
||||
|
||||
function getNativeInterface(accDoc, id) {
|
||||
return findAccessibleChildByID(accDoc, id).nativeInterface.QueryInterface(
|
||||
|
@ -172,7 +172,8 @@ void a11y::ProxyFocusEvent(RemoteAccessible* aTarget,
|
||||
}
|
||||
|
||||
void a11y::ProxyCaretMoveEvent(RemoteAccessible* aTarget,
|
||||
const LayoutDeviceIntRect& aCaretRect) {
|
||||
const LayoutDeviceIntRect& aCaretRect,
|
||||
int32_t aGranularity) {
|
||||
AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect);
|
||||
MsaaAccessible::FireWinEvent(aTarget,
|
||||
nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED);
|
||||
|
Loading…
Reference in New Issue
Block a user