diff options
Diffstat (limited to 'accessible/tests/mochitest/treeupdate')
35 files changed, 5874 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/treeupdate/a11y.ini b/accessible/tests/mochitest/treeupdate/a11y.ini new file mode 100644 index 000000000..d725ebff3 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/a11y.ini @@ -0,0 +1,41 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + !/accessible/tests/mochitest/moz.png + +[test_ariadialog.html] +[test_ariaowns.html] +[test_bug852150.xhtml] +[test_bug883708.xhtml] +[test_bug884251.xhtml] +[test_bug895082.html] +[test_bug1040735.html] +[test_bug1100602.html] +[test_bug1175913.html] +[test_bug1189277.html] +[test_bug1276857.html] +[test_canvas.html] +[test_colorpicker.xul] +[test_contextmenu.xul] +[test_cssoverflow.html] +[test_deck.xul] +[test_doc.html] +[test_gencontent.html] +[test_general.html] +[test_hidden.html] +[test_imagemap.html] +skip-if = buildapp == "mulet" +[test_list.html] +[test_list_editabledoc.html] +[test_listbox.xul] +[test_menu.xul] +[test_menubutton.xul] +[test_optgroup.html] +[test_recreation.html] +[test_select.html] +[test_shutdown.xul] +[test_table.html] +[test_textleaf.html] +[test_visibility.html] +[test_whitespace.html] diff --git a/accessible/tests/mochitest/treeupdate/test_ariadialog.html b/accessible/tests/mochitest/treeupdate/test_ariadialog.html new file mode 100644 index 000000000..559431626 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_ariadialog.html @@ -0,0 +1,119 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Table creation in ARIA dialog test</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function showARIADialog(aID) + { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.node) + ]; + + this.invoke = function showARIADialog_invoke() + { + getNode("dialog").style.display = "block"; + getNode("table").style.visibility = "visible"; + getNode("a").textContent = "link"; + getNode("input").value = "hello"; + getNode("input").focus(); + } + + this.finalCheck = function showARIADialog_finalCheck() + { + var tree = { + role: ROLE_DIALOG, + children: [ + { + role: ROLE_PUSHBUTTON, + children: [ { role: ROLE_TEXT_LEAF } ] + }, + { + role: ROLE_ENTRY + } + ] + }; + testAccessibleTree(aID, tree); + } + + this.getID = function showARIADialog_getID() + { + return "show ARIA dialog"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + //enableLogging("tree"); + gQueue = new eventQueue(); + + // make the accessible an inaccessible + gQueue.push(new showARIADialog("dialog")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Rework accessible tree update code" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> + Mozilla Bug 570275 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="dialog" role="dialog" style="display: none;"> + <table id="table" role="presentation" + style="display: block; position: fixed; top: 88px; left: 312.5px; z-index: 10010; visibility: hidden;"> + <tbody> + <tr> + <td role="presentation"> + <div role="presentation"> + <a id="a" role="button">text</a> + </div> + <input id="input"> + </td> + </tr> + </tbody> + </table> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_ariaowns.html b/accessible/tests/mochitest/treeupdate/test_ariaowns.html new file mode 100644 index 000000000..44fc22a2d --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html @@ -0,0 +1,693 @@ +<!DOCTYPE html> +<html> + +<head> + <title>@aria-owns attribute testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + //////////////////////////////////////////////////////////////////////////// + + function changeARIAOwns() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_button")), + // no hide for t1_subdiv because it is contained by hidden t1_checkbox + new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")), + new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")), + new invokerChecker(EVENT_SHOW, getNode("t1_button")), + new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")), + new invokerChecker(EVENT_REORDER, getNode("t1_container")) + ]; + + this.invoke = function setARIAOwns_invoke() + { + // children are swapped by ARIA owns + var tree = + { SECTION: [ + { CHECKBUTTON: [ + { SECTION: [] } + ] }, + { PUSHBUTTON: [ ] } + ] }; + testAccessibleTree("t1_container", tree); + + getNode("t1_container"). + setAttribute("aria-owns", "t1_button t1_subdiv"); + } + + this.finalCheck = function setARIAOwns_finalCheck() + { + // children are swapped again, button and subdiv are appended to + // the children. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // checkbox, native order + { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own + { SECTION: [ ] } // subdiv from the subtree, ARIA owned + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function setARIAOwns_getID() + { + return "Change @aria-owns attribute"; + } + } + + function removeARIAOwns() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_button")), + new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")), + new orderChecker(), + new asyncInvokerChecker(EVENT_SHOW, getNode("t1_button")), + new asyncInvokerChecker(EVENT_SHOW, getNode("t1_subdiv")), + new orderChecker(), + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + new unexpectedInvokerChecker(EVENT_REORDER, getNode("t1_checkbox")) + ]; + + this.invoke = function removeARIAOwns_invoke() + { + getNode("t1_container").removeAttribute("aria-owns"); + } + + this.finalCheck = function removeARIAOwns_finalCheck() + { + // children follow the DOM order + var tree = + { SECTION: [ + { PUSHBUTTON: [ ] }, + { CHECKBUTTON: [ + { SECTION: [] } + ] } + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function removeARIAOwns_getID() + { + return "Remove @aria-owns attribute"; + } + } + + function setARIAOwns() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_button")), + new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")), + new invokerChecker(EVENT_SHOW, getNode("t1_button")), + new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")), + new invokerChecker(EVENT_REORDER, getNode("t1_container")) + ]; + + this.invoke = function setARIAOwns_invoke() + { + getNode("t1_container"). + setAttribute("aria-owns", "t1_button t1_subdiv"); + } + + this.finalCheck = function setARIAOwns_finalCheck() + { + // children are swapped again, button and subdiv are appended to + // the children. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // checkbox + { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own + { SECTION: [ ] } // subdiv from the subtree, ARIA owned + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function setARIAOwns_getID() + { + return "Set @aria-owns attribute"; + } + } + + function addIdToARIAOwns() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_group")), + new invokerChecker(EVENT_SHOW, getNode("t1_group")), + new invokerChecker(EVENT_REORDER, document) + ]; + + this.invoke = function addIdToARIAOwns_invoke() + { + getNode("t1_container"). + setAttribute("aria-owns", "t1_button t1_subdiv t1_group"); + } + + this.finalCheck = function addIdToARIAOwns_finalCheck() + { + // children are swapped again, button and subdiv are appended to + // the children. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // t1_checkbox + { PUSHBUTTON: [ ] }, // button, t1_button + { SECTION: [ ] }, // subdiv from the subtree, t1_subdiv + { GROUPING: [ ] } // group from outside, t1_group + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function addIdToARIAOwns_getID() + { + return "Add id to @aria-owns attribute value"; + } + } + + function appendEl() + { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getNode, "t1_child3"), + new invokerChecker(EVENT_REORDER, getNode("t1_container")) + ]; + + this.invoke = function appendEl_invoke() + { + var div = document.createElement("div"); + div.setAttribute("id", "t1_child3"); + div.setAttribute("role", "radio") + getNode("t1_container").appendChild(div); + } + + this.finalCheck = function appendEl_finalCheck() + { + // children are invalidated, they includes aria-owns swapped kids and + // newly inserted child. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // existing explicit, t1_checkbox + { RADIOBUTTON: [ ] }, // new explicit, t1_child3 + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + { SECTION: [ ] }, // ARIA owned, t1_subdiv + { GROUPING: [ ] } // ARIA owned, t1_group + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function appendEl_getID() + { + return "Append child under @aria-owns element"; + } + } + + function removeEl() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode, "t1_checkbox"), + new invokerChecker(EVENT_SHOW, getNode, "t1_checkbox"), + new invokerChecker(EVENT_REORDER, getNode("t1_container")) + ]; + + this.invoke = function removeEl_invoke() + { + // remove a container of t1_subdiv + getNode("t1_span").parentNode.removeChild(getNode("t1_span")); + } + + this.finalCheck = function removeEl_finalCheck() + { + // subdiv should go away + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // explicit, t1_checkbox + { RADIOBUTTON: [ ] }, // explicit, t1_child3 + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + { GROUPING: [ ] } // ARIA owned, t1_group + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function removeEl_getID() + { + return "Remove a container of ARIA owned element"; + } + } + + function removeId() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_group")), + new invokerChecker(EVENT_SHOW, getNode("t1_group")), + new invokerChecker(EVENT_REORDER, document) + ]; + + this.invoke = function removeId_invoke() + { + getNode("t1_group").removeAttribute("id"); + } + + this.finalCheck = function removeId_finalCheck() + { + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, + { RADIOBUTTON: [ ] }, + { PUSHBUTTON: [ ] } // ARIA owned, t1_button + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function removeId_getID() + { + return "Remove ID from ARIA owned element"; + } + } + + function setId() + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")), + new invokerChecker(EVENT_SHOW, getNode("t1_grouptmp")), + new invokerChecker(EVENT_REORDER, document) + ]; + + this.invoke = function setId_invoke() + { + getNode("t1_grouptmp").setAttribute("id", "t1_group"); + } + + this.finalCheck = function setId_finalCheck() + { + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, + { RADIOBUTTON: [ ] }, + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + { GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp + ] }; + testAccessibleTree("t1_container", tree); + } + + this.getID = function setId_getID() + { + return "Set ID that is referred by ARIA owns"; + } + } + + /** + * Remove an accessible DOM element containing an element referred by + * ARIA owns. + */ + function removeA11eteiner() + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t2_container1")) + ]; + + this.invoke = function removeA11eteiner_invoke() + { + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] } // ARIA owned, 't2_owned' + ] }; + testAccessibleTree("t2_container1", tree); + + getNode("t2_container2").removeChild(getNode("t2_container3")); + } + + this.finalCheck = function removeA11eteiner_finalCheck() + { + var tree = + { SECTION: [ + ] }; + testAccessibleTree("t2_container1", tree); + } + + this.getID = function removeA11eteiner_getID() + { + return "Remove an accessible DOM element containing an element referred by ARIA owns"; + } + } + + /** + * Steal an element from other ARIA owns element. This use case guarantees + * that result of setAttribute/removeAttribute doesn't depend on their order. + */ + function stealFromOtherARIAOwns() + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t3_container2")) + ]; + + this.invoke = function stealFromOtherARIAOwns_invoke() + { + getNode("t3_container2").setAttribute("aria-owns", "t3_child"); + } + + this.finalCheck = function stealFromOtherARIAOwns_finalCheck() + { + var tree = + { SECTION: [ + ] }; + testAccessibleTree("t3_container1", tree); + + tree = + { SECTION: [ + { CHECKBUTTON: [ + ] } + ] }; + testAccessibleTree("t3_container2", tree); + } + + this.getID = function stealFromOtherARIAOwns_getID() + { + return "Steal an element from other ARIA owns element"; + } + } + + function appendElToRecacheChildren() + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t3_container2")) + ]; + + this.invoke = function appendElToRecacheChildren_invoke() + { + var div = document.createElement("div"); + div.setAttribute("role", "radio") + getNode("t3_container2").appendChild(div); + } + + this.finalCheck = function appendElToRecacheChildren_finalCheck() + { + var tree = + { SECTION: [ + ] }; + testAccessibleTree("t3_container1", tree); + + tree = + { SECTION: [ + { RADIOBUTTON: [ ] }, + { CHECKBUTTON: [ ] } // ARIA owned + ] }; + testAccessibleTree("t3_container2", tree); + } + + this.getID = function appendElToRecacheChildren_getID() + { + return "Append a child under @aria-owns element to trigger children recache"; + } + } + + function showHiddenElement() + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t4_container1")) + ]; + + this.invoke = function showHiddenElement_invoke() + { + var tree = + { SECTION: [ + { RADIOBUTTON: [] } + ] }; + testAccessibleTree("t4_container1", tree); + + getNode("t4_child1").style.display = "block"; + } + + this.finalCheck = function showHiddenElement_finalCheck() + { + var tree = + { SECTION: [ + { CHECKBUTTON: [] }, + { RADIOBUTTON: [] } + ] }; + testAccessibleTree("t4_container1", tree); + } + + this.getID = function showHiddenElement_getID() + { + return "Show hidden ARIA owns referred element"; + } + } + + function rearrangeARIAOwns(aContainer, aAttr, aIdList, aRoleList) + { + this.eventSeq = []; + for (var id of aIdList) { + this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id))); + } + + for (var id of aIdList) { + this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id))); + } + this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer))); + + this.invoke = function rearrangeARIAOwns_invoke() + { + getNode(aContainer).setAttribute("aria-owns", aAttr); + } + + this.finalCheck = function rearrangeARIAOwns_finalCheck() + { + var tree = { SECTION: [ ] }; + for (var role of aRoleList) { + var ch = {}; + ch[role] = []; + tree["SECTION"].push(ch); + } + testAccessibleTree(aContainer, tree); + } + + this.getID = function rearrangeARIAOwns_getID() + { + return `Rearrange @aria-owns attribute to '${aAttr}'`; + } + } + + function removeNotARIAOwnedEl(aContainer, aChild) + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer) + ]; + + this.invoke = function removeNotARIAOwnedEl_invoke() + { + var tree = { + SECTION: [ + { TEXT_LEAF: [ ] }, + { GROUPING: [ ] } + ] + }; + testAccessibleTree(aContainer, tree); + + getNode(aContainer).removeChild(getNode(aChild)); + } + + this.finalCheck = function removeNotARIAOwnedEl_finalCheck() + { + var tree = { + SECTION: [ + { GROUPING: [ ] } + ] + }; + testAccessibleTree(aContainer, tree); + } + + this.getID = function removeNotARIAOwnedEl_getID() + { + return `remove not ARIA owned child`; + } + } + + function setARIAOwnsOnElToRemove(aParent, aChild) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getAccessible(aParent)) + ]; + + this.invoke = function setARIAOwnsOnElToRemove_invoke() + { + getNode(aChild).setAttribute("aria-owns", "no_id"); + getNode(aParent).removeChild(getNode(aChild)); + getNode(aParent).parentNode.removeChild(getNode(aParent)); + } + + this.getID = function setARIAOwnsOnElToRemove_getID() + { + return `set ARIA owns on an element, and then remove it, and then remove its parent`; + } + } + + /** + * Set ARIA owns on inaccessible span element that contains + * accessible children. This will move children from the container for + * the span. + */ + function test8() + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "t8_container") + ]; + + this.invoke = function test8_invoke() + { + var tree = + { SECTION: [ + { PUSHBUTTON: [] }, + { ENTRY: [] }, + { ENTRY: [] }, + { ENTRY: [] } + ] }; + testAccessibleTree("t8_container", tree); + + getNode(t8_container).setAttribute("aria-owns", "t8_span t8_button"); + } + + this.finalCheck = function test8_finalCheck() + { + var tree = + { SECTION: [ + { TEXT: [ + { ENTRY: [] }, + { ENTRY: [] }, + { ENTRY: [] } + ] }, + { PUSHBUTTON: [] } + ] }; + testAccessibleTree("t8_container", tree); + } + + this.getID = function test8_getID() + { + return `Set ARIA owns on inaccessible span element that contains accessible children`; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + //////////////////////////////////////////////////////////////////////////// + + //gA11yEventDumpToConsole = true; + //enableLogging("tree,eventTree,verbose"); // debug stuff + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + // test1 + gQueue.push(new changeARIAOwns()); + gQueue.push(new removeARIAOwns()); + gQueue.push(new setARIAOwns()); + gQueue.push(new addIdToARIAOwns()); + gQueue.push(new appendEl()); + gQueue.push(new removeEl()); + gQueue.push(new removeId()); + gQueue.push(new setId()); + + // test2 + gQueue.push(new removeA11eteiner()); + + // test3 + gQueue.push(new stealFromOtherARIAOwns()); + gQueue.push(new appendElToRecacheChildren()); + + // test4 + gQueue.push(new showHiddenElement()); + + // test5 + gQueue.push(new rearrangeARIAOwns( + "t5_container", "t5_checkbox t5_radio t5_button", + [ "t5_checkbox", "t5_radio", "t5_button" ], + [ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ])); + gQueue.push(new rearrangeARIAOwns( + "t5_container", "t5_radio t5_button t5_checkbox", + [ "t5_radio", "t5_button" ], + [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ])); + + gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span")); + + gQueue.push(new setARIAOwnsOnElToRemove("t7_parent", "t7_child")); + + gQueue.push(new test8()); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + + </script> +</head> + +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="t1_container" aria-owns="t1_checkbox t1_button"> + <div role="button" id="t1_button"></div> + <div role="checkbox" id="t1_checkbox"> + <span id="t1_span"> + <div id="t1_subdiv"></div> + </span> + </div> + </div> + <div id="t1_group" role="group"></div> + <div id="t1_grouptmp" role="group"></div> + + <div id="t2_container1" aria-owns="t2_owned"></div> + <div id="t2_container2"> + <div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div> + </div> + + <div id="t3_container1" aria-owns="t3_child"></div> + <div id="t3_child" role="checkbox"></div> + <div id="t3_container2"></div> + + <div id="t4_container1" aria-owns="t4_child1 t4_child2"></div> + <div id="t4_container2"> + <div id="t4_child1" style="display:none" role="checkbox"></div> + <div id="t4_child2" role="radio"></div> + </div> + + <div id="t5_container"> + <div role="button" id="t5_button"></div> + <div role="checkbox" id="t5_checkbox"></div> + <div role="radio" id="t5_radio"></div> + </div> + + <div id="t6_container" aria-owns="t6_fake"> + <span id="t6_span">hey</span> + </div> + <div id="t6_fake" role="group"></div> + + <div id="t7_container"> + <div id="t7_parent"> + <div id="t7_child"></div> + </div> + </div> + + <div id="t8_container"> + <input id="t8_button" type="button"><span id="t8_span"><input><input><input></span> + </div> +</body> + +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1040735.html b/accessible/tests/mochitest/treeupdate/test_bug1040735.html new file mode 100644 index 000000000..527f23197 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1040735.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> + <title>Adopt DOM node from anonymous subtree</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="../common.js"></script> + + <script type="application/javascript"> + function doTest() + { + document.body.appendChild(document.getElementById("mw_a")); + setTimeout(function() { ok(true, "no crash and assertions"); SimpleTest.finish(); } , 0); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1040735" + title="Bug 1040735 - DOM node reinsertion under anonymous content may trigger a11y child adoption"> + Bug 1040735</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <marquee> + <div id="mw_a" style="visibility: hidden;"> + <div style="visibility: visible;" id="mw_inside"></div> + </div> + </marquee> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1100602.html b/accessible/tests/mochitest/treeupdate/test_bug1100602.html new file mode 100644 index 000000000..1637e5057 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1100602.html @@ -0,0 +1,114 @@ +<html> + +<head> + <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../name.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + /** + * Change list style type to none. + */ + function hideBullet() + { + this.eventSeq = []; + this.liAcc = getAccessible("list_element"); + this.bullet = this.liAcc.firstChild; + + this.eventSeq.push(new invokerChecker(EVENT_HIDE, this.bullet)); + this.eventSeq.push(new invokerChecker(EVENT_REORDER, this.liAcc)); + + this.invoke = function hideBullet_invoke() + { + getNode("list").setAttribute("style", "list-style-type: none;"); + } + + this.finalCheck = function hideBullet_finalCheck() + { + is(this.liAcc.name, "list element", + "Check that first child of LI is not a bullet."); + } + + this.getID = function hideBullet_getID() + { + return "Hide bullet by setting style to none"; + } + } + + /** + * Change list style type to circles. + */ + function showBullet() + { + this.eventSeq = []; + this.liAcc = getAccessible("list_element"); + + this.eventSeq.push(new invokerChecker(EVENT_SHOW, + function(aNode) { return aNode.firstChild; }, + this.liAcc)); + this.eventSeq.push(new invokerChecker(EVENT_REORDER, this.liAcc)); + + this.invoke = function showBullet_invoke() + { + getNode("list").setAttribute("style", "list-style-type: circle;"); + } + + this.finalCheck = function showBullet_finalCheck() + { + is(this.liAcc.name, "â—¦ list element", + "Check that first child of LI is a circle bullet."); + } + + this.getID = function showBullet_getID() + { + return "Show bullet by setting style to circle"; + } + } + + var gQueue = null; + function doTest() + { + + var list = getNode("list"); + list.setAttribute("style", "list-style-type: circle;"); + + gQueue = new eventQueue(); + gQueue.push(new hideBullet()); + gQueue.push(new showBullet()); + gQueue.invoke(); // SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100602" + title="[e10s] crash in mozilla::a11y::ProxyAccessible::Shutdown()"> + Mozilla Bug 1100602 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ol id="list"> + <li id="list_element">list element</li> + </ol> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1175913.html b/accessible/tests/mochitest/treeupdate/test_bug1175913.html new file mode 100644 index 000000000..5dab9b5a8 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1175913.html @@ -0,0 +1,105 @@ +<html> + +<head> + <title>Test hide/show events on event listener changes</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + function dummyListener() {} + + function testAddListener() + { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getNode("parent")), + ]; + + this.invoke = function testAddListener_invoke() + { + is(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that parent is not accessible."); + is(getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that child is not accessible."); + getNode("parent").addEventListener("click", dummyListener); + } + + this.finalCheck = function testAddListener_finalCheck() + { + var tree = { TEXT: [] }; + testAccessibleTree("parent", tree); + } + + this.getID = function testAddListener_getID() + { + return "Test that show event is sent when click listener is added"; + } + } + + function testRemoveListener() + { + this.eventSeq = [ + new unexpectedInvokerChecker(EVENT_HIDE, getNode("parent")), + ]; + + this.invoke = function testRemoveListener_invoke() + { + getNode("parent").removeEventListener("click", dummyListener); + } + + this.finalCheck = function testRemoveListener_finalCheck() + { + ok(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), + "Parent stays accessible after click event listener is removed"); + ok(!getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), + "Child stays inaccessible"); + } + + this.getID = function testRemoveListener_getID() + { + return "Test that hide event is sent when click listener is removed"; + } + } + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new testAddListener()); + gQueue.push(new testRemoveListener()); + gQueue.invoke(); // SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175913" + title="Crash in mozilla::a11y::DocAccessibleParent::RemoveAccessible(ProxyAccessible* aAccessible)"> + Mozilla Bug 1175913 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <span id="parent"> + <span id="child"> + </span> + </span> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1189277.html b/accessible/tests/mochitest/treeupdate/test_bug1189277.html new file mode 100644 index 000000000..9c995d49e --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1189277.html @@ -0,0 +1,86 @@ +<html> + +<head> + <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../name.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function runTest() + { + this.containerNode = getNode("outerDiv"); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("child")), + new invokerChecker(EVENT_HIDE, getNode("childDoc")), + new invokerChecker(EVENT_SHOW, "newChildDoc"), + new invokerChecker(EVENT_REORDER, this.containerNode) + ]; + + this.invoke = function runTest_invoke() + { + this.containerNode.removeChild(getNode("child")); + + var docContainer = getNode("docContainer"); + var iframe = document.createElement("iframe"); + iframe.setAttribute("src", "http://example.com"); + iframe.setAttribute("id", "newChildDoc"); + + docContainer.removeChild(getNode("childDoc")); + docContainer.appendChild(iframe); + } + + this.getID = function runTest_getID() + { + return "check show events are not incorrectly coalesced"; + } + } + + //enableLogging("tree"); + gA11yEventDumpToConsole = true; + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new runTest()); + gQueue.invoke(); // SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1189277" + title="content process crash caused by missing show event"> + Mozilla Bug 1189277 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="outerDiv"> + <div id="child">foo</div> + <div id="docContainer"> + <iframe id="childDoc" src="about:blank"> + </iframe> + </div> + </div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857.html b/accessible/tests/mochitest/treeupdate/test_bug1276857.html new file mode 100644 index 000000000..5eceae9eb --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html @@ -0,0 +1,143 @@ +<!DOCTYPE html> +<html> + +<head> + <title>DOM mutations test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + function runTest() + { + // children change will recreate the table + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode('c1')) + ]; + + this.invoke = function runTest_invoke() { + var tree = { + SECTION: [ // c1 + { TEXT_LEAF: [] }, // Some text + { TEXT_CONTAINER: [ + { TEXT_LEAF: [] } // something with .. + ] }, + { TEXT_LEAF: [] } // More text + ] + }; + testAccessibleTree('c1', tree); + + getNode('c1_t').querySelector('span').remove(); + }; + + this.finalCheck = function runTest_finalCheck() { + var tree = { + SECTION: [ // c1 + { TEXT_LEAF: [] }, // Some text + { TEXT_LEAF: [] } // More text + ] + }; + testAccessibleTree('c1', tree); + }; + + this.getID = function runTest_getID() + { + return 'child DOM node is removed before the layout notifies the a11y about parent removal/show'; + }; + } + + function runShadowTest() + { + // children change will recreate the table + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, 'c2') + ]; + + this.invoke = function runShadowTest_invoke() { + var tree = { + SECTION: [ // c2 + { TEXT_LEAF: [] }, // Some text + { TEXT_CONTAINER: [ + { TEXT_LEAF: [] } // something with .. + ] }, + { TEXT_LEAF: [] } // More text + ] + }; + testAccessibleTree('c2', tree); + + gShadowRoot.firstElementChild.querySelector('span').remove(); + }; + + this.finalCheck = function runShadowTest_finalCheck() { + var tree = { + SECTION: [ // c2 + { TEXT_LEAF: [] }, // Some text + { TEXT_LEAF: [] } // More text + ] + }; + testAccessibleTree('c2', tree); + }; + + this.getID = function runShadowTest_getID() { + return 'child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM'; + }; + } + + //enableLogging("tree"); + //gA11yEventDumpToConsole = true; + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new runTest()); + gQueue.push(new runShadowTest()); + gQueue.invoke(); // will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1"> + <div id="c1_t" style="display: table" role="presentation"> + Some text + <span style="display: table-cell">something with accessibles goes here</span> + More text + </div> + </div> + + <template id="tmpl"> + <div style="display: table" role="presentation"> + Some text + <span style="display: table-cell">something with accessibles goes here</span> + More text + </div> + </template> + + <div id="c2"><div id="c2_c" role="presentation"></div></div> + + <script> + var gShadowRoot = document.getElementById('c2_c').createShadowRoot(); + var tmpl = document.getElementById('tmpl'); + gShadowRoot.appendChild(document.importNode(tmpl.content, true)); + </script> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml b/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml new file mode 100644 index 000000000..094d148a0 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml @@ -0,0 +1,59 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Canvas subdom mutation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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> + <![CDATA[ + function doTest() + { + var the_displayNone = getNode("the_displaynone"); + var the_table = getNode("the_table"); + var the_row = getNode("the_row"); + ok(isAccessible(the_table), "table should be accessible"); + the_displayNone.appendChild(the_table); + ok(!isAccessible(the_table), "table in display none tree shouldn't be accessible"); + + setTimeout(function() { + document.body.removeChild(the_row); + // make sure no accessibles have stuck around. + ok(!isAccessible(the_row), "row shouldn't be accessible"); + ok(!isAccessible(the_table), "table shouldn't be accessible"); + ok(!isAccessible(the_displayNone), "display none things shouldn't be accessible"); + SimpleTest.finish(); + }, 0); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> +</head> +<body> + + <a target="_blank" + title="test accessible removal when reframe root isn't accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=852150"> + Mozilla Bug 852150 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="the_displaynone" style="display: none;"></div> + <table id="the_table"></table> + <tr id="the_row"></tr> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml b/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml new file mode 100644 index 000000000..6265a1c7f --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml @@ -0,0 +1,33 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script> + +function boom() +{ + var newSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "span"); + c.insertBefore(newSpan, d); + a.style.visibility = "visible"; + ok(true, "test didn't crash or assert"); + SimpleTest.finish(); +} + +</script> +</head> + +<body onload="boom();"> + <a target="_blank" + title="test reparenting accessible subtree when inaccessible element becomes accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=883708"> + Mozilla Bug 883708 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + +<div style="visibility: collapse;" id="a"><div style="float: right; visibility: visible;"><div id="c"><td id="d"></td></div></div></div></body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml b/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml new file mode 100644 index 000000000..d1821188b --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml @@ -0,0 +1,21 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script> + +function boom() +{ + document.getElementById("k").removeAttribute("href"); + ok(true, "changing iframe contents doesn't cause assertions"); + SimpleTest.finish(); +} + +</script> +</head> + +<body onload="boom();"> +<iframe src="data:text/html,1"><link id="k" href="data:text/html,2" /></iframe> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug895082.html b/accessible/tests/mochitest/treeupdate/test_bug895082.html new file mode 100644 index 000000000..aaefdc46c --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug895082.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> +<head> +<title>Replace body test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> +function doTest() +{ + var y = document.getElementById("y"); + var oldBody = document.body; + var newBody = document.createElement("body") + document.documentElement.insertBefore(newBody, oldBody); + setTimeout(function() { + document.documentElement.removeChild(oldBody); + newBody.appendChild(y); + ok(true, "we didn't assert"); + SimpleTest.finish(); + }, 0); +} + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=895082" + title="Bug 895082 - replacing body element asserts"> + Bug 895082</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + +<div><div id="y"></div></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_canvas.html b/accessible/tests/mochitest/treeupdate/test_canvas.html new file mode 100644 index 000000000..6ae129a67 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_canvas.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Canvas subdom mutation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function addSubtree(aID) + { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.node) + ]; + + this.invoke = function addSubtree_invoke() + { + // ensure we start with no subtree + testAccessibleTree("canvas", { CANVAS: [] }); + getNode("dialog").style.display = "block"; + } + + this.finalCheck = function addSubtree_finalCheck() { + testAccessibleTree("dialog", { DIALOG: [] }); + } + + this.getID = function addSubtree_getID() + { + return "show canvas subdom"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + // make the subdom come alive! + gQueue.push(new addSubtree("dialog")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Expose content in Canvas element" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912"> + Mozilla Bug 495912 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <canvas id="canvas"> + <div id="dialog" role="dialog" style="display: none;"> + </div> + </canvas> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_colorpicker.xul b/accessible/tests/mochitest/treeupdate/test_colorpicker.xul new file mode 100644 index 000000000..9b15c9174 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_colorpicker.xul @@ -0,0 +1,150 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL button hierarchy tests"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function openColorpicker(aColorpickerID) + { + this.node = getNode(aColorpickerID); + + this.eventSeq = []; + + var it = new colorButtonIterator(this.node); + for (var btnNode = it.next(); btnNode = it.next(); btnNode) + this.eventSeq.push(new invokerChecker(EVENT_SHOW, btnNode)); + + var popupNode = getPopupNode(this.node); + this.eventSeq.push(new invokerChecker(EVENT_REORDER, popupNode)); + + this.invoke = function openColorpicker_invoke() + { + var tree = + { BUTTONDROPDOWNGRID: [ + { ALERT: [ ] } + ] }; + testAccessibleTree(this.node, tree); + + this.node.showPopup(); + } + + this.finalCheck = function openColorpicker_finalCheck() + { + var tree = + { BUTTONDROPDOWNGRID: [ + { ALERT: [ ] } + ] }; + + var colorButtons = tree.BUTTONDROPDOWNGRID[0].ALERT; + var it = new colorButtonIterator(this.node); + while (it.next()) { + var obj = { PUSHBUTTON: [ ] }; + colorButtons.push(obj); + } + + testAccessibleTree(this.node, tree); + } + + this.getID = function openColorpicker_getID() + { + return "open colorpicker " + prettyName(aColorpickerID); + } + } + + function getPopupNode(aColorpickerNode) + { + return aColorpickerNode.mPicker.parentNode; + } + + function colorButtonIterator(aColorpickerNode) + { + this.container = aColorpickerNode.mPicker.mBox; + this.group = this.container.firstChild; + this.item = null; + + this.next = function colorButtonIterator_next() + { + if (!this.group) + return null; + + if (!this.item) { + this.item = this.group.firstChild; + return this.item; + } + + if (this.item.nextSibling) { + this.item = this.item.nextSibling; + return this.item; + } + + if (this.group.nextSibling) { + this.group = this.group.nextSibling; + this.item = this.group.firstChild; + return this.item; + } + + this.group = null; + this.item = null; + return null; + } + } + + //gA11yEventDumpToConsole = true; // debug stuff + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new openColorpicker("colorpicker")); + gQueue.invoke(); // Will call SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'"> + Mozilla Bug 249292 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children."> + Mozilla Bug 630486 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <colorpicker id="colorpicker" type="button"/> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_contextmenu.xul b/accessible/tests/mochitest/treeupdate/test_contextmenu.xul new file mode 100644 index 000000000..5b31e0136 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_contextmenu.xul @@ -0,0 +1,317 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="menu tree and events"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../events.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + + function openMenu(aID, aTree) + { + this.eventSeq = [ + new invokerChecker(EVENT_MENUPOPUP_START, getNode(aID)) + ]; + + this.invoke = function openMenu_invoke() + { + var button = getNode("button"); + getNode(aID).openPopup(button, "after_start", 0, 0, true, false); + } + + this.finalCheck = function openMenu_finalCheck(aEvent) + { + testAccessibleTree(aID, aTree); + } + + this.getID = function openMenu_getID() + { + return "open menu " + prettyName(aID); + } + } + + function selectNextMenuItem(aID) + { + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getNode(aID)) + ]; + + this.invoke = function selectMenuItem_invoke() + { + synthesizeKey("VK_DOWN", { }); + } + + this.getID = function selectMenuItem_getID() + { + return "select menuitem " + prettyName(aID); + } + } + + function openSubMenu(aSubMenuID, aItemID, aMenuID, aTree) + { + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getNode(aItemID)), + ]; + + this.invoke = function openSubMenu_invoke() + { + synthesizeKey("VK_RETURN", { }); + } + + this.finalCheck = function openSubMenu_finalCheck(aEvent) + { + testAccessibleTree(aMenuID, aTree); + } + + this.getID = function openSubMenu_getID() + { + return "open submenu " + prettyName(aSubMenuID) + " focusing item " + prettyName(aItemID); + } + } + + function closeSubMenu(aSubMenuID, aItemID) + { + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getNode(aItemID)), + ]; + + this.invoke = function closeSubMenu_invoke() + { + synthesizeKey("VK_ESCAPE", { }); + } + + this.getID = function closeSubMenu_getID() + { + return "close submenu " + prettyName(aSubMenuID) + " focusing item " + prettyName(aItemID); + } + } + + function closeMenu(aID) + { + this.eventSeq = [ + new invokerChecker(EVENT_MENUPOPUP_END, getNode(aID)) + ]; + + this.invoke = function closeMenu_invoke() + { + synthesizeKey("VK_ESCAPE", { }); + } + + this.getID = function closeMenu_getID() + { + return "close menu " + prettyName(aID); + } + } + + //gA11yEventDumpToConsole = true; + //enableLogging("tree,verbose"); + + var gQueue = null; + var gContextTree = {}; + + // Linux and Windows menu trees discrepancy: bug 527646. + + /** + * Return the context menu tree before submenus were open. + */ + function getMenuTree1() + { + if (LINUX || SOLARIS) { + var tree = { + role: ROLE_MENUPOPUP, + children: [ + { + name: "item0", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item1", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item2", + role: ROLE_PARENT_MENUITEM, + children: [ ] + } + ] + }; + return tree; + } + + // Windows + var tree = { + role: ROLE_MENUPOPUP, + children: [ + { + name: "item0", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item1", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item2", + role: ROLE_PARENT_MENUITEM, + children: [ + { + name: "item2", + role: ROLE_MENUPOPUP, + children: [ ] + } + ] + } + ] + }; + return tree; + } + + /** + * Return context menu tree when submenu was open. + */ + function getMenuTree2() + { + var tree = getMenuTree1(); + if (LINUX || SOLARIS) { + var submenuTree = + { + name: "item2.0", + role: ROLE_PARENT_MENUITEM, + children: [ ] + }; + tree.children[2].children.push(submenuTree); + return tree; + } + + // Windows + var submenuTree = + { + name: "item2.0", + role: ROLE_PARENT_MENUITEM, + children: [ + { + name: "item2.0", + role: ROLE_MENUPOPUP, + children: [ ] + } + ] + }; + + tree.children[2].children[0].children.push(submenuTree); + return tree; + } + + /** + * Return context menu tree when subsub menu was open. + */ + function getMenuTree3() + { + var tree = getMenuTree2(); + var subsubmenuTree = + { + name: "item2.0.0", + role: ROLE_MENUITEM, + children: [] + }; + + if (LINUX || SOLARIS) + tree.children[2].children[0].children.push(subsubmenuTree); + else + tree.children[2].children[0].children[0].children[0].children.push(subsubmenuTree); + + return tree; + } + + + function doTests() + { + gQueue = new eventQueue(); + + // Check initial empty tree + testAccessibleTree("context", { MENUPOPUP: [] }); + + // Open context menu and check that menu item accesibles are created. + gQueue.push(new openMenu("context", getMenuTree1())); + + // Select items and check focus event on them. + gQueue.push(new selectNextMenuItem("item0")); + gQueue.push(new selectNextMenuItem("item1")); + gQueue.push(new selectNextMenuItem("item2")); + + // Open sub menu and check menu accessible tree and focus event. + gQueue.push(new openSubMenu("submenu2", "item2.0", + "context", getMenuTree2())); + gQueue.push(new openSubMenu("submenu2.0", "item2.0.0", + "context", getMenuTree3())); + + // Close submenus and check that focus goes to parent. + gQueue.push(new closeSubMenu("submenu2.0", "item2.0")); + gQueue.push(new closeSubMenu("submenu2", "item2")); + + gQueue.push(new closeMenu("context")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630194" + title="Update accessible tree when opening the menu popup"> + Mozilla Bug 630194 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children."> + Mozilla Bug 630486 + </a> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + + <menupopup id="context"> + <menuitem id="item0" label="item0"/> + <menuitem id="item1" label="item1"/> + <menu id="item2" label="item2"> + <menupopup id="submenu2"> + <menu id="item2.0" label="item2.0"> + <menupopup id="submenu2.0"> + <menuitem id="item2.0.0" label="item2.0.0"/> + </menupopup> + </menu> + </menupopup> + </menu> + </menupopup> + + <button context="context" id="button">btn</button> + </vbox> + </hbox> +</window> diff --git a/accessible/tests/mochitest/treeupdate/test_cssoverflow.html b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html new file mode 100644 index 000000000..ed9edd66e --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html @@ -0,0 +1,143 @@ +<html> + +<head> + <title>Testing HTML scrollable frames (css overflow style)</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + //////////////////////////////////////////////////////////////////////////// + + /** + * Change scroll range to not empty size and inserts a child into container + * to trigger tree update of the container. Prior to bug 677154 not empty + * size resulted to accessible creation for scroll area, container tree + * update picked up that accessible unattaching scroll area accessible + * subtree. + */ + function changeScrollRange(aContainerID, aScrollAreaID) + { + this.containerNode = getNode(aContainerID); + this.container = getAccessible(this.containerNode); + this.scrollAreaNode = getNode(aScrollAreaID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function changeScrollRange_invoke() + { + this.scrollAreaNode.style.width = "20px"; + this.containerNode.appendChild(document.createElement("input")); + } + + this.finalCheck = function changeScrollRange_finalCheck() + { + var accTree = + { SECTION: [ // container + { SECTION: [ // scroll area + { ENTRY: [] } // child content + ] }, + { ENTRY: [] } // inserted input + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function changeScrollRange_getID() + { + return "change scroll range for " + prettyName(aScrollAreaID); + } + } + + /** + * Change scrollbar styles from hidden to auto. That makes us to create an + * accessible for scroll area. + */ + function changeScrollbarStyles(aContainerID, aScrollAreaID) + { + this.container = getAccessible(aContainerID); + this.scrollAreaNode = getNode(aScrollAreaID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAccessible, this.scrollAreaNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function changeScrollbarStyles_invoke() + { + var accTree = + { SECTION: [] }; + testAccessibleTree(this.container, accTree); + + this.scrollAreaNode.style.overflow = "auto"; + } + + this.finalCheck = function changeScrollbarStyles_finalCheck() + { + var accTree = + { SECTION: [ // container + { SECTION: [] } // scroll area + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function changeScrollbarStyles_getID() + { + return "change scrollbar styles " + prettyName(aScrollAreaID); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Do tests + //////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + function doTests() + { + gQueue = new eventQueue(); + + gQueue.push(new changeScrollRange("container", "scrollarea")); + gQueue.push(new changeScrollbarStyles("container2", "scrollarea2")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=677154" + title="Detached document accessibility tree"> + Mozilla Bug 677154</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="eventdump"></div> + + <div id="container"><div id="scrollarea" style="overflow:auto;"><input></div></div> + <div id="container2"><div id="scrollarea2" style="overflow:hidden;"></div></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_deck.xul b/accessible/tests/mochitest/treeupdate/test_deck.xul new file mode 100644 index 000000000..13561abab --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_deck.xul @@ -0,0 +1,109 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Tree update on XUL deck panel switching"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../states.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + function switchDeckPanel(aContainerID, aDeckID) + { + this.panelIndex = 0; + + this.container = getAccessible(aContainerID); + this.deckNode = getNode(aDeckID); + this.prevPanel = getAccessible(this.deckNode.selectedPanel); + this.panelNode = this.deckNode.childNodes[this.panelIndex]; + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.prevPanel), + new invokerChecker(EVENT_SHOW, this.panelNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function switchDeckPanel_invoke() + { + var tree = + { GROUPING: [ // role="group" + { GROUPING: [ // groupbox, a selected panel #2 + { PUSHBUTTON: [ ] } // button + ] } + ] }; + testAccessibleTree(this.container, tree); + + this.deckNode.selectedIndex = this.panelIndex; + } + + this.finalCheck = function switchDeckPanel_finalCheck() + { + var tree = + { GROUPING: [ // role="group" + { LABEL: [ // description, a selected panel #1 + { TEXT_LEAF: [] } // text leaf, a description value + ] } + ] }; + testAccessibleTree(this.container, tree); + } + + this.getID = function switchDeckPanel_getID() + { + return "switch deck panel"; + } + } + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new switchDeckPanel("container", "deck")); + gQueue.invoke(); // will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=814836" + title=" xul:deck element messes up screen reader"> + Mozilla Bug 814836 + </a> + + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1" id="container" role="group"> + + <deck id="deck" selectedIndex="1"> + <description>This is the first page</description> + <groupbox> + <button label="This is the second page"/> + </groupbox> + </deck> + + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_doc.html b/accessible/tests/mochitest/treeupdate/test_doc.html new file mode 100644 index 000000000..05896e7b4 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_doc.html @@ -0,0 +1,466 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test document root content mutations</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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"> + + //////////////////////////////////////////////////////////////////////////// + // Helpers + + function getDocNode(aID) + { + return getNode(aID).contentDocument; + } + function getDocChildNode(aID) + { + return getDocNode(aID).body.firstChild; + } + + function rootContentReplaced(aID, aTextName, aRootContentRole) + { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getDocChildNode, aID), + new invokerChecker(EVENT_REORDER, getDocNode, aID) + ]; + + this.finalCheck = function rootContentReplaced_finalCheck() + { + var tree = { + role: aRootContentRole || ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: aTextName + } + ] + }; + testAccessibleTree(getDocNode(aID), tree); + } + } + + function rootContentRemoved(aID) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, null), + new invokerChecker(EVENT_REORDER, getDocNode, aID) + ]; + + this.preinvoke = function rootContentRemoved_preinvoke() + { + // Set up target for hide event before we invoke. + var text = getAccessible(getAccessible(getDocNode(aID)).firstChild); + this.eventSeq[0].target = text; + } + + this.finalCheck = function rootContentRemoved_finalCheck() + { + var tree = { + role: ROLE_DOCUMENT, + children: [ ] + }; + testAccessibleTree(getDocNode(aID), tree); + } + } + + function rootContentInserted(aID, aTextName) + { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getDocChildNode, aID), + new invokerChecker(EVENT_REORDER, getDocNode, aID) + ]; + + this.finalCheck = function rootContentInserted_finalCheck() + { + var tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: aTextName + } + ] + }; + testAccessibleTree(getDocNode(aID), tree); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function writeIFrameDoc(aID) + { + this.__proto__ = new rootContentReplaced(aID, "hello"); + + this.invoke = function writeIFrameDoc_invoke() + { + var docNode = getDocNode(aID); + + // We can't use open/write/close outside of iframe document because of + // security error. + var script = docNode.createElement("script"); + script.textContent = "document.open(); document.write('hello'); document.close();"; + docNode.body.appendChild(script); + } + + this.getID = function writeIFrameDoc_getID() + { + return "write document"; + } + } + + /** + * Replace HTML element. + */ + function replaceIFrameHTMLElm(aID) + { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getDocChildNode, aID), + new invokerChecker(EVENT_REORDER, getDocNode, aID) + ]; + + this.invoke = function replaceIFrameHTMLElm_invoke() + { + var docNode = getDocNode(aID); + var newHTMLNode = docNode.createElement("html"); + newHTMLNode.innerHTML = `<body><p>New Wave</p></body`; + docNode.replaceChild(newHTMLNode, docNode.documentElement); + } + + this.finalCheck = function replaceIFrameHTMLElm_finalCheck() + { + var tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_PARAGRAPH, + children: [ + { + role: ROLE_TEXT_LEAF, + name: 'New Wave' + } + ] + } + ] + }; + testAccessibleTree(getDocNode(aID), tree); + } + + this.getID = function replaceIFrameHTMLElm_getID() + { + return "replace HTML element"; + } + } + + /** + * Replace HTML body on new body having ARIA role. + */ + function replaceIFrameBody(aID) + { + this.__proto__ = new rootContentReplaced(aID, "New Hello"); + + this.invoke = function replaceIFrameBody_invoke() + { + var docNode = getDocNode(aID); + var newBodyNode = docNode.createElement("body"); + var newTextNode = docNode.createTextNode("New Hello"); + newBodyNode.appendChild(newTextNode); + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + } + + this.getID = function replaceIFrameBody_getID() + { + return "replace body"; + } + } + + /** + * Replace HTML body on new body having ARIA role. + */ + function replaceIFrameBodyOnARIARoleBody(aID) + { + this.__proto__ = new rootContentReplaced(aID, "New Hello", + ROLE_PUSHBUTTON); + + this.invoke = function replaceIFrameBodyOnARIARoleBody_invoke() + { + var docNode = getDocNode(aID); + var newBodyNode = docNode.createElement("body"); + var newTextNode = docNode.createTextNode("New Hello"); + newBodyNode.appendChild(newTextNode); + newBodyNode.setAttribute("role", "button"); + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + } + + this.getID = function replaceIFrameBodyOnARIARoleBody_getID() + { + return "replace body on body having ARIA role"; + } + } + + /** + * Open/close document pair. + */ + function openIFrameDoc(aID) + { + this.__proto__ = new rootContentRemoved(aID); + + this.invoke = function openIFrameDoc_invoke() + { + this.preinvoke(); + + // Open document. + var docNode = getDocNode(aID); + var script = docNode.createElement("script"); + script.textContent = "function closeMe() { document.write('Works?'); document.close(); } window.closeMe = closeMe; document.open();"; + docNode.body.appendChild(script); + } + + this.getID = function openIFrameDoc_getID() + { + return "open document"; + } + } + + function closeIFrameDoc(aID) + { + this.__proto__ = new rootContentInserted(aID, "Works?"); + + this.invoke = function closeIFrameDoc_invoke() + { + // Write and close document. + getDocNode(aID).write('Works?'); getDocNode(aID).close(); + } + + this.getID = function closeIFrameDoc_getID() + { + return "close document"; + } + } + + /** + * Remove/insert HTML element pair. + */ + function removeHTMLFromIFrameDoc(aID) + { + this.__proto__ = new rootContentRemoved(aID); + + this.invoke = function removeHTMLFromIFrameDoc_invoke() + { + this.preinvoke(); + + // Remove HTML element. + var docNode = getDocNode(aID); + docNode.removeChild(docNode.firstChild); + } + + this.getID = function removeHTMLFromIFrameDoc_getID() + { + return "remove HTML element"; + } + } + + function insertHTMLToIFrameDoc(aID) + { + this.__proto__ = new rootContentInserted(aID, "Haha"); + + this.invoke = function insertHTMLToIFrameDoc_invoke() + { + // Insert HTML element. + var docNode = getDocNode(aID); + var html = docNode.createElement("html"); + var body = docNode.createElement("body"); + var text = docNode.createTextNode("Haha"); + body.appendChild(text); + html.appendChild(body); + docNode.appendChild(html); + } + + this.getID = function insertHTMLToIFrameDoc_getID() + { + return "insert HTML element document"; + } + } + + /** + * Remove/insert HTML body pair. + */ + function removeBodyFromIFrameDoc(aID) + { + this.__proto__ = new rootContentRemoved(aID); + + this.invoke = function removeBodyFromIFrameDoc_invoke() + { + this.preinvoke(); + + // Remove body element. + var docNode = getDocNode(aID); + docNode.documentElement.removeChild(docNode.body); + } + + this.getID = function removeBodyFromIFrameDoc_getID() + { + return "remove body element"; + } + } + + function insertElmUnderDocElmWhileBodyMissed(aID) + { + this.docNode = null; + this.inputNode = null; + + function getInputNode() + { return this.inputNode; } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getInputNode.bind(this)), + new invokerChecker(EVENT_REORDER, getDocNode, aID) + ]; + + this.invoke = function invoke() + { + this.docNode = getDocNode(aID); + this.inputNode = this.docNode.createElement("input"); + this.docNode.documentElement.appendChild(this.inputNode); + } + + this.finalCheck = function finalCheck() + { + var tree = + { DOCUMENT: [ + { ENTRY: [ ] } + ] }; + testAccessibleTree(this.docNode, tree); + + // Remove aftermath of this test before next test starts. + this.docNode.documentElement.removeChild(this.inputNode); + } + + this.getID = function getID() + { + return "Insert element under document element while body is missed."; + } + } + + function insertBodyToIFrameDoc(aID) + { + this.__proto__ = new rootContentInserted(aID, "Yo ho ho i butylka roma!"); + + this.invoke = function insertBodyToIFrameDoc_invoke() + { + // Insert body element. + var docNode = getDocNode(aID); + var body = docNode.createElement("body"); + var text = docNode.createTextNode("Yo ho ho i butylka roma!"); + body.appendChild(text); + docNode.documentElement.appendChild(body); + } + + this.getID = function insertBodyToIFrameDoc_getID() + { + return "insert body element"; + } + } + + function changeSrc(aID) + { + this.containerNode = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.containerNode) + ]; + + this.invoke = function changeSrc_invoke() + { + this.containerNode.src = "data:text/html,<html><input></html>"; + } + + this.finalCheck = function changeSrc_finalCheck() + { + var tree = + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { ENTRY: [ ] } + ] } + ] }; + testAccessibleTree(this.containerNode, tree); + } + + this.getID = function changeSrc_getID() + { + return "change src on iframe"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpToConsole = true; + //enableLogging('tree,verbose'); + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new writeIFrameDoc("iframe")); + gQueue.push(new replaceIFrameHTMLElm("iframe")); + gQueue.push(new replaceIFrameBody("iframe")); + gQueue.push(new openIFrameDoc("iframe")); + gQueue.push(new closeIFrameDoc("iframe")); + gQueue.push(new removeHTMLFromIFrameDoc("iframe")); + gQueue.push(new insertHTMLToIFrameDoc("iframe")); + gQueue.push(new removeBodyFromIFrameDoc("iframe")); + gQueue.push(new insertElmUnderDocElmWhileBodyMissed("iframe")); + gQueue.push(new insertBodyToIFrameDoc("iframe")); + gQueue.push(new changeSrc("iframe")); + gQueue.push(new replaceIFrameBodyOnARIARoleBody("iframe")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Update accessible tree when root element is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=606082">Mozilla Bug 606082</a> + <a target="_blank" + title="Elements inserted outside the body aren't accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a> + <a target="_blank" + title="Reorder event for document must be fired after document initial tree creation" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=669263">Mozilla Bug 669263</a> + <a target="_blank" + title="Changing the HTML body doesn't pick up ARIA role" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=818407">Mozilla Bug 818407</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <iframe id="iframe"></iframe> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_gencontent.html b/accessible/tests/mochitest/treeupdate/test_gencontent.html new file mode 100644 index 000000000..1cf22b36f --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_gencontent.html @@ -0,0 +1,160 @@ +<html> + +<head> + <title>Elements with CSS generated content</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + .gentext:before { + content: "START" + } + .gentext:after { + content: "END" + } + </style> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + //////////////////////////////////////////////////////////////////////////// + + /** + * Insert new node with CSS generated content style applied to container. + */ + function insertNodeHavingGenContent(aContainerID) + { + this.containerNode = getNode(aContainerID); + this.container = getAccessible(this.containerNode); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getFirstChild, this.container), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function insertNodeHavingGenContent_invoke() + { + var node = document.createElement("div"); + node.textContent = "text"; + node.setAttribute("class", "gentext"); + this.containerNode.appendChild(node); + } + + this.finalCheck = function insertNodeHavingGenContent_finalCheck() + { + var accTree = + { SECTION: [ // container + { SECTION: [ // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] } // :after + ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function insertNodeHavingGenContent_getID() + { + return "insert node having generated content to " + prettyName(aContainerID); + } + } + + /** + * Add CSS generated content to the given node contained by container node. + */ + function addGenContent(aContainerID, aNodeID) + { + this.container = getAccessible(aContainerID); + this.node = getNode(aNodeID); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.container.firstChild), + new invokerChecker(EVENT_SHOW, getFirstChild, this.container), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function addGenContent_invoke() + { + this.node.setAttribute("class", "gentext"); + } + + this.finalCheck = function insertNodeHavingGenContent_finalCheck() + { + var accTree = + { SECTION: [ // container + { SECTION: [ // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] } // :after + ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function addGenContent_getID() + { + return "add generated content to" + prettyName(aNodeID); + } + } + + /** + * Target getters. + */ + function getFirstChild(aAcc) + { + try { return aAcc.getChildAt(0); } + catch (e) { return null; } + } + + //////////////////////////////////////////////////////////////////////////// + // Do tests + //////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + function doTests() + { + gQueue = new eventQueue(); + + gQueue.push(new insertNodeHavingGenContent("container1")); + gQueue.push(new addGenContent("container2", "container2_child")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=646350" + title="Add a test for dynamic chnages of CSS generated content"> + Mozilla Bug 646350</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="eventdump"></div> + + <div id="container1"></div> + <div id="container2"><div id="container2_child">text</div></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_general.html b/accessible/tests/mochitest/treeupdate/test_general.html new file mode 100644 index 000000000..9a8862bea --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_general.html @@ -0,0 +1,150 @@ +<html> + +<head> + <title>Testing the tree updates</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + //////////////////////////////////////////////////////////////////////////// + + function prependAppend(aContainer) + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer) + ]; + + this.invoke = function prependAppend_invoke() + { + var checkbox = document.createElement('input'); + checkbox.setAttribute('type', 'checkbox'); + getNode(aContainer).insertBefore(checkbox, getNode(aContainer).firstChild); + + var button = document.createElement('input'); + button.setAttribute('type', 'button'); + getNode(aContainer).appendChild(button); + } + + this.finalCheck = function prependAppend_finalCheck() + { + var accTree = + { SECTION: [ // container + { CHECKBUTTON: [ ] }, + { ENTRY: [ ] }, + { PUSHBUTTON: [ ] } + ] }; + testAccessibleTree(aContainer, accTree); + } + + this.getID = function prependAppend_getID() + { + return "prepends a child and appends a child"; + } + } + + function removeRemove(aContainer) + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer) + ]; + + this.invoke = function removeRemove_invoke() + { + getNode(aContainer).removeChild(getNode(aContainer).firstChild); + } + + this.finalCheck = function removeRemove_finalCheck() + { + var accTree = + { SECTION: [ // container + { PUSHBUTTON: [ ] } + ] }; + testAccessibleTree(aContainer, accTree); + } + + this.getID = function removeRemove_getID() + { + return "remove first and second children"; + } + } + + function insertInaccessibleAccessibleSiblings() + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "c3") + ]; + + this.invoke = function insertInaccessibleAccessibleSiblings_invoke() + { + getNode("c3").appendChild(document.createElement("span")); + getNode("c3").appendChild(document.createElement("input")); + } + + this.finalCheck = function insertInaccessibleAccessibleSiblings_finalCheck() + { + var accTree = + { SECTION: [ // container + { PUSHBUTTON: [ + { TEXT_LEAF: [] } + ] }, + { ENTRY: [ ] } + ] }; + testAccessibleTree("c3", accTree); + } + + this.getID = function insertInaccessibleAccessibleSiblings_getID() + { + return "insert inaccessible and then accessible siblings"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Do tests + //////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + function doTests() + { + gQueue = new eventQueue(); + + gQueue.push(new prependAppend("c1")); + gQueue.push(new removeRemove("c2")); + gQueue.push(new insertInaccessibleAccessibleSiblings()); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1"><input></div> + <div id="c2"><span><input type="checkbox"><input></span><input type="button"></div> + + <div id="c3"><input type="button" value="button"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_hidden.html b/accessible/tests/mochitest/treeupdate/test_hidden.html new file mode 100644 index 000000000..2adb9efeb --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_hidden.html @@ -0,0 +1,135 @@ +<!DOCTYPE html> +<html> + +<head> + <title>@hidden attribute testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + //////////////////////////////////////////////////////////////////////////// + + /** + * Set @hidden attribute + */ + function setHiddenAttr(aContainerID, aChildID) + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function setHiddenAttr_invoke() + { + var tree = + { SECTION: [ + { ENTRY: [ + ] } + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aChildID).setAttribute("hidden", "true"); + } + + this.finalCheck = function setHiddenAttr_finalCheck() + { + var tree = + { SECTION: [ + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function setHiddenAttr_getID() + { + return "Set @hidden attribute on input and test accessible tree for div"; + } + } + + /** + * Remove @hidden attribute + */ + function removeHiddenAttr(aContainerID, aChildID) + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function removeHiddenAttr_invoke() + { + var tree = + { SECTION: [ + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aChildID).removeAttribute("hidden"); + } + + this.finalCheck = function removeHiddenAttr_finalCheck() + { + var tree = + { SECTION: [ + { ENTRY: [ + ] } + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function removeHiddenAttr_getID() + { + return "Remove @hidden attribute on input and test accessible tree for div"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + //////////////////////////////////////////////////////////////////////////// + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new setHiddenAttr("container", "child")); + gQueue.push(new removeHiddenAttr("container", "child")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + + </script> + +</head> + +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container"><input id="child"></div> + + <div id="eventdump"></div> + +</body> + +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_imagemap.html b/accessible/tests/mochitest/treeupdate/test_imagemap.html new file mode 100644 index 000000000..c7c06b3d1 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_imagemap.html @@ -0,0 +1,442 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML img map accessible tree update tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function insertArea(aImageMapID, aMapID) + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode = getNode(aMapID); + + function getInsertedArea(aThisObj) + { + return aThisObj.imageMap.firstChild; + } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getInsertedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap) + ]; + + this.invoke = function insertArea_invoke() + { + var areaElm = document.createElement("area"); + areaElm.setAttribute("href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#a"); + areaElm.setAttribute("coords", "0,0,13,14"); + areaElm.setAttribute("alt", "a"); + areaElm.setAttribute("shape", "rect"); + + this.mapNode.insertBefore(areaElm, this.mapNode.firstChild); + } + + this.finalCheck = function insertArea_finalCheck() + { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "a", + children: [ ] + }, + { + role: ROLE_LINK, + name: "b", + children: [ ] + }, + ] }; + testAccessibleTree(this.imageMap, accTree); + } + + this.getID = function insertArea_getID() + { + return "insert area element"; + } + } + + function appendArea(aImageMapID, aMapID) + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode = getNode(aMapID); + + function getAppendedArea(aThisObj) + { + return aThisObj.imageMap.lastChild; + } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAppendedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap) + ]; + + this.invoke = function appendArea_invoke() + { + var areaElm = document.createElement("area"); + areaElm.setAttribute("href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#c"); + areaElm.setAttribute("coords", "34,0,47,14"); + areaElm.setAttribute("alt", "c"); + areaElm.setAttribute("shape", "rect"); + + this.mapNode.appendChild(areaElm); + } + + this.finalCheck = function appendArea_finalCheck() + { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "a", + children: [ ] + }, + { + role: ROLE_LINK, + name: "b", + children: [ ] + }, + { + role: ROLE_LINK, + name: "c", + children: [ ] + } + ] }; + testAccessibleTree(this.imageMap, accTree); + } + + this.getID = function appendArea_getID() + { + return "append area element"; + } + } + + function removeArea(aImageMapID, aMapID) + { + this.imageMap = getAccessible(aImageMapID); + this.area = null; + this.mapNode = getNode(aMapID); + + function getRemovedArea(aThisObj) + { + return aThisObj.area; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getRemovedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap) + ]; + + this.invoke = function removeArea_invoke() + { + this.area = this.imageMap.firstChild; + this.mapNode.removeChild(this.mapNode.firstElementChild); + } + + this.finalCheck = function removeArea_finalCheck() + { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "b", + children: [ ] + }, + { + role: ROLE_LINK, + name: "c", + children: [ ] + } + ] }; + testAccessibleTree(this.imageMap, accTree); + } + + this.getID = function removeArea_getID() + { + return "remove area element"; + } + } + + function removeNameOnMap(aImageMapContainerID, aImageMapID, aMapID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = getAccessible(aImageMapID); + this.imgNode = this.imageMap.DOMNode; + this.mapNode = getNode(aMapID); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.imageMap), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function removeNameOnMap_invoke() + { + this.mapNode.removeAttribute("name"); + } + + this.finalCheck = function removeNameOnMap_finalCheck() + { + var accTree = + { SECTION: [ + { GRAPHIC: [ ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function removeNameOnMap_getID() + { + return "remove @name on map element"; + } + } + + function restoreNameOnMap(aImageMapContainerID, aImageMapID, aMapID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = null; + this.imgNode = getNode(aImageMapID); + this.mapNode = getNode(aMapID); + + function getImageMap(aThisObj) + { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function restoreNameOnMap_invoke() + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode.setAttribute("name", "atoz_map"); + + // XXXhack: force repainting of the image (see bug 745788 for details). + waveOverImageMap(aImageMapID); + } + + this.finalCheck = function removeNameOnMap_finalCheck() + { + var accTree = + { SECTION: [ + { IMAGE_MAP: [ + { LINK: [ ] }, + { LINK: [ ] } + ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function removeNameOnMap_getID() + { + return "restore @name on map element"; + } + } + + function removeMap(aImageMapContainerID, aImageMapID, aMapID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = null; + this.imgNode = getNode(aImageMapID); + this.mapNode = getNode(aMapID); + + function getImageMap(aThisObj) + { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function removeMap_invoke() + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode.parentNode.removeChild(this.mapNode); + } + + this.finalCheck = function removeMap_finalCheck() + { + var accTree = + { SECTION: [ + { GRAPHIC: [ ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function removeMap_getID() + { + return "remove map element"; + } + } + + function insertMap(aImageMapContainerID, aImageID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.image = null; + this.imgMapNode = getNode(aImageID); + + function getImage(aThisObj) + { + return aThisObj.image; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImage, this), + new invokerChecker(EVENT_SHOW, this.imgMapNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function insertMap_invoke() + { + this.image = getAccessible(aImageID); + + var map = document.createElement("map"); + map.setAttribute("name", "atoz_map"); + map.setAttribute("id", "map"); + + var area = document.createElement("area") + area.setAttribute("href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#b"); + area.setAttribute("coords", "17,0,30,14"); + area.setAttribute("alt", "b"); + area.setAttribute("shape", "rect"); + + map.appendChild(area); + + this.containerNode.appendChild(map); + } + + this.finalCheck = function insertMap_finalCheck() + { + var accTree = + { SECTION: [ + { IMAGE_MAP: [ + { LINK: [ ] } + ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function insertMap_getID() + { + return "insert map element"; + } + } + + function hideImageMap(aContainerID, aImageID) + { + this.container = getAccessible(aContainerID); + this.imageMap = null; + this.imageMapNode = getNode(aImageID); + + function getImageMap(aThisObj) + { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_REORDER, aContainerID) + ]; + + this.invoke = function hideImageMap_invoke() + { + this.imageMap = getAccessible(this.imageMapNode); + this.imageMapNode.style.display = "none"; + } + + this.finalCheck = function hideImageMap_finalCheck() + { + var accTree = + { SECTION: [ ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function hideImageMap_getID() + { + return "display:none image"; + } + } + + //gA11yEventDumpToConsole = true; // debug stuff + function doPreTest() + { + waitForImageMap("imgmap", doTest); + } + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new insertArea("imgmap", "map")); + gQueue.push(new appendArea("imgmap", "map")); + gQueue.push(new removeArea("imgmap", "map")); + gQueue.push(new removeNameOnMap("container", "imgmap", "map")); + gQueue.push(new restoreNameOnMap("container", "imgmap", "map")); + gQueue.push(new removeMap("container", "imgmap", "map")); + gQueue.push(new insertMap("container", "imgmap")); + gQueue.push(new hideImageMap("container", "imgmap")); + + //enableLogging("tree"); // debug stuff + //gQueue.onFinish = function() { disableLogging("tree"); } + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doPreTest); + </script> + +</head> +<body> + + <a target="_blank" + title="Image map accessible tree is not updated when image map is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=732389"> + Mozilla Bug 732389 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <map name="atoz_map" id="map"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b" + coords="17,0,30,14" alt="b" shape="rect"> + </map> + + <div id="container"> + <img id="imgmap" width="447" height="15" + usemap="#atoz_map" + src="../letters.gif"><!-- + Important: no whitespace between the <img> and the </div>, so we + don't end up with textframes there, because those would be reflected + in our accessible tree in some cases. + --></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_list.html b/accessible/tests/mochitest/treeupdate/test_list.html new file mode 100644 index 000000000..9196142d9 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_list.html @@ -0,0 +1,152 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test HTML li and listitem bullet accessible cache</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Helpers + + function testLiAccessibleTree() + { + // Test accessible tree. + var accTree = { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_STATICTEXT, + children: [] + }, + { + role: ROLE_TEXT_LEAF, + children: [] + } + ] + }; + + testAccessibleTree("li", accTree); + } + + //////////////////////////////////////////////////////////////////////////// + // Sequence item processors + + function hideProcessor() + { + this.liNode = getNode("li"); + this.li = getAccessible(this.liNode); + this.bullet = this.li.firstChild; + + this.process = function hideProcessor_process() + { + this.liNode.style.display = "none"; + } + + this.onProcessed = function hideProcessor_onProcessed() + { + window.setTimeout( + function(aLiAcc, aLiNode, aBulletAcc) + { + testDefunctAccessible(aLiAcc, aLiNode); + testDefunctAccessible(aBulletAcc); + + gSequence.processNext(); + }, + 0, this.li, this.liNode, this.bullet + ); + } + }; + + function showProcessor() + { + this.liNode = getNode("li"); + + this.process = function showProcessor_process() + { + this.liNode.style.display = "list-item"; + } + + this.onProcessed = function showProcessor_onProcessed() + { + testLiAccessibleTree(); + gSequence.processNext(); + } + }; + + function textReplaceProcessor() + { + this.liNode = getNode("li"); + + this.process = function textReplaceProcessor_process() + { + this.liNode.textContent = "hey"; + } + + this.onProcessed = function textReplaceProcessor_onProcessed() + { + var tree = { + LISTITEM: [ + { STATICTEXT: [] }, + { TEXT_LEAF: [] } + ] + }; + testAccessibleTree(this.liNode, tree); + SimpleTest.finish(); + } + }; + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpToConsole = true; + + var gSequence = null; + function doTest() + { + testLiAccessibleTree(); + + gSequence = new sequence(); + + gSequence.append(new hideProcessor(), EVENT_HIDE, getAccessible("li"), + "hide HTML li"); + gSequence.append(new showProcessor(), EVENT_SHOW, getNode("li"), + "show HTML li"); + gSequence.append(new textReplaceProcessor(), EVENT_REORDER, getNode("li"), + "change text of HTML li"); + + gSequence.processNext(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="setParent shouldn't be virtual" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=496783">Mozilla Bug 496783</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ul> + <li id="li">item1</li> + </ul> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html b/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html new file mode 100644 index 000000000..d4c178cb9 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test HTML li and listitem bullet accessible insertion into editable document</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function addLi(aID) + { + this.listNode = getNode(aID); + this.liNode = document.createElement("li"); + this.liNode.textContent = "item"; + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAccessible, this.liNode), + new invokerChecker(EVENT_REORDER, this.listNode) + ]; + + this.invoke = function addLi_invoke() + { + this.listNode.appendChild(this.liNode); + } + + this.finalCheck = function addLi_finalCheck() + { + var tree = { + role: ROLE_LIST, + children: [ + { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_STATICTEXT, + name: "1. ", + children: [] + }, + { + role: ROLE_TEXT_LEAF, + children: [] + } + ] + } + ] + }; + testAccessibleTree(aID, tree); + } + + this.getID = function addLi_getID() + { + return "add li"; + } + }; + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new addLi("list")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body contentEditable="true"> + + <a target="_blank" + title="Wrong list bullet text of accessible for the first numbered HTML:li in CKEditor" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=557795">Mozilla Bug 557795</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ol id="list"> + </ol> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_listbox.xul b/accessible/tests/mochitest/treeupdate/test_listbox.xul new file mode 100644 index 000000000..862b4dde8 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_listbox.xul @@ -0,0 +1,180 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL listbox hierarchy tests"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function insertListitem(aListboxID) + { + this.listboxNode = getNode(aListboxID); + + this.listitemNode = document.createElement("listitem"); + this.listitemNode.setAttribute("label", "item1"); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.listitemNode), + new invokerChecker(EVENT_REORDER, this.listboxNode) + ]; + + this.invoke = function insertListitem_invoke() + { + this.listboxNode.insertBefore(this.listitemNode, + this.listboxNode.firstChild); + } + + this.finalCheck = function insertListitem_finalCheck() + { + var tree = + { LISTBOX: [ + { + role: ROLE_RICH_OPTION, + name: "item1" + }, + { + role: ROLE_RICH_OPTION, + name: "item2" + }, + { + role: ROLE_RICH_OPTION, + name: "item3" + }, + { + role: ROLE_RICH_OPTION, + name: "item4" + } + ] }; + testAccessibleTree(this.listboxNode, tree); + } + + this.getID = function insertListitem_getID() + { + return "insert listitem "; + } + } + + function removeListitem(aListboxID) + { + this.listboxNode = getNode(aListboxID); + this.listitemNode = null; + this.listitem; + + function getListitem(aThisObj) + { + return aThisObj.listitem; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getListitem, this), + new invokerChecker(EVENT_REORDER, this.listboxNode) + ]; + + this.invoke = function removeListitem_invoke() + { + this.listitemNode = this.listboxNode.firstChild; + this.listitem = getAccessible(this.listitemNode); + + this.listboxNode.removeChild(this.listitemNode); + } + + this.finalCheck = function removeListitem_finalCheck() + { + var tree = + { LISTBOX: [ + { + role: ROLE_RICH_OPTION, + name: "item2" + }, + { + role: ROLE_RICH_OPTION, + name: "item3" + }, + { + role: ROLE_RICH_OPTION, + name: "item4" + } + ] }; + testAccessibleTree(this.listboxNode, tree); + } + + this.getID = function removeListitem_getID() + { + return "remove listitem "; + } + } + + //gA11yEventDumpToConsole = true; // debug stuff + + var gQueue = null; + function doTest() + { + var tree = + { LISTBOX: [ + { + role: ROLE_RICH_OPTION, + name: "item2" + }, + { + role: ROLE_RICH_OPTION, + name: "item3" + }, + { + role: ROLE_RICH_OPTION, + name: "item4" + } + ] }; + testAccessibleTree("listbox", tree); + + gQueue = new eventQueue(); + gQueue.push(new insertListitem("listbox")); + gQueue.push(new removeListitem("listbox")); + gQueue.invoke(); // Will call SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=656225" + title="XUL listbox accessible tree doesn't get updated"> + Mozilla Bug 656225 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <listbox id="listbox" rows="2"> + <listitem label="item2"/> + <listitem label="item3"/> + <listitem label="item4"/> + </listbox> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_menu.xul b/accessible/tests/mochitest/treeupdate/test_menu.xul new file mode 100644 index 000000000..abdea217e --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_menu.xul @@ -0,0 +1,128 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL menu hierarchy tests"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function openMenu(aID) + { + this.menuNode = getNode(aID), + + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, this.menuNode) + ]; + + this.invoke = function openMenu_invoke() + { + var tree; + if (LINUX || SOLARIS) { + tree = + { PARENT_MENUITEM: [ ] }; + + } else { + tree = + { PARENT_MENUITEM: [ + { MENUPOPUP: [ ] } + ] }; + } + testAccessibleTree(aID, tree); + + // Show menu. + this.menuNode.open = true; + } + + this.finalCheck = function openMenu_finalCheck() + { + var tree; + if (LINUX || SOLARIS) { + tree = + { PARENT_MENUITEM: [ + { MENUITEM: [ ] }, + { MENUITEM: [ ] } + ] }; + + } else { + tree = + { PARENT_MENUITEM: [ + { MENUPOPUP: [ + { MENUITEM: [ ] }, + { MENUITEM: [ ] } + ] } + ] }; + } + testAccessibleTree(aID, tree); + } + + this.getID = function openMenu_getID() + { + return "open menu " + prettyName(aID); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new openMenu("menu")); + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'"> + Mozilla Bug 249292 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children."> + Mozilla Bug 630486 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <menubar> + <menu id="menu" label="menu"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </menu> + </menubar> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_menubutton.xul b/accessible/tests/mochitest/treeupdate/test_menubutton.xul new file mode 100644 index 000000000..4821a265b --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_menubutton.xul @@ -0,0 +1,198 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL button hierarchy tests"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function openMenu(aButtonID, aMenuItemRole) + { + var menuItemRole = aMenuItemRole || ROLE_MENUITEM; + this.button = getAccessible(aButtonID); + this.menupopup = this.button.firstChild; + + var checker = new invokerChecker(EVENT_REORDER, this.menupopup); + this.__proto__ = new synthClick(aButtonID, checker); + + this.invoke = function openMenu_invoke() + { + var tree = + { PUSHBUTTON: [ + { MENUPOPUP: [ ] } + ] }; + testAccessibleTree(this.button, tree); + + this.__proto__.invoke(); + } + + this.finalCheck = function openMenu_finalCheck() + { + var tree = + { PUSHBUTTON: [ + { MENUPOPUP: [ + { role: menuItemRole, children: [ ] }, + { role: menuItemRole, children: [ ] } + ] } + ] }; + testAccessibleTree(this.button, tree); + + synthesizeKey("VK_ESCAPE", { }); + } + + this.getID = function openMenu_getID() + { + return "open menu of the button " + prettyName(aButtonID); + } + } + + function openMenuButton(aButtonID) + { + this.buttonNode = getNode(aButtonID); + this.menupoupNode = this.buttonNode.firstChild; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.menupoupNode) + ]; + + this.invoke = function openMenu_invoke() + { + var tree = + { PUSHBUTTON: [ + { MENUPOPUP: [ ] }, + { PUSHBUTTON: [ ] } + ] }; + testAccessibleTree(this.buttonNode, tree); + + this.buttonNode.open = true; + } + + this.finalCheck = function openMenu_finalCheck() + { + var tree = + { PUSHBUTTON: [ + { MENUPOPUP: [ + { MENUITEM: [ ] }, + { MENUITEM: [ ] } + ] }, + { PUSHBUTTON: [ ] } + ] }; + testAccessibleTree(this.buttonNode, tree); + + this.buttonNode.open = false; + } + + this.getID = function openMenu_getID() + { + return "open menu for menu button " + prettyName(aButtonID); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Do test + + gA11yEventDumpToConsole = true; // debug stuff + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new openMenu("button1")); + gQueue.push(new openMenuButton("button2")); + gQueue.push(new openMenu("button3")); + gQueue.push(new openMenuButton("button4")); + + var columnPickerBtn = getAccessible("tree").firstChild.lastChild; + gQueue.push(new openMenu(columnPickerBtn, ROLE_CHECK_MENU_ITEM)); + gQueue.invoke(); // SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'"> + Bug 249292 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children"> + Bug 630486 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=722265" + title="Column header selection popup no longer exposed to accessibility APIs"> + Bug 722265 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <button id="button1" type="menu" label="button"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </button> + <button id="button2" type="menu-button" label="menu button"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </button> + + <toolbarbutton id="button3" type="menu" label="toolbarbutton"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </toolbarbutton> + <toolbarbutton id="button4" type="menu-button" label="menu toolbarbutton"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </toolbarbutton> + + <tree id="tree" flex="1"> + <treecols> + <treecol id="col" flex="1" primary="true" label="column"/> + <treecol id="col2" flex="1" label="another column"/> + </treecols> + <treechildren/> + </tree> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_optgroup.html b/accessible/tests/mochitest/treeupdate/test_optgroup.html new file mode 100644 index 000000000..27323bbc3 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_optgroup.html @@ -0,0 +1,137 @@ +<!DOCTYPE html> +<html> +<head> + <title>Add and remove optgroup test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + function addOptGroup(aID) + { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function addOptGroup_invoke() + { + var optGroup = document.createElement("optgroup"); + for (i = 0; i < 2; i++) { + var opt = document.createElement("option"); + opt.value = i; + opt.text = "Option: Value " + i; + + optGroup.appendChild(opt); + } + + this.selectNode.add(optGroup, null); + var option = document.createElement("option"); + this.selectNode.add(option, null); + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList) + ]; + + this.finalCheck = function addOptGroup_finalCheck() + { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { GROUPING: [ + { COMBOBOX_OPTION: [ + { TEXT_LEAF: [] } + ] }, + { COMBOBOX_OPTION: [ + { TEXT_LEAF: [] } + ] }, + ]}, + { COMBOBOX_OPTION: [] } + ] } + ] }; + testAccessibleTree(this.select, tree); + } + + this.getID = function addOptGroup_getID() + { + return "test optgroup's insertion into a select"; + } + } + + function removeOptGroup(aID) + { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function removeOptGroup_invoke() + { + this.option1Node = this.selectNode.firstChild.firstChild; + this.selectNode.removeChild(this.selectNode.firstChild); + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList) + ]; + + this.finalCheck = function removeOptGroup_finalCheck() + { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { COMBOBOX_OPTION: [] } + ] } + ] }; + testAccessibleTree(this.select, tree); + is(isAccessible(this.option1Node), false, "removed option shouldn't be accessible anymore!"); + } + + this.getID = function removeOptGroup_getID() + { + return "test optgroup's removal from a select"; + } + } + + //gA11yEventDumpToConsole = true; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new addOptGroup("select")); + gQueue.push(new removeOptGroup("select")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452" + title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree"> + Bug 616452</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <select id="select"></select> + + <div id="debug"/> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_recreation.html b/accessible/tests/mochitest/treeupdate/test_recreation.html new file mode 100644 index 000000000..7754eb703 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_recreation.html @@ -0,0 +1,155 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test accessible recreation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function recreateAccessible(aID, aWontBeAccessible) + { + this.node = getNode(aID); + this.accessible = + isAccessible(this.node) ? getAccessible(this.node) : null; + + this.eventSeq = [ ]; + + if (this.accessible) + this.eventSeq.push(new invokerChecker(EVENT_HIDE, + this.accessible)); + + if (!aWontBeAccessible) + this.eventSeq.push(new invokerChecker(EVENT_SHOW, getAccessible, + this.node)); + + this.eventSeq.push(new invokerChecker(EVENT_REORDER, + getContainerAccessible(this.node))); + + if (this.accessible) { + this.unexpectedEventSeq = [ + new invokerChecker(EVENT_SHOW, this.accessible) + ]; + } + } + + function changeAttr(aID, aAttr, aValue) + { + this.__proto__ = new recreateAccessible(aID); + + this.invoke = function changeAttr_invoke() + { + this.node.setAttribute(aAttr, aValue); + } + + this.getID = function changeAttr_getID() + { + return "change " + aAttr + "attribute for " + aID; + } + } + + function removeAttr(aID, aAttr) + { + this.__proto__ = new recreateAccessible(aID, true); + + this.invoke = function remvoeAttr_invoke() + { + this.node.removeAttribute(aAttr); + } + + this.getID = function remvoeAttr_getID() + { + return "remove " + aAttr + "attribute for " + aID; + } + } + + function changeRole(aID, aHasAccessible) + { + this.__proto__ = new changeAttr(aID, "role", "button"); + } + + function removeRole(aID) + { + this.__proto__ = new removeAttr(aID, "role"); + } + + function changeHref(aID) + { + this.__proto__ = new changeAttr(aID, "href", "www"); + } + + function changeMultiselectable(aID) + { + this.__proto__ = new changeAttr(aID, "aria-multiselectable", "true"); + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + // make the accessible an inaccessible + gQueue.push(new changeRole("span")); + + // make the inaccessible an accessible + gQueue.push(new removeRole("span")); + + // recreate an accessible by role change + gQueue.push(new changeRole("div1")); + + // recreate an accessible by href change + gQueue.push(new changeHref("anchor")); + + // recreate an accessible by aria-multiselectable change + gQueue.push(new changeMultiselectable("div3")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Rework accessible tree update code" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> + Mozilla Bug 570275 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <span id="span">span</span> + <div id="div1">div</div> + <a id="anchor">anchor</a> + <div id="div3" role="listbox">list</div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_select.html b/accessible/tests/mochitest/treeupdate/test_select.html new file mode 100644 index 000000000..006618b80 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_select.html @@ -0,0 +1,130 @@ +<!DOCTYPE html> +<html> +<head> + <title>Add select options test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + function addOptions(aID) + { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function addOptions_invoke() + { + for (i = 0; i < 2; i++) { + var opt = document.createElement("option"); + opt.value = i; + opt.text = "Option: Value " + i; + + this.selectNode.add(opt, null); + } + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList) + ]; + + this.finalCheck = function addOptions_finalCheck() + { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { COMBOBOX_OPTION: [ + { TEXT_LEAF: [] } + ] }, + { COMBOBOX_OPTION: [ + { TEXT_LEAF: [] } + ] } + ] } + ] }; + testAccessibleTree(this.select, tree); + } + + this.getID = function addOptions_getID() + { + return "test elements insertion into a select"; + } + } + + function removeOptions(aID) + { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function removeOptions_invoke() + { + while (this.selectNode.length) + this.selectNode.remove(0); + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList) + ]; + + this.finalCheck = function removeOptions_finalCheck() + { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [] } + ] }; + testAccessibleTree(this.select, tree); + } + + this.getID = function removeptions_getID() + { + return "test elements removal from a select"; + } + } + + //gA11yEventDumpID = "debug"; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new addOptions("select")); + gQueue.push(new removeOptions("select")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452" + title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree"> + Mozilla Bug 616452</a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=616940" + title="Removed select option accessibles aren't removed until hide event is fired"> + Mozilla Bug 616940</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <select id="select"></select> + + <div id="debug"/> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_shutdown.xul b/accessible/tests/mochitest/treeupdate/test_shutdown.xul new file mode 100644 index 000000000..2e6f7a7b3 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_shutdown.xul @@ -0,0 +1,132 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL tree hierarchy tests"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../treeview.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../states.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + function setXULTreeView(aTreeID, aTreeView) + { + this.treeNode = getNode(aTreeID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.treeNode) + ]; + + this.invoke = function loadXULTree_invoke() + { + this.treeNode.view = aTreeView; + }; + + this.getID = function loadXULTree_getID() + { + return "Load XUL tree " + prettyName(aTreeID); + }; + } + + function removeTree(aID) + { + this.tree = getAccessible(aID); + this.lastItem = null; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, document) + ]; + + this.invoke = function invoke() + { + this.lastItem = getAccessible(aID).lastChild; + this.lastCell = this.lastItem.lastChild; + getNode(aID).parentNode.removeChild(getNode(aID)); + }; + + this.check = function check(aEvent) + { + testIsDefunct(this.tree, aID); + testIsDefunct(this.lastItem, "last item of " + aID); + if (this.lastCell) { + testIsDefunct(this.lastCell, "last item cell of " + aID); + } + }; + + this.getID = function getID() + { + return "Remove tree from DOM"; + }; + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "debug"; + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new setXULTreeView("tree", new nsTreeTreeView())); + gQueue.push(new removeTree("tree")); + + gQueue.push(new setXULTreeView("treetable", new nsTreeTreeView())); + gQueue.push(new removeTree("treetable")); + + gQueue.invoke(); // Will call SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727" + title="Reorganize implementation of XUL tree accessibility"> + Bug 503727 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <tree id="tree" flex="1"> + <treecols> + <treecol id="col" flex="1" primary="true" label="column"/> + </treecols> + <treechildren/> + </tree> + + <tree id="treetable" flex="1"> + <treecols> + <treecol id="col1" flex="1" primary="true" label="column"/> + <treecol id="col2" flex="1" label="column 2"/> + </treecols> + <treechildren/> + </tree> + </vbox> + </hbox> + +</window> diff --git a/accessible/tests/mochitest/treeupdate/test_table.html b/accessible/tests/mochitest/treeupdate/test_table.html new file mode 100644 index 000000000..abadefdb0 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_table.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<html> +<head> + <title>Table update tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + function appendCaption(aTableID) + { + this.invoke = function appendCaption_invoke() + { + // append a caption, it should appear as a first element in the + // accessible tree. + var caption = document.createElement("caption"); + caption.textContent = "table caption"; + getNode(aTableID).appendChild(caption); + } + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aTableID) + ]; + + this.finalCheck = function appendCaption_finalCheck() + { + var tree = + { TABLE: [ + { CAPTION: [ + { TEXT_LEAF: [] } + ] }, + { ROW: [ + { CELL: [ {TEXT_LEAF: [] }]}, + { CELL: [ {TEXT_LEAF: [] }]} + ] } + ] }; + testAccessibleTree(aTableID, tree); + } + + this.getID = function appendCaption_getID() + { + return "append caption"; + } + } + + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new appendCaption("table")); + gQueue.invoke(); // Will call SimpleTest.finish(); + + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <table id="table"> + <tr> + <td>cell1</td> + <td>cell2</td> + </tr> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_textleaf.html b/accessible/tests/mochitest/treeupdate/test_textleaf.html new file mode 100644 index 000000000..16d3a1a2b --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html @@ -0,0 +1,180 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test accessible recreation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function textLeafUpdate(aID, aIsTextLeafLinkable) + { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.node.parentNode) + ]; + + this.finalCheck = function textLeafUpdate_finalCheck() + { + var textLeaf = getAccessible(this.node).firstChild; + is(textLeaf.actionCount, (aIsTextLeafLinkable ? 1 : 0), + "Wrong action numbers!"); + } + } + + function setOnClickAttr(aID) + { + var node = getNode(aID); + node.setAttribute("onclick", "alert(3);"); + var textLeaf = getAccessible(node).firstChild; + is(textLeaf.actionCount, 1, "setOnClickAttr: wrong action numbers!"); + } + + function removeOnClickAttr(aID) + { + var node = getNode(aID); + node.removeAttribute("onclick"); + var textLeaf = getAccessible(node).firstChild; + is(textLeaf.actionCount, 0, + "removeOnClickAttr: wrong action numbers!"); + } + + function setOnClickNRoleAttrs(aID) + { + this.__proto__ = new textLeafUpdate(aID, true); + + this.invoke = function setOnClickAttr_invoke() + { + this.node.setAttribute("role", "link"); + this.node.setAttribute("onclick", "alert(3);"); + } + + this.getID = function setOnClickAttr_getID() + { + return "make " + prettyName(aID) + " linkable"; + } + } + + function removeTextData(aID, aRole) + { + this.containerNode = getNode(aID); + this.textNode = this.containerNode.firstChild; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.containerNode) + ]; + + this.invoke = function removeTextData_invoke() + { + var tree = { + role: aRole, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "text" + } + ] + }; + testAccessibleTree(this.containerNode, tree); + + this.textNode.data = ""; + } + + this.finalCheck = function removeTextData_finalCheck() + { + var tree = { + role: aRole, + children: [] + }; + testAccessibleTree(this.containerNode, tree); + } + + this.getID = function removeTextData_finalCheck() + { + return "remove text data of text node inside '" + aID + "'."; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + // adds onclick on element, text leaf should inherit its action + setOnClickAttr("div"); + // remove onclick attribute, text leaf shouldn't have any action + removeOnClickAttr("div"); + + // Call rest of event tests. + gQueue = new eventQueue(); + + // set onclick attribute making span accessible, it's inserted into tree + // and adopts text leaf accessible, text leaf should have an action + gQueue.push(new setOnClickNRoleAttrs("span")); + + // text data removal of text node should remove its text accessible + gQueue.push(new removeTextData("p", ROLE_PARAGRAPH)); + gQueue.push(new removeTextData("pre", ROLE_TEXT_CONTAINER)); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Clean up the code of accessible initialization and binding to the tree" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=545465"> + Mozilla Bug 545465 + </a> + <a target="_blank" + title="Make sure accessible tree is correct when rendered text is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652"> + Mozilla Bug 625652 + </a> + <a target="_blank" + title="Remove text accesible getting no text inside a preformatted area" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=706335"> + Mozilla Bug 706335 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container"> + <div id="div">div</div> + <span id="span">span</span> + </div> + + <p id="p">text</p> + <pre id="pre">text</pre> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_visibility.html b/accessible/tests/mochitest/treeupdate/test_visibility.html new file mode 100644 index 000000000..a1c130fb6 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_visibility.html @@ -0,0 +1,437 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Style visibility tree update test</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + /** + * Hide parent while child stays visible. + */ + function test1(aContainerID, aParentID, aChildID) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aParentID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function invoke() + { + var tree = + { SECTION: [ + { SECTION: [ + { SECTION: [ + { TEXT_LEAF: [] } + ] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aParentID).style.visibility = "hidden"; + } + + this.finalCheck = function finalCheck() + { + var tree = + { SECTION: [ + { SECTION: [ + { TEXT_LEAF: [] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function getID() + { + return "hide parent while child stays visible"; + } + } + + /** + * Hide grand parent while its children stay visible. + */ + function test2(aContainerID, aGrandParentID, aChildID, aChild2ID) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aGrandParentID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_SHOW, getNode(aChild2ID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function invoke() + { + var tree = + { SECTION: [ // container + { SECTION: [ // grand parent + { SECTION: [ + { SECTION: [ // child + { TEXT_LEAF: [] } + ] }, + { SECTION: [ // child2 + { TEXT_LEAF: [] } + ] } + ] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aGrandParentID).style.visibility = "hidden"; + } + + this.finalCheck = function finalCheck() + { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] } + ] }, + { SECTION: [ // child2 + { TEXT_LEAF: [] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function getID() + { + return "hide grand parent while its children stay visible"; + } + } + + /** + * Change container style, hide parents while their children stay visible. + */ + function test3(aContainerID, aParentID, aParent2ID, aChildID, aChild2ID) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aParentID)), + new invokerChecker(EVENT_HIDE, getNode(aParent2ID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_SHOW, getNode(aChild2ID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function invoke() + { + var tree = + { SECTION: [ // container + { SECTION: [ // parent + { SECTION: [ // child + { TEXT_LEAF: [] } + ] } + ] }, + { SECTION: [ // parent2 + { SECTION: [ // child2 + { TEXT_LEAF: [] } + ] }, + ] } + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aContainerID).style.color = "red"; + getNode(aParentID).style.visibility = "hidden"; + getNode(aParent2ID).style.visibility = "hidden"; + } + + this.finalCheck = function finalCheck() + { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] } + ] }, + { SECTION: [ // child2 + { TEXT_LEAF: [] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function getID() + { + return "change container style, hide parents while their children stay visible"; + } + } + + /** + * Change container style and make visible child inside the table. + */ + function test4(aContainerID, aChildID) + { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_REORDER, getNode(aChildID).parentNode) + ]; + + this.invoke = function invoke() + { + var tree = + { SECTION: [ + { TABLE: [ + { ROW: [ + { CELL: [ ] } + ] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aContainerID).style.color = "red"; + getNode(aChildID).style.visibility = "visible"; + } + + this.finalCheck = function finalCheck() + { + var tree = + { SECTION: [ + { TABLE: [ + { ROW: [ + { CELL: [ + { SECTION: [ + { TEXT_LEAF: [] } + ] } + ] } + ] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function getID() + { + return "change container style, make visible child insdie the table"; + } + } + + /** + * Hide subcontainer while child inside the table stays visible. + */ + function test5(aContainerID, aSubContainerID, aChildID) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function invoke() + { + var tree = + { SECTION: [ // container + { SECTION: [ // subcontainer + { TABLE: [ + { ROW: [ + { CELL: [ + { SECTION: [ // child + { TEXT_LEAF: [] } + ] } + ] } + ] } + ] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aSubContainerID).style.visibility = "hidden"; + } + + this.finalCheck = function finalCheck() + { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] } + ] } + ] }; + testAccessibleTree(aContainerID, tree); + } + + this.getID = function getID() + { + return "hide subcontainer while child inside the table stays visible"; + } + } + + /** + * Hide subcontainer while its child and child inside the nested table stays visible. + */ + function test6(aContainerID, aSubContainerID, aChildID, aChild2ID) + { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_SHOW, getNode(aChild2ID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)) + ]; + + this.invoke = function invoke() + { + var tree = + { SECTION: [ // container + { SECTION: [ // subcontainer + { TABLE: [ + { ROW: [ + { CELL: [ + { TABLE: [ // nested table + { ROW: [ + { CELL: [ + { SECTION: [ // child + { TEXT_LEAF: [] } ]} ]} ]} ]} ]} ]} ]}, + { SECTION: [ // child2 + { TEXT_LEAF: [] } ]} ]} ]}; + + testAccessibleTree(aContainerID, tree); + + // invoke + getNode(aSubContainerID).style.visibility = "hidden"; + } + + this.finalCheck = function finalCheck() + { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] } ]}, + { SECTION: [ // child2 + { TEXT_LEAF: [] } ]} ]}; + + testAccessibleTree(aContainerID, tree); + } + + this.getID = function getID() + { + return "hide subcontainer while its child and child inside the nested table stays visible"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new test1("t1_container", "t1_parent", "t1_child")); + gQueue.push(new test2("t2_container", "t2_grandparent", "t2_child", "t2_child2")); + gQueue.push(new test3("t3_container", "t3_parent", "t3_parent2", "t3_child", "t3_child2")); + gQueue.push(new test4("t4_container", "t4_child")); + gQueue.push(new test5("t5_container", "t5_subcontainer", "t5_child")); + gQueue.push(new test6("t6_container", "t6_subcontainer", "t6_child", "t6_child2")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Develop a way to handle visibility style" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125"> + Mozilla Bug 606125 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- hide parent while child stays visible --> + <div id="t1_container"> + <div id="t1_parent"> + <div id="t1_child" style="visibility: visible">text</div> + </div> + </div> + + <!-- hide grandparent while its children stay visible --> + <div id="t2_container"> + <div id="t2_grandparent"> + <div> + <div id="t2_child" style="visibility: visible">text</div> + <div id="t2_child2" style="visibility: visible">text</div> + </div> + </div> + </div> + + <!-- change container style, hide parents while their children stay visible --> + <div id="t3_container"> + <div id="t3_parent"> + <div id="t3_child" style="visibility: visible">text</div> + </div> + <div id="t3_parent2"> + <div id="t3_child2" style="visibility: visible">text</div> + </div> + </div> + + <!-- change container style, show child inside the table --> + <div id="t4_container"> + <table> + <tr> + <td> + <div id="t4_child" style="visibility: hidden;">text</div> + </td> + </tr> + </table> + </div> + + <!-- hide subcontainer while child inside the table stays visible --> + <div id="t5_container"> + <div id="t5_subcontainer"> + <table> + <tr> + <td> + <div id="t5_child" style="visibility: visible;">text</div> + </td> + </tr> + </table> + </div> + </div> + + <!-- hide subcontainer while its child and child inside the nested table stays visible --> + <div id="t6_container"> + <div id="t6_subcontainer"> + <table> + <tr> + <td> + <table> + <tr> + <td> + <div id="t6_child" style="visibility: visible;">text</div> + </td> + </tr> + </table> + </td> + </tr> + </table> + <div id="t6_child2" style="visibility: visible">text</div> + </div> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_whitespace.html b/accessible/tests/mochitest/treeupdate/test_whitespace.html new file mode 100644 index 000000000..e7ba9b059 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_whitespace.html @@ -0,0 +1,187 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Whitespace text accessible creation/desctruction</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + 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="../events.js"></script> + + <script type="application/javascript"> + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + /** + * Middle image accessible removal results in text accessible removal. + * + * Before: + * DOM: whitespace img1 whitespace img2 whitespace img3 whitespace, + * a11y: img1 whitespace img2 whitespace img3 + * After: + * DOM: whitespace img1 whitespace whitespace img3 whitespace, + * a11y: img1 whitespace img3 + */ + function removeImg() + { + this.containerNode = getNode("container1"); + this.imgNode = getNode("img1"); + this.img = getAccessible(this.imgNode); + this.text = this.img.nextSibling; + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.img), + new invokerChecker(EVENT_HIDE, this.text), + new invokerChecker(EVENT_REORDER, this.containerNode) + ]; + + this.finalCheck = function textLeafUpdate_finalCheck() + { + var tree = + { SECTION: [ + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] } + ] }; + + testAccessibleTree(this.containerNode, tree); + } + + this.invoke = function setOnClickAttr_invoke() + { + var tree = + { SECTION: [ + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] } + ] }; + + testAccessibleTree(this.containerNode, tree); + + this.containerNode.removeChild(this.imgNode); + } + + this.getID = function setOnClickAttr_getID() + { + return "remove middle img"; + } + } + + /** + * Append image making the whitespace visible and thus accessible. + * Note: images and whitespaces are on different leves of accessible trees, + * so that image container accessible update doesn't update the tree + * of whitespace container. + * + * Before: + * DOM: whitespace emptylink whitespace linkwithimg whitespace + * a11y: emptylink linkwithimg + * After: + * DOM: whitespace linkwithimg whitespace linkwithimg whitespace + * a11y: linkwithimg whitespace linkwithimg + */ + function insertImg() + { + this.containerNode = getNode("container2"); + this.topNode = this.containerNode.parentNode; + this.textNode = this.containerNode.nextSibling; + this.imgNode = document.createElement("img"); + this.imgNode.setAttribute("src", "../moz.png"); + + this.eventSeq = [ + new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.textNode), + new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.imgNode), + new orderChecker(), + new invokerChecker(EVENT_REORDER, this.topNode) + ]; + + this.invoke = function insertImg_invoke() + { + var tree = + { SECTION: [ + { LINK: [] }, + { LINK: [ + { GRAPHIC: [] } + ] } + ] }; + + testAccessibleTree(this.topNode, tree); + + this.containerNode.appendChild(this.imgNode); + } + + this.finalCheck = function insertImg_finalCheck() + { + var tree = + { SECTION: [ + { LINK: [ + { GRAPHIC: [ ] } + ] }, + { TEXT_LEAF: [ ] }, + { LINK: [ + { GRAPHIC: [ ] } + ] } + ] }; + + testAccessibleTree(this.topNode, tree); + } + + this.getID = function appendImg_getID() + { + return "insert img into internal container"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + //gA11yEventDumpID = "eventdump"; // debug stuff + //gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new removeImg()); + gQueue.push(new insertImg()); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Make sure accessible tree is correct when rendered text is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652"> + Mozilla Bug 625652 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container1"> <img src="../moz.png"> <img id="img1" src="../moz.png"> <img src="../moz.png"> </div> + <div> <a id="container2"></a> <a><img src="../moz.png"></a> </div> + + <div id="eventdump"></div> +</body> +</html> |