diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/tests/mochitest/events/test_coalescence.html | 817 |
1 files changed, 817 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/events/test_coalescence.html b/accessible/tests/mochitest/events/test_coalescence.html new file mode 100644 index 0000000000..0f8ad52a8b --- /dev/null +++ b/accessible/tests/mochitest/events/test_coalescence.html @@ -0,0 +1,817 @@ +<html> + +<head> + <title>Accessible mutation events coalescence testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../states.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invoker base classes + + const kRemoveElm = 1; + const kHideElm = 2; + const kAddElm = 3; + const kShowElm = 4; + + /** + * Base class to test of mutation events coalescence. + */ + function coalescenceBase(aChildAction, aParentAction, + aPerformActionOnChildInTheFirstPlace) { + // Invoker interface + + this.invoke = function coalescenceBase_invoke() { + if (aPerformActionOnChildInTheFirstPlace) { + this.invokeAction(this.childNode, aChildAction); + this.invokeAction(this.parentNode, aParentAction); + } else { + this.invokeAction(this.parentNode, aParentAction); + this.invokeAction(this.childNode, aChildAction); + } + }; + + this.getID = function coalescenceBase_getID() { + var childAction = this.getActionName(aChildAction) + " child"; + var parentAction = this.getActionName(aParentAction) + " parent"; + + if (aPerformActionOnChildInTheFirstPlace) + return childAction + " and then " + parentAction; + + return parentAction + " and then " + childAction; + }; + + this.finalCheck = function coalescenceBase_check() { + if (this.getEventType(aChildAction) == EVENT_HIDE) { + testIsDefunct(this.child); + } + if (this.getEventType(aParentAction) == EVENT_HIDE) { + testIsDefunct(this.parent); + } + }; + + // Implementation details + + this.invokeAction = function coalescenceBase_invokeAction(aNode, aAction) { + switch (aAction) { + case kRemoveElm: + aNode.remove(); + break; + + case kHideElm: + aNode.style.display = "none"; + break; + + case kAddElm: + if (aNode == this.parentNode) + this.hostNode.appendChild(this.parentNode); + else + this.parentNode.appendChild(this.childNode); + break; + + case kShowElm: + aNode.style.display = "block"; + break; + + default: + return INVOKER_ACTION_FAILED; + } + // 0 means the action succeeded. + return 0; + }; + + this.getEventType = function coalescenceBase_getEventType(aAction) { + switch (aAction) { + case kRemoveElm: case kHideElm: + return EVENT_HIDE; + case kAddElm: case kShowElm: + return EVENT_SHOW; + } + return 0; + }; + + this.getActionName = function coalescenceBase_getActionName(aAction) { + switch (aAction) { + case kRemoveElm: + return "remove"; + case kHideElm: + return "hide"; + case kAddElm: + return "add"; + case kShowElm: + return "show"; + default: + return "??"; + } + }; + + this.initSequence = function coalescenceBase_initSequence() { + // expected events + var eventType = this.getEventType(aParentAction); + this.eventSeq = [ + new invokerChecker(eventType, this.parentNode), + new invokerChecker(EVENT_REORDER, this.hostNode), + ]; + + // unexpected events + this.unexpectedEventSeq = [ + new invokerChecker(this.getEventType(aChildAction), this.childNode), + new invokerChecker(EVENT_REORDER, this.parentNode), + ]; + }; + } + + /** + * Remove or hide mutation events coalescence testing. + */ + function removeOrHideCoalescenceBase(aChildID, aParentID, + aChildAction, aParentAction, + aPerformActionOnChildInTheFirstPlace) { + this.__proto__ = new coalescenceBase(aChildAction, aParentAction, + aPerformActionOnChildInTheFirstPlace); + + this.init = function removeOrHideCoalescenceBase_init() { + this.childNode = getNode(aChildID); + this.parentNode = getNode(aParentID); + this.child = getAccessible(this.childNode); + this.parent = getAccessible(this.parentNode); + this.hostNode = this.parentNode.parentNode; + }; + + // Initalization + + this.init(); + this.initSequence(); + } + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + /** + * Remove child node and then its parent node from DOM tree. + */ + function removeChildNParent(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kRemoveElm, kRemoveElm, + true); + } + + /** + * Remove parent node and then its child node from DOM tree. + */ + function removeParentNChild(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kRemoveElm, kRemoveElm, + false); + } + + /** + * Hide child node and then its parent node. + */ + function hideChildNParent(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kHideElm, kHideElm, + true); + } + + /** + * Hide parent node and then its child node. + */ + function hideParentNChild(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kHideElm, kHideElm, + false); + } + + /** + * Hide child node and then remove its parent node. + */ + function hideChildNRemoveParent(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kHideElm, kRemoveElm, + true); + } + + /** + * Hide parent node and then remove its child node. + */ + function hideParentNRemoveChild(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kRemoveElm, kHideElm, + false); + } + + /** + * Remove child node and then hide its parent node. + */ + function removeChildNHideParent(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kRemoveElm, kHideElm, + true); + } + + /** + * Remove parent node and then hide its child node. + */ + function removeParentNHideChild(aChildID, aParentID) { + this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID, + kHideElm, kRemoveElm, + false); + } + + /** + * Create and append parent node and create and append child node to it. + */ + function addParentNChild(aHostID, aPerformActionOnChildInTheFirstPlace) { + this.init = function addParentNChild_init() { + this.hostNode = getNode(aHostID); + this.parentNode = document.createElement("select"); + this.childNode = document.createElement("option"); + this.childNode.textContent = "testing"; + }; + + this.__proto__ = new coalescenceBase(kAddElm, kAddElm, + aPerformActionOnChildInTheFirstPlace); + + this.init(); + this.initSequence(); + } + + /** + * Show parent node and show child node to it. + */ + function showParentNChild(aParentID, aChildID, + aPerformActionOnChildInTheFirstPlace) { + this.init = function showParentNChild_init() { + this.parentNode = getNode(aParentID); + this.hostNode = this.parentNode.parentNode; + this.childNode = getNode(aChildID); + }; + + this.__proto__ = new coalescenceBase(kShowElm, kShowElm, + aPerformActionOnChildInTheFirstPlace); + + this.init(); + this.initSequence(); + } + + /** + * Create and append child node to the DOM and then show parent node. + */ + function showParentNAddChild(aParentID, + aPerformActionOnChildInTheFirstPlace) { + this.init = function showParentNAddChild_init() { + this.parentNode = getNode(aParentID); + this.hostNode = this.parentNode.parentNode; + this.childNode = document.createElement("option"); + this.childNode.textContent = "testing"; + }; + + this.__proto__ = new coalescenceBase(kAddElm, kShowElm, + aPerformActionOnChildInTheFirstPlace); + + this.init(); + this.initSequence(); + } + + /** + * Remove children and parent + */ + function removeGrandChildrenNHideParent(aChild1Id, aChild2Id, aParentId) { + this.child1 = getNode(aChild1Id); + this.child2 = getNode(aChild2Id); + this.parent = getNode(aParentId); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getAccessible(aParentId)), + new invokerChecker(EVENT_REORDER, getNode(aParentId).parentNode), + new unexpectedInvokerChecker(EVENT_HIDE, getAccessible(aChild1Id)), + new unexpectedInvokerChecker(EVENT_HIDE, getAccessible(aChild2Id)), + new unexpectedInvokerChecker(EVENT_REORDER, getAccessible(aParentId)), + ]; + + this.invoke = function removeGrandChildrenNHideParent_invoke() { + this.child1.remove(); + this.child2.remove(); + this.parent.hidden = true; + }; + + this.getID = function removeGrandChildrenNHideParent_getID() { + return "remove grand children of different parents and then hide their grand parent"; + }; + } + + /** + * Remove a child, and then its parent. + */ + function test3() { + this.o = getAccessible("t3_o"); + this.ofc = getAccessible("t3_o").firstChild; + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.o), + new invokerChecker(EVENT_REORDER, "t3_lb"), + new unexpectedInvokerChecker(EVENT_HIDE, this.ofc), + new unexpectedInvokerChecker(EVENT_REORDER, this.o), + ]; + + this.invoke = function test3_invoke() { + getNode("t3_o").textContent = ""; + getNode("t3_lb").removeChild(getNode("t3_o")); + }; + + this.finalCheck = function test3_finalCheck() { + testIsDefunct(this.o); + testIsDefunct(this.ofc); + }; + + this.getID = function test3_getID() { + return "remove a child, and then its parent"; + }; + } + + /** + * Remove children, and then a parent of 2nd child. + */ + function test4() { + this.o1 = getAccessible("t4_o1"); + this.o1fc = this.o1.firstChild; + this.o2 = getAccessible("t4_o2"); + this.o2fc = this.o2.firstChild; + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.o1fc), + new invokerChecker(EVENT_HIDE, this.o2), + new invokerChecker(EVENT_REORDER, "t4_lb"), + new unexpectedInvokerChecker(EVENT_HIDE, this.o2fc), + new unexpectedInvokerChecker(EVENT_REORDER, this.o1), + new unexpectedInvokerChecker(EVENT_REORDER, this.o2), + ]; + + this.invoke = function test4_invoke() { + getNode("t4_o1").textContent = ""; + getNode("t4_o2").textContent = ""; + getNode("t4_lb").removeChild(getNode("t4_o2")); + }; + + this.finalCheck = function test4_finalCheck() { + testIsDefunct(this.o1fc); + testIsDefunct(this.o2); + testIsDefunct(this.o2fc); + }; + + this.getID = function test4_getID() { + return "remove children, and then a parent of 2nd child"; + }; + } + + /** + * Remove a child, remove a parent sibling, remove the parent + */ + function test5() { + this.o = getAccessible("t5_o"); + this.ofc = this.o.firstChild; + this.b = getAccessible("t5_b"); + this.lb = getAccessible("t5_lb"); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.b), + new invokerChecker(EVENT_HIDE, this.o), + new invokerChecker(EVENT_REORDER, "t5"), + new unexpectedInvokerChecker(EVENT_HIDE, this.ofc), + new unexpectedInvokerChecker(EVENT_REORDER, this.o), + new unexpectedInvokerChecker(EVENT_REORDER, this.lb), + ]; + + this.invoke = function test5_invoke() { + getNode("t5_o").textContent = ""; + getNode("t5").removeChild(getNode("t5_b")); + getNode("t5_lb").removeChild(getNode("t5_o")); + }; + + this.finalCheck = function test5_finalCheck() { + testIsDefunct(this.ofc); + testIsDefunct(this.o); + testIsDefunct(this.b); + }; + + this.getID = function test5_getID() { + return "remove a child, remove a parent sibling, remove the parent"; + }; + } + + /** + * Insert accessibles with a child node moved by aria-owns + * Markup: + * <div id="t6_fc"> + * <div id="t6_owns"></div> + * </div> + * <div id="t6_sc" aria-owns="t6_owns"></div> + */ + function test6() { + this.parent = getNode("t6"); + this.fc = document.createElement("div"); + this.fc.setAttribute("id", "t6_fc"); + this.owns = document.createElement("div"); + this.owns.setAttribute("id", "t6_owns"); + this.sc = document.createElement("div"); + this.sc.setAttribute("id", "t6_sc"); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.fc), + new invokerChecker(EVENT_SHOW, this.sc), + new invokerChecker(EVENT_REORDER, this.parent), + new unexpectedInvokerChecker(EVENT_REORDER, this.fc), + new unexpectedInvokerChecker(EVENT_REORDER, this.sc), + new unexpectedInvokerChecker(EVENT_HIDE, this.owns), + new unexpectedInvokerChecker(EVENT_SHOW, this.owns), + ]; + + this.invoke = function test6_invoke() { + getNode("t6").appendChild(this.fc); + getNode("t6_fc").appendChild(this.owns); + getNode("t6").appendChild(this.sc); + getNode("t6_sc").setAttribute("aria-owns", "t6_owns"); + }; + + this.getID = function test6_getID() { + return "Insert accessibles with a child node moved by aria-owns"; + }; + } + + /** + * Insert text nodes under direct and grand children, and then hide + * their container by means of aria-owns. + * + * Markup: + * <div id="t7_moveplace" aria-owns="t7_c"></div> + * <div id="t7_c"> + * <div id="t7_c_directchild">ha</div> + * <div><div id="t7_c_grandchild">ha</div></div> + * </div> + */ + function test7() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t7_c")), + new invokerChecker(EVENT_SHOW, getNode("t7_c")), + new invokerChecker(EVENT_REORDER, getNode("t7")), + new unexpectedInvokerChecker(EVENT_REORDER, getNode("t7_c_directchild")), + new unexpectedInvokerChecker(EVENT_REORDER, getNode("t7_c_grandchild")), + new unexpectedInvokerChecker(EVENT_SHOW, () => getNode("t7_c_directchild").firstChild), + new unexpectedInvokerChecker(EVENT_SHOW, () => getNode("t7_c_grandchild").firstChild), + ]; + + this.invoke = function test7_invoke() { + getNode("t7_c_directchild").textContent = "ha"; + getNode("t7_c_grandchild").textContent = "ha"; + getNode("t7_moveplace").setAttribute("aria-owns", "t7_c"); + }; + + this.getID = function test7_getID() { + return "Show child accessibles and then hide their container"; + }; + } + + /** + * Move a node by aria-owns from right to left in the tree, so that + * the eventing looks this way: + * reorder for 't8_c1' + * hide for 't8_c1_child' + * show for 't8_c2_moved' + * reorder for 't8_c2' + * hide for 't8_c2_moved' + * + * The hide event should be delivered before the paired show event. + */ + function test8() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t8_c1_child")), + new invokerChecker(EVENT_HIDE, "t8_c2_moved"), + new invokerChecker(EVENT_SHOW, "t8_c2_moved"), + new invokerChecker(EVENT_REORDER, "t8_c2"), + new invokerChecker(EVENT_REORDER, "t8_c1"), + ]; + + this.invoke = function test8_invoke() { + // Remove a node from 't8_c1' container to give the event tree a + // desired structure (the 't8_c1' container node goes first in the event + // tree) + getNode("t8_c1_child").remove(); + // then move 't8_c2_moved' from 't8_c2' to 't8_c1'. + getNode("t8_c1").setAttribute("aria-owns", "t8_c2_moved"); + }; + + this.getID = function test8_getID() { + return "Move a node by aria-owns to left within the tree"; + }; + } + + /** + * Move 't9_c3_moved' node under 't9_c2_moved', and then move 't9_c2_moved' + * node by aria-owns (same as test10 but has different aria-owns + * ordering), the eventing looks same way as in test10: + * reorder for 't9_c1' + * hide for 't9_c1_child' + * show for 't9_c2_moved' + * reorder for 't9_c2' + * hide for 't9_c2_child' + * hide for 't9_c2_moved' + * reorder for 't9_c3' + * hide for 't9_c3_moved' + * + * The hide events for 't9_c2_moved' and 't9_c3_moved' should be delivered + * before the show event for 't9_c2_moved'. + */ + function test9() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t9_c1_child")), + new invokerChecker(EVENT_HIDE, getNode("t9_c2_child")), + new invokerChecker(EVENT_HIDE, "t9_c3_moved"), + new invokerChecker(EVENT_HIDE, "t9_c2_moved"), + new invokerChecker(EVENT_SHOW, "t9_c2_moved"), + new invokerChecker(EVENT_REORDER, "t9_c3"), + new invokerChecker(EVENT_REORDER, "t9_c2"), + new invokerChecker(EVENT_REORDER, "t9_c1"), + new unexpectedInvokerChecker(EVENT_SHOW, "t9_c3_moved"), + ]; + + this.invoke = function test9_invoke() { + // Remove child nodes from 't9_c1' and 't9_c2' containers to give + // the event tree a needed structure ('t9_c1' and 't9_c2' nodes go + // first in the event tree), + getNode("t9_c1_child").remove(); + getNode("t9_c2_child").remove(); + // then do aria-owns magic. + getNode("t9_c2_moved").setAttribute("aria-owns", "t9_c3_moved"); + getNode("t9_c1").setAttribute("aria-owns", "t9_c2_moved"); + }; + + this.getID = function test9_getID() { + return "Move node #1 by aria-owns and then move node #2 into node #1"; + }; + } + + /** + * Move a node 't10_c3_moved' by aria-owns under a node 't10_c2_moved', + * moved by under 't10_1', so that the eventing looks this way: + * reorder for 't10_c1' + * hide for 't10_c1_child' + * show for 't10_c2_moved' + * reorder for 't10_c2' + * hide for 't10_c2_child' + * hide for 't10_c2_moved' + * reorder for 't10_c3' + * hide for 't10_c3_moved' + * + * The hide events for 't10_c2_moved' and 't10_c3_moved' should be delivered + * before the show event for 't10_c2_moved'. + */ + function test10() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t10_c1_child")), + new invokerChecker(EVENT_HIDE, getNode("t10_c2_child")), + new invokerChecker(EVENT_HIDE, getNode("t10_c2_moved")), + new invokerChecker(EVENT_HIDE, getNode("t10_c3_moved")), + new invokerChecker(EVENT_SHOW, getNode("t10_c2_moved")), + new invokerChecker(EVENT_REORDER, "t10_c2"), + new invokerChecker(EVENT_REORDER, "t10_c1"), + new invokerChecker(EVENT_REORDER, "t10_c3"), + ]; + + this.invoke = function test10_invoke() { + // Remove child nodes from 't10_c1' and 't10_c2' containers to give + // the event tree a needed structure ('t10_c1' and 't10_c2' nodes go first + // in the event tree), + getNode("t10_c1_child").remove(); + getNode("t10_c2_child").remove(); + // then do aria-owns stuff. + getNode("t10_c1").setAttribute("aria-owns", "t10_c2_moved"); + getNode("t10_c2_moved").setAttribute("aria-owns", "t10_c3_moved"); + }; + + this.getID = function test10_getID() { + return "Move a node by aria-owns into a node moved by aria-owns to left within the tree"; + }; + } + + /** + * Move a node by aria-owns from right to left in the tree, and then + * move its parent too by aria-owns. No hide event should be fired for + * original node. + */ + function test11() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t11_c1_child")), + new invokerChecker(EVENT_HIDE, getNode("t11_c2")), + new orderChecker(), + new asyncInvokerChecker(EVENT_SHOW, "t11_c2_child"), + new asyncInvokerChecker(EVENT_SHOW, "t11_c2"), + new orderChecker(), + new invokerChecker(EVENT_REORDER, "t11"), + new unexpectedInvokerChecker(EVENT_HIDE, "t11_c2_child"), + new unexpectedInvokerChecker(EVENT_REORDER, "t11_c1"), + new unexpectedInvokerChecker(EVENT_REORDER, "t11_c2"), + new unexpectedInvokerChecker(EVENT_REORDER, "t11_c3"), + ]; + + this.invoke = function test11_invoke() { + // Remove a node from 't11_c1' container to give the event tree a + // desired structure (the 't11_c1' container node goes first in + // the event tree), + getNode("t11_c1_child").remove(); + // then move 't11_c2_moved' from 't11_c2' to 't11_c1', and then move + // 't11_c2' to 't11_c3'. + getNode("t11_c1").setAttribute("aria-owns", "t11_c2_child"); + getNode("t11_c3").setAttribute("aria-owns", "t11_c2"); + }; + + this.getID = function test11_getID() { + return "Move a node by aria-owns to left within the tree"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Do tests. + + gA11yEventDumpToConsole = true; // debug stuff + // enableLogging("eventTree"); + + var gQueue = null; + function doTests() { + gQueue = new eventQueue(); + + gQueue.push(new removeChildNParent("option1", "select1")); + gQueue.push(new removeParentNChild("option2", "select2")); + gQueue.push(new hideChildNParent("option3", "select3")); + gQueue.push(new hideParentNChild("option4", "select4")); + gQueue.push(new hideChildNRemoveParent("option5", "select5")); + gQueue.push(new hideParentNRemoveChild("option6", "select6")); + gQueue.push(new removeChildNHideParent("option7", "select7")); + gQueue.push(new removeParentNHideChild("option8", "select8")); + + gQueue.push(new addParentNChild("testContainer", false)); + gQueue.push(new addParentNChild("testContainer", true)); + gQueue.push(new showParentNChild("select9", "option9", false)); + gQueue.push(new showParentNChild("select10", "option10", true)); + gQueue.push(new showParentNAddChild("select11", false)); + gQueue.push(new showParentNAddChild("select12", true)); + + gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent")); + gQueue.push(new test3()); + gQueue.push(new test4()); + gQueue.push(new test5()); + gQueue.push(new test6()); + gQueue.push(new test7()); + gQueue.push(new test8()); + gQueue.push(new test9()); + gQueue.push(new test10()); + gQueue.push(new test11()); + + 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=513213" + title="coalesce events when new event is appended to the queue"> + Mozilla Bug 513213 + </a><br> + <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="testContainer"> + <select id="select1"> + <option id="option1">option</option> + </select> + <select id="select2"> + <option id="option2">option</option> + </select> + <select id="select3"> + <option id="option3">option</option> + </select> + <select id="select4"> + <option id="option4">option</option> + </select> + <select id="select5"> + <option id="option5">option</option> + </select> + <select id="select6"> + <option id="option6">option</option> + </select> + <select id="select7"> + <option id="option7">option</option> + </select> + <select id="select8"> + <option id="option8">option</option> + </select> + + <select id="select9" style="display: none"> + <option id="option9" style="display: none">testing</option> + </select> + <select id="select10" style="display: none"> + <option id="option10" style="display: none">testing</option> + </select> + <select id="select11" style="display: none"></select> + <select id="select12" style="display: none"></select> + </div> + + <div id="testContainer2"> + <div id="t1_parent"> + <div id="t1_mid1"><div id="t1_child1"></div></div> + <div id="t1_mid2"><div id="t1_child2"></div></div> + </div> + </div> + + <div id="t3"> + <div role="listbox" id="t3_lb"> + <div role="option" id="t3_o">opt</div> + </div> + </div> + + <div id="t4"> + <div role="listbox" id="t4_lb"> + <div role="option" id="t4_o1">opt1</div> + <div role="option" id="t4_o2">opt2</div> + </div> + </div> + + <div id="t5"> + <div role="button" id="t5_b">btn</div> + <div role="listbox" id="t5_lb"> + <div role="option" id="t5_o">opt</div> + </div> + </div> + + <div id="t6"> + </div> + + <div id="t7"> + <div id="t7_moveplace"></div> + <div id="t7_c"> + <div><div id="t7_c_grandchild"></div></div> + <div id="t7_c_directchild"></div> + </div> + </div> + + <div id="t8"> + <div id="t8_c1"><div id="t8_c1_child"></div></div> + <div id="t8_c2"> + <div id="t8_c2_moved"></div> + </div> + </div> + + <div id="t9"> + <div id="t9_c1"><div id="t9_c1_child"></div></div> + <div id="t9_c2"> + <div id="t9_c2_child"></div> + <div id="t9_c2_moved"></div> + </div> + <div id="t9_c3"> + <div id="t9_c3_moved"></div> + </div> + </div> + + <div id="t10"> + <div id="t10_c1"><div id="t10_c1_child"></div></div> + <div id="t10_c2"> + <div id="t10_c2_child"></div> + <div id="t10_c2_moved"></div> + </div> + <div id="t10_c3"> + <div id="t10_c3_moved"></div> + </div> + </div> + + <div id="t11"> + <div id="t11_c1"><div id="t11_c1_child"></div></div> + <div id="t11_c2"><div id="t11_c2_child"></div></div> + <div id="t11_c3"></div> + </div> +</body> +</html> |