diff options
Diffstat (limited to 'accessible/tests/browser/tree')
-rw-r--r-- | accessible/tests/browser/tree/browser.toml | 31 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_aria_owns.js | 278 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_browser_element.js | 16 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_css_content_visibility.js | 121 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_general.js | 128 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_lazy_tabs.js | 43 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_link.js | 208 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_searchbar.js | 96 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_shadowdom.js | 98 | ||||
-rw-r--r-- | accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js | 54 | ||||
-rw-r--r-- | accessible/tests/browser/tree/head.js | 33 |
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.` + ); +} |