/* 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";
/**
* Test simple text selection
*/
addAccessibleTask(`<p id=
"p">Hello World</p>`, async (browser, accDoc) => {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
let startMarker = macDoc.getAttributeValue(
"AXStartTextMarker");
let endMarker = macDoc.getAttributeValue(
"AXEndTextMarker");
let range = macDoc.getParameterizedAttributeValue(
"AXTextMarkerRangeForUnorderedTextMarkers",
[startMarker, endMarker]
);
is(stringForRange(macDoc, range),
"Hello World");
let evt = waitForMacEventWithInfo(
"AXSelectedTextChanged", (elem, info) => {
return (
!info.AXTextStateSync &&
info.AXTextStateChangeType == AXTextStateChangeTypeSelectionExtend &&
elem.getAttributeValue(
"AXRole") ==
"AXWebArea"
);
});
await SpecialPowers.spawn(browser, [], () => {
let p = content.document.getElementById(
"p");
let r =
new content.Range();
r.setStart(p.firstChild,
1);
r.setEnd(p.firstChild,
8);
let s = content.getSelection();
s.addRange(r);
});
await evt;
range = macDoc.getAttributeValue(
"AXSelectedTextMarkerRange");
is(stringForRange(macDoc, range),
"ello Wo");
let firstWordRange = macDoc.getParameterizedAttributeValue(
"AXRightWordTextMarkerRangeForTextMarker",
startMarker
);
is(stringForRange(macDoc, firstWordRange),
"Hello");
evt = waitForMacEventWithInfo(
"AXSelectedTextChanged", (elem, info) => {
return (
!info.AXTextStateSync &&
info.AXTextStateChangeType == AXTextStateChangeTypeSelectionExtend &&
elem.getAttributeValue(
"AXRole") ==
"AXWebArea"
);
});
macDoc.setAttributeValue(
"AXSelectedTextMarkerRange", firstWordRange);
await evt;
range = macDoc.getAttributeValue(
"AXSelectedTextMarkerRange");
is(stringForRange(macDoc, range),
"Hello");
// Collapse selection
evt = waitForMacEventWithInfo(
"AXSelectedTextChanged", (elem, info) => {
return (
info.AXTextStateSync &&
info.AXTextStateChangeType == AXTextStateChangeTypeSelectionMove &&
elem.getAttributeValue(
"AXRole") ==
"AXWebArea"
);
});
await SpecialPowers.spawn(browser, [], () => {
let s = content.getSelection();
s.collapseToEnd();
});
await evt;
});
/**
* Test text selection events caused by focus change
*/
addAccessibleTask(
`<p>
Hello <a href=
"#" id=
"link">World</a>,
I <a href=
"#" style=
"user-select: none;" id=
"unselectable_link">love</a>
<button id=
"button">you</button></p>`,
async browser => {
// Selection only moves to focused <a> if caret browsing is enabled
await SpecialPowers.pushPrefEnv({
set: [[
"accessibility.browsewithcaret",
true]],
});
// Set up an AXSelectedTextChanged listener here. It will get resolved
// on the first non-root event it encounters, so if we test its data at the end
// of this test it will show us the first text-selectable object that was focused,
// which is "link".
let selTextChanged = waitForMacEvent(
"AXSelectedTextChanged",
e => e.getAttributeValue(
"AXDOMIdentifier") !=
"body"
);
let focusChanged = waitForMacEvent(
"AXFocusedUIElementChanged");
await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById(
"unselectable_link").focus();
});
let focusChangedTarget = await focusChanged;
is(
focusChangedTarget.getAttributeValue(
"AXDOMIdentifier"),
"unselectable_link",
"Correct event target"
);
focusChanged = waitForMacEvent(
"AXFocusedUIElementChanged");
await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById(
"button").focus();
});
focusChangedTarget = await focusChanged;
is(
focusChangedTarget.getAttributeValue(
"AXDOMIdentifier"),
"button",
"Correct event target"
);
focusChanged = waitForMacEvent(
"AXFocusedUIElementChanged");
await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById(
"link").focus();
});
focusChangedTarget = await focusChanged;
is(
focusChangedTarget.getAttributeValue(
"AXDOMIdentifier"),
"link",
"Correct event target"
);
let selTextChangedTarget = await selTextChanged;
is(
selTextChangedTarget.getAttributeValue(
"AXDOMIdentifier"),
"link",
"Correct event target"
);
}
);
/**
* Test text selection with focus change
*/
addAccessibleTask(
`<p id=
"p">Hello <input id=
"input"></p>`,
async (browser, accDoc) => {
let macDoc = accDoc.nativeInterface.QueryInterface(
Ci.nsIAccessibleMacInterface
);
let evt = waitForMacEventWithInfo(
"AXSelectedTextChanged", (elem, info) => {
return (
!info.AXTextStateSync &&
info.AXTextStateChangeType == AXTextStateChangeTypeSelectionExtend &&
elem.getAttributeValue(
"AXRole") ==
"AXWebArea"
);
});
await SpecialPowers.spawn(browser, [], () => {
let p = content.document.getElementById(
"p");
let r =
new content.Range();
r.setStart(p.firstChild,
1);
r.setEnd(p.firstChild,
3);
let s = content.getSelection();
s.addRange(r);
});
await evt;
let range = macDoc.getAttributeValue(
"AXSelectedTextMarkerRange");
is(stringForRange(macDoc, range),
"el");
let events = Promise.all([
waitForMacEvent(
"AXFocusedUIElementChanged"),
waitForMacEventWithInfo(
"AXSelectedTextChanged"),
]);
await SpecialPowers.spawn(browser, [], () => {
content.document.getElementById(
"input").focus();
});
let [, { data }] = await events;
ok(
data.AXTextSelectionChangedFocus,
"have AXTextSelectionChangedFocus in event info"
);
ok(!data.AXTextStateSync,
"no AXTextStateSync in editables");
is(
data.AXTextSelectionDirection,
AXTextSelectionDirectionDiscontiguous,
"discontigous direction"
);
}
);
/**
* Verify setting AXSelectedTextRange on a text field sets DOM focus on the field
* for both collapsed and non-collapsed ranges.
*/
addAccessibleTask(
`<p><input id=
"one" value=
"goodbye"> Hello <input id=
"two" value=
"world"></p>`,
async (browser, accDoc) => {
let inputIface = getNativeInterface(accDoc,
"two");
is(
inputIface.getAttributeValue(
"AXFocused"),
0,
"second input is not focused"
);
let focusChanged = waitForMacEvent(
"AXFocusedUIElementChanged",
iface => iface.getAttributeValue(
"AXDOMIdentifier") ==
"two"
);
inputIface.setAttributeValue(
"AXSelectedTextRange", NSRange(
0,
5));
await focusChanged;
is(inputIface.getAttributeValue(
"AXFocused"),
1,
"second input is focused");
// now move focus to the other input via setting AXSelectedTextRange
// with a collapsed range value
inputIface = getNativeInterface(accDoc,
"one");
focusChanged = waitForMacEvent(
"AXFocusedUIElementChanged",
iface => iface.getAttributeValue(
"AXDOMIdentifier") ==
"one"
);
inputIface.setAttributeValue(
"AXSelectedTextRange", NSRange(
0,
0));
await focusChanged;
is(inputIface.getAttributeValue(
"AXFocused"),
1,
"first input is focused");
}
);
/**
* Verify setting AXSelectedTextMarkerRange does not move DOM focus away
* from a link that was already focused.
*/
addAccessibleTask(
`<p id=
"p">Hello <a href=
"#" id=
"link">World</a> more text</p>`,
async (browser, accDoc) => {
const p = getNativeInterface(accDoc,
"p");
const link = getNativeInterface(accDoc,
"link");
// focus the link
const focusChanged = waitForMacEvent(
"AXFocusedUIElementChanged",
iface => iface.getAttributeValue(
"AXDOMIdentifier") ==
"link"
);
link.setAttributeValue(
"AXFocused",
true);
await focusChanged;
is(link.getAttributeValue(
"AXFocused"),
1,
"link is focused");
// set selection to the beginning of the paragraph via collapsed
// selection range
const startMarker = p.getAttributeValue(
"AXStartTextMarker");
const startRange = p.getParameterizedAttributeValue(
"AXTextMarkerRangeForUnorderedTextMarkers",
[startMarker, startMarker]
);
const selChanged = waitForMacEventWithInfo(
"AXSelectedTextChanged",
(elem, info) =>
info.AXTextStateChangeType == AXTextStateChangeTypeSelectionMove
);
p.setAttributeValue(
"AXSelectedTextMarkerRange", startRange);
await selChanged;
is(
link.getAttributeValue(
"AXFocused"),
1,
"link should remain focused after selection moves elsewhere"
);
}
);