/* 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";
/* import-globals-from ../../mochitest/attributes.js */
/* import-globals-from ../../mochitest/states.js */
loadScripts(
{ name: "attributes.js", dir: MOCHITESTS_DIR },
{ name: "states.js", dir: MOCHITESTS_DIR }
);
/* import-globals-from ../../mochitest/text.js */
/**
* Test caret retrieval.
*/
addAccessibleTask(
`
`,
async function (browser, docAcc) {
const textarea = findAccessibleChildByID(docAcc, "textarea", [
nsIAccessibleText,
]);
info("textarea: Set caret offset to 0");
let focused = waitForEvent(EVENT_FOCUS, textarea);
let caretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
textarea.caretOffset = 0;
await focused;
await caretMoved;
is(textarea.caretOffset, 0, "textarea caret correct");
// Test setting caret to another line.
info("textarea: Set caret offset to 3");
caretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
textarea.caretOffset = 3;
await caretMoved;
is(textarea.caretOffset, 3, "textarea caret correct");
// Test setting caret to the end.
info("textarea: Set caret offset to 4 (end)");
caretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, textarea);
textarea.caretOffset = 4;
await caretMoved;
is(textarea.caretOffset, 4, "textarea caret correct");
const editable = findAccessibleChildByID(docAcc, "editable", [
nsIAccessibleText,
]);
focused = waitForEvent(EVENT_FOCUS, editable);
editable.takeFocus();
await focused;
const p = findAccessibleChildByID(docAcc, "p", [nsIAccessibleText]);
info("p: Set caret offset to 0");
caretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, p);
p.caretOffset = 0;
await focused;
await caretMoved;
is(p.caretOffset, 0, "p caret correct");
const link = findAccessibleChildByID(docAcc, "link", [nsIAccessibleText]);
info("link: Set caret offset to 0");
caretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, link);
link.caretOffset = 0;
await caretMoved;
is(link.caretOffset, 0, "link caret correct");
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);
/**
* Test setting the caret in a contentEditable which is aria-hidden. Arguably,
* we shouldn't fire caret events at all in this case, but really, this is just
* bad authoring and shouldn't happen. We just need to make sure that the
* offsets we do fire are at least valid so we don't trigger assertions or
* confuse clients.
*/
addAccessibleTask(
`
abcd
`,
async function testSetCaretInAriaHidden(browser, docAcc) {
info("Focusing editable");
let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc);
await invokeContentTask(browser, [], () => {
content.document.getElementById("editable").focus();
});
let evt = await moved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
is(evt.caretOffset, 0, "Caret event is for offset 0");
info("Setting caret in editable to c");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc);
await invokeContentTask(browser, [], () => {
const text = content.document.getElementById("editable").firstChild;
content.getSelection().setBaseAndExtent(text, 3, text, 3);
});
evt = await moved;
evt.QueryInterface(nsIAccessibleCaretMoveEvent);
is(evt.caretOffset, 0, "Caret event is for offset 0");
}
);
/**
* Test the caret when clicking in an empty area of a container immediately
* before/after a text input.
*/
addAccessibleTask(
`
`,
async function testEmptyNearInput(browser, docAcc) {
info("Focusing inputBeforeEmpty");
let input = findAccessibleChildByID(docAcc, "inputBeforeEmpty", [
nsIAccessibleText,
]);
let moved = waitForEvents([
[EVENT_FOCUS, input],
[EVENT_TEXT_CARET_MOVED, input],
]);
input.takeFocus();
await moved;
is(input.caretOffset, 0, "caretOffset 0");
info("Clicking at bottom of inputThenEmpty");
// BrowserTestUtils.synthesizeMouseAtPoint takes coordinates relative to the
// document.
const docX = {};
const docY = {};
docAcc.getBounds(docX, docY, {}, {});
let container = findAccessibleChildByID(docAcc, "inputThenEmpty", [
nsIAccessibleText,
]);
const containerX = {};
const containerY = {};
const containerH = {};
container.getBounds(containerX, containerY, {}, containerH);
moved = waitForEvents([
[EVENT_FOCUS, docAcc],
[EVENT_TEXT_CARET_MOVED, container],
]);
await BrowserTestUtils.synthesizeMouseAtPoint(
containerX.value - docX.value,
containerY.value + containerH.value - 1 - docY.value,
{},
docAcc.browsingContext
);
await moved;
docAcc.QueryInterface(nsIAccessibleText);
is(input.caretOffset, -1, "No caret in inputBeforeEmpty");
info("Focusing inputAfterEmpty");
input = findAccessibleChildByID(docAcc, "inputAfterEmpty", [
nsIAccessibleText,
]);
moved = waitForEvents([
[EVENT_FOCUS, input],
[EVENT_TEXT_CARET_MOVED, input],
]);
input.takeFocus();
await moved;
is(input.caretOffset, 0, "caretOffset 0");
info("Clicking at top of emptyThenInput");
container = findAccessibleChildByID(docAcc, "emptyThenInput", [
nsIAccessibleText,
]);
container.getBounds(containerX, containerY, {}, {});
// The caret event fires in the input instead of the container, but this
// isn't really important.
moved = waitForEvents([
[EVENT_FOCUS, docAcc],
[EVENT_TEXT_CARET_MOVED, input],
]);
await BrowserTestUtils.synthesizeMouseAtPoint(
containerX.value - docX.value,
containerY.value - docY.value + 1,
{},
docAcc.browsingContext
);
await moved;
is(input.caretOffset, -1, "No caret in inputAfterEmpty");
},
{ chrome: true, topLevel: true }
);
/**
* Test retrieving the caret line number.
*/
addAccessibleTask(
`
ab
cd
ef
gh
ij
`,
async function testLineNumber(browser, docAcc) {
docAcc.QueryInterface(nsIAccessibleText);
testAttrs(docAcc, { "line-number": "1" }, true);
info("Moving caret to b");
let moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc);
docAcc.caretOffset = 1;
await moved;
testAttrs(docAcc, { "line-number": "1" }, true);
info("Moving caret to c");
const blockquote = findAccessibleChildByID(docAcc, "blockquote", [
nsIAccessibleText,
]);
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, blockquote);
blockquote.caretOffset = 0;
await moved;
testAttrs(docAcc, { "line-number": "2" }, true);
info("Moving caret to d");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, blockquote);
blockquote.caretOffset = 1;
await moved;
testAttrs(docAcc, { "line-number": "2" }, true);
info("Moving caret to e");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, blockquote);
blockquote.caretOffset = 3;
await moved;
testAttrs(docAcc, { "line-number": "3" }, true);
info("Moving caret to f");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, blockquote);
blockquote.caretOffset = 4;
await moved;
testAttrs(docAcc, { "line-number": "3" }, true);
info("moving caret to g");
const p = findAccessibleChildByID(docAcc, "p", [nsIAccessibleText]);
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, p);
p.caretOffset = 0;
await moved;
testAttrs(docAcc, { "line-number": "4" }, true);
info("moving caret to h");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, p);
p.caretOffset = 1;
await moved;
testAttrs(docAcc, { "line-number": "4" }, true);
info("moving caret to i");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc);
docAcc.caretOffset = 4;
await moved;
testAttrs(docAcc, { "line-number": "5" }, true);
info("moving caret to j");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc);
docAcc.caretOffset = 5;
await moved;
testAttrs(docAcc, { "line-number": "5" }, true);
info("moving caret to end");
moved = waitForEvent(EVENT_TEXT_CARET_MOVED, docAcc);
// We end up with space at the end of the document, so use characterCount to
// ensure we really move to the end.
docAcc.caretOffset = docAcc.characterCount;
await moved;
testAttrs(docAcc, { "line-number": "5" }, true);
},
{
// Bug 2007033: This is currently only supported for LocalAccessible.
chrome: true,
topLevel: false,
contentSetup: async function contentSetup() {
content.document.designMode = "on";
},
}
);
/**
* Test setting the caret in a document which isn't focused.
*/
addAccessibleTask(
`