summaryrefslogtreecommitdiffstats
path: root/accessible/tests/browser/tree
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/browser/tree')
-rw-r--r--accessible/tests/browser/tree/browser.toml31
-rw-r--r--accessible/tests/browser/tree/browser_aria_owns.js278
-rw-r--r--accessible/tests/browser/tree/browser_browser_element.js16
-rw-r--r--accessible/tests/browser/tree/browser_css_content_visibility.js121
-rw-r--r--accessible/tests/browser/tree/browser_general.js128
-rw-r--r--accessible/tests/browser/tree/browser_lazy_tabs.js43
-rw-r--r--accessible/tests/browser/tree/browser_link.js208
-rw-r--r--accessible/tests/browser/tree/browser_searchbar.js96
-rw-r--r--accessible/tests/browser/tree/browser_shadowdom.js98
-rw-r--r--accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js54
-rw-r--r--accessible/tests/browser/tree/head.js33
11 files changed, 1106 insertions, 0 deletions
diff --git a/accessible/tests/browser/tree/browser.toml b/accessible/tests/browser/tree/browser.toml
new file mode 100644
index 0000000000..64be6853d1
--- /dev/null
+++ b/accessible/tests/browser/tree/browser.toml
@@ -0,0 +1,31 @@
+[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_aria_owns.js"]
+skip-if = [
+ "true", #Bug 1445513
+ "verify && !debug && os == 'linux'",
+]
+
+["browser_browser_element.js"]
+
+["browser_css_content_visibility.js"]
+
+["browser_general.js"]
+
+["browser_lazy_tabs.js"]
+
+["browser_link.js"]
+
+["browser_searchbar.js"]
+
+["browser_shadowdom.js"]
+
+["browser_test_nsIAccessibleDocument_URL.js"]
diff --git a/accessible/tests/browser/tree/browser_aria_owns.js b/accessible/tests/browser/tree/browser_aria_owns.js
new file mode 100644
index 0000000000..0ca55ed357
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_aria_owns.js
@@ -0,0 +1,278 @@
+/* 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";
+
+let NO_MOVE = { unexpected: [[EVENT_REORDER, "container"]] };
+let MOVE = { expected: [[EVENT_REORDER, "container"]] };
+
+// Set last ordinal child as aria-owned, should produce no reorder.
+addAccessibleTask(
+ `<ul id="container"><li id="a">Test</li></ul>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc, ["a"]);
+
+ await contentSpawnMutation(browser, NO_MOVE, function () {
+ // aria-own ordinal child in place, should be a no-op.
+ content.document
+ .getElementById("container")
+ .setAttribute("aria-owns", "a");
+ });
+
+ testChildrenIds(containerAcc, ["a"]);
+ }
+);
+
+// Add a new ordinal child to a container with an aria-owned child.
+// Order should respect aria-owns.
+addAccessibleTask(
+ `<ul id="container"><li id="a">Test</li></ul>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc, ["a"]);
+
+ await contentSpawnMutation(browser, MOVE, function () {
+ let container = content.document.getElementById("container");
+ container.setAttribute("aria-owns", "a");
+
+ let aa = content.document.createElement("li");
+ aa.id = "aa";
+ container.appendChild(aa);
+ });
+
+ testChildrenIds(containerAcc, ["aa", "a"]);
+
+ await contentSpawnMutation(browser, MOVE, function () {
+ content.document.getElementById("container").removeAttribute("aria-owns");
+ });
+
+ testChildrenIds(containerAcc, ["a", "aa"]);
+ }
+);
+
+// Remove a no-move aria-owns attribute, should result in a no-move.
+addAccessibleTask(
+ `<ul id="container" aria-owns="a"><li id="a">Test</li></ul>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc, ["a"]);
+
+ await contentSpawnMutation(browser, NO_MOVE, function () {
+ // remove aria-owned child that is already ordinal, should be no-op.
+ content.document.getElementById("container").removeAttribute("aria-owns");
+ });
+
+ testChildrenIds(containerAcc, ["a"]);
+ }
+);
+
+// Attempt to steal an aria-owned child. The attempt should fail.
+addAccessibleTask(
+ `
+ <ul>
+ <li id="a">Test</li>
+ </ul>
+ <ul aria-owns="a"></ul>
+ <ul id="container"></ul>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc, []);
+
+ await contentSpawnMutation(browser, NO_MOVE, function () {
+ content.document
+ .getElementById("container")
+ .setAttribute("aria-owns", "a");
+ });
+
+ testChildrenIds(containerAcc, []);
+ }
+);
+
+// Don't aria-own children of <select>
+addAccessibleTask(
+ `
+ <div id="container" role="group" aria-owns="b"></div>
+ <select id="select">
+ <option id="a"></option>
+ <option id="b"></option>
+ </select>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+ let selectAcc = findAccessibleChildByID(accDoc, "select");
+
+ testChildrenIds(containerAcc, []);
+ testChildrenIds(selectAcc.firstChild, ["a", "b"]);
+ }
+);
+
+// Don't allow <select> to aria-own
+addAccessibleTask(
+ `
+ <div id="container" role="group">
+ <div id="a"></div>
+ <div id="b"></div>
+ </div>
+ <select id="select" aria-owns="a">
+ <option id="c"></option>
+ </select>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+ let selectAcc = findAccessibleChildByID(accDoc, "select");
+
+ testChildrenIds(containerAcc, ["a", "b"]);
+ testChildrenIds(selectAcc.firstChild, ["c"]);
+ }
+);
+
+// Don't allow one <select> to aria-own an <option> from another <select>.
+addAccessibleTask(
+ `
+ <select id="select1" aria-owns="c">
+ <option id="a"></option>
+ <option id="b"></option>
+ </select>
+ <select id="select2">
+ <option id="c"></option>
+ </select>`,
+ async function (browser, accDoc) {
+ let selectAcc1 = findAccessibleChildByID(accDoc, "select1");
+ let selectAcc2 = findAccessibleChildByID(accDoc, "select2");
+
+ testChildrenIds(selectAcc1.firstChild, ["a", "b"]);
+ testChildrenIds(selectAcc2.firstChild, ["c"]);
+ }
+);
+
+// Don't allow a <select> to reorder its children with aria-owns.
+addAccessibleTask(
+ `
+ <select id="container" aria-owns="c b a">
+ <option id="a"></option>
+ <option id="b"></option>
+ <option id="c"></option>
+ </select>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc.firstChild, ["a", "b", "c"]);
+
+ await contentSpawnMutation(browser, NO_MOVE, function () {
+ content.document
+ .getElementById("container")
+ .setAttribute("aria-owns", "a c b");
+ });
+
+ testChildrenIds(containerAcc.firstChild, ["a", "b", "c"]);
+ }
+);
+
+// Don't crash if ID in aria-owns does not exist
+addAccessibleTask(
+ `
+ <select id="container" aria-owns="boom" multiple></select>`,
+ async function (browser, accDoc) {
+ ok(true, "Did not crash");
+ }
+);
+
+addAccessibleTask(
+ `
+ <ul id="one">
+ <li id="a">Test</li>
+ <li id="b">Test 2</li>
+ <li id="c">Test 3</li>
+ </ul>
+ <ul id="two"></ul>`,
+ async function (browser, accDoc) {
+ let one = findAccessibleChildByID(accDoc, "one");
+ let two = findAccessibleChildByID(accDoc, "two");
+
+ let waitfor = {
+ expected: [
+ [EVENT_REORDER, "one"],
+ [EVENT_REORDER, "two"],
+ ],
+ };
+
+ await contentSpawnMutation(browser, waitfor, function () {
+ // Put same id twice in aria-owns
+ content.document.getElementById("two").setAttribute("aria-owns", "a a");
+ });
+
+ testChildrenIds(one, ["b", "c"]);
+ testChildrenIds(two, ["a"]);
+
+ await contentSpawnMutation(browser, waitfor, function () {
+ // If the previous double-id aria-owns worked correctly, we should
+ // be in a good state and all is fine..
+ content.document.getElementById("two").setAttribute("aria-owns", "a b");
+ });
+
+ testChildrenIds(one, ["c"]);
+ testChildrenIds(two, ["a", "b"]);
+ }
+);
+
+addAccessibleTask(
+ `<div id="a"></div><div id="b"></div>`,
+ async function (browser, accDoc) {
+ testChildrenIds(accDoc, ["a", "b"]);
+
+ let waitFor = {
+ expected: [[EVENT_REORDER, e => e.accessible == accDoc]],
+ };
+
+ await contentSpawnMutation(browser, waitFor, function () {
+ content.document.documentElement.style.display = "none";
+ content.document.documentElement.getBoundingClientRect();
+ content.document.body.setAttribute("aria-owns", "b a");
+ content.document.documentElement.remove();
+ });
+
+ testChildrenIds(accDoc, []);
+ }
+);
+
+// Don't allow ordinal child to be placed after aria-owned child (bug 1405796)
+addAccessibleTask(
+ `<div id="container"><div id="a">Hello</div></div>
+ <div><div id="c">There</div><div id="d">There</div></div>`,
+ async function (browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc, ["a"]);
+
+ await contentSpawnMutation(browser, MOVE, function () {
+ content.document
+ .getElementById("container")
+ .setAttribute("aria-owns", "c");
+ });
+
+ testChildrenIds(containerAcc, ["a", "c"]);
+
+ await contentSpawnMutation(browser, MOVE, function () {
+ let span = content.document.createElement("span");
+ content.document.getElementById("container").appendChild(span);
+
+ let b = content.document.createElement("div");
+ b.id = "b";
+ content.document.getElementById("container").appendChild(b);
+ });
+
+ testChildrenIds(containerAcc, ["a", "b", "c"]);
+
+ await contentSpawnMutation(browser, MOVE, function () {
+ content.document
+ .getElementById("container")
+ .setAttribute("aria-owns", "c d");
+ });
+
+ testChildrenIds(containerAcc, ["a", "b", "c", "d"]);
+ }
+);
diff --git a/accessible/tests/browser/tree/browser_browser_element.js b/accessible/tests/browser/tree/browser_browser_element.js
new file mode 100644
index 0000000000..82be24d93c
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_browser_element.js
@@ -0,0 +1,16 @@
+/* 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 });
+
+// Test that the tree is correct for browser elements containing remote
+// documents.
+addAccessibleTask(`test`, async function (browser, docAcc) {
+ // testAccessibleTree also verifies childCount, indexInParent and parent.
+ testAccessibleTree(browser, {
+ INTERNAL_FRAME: [{ DOCUMENT: [{ TEXT_LEAF: [] }] }],
+ });
+});
diff --git a/accessible/tests/browser/tree/browser_css_content_visibility.js b/accessible/tests/browser/tree/browser_css_content_visibility.js
new file mode 100644
index 0000000000..798e409d86
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_css_content_visibility.js
@@ -0,0 +1,121 @@
+/* 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/role.js */
+loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
+
+const snippet1 = `
+ <style>
+ #container {
+ width: 150px;
+ height: 150px;
+ background: lightblue;
+ }
+ .hidden {
+ content-visibility: hidden;
+ }
+ .auto {
+ content-visibility: auto;
+ }
+ </style>
+ <div id="container">
+ <div class="hidden" id="hidden-target">
+ hidden target
+ <div id="hidden-child">
+ hidden child
+ </div>
+ </div>
+ <div class="auto" id="auto-target">
+ auto target
+ <div id="auto-child">
+ auto child
+ </div>
+ </div>
+ </div>
+ `;
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["layout.css.content-visibility.enabled", true]],
+ });
+});
+
+// Check if the element specified with `content-visibility` property is accessible
+addAccessibleTask(
+ snippet1,
+ async function (browser, accDoc) {
+ const container = findAccessibleChildByID(accDoc, "container");
+ ok(
+ findAccessibleChildByID(container, "hidden-target"),
+ "hidden-target is accessible"
+ );
+ ok(
+ findAccessibleChildByID(container, "auto-target"),
+ "auto-target is accessible"
+ );
+
+ // The test checks if the child element of the element specified with
+ // `content-visibility: hidden` is ignored from a11y tree
+ let target = findAccessibleChildByID(accDoc, "hidden-target");
+ ok(
+ !findAccessibleChildByID(target, "hidden-child"),
+ "Children of hidden-target is not accessible"
+ );
+
+ // The test checks if the child element of the element specified with
+ // `content-visibility: auto` is showen in a11y tree
+ target = findAccessibleChildByID(accDoc, "auto-target");
+ ok(
+ findAccessibleChildByID(target, "auto-child"),
+ "Children of auto-target is accessible"
+ );
+ },
+ { iframe: true, remoteIframe: true, chrome: true }
+);
+
+// Check if the element having `display: contents` and a child of `content-visibility: hidden` element isn't accessible
+const snippet2 = `
+ <style>
+ #target {
+ width: 150px;
+ height: 150px;
+ background-color: lightblue;
+ }
+ #child {
+ width: 100px;
+ height: 100px;
+ background-color: lightgreen;
+ }
+ #grandchild {
+ width: 50px;
+ height: 50px;
+ background-color: red;
+ }
+ .hidden {
+ content-visibility: hidden;
+ }
+ .display-contents {
+ display: contents;
+ }
+ </style>
+ <div id="target" class="hidden">
+ <div id="child" class="display-contents">
+ <div id="grandchild"></div>
+ </div>
+ </div>
+ `;
+
+addAccessibleTask(
+ snippet2,
+ async function (browser, accDoc) {
+ const target = findAccessibleChildByID(accDoc, "target");
+ ok(
+ !findAccessibleChildByID(target, "child"),
+ "Element having `display: contents` and a child of `content-visibility: hidden` element isn't accessible"
+ );
+ testAccessibleTree(target, { SECTION: [] });
+ },
+ { iframe: true, remoteIframe: true, chrome: true }
+);
diff --git a/accessible/tests/browser/tree/browser_general.js b/accessible/tests/browser/tree/browser_general.js
new file mode 100644
index 0000000000..0d16271a36
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_general.js
@@ -0,0 +1,128 @@
+/* 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 adding `overflow:hidden;` styling to a div causes it to
+ * get an accessible.
+ */
+addAccessibleTask(`<p>hello world</p>`, async function (browser, docAcc) {
+ const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding div element");
+ await contentSpawnMutation(
+ browser,
+ { unexpected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ const d = content.document.createElement("div");
+ content.document.body.appendChild(d);
+ }
+ );
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding overflow:hidden styling to div");
+ await contentSpawnMutation(
+ browser,
+ { expected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ content.document.body.lastElementChild.setAttribute(
+ "style",
+ "overflow:hidden;"
+ );
+ }
+ );
+
+ testAccessibleTree(docAcc, {
+ DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }, { TEXT_CONTAINER: [] }],
+ });
+});
+
+/**
+ * Verify adding `overflow:scroll;` styling to a div causes
+ * it to get an accessible.
+ */
+addAccessibleTask(`<p>hello world</p>`, async function (browser, docAcc) {
+ const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding div element");
+ await contentSpawnMutation(
+ browser,
+ { unexpected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ const d = content.document.createElement("div");
+ content.document.body.appendChild(d);
+ }
+ );
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding overflow:scroll styling to div");
+ await contentSpawnMutation(
+ browser,
+ { expected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ content.document.body.lastElementChild.setAttribute(
+ "style",
+ "overflow:scroll;"
+ );
+ }
+ );
+
+ testAccessibleTree(docAcc, {
+ DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }, { TEXT_CONTAINER: [] }],
+ });
+});
+
+/**
+ * Verify adding `overflow:auto;` styling to a div causes
+ * it to get an accessible, but `overflow:visible` does not.
+ */
+addAccessibleTask(`<p>hello world</p>`, async function (browser, docAcc) {
+ const originalTree = { DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }] };
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding div element");
+ await contentSpawnMutation(
+ browser,
+ { unexpected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ const d = content.document.createElement("div");
+ content.document.body.appendChild(d);
+ }
+ );
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding overflow:visible styling to div");
+ await contentSpawnMutation(
+ browser,
+ { unexpected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ content.document.body.lastElementChild.setAttribute(
+ "style",
+ "overflow:visible;"
+ );
+ }
+ );
+
+ testAccessibleTree(docAcc, originalTree);
+ info("Adding overflow:auto styling to div");
+ await contentSpawnMutation(
+ browser,
+ { expected: [[EVENT_REORDER, docAcc]] },
+ function () {
+ content.document.body.lastElementChild.setAttribute(
+ "style",
+ "overflow:auto;"
+ );
+ }
+ );
+
+ testAccessibleTree(docAcc, {
+ DOCUMENT: [{ PARAGRAPH: [{ TEXT_LEAF: [] }] }, { TEXT_CONTAINER: [] }],
+ });
+});
diff --git a/accessible/tests/browser/tree/browser_lazy_tabs.js b/accessible/tests/browser/tree/browser_lazy_tabs.js
new file mode 100644
index 0000000000..f7aa9bdeb2
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_lazy_tabs.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that lazy background tabs aren't unintentionally loaded when building
+// the a11y tree (bug 1700708).
+addAccessibleTask(``, async function (browser, accDoc) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.sessionstore.restore_on_demand", true]],
+ });
+
+ info("Opening a new window");
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+ // Window is opened with a blank tab.
+ info("Loading second tab");
+ await BrowserTestUtils.openNewForegroundTab({
+ gBrowser: win.gBrowser,
+ url: "data:text/html,2",
+ });
+ info("Loading third tab");
+ await BrowserTestUtils.openNewForegroundTab({
+ gBrowser: win.gBrowser,
+ url: "data:text/html,3",
+ });
+ info("Closing the window");
+ await BrowserTestUtils.closeWindow(win);
+
+ is(SessionStore.getClosedWindowCount(), 1, "Should have a window to restore");
+ info("Restoring the window");
+ win = SessionStore.undoCloseWindow(0);
+ await BrowserTestUtils.waitForEvent(win, "SSWindowStateReady");
+ await BrowserTestUtils.waitForEvent(
+ win.gBrowser.tabContainer,
+ "SSTabRestored"
+ );
+ is(win.gBrowser.tabs.length, 3, "3 tabs restored");
+ ok(win.gBrowser.tabs[2].selected, "Third tab selected");
+ ok(getAccessible(win.gBrowser.tabs[1]), "Second tab has accessible");
+ ok(!win.gBrowser.browsers[1].isConnected, "Second tab is lazy");
+ info("Closing the restored window");
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/accessible/tests/browser/tree/browser_link.js b/accessible/tests/browser/tree/browser_link.js
new file mode 100644
index 0000000000..b0ff992365
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_link.js
@@ -0,0 +1,208 @@
+/* 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 that an anchor element reports a generic role without an href
+ * attribute and reports a LINK role with it present. Verify that these roles
+ * change as the attribute appears and disappears.
+ */
+addAccessibleTask(
+ `
+<a id="link">test</a>
+ `,
+ async function (browser, accDoc) {
+ let link = findAccessibleChildByID(accDoc, "link");
+ is(link.role, ROLE_TEXT, "Checking role of anchor element without href");
+
+ let onHideAndShow = waitForEvents({
+ expected: [
+ [EVENT_HIDE, link],
+ [EVENT_SHOW, "link"],
+ ],
+ });
+ info("Adding an href to the anchor element");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("link").setAttribute("href", "#");
+ });
+ await onHideAndShow;
+
+ link = findAccessibleChildByID(accDoc, "link");
+ is(link.role, ROLE_LINK, "Checking role of anchor element with href");
+
+ onHideAndShow = waitForEvents({
+ expected: [
+ [EVENT_HIDE, link],
+ [EVENT_SHOW, "link"],
+ ],
+ });
+ info("Removing the href from the anchor element");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("link").removeAttribute("href");
+ });
+ await onHideAndShow;
+ link = findAccessibleChildByID(accDoc, "link");
+ is(link.role, ROLE_TEXT, "Checking role of anchor element without href");
+ },
+ {
+ chrome: true,
+ topLevel: true,
+ iframe: true,
+ remoteIframe: true,
+ }
+);
+
+/**
+ * Verify that an anchor element reports a generic role without a click listener
+ * and reports a LINK role with it present. Verify that these roles change as
+ * the click listener appears.
+ */
+addAccessibleTask(
+ `
+<a id="link">test</a>
+ `,
+ async function (browser, accDoc) {
+ let link = findAccessibleChildByID(accDoc, "link");
+ is(
+ link.role,
+ ROLE_TEXT,
+ "Checking role of anchor element without click listener"
+ );
+
+ let onHideAndShow = waitForEvents({
+ expected: [
+ [EVENT_HIDE, link],
+ [EVENT_SHOW, "link"],
+ ],
+ });
+ info("Adding a click listener to the anchor element");
+ await invokeContentTask(browser, [], () => {
+ content.document
+ .getElementById("link")
+ .addEventListener("click", () => {});
+ });
+ await onHideAndShow;
+
+ link = findAccessibleChildByID(accDoc, "link");
+ is(
+ link.role,
+ ROLE_LINK,
+ "Checking role of anchor element with click listener"
+ );
+ },
+ {
+ chrome: true,
+ topLevel: true,
+ iframe: true,
+ remoteIframe: true,
+ }
+);
+
+/**
+ * Verify that an area element reports a generic role without an href
+ * attribute and reports a LINK role with it present. Verify that these roles
+ * change as the attribute appears and disappears.
+ */
+addAccessibleTask(
+ `
+<map name="map">
+ <area id="link">
+</map>
+<img id="img" usemap="#map" src="http://example.com/a11y/accessible/tests/mochitest/letters.gif">
+`,
+ async function (browser, accDoc) {
+ let link = findAccessibleChildByID(accDoc, "link");
+ is(link.role, ROLE_TEXT, "Checking role of area element without href");
+
+ let img = findAccessibleChildByID(accDoc, "img");
+ let onHideAndShow = waitForEvents({
+ expected: [
+ [EVENT_HIDE, img],
+ [EVENT_SHOW, "img"],
+ ],
+ });
+ info("Adding an href to the area element");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("link").setAttribute("href", "#");
+ });
+ await onHideAndShow;
+
+ link = findAccessibleChildByID(accDoc, "link");
+ is(link.role, ROLE_LINK, "Checking role of area element with href");
+
+ img = findAccessibleChildByID(accDoc, "img");
+ onHideAndShow = waitForEvents({
+ expected: [
+ [EVENT_HIDE, img],
+ [EVENT_SHOW, "img"],
+ ],
+ });
+ info("Removing the href from the area element");
+ await invokeContentTask(browser, [], () => {
+ content.document.getElementById("link").removeAttribute("href");
+ });
+ await onHideAndShow;
+ link = findAccessibleChildByID(accDoc, "link");
+ is(link.role, ROLE_TEXT, "Checking role of area element without href");
+ },
+ {
+ chrome: true,
+ topLevel: true,
+ iframe: true,
+ remoteIframe: true,
+ }
+);
+
+/**
+ * Verify that an area element reports a generic role without a click listener
+ * and reports a LINK role with it present. Verify that these roles change as
+ * the click listener appears.
+ */
+addAccessibleTask(
+ `
+<map name="map">
+ <area id="link">
+</map>
+<img id="img" usemap="#map" src="http://example.com/a11y/accessible/tests/mochitest/letters.gif">
+ `,
+ async function (browser, accDoc) {
+ let link = findAccessibleChildByID(accDoc, "link");
+ is(
+ link.role,
+ ROLE_TEXT,
+ "Checking role of area element without click listener"
+ );
+
+ let img = findAccessibleChildByID(accDoc, "img");
+ let onHideAndShow = waitForEvents({
+ expected: [
+ [EVENT_HIDE, img],
+ [EVENT_SHOW, "img"],
+ ],
+ });
+ info("Adding a click listener to the area element");
+ await invokeContentTask(browser, [], () => {
+ content.document
+ .getElementById("link")
+ .addEventListener("click", () => {});
+ });
+ await onHideAndShow;
+
+ link = findAccessibleChildByID(accDoc, "link");
+ is(
+ link.role,
+ ROLE_LINK,
+ "Checking role of area element with click listener"
+ );
+ },
+ {
+ chrome: true,
+ topLevel: true,
+ iframe: true,
+ remoteIframe: true,
+ }
+);
diff --git a/accessible/tests/browser/tree/browser_searchbar.js b/accessible/tests/browser/tree/browser_searchbar.js
new file mode 100644
index 0000000000..600c14e143
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_searchbar.js
@@ -0,0 +1,96 @@
+"use strict";
+
+/* import-globals-from ../../mochitest/role.js */
+loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
+
+// eslint-disable-next-line camelcase
+add_task(async function test_searchbar_a11y_tree() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.search.widget.inNavBar", true]],
+ });
+
+ // This used to rely on the implied 100ms initial timer of
+ // TestUtils.waitForCondition. See bug 1700735.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 100));
+ let searchbar = await TestUtils.waitForCondition(
+ () => document.getElementById("searchbar"),
+ "wait for search bar to appear"
+ );
+
+ // Make sure the popup has been rendered so it shows up in the a11y tree.
+ let popup = document.getElementById("PopupSearchAutoComplete");
+ let promise = Promise.all([
+ BrowserTestUtils.waitForEvent(popup, "popupshown", false),
+ waitForEvent(EVENT_SHOW, popup),
+ ]);
+ searchbar.textbox.openPopup();
+ await promise;
+
+ let TREE = {
+ role: ROLE_EDITCOMBOBOX,
+
+ children: [
+ // image button toggling the results list
+ {
+ role: ROLE_BUTTONMENU,
+ children: [],
+ },
+
+ // input element
+ {
+ role: ROLE_ENTRY,
+ children: [],
+ },
+
+ // context menu
+ {
+ role: ROLE_COMBOBOX_LIST,
+ children: [],
+ },
+
+ // result list
+ {
+ role: ROLE_GROUPING,
+ // not testing the structure inside the result list
+ },
+ ],
+ };
+
+ testAccessibleTree(searchbar, TREE);
+
+ promise = Promise.all([
+ BrowserTestUtils.waitForEvent(popup, "popuphidden", false),
+ waitForEvent(EVENT_HIDE, popup),
+ ]);
+ searchbar.textbox.closePopup();
+ await promise;
+
+ TREE = {
+ role: ROLE_EDITCOMBOBOX,
+
+ children: [
+ // image button toggling the results list
+ {
+ role: ROLE_BUTTONMENU,
+ children: [],
+ },
+
+ // input element
+ {
+ role: ROLE_ENTRY,
+ children: [],
+ },
+
+ // context menu
+ {
+ role: ROLE_COMBOBOX_LIST,
+ children: [],
+ },
+
+ // the result list should be removed from the tree on popuphidden
+ ],
+ };
+
+ testAccessibleTree(searchbar, TREE);
+});
diff --git a/accessible/tests/browser/tree/browser_shadowdom.js b/accessible/tests/browser/tree/browser_shadowdom.js
new file mode 100644
index 0000000000..6d9f06f9ff
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_shadowdom.js
@@ -0,0 +1,98 @@
+/* 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 REORDER = { expected: [[EVENT_REORDER, "container"]] };
+
+// Dynamically inserted slotted accessible elements should be in
+// the accessible tree.
+const snippet = `
+<script>
+customElements.define("x-el", class extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({ mode: "open" });
+ this.shadowRoot.innerHTML =
+ "<div role='presentation'><slot></slot></div>";
+ }
+});
+</script>
+<x-el id="container" role="group"><label id="l1">label1</label></x-el>
+`;
+
+addAccessibleTask(snippet, async function (browser, accDoc) {
+ let container = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(container, ["l1"]);
+
+ await contentSpawnMutation(browser, REORDER, function () {
+ let labelEl = content.document.createElement("label");
+ labelEl.id = "l2";
+
+ let containerEl = content.document.getElementById("container");
+ containerEl.appendChild(labelEl);
+ });
+
+ testChildrenIds(container, ["l1", "l2"]);
+});
+
+// Dynamically inserted not accessible custom element containing an accessible
+// in its shadow DOM.
+const snippet2 = `
+<script>
+customElements.define("x-el2", class extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({ mode: "open" });
+ this.shadowRoot.innerHTML = "<input id='input'>";
+ }
+});
+</script>
+<div role="group" id="container"></div>
+`;
+
+addAccessibleTask(snippet2, async function (browser, accDoc) {
+ let container = findAccessibleChildByID(accDoc, "container");
+
+ await contentSpawnMutation(browser, REORDER, function () {
+ content.document.getElementById("container").innerHTML = "<x-el2></x-el2>";
+ });
+
+ testChildrenIds(container, ["input"]);
+});
+
+/**
+ * Ensure that changing the slot on the body while moving the body doesn't
+ * try to remove the DocAccessible. We test this here instead of in
+ * accessible/tests/mochitest/treeupdate/test_shadow_slots.html because this
+ * messes with the body element and we don't want that to impact other tests.
+ */
+addAccessibleTask(
+ `
+<div id="host"></div>
+<script>
+ const host = document.getElementById("host");
+ host.attachShadow({ mode: "open" });
+ const emptyScript = document.createElement("script");
+ emptyScript.id = "emptyScript";
+ document.head.append(emptyScript);
+</script>
+ `,
+ async function (browser, docAcc) {
+ info("Moving body and setting slot on body");
+ let reordered = waitForEvent(EVENT_REORDER, docAcc);
+ await invokeContentTask(browser, [], () => {
+ const host = content.document.getElementById("host");
+ const emptyScript = content.document.getElementById("emptyScript");
+ const body = content.document.body;
+ emptyScript.append(host);
+ host.append(body);
+ body.slot = "";
+ });
+ await reordered;
+ is(docAcc.childCount, 0, "document has no children after body move");
+ },
+ { chrome: true, topLevel: true, iframe: true, remoteIframe: true }
+);
diff --git a/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js b/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js
new file mode 100644
index 0000000000..623f2640f0
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function promiseEventDocumentLoadComplete(expectedURL) {
+ return new Promise(resolve => {
+ waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, event => {
+ try {
+ if (
+ event.accessible.QueryInterface(nsIAccessibleDocument).URL ==
+ expectedURL
+ ) {
+ resolve(event.accessible.QueryInterface(nsIAccessibleDocument));
+ return true;
+ }
+ return false;
+ } catch (e) {
+ return false;
+ }
+ });
+ });
+}
+
+add_task(async function testInDataURI() {
+ const kURL = "data:text/html,Some text";
+ const waitForDocumentLoadComplete = promiseEventDocumentLoadComplete("");
+ await BrowserTestUtils.withNewTab(kURL, async browser => {
+ is(
+ (await waitForDocumentLoadComplete).URL,
+ "",
+ "nsIAccessibleDocument.URL shouldn't return data URI"
+ );
+ });
+});
+
+add_task(async function testInHTTPSURIContainingPrivateThings() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.auth.confirmAuth.enabled", false]],
+ });
+ const kURL =
+ "https://username:password@example.com/browser/toolkit/content/tests/browser/file_empty.html?query=some#ref";
+ const kURLWithoutUserPass =
+ "https://example.com/browser/toolkit/content/tests/browser/file_empty.html?query=some#ref";
+ const waitForDocumentLoadComplete =
+ promiseEventDocumentLoadComplete(kURLWithoutUserPass);
+ await BrowserTestUtils.withNewTab(kURL, async browser => {
+ is(
+ (await waitForDocumentLoadComplete).URL,
+ kURLWithoutUserPass,
+ "nsIAccessibleDocument.URL shouldn't contain user/pass section"
+ );
+ });
+});
diff --git a/accessible/tests/browser/tree/head.js b/accessible/tests/browser/tree/head.js
new file mode 100644
index 0000000000..b9c787e9e2
--- /dev/null
+++ b/accessible/tests/browser/tree/head.js
@@ -0,0 +1,33 @@
+/* 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";
+
+/* exported testChildrenIds */
+
+// 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 }
+);
+
+/*
+ * A test function for comparing the IDs of an accessible's children
+ * with an expected array of IDs.
+ */
+function testChildrenIds(acc, expectedIds) {
+ let ids = arrayFromChildren(acc).map(child => getAccessibleDOMNodeID(child));
+ Assert.deepEqual(
+ ids,
+ expectedIds,
+ `Children for ${getAccessibleDOMNodeID(acc)} are wrong.`
+ );
+}