diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:13:27 +0000 |
commit | 40a355a42d4a9444dc753c04c6608dade2f06a23 (patch) | |
tree | 871fc667d2de662f171103ce5ec067014ef85e61 /accessible/tests/browser | |
parent | Adding upstream version 124.0.1. (diff) | |
download | firefox-upstream/125.0.1.tar.xz firefox-upstream/125.0.1.zip |
Adding upstream version 125.0.1.upstream/125.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'accessible/tests/browser')
65 files changed, 1320 insertions, 191 deletions
diff --git a/accessible/tests/browser/atk/browser_role.js b/accessible/tests/browser/atk/browser_role.js index 7b870b3337..47cc5ef28f 100644 --- a/accessible/tests/browser/atk/browser_role.js +++ b/accessible/tests/browser/atk/browser_role.js @@ -11,7 +11,7 @@ addAccessibleTask( ` <p id="p">p</p> `, - async function (browser, docAcc) { + async function () { let role = await runPython(` global doc doc = getDoc() diff --git a/accessible/tests/browser/atk/browser_table.js b/accessible/tests/browser/atk/browser_table.js index 98b3270465..55452709e8 100644 --- a/accessible/tests/browser/atk/browser_table.js +++ b/accessible/tests/browser/atk/browser_table.js @@ -20,7 +20,7 @@ addAccessibleTask( </tr> </table> `, - async function (browser, docAcc) { + async function () { let result = await runPython(` global doc doc = getDoc() diff --git a/accessible/tests/browser/bounds/browser_accessible_moved.js b/accessible/tests/browser/bounds/browser_accessible_moved.js index 307c680000..a62f7ad8d0 100644 --- a/accessible/tests/browser/bounds/browser_accessible_moved.js +++ b/accessible/tests/browser/bounds/browser_accessible_moved.js @@ -12,10 +12,10 @@ function assertBoundsNonZero(acc) { let width = {}; let height = {}; acc.getBounds(x, y, width, height); - ok(x.value > 0, "x is non-0"); - ok(y.value > 0, "y is non-0"); - ok(width.value > 0, "width is non-0"); - ok(height.value > 0, "height is non-0"); + Assert.greater(x.value, 0, "x is non-0"); + Assert.greater(y.value, 0, "y is non-0"); + Assert.greater(width.value, 0, "width is non-0"); + Assert.greater(height.value, 0, "height is non-0"); } /** diff --git a/accessible/tests/browser/bounds/browser_caret_rect.js b/accessible/tests/browser/bounds/browser_caret_rect.js index ac0ee3aa50..9f5cac33a5 100644 --- a/accessible/tests/browser/bounds/browser_caret_rect.js +++ b/accessible/tests/browser/bounds/browser_caret_rect.js @@ -46,7 +46,7 @@ async function testCaretRect(browser, docAcc, id, offset) { ); const [caretX, caretY, caretW, caretH] = await getCaretRect(browser, id); if (atEnd) { - ok(caretX > charX.value, "Caret x after last character x"); + Assert.greater(caretX, charX.value, "Caret x after last character x"); } else { is(caretX, charX.value, "Caret x same as character x"); } diff --git a/accessible/tests/browser/bounds/browser_test_display_contents.js b/accessible/tests/browser/bounds/browser_test_display_contents.js index db1bfce178..4111ac5a81 100644 --- a/accessible/tests/browser/bounds/browser_test_display_contents.js +++ b/accessible/tests/browser/bounds/browser_test_display_contents.js @@ -16,7 +16,11 @@ async function testContentBounds(browser, acc) { is(x, expectedX, "Wrong x coordinate of " + prettyAccName); is(y, expectedY, "Wrong y coordinate of " + prettyAccName); is(width, expectedWidth, "Wrong width of " + prettyAccName); - ok(height >= expectedHeight, "Wrong height of " + prettyAccName); + Assert.greaterOrEqual( + height, + expectedHeight, + "Wrong height of " + prettyAccName + ); } async function runTests(browser, accDoc) { diff --git a/accessible/tests/browser/bounds/browser_test_iframe_transform.js b/accessible/tests/browser/bounds/browser_test_iframe_transform.js index a44ab75faf..015d466da9 100644 --- a/accessible/tests/browser/bounds/browser_test_iframe_transform.js +++ b/accessible/tests/browser/bounds/browser_test_iframe_transform.js @@ -68,7 +68,7 @@ function testBoundsWithOffset(browser, iframeDocAcc, id, domElmBounds, offset) { addAccessibleTask( `<div id='${ELEM_ID}'>hello world</div>`, - async function (browser, iframeDocAcc, contentDocAcc) { + async function (browser, iframeDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); await testBoundsWithContent(iframeDocAcc, ELEM_ID, browser); @@ -143,7 +143,7 @@ addAccessibleTask( */ addAccessibleTask( `<div id="div" style="width: 30px; height: 30px"></div>`, - async function (browser, accDoc, foo) { + async function (browser, accDoc) { const docWidth = () => { let width = {}; accDoc.getBounds({}, {}, width, {}); diff --git a/accessible/tests/browser/bounds/browser_test_simple_transform.js b/accessible/tests/browser/bounds/browser_test_simple_transform.js index 7197968b40..b5cb983e72 100644 --- a/accessible/tests/browser/bounds/browser_test_simple_transform.js +++ b/accessible/tests/browser/bounds/browser_test_simple_transform.js @@ -9,7 +9,7 @@ loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); // test basic translation addAccessibleTask( `<p id="translate">hello world</p>`, - async function (browser, iframeDocAcc, contentDocAcc) { + async function (browser, iframeDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); await testBoundsWithContent(iframeDocAcc, "translate", browser); @@ -42,7 +42,7 @@ addAccessibleTask( // test basic rotation addAccessibleTask( `<p id="rotate">hello world</p>`, - async function (browser, iframeDocAcc, contentDocAcc) { + async function (browser, iframeDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); await testBoundsWithContent(iframeDocAcc, "rotate", browser); @@ -60,7 +60,7 @@ addAccessibleTask( // test basic scale addAccessibleTask( `<p id="scale">hello world</p>`, - async function (browser, iframeDocAcc, contentDocAcc) { + async function (browser, iframeDocAcc) { ok(iframeDocAcc, "IFRAME document accessible is present"); await testBoundsWithContent(iframeDocAcc, "scale", browser); diff --git a/accessible/tests/browser/bounds/browser_test_zoom.js b/accessible/tests/browser/bounds/browser_test_zoom.js index ac84e485a4..1af84d61d8 100644 --- a/accessible/tests/browser/bounds/browser_test_zoom.js +++ b/accessible/tests/browser/bounds/browser_test_zoom.js @@ -16,7 +16,11 @@ async function testContentBounds(browser, acc) { is(x, expectedX, "Wrong x coordinate of " + prettyAccName); is(y, expectedY, "Wrong y coordinate of " + prettyAccName); is(width, expectedWidth, "Wrong width of " + prettyAccName); - ok(height >= expectedHeight, "Wrong height of " + prettyAccName); + Assert.greaterOrEqual( + height, + expectedHeight, + "Wrong height of " + prettyAccName + ); } async function runTests(browser, accDoc) { diff --git a/accessible/tests/browser/bounds/browser_zero_area.js b/accessible/tests/browser/bounds/browser_zero_area.js index c0f9db2673..80954fbd1a 100644 --- a/accessible/tests/browser/bounds/browser_zero_area.js +++ b/accessible/tests/browser/bounds/browser_zero_area.js @@ -67,10 +67,10 @@ addAccessibleTask( const radio = findAccessibleChildByID(accDoc, "radio"); const contentDPR = await getContentDPR(browser); const [x, y, width, height] = getBounds(radio, contentDPR); - ok(x < 0, "X coordinate should be negative"); - ok(y > 0, "Y coordinate should be positive"); - ok(width > 0, "Width should be positive"); - ok(height > 0, "Height should be positive"); + Assert.less(x, 0, "X coordinate should be negative"); + Assert.greater(y, 0, "Y coordinate should be positive"); + Assert.greater(width, 0, "Width should be positive"); + Assert.greater(height, 0, "Height should be positive"); // Note: the exact values of x, y, width, and height // are inconsistent with the DOM element values of those // fields, so we don't check our bounds against them with diff --git a/accessible/tests/browser/browser_shutdown_acc_reference.js b/accessible/tests/browser/browser_shutdown_acc_reference.js index 1768095f94..975eab55a0 100644 --- a/accessible/tests/browser/browser_shutdown_acc_reference.js +++ b/accessible/tests/browser/browser_shutdown_acc_reference.js @@ -36,7 +36,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_doc_acc_reference.js b/accessible/tests/browser/browser_shutdown_doc_acc_reference.js index 8f7bf6d423..7d999d7d13 100644 --- a/accessible/tests/browser/browser_shutdown_doc_acc_reference.js +++ b/accessible/tests/browser/browser_shutdown_doc_acc_reference.js @@ -28,7 +28,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js b/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js index 273fc7175d..1200e02116 100644 --- a/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js +++ b/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js @@ -39,7 +39,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js b/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js index af21b3dc4c..346aa9b6c4 100644 --- a/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js +++ b/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js @@ -39,7 +39,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js index e4091c5216..84429cf077 100644 --- a/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js +++ b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js @@ -30,7 +30,7 @@ add_task(async function () { <body id="body"><div id="div"></div></body> </html>`, }, - async function (browser) { + async function () { let docLoadedEvent = await docLoaded; let docAcc = docLoadedEvent.accessibleDocument; ok(docAcc, "Accessible document proxy is created"); @@ -46,7 +46,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js index f6eca362b0..b7356f3e6f 100644 --- a/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js +++ b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js @@ -30,7 +30,7 @@ add_task(async function () { <body id="body"><div id="div"></div></body> </html>`, }, - async function (browser) { + async function () { let docLoadedEvent = await docLoaded; let docAcc = docLoadedEvent.accessibleDocument; ok(docAcc, "Accessible document proxy is created"); @@ -46,7 +46,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_multi_reference.js b/accessible/tests/browser/browser_shutdown_multi_reference.js index a92f6faf61..49ee822afa 100644 --- a/accessible/tests/browser/browser_shutdown_multi_reference.js +++ b/accessible/tests/browser/browser_shutdown_multi_reference.js @@ -31,7 +31,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_parent_own_reference.js b/accessible/tests/browser/browser_shutdown_parent_own_reference.js index 472e977626..ffbe33f137 100644 --- a/accessible/tests/browser/browser_shutdown_parent_own_reference.js +++ b/accessible/tests/browser/browser_shutdown_parent_own_reference.js @@ -61,7 +61,7 @@ add_task(async function () { shutdownAccService(browser); await contentA11yShutdownObserver; const contentA11yShutdown = new Promise((resolve, reject) => - contentA11yShutdownPromise.then(flag => + contentA11yShutdownPromise.then(() => contentCanShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js b/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js index 7144cff019..47fa47da53 100644 --- a/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js +++ b/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js @@ -41,7 +41,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js b/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js index 6d4ad71f1e..72a424b21f 100644 --- a/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js +++ b/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js @@ -30,7 +30,7 @@ add_task(async function () { <body id="body"></body> </html>`, }, - async function (browser) { + async function () { let docLoadedEvent = await docLoaded; let docAcc = docLoadedEvent.accessibleDocument; ok(docAcc, "Accessible document proxy is created"); @@ -43,7 +43,7 @@ add_task(async function () { const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); await a11yShutdownObserver; const a11yShutdown = new Promise((resolve, reject) => - a11yShutdownPromise.then(flag => + a11yShutdownPromise.then(() => canShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/browser_shutdown_remote_own_reference.js b/accessible/tests/browser/browser_shutdown_remote_own_reference.js index a30d191b53..83c68689b7 100644 --- a/accessible/tests/browser/browser_shutdown_remote_own_reference.js +++ b/accessible/tests/browser/browser_shutdown_remote_own_reference.js @@ -108,7 +108,7 @@ add_task(async function () { const [contentA11yShutdownObserver, contentA11yShutdownPromise] = shutdownAccService(browser); const contentA11yShutdown = new Promise((resolve, reject) => - contentA11yShutdownPromise.then(flag => + contentA11yShutdownPromise.then(() => contentCanShutdown ? resolve() : reject("Accessible service was shut down incorrectly") diff --git a/accessible/tests/browser/e10s/browser.toml b/accessible/tests/browser/e10s/browser.toml index dfac6b5219..914f839993 100644 --- a/accessible/tests/browser/e10s/browser.toml +++ b/accessible/tests/browser/e10s/browser.toml @@ -18,9 +18,12 @@ support-files = [ ] prefs = [ "javascript.options.asyncstack_capture_debuggee_only=false", - "dom.element.popover.enabled=true" + "dom.element.popover.enabled=true", + "accessibility.ARIAElementReflection.enabled=true" ] +["browser_aria_activedescendant.js"] + # Caching tests ["browser_caching_actions.js"] diff --git a/accessible/tests/browser/e10s/browser_aria_activedescendant.js b/accessible/tests/browser/e10s/browser_aria_activedescendant.js new file mode 100644 index 0000000000..f58c5aab39 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_aria_activedescendant.js @@ -0,0 +1,485 @@ +/* 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 */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +async function synthFocus(browser, container, item) { + let focusPromise = waitForEvent(EVENT_FOCUS, item); + await invokeContentTask(browser, [container], _container => { + let elm = ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)(_container); + elm.focus(); + }); + await focusPromise; +} + +async function changeARIAActiveDescendant( + browser, + container, + itemId, + prevItemId, + elementReflection +) { + let expectedEvents = [[EVENT_FOCUS, itemId]]; + + if (prevItemId) { + info("A state change of the previous item precedes the new one."); + expectedEvents.push( + stateChangeEventArgs(prevItemId, EXT_STATE_ACTIVE, false, true) + ); + } + + expectedEvents.push( + stateChangeEventArgs(itemId, EXT_STATE_ACTIVE, true, true) + ); + + let expectedPromise = waitForEvents(expectedEvents); + await invokeContentTask( + browser, + [container, itemId, elementReflection], + (_container, _itemId, _elementReflection) => { + let getElm = ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document); + let elm = getElm(_container); + if (_elementReflection) { + elm.ariaActiveDescendantElement = getElm(_itemId); + } else { + elm.setAttribute("aria-activedescendant", _itemId); + } + } + ); + + await expectedPromise; +} + +async function clearARIAActiveDescendant( + browser, + container, + prevItemId, + defaultId, + elementReflection +) { + let expectedEvents = [[EVENT_FOCUS, defaultId || container]]; + if (prevItemId) { + expectedEvents.push( + stateChangeEventArgs(prevItemId, EXT_STATE_ACTIVE, false, true) + ); + } + + if (defaultId) { + expectedEvents.push( + stateChangeEventArgs(defaultId, EXT_STATE_ACTIVE, true, true) + ); + } + + let expectedPromise = waitForEvents(expectedEvents); + await invokeContentTask( + browser, + [container, elementReflection], + (_container, _elementReflection) => { + let elm = ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)(_container); + if (_elementReflection) { + elm.ariaActiveDescendantElement = null; + } else { + elm.removeAttribute("aria-activedescendant"); + } + } + ); + + await expectedPromise; +} + +async function insertItemNFocus( + browser, + container, + newItemID, + prevItemId, + elementReflection +) { + let expectedEvents = [ + [EVENT_SHOW, newItemID], + [EVENT_FOCUS, newItemID], + ]; + + if (prevItemId) { + info("A state change of the previous item precedes the new one."); + expectedEvents.push( + stateChangeEventArgs(prevItemId, EXT_STATE_ACTIVE, false, true) + ); + } + + expectedEvents.push( + stateChangeEventArgs(newItemID, EXT_STATE_ACTIVE, true, true) + ); + + let expectedPromise = waitForEvents(expectedEvents); + + await invokeContentTask( + browser, + [container, newItemID, elementReflection], + (_container, _newItemID, _elementReflection) => { + let elm = ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)(_container); + let itemElm = content.document.createElement("div"); + itemElm.setAttribute("id", _newItemID); + itemElm.setAttribute("role", "listitem"); + itemElm.textContent = _newItemID; + elm.appendChild(itemElm); + if (_elementReflection) { + elm.ariaActiveDescendantElement = itemElm; + } else { + elm.setAttribute("aria-activedescendant", _newItemID); + } + } + ); + + await expectedPromise; +} + +async function moveARIAActiveDescendantID(browser, fromID, toID) { + let expectedEvents = [ + [EVENT_FOCUS, toID], + stateChangeEventArgs(toID, EXT_STATE_ACTIVE, true, true), + ]; + + let expectedPromise = waitForEvents(expectedEvents); + await invokeContentTask(browser, [fromID, toID], (_fromID, _toID) => { + let orig = ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)(_toID); + if (orig) { + orig.id = ""; + } + ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)(_fromID).id = _toID; + }); + await expectedPromise; +} + +async function changeARIAActiveDescendantInvalid( + browser, + container, + invalidID = "invalid", + prevItemId = null +) { + let expectedEvents = [[EVENT_FOCUS, container]]; + if (prevItemId) { + expectedEvents.push( + stateChangeEventArgs(prevItemId, EXT_STATE_ACTIVE, false, true) + ); + } + + let expectedPromise = waitForEvents(expectedEvents); + await invokeContentTask( + browser, + [container, invalidID], + (_container, _invalidID) => { + let elm = ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)(_container); + elm.setAttribute("aria-activedescendant", _invalidID); + } + ); + + await expectedPromise; +} + +const LISTBOX_MARKUP = ` +<div role="listbox" aria-activedescendant="item1" id="listbox" tabindex="1" +aria-owns="item3"> +<div role="listitem" id="item1">item1</div> +<div role="listitem" id="item2">item2</div> +<div role="listitem" id="roaming" data-id="roaming">roaming</div> +<div role="listitem" id="roaming2" data-id="roaming2">roaming2</div> +</div> +<div role="listitem" id="item3">item3</div> +<div role="combobox" id="combobox"> +<input id="combobox_entry"> +<ul> + <li role="option" id="combobox_option1">option1</li> + <li role="option" id="combobox_option2">option2</li> +</ul> +</div>`; + +async function basicListboxTest(browser, elementReflection) { + await synthFocus(browser, "listbox", "item1"); + await changeARIAActiveDescendant( + browser, + "listbox", + "item2", + "item1", + elementReflection + ); + await changeARIAActiveDescendant( + browser, + "listbox", + "item3", + "item2", + elementReflection + ); + + info("Focus out of listbox"); + await synthFocus(browser, "combobox_entry", "combobox_entry"); + await changeARIAActiveDescendant( + browser, + "combobox", + "combobox_option2", + null, + elementReflection + ); + await changeARIAActiveDescendant( + browser, + "combobox", + "combobox_option1", + null, + elementReflection + ); + + info("Focus back in listbox"); + await synthFocus(browser, "listbox", "item3"); + await insertItemNFocus( + browser, + "listbox", + "item4", + "item3", + elementReflection + ); + + await clearARIAActiveDescendant( + browser, + "listbox", + "item4", + null, + elementReflection + ); + await changeARIAActiveDescendant( + browser, + "listbox", + "item1", + null, + elementReflection + ); +} + +addAccessibleTask( + LISTBOX_MARKUP, + async function (browser, docAcc) { + info("Test aria-activedescendant content attribute"); + await basicListboxTest(browser, false); + + await changeARIAActiveDescendantInvalid( + browser, + "listbox", + "invalid", + "item1" + ); + + await changeARIAActiveDescendant(browser, "listbox", "roaming"); + await moveARIAActiveDescendantID(browser, "roaming2", "roaming"); + await changeARIAActiveDescendantInvalid( + browser, + "listbox", + "roaming3", + "roaming" + ); + await moveARIAActiveDescendantID(browser, "roaming", "roaming3"); + }, + { topLevel: true, chrome: true } +); + +addAccessibleTask( + LISTBOX_MARKUP, + async function (browser, docAcc) { + info("Test ariaActiveDescendantElement element reflection"); + await basicListboxTest(browser, true); + }, + { topLevel: true, chrome: true } +); + +addAccessibleTask( + ` +<input id="activedesc_nondesc_input" aria-activedescendant="activedesc_nondesc_option"> +<div role="listbox"> + <div role="option" id="activedesc_nondesc_option">option</div> +</div>`, + async function (browser, docAcc) { + info("Test aria-activedescendant non-descendant"); + await synthFocus( + browser, + "activedesc_nondesc_input", + "activedesc_nondesc_option" + ); + }, + { topLevel: true, chrome: true } +); + +addAccessibleTask( + ` +<div id="shadow"></div> +<script> + let host = document.getElementById("shadow"); + let shadow = host.attachShadow({mode: "open"}); + let listbox = document.createElement("div"); + listbox.id = "shadowListbox"; + listbox.setAttribute("role", "listbox"); + listbox.setAttribute("tabindex", "0"); + shadow.appendChild(listbox); + let item = document.createElement("div"); + item.id = "shadowItem1"; + item.setAttribute("role", "option"); + listbox.appendChild(item); + listbox.setAttribute("aria-activedescendant", "shadowItem1"); + item = document.createElement("div"); + item.id = "shadowItem2"; + item.setAttribute("role", "option"); + listbox.appendChild(item); +</script>`, + async function (browser, docAcc) { + info("Test aria-activedescendant in shadow root"); + // We want to retrieve elements using their IDs inside the shadow root, so + // we define a custom get element by ID method that our utility functions + // above call into if it exists. + await invokeContentTask(browser, [], () => { + content.document._testGetElementById = id => + content.document.getElementById("shadow").shadowRoot.getElementById(id); + }); + + await synthFocus(browser, "shadowListbox", "shadowItem1"); + await changeARIAActiveDescendant( + browser, + "shadowListbox", + "shadowItem2", + "shadowItem1" + ); + info("Do it again with element reflection"); + await changeARIAActiveDescendant( + browser, + "shadowListbox", + "shadowItem1", + "shadowItem2", + true + ); + }, + { topLevel: true, chrome: true } +); + +addAccessibleTask( + ` +<div id="comboboxWithHiddenList" tabindex="0" role="combobox" aria-owns="hiddenList"> +</div> +<div id="hiddenList" hidden role="listbox"> + <div id="hiddenListOption" role="option"></div> +</div>`, + async function (browser, docAcc) { + info("Test simultaneous insertion, relocation and aria-activedescendant"); + await synthFocus( + browser, + "comboboxWithHiddenList", + "comboboxWithHiddenList" + ); + + testStates( + findAccessibleChildByID(docAcc, "comboboxWithHiddenList"), + STATE_FOCUSED + ); + let evtProm = Promise.all([ + waitForEvent(EVENT_FOCUS, "hiddenListOption"), + waitForStateChange("hiddenListOption", EXT_STATE_ACTIVE, true, true), + ]); + await invokeContentTask(browser, [], () => { + info("hiddenList is owned, so unhiding causes insertion and relocation."); + ( + content.document._testGetElementById || content.document.getElementById + ).bind(content.document)("hiddenList").hidden = false; + content.document + .getElementById("comboboxWithHiddenList") + .setAttribute("aria-activedescendant", "hiddenListOption"); + }); + await evtProm; + testStates( + findAccessibleChildByID(docAcc, "hiddenListOption"), + STATE_FOCUSED + ); + }, + { topLevel: true, chrome: true } +); + +addAccessibleTask( + ` +<custom-listbox id="custom-listbox1"> + <div role="listitem" id="l1_1"></div> + <div role="listitem" id="l1_2"></div> + <div role="listitem" id="l1_3"></div> +</custom-listbox> + +<custom-listbox id="custom-listbox2" aria-activedescendant="l2_1"> + <div role="listitem" id="l2_1"></div> + <div role="listitem" id="l2_2"></div> + <div role="listitem" id="l2_3"></div> +</custom-listbox> + +<script> +customElements.define("custom-listbox", + class extends HTMLElement { + constructor() { + super(); + this.tabIndex = "0" + this._internals = this.attachInternals(); + this._internals.role = "listbox"; + this._internals.ariaActiveDescendantElement = this.lastElementChild; + } + get internals() { + return this._internals; + } + } +); +</script>`, + async function (browser, docAcc) { + await synthFocus(browser, "custom-listbox1", "l1_3"); + + let evtProm = Promise.all([ + waitForEvent(EVENT_FOCUS, "l1_2"), + waitForStateChange("l1_3", EXT_STATE_ACTIVE, false, true), + waitForStateChange("l1_2", EXT_STATE_ACTIVE, true, true), + ]); + + await invokeContentTask(browser, [], () => { + content.document.getElementById( + "custom-listbox1" + ).internals.ariaActiveDescendantElement = + content.document.getElementById("l1_2"); + }); + + await evtProm; + + evtProm = Promise.all([ + waitForEvent(EVENT_FOCUS, "custom-listbox1"), + waitForStateChange("l1_2", EXT_STATE_ACTIVE, false, true), + ]); + + await invokeContentTask(browser, [], () => { + content.document.getElementById( + "custom-listbox1" + ).internals.ariaActiveDescendantElement = null; + }); + + await evtProm; + + await synthFocus(browser, "custom-listbox2", "l2_1"); + await clearARIAActiveDescendant(browser, "custom-listbox2", "l2_1", "l2_3"); + } +); diff --git a/accessible/tests/browser/e10s/browser_caching_attributes.js b/accessible/tests/browser/e10s/browser_caching_attributes.js index 139015061f..7a1f90ec13 100644 --- a/accessible/tests/browser/e10s/browser_caching_attributes.js +++ b/accessible/tests/browser/e10s/browser_caching_attributes.js @@ -733,7 +733,7 @@ addAccessibleTask( */ addAccessibleTask( `<div id="popover" popover>popover</div>`, - async function testIspopup(browser, docAcc) { + async function testIspopup(browser) { info("Showing popover"); let shown = waitForEvent(EVENT_SHOW, "popover"); await invokeContentTask(browser, [], () => { diff --git a/accessible/tests/browser/e10s/browser_caching_large_update.js b/accessible/tests/browser/e10s/browser_caching_large_update.js index ccf8a86921..9a36ac7326 100644 --- a/accessible/tests/browser/e10s/browser_caching_large_update.js +++ b/accessible/tests/browser/e10s/browser_caching_large_update.js @@ -8,59 +8,56 @@ * Test a large update which adds many thousands of Accessibles with a * lot of content in each. */ -addAccessibleTask( - `<main id="main" hidden></main>`, - async function (browser, docAcc) { - let shown = waitForEvent(EVENT_SHOW, "main"); - await invokeContentTask(browser, [], () => { - // Make a long string. - let text = ""; - for (let i = 0; i < 100; ++i) { - text += "a"; - } - // Create lots of nodes which include the long string. - const contMain = content.document.getElementById("main"); - // 15000 children of main. - for (let w = 0; w < 15000; ++w) { - // Each of those goes 9 deep. - let parent = contMain; - for (let d = 0; d < 10; ++d) { - const div = content.document.createElement("div"); - div.setAttribute("aria-label", `${w} ${d} ${text}`); - parent.append(div); - parent = div; - } - } - contMain.hidden = false; - }); - const main = (await shown).accessible; - is(main.childCount, 15000, "main has correct number of children"); - - // We don't want to output passes for every check, since that would output - // hundreds of thousands of lines, which slows the test to a crawl. Instead, - // output any failures and keep track of overall success/failure. - let treeOk = true; - function check(val, msg) { - if (!val) { - ok(false, msg); - treeOk = false; - } +addAccessibleTask(`<main id="main" hidden></main>`, async function (browser) { + let shown = waitForEvent(EVENT_SHOW, "main"); + await invokeContentTask(browser, [], () => { + // Make a long string. + let text = ""; + for (let i = 0; i < 100; ++i) { + text += "a"; } - - info("Checking tree"); + // Create lots of nodes which include the long string. + const contMain = content.document.getElementById("main"); + // 15000 children of main. for (let w = 0; w < 15000; ++w) { - let acc = main.getChildAt(w); - let parent = main; + // Each of those goes 9 deep. + let parent = contMain; for (let d = 0; d < 10; ++d) { - check(acc, `Got child ${w} depth ${d}`); - const name = `${w} ${d}`; - check(acc.name.startsWith(name + " "), `${name}: correct name`); - check(acc.parent == parent, `${name}: correct parent`); - parent = acc; - acc = acc.firstChild; + const div = content.document.createElement("div"); + div.setAttribute("aria-label", `${w} ${d} ${text}`); + parent.append(div); + parent = div; } } - // check() sets treeOk to false for any failure. - ok(treeOk, "Tree is correct"); + contMain.hidden = false; + }); + const main = (await shown).accessible; + is(main.childCount, 15000, "main has correct number of children"); + + // We don't want to output passes for every check, since that would output + // hundreds of thousands of lines, which slows the test to a crawl. Instead, + // output any failures and keep track of overall success/failure. + let treeOk = true; + function check(val, msg) { + if (!val) { + ok(false, msg); + treeOk = false; + } + } + + info("Checking tree"); + for (let w = 0; w < 15000; ++w) { + let acc = main.getChildAt(w); + let parent = main; + for (let d = 0; d < 10; ++d) { + check(acc, `Got child ${w} depth ${d}`); + const name = `${w} ${d}`; + check(acc.name.startsWith(name + " "), `${name}: correct name`); + check(acc.parent == parent, `${name}: correct parent`); + parent = acc; + acc = acc.firstChild; + } } -); + // check() sets treeOk to false for any failure. + ok(treeOk, "Tree is correct"); +}); diff --git a/accessible/tests/browser/e10s/browser_caching_name.js b/accessible/tests/browser/e10s/browser_caching_name.js index 55f506b85a..383d268d7d 100644 --- a/accessible/tests/browser/e10s/browser_caching_name.js +++ b/accessible/tests/browser/e10s/browser_caching_name.js @@ -477,7 +477,7 @@ markupTests.forEach(({ id, ruleset, markup, expected }) => markup, async function (browser, accDoc) { const observer = { - observe(subject, topic, data) { + observe(subject) { const event = subject.QueryInterface(nsIAccessibleEvent); console.log(eventToString(event)); }, diff --git a/accessible/tests/browser/e10s/browser_caching_relations_002.js b/accessible/tests/browser/e10s/browser_caching_relations_002.js index 072656eb5e..61d92ba4ac 100644 --- a/accessible/tests/browser/e10s/browser_caching_relations_002.js +++ b/accessible/tests/browser/e10s/browser_caching_relations_002.js @@ -293,7 +293,7 @@ addAccessibleTask( ); /** - * Test details relations on popovers and their invokers. + * Test details relations for the popovertarget content attribute. */ addAccessibleTask( ` @@ -304,7 +304,7 @@ addAccessibleTask( <div id="popover" popover>popover</div> <div id="details">details</div> `, - async function testPopover(browser, docAcc) { + async function testPopoverContent(browser, docAcc) { // The popover is hidden, so nothing should be referring to it. const hide = findAccessibleChildByID(docAcc, "hide"); await testCachedRelation(hide, RELATION_DETAILS, []); @@ -330,7 +330,7 @@ addAccessibleTask( await testCachedRelation(toggleSibling, RELATION_DETAILS, []); await testCachedRelation(popover, RELATION_DETAILS_FOR, toggle1); - info("Setting toggle2 popovertargetaction"); + info("Setting toggle2 popovertarget"); await invokeSetAttribute(browser, "toggle2", "popovertarget", "popover"); await testCachedRelation(toggle2, RELATION_DETAILS, popover); await testCachedRelation(popover, RELATION_DETAILS_FOR, [toggle1, toggle2]); @@ -364,3 +364,106 @@ addAccessibleTask( }, { chrome: false, topLevel: true } ); + +/** + * Test details relations for the popoverTargetElement WebIDL attribute. + */ +addAccessibleTask( + ` +<button id="toggle1">toggle1</button> +<button id="toggle2">toggle2</button> +between +<div id="popover1" popover>popover1</div> +<button id="toggle3">toggle3</button> +<div id="shadowHost"><template shadowrootmode="open"> + <button id="toggle4">toggle4</button> + between + <div id="popover2" popover>popover2</div> + <button id="toggle5">toggle5</button> +</template></div> +<script> + const toggle1 = document.getElementById("toggle1"); + const popover1 = document.getElementById("popover1"); + toggle1.popoverTargetElement = popover1; + const toggle3 = document.getElementById("toggle3"); + const shadow = document.getElementById("shadowHost").shadowRoot; + const toggle4 = shadow.getElementById("toggle4"); + const popover2 = shadow.getElementById("popover2"); + toggle3.popoverTargetElement = popover2; + toggle4.popoverTargetElement = popover2; + const toggle5 = shadow.getElementById("toggle5"); + toggle5.popoverTargetElement = popover1; +</script> + `, + async function testPopoverIdl(browser, docAcc) { + // No popover is showing, so there shouldn't be any details relations. + const toggle1 = findAccessibleChildByID(docAcc, "toggle1"); + await testCachedRelation(toggle1, RELATION_DETAILS, []); + const toggle2 = findAccessibleChildByID(docAcc, "toggle2"); + await testCachedRelation(toggle2, RELATION_DETAILS, []); + const toggle3 = findAccessibleChildByID(docAcc, "toggle3"); + await testCachedRelation(toggle3, RELATION_DETAILS, []); + const toggle4 = findAccessibleChildByID(docAcc, "toggle4"); + await testCachedRelation(toggle4, RELATION_DETAILS, []); + const toggle5 = findAccessibleChildByID(docAcc, "toggle5"); + await testCachedRelation(toggle5, RELATION_DETAILS, []); + + info("Showing popover1"); + let shown = waitForEvent(EVENT_SHOW, "popover1"); + toggle1.doAction(0); + const popover1 = (await shown).accessible; + await testCachedRelation(toggle1, RELATION_DETAILS, popover1); + // toggle5 is inside the shadow DOM and popover1 is outside, so the target + // is valid. + await testCachedRelation(toggle5, RELATION_DETAILS, popover1); + await testCachedRelation(popover1, RELATION_DETAILS_FOR, [ + toggle1, + toggle5, + ]); + info("Setting toggle2's popover target to popover1"); + await invokeContentTask(browser, [], () => { + const toggle2Dom = content.document.getElementById("toggle2"); + const popover1Dom = content.document.getElementById("popover1"); + toggle2Dom.popoverTargetElement = popover1Dom; + }); + await testCachedRelation(toggle2, RELATION_DETAILS, popover1); + await testCachedRelation(popover1, RELATION_DETAILS_FOR, [ + toggle1, + toggle2, + toggle5, + ]); + info("Clearing toggle2's popover target"); + await invokeContentTask(browser, [], () => { + const toggle2Dom = content.document.getElementById("toggle2"); + toggle2Dom.popoverTargetElement = null; + }); + await testCachedRelation(toggle2, RELATION_DETAILS, []); + await testCachedRelation(popover1, RELATION_DETAILS_FOR, [ + toggle1, + toggle5, + ]); + info("Hiding popover1"); + let hidden = waitForEvent(EVENT_HIDE, popover1); + toggle1.doAction(0); + await hidden; + await testCachedRelation(toggle1, RELATION_DETAILS, []); + await testCachedRelation(toggle2, RELATION_DETAILS, []); + await testCachedRelation(toggle5, RELATION_DETAILS, []); + + info("Showing popover2"); + shown = waitForEvent(EVENT_SHOW, "popover2"); + toggle4.doAction(0); + const popover2 = (await shown).accessible; + // toggle4 is in the same shadow DOM as popover2. + await testCachedRelation(toggle4, RELATION_DETAILS, popover2); + // toggle3 is outside popover2's shadow DOM, so the target isn't valid. + await testCachedRelation(toggle3, RELATION_DETAILS, []); + await testCachedRelation(popover2, RELATION_DETAILS_FOR, [toggle4]); + info("Hiding popover2"); + hidden = waitForEvent(EVENT_HIDE, popover2); + toggle4.doAction(0); + await hidden; + await testCachedRelation(toggle4, RELATION_DETAILS, []); + }, + { chrome: true, topLevel: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_states.js b/accessible/tests/browser/e10s/browser_caching_states.js index 37f8c46966..7292228f25 100644 --- a/accessible/tests/browser/e10s/browser_caching_states.js +++ b/accessible/tests/browser/e10s/browser_caching_states.js @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; +requestLongerTimeout(2); /* import-globals-from ../../mochitest/role.js */ /* import-globals-from ../../mochitest/states.js */ @@ -484,7 +485,7 @@ addAccessibleTask( ); /** - * Test caching of the expanded state for popover target element. + * Test caching of the expanded state for the popovertarget content attribute. */ addAccessibleTask( ` @@ -550,3 +551,157 @@ addAccessibleTask( }, { chrome: true, topLevel: true, remoteIframe: true } ); + +/** + * Test caching of the expanded state for the popoverTargetElement WebIDL + * attribute. + */ +addAccessibleTask( + ` +<button id="toggle1">toggle</button> +<div id="popover1" popover>popover1</div> +<button id="toggle2">toggle2</button> +<button id="toggle3">toggle3</button> +<div id="shadowHost"><template shadowrootmode="open"> + <button id="toggle4">toggle4</button> + <div id="popover2" popover>popover2</div> + <button id="toggle5">toggle5</button> +</template></div> +<script> + const toggle1 = document.getElementById("toggle1"); + const popover1 = document.getElementById("popover1"); + toggle1.popoverTargetElement = popover1; + const toggle3 = document.getElementById("toggle3"); + const shadow = document.getElementById("shadowHost").shadowRoot; + const toggle4 = shadow.getElementById("toggle4"); + const popover2 = shadow.getElementById("popover2"); + toggle3.popoverTargetElement = popover2; + toggle4.popoverTargetElement = popover2; + const toggle5 = shadow.getElementById("toggle5"); + toggle5.popoverTargetElement = popover1; +</script> + `, + async function (browser, docAcc) { + const toggle1 = findAccessibleChildByID(docAcc, "toggle1"); + // toggle1's popover target is set and connected to the document. + testStates(toggle1, STATE_COLLAPSED); + + const toggle2 = findAccessibleChildByID(docAcc, "toggle2"); + // toggle2's popover target isn't set yet. + testStates( + toggle2, + 0, + 0, + STATE_EXPANDED | STATE_COLLAPSED, + EXT_STATE_EXPANDABLE + ); + info("Setting toggle2's popoverTargetElement"); + let changed = waitForStateChange(toggle2, EXT_STATE_EXPANDABLE, true, true); + await invokeContentTask(browser, [], () => { + const toggle2Dom = content.document.getElementById("toggle2"); + const popover1 = content.document.getElementById("popover1"); + toggle2Dom.popoverTargetElement = popover1; + }); + await changed; + testStates(toggle2, STATE_COLLAPSED); + + const toggle5 = findAccessibleChildByID(docAcc, "toggle5"); + // toggle5 is inside the shadow DOM and popover1 is outside, so the target + // is valid. + testStates(toggle5, STATE_COLLAPSED); + + // Changes to the popover should fire events on all invokers. + const changeEvents = [ + [EVENT_STATE_CHANGE, toggle1], + [EVENT_STATE_CHANGE, toggle2], + [EVENT_STATE_CHANGE, toggle5], + ]; + info("Showing popover1"); + changed = waitForEvents(changeEvents); + toggle1.doAction(0); + await changed; + testStates(toggle1, STATE_EXPANDED); + testStates(toggle2, STATE_EXPANDED); + + info("Hiding popover1"); + changed = waitForEvents(changeEvents); + toggle1.doAction(0); + await changed; + testStates(toggle1, STATE_COLLAPSED); + testStates(toggle2, STATE_COLLAPSED); + + info("Clearing toggle1's popover target"); + changed = waitForStateChange(toggle1, EXT_STATE_EXPANDABLE, false, true); + await invokeContentTask(browser, [], () => { + const toggle1Dom = content.document.getElementById("toggle1"); + toggle1Dom.popoverTargetElement = null; + }); + await changed; + testStates( + toggle1, + 0, + 0, + STATE_EXPANDED | STATE_COLLAPSED, + EXT_STATE_EXPANDABLE + ); + + info("Setting toggle2's popover target to a disconnected node"); + changed = waitForStateChange(toggle2, EXT_STATE_EXPANDABLE, false, true); + await invokeContentTask(browser, [], () => { + const toggle2Dom = content.document.getElementById("toggle2"); + const popover3 = content.document.createElement("div"); + popover3.popover = "auto"; + popover3.textContent = "popover3"; + // We don't append popover3 anywhere, so it is disconnected. + toggle2Dom.popoverTargetElement = popover3; + }); + await changed; + testStates( + toggle2, + 0, + 0, + STATE_EXPANDED | STATE_COLLAPSED, + EXT_STATE_EXPANDABLE + ); + + const toggle3 = findAccessibleChildByID(docAcc, "toggle3"); + // toggle3 is outside popover2's shadow DOM, so the target isn't valid. + testStates( + toggle3, + 0, + 0, + STATE_EXPANDED | STATE_COLLAPSED, + EXT_STATE_EXPANDABLE + ); + const toggle4 = findAccessibleChildByID(docAcc, "toggle4"); + // toggle4 is in the same shadow DOM as popover2. + testStates(toggle4, STATE_COLLAPSED); + }, + { chrome: true, topLevel: true } +); + +/** + * Test the mixed state of indeterminate HTML checkboxes. + */ +addAccessibleTask( + `<input type="checkbox" id="checkbox">`, + async function testHTMLCheckboxMixed(browser, docAcc) { + const checkbox = findAccessibleChildByID(docAcc, "checkbox"); + testStates(checkbox, 0, 0, STATE_MIXED); + info("Setting indeterminate on checkbox"); + let changed = waitForStateChange(checkbox, STATE_MIXED, true); + await invokeContentTask(browser, [], () => { + content.document.getElementById("checkbox").indeterminate = true; + }); + await changed; + testStates(checkbox, STATE_MIXED); + info("Clearing indeterminate on checkbox"); + changed = waitForStateChange(checkbox, STATE_MIXED, false); + await invokeContentTask(browser, [], () => { + content.document.getElementById("checkbox").indeterminate = false; + }); + await changed; + testStates(checkbox, 0, 0, STATE_MIXED); + }, + { chrome: true, topLevel: true, iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_table.js b/accessible/tests/browser/e10s/browser_caching_table.js index 9c8bcb9616..0329e6411b 100644 --- a/accessible/tests/browser/e10s/browser_caching_table.js +++ b/accessible/tests/browser/e10s/browser_caching_table.js @@ -482,7 +482,7 @@ addAccessibleTask( */ addAccessibleTask( `<table><tr id="tr"></tr></table>`, - async function (browser, docAcc) { + async function (browser) { let reordered = waitForEvent(EVENT_REORDER, "tr"); await invokeContentTask(browser, [], () => { const iframe = content.document.createElement("iframe"); diff --git a/accessible/tests/browser/e10s/browser_caching_text_bounds.js b/accessible/tests/browser/e10s/browser_caching_text_bounds.js index 3e37bf7490..486e28df53 100644 --- a/accessible/tests/browser/e10s/browser_caching_text_bounds.js +++ b/accessible/tests/browser/e10s/browser_caching_text_bounds.js @@ -138,9 +138,13 @@ async function testLineWithNonRenderedSpace(docAcc, browser, id, length) { const w = {}; const h = {}; acc.getCharacterExtents(offset, x, y, w, h, COORDTYPE_SCREEN_RELATIVE); - ok(x.value > prevX, `${id}: offset ${offset} x is larger (${x.value})`); + Assert.greater( + x.value, + prevX, + `${id}: offset ${offset} x is larger (${x.value})` + ); prevX = x.value; - ok(w.value > 0, `${id}: offset ${offset} width > 0`); + Assert.greater(w.value, 0, `${id}: offset ${offset} width > 0`); } } @@ -566,7 +570,11 @@ c</textarea> {}, COORDTYPE_SCREEN_RELATIVE ); - ok(newY.value < oldY.value, "y coordinate smaller after scrolling down"); + Assert.less( + newY.value, + oldY.value, + "y coordinate smaller after scrolling down" + ); }, { chrome: true, topLevel: true, iframe: !true } ); diff --git a/accessible/tests/browser/e10s/browser_file_input.js b/accessible/tests/browser/e10s/browser_file_input.js index 238e48740e..4c68e8e6da 100644 --- a/accessible/tests/browser/e10s/browser_file_input.js +++ b/accessible/tests/browser/e10s/browser_file_input.js @@ -34,7 +34,7 @@ addAccessibleTask( function chooseFile(id) { return invokeContentTask(browser, [id], contentId => { const MockFilePicker = content.SpecialPowers.MockFilePicker; - MockFilePicker.init(content); + MockFilePicker.init(content.browsingContext); MockFilePicker.useBlobFile(); MockFilePicker.returnValue = MockFilePicker.returnOK; const input = content.document.getElementById(contentId); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js b/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js index a82fc4c04d..8ccbe58751 100644 --- a/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js +++ b/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js @@ -24,7 +24,7 @@ const snippet = ` addAccessibleTask( snippet, - async function (browser, accDoc) { + async function (browser) { await invokeFocus(browser, "select"); // Expand the select. A dropdown item should get focus. // Note that the dropdown is rendered in the parent process. diff --git a/accessible/tests/browser/events/browser_test_docload.js b/accessible/tests/browser/events/browser_test_docload.js index 78ac77fd8c..12076b3002 100644 --- a/accessible/tests/browser/events/browser_test_docload.js +++ b/accessible/tests/browser/events/browser_test_docload.js @@ -30,7 +30,7 @@ function urlChecker(url) { }; } -async function runTests(browser, accDoc) { +async function runTests(browser) { let onLoadEvents = waitForEvents({ expected: [ [EVENT_REORDER, getAccessible(browser)], diff --git a/accessible/tests/browser/events/browser_test_focus_browserui.js b/accessible/tests/browser/events/browser_test_focus_browserui.js index 969d336c74..2f67cb3681 100644 --- a/accessible/tests/browser/events/browser_test_focus_browserui.js +++ b/accessible/tests/browser/events/browser_test_focus_browserui.js @@ -11,7 +11,7 @@ loadScripts( { name: "role.js", dir: MOCHITESTS_DIR } ); -async function runTests(browser, accDoc) { +async function runTests(browser) { await SpecialPowers.pushPrefEnv({ // If Fission is disabled, the pref is no-op. set: [["fission.bfcacheInParent", true]], diff --git a/accessible/tests/browser/events/browser_test_focus_dialog.js b/accessible/tests/browser/events/browser_test_focus_dialog.js index 71485a678d..03a1b82dc1 100644 --- a/accessible/tests/browser/events/browser_test_focus_dialog.js +++ b/accessible/tests/browser/events/browser_test_focus_dialog.js @@ -11,7 +11,7 @@ loadScripts( { name: "role.js", dir: MOCHITESTS_DIR } ); -async function runTests(browser, accDoc) { +async function runTests(browser) { let onFocus = waitForEvent(EVENT_FOCUS, "button"); await SpecialPowers.spawn(browser, [], () => { content.document.getElementById("button").focus(); diff --git a/accessible/tests/browser/events/browser_test_focus_urlbar.js b/accessible/tests/browser/events/browser_test_focus_urlbar.js index 68b2b07f3c..647b837e9f 100644 --- a/accessible/tests/browser/events/browser_test_focus_urlbar.js +++ b/accessible/tests/browser/events/browser_test_focus_urlbar.js @@ -68,10 +68,10 @@ class TipTestProvider extends UrlbarProvider { get type() { return UrlbarUtils.PROVIDER_TYPE.PROFILE; } - isActive(context) { + isActive() { return true; } - isRestricting(context) { + isRestricting() { return true; } async startQuery(context, addCallback) { diff --git a/accessible/tests/browser/events/browser_test_scrolling.js b/accessible/tests/browser/events/browser_test_scrolling.js index 9678ee767b..d9425721bf 100644 --- a/accessible/tests/browser/events/browser_test_scrolling.js +++ b/accessible/tests/browser/events/browser_test_scrolling.js @@ -25,13 +25,15 @@ c</textarea> }); let [scrollEvent1, scrollEndEvent1] = await onScrolling; scrollEvent1.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEvent1.maxScrollY >= scrollEvent1.scrollY, + Assert.greaterOrEqual( + scrollEvent1.maxScrollY, + scrollEvent1.scrollY, "scrollY is within max" ); scrollEndEvent1.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEndEvent1.maxScrollY >= scrollEndEvent1.scrollY, + Assert.greaterOrEqual( + scrollEndEvent1.maxScrollY, + scrollEndEvent1.scrollY, "scrollY is within max" ); @@ -44,13 +46,15 @@ c</textarea> }); let [scrollEvent2, scrollEndEvent2] = await onScrolling; scrollEvent2.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEvent2.scrollY > scrollEvent1.scrollY, + Assert.greater( + scrollEvent2.scrollY, + scrollEvent1.scrollY, `${scrollEvent2.scrollY} > ${scrollEvent1.scrollY}` ); scrollEndEvent2.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEndEvent2.maxScrollY >= scrollEndEvent2.scrollY, + Assert.greaterOrEqual( + scrollEndEvent2.maxScrollY, + scrollEndEvent2.scrollY, "scrollY is within max" ); @@ -63,17 +67,20 @@ c</textarea> }); let [scrollEvent3, scrollEndEvent3] = await onScrolling; scrollEvent3.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEvent3.maxScrollX >= scrollEvent3.scrollX, + Assert.greaterOrEqual( + scrollEvent3.maxScrollX, + scrollEvent3.scrollX, "scrollX is within max" ); scrollEndEvent3.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEndEvent3.maxScrollX >= scrollEndEvent3.scrollX, + Assert.greaterOrEqual( + scrollEndEvent3.maxScrollX, + scrollEndEvent3.scrollX, "scrollY is within max" ); - ok( - scrollEvent3.scrollX > scrollEvent2.scrollX, + Assert.greater( + scrollEvent3.scrollX, + scrollEvent2.scrollX, `${scrollEvent3.scrollX} > ${scrollEvent2.scrollX}` ); @@ -87,13 +94,15 @@ c</textarea> }); let [scrollEvent4, scrollEndEvent4] = await onScrolling; scrollEvent4.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEvent4.maxScrollY >= scrollEvent4.scrollY, + Assert.greaterOrEqual( + scrollEvent4.maxScrollY, + scrollEvent4.scrollY, "scrollY is within max" ); scrollEndEvent4.QueryInterface(nsIAccessibleScrollingEvent); - ok( - scrollEndEvent4.maxScrollY >= scrollEndEvent4.scrollY, + Assert.greaterOrEqual( + scrollEndEvent4.maxScrollY, + scrollEndEvent4.scrollY, "scrollY is within max" ); @@ -119,7 +128,7 @@ addAccessibleTask( <p>b</p> <p id="c">c</p> `, - async function (browser, accDoc) { + async function (browser) { let onScrollingStart = waitForEvent(EVENT_SCROLLING_START, "c"); await SpecialPowers.spawn(browser, [], () => { content.location.hash = "#c"; @@ -137,7 +146,7 @@ addAccessibleTask( <h1 style="height: 300%;" id="inside-scrollable">test</h1> </div> `, - async function (browser, accDoc) { + async function (browser) { let onScrollingStart = waitForEvent( EVENT_SCROLLING_START, "inside-scrollable" diff --git a/accessible/tests/browser/mac/browser_app.js b/accessible/tests/browser/mac/browser_app.js index bedefae440..e7e18b5ddd 100644 --- a/accessible/tests/browser/mac/browser_app.js +++ b/accessible/tests/browser/mac/browser_app.js @@ -138,7 +138,7 @@ add_task(async () => { gBrowser, url: "about:license", }, - async browser => { + async () => { let root = await getMacAccessible(document); let rootChildCount = () => root.getAttributeValue("AXChildren").length; @@ -206,8 +206,10 @@ add_task(async () => { is(rootChildCount(), baseRootChildCount + 1, "Root has another child"); // Close popup + let hide = waitForMacEvent("AXUIElementDestroyed"); EventUtils.synthesizeKey("KEY_Escape"); await BrowserTestUtils.waitForPopupEvent(identityPopup, "hidden"); + await hide; // We're back to the base child count is(rootChildCount(), baseRootChildCount, "Root has the base child count"); @@ -225,7 +227,7 @@ add_task(async () => { // eslint-disable-next-line @microsoft/sdl/no-insecure-url url: "http://example.com", }, - async browser => { + async () => { let input = await getMacAccessible("urlbar-input"); is( input.getAttributeValue("AXValue"), @@ -238,6 +240,38 @@ add_task(async () => { }); /** + * Tests attributed text in nav bar has no invisible AXAttachments + */ +add_task(async () => { + await BrowserTestUtils.withNewTab( + { + gBrowser, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + url: "http://example.com", + }, + async () => { + let root = await getMacAccessible(document); + let navBar = await getMacAccessible("nav-bar"); + let elemRange = root.getParameterizedAttributeValue( + "AXTextMarkerRangeForUIElement", + navBar + ); + let attributedString = root.getParameterizedAttributeValue( + "AXAttributedStringForTextMarkerRange", + elemRange + ); + let attachmentRoles = attributedString.map(s => + s.AXAttachment ? s.AXAttachment.getAttributeValue("AXRole") : null + ); + ok( + !attachmentRoles.includes("AXMenu"), + "Collapsed menu should be embedded in attributed text" + ); + } + ); +}); + +/** * Test context menu */ add_task(async () => { diff --git a/accessible/tests/browser/mac/browser_bounds.js b/accessible/tests/browser/mac/browser_bounds.js index 09343d7c9d..bc7939cfe0 100644 --- a/accessible/tests/browser/mac/browser_bounds.js +++ b/accessible/tests/browser/mac/browser_bounds.js @@ -21,22 +21,26 @@ addAccessibleTask( // test them here instead of calling AXFrame directly. const [helloWidth, helloHeight] = hello.getAttributeValue("AXSize"); const [worldWidth, worldHeight] = world.getAttributeValue("AXSize"); - ok(helloWidth > 0, "Hello has a positive width"); - ok(helloHeight > 0, "Hello has a positive height"); - ok(worldWidth > 0, "World has a positive width"); - ok(worldHeight > 0, "World has a positive height"); - ok(helloHeight < worldHeight, "Hello has a smaller height than world"); - ok(helloWidth < worldWidth, "Hello has a smaller width than world"); + Assert.greater(helloWidth, 0, "Hello has a positive width"); + Assert.greater(helloHeight, 0, "Hello has a positive height"); + Assert.greater(worldWidth, 0, "World has a positive width"); + Assert.greater(worldHeight, 0, "World has a positive height"); + Assert.less( + helloHeight, + worldHeight, + "Hello has a smaller height than world" + ); + Assert.less(helloWidth, worldWidth, "Hello has a smaller width than world"); // Note: these are mac screen coords, so our origin is bottom left const [helloX, helloY] = hello.getAttributeValue("AXPosition"); const [worldX, worldY] = world.getAttributeValue("AXPosition"); - ok(helloX > 0, "Hello has a positive X"); - ok(helloY > 0, "Hello has a positive Y"); - ok(worldX > 0, "World has a positive X"); - ok(worldY > 0, "World has a positive Y"); - ok(helloY > worldY, "Hello has a larger Y than world"); - ok(helloX == worldX, "Hello and world have the same X"); + Assert.greater(helloX, 0, "Hello has a positive X"); + Assert.greater(helloY, 0, "Hello has a positive Y"); + Assert.greater(worldX, 0, "World has a positive X"); + Assert.greater(worldY, 0, "World has a positive Y"); + Assert.greater(helloY, worldY, "Hello has a larger Y than world"); + Assert.equal(helloX, worldX, "Hello and world have the same X"); } ); @@ -57,21 +61,25 @@ addAccessibleTask( // test them here instead of calling AXFrame directly. const [helloWidth, helloHeight] = hello.getAttributeValue("AXSize"); const [worldWidth, worldHeight] = world.getAttributeValue("AXSize"); - ok(helloWidth > 0, "Hello has a positive width"); - ok(helloHeight > 0, "Hello has a positive height"); - ok(worldWidth > 0, "World has a positive width"); - ok(worldHeight > 0, "World has a positive height"); - ok(helloHeight < worldHeight, "Hello has a smaller height than world"); - ok(helloWidth < worldWidth, "Hello has a smaller width than world"); + Assert.greater(helloWidth, 0, "Hello has a positive width"); + Assert.greater(helloHeight, 0, "Hello has a positive height"); + Assert.greater(worldWidth, 0, "World has a positive width"); + Assert.greater(worldHeight, 0, "World has a positive height"); + Assert.less( + helloHeight, + worldHeight, + "Hello has a smaller height than world" + ); + Assert.less(helloWidth, worldWidth, "Hello has a smaller width than world"); // Note: these are mac screen coords, so our origin is bottom left const [helloX, helloY] = hello.getAttributeValue("AXPosition"); const [worldX, worldY] = world.getAttributeValue("AXPosition"); - ok(helloX < 0, "Hello has a negative X"); - ok(helloY > 0, "Hello has a positive Y"); - ok(worldX < 0, "World has a negative X"); - ok(worldY > 0, "World has a positive Y"); - ok(helloY > worldY, "Hello has a larger Y than world"); - ok(helloX == worldX, "Hello and world have the same X"); + Assert.less(helloX, 0, "Hello has a negative X"); + Assert.greater(helloY, 0, "Hello has a positive Y"); + Assert.less(worldX, 0, "World has a negative X"); + Assert.greater(worldY, 0, "World has a positive Y"); + Assert.greater(helloY, worldY, "Hello has a larger Y than world"); + Assert.equal(helloX, worldX, "Hello and world have the same X"); } ); diff --git a/accessible/tests/browser/mac/browser_live_regions.js b/accessible/tests/browser/mac/browser_live_regions.js index 10a03120f8..aa07f003df 100644 --- a/accessible/tests/browser/mac/browser_live_regions.js +++ b/accessible/tests/browser/mac/browser_live_regions.js @@ -131,7 +131,7 @@ addAccessibleTask( <button id="button" aria-label="Start"></button> </div> `, - async (browser, accDoc) => { + async browser => { let liveRegionChanged = waitForMacEvent("AXLiveRegionChanged", "live"); await SpecialPowers.spawn(browser, [], () => { content.document.getElementById("time").textContent = "4:56pm"; diff --git a/accessible/tests/browser/mac/browser_menulist.js b/accessible/tests/browser/mac/browser_menulist.js index b26a0be782..3b0fe8b210 100644 --- a/accessible/tests/browser/mac/browser_menulist.js +++ b/accessible/tests/browser/mac/browser_menulist.js @@ -74,7 +74,7 @@ addAccessibleTask( "First menu item is selected" ); // focus the second item, and verify it is selected - event = waitForMacEvent("AXFocusedUIElementChanged", (iface, data) => { + event = waitForMacEvent("AXFocusedUIElementChanged", iface => { try { return iface.getAttributeValue("AXTitle") == "100%"; } catch (e) { diff --git a/accessible/tests/browser/mac/browser_roles_elements.js b/accessible/tests/browser/mac/browser_roles_elements.js index 791598fed6..b6049e7afd 100644 --- a/accessible/tests/browser/mac/browser_roles_elements.js +++ b/accessible/tests/browser/mac/browser_roles_elements.js @@ -47,6 +47,7 @@ addAccessibleTask( <div id="complementary" role="complementary"></div> <div id="contentinfo" role="contentinfo"></div> <div id="form" role="form"></div> + <div id="form_label" aria-label="form" role="form"></div> <div id="main" role="main"></div> <div id="navigation" role="navigation"></div> <div id="search" role="search"></div> @@ -149,7 +150,8 @@ addAccessibleTask( "AXLandmarkComplementary" ); testRoleAndSubRole(accDoc, "contentinfo", null, "AXLandmarkContentInfo"); - testRoleAndSubRole(accDoc, "form", null, "AXLandmarkForm"); + testRoleAndSubRole(accDoc, "form", null, "AXApplicationGroup"); + testRoleAndSubRole(accDoc, "form_label", null, "AXLandmarkForm"); testRoleAndSubRole(accDoc, "main", null, "AXLandmarkMain"); testRoleAndSubRole(accDoc, "navigation", null, "AXLandmarkNavigation"); testRoleAndSubRole(accDoc, "search", null, "AXLandmarkSearch"); diff --git a/accessible/tests/browser/mac/browser_rotor.js b/accessible/tests/browser/mac/browser_rotor.js index 3f13506757..87ac40592d 100644 --- a/accessible/tests/browser/mac/browser_rotor.js +++ b/accessible/tests/browser/mac/browser_rotor.js @@ -269,7 +269,7 @@ addAccessibleTask( "AXUIElementCountForSearchPredicate", NSDictionary(searchPred) ); - is(4, tableCount, "Found four tables"); + is(tableCount, 3, "Found three tables"); const tables = webArea.getParameterizedAttributeValue( "AXUIElementsForSearchPredicate", @@ -278,7 +278,6 @@ addAccessibleTask( const shapes = getNativeInterface(accDoc, "shapes"); const food = getNativeInterface(accDoc, "food"); const ariaTable = getNativeInterface(accDoc, "ariaTable"); - const grid = getNativeInterface(accDoc, "grid"); is( shapes.getAttributeValue("AXColumnCount"), @@ -295,11 +294,6 @@ addAccessibleTask( tables[2].getAttributeValue("AXColumnCount"), "Found correct third table" ); - is( - grid.getAttributeValue("AXColumnCount"), - tables[3].getAttributeValue("AXColumnCount"), - "Found correct fourth table" - ); } ); diff --git a/accessible/tests/browser/mac/browser_text_leaf.js b/accessible/tests/browser/mac/browser_text_leaf.js index 21deed6212..c65e8c6ebe 100644 --- a/accessible/tests/browser/mac/browser_text_leaf.js +++ b/accessible/tests/browser/mac/browser_text_leaf.js @@ -77,7 +77,11 @@ addAccessibleTask( NSRange(3, 8) ); - ok(smallBounds.size[0] < largeBounds.size[0], "longer range is wider"); + Assert.less( + smallBounds.size[0], + largeBounds.size[0], + "longer range is wider" + ); }, { chrome: true, iframe: true, remoteIframe: true } ); diff --git a/accessible/tests/browser/mac/browser_text_selection.js b/accessible/tests/browser/mac/browser_text_selection.js index a914adba8e..7e2145631c 100644 --- a/accessible/tests/browser/mac/browser_text_selection.js +++ b/accessible/tests/browser/mac/browser_text_selection.js @@ -82,7 +82,7 @@ addAccessibleTask( Hello <a href="#" id="link">World</a>, I <a href="#" style="user-select: none;" id="unselectable_link">love</a> <button id="button">you</button></p>`, - async (browser, accDoc) => { + async browser => { // Set up an AXSelectedTextChanged listener here. It will get resolved // on the first non-root event it encounters, so if we test its data at the end // of this test it will show us the first text-selectable object that was focused, diff --git a/accessible/tests/browser/mac/browser_toggle_radio_check.js b/accessible/tests/browser/mac/browser_toggle_radio_check.js index 1695d73b0d..f9094ac3d7 100644 --- a/accessible/tests/browser/mac/browser_toggle_radio_check.js +++ b/accessible/tests/browser/mac/browser_toggle_radio_check.js @@ -128,7 +128,7 @@ addAccessibleTask( // Changing from checked to mixed fires two events. Make sure we wait until // the second so we're asserting based on the latest state. - evt = waitForMacEvent("AXValueChanged", (iface, data) => { + evt = waitForMacEvent("AXValueChanged", iface => { return ( iface.getAttributeValue("AXDOMIdentifier") == "checkbox" && iface.getAttributeValue("AXValue") == 2 diff --git a/accessible/tests/browser/mac/browser_webarea.js b/accessible/tests/browser/mac/browser_webarea.js index ac6122de14..4872c58845 100644 --- a/accessible/tests/browser/mac/browser_webarea.js +++ b/accessible/tests/browser/mac/browser_webarea.js @@ -8,8 +8,8 @@ loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); // Test web area role and AXLoadComplete event -addAccessibleTask(``, async (browser, accDoc) => { - let evt = waitForMacEvent("AXLoadComplete", (iface, data) => { +addAccessibleTask(``, async browser => { + let evt = waitForMacEvent("AXLoadComplete", iface => { return iface.getAttributeValue("AXDescription") == "webarea test"; }); await SpecialPowers.spawn(browser, [], () => { @@ -29,16 +29,16 @@ addAccessibleTask(``, async (browser, accDoc) => { }); // Test iframe web area role and AXLayoutComplete event -addAccessibleTask(`<title>webarea test</title>`, async (browser, accDoc) => { +addAccessibleTask(`<title>webarea test</title>`, async browser => { // If the iframe loads before the top level document finishes loading, we'll // get both an AXLayoutComplete event for the iframe and an AXLoadComplete // event for the document. Otherwise, if the iframe loads after the // document, we'll get one AXLoadComplete event. let eventPromise = Promise.race([ - waitForMacEvent("AXLayoutComplete", (iface, data) => { + waitForMacEvent("AXLayoutComplete", iface => { return iface.getAttributeValue("AXDescription") == "iframe document"; }), - waitForMacEvent("AXLoadComplete", (iface, data) => { + waitForMacEvent("AXLoadComplete", iface => { return iface.getAttributeValue("AXDescription") == "webarea test"; }), ]); diff --git a/accessible/tests/browser/scroll/browser_test_scroll_bounds.js b/accessible/tests/browser/scroll/browser_test_scroll_bounds.js index 31de002cda..33a99266db 100644 --- a/accessible/tests/browser/scroll/browser_test_scroll_bounds.js +++ b/accessible/tests/browser/scroll/browser_test_scroll_bounds.js @@ -238,7 +238,11 @@ addAccessibleTask( newTopBounds[0], "x of non-fixed element remains accurate." ); - ok(newTopBounds[1] < 0, "y coordinate shows item scrolled off page"); + Assert.less( + newTopBounds[1], + 0, + "y coordinate shows item scrolled off page" + ); is( origTopBounds[2], newTopBounds[2], @@ -254,7 +258,11 @@ addAccessibleTask( newDBounds[0], "x of non-fixed container element remains accurate." ); - ok(newDBounds[1] < 0, "y coordinate shows container scrolled off page"); + Assert.less( + newDBounds[1], + 0, + "y coordinate shows container scrolled off page" + ); // Removing the position styling on this acc causes it to be bound by // its parent's bounding box, which alters its width as a block element. // We don't particularly care about width in this test, so skip it. @@ -481,7 +489,7 @@ addAccessibleTask( newBounds[0], `x coord of non-sticky element remains accurate.` ); - ok(newBounds[1] < 0, "y coordinate shows item scrolled off page"); + Assert.less(newBounds[1], 0, "y coordinate shows item scrolled off page"); // Removing the position styling on this acc causes it to be bound by // its parent's bounding box, which alters its width as a block element. diff --git a/accessible/tests/browser/scroll/browser_test_scroll_substring.js b/accessible/tests/browser/scroll/browser_test_scroll_substring.js index e8426d00ca..031b8a5124 100644 --- a/accessible/tests/browser/scroll/browser_test_scroll_substring.js +++ b/accessible/tests/browser/scroll/browser_test_scroll_substring.js @@ -45,8 +45,9 @@ The only thing I found in the fridge was a dead dove in a bag. text.getCharacterExtents(7, {}, objY, {}, {}, COORDTYPE_SCREEN_RELATIVE); return objY.value; }; - ok( - containerHeight < getCharY(), + Assert.less( + containerHeight, + getCharY(), "Character is outside of container bounds" ); text.scrollSubstringTo(7, 8, SCROLL_TYPE_TOP_EDGE); diff --git a/accessible/tests/browser/selectable/browser_test_select.js b/accessible/tests/browser/selectable/browser_test_select.js index f86a371d81..6a51fd4f3b 100644 --- a/accessible/tests/browser/selectable/browser_test_select.js +++ b/accessible/tests/browser/selectable/browser_test_select.js @@ -313,7 +313,7 @@ addAccessibleTask( </select> </form> `, - async function (browser, docAcc) { + async function (browser) { let selected = waitForEvent(EVENT_SELECTION_WITHIN, "select"); await invokeContentTask(browser, [], () => { const form = content.document.getElementById("form"); diff --git a/accessible/tests/browser/shared-head.js b/accessible/tests/browser/shared-head.js index fe87a77765..c238a5a7a0 100644 --- a/accessible/tests/browser/shared-head.js +++ b/accessible/tests/browser/shared-head.js @@ -830,7 +830,7 @@ const CACHE_WAIT_TIMEOUT_MS = 5000; * be used to record a pass or fail in the test. */ function untilCacheCondition(conditionFunc, argsFunc) { - return new Promise((resolve, reject) => { + return new Promise(resolve => { let args = argsFunc(); if (conditionFunc(...args)) { resolve(args); @@ -838,7 +838,7 @@ function untilCacheCondition(conditionFunc, argsFunc) { } let cacheObserver = { - observe(subject) { + observe() { args = argsFunc(); if (conditionFunc(...args)) { clearTimeout(this.timer); @@ -945,7 +945,7 @@ function runPython(code) { "ws://mochi.test:8888/browser/accessible/tests/browser/python_runner" ); if (gPythonSocket.readyState != WebSocket.OPEN) { - gPythonSocket.onopen = evt => { + gPythonSocket.onopen = () => { gPythonSocket.send(code); gPythonSocket.onopen = null; }; diff --git a/accessible/tests/browser/text/browser_text_paragraph_boundary.js b/accessible/tests/browser/text/browser_text_paragraph_boundary.js index 04e64520e8..b206829398 100644 --- a/accessible/tests/browser/text/browser_text_paragraph_boundary.js +++ b/accessible/tests/browser/text/browser_text_paragraph_boundary.js @@ -8,7 +8,7 @@ // boundary on an Accessible which has remote ProxyAccessible descendants. addAccessibleTask( `test`, - async function testParagraphBoundaryWithRemoteDescendants(browser, accDoc) { + async function testParagraphBoundaryWithRemoteDescendants() { const root = getRootAccessible(document).QueryInterface( Ci.nsIAccessibleText ); diff --git a/accessible/tests/browser/text/head.js b/accessible/tests/browser/text/head.js index fa4b095892..72195ddbb0 100644 --- a/accessible/tests/browser/text/head.js +++ b/accessible/tests/browser/text/head.js @@ -142,7 +142,7 @@ function testBoundarySequence( // Editable text async function waitForCopy(browser) { - await BrowserTestUtils.waitForContentEvent(browser, "copy", false, evt => { + await BrowserTestUtils.waitForContentEvent(browser, "copy", false, () => { return true; }); diff --git a/accessible/tests/browser/tree/browser_aria_owns.js b/accessible/tests/browser/tree/browser_aria_owns.js index 0ca55ed357..904597cc66 100644 --- a/accessible/tests/browser/tree/browser_aria_owns.js +++ b/accessible/tests/browser/tree/browser_aria_owns.js @@ -176,7 +176,7 @@ addAccessibleTask( addAccessibleTask( ` <select id="container" aria-owns="boom" multiple></select>`, - async function (browser, accDoc) { + async function () { ok(true, "Did not crash"); } ); diff --git a/accessible/tests/browser/tree/browser_browser_element.js b/accessible/tests/browser/tree/browser_browser_element.js index 82be24d93c..d6ece6676e 100644 --- a/accessible/tests/browser/tree/browser_browser_element.js +++ b/accessible/tests/browser/tree/browser_browser_element.js @@ -8,7 +8,7 @@ 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) { +addAccessibleTask(`test`, async function (browser) { // testAccessibleTree also verifies childCount, indexInParent and parent. testAccessibleTree(browser, { INTERNAL_FRAME: [{ DOCUMENT: [{ TEXT_LEAF: [] }] }], diff --git a/accessible/tests/browser/tree/browser_lazy_tabs.js b/accessible/tests/browser/tree/browser_lazy_tabs.js index f7aa9bdeb2..46e10d0bac 100644 --- a/accessible/tests/browser/tree/browser_lazy_tabs.js +++ b/accessible/tests/browser/tree/browser_lazy_tabs.js @@ -5,7 +5,7 @@ // Test that lazy background tabs aren't unintentionally loaded when building // the a11y tree (bug 1700708). -addAccessibleTask(``, async function (browser, accDoc) { +addAccessibleTask(``, async function () { await SpecialPowers.pushPrefEnv({ set: [["browser.sessionstore.restore_on_demand", true]], }); diff --git a/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js b/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js index 623f2640f0..e6b55d0fd3 100644 --- a/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js +++ b/accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js @@ -25,7 +25,7 @@ async function promiseEventDocumentLoadComplete(expectedURL) { add_task(async function testInDataURI() { const kURL = "data:text/html,Some text"; const waitForDocumentLoadComplete = promiseEventDocumentLoadComplete(""); - await BrowserTestUtils.withNewTab(kURL, async browser => { + await BrowserTestUtils.withNewTab(kURL, async () => { is( (await waitForDocumentLoadComplete).URL, "", @@ -44,7 +44,7 @@ add_task(async function testInHTTPSURIContainingPrivateThings() { "https://example.com/browser/toolkit/content/tests/browser/file_empty.html?query=some#ref"; const waitForDocumentLoadComplete = promiseEventDocumentLoadComplete(kURLWithoutUserPass); - await BrowserTestUtils.withNewTab(kURL, async browser => { + await BrowserTestUtils.withNewTab(kURL, async () => { is( (await waitForDocumentLoadComplete).URL, kURLWithoutUserPass, diff --git a/accessible/tests/browser/windows/a11y_setup.py b/accessible/tests/browser/windows/a11y_setup.py index d6dc19f0fb..726eea07a4 100644 --- a/accessible/tests/browser/windows/a11y_setup.py +++ b/accessible/tests/browser/windows/a11y_setup.py @@ -9,19 +9,28 @@ import ctypes import os from ctypes import POINTER, byref from ctypes.wintypes import BOOL, HWND, LPARAM, POINT # noqa: F401 +from dataclasses import dataclass +import comtypes.automation import comtypes.client import psutil from comtypes import COMError, IServiceProvider +CHILDID_SELF = 0 +COWAIT_DEFAULT = 0 +EVENT_OBJECT_FOCUS = 0x8005 +GA_ROOT = 2 +NAVRELATION_EMBEDS = 0x1009 +OBJID_CLIENT = -4 +RPC_S_CALLPENDING = -2147417835 +WINEVENT_OUTOFCONTEXT = 0 +WM_CLOSE = 0x0010 + user32 = ctypes.windll.user32 oleacc = ctypes.oledll.oleacc oleaccMod = comtypes.client.GetModule("oleacc.dll") IAccessible = oleaccMod.IAccessible del oleaccMod -OBJID_CLIENT = -4 -CHILDID_SELF = 0 -NAVRELATION_EMBEDS = 0x1009 # This is the path if running locally. ia2Tlb = os.path.join( os.getcwd(), @@ -65,6 +74,13 @@ def AccessibleObjectFromWindow(hwnd, objectID=OBJID_CLIENT): return p +def getWindowClass(hwnd): + MAX_CHARS = 257 + buffer = ctypes.create_unicode_buffer(MAX_CHARS) + user32.GetClassNameW(hwnd, buffer, MAX_CHARS) + return buffer.value + + def getFirefoxHwnd(): """Search all top level windows for the Firefox instance being tested. @@ -78,9 +94,7 @@ def getFirefoxHwnd(): @ctypes.WINFUNCTYPE(BOOL, HWND, LPARAM) def callback(hwnd, lParam): - name = ctypes.create_unicode_buffer(100) - user32.GetClassNameW(hwnd, name, 100) - if name.value != "MozillaWindowClass": + if getWindowClass(hwnd) != "MozillaWindowClass": return True pid = ctypes.wintypes.DWORD() user32.GetWindowThreadProcessId(hwnd, byref(pid)) @@ -127,6 +141,106 @@ def findIa2ByDomId(root, id): return descendant +@dataclass +class WinEvent: + event: int + hwnd: int + objectId: int + childId: int + + def getIa2(self): + acc = ctypes.POINTER(IAccessible)() + child = comtypes.automation.VARIANT() + ctypes.oledll.oleacc.AccessibleObjectFromEvent( + self.hwnd, + self.objectId, + self.childId, + ctypes.byref(acc), + ctypes.byref(child), + ) + if child.value != CHILDID_SELF: + # This isn't an IAccessible2 object. + return None + return toIa2(acc) + + +class WaitForWinEvent: + """Wait for a win event, usually for IAccessible2. + This should be used as follows: + 1. Create an instance to wait for the desired event. + 2. Perform the action that should fire the event. + 3. Call wait() on the instance you created in 1) to wait for the event. + """ + + def __init__(self, eventId, match): + """event is the event id to wait for. + match is either None to match any object, an str containing the DOM id + of the desired object, or a function taking a WinEvent which should + return True if this is the requested event. + """ + self._matched = None + # A kernel event used to signal when we get the desired event. + self._signal = ctypes.windll.kernel32.CreateEventW(None, True, False, None) + + # We define this as a nested function because it has to be a static + # function, but we need a reference to self. + @ctypes.WINFUNCTYPE( + None, + ctypes.wintypes.HANDLE, + ctypes.wintypes.DWORD, + ctypes.wintypes.HWND, + ctypes.wintypes.LONG, + ctypes.wintypes.LONG, + ctypes.wintypes.DWORD, + ctypes.wintypes.DWORD, + ) + def winEventProc(hook, eventId, hwnd, objectId, childId, thread, time): + event = WinEvent(eventId, hwnd, objectId, childId) + if isinstance(match, str): + try: + ia2 = event.getIa2() + if f"id:{match};" in ia2.attributes: + self._matched = event + except (comtypes.COMError, TypeError): + pass + elif callable(match): + try: + if match(event): + self._matched = event + except Exception as e: + self._matched = e + if self._matched: + ctypes.windll.kernel32.SetEvent(self._signal) + + self._hook = user32.SetWinEventHook( + eventId, eventId, None, winEventProc, 0, 0, WINEVENT_OUTOFCONTEXT + ) + # Hold a reference to winEventProc so it doesn't get destroyed. + self._proc = winEventProc + + def wait(self): + """Wait for and return the desired WinEvent.""" + # Pump Windows messages until we get the desired event, which will be + # signalled using a kernel event. + handles = (ctypes.c_void_p * 1)(self._signal) + index = ctypes.wintypes.DWORD() + TIMEOUT = 10000 + try: + ctypes.oledll.ole32.CoWaitForMultipleHandles( + COWAIT_DEFAULT, TIMEOUT, 1, handles, ctypes.byref(index) + ) + except WindowsError as e: + if e.winerror == RPC_S_CALLPENDING: + raise TimeoutError("Timeout before desired event received") + raise + finally: + user32.UnhookWinEvent(self._hook) + self._proc = None + if isinstance(self._matched, Exception): + raise self._matched from self._matched + return self._matched + + def getDocUia(): """Get the IUIAutomationElement for the document being tested.""" # We start with IAccessible2 because there's no efficient way to diff --git a/accessible/tests/browser/windows/ia2/browser.toml b/accessible/tests/browser/windows/ia2/browser.toml index d72b5f8a2d..d6226b73cc 100644 --- a/accessible/tests/browser/windows/ia2/browser.toml +++ b/accessible/tests/browser/windows/ia2/browser.toml @@ -6,4 +6,6 @@ skip-if = [ ] support-files = ["head.js"] +["browser_osPicker.js"] + ["browser_role.js"] diff --git a/accessible/tests/browser/windows/ia2/browser_osPicker.js b/accessible/tests/browser/windows/ia2/browser_osPicker.js new file mode 100644 index 0000000000..b14f2d0a5f --- /dev/null +++ b/accessible/tests/browser/windows/ia2/browser_osPicker.js @@ -0,0 +1,51 @@ +/* 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( + `<input id="file" type="file">`, + async function (browser, docAcc) { + info("Focusing file input"); + await runPython(` + global focused + focused = WaitForWinEvent(EVENT_OBJECT_FOCUS, "file") + `); + const file = findAccessibleChildByID(docAcc, "file"); + file.takeFocus(); + await runPython(` + focused.wait() + `); + ok(true, "file input got focus"); + info("Opening file picker"); + await runPython(` + global focused + focused = WaitForWinEvent( + EVENT_OBJECT_FOCUS, + lambda evt: getWindowClass(evt.hwnd) == "Edit" + ) + `); + file.doAction(0); + await runPython(` + global event + event = focused.wait() + `); + ok(true, "Picker got focus"); + info("Dismissing picker"); + await runPython(` + # If the picker is dismissed too quickly, it seems to re-enable the root + # window before we do. This sleep isn't ideal, but it's more likely to + # reproduce the case that our root window gets focus before it is enabled. + # See bug 1883568 for further details. + import time + time.sleep(1) + focused = WaitForWinEvent(EVENT_OBJECT_FOCUS, "file") + # Sending key presses to the picker is unreliable, so use WM_CLOSE. + pickerRoot = user32.GetAncestor(event.hwnd, GA_ROOT) + user32.SendMessageW(pickerRoot, WM_CLOSE, 0, 0) + focused.wait() + `); + ok(true, "file input got focus"); + } +); diff --git a/accessible/tests/browser/windows/ia2/browser_role.js b/accessible/tests/browser/windows/ia2/browser_role.js index 08e44c280f..89b560ab49 100644 --- a/accessible/tests/browser/windows/ia2/browser_role.js +++ b/accessible/tests/browser/windows/ia2/browser_role.js @@ -12,7 +12,7 @@ addAccessibleTask( ` <p id="p">p</p> `, - async function (browser, docAcc) { + async function () { let role = await runPython(` global doc doc = getDocIa2() diff --git a/accessible/tests/browser/windows/uia/browser.toml b/accessible/tests/browser/windows/uia/browser.toml index f7974d69c7..d1513c1822 100644 --- a/accessible/tests/browser/windows/uia/browser.toml +++ b/accessible/tests/browser/windows/uia/browser.toml @@ -9,3 +9,5 @@ support-files = ["head.js"] ["browser_controlType.js"] ["browser_elementFromPoint.js"] + +["browser_tree.js"] diff --git a/accessible/tests/browser/windows/uia/browser_controlType.js b/accessible/tests/browser/windows/uia/browser_controlType.js index 16db892581..3bb994f437 100644 --- a/accessible/tests/browser/windows/uia/browser_controlType.js +++ b/accessible/tests/browser/windows/uia/browser_controlType.js @@ -9,11 +9,11 @@ const UIA_ButtonControlTypeId = 50000; const UIA_DocumentControlTypeId = 50030; /* eslint-enable camelcase */ -addAccessibleTask( +addUiaTask( ` <button id="button">button</button> `, - async function (browser, docAcc) { + async function () { let controlType = await runPython(` global doc doc = getDocUia() diff --git a/accessible/tests/browser/windows/uia/browser_elementFromPoint.js b/accessible/tests/browser/windows/uia/browser_elementFromPoint.js index e2fda4ab30..acf6fe91c7 100644 --- a/accessible/tests/browser/windows/uia/browser_elementFromPoint.js +++ b/accessible/tests/browser/windows/uia/browser_elementFromPoint.js @@ -4,12 +4,12 @@ "use strict"; -addAccessibleTask( +addUiaTask( ` <button id="button">button</p> <a id="a" href="#">a</a> `, - async function (browser, docAcc) { + async function () { ok( await runPython(` global doc diff --git a/accessible/tests/browser/windows/uia/browser_tree.js b/accessible/tests/browser/windows/uia/browser_tree.js new file mode 100644 index 0000000000..778609bedb --- /dev/null +++ b/accessible/tests/browser/windows/uia/browser_tree.js @@ -0,0 +1,104 @@ +/* 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"; + +async function testIsControl(pyVar, isControl) { + const result = await runPython(`bool(${pyVar}.CurrentIsControlElement)`); + if (isControl) { + ok(result, `${pyVar} is a control element`); + } else { + ok(!result, `${pyVar} isn't a control element`); + } +} + +/** + * Define a global Python variable and assign it to a given Python expression. + */ +function definePyVar(varName, expression) { + return runPython(` + global ${varName} + ${varName} = ${expression} + `); +} + +/** + * Get the UIA element with the given id and assign it to a global Python + * variable using the id as the variable name. + */ +function assignPyVarToUiaWithId(id) { + return definePyVar(id, `findUiaByDomId(doc, "${id}")`); +} + +addUiaTask( + ` +<p id="p">paragraph</p> +<div id="div">div</div> +<!-- The spans are because the UIA -> IA2 proxy seems to remove a single text + leaf child from even the raw tree. + --> +<a id="link" href="#">link<span> </span>></a> +<h1 id="h1">h1<span> </span></h1> +<h1 id="h1WithDiv"><div>h1 with div<span> </span></div></h1> +<input id="range" type="range"> +<div onclick=";" id="clickable">clickable</div> +<div id="editable" contenteditable>editable</div> +<table id="table"><tr><th>th</th></tr></table> + `, + async function (browser, docAcc) { + await definePyVar("doc", `getDocUia()`); + await assignPyVarToUiaWithId("p"); + await testIsControl("p", false); + await definePyVar( + "pTextLeaf", + `uiaClient.RawViewWalker.GetFirstChildElement(p)` + ); + await testIsControl("pTextLeaf", true); + await assignPyVarToUiaWithId("div"); + await testIsControl("div", false); + await definePyVar( + "divTextLeaf", + `uiaClient.RawViewWalker.GetFirstChildElement(div)` + ); + await testIsControl("divTextLeaf", true); + await assignPyVarToUiaWithId("link"); + await testIsControl("link", true); + await assignPyVarToUiaWithId("range"); + await testIsControl("range", true); + await assignPyVarToUiaWithId("editable"); + await testIsControl("editable", true); + await assignPyVarToUiaWithId("table"); + await testIsControl("table", true); + if (!gIsUiaEnabled) { + // The remaining tests are broken with the UIA -> IA2 proxy. + return; + } + await definePyVar( + "linkTextLeaf", + `uiaClient.RawViewWalker.GetFirstChildElement(link)` + ); + await testIsControl("linkTextLeaf", false); + await assignPyVarToUiaWithId("h1"); + await testIsControl("h1", true); + await definePyVar( + "h1TextLeaf", + `uiaClient.RawViewWalker.GetFirstChildElement(h1)` + ); + await testIsControl("h1TextLeaf", false); + await assignPyVarToUiaWithId("h1WithDiv"); + await testIsControl("h1WithDiv", true); + // h1WithDiv's text leaf is its grandchild. + await definePyVar( + "h1WithDivTextLeaf", + `uiaClient.RawViewWalker.GetFirstChildElement( + uiaClient.RawViewWalker.GetFirstChildElement( + h1WithDiv + ) + )` + ); + await testIsControl("h1WithDivTextLeaf", false); + await assignPyVarToUiaWithId("clickable"); + await testIsControl("clickable", true); + } +); diff --git a/accessible/tests/browser/windows/uia/head.js b/accessible/tests/browser/windows/uia/head.js index afc50984bd..e659354c7c 100644 --- a/accessible/tests/browser/windows/uia/head.js +++ b/accessible/tests/browser/windows/uia/head.js @@ -4,6 +4,8 @@ "use strict"; +/* exported gIsUiaEnabled, addUiaTask */ + // Load the shared-head file first. Services.scriptloader.loadSubScript( "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", @@ -16,3 +18,38 @@ loadScripts( { name: "common.js", dir: MOCHITESTS_DIR }, { name: "promisified-events.js", dir: MOCHITESTS_DIR } ); + +let gIsUiaEnabled = false; + +/** + * This is like addAccessibleTask, but takes two additional boolean options: + * - uiaEnabled: Whether to run a variation of this test with Gecko UIA enabled. + * - uiaDisabled: Whether to run a variation of this test with UIA disabled. In + * this case, UIA will rely entirely on the IA2 -> UIA proxy. + * If both are set, the test will be run twice with different configurations. + * You can determine which variant is currently running using the gIsUiaEnabled + * variable. This is useful for conditional tests; e.g. if Gecko UIA supports + * something that the IA2 -> UIA proxy doesn't support. + */ +function addUiaTask(doc, task, options = {}) { + const { uiaEnabled = true, uiaDisabled = true } = options; + + function addTask(shouldEnable) { + async function uiaTask(browser, docAcc, topDocAcc) { + await SpecialPowers.pushPrefEnv({ + set: [["accessibility.uia.enable", shouldEnable]], + }); + gIsUiaEnabled = shouldEnable; + info(shouldEnable ? "Gecko UIA enabled" : "Gecko UIA disabled"); + await task(browser, docAcc, topDocAcc); + } + addAccessibleTask(doc, uiaTask, options); + } + + if (uiaEnabled) { + addTask(true); + } + if (uiaDisabled) { + addTask(false); + } +} |