summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/mochitest/events/test_focus_aria_activedescendant.html')
-rw-r--r--accessible/tests/mochitest/events/test_focus_aria_activedescendant.html327
1 files changed, 327 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
new file mode 100644
index 0000000000..661284619a
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
@@ -0,0 +1,327 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429547
+-->
+<head>
+ <title>aria-activedescendant focus tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ let PromEvents = {};
+ Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/a11y/accessible/tests/mochitest/promisified-events.js",
+ PromEvents);
+ // gA11yEventDumpToConsole = true; // debugging
+
+ function changeARIAActiveDescendant(aContainer, aItem, aPrevItemId) {
+ let itemID = Node.isInstance(aItem) ? aItem.id : aItem;
+ this.eventSeq = [new focusChecker(aItem)];
+
+ if (aPrevItemId) {
+ this.eventSeq.push(
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, false, aPrevItemId)
+ );
+ }
+
+ this.eventSeq.push(
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, true, aItem)
+ );
+
+ this.invoke = function changeARIAActiveDescendant_invoke() {
+ getNode(aContainer).setAttribute("aria-activedescendant", itemID);
+ };
+
+ this.getID = function changeARIAActiveDescendant_getID() {
+ return "change aria-activedescendant on " + itemID;
+ };
+ }
+
+ function clearARIAActiveDescendant(aID, aPrevItemId) {
+ this.eventSeq = [
+ new focusChecker(aID),
+ ];
+
+ if (aPrevItemId) {
+ this.eventSeq.push(
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, false, aPrevItemId)
+ );
+ }
+
+ this.invoke = function clearARIAActiveDescendant_invoke() {
+ getNode(aID).removeAttribute("aria-activedescendant");
+ };
+
+ this.getID = function clearARIAActiveDescendant_getID() {
+ return "clear aria-activedescendant on container " + aID;
+ };
+ }
+
+ /**
+ * Change aria-activedescendant to an invalid (non-existent) id.
+ * Ensure that focus is fired on the element itself.
+ */
+ function changeARIAActiveDescendantInvalid(aID, aInvalidID, aPrevItemId) {
+ if (!aInvalidID) {
+ aInvalidID = "invalid";
+ }
+
+ this.eventSeq = [
+ new focusChecker(aID),
+ ];
+
+ if (aPrevItemId) {
+ this.eventSeq.push(
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, false, aPrevItemId)
+ );
+ }
+
+ this.invoke = function changeARIAActiveDescendant_invoke() {
+ getNode(aID).setAttribute("aria-activedescendant", aInvalidID);
+ };
+
+ this.getID = function changeARIAActiveDescendant_getID() {
+ return "change aria-activedescendant to invalid id";
+ };
+ }
+
+ function insertItemNFocus(aID, aNewItemID, aPrevItemId) {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, aNewItemID),
+ new focusChecker(aNewItemID),
+ ];
+
+ if (aPrevItemId) {
+ this.eventSeq.push(
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, false, aPrevItemId)
+ );
+ }
+
+ this.eventSeq.push(
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, true, aNewItemID)
+ );
+
+ this.invoke = function insertItemNFocus_invoke() {
+ var container = getNode(aID);
+
+ var itemNode = document.createElement("div");
+ itemNode.setAttribute("id", aNewItemID);
+ itemNode.setAttribute("role", "listitem");
+ itemNode.textContent = aNewItemID;
+ container.appendChild(itemNode);
+
+ container.setAttribute("aria-activedescendant", aNewItemID);
+ };
+
+ this.getID = function insertItemNFocus_getID() {
+ return "insert new node and focus it with ID: " + aNewItemID;
+ };
+ }
+
+ /**
+ * Change the id of an element to another id which is the target of
+ * aria-activedescendant.
+ * If another element already has the desired id, remove it from that
+ * element first.
+ * Ensure that focus is fired on the target element which was given the
+ * desired id.
+ * @param aFromID The existing id of the target element.
+ * @param aToID The desired id to be given to the target element.
+ */
+ function moveARIAActiveDescendantID(aFromID, aToID) {
+ this.eventSeq = [
+ new focusChecker(aToID),
+ new stateChangeChecker(EXT_STATE_ACTIVE, true, true, aToID),
+ ];
+
+ this.invoke = function moveARIAActiveDescendantID_invoke() {
+ let orig = document.getElementById(aToID);
+ if (orig) {
+ orig.id = "";
+ }
+ document.getElementById(aFromID).id = aToID;
+ };
+
+ this.getID = function moveARIAActiveDescendantID_getID() {
+ return "move aria-activedescendant id " + aToID;
+ };
+ }
+
+ var gQueue = null;
+ async function doTest() {
+ gQueue = new eventQueue();
+ // Later tests use await.
+ let queueFinished = new Promise(resolve => {
+ gQueue.onFinish = function() {
+ resolve();
+ return DO_NOT_FINISH_TEST;
+ };
+ });
+
+ gQueue.push(new synthFocus("listbox", new focusChecker("item1")));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item2", "item1"));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item3", "item2"));
+
+ gQueue.push(new synthFocus("combobox_entry", new focusChecker("combobox_entry")));
+ gQueue.push(new changeARIAActiveDescendant("combobox", "combobox_option2"));
+
+ gQueue.push(new synthFocus("listbox", new focusChecker("item3")));
+ gQueue.push(new insertItemNFocus("listbox", "item4", "item3"));
+
+ gQueue.push(new clearARIAActiveDescendant("listbox", "item4"));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item1"));
+ gQueue.push(new changeARIAActiveDescendantInvalid("listbox", "invalid", "item1"));
+
+ gQueue.push(new changeARIAActiveDescendant("listbox", "roaming"));
+ gQueue.push(new moveARIAActiveDescendantID("roaming2", "roaming"));
+ gQueue.push(new changeARIAActiveDescendantInvalid("listbox", "roaming3", "roaming"));
+ gQueue.push(new moveARIAActiveDescendantID("roaming", "roaming3"));
+
+ gQueue.push(new synthFocus("activedesc_nondesc_input",
+ new focusChecker("activedesc_nondesc_option")));
+
+ let shadowRoot = document.getElementById("shadow").shadowRoot;
+ let shadowListbox = shadowRoot.getElementById("shadowListbox");
+ let shadowItem1 = shadowRoot.getElementById("shadowItem1");
+ let shadowItem2 = shadowRoot.getElementById("shadowItem2");
+ gQueue.push(new synthFocus(shadowListbox, new focusChecker(shadowItem1)));
+ gQueue.push(new changeARIAActiveDescendant(shadowListbox, shadowItem2));
+
+ gQueue.invoke();
+ await queueFinished;
+ // Tests beyond this point use await rather than eventQueue.
+
+ info("Testing simultaneous insertion, relocation and aria-activedescendant");
+ let comboboxWithHiddenList = getNode("comboboxWithHiddenList");
+ let evtProm = PromEvents.waitForEvent(EVENT_FOCUS, comboboxWithHiddenList);
+ comboboxWithHiddenList.focus();
+ await evtProm;
+ testStates(comboboxWithHiddenList, STATE_FOCUSED);
+ // hiddenList is owned, so unhiding causes insertion and relocation.
+ getNode("hiddenList").hidden = false;
+ evtProm = Promise.all([
+ PromEvents.waitForEvent(EVENT_FOCUS, "hiddenListOption"),
+ PromEvents.waitForStateChange("hiddenListOption", EXT_STATE_ACTIVE, true, true),
+ ]);
+ comboboxWithHiddenList.setAttribute("aria-activedescendant", "hiddenListOption");
+ await evtProm;
+ testStates("hiddenListOption", STATE_FOCUSED);
+
+ info("Testing active state changes when not focused");
+ testStates("listbox", 0, 0, STATE_FOCUSED);
+ evtProm = Promise.all([
+ PromEvents.waitForStateChange("roaming3", EXT_STATE_ACTIVE, false, true),
+ PromEvents.waitForStateChange("item1", EXT_STATE_ACTIVE, true, true),
+ ]);
+ getNode("listbox").setAttribute("aria-activedescendant", "item1");
+ await evtProm;
+
+ info("Testing that focus is always fired first");
+ const listbox = getNode("listbox");
+ evtProm = PromEvents.waitForEvent(EVENT_FOCUS, "item1");
+ listbox.focus();
+ await evtProm;
+ const item1 = getNode("item1");
+ evtProm = PromEvents.waitForOrderedEvents([
+ [EVENT_FOCUS, "item2"],
+ [EVENT_NAME_CHANGE, item1],
+ ], "Focus then name change");
+ item1.setAttribute("aria-label", "changed");
+ listbox.setAttribute("aria-activedescendant", "item2");
+ await evtProm;
+
+ info("Setting aria-activedescendant to invalid id on non-focused node");
+ const combobox_entry = getNode("combobox_entry");
+ evtProm = PromEvents.waitForEvents({
+ expected: [[EVENT_FOCUS, combobox_entry]],
+ unexpected: [[EVENT_FOCUS, listbox]],
+ });
+ combobox_entry.focus();
+ listbox.setAttribute("aria-activedescendant", "invalid");
+ await evtProm;
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
+ title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
+ Mozilla Bug 429547
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761102"
+ title="Focus may be missed when ARIA active-descendant is changed on active composite widget">
+ Mozilla Bug 761102
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <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">roaming</div>
+ <div role="listitem" 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>
+
+ <!-- aria-activedescendant targeting a non-descendant -->
+ <input id="activedesc_nondesc_input" aria-activedescendant="activedesc_nondesc_option">
+ <div role="listbox">
+ <div role="option" id="activedesc_nondesc_option">option</div>
+ </div>
+
+ <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>
+
+ <div id="comboboxWithHiddenList" tabindex="0" role="combobox" aria-owns="hiddenList">
+ </div>
+ <div id="hiddenList" hidden role="listbox">
+ <div id="hiddenListOption" role="option"></div>
+ </div>
+</body>
+</html>