Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<html>
<head>
<title>Accessible mutation events coalescence testing</title>
<link rel="stylesheet" type="text/css"
<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"
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"
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>