/* 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 } ); /** * Test data has the format of: * { * desc {String} description for better logging * expected {Array} expected states for a given accessible that have the * following format: * [ * expected state, * expected extra state, * absent state, * absent extra state * ] * attrs {?Array} an optional list of attributes to update * } */ // State caching tests for attribute changes const attributeTests = [ { desc: "Checkbox with @checked attribute set to true should have checked " + "state", attrs: [ { attr: "checked", value: "true", }, ], expected: [STATE_CHECKED, 0], }, { desc: "Checkbox with no @checked attribute should not have checked state", attrs: [ { attr: "checked", }, ], expected: [0, 0, STATE_CHECKED], }, ]; // State caching tests for ARIA changes const ariaTests = [ { desc: "File input has busy state when @aria-busy attribute is set to true", attrs: [ { attr: "aria-busy", value: "true", }, ], expected: [STATE_BUSY, 0, STATE_REQUIRED | STATE_INVALID], }, { desc: "File input has required state when @aria-required attribute is set " + "to true", attrs: [ { attr: "aria-required", value: "true", }, ], expected: [STATE_REQUIRED, 0, STATE_INVALID], }, { desc: "File input has invalid state when @aria-invalid attribute is set to " + "true", attrs: [ { attr: "aria-invalid", value: "true", }, ], expected: [STATE_INVALID, 0], }, ]; // Extra state caching tests const extraStateTests = [ { desc: "Input has no extra enabled state when aria and native disabled " + "attributes are set at once", attrs: [ { attr: "aria-disabled", value: "true", }, { attr: "disabled", value: "true", }, ], expected: [0, 0, 0, EXT_STATE_ENABLED], }, { desc: "Input has an extra enabled state when aria and native disabled " + "attributes are unset at once", attrs: [ { attr: "aria-disabled", }, { attr: "disabled", }, ], expected: [0, EXT_STATE_ENABLED], }, ]; async function runStateTests(browser, accDoc, id, tests) { let acc = findAccessibleChildByID(accDoc, id); for (let { desc, attrs, expected } of tests) { const [expState, expExtState, absState, absExtState] = expected; info(desc); let onUpdate = waitForEvent(EVENT_STATE_CHANGE, evt => { if (getAccessibleDOMNodeID(evt.accessible) != id) { return false; } // Events can be fired for states other than the ones we're interested // in. If this happens, the states we're expecting might not be exposed // yet. const scEvt = evt.QueryInterface(nsIAccessibleStateChangeEvent); if (scEvt.isExtraState) { if (scEvt.state & expExtState || scEvt.state & absExtState) { return true; } return false; } return scEvt.state & expState || scEvt.state & absState; }); for (let { attr, value } of attrs) { await invokeSetAttribute(browser, id, attr, value); } await onUpdate; testStates(acc, ...expected); } } /** * Test caching of accessible object states */ addAccessibleTask( ` `, async function (browser, accDoc) { await runStateTests(browser, accDoc, "checkbox", attributeTests); await runStateTests(browser, accDoc, "file", ariaTests); await runStateTests(browser, accDoc, "text", extraStateTests); }, { iframe: true, remoteIframe: true } ); /** * Test caching of the focused state. */ addAccessibleTask( ` `, async function (browser, docAcc) { const b1 = findAccessibleChildByID(docAcc, "b1"); const b2 = findAccessibleChildByID(docAcc, "b2"); let focused = waitForEvent(EVENT_FOCUS, b1); await invokeFocus(browser, "b1"); await focused; testStates(docAcc, 0, 0, STATE_FOCUSED); testStates(b1, STATE_FOCUSED); testStates(b2, 0, 0, STATE_FOCUSED); focused = waitForEvent(EVENT_FOCUS, b2); await invokeFocus(browser, "b2"); await focused; testStates(b2, STATE_FOCUSED); testStates(b1, 0, 0, STATE_FOCUSED); }, { iframe: true, remoteIframe: true } ); /** * Test that the document initially gets the focused state. * We can't do this in the test above because that test runs in iframes as well * as a top level document. */ addAccessibleTask( ` `, async function (browser, docAcc) { testStates(docAcc, STATE_FOCUSED); } ); /** * Test caching of the focused state in iframes. */ addAccessibleTask( ` `, async function (browser, iframeDocAcc, topDocAcc) { testStates(topDocAcc, STATE_FOCUSED); const button = findAccessibleChildByID(iframeDocAcc, "button"); testStates(button, 0, 0, STATE_FOCUSED); let focused = waitForEvent(EVENT_FOCUS, button); info("Focusing button in iframe"); button.takeFocus(); await focused; testStates(topDocAcc, 0, 0, STATE_FOCUSED); testStates(button, STATE_FOCUSED); }, { topLevel: false, iframe: true, remoteIframe: true } ); /** * Test caching of the focusable state in iframes which are initially visibility: hidden. */ addAccessibleTask( ` span`, async function (browser, topDocAcc) { info("Changing visibility on iframe"); let reordered = waitForEvent(EVENT_REORDER, topDocAcc); await SpecialPowers.spawn(browser, [DEFAULT_IFRAME_ID], iframeId => { content.document.getElementById(iframeId).style.visibility = ""; }); await reordered; // The iframe doc a11y tree might not be built yet. const iframeDoc = await TestUtils.waitForCondition(() => findAccessibleChildByID(topDocAcc, DEFAULT_IFRAME_DOC_BODY_ID) ); // Log/verify whether this is an in-process or OOP iframe. await comparePIDs(browser, gIsRemoteIframe); const button = findAccessibleChildByID(iframeDoc, "button"); testStates(button, STATE_FOCUSABLE); const span = findAccessibleChildByID(iframeDoc, "span"); ok(span, "span Accessible exists"); testStates(span, STATE_FOCUSABLE); }, { topLevel: false, iframe: true, remoteIframe: true, iframeAttrs: { style: "visibility: hidden;" }, skipFissionDocLoad: true, } ); function checkOpacity(acc, present) { let [, extraState] = getStates(acc); let currOpacity = extraState & EXT_STATE_OPAQUE; return present ? currOpacity : !currOpacity; } /** * Test caching of the OPAQUE1 state. */ addAccessibleTask( `