summaryrefslogtreecommitdiffstats
path: root/accessible/tests/browser/events
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/browser/events')
-rw-r--r--accessible/tests/browser/events/browser.toml33
-rw-r--r--accessible/tests/browser/events/browser_alert.js46
-rw-r--r--accessible/tests/browser/events/browser_test_A11yUtils_announce.js57
-rw-r--r--accessible/tests/browser/events/browser_test_caret_move_granularity.js102
-rw-r--r--accessible/tests/browser/events/browser_test_docload.js128
-rw-r--r--accessible/tests/browser/events/browser_test_focus_browserui.js57
-rw-r--r--accessible/tests/browser/events/browser_test_focus_dialog.js76
-rw-r--r--accessible/tests/browser/events/browser_test_focus_urlbar.js438
-rw-r--r--accessible/tests/browser/events/browser_test_panel.js54
-rw-r--r--accessible/tests/browser/events/browser_test_scrolling.js153
-rw-r--r--accessible/tests/browser/events/browser_test_selection_urlbar.js61
-rw-r--r--accessible/tests/browser/events/browser_test_textcaret.js58
-rw-r--r--accessible/tests/browser/events/head.js18
13 files changed, 1281 insertions, 0 deletions
diff --git a/accessible/tests/browser/events/browser.toml b/accessible/tests/browser/events/browser.toml
new file mode 100644
index 0000000000..7ec3c3621a
--- /dev/null
+++ b/accessible/tests/browser/events/browser.toml
@@ -0,0 +1,33 @@
+[DEFAULT]
+subsuite = "a11y"
+support-files = [
+ "head.js",
+ "!/accessible/tests/browser/shared-head.js",
+ "!/accessible/tests/mochitest/*.js",
+ "!/accessible/tests/browser/*.jsm",
+]
+prefs = ["javascript.options.asyncstack_capture_debuggee_only=false"]
+
+["browser_alert.js"]
+
+["browser_test_A11yUtils_announce.js"]
+
+["browser_test_caret_move_granularity.js"]
+
+["browser_test_docload.js"]
+skip-if = ["true"]
+
+["browser_test_focus_browserui.js"]
+
+["browser_test_focus_dialog.js"]
+
+["browser_test_focus_urlbar.js"]
+skip-if = ["os == 'win'"] # Bug 1818994
+
+["browser_test_panel.js"]
+
+["browser_test_scrolling.js"]
+
+["browser_test_selection_urlbar.js"]
+
+["browser_test_textcaret.js"]
diff --git a/accessible/tests/browser/events/browser_alert.js b/accessible/tests/browser/events/browser_alert.js
new file mode 100644
index 0000000000..f35a602fa3
--- /dev/null
+++ b/accessible/tests/browser/events/browser_alert.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test that alert events aren't fired when reflow happens but no actual
+ * insertion occurs.
+ */
+addAccessibleTask(
+ `
+<div id="alert" role="alert">
+ <div id="content" hidden>content</div>
+</div>
+ `,
+ async function (browser, docAcc) {
+ const alert = findAccessibleChildByID(docAcc, "alert");
+ info("Showing content");
+ await contentSpawnMutation(
+ browser,
+ { expected: [[EVENT_ALERT, alert]] },
+ () => {
+ content.document.getElementById("content").hidden = false;
+ }
+ );
+ info("Changing content display style and removing text");
+ const content = findAccessibleChildByID(docAcc, "content");
+ await contentSpawnMutation(
+ browser,
+ {
+ expected: [[EVENT_REORDER, content]],
+ unexpected: [[EVENT_ALERT, alert]],
+ },
+ () => {
+ const node = content.document.getElementById("content");
+ node.textContent = "";
+ // This causes the node's layout frame to be reconstructed. This in
+ // turn causes a11y to queue it as an insertion in case there were
+ // changes. Because it already has an Accessible, This node is skipped
+ // when processing insertions, so we should not fire an alert event.
+ node.style.display = "flex";
+ }
+ );
+ },
+ { chrome: true, topLevel: true, remoteIframe: true }
+);
diff --git a/accessible/tests/browser/events/browser_test_A11yUtils_announce.js b/accessible/tests/browser/events/browser_test_A11yUtils_announce.js
new file mode 100644
index 0000000000..b2848f35c2
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_A11yUtils_announce.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from ../../mochitest/role.js */
+loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
+
+// Check that the browser A11yUtils.announce() function works correctly.
+// Note that this does not use mozilla::a11y::Accessible::Announce and a11y
+// announcement events, as these aren't yet supported on desktop.
+async function runTests() {
+ const alert = document.getElementById("a11y-announcement");
+ let alerted = waitForEvent(EVENT_ALERT, alert);
+ A11yUtils.announce({ raw: "first" });
+ let event = await alerted;
+ const alertAcc = event.accessible;
+ is(alertAcc.role, ROLE_ALERT);
+ ok(!alertAcc.name);
+ is(alertAcc.childCount, 1);
+ is(alertAcc.firstChild.name, "first");
+
+ alerted = waitForEvent(EVENT_ALERT, alertAcc);
+ A11yUtils.announce({ raw: "second" });
+ event = await alerted;
+ ok(!alertAcc.name);
+ is(alertAcc.childCount, 1);
+ is(alertAcc.firstChild.name, "second");
+
+ info("Testing Fluent message");
+ // We need a simple Fluent message here without arguments or attributes.
+ const fluentId = "search-one-offs-with-title";
+ const fluentMessage = await document.l10n.formatValue(fluentId);
+ alerted = waitForEvent(EVENT_ALERT, alertAcc);
+ A11yUtils.announce({ id: fluentId });
+ event = await alerted;
+ ok(!alertAcc.name);
+ is(alertAcc.childCount, 1);
+ is(alertAcc.firstChild.name, fluentMessage);
+
+ info("Ensuring Fluent message is cancelled if announce is re-entered");
+ alerted = waitForEvent(EVENT_ALERT, alertAcc);
+ // This call runs async.
+ let asyncAnnounce = A11yUtils.announce({ id: fluentId });
+ // Before the async call finishes, call announce again.
+ A11yUtils.announce({ raw: "third" });
+ // Wait for the async call to complete.
+ await asyncAnnounce;
+ event = await alerted;
+ ok(!alertAcc.name);
+ is(alertAcc.childCount, 1);
+ // The async call should have been cancelled. If it wasn't, we would get
+ // fluentMessage here instead of "third".
+ is(alertAcc.firstChild.name, "third");
+}
+
+addAccessibleTask(``, runTests);
diff --git a/accessible/tests/browser/events/browser_test_caret_move_granularity.js b/accessible/tests/browser/events/browser_test_caret_move_granularity.js
new file mode 100644
index 0000000000..c72ae42d85
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_caret_move_granularity.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");
+ }
+);
diff --git a/accessible/tests/browser/events/browser_test_docload.js b/accessible/tests/browser/events/browser_test_docload.js
new file mode 100644
index 0000000000..78ac77fd8c
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_docload.js
@@ -0,0 +1,128 @@
+/* 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";
+
+function busyChecker(isBusy) {
+ return function (event) {
+ let scEvent;
+ try {
+ scEvent = event.QueryInterface(nsIAccessibleStateChangeEvent);
+ } catch (e) {
+ return false;
+ }
+
+ return scEvent.state == STATE_BUSY && scEvent.isEnabled == isBusy;
+ };
+}
+
+function inIframeChecker(iframeId) {
+ return function (event) {
+ return getAccessibleDOMNodeID(event.accessibleDocument.parent) == iframeId;
+ };
+}
+
+function urlChecker(url) {
+ return function (event) {
+ info(`${event.accessibleDocument.URL} == ${url}`);
+ return event.accessibleDocument.URL == url;
+ };
+}
+
+async function runTests(browser, accDoc) {
+ let onLoadEvents = waitForEvents({
+ expected: [
+ [EVENT_REORDER, getAccessible(browser)],
+ [EVENT_DOCUMENT_LOAD_COMPLETE, "body2"],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ ],
+ unexpected: [
+ [EVENT_DOCUMENT_LOAD_COMPLETE, inIframeChecker("iframe1")],
+ [EVENT_STATE_CHANGE, inIframeChecker("iframe1")],
+ ],
+ });
+
+ BrowserTestUtils.startLoadingURIString(
+ browser,
+ `data:text/html;charset=utf-8,
+ <html><body id="body2">
+ <iframe id="iframe1" src="http://example.com"></iframe>
+ </body></html>`
+ );
+
+ await onLoadEvents;
+
+ onLoadEvents = waitForEvents([
+ [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("about:about")],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ [EVENT_REORDER, getAccessible(browser)],
+ ]);
+
+ BrowserTestUtils.startLoadingURIString(browser, "about:about");
+
+ await onLoadEvents;
+
+ onLoadEvents = waitForEvents([
+ [EVENT_DOCUMENT_RELOAD, evt => evt.isFromUserInput],
+ [EVENT_REORDER, getAccessible(browser)],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ ]);
+
+ EventUtils.synthesizeKey("VK_F5", {}, browser.ownerGlobal);
+
+ await onLoadEvents;
+
+ onLoadEvents = waitForEvents([
+ [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("about:mozilla")],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ [EVENT_REORDER, getAccessible(browser)],
+ ]);
+
+ BrowserTestUtils.startLoadingURIString(browser, "about:mozilla");
+
+ await onLoadEvents;
+
+ onLoadEvents = waitForEvents([
+ [EVENT_DOCUMENT_RELOAD, evt => !evt.isFromUserInput],
+ [EVENT_REORDER, getAccessible(browser)],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ ]);
+
+ browser.reload();
+
+ await onLoadEvents;
+
+ onLoadEvents = waitForEvents([
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("http://www.wronguri.wronguri/")],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ [EVENT_REORDER, getAccessible(browser)],
+ ]);
+
+ BrowserTestUtils.startLoadingURIString(
+ browser,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://www.wronguri.wronguri/"
+ );
+
+ await onLoadEvents;
+
+ onLoadEvents = waitForEvents([
+ [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("https://nocert.example.com/")],
+ [EVENT_STATE_CHANGE, busyChecker(false)],
+ [EVENT_REORDER, getAccessible(browser)],
+ ]);
+
+ BrowserTestUtils.startLoadingURIString(
+ browser,
+ "https://nocert.example.com:443/"
+ );
+
+ await onLoadEvents;
+}
+
+/**
+ * Test caching of accessible object states
+ */
+addAccessibleTask("", runTests);
diff --git a/accessible/tests/browser/events/browser_test_focus_browserui.js b/accessible/tests/browser/events/browser_test_focus_browserui.js
new file mode 100644
index 0000000000..969d336c74
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_focus_browserui.js
@@ -0,0 +1,57 @@
+/* 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/states.js */
+/* import-globals-from ../../mochitest/role.js */
+loadScripts(
+ { name: "states.js", dir: MOCHITESTS_DIR },
+ { name: "role.js", dir: MOCHITESTS_DIR }
+);
+
+async function runTests(browser, accDoc) {
+ await SpecialPowers.pushPrefEnv({
+ // If Fission is disabled, the pref is no-op.
+ set: [["fission.bfcacheInParent", true]],
+ });
+
+ let onFocus = waitForEvent(EVENT_FOCUS, "input");
+ EventUtils.synthesizeKey("VK_TAB", {}, browser.ownerGlobal);
+ let evt = await onFocus;
+ testStates(evt.accessible, STATE_FOCUSED);
+
+ onFocus = waitForEvent(EVENT_FOCUS, "buttonInputDoc");
+ let url = snippetToURL(`<input id="input" type="button" value="button">`, {
+ contentDocBodyAttrs: { id: "buttonInputDoc" },
+ });
+ browser.loadURI(Services.io.newURI(url), {
+ triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+ });
+ evt = await onFocus;
+ testStates(evt.accessible, STATE_FOCUSED);
+
+ onFocus = waitForEvent(EVENT_FOCUS, "input");
+ browser.goBack();
+ evt = await onFocus;
+ testStates(evt.accessible, STATE_FOCUSED);
+
+ onFocus = waitForEvent(
+ EVENT_FOCUS,
+ event => event.accessible.DOMNode == gURLBar.inputField
+ );
+ EventUtils.synthesizeKey("t", { accelKey: true }, browser.ownerGlobal);
+ evt = await onFocus;
+ testStates(evt.accessible, STATE_FOCUSED);
+
+ onFocus = waitForEvent(EVENT_FOCUS, "input");
+ EventUtils.synthesizeKey("w", { accelKey: true }, browser.ownerGlobal);
+ evt = await onFocus;
+ testStates(evt.accessible, STATE_FOCUSED);
+}
+
+/**
+ * Accessibility loading document events test.
+ */
+addAccessibleTask(`<input id="input">`, runTests);
diff --git a/accessible/tests/browser/events/browser_test_focus_dialog.js b/accessible/tests/browser/events/browser_test_focus_dialog.js
new file mode 100644
index 0000000000..71485a678d
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_focus_dialog.js
@@ -0,0 +1,76 @@
+/* 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/states.js */
+/* import-globals-from ../../mochitest/role.js */
+loadScripts(
+ { name: "states.js", dir: MOCHITESTS_DIR },
+ { name: "role.js", dir: MOCHITESTS_DIR }
+);
+
+async function runTests(browser, accDoc) {
+ let onFocus = waitForEvent(EVENT_FOCUS, "button");
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.getElementById("button").focus();
+ });
+ let button = (await onFocus).accessible;
+ testStates(button, STATE_FOCUSED);
+
+ // Bug 1377942 - The target of the focus event changes under different
+ // circumstances.
+ // In e10s the focus event is the new window, in non-e10s it's the doc.
+ onFocus = waitForEvent(EVENT_FOCUS, () => true);
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ // button should be blurred
+ await onFocus;
+ testStates(button, 0, 0, STATE_FOCUSED);
+
+ onFocus = waitForEvent(EVENT_FOCUS, "button");
+ await BrowserTestUtils.closeWindow(newWin);
+ testStates((await onFocus).accessible, STATE_FOCUSED);
+
+ onFocus = waitForEvent(EVENT_FOCUS, "body2");
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document
+ .getElementById("editabledoc")
+ .contentWindow.document.body.focus();
+ });
+ testStates((await onFocus).accessible, STATE_FOCUSED);
+
+ onFocus = waitForEvent(EVENT_FOCUS, "body2");
+ newWin = await BrowserTestUtils.openNewBrowserWindow();
+ await BrowserTestUtils.closeWindow(newWin);
+ testStates((await onFocus).accessible, STATE_FOCUSED);
+
+ let onShow = waitForEvent(EVENT_SHOW, "alertdialog");
+ onFocus = waitForEvent(EVENT_FOCUS, "alertdialog");
+ await SpecialPowers.spawn(browser, [], () => {
+ let alertDialog = content.document.getElementById("alertdialog");
+ alertDialog.style.display = "block";
+ alertDialog.focus();
+ });
+ await onShow;
+ testStates((await onFocus).accessible, STATE_FOCUSED);
+}
+
+/**
+ * Accessible dialog focus testing
+ */
+addAccessibleTask(
+ `
+ <button id="button">button</button>
+ <iframe id="editabledoc"
+ src="${snippetToURL("", {
+ contentDocBodyAttrs: { id: "body2", contentEditable: "true" },
+ })}">
+ </iframe>
+ <div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
+ <div id="title2">Blah blah</div>
+ <div id="desc2">Woof woof woof.</div>
+ <button>Close</button>
+ </div>`,
+ runTests
+);
diff --git a/accessible/tests/browser/events/browser_test_focus_urlbar.js b/accessible/tests/browser/events/browser_test_focus_urlbar.js
new file mode 100644
index 0000000000..68b2b07f3c
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_focus_urlbar.js
@@ -0,0 +1,438 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from ../../mochitest/states.js */
+/* import-globals-from ../../mochitest/role.js */
+loadScripts(
+ { name: "states.js", dir: MOCHITESTS_DIR },
+ { name: "role.js", dir: MOCHITESTS_DIR }
+);
+
+ChromeUtils.defineESModuleGetters(this, {
+ PlacesTestUtils: "resource://testing-common/PlacesTestUtils.sys.mjs",
+ PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
+ UrlbarProvider: "resource:///modules/UrlbarUtils.sys.mjs",
+ UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.sys.mjs",
+ UrlbarResult: "resource:///modules/UrlbarResult.sys.mjs",
+ UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs",
+ UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
+});
+
+function isEventForAutocompleteItem(event) {
+ return event.accessible.role == ROLE_COMBOBOX_OPTION;
+}
+
+function isEventForButton(event) {
+ return event.accessible.role == ROLE_PUSHBUTTON;
+}
+
+function isEventForOneOffEngine(event) {
+ let parent = event.accessible.parent;
+ return (
+ event.accessible.role == ROLE_PUSHBUTTON &&
+ parent &&
+ parent.role == ROLE_GROUPING &&
+ parent.name
+ );
+}
+
+function isEventForMenuPopup(event) {
+ return event.accessible.role == ROLE_MENUPOPUP;
+}
+
+function isEventForMenuItem(event) {
+ return event.accessible.role == ROLE_MENUITEM;
+}
+
+function isEventForResultButton(event) {
+ let parent = event.accessible.parent;
+ return (
+ event.accessible.role == ROLE_PUSHBUTTON &&
+ parent?.role == ROLE_COMBOBOX_LIST
+ );
+}
+
+/**
+ * A test provider.
+ */
+class TipTestProvider extends UrlbarProvider {
+ constructor(matches) {
+ super();
+ this._matches = matches;
+ }
+ get name() {
+ return "TipTestProvider";
+ }
+ get type() {
+ return UrlbarUtils.PROVIDER_TYPE.PROFILE;
+ }
+ isActive(context) {
+ return true;
+ }
+ isRestricting(context) {
+ return true;
+ }
+ async startQuery(context, addCallback) {
+ this._context = context;
+ for (const match of this._matches) {
+ addCallback(this, match);
+ }
+ }
+}
+
+// Check that the URL bar manages accessibility focus appropriately.
+async function runTests() {
+ registerCleanupFunction(async function () {
+ await UrlbarTestUtils.promisePopupClose(window);
+ await PlacesUtils.history.clear();
+ });
+
+ await PlacesTestUtils.addVisits([
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example1.com/blah",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example2.com/blah",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example1.com/",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example2.com/",
+ ]);
+
+ // Ensure initial state.
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ let focused = waitForEvent(
+ EVENT_FOCUS,
+ event => event.accessible.role == ROLE_ENTRY
+ );
+ gURLBar.focus();
+ let event = await focused;
+ let textBox = event.accessible;
+ // Ensure the URL bar is ready for a new URL to be typed.
+ // Sometimes, when this test runs, the existing text isn't selected when the
+ // URL bar is focused. Pressing escape twice ensures that the popup is
+ // closed and that the existing text is selected.
+ EventUtils.synthesizeKey("KEY_Escape");
+ EventUtils.synthesizeKey("KEY_Escape");
+
+ info("Ensuring no focus change when first text is typed");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ waitForFocus,
+ value: "example",
+ fireInputEvent: true,
+ });
+ // Wait a tick for a11y events to fire.
+ await TestUtils.waitForTick();
+ testStates(textBox, STATE_FOCUSED);
+
+ info("Ensuring no focus change on backspace");
+ EventUtils.synthesizeKey("KEY_Backspace");
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ // Wait a tick for a11y events to fire.
+ await TestUtils.waitForTick();
+ testStates(textBox, STATE_FOCUSED);
+
+ info("Ensuring no focus change on text selection and delete");
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ EventUtils.synthesizeKey("KEY_Delete");
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ // Wait a tick for a11y events to fire.
+ await TestUtils.waitForTick();
+ testStates(textBox, STATE_FOCUSED);
+
+ info("Ensuring autocomplete focus on down arrow (1)");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring focus of another autocomplete item on down arrow");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring previous arrow selection state doesn't get stale on input");
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ EventUtils.sendString("z");
+ await focused;
+ EventUtils.synthesizeKey("KEY_Backspace");
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ testStates(textBox, STATE_FOCUSED);
+
+ info("Ensuring focus of another autocomplete item on down arrow");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ if (AppConstants.platform == "macosx") {
+ info("Ensuring focus of another autocomplete item on ctrl-n");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring focus of another autocomplete item on ctrl-p");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+ }
+
+ info("Ensuring focus of another autocomplete item on up arrow");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring text box focus on left arrow");
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ EventUtils.synthesizeKey("KEY_ArrowLeft");
+ await focused;
+ testStates(textBox, STATE_FOCUSED);
+
+ gURLBar.view.close();
+ // On Mac, down arrow when not at the end of the field moves to the end.
+ // Move back to the end so the next press of down arrow opens the popup.
+ EventUtils.synthesizeKey("KEY_ArrowRight");
+
+ info("Ensuring autocomplete focus on down arrow (2)");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring autocomplete focus on arrow up for search settings button");
+ focused = waitForEvent(EVENT_FOCUS, isEventForButton);
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring text box focus when text is typed");
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ EventUtils.sendString("z");
+ await focused;
+ testStates(textBox, STATE_FOCUSED);
+ EventUtils.synthesizeKey("KEY_Backspace");
+ await UrlbarTestUtils.promiseSearchComplete(window);
+
+ info("Ensuring autocomplete focus on down arrow (3)");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring text box focus on backspace");
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ EventUtils.synthesizeKey("KEY_Backspace");
+ await focused;
+ testStates(textBox, STATE_FOCUSED);
+ await UrlbarTestUtils.promiseSearchComplete(window);
+
+ info("Ensuring autocomplete focus on arrow down (4)");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ // Arrow down to the last result.
+ const resultCount = UrlbarTestUtils.getResultCount(window);
+ while (UrlbarTestUtils.getSelectedRowIndex(window) != resultCount - 1) {
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ }
+
+ info("Ensuring one-off search button focus on arrow down");
+ focused = waitForEvent(EVENT_FOCUS, isEventForOneOffEngine);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring autocomplete focus on arrow up");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring text box focus on text selection");
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ await focused;
+ testStates(textBox, STATE_FOCUSED);
+
+ if (AppConstants.platform == "macosx") {
+ // On Mac, ctrl-n after arrow left/right does not re-open the popup.
+ // Type some text so the next press of ctrl-n opens the popup.
+ EventUtils.sendString("ple");
+
+ info("Ensuring autocomplete focus on ctrl-n");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+ }
+
+ if (
+ AppConstants.platform == "macosx" &&
+ Services.prefs.getBoolPref("widget.macos.native-context-menus", false)
+ ) {
+ // With native context menus, we do not observe accessibility events and we
+ // cannot send synthetic key events to the menu.
+ info("Opening and closing context native context menu");
+ let contextMenu = gURLBar.querySelector("menupopup");
+ let popupshown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
+ EventUtils.synthesizeMouseAtCenter(gURLBar.querySelector("moz-input-box"), {
+ type: "contextmenu",
+ });
+ await popupshown;
+ let popuphidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+ contextMenu.hidePopup();
+ await popuphidden;
+ } else {
+ info(
+ "Ensuring context menu gets menu event on launch, and item focus on down"
+ );
+ let menuEvent = waitForEvent(
+ nsIAccessibleEvent.EVENT_MENUPOPUP_START,
+ isEventForMenuPopup
+ );
+ EventUtils.synthesizeMouseAtCenter(gURLBar.querySelector("moz-input-box"), {
+ type: "contextmenu",
+ });
+ await menuEvent;
+
+ focused = waitForEvent(EVENT_FOCUS, isEventForMenuItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ let closed = waitForEvent(
+ nsIAccessibleEvent.EVENT_MENUPOPUP_END,
+ isEventForMenuPopup
+ );
+ EventUtils.synthesizeKey("KEY_Escape");
+ await closed;
+ await focused;
+ }
+ info("Ensuring address bar is focused after context menu is dismissed.");
+ testStates(textBox, STATE_FOCUSED);
+}
+
+// We test TIP results in their own test so the spoofed results don't interfere
+// with the main test.
+async function runTipTests() {
+ let matches = [
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.HISTORY,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ { url: "http://mozilla.org/a" }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.TIP,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ helpUrl: "http://example.com/",
+ type: "test",
+ titleL10n: { id: "urlbar-search-tips-confirm" },
+ buttons: [
+ {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: "http://example.com/",
+ l10n: { id: "urlbar-search-tips-confirm" },
+ },
+ ],
+ }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.HISTORY,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ { url: "http://mozilla.org/b" }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.HISTORY,
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ { url: "http://mozilla.org/c" }
+ ),
+ ];
+
+ // Ensure the tip appears in the expected position.
+ matches[1].suggestedIndex = 2;
+
+ let provider = new TipTestProvider(matches);
+ UrlbarProvidersManager.registerProvider(provider);
+
+ registerCleanupFunction(async function () {
+ UrlbarProvidersManager.unregisterProvider(provider);
+ });
+
+ let focused = waitForEvent(
+ EVENT_FOCUS,
+ event => event.accessible.role == ROLE_ENTRY
+ );
+ gURLBar.focus();
+ let event = await focused;
+ let textBox = event.accessible;
+
+ EventUtils.synthesizeKey("KEY_Escape");
+ EventUtils.synthesizeKey("KEY_Escape");
+
+ info("Ensuring no focus change when first text is typed");
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ waitForFocus,
+ value: "example",
+ fireInputEvent: true,
+ });
+ // Wait a tick for a11y events to fire.
+ await TestUtils.waitForTick();
+ testStates(textBox, STATE_FOCUSED);
+
+ info("Ensuring autocomplete focus on down arrow (1)");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring the tip button is focused on down arrow");
+ info("Also ensuring that the tip button is a part of a labelled group");
+ focused = waitForEvent(EVENT_FOCUS, isEventForResultButton);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring the help button is focused on tab");
+ info("Also ensuring that the help button is a part of a labelled group");
+ focused = waitForEvent(EVENT_FOCUS, isEventForResultButton);
+ EventUtils.synthesizeKey("KEY_Tab");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring autocomplete focus on down arrow (2)");
+ focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring the help button is focused on shift+tab");
+ focused = waitForEvent(EVENT_FOCUS, isEventForResultButton);
+ EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
+ event = await focused;
+ testStates(event.accessible, STATE_FOCUSED);
+
+ info("Ensuring text box focus on left arrow, and not back to the tip button");
+ focused = waitForEvent(EVENT_FOCUS, textBox);
+ EventUtils.synthesizeKey("KEY_ArrowLeft");
+ await focused;
+ testStates(textBox, STATE_FOCUSED);
+}
+
+addAccessibleTask(``, runTests);
+addAccessibleTask(``, runTipTests);
diff --git a/accessible/tests/browser/events/browser_test_panel.js b/accessible/tests/browser/events/browser_test_panel.js
new file mode 100644
index 0000000000..8a4ab89705
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_panel.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from ../../mochitest/role.js */
+loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
+
+// Verify we receive hide and show notifications when the chrome
+// XUL alert is closed or opened. Mac expects both notifications to
+// properly communicate live region changes.
+async function runTests(browser) {
+ ok(PopupNotifications, "PopupNotifications object exists");
+ ok(PopupNotifications.panel, "PopupNotifications panel exists");
+
+ // When available, the popup panel makes itself a child of the chrome window.
+ // To verify it isn't accessible without reproducing the entirety of the chrome
+ // window tree, we check instead that the panel is not accessible.
+ ok(!isAccessible(PopupNotifications.panel), "Popup panel is not accessible");
+
+ const panelShown = waitForEvent(EVENT_SHOW, PopupNotifications.panel);
+ const notification = PopupNotifications.show(
+ browser,
+ "test-notification",
+ "hello world",
+ PopupNotifications.panel.id
+ );
+
+ await panelShown;
+
+ ok(isAccessible(PopupNotifications.panel), "Popup panel is accessible");
+ testAccessibleTree(PopupNotifications.panel, {
+ ALERT: [
+ { LABEL: [{ TEXT_LEAF: [] }] },
+ { PUSHBUTTON: [] },
+ { PUSHBUTTON: [] },
+ ],
+ });
+ // Verify the popup panel is associated with the chrome window.
+ is(
+ PopupNotifications.panel.ownerGlobal,
+ getMainChromeWindow(window),
+ "Popup panel is associated with the chrome window"
+ );
+
+ const panelHidden = waitForEvent(EVENT_HIDE, PopupNotifications.panel);
+ PopupNotifications.remove(notification);
+
+ await panelHidden;
+
+ ok(!isAccessible(PopupNotifications.panel), "Popup panel is not accessible");
+}
+
+addAccessibleTask(``, runTests);
diff --git a/accessible/tests/browser/events/browser_test_scrolling.js b/accessible/tests/browser/events/browser_test_scrolling.js
new file mode 100644
index 0000000000..9678ee767b
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_scrolling.js
@@ -0,0 +1,153 @@
+/* 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";
+
+addAccessibleTask(
+ `
+ <div style="height: 100vh" id="one">one</div>
+ <div style="height: 100vh" id="two">two</div>
+ <div style="height: 100vh; width: 200vw; overflow: auto;" id="three">
+ <div style="height: 300%;">three</div>
+ </div>
+ <textarea id="textarea" rows="1">a
+b
+c</textarea>
+ `,
+ async function (browser, accDoc) {
+ let onScrolling = waitForEvents([
+ [EVENT_SCROLLING, accDoc],
+ [EVENT_SCROLLING_END, accDoc],
+ ]);
+ await SpecialPowers.spawn(browser, [], () => {
+ content.location.hash = "#two";
+ });
+ let [scrollEvent1, scrollEndEvent1] = await onScrolling;
+ scrollEvent1.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEvent1.maxScrollY >= scrollEvent1.scrollY,
+ "scrollY is within max"
+ );
+ scrollEndEvent1.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEndEvent1.maxScrollY >= scrollEndEvent1.scrollY,
+ "scrollY is within max"
+ );
+
+ onScrolling = waitForEvents([
+ [EVENT_SCROLLING, accDoc],
+ [EVENT_SCROLLING_END, accDoc],
+ ]);
+ await SpecialPowers.spawn(browser, [], () => {
+ content.location.hash = "#three";
+ });
+ let [scrollEvent2, scrollEndEvent2] = await onScrolling;
+ scrollEvent2.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEvent2.scrollY > scrollEvent1.scrollY,
+ `${scrollEvent2.scrollY} > ${scrollEvent1.scrollY}`
+ );
+ scrollEndEvent2.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEndEvent2.maxScrollY >= scrollEndEvent2.scrollY,
+ "scrollY is within max"
+ );
+
+ onScrolling = waitForEvents([
+ [EVENT_SCROLLING, accDoc],
+ [EVENT_SCROLLING_END, accDoc],
+ ]);
+ await SpecialPowers.spawn(browser, [], () => {
+ content.scrollTo(10, 0);
+ });
+ let [scrollEvent3, scrollEndEvent3] = await onScrolling;
+ scrollEvent3.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEvent3.maxScrollX >= scrollEvent3.scrollX,
+ "scrollX is within max"
+ );
+ scrollEndEvent3.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEndEvent3.maxScrollX >= scrollEndEvent3.scrollX,
+ "scrollY is within max"
+ );
+ ok(
+ scrollEvent3.scrollX > scrollEvent2.scrollX,
+ `${scrollEvent3.scrollX} > ${scrollEvent2.scrollX}`
+ );
+
+ // non-doc scrolling
+ onScrolling = waitForEvents([
+ [EVENT_SCROLLING, "three"],
+ [EVENT_SCROLLING_END, "three"],
+ ]);
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.querySelector("#three").scrollTo(0, 10);
+ });
+ let [scrollEvent4, scrollEndEvent4] = await onScrolling;
+ scrollEvent4.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEvent4.maxScrollY >= scrollEvent4.scrollY,
+ "scrollY is within max"
+ );
+ scrollEndEvent4.QueryInterface(nsIAccessibleScrollingEvent);
+ ok(
+ scrollEndEvent4.maxScrollY >= scrollEndEvent4.scrollY,
+ "scrollY is within max"
+ );
+
+ // textarea scrolling
+ info("Moving textarea caret to c");
+ onScrolling = waitForEvents([
+ [EVENT_SCROLLING, "textarea"],
+ [EVENT_SCROLLING_END, "textarea"],
+ ]);
+ await invokeContentTask(browser, [], () => {
+ const textareaDom = content.document.getElementById("textarea");
+ textareaDom.focus();
+ textareaDom.selectionStart = 4;
+ });
+ await onScrolling;
+ }
+);
+
+// Verify that the scrolling start event is fired for an anchor change.
+addAccessibleTask(
+ `
+ <p>a</p>
+ <p>b</p>
+ <p id="c">c</p>
+ `,
+ async function (browser, accDoc) {
+ let onScrollingStart = waitForEvent(EVENT_SCROLLING_START, "c");
+ await SpecialPowers.spawn(browser, [], () => {
+ content.location.hash = "#c";
+ });
+ await onScrollingStart;
+ },
+ { chrome: true, topLevel: true }
+);
+
+// Ensure that a scrollable, focused non-interactive element receives a
+// scrolling start event when an anchor jump to that element is triggered.
+addAccessibleTask(
+ `
+<div style="height: 100vh; width: 100vw; overflow: auto;" id="scrollable">
+ <h1 style="height: 300%;" id="inside-scrollable">test</h1>
+</div>
+ `,
+ async function (browser, accDoc) {
+ let onScrollingStart = waitForEvent(
+ EVENT_SCROLLING_START,
+ "inside-scrollable"
+ );
+ await invokeContentTask(browser, [], () => {
+ const scrollable = content.document.getElementById("scrollable");
+ scrollable.focus();
+ content.location.hash = "#inside-scrollable";
+ });
+ await onScrollingStart;
+ },
+ { chrome: true, topLevel: true, iframe: true, remoteIframe: true }
+);
diff --git a/accessible/tests/browser/events/browser_test_selection_urlbar.js b/accessible/tests/browser/events/browser_test_selection_urlbar.js
new file mode 100644
index 0000000000..8f8fdb92f7
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_selection_urlbar.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from ../../mochitest/role.js */
+loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
+
+ChromeUtils.defineESModuleGetters(this, {
+ UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs",
+});
+
+// Check that the URL bar manages accessibility
+// selection notifications appropriately on startup (new window).
+async function runTests() {
+ let focused = waitForEvent(
+ EVENT_FOCUS,
+ event => event.accessible.role == ROLE_ENTRY
+ );
+ info("Creating new window");
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ let bookmark = await PlacesUtils.bookmarks.insert({
+ parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+ title: "addons",
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ url: Services.io.newURI("http://www.addons.mozilla.org/"),
+ });
+
+ registerCleanupFunction(async function () {
+ await BrowserTestUtils.closeWindow(newWin);
+ await PlacesUtils.bookmarks.remove(bookmark);
+ });
+ info("Focusing window");
+ newWin.focus();
+ await focused;
+
+ // Ensure the URL bar is ready for a new URL to be typed.
+ // Sometimes, when this test runs, the existing text isn't selected when the
+ // URL bar is focused. Pressing escape twice ensures that the popup is
+ // closed and that the existing text is selected.
+ EventUtils.synthesizeKey("KEY_Escape", {}, newWin);
+ EventUtils.synthesizeKey("KEY_Escape", {}, newWin);
+ let caretMoved = waitForEvent(
+ EVENT_TEXT_CARET_MOVED,
+ event => event.accessible.role == ROLE_ENTRY
+ );
+
+ info("Autofilling after typing `a` in new window URL bar.");
+ EventUtils.synthesizeKey("a", {}, newWin);
+ await UrlbarTestUtils.promiseSearchComplete(newWin);
+ Assert.equal(
+ newWin.gURLBar.inputField.value,
+ "addons.mozilla.org/",
+ "autofilled value as expected"
+ );
+
+ info("Ensuring caret moved on text selection");
+ await caretMoved;
+}
+
+addAccessibleTask(``, runTests);
diff --git a/accessible/tests/browser/events/browser_test_textcaret.js b/accessible/tests/browser/events/browser_test_textcaret.js
new file mode 100644
index 0000000000..e0f6f1a176
--- /dev/null
+++ b/accessible/tests/browser/events/browser_test_textcaret.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Caret move events checker.
+ */
+function caretMoveChecker(target, caretOffset) {
+ return function (event) {
+ let cmEvent = event.QueryInterface(nsIAccessibleCaretMoveEvent);
+ return (
+ cmEvent.accessible == getAccessible(target) &&
+ cmEvent.caretOffset == caretOffset
+ );
+ };
+}
+
+async function checkURLBarCaretEvents() {
+ const kURL = "about:mozilla";
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+ BrowserTestUtils.startLoadingURIString(newWin.gBrowser.selectedBrowser, kURL);
+ newWin.gBrowser.selectedBrowser.focus();
+
+ await waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, event => {
+ try {
+ return event.accessible.QueryInterface(nsIAccessibleDocument).URL == kURL;
+ } catch (e) {
+ return false;
+ }
+ });
+ info("Loaded " + kURL);
+
+ let urlbarInputEl = newWin.gURLBar.inputField;
+ let urlbarInput = getAccessible(urlbarInputEl, [nsIAccessibleText]);
+
+ let onCaretMove = waitForEvents([
+ [EVENT_TEXT_CARET_MOVED, caretMoveChecker(urlbarInput, kURL.length)],
+ [EVENT_FOCUS, urlbarInput],
+ ]);
+
+ urlbarInput.caretOffset = -1;
+ await onCaretMove;
+ ok(true, "Caret move in URL bar #1");
+
+ onCaretMove = waitForEvent(
+ EVENT_TEXT_CARET_MOVED,
+ caretMoveChecker(urlbarInput, 0)
+ );
+
+ urlbarInput.caretOffset = 0;
+ await onCaretMove;
+ ok(true, "Caret move in URL bar #2");
+
+ await BrowserTestUtils.closeWindow(newWin);
+}
+
+add_task(checkURLBarCaretEvents);
diff --git a/accessible/tests/browser/events/head.js b/accessible/tests/browser/events/head.js
new file mode 100644
index 0000000000..afc50984bd
--- /dev/null
+++ b/accessible/tests/browser/events/head.js
@@ -0,0 +1,18 @@
+/* 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";
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
+ this
+);
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as promisified-events.js.
+loadScripts(
+ { name: "common.js", dir: MOCHITESTS_DIR },
+ { name: "promisified-events.js", dir: MOCHITESTS_DIR }
+);