1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/tests/mochitest/events/test_mutation.html Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,520 @@ 1.4 +<html> 1.5 + 1.6 +<head> 1.7 + <title>Accessible mutation events testing</title> 1.8 + 1.9 + <link rel="stylesheet" type="text/css" 1.10 + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> 1.11 + 1.12 + <style> 1.13 + div.displayNone a { display:none; } 1.14 + div.visibilityHidden a { visibility:hidden; } 1.15 +</style> 1.16 + 1.17 + <script type="application/javascript" 1.18 + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 1.19 + <script type="application/javascript" 1.20 + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 1.21 + 1.22 + <script type="application/javascript" 1.23 + src="../common.js"></script> 1.24 + <script type="application/javascript" 1.25 + src="../events.js"></script> 1.26 + 1.27 + <script type="application/javascript"> 1.28 + /** 1.29 + * Invokers. 1.30 + */ 1.31 + var kNoEvents = 0; 1.32 + 1.33 + var kShowEvent = 1; 1.34 + var kHideEvent = 2; 1.35 + var kReorderEvent = 4; 1.36 + var kShowEvents = kShowEvent | kReorderEvent; 1.37 + var kHideEvents = kHideEvent | kReorderEvent; 1.38 + var kHideAndShowEvents = kHideEvents | kShowEvent; 1.39 + 1.40 + /** 1.41 + * Base class to test mutation a11y events. 1.42 + * 1.43 + * @param aNodeOrID [in] node invoker's action is executed for 1.44 + * @param aEventTypes [in] events to register (see constants above) 1.45 + * @param aDoNotExpectEvents [in] boolean indicates if events are expected 1.46 + */ 1.47 + function mutateA11yTree(aNodeOrID, aEventTypes, aDoNotExpectEvents) 1.48 + { 1.49 + // Interface 1.50 + this.DOMNode = getNode(aNodeOrID); 1.51 + this.doNotExpectEvents = aDoNotExpectEvents; 1.52 + this.eventSeq = []; 1.53 + this.unexpectedEventSeq = []; 1.54 + 1.55 + /** 1.56 + * Change default target (aNodeOrID) registered for the given event type. 1.57 + */ 1.58 + this.setTarget = function mutateA11yTree_setTarget(aEventType, aTarget) 1.59 + { 1.60 + var type = this.getA11yEventType(aEventType); 1.61 + for (var idx = 0; idx < this.getEventSeq().length; idx++) { 1.62 + if (this.getEventSeq()[idx].type == type) { 1.63 + this.getEventSeq()[idx].target = aTarget; 1.64 + return idx; 1.65 + } 1.66 + } 1.67 + return -1; 1.68 + } 1.69 + 1.70 + /** 1.71 + * Replace the default target currently registered for a given event type 1.72 + * with the nodes in the targets array. 1.73 + */ 1.74 + this.setTargets = function mutateA11yTree_setTargets(aEventType, aTargets) { 1.75 + var targetIdx = this.setTarget(aEventType, aTargets[0]); 1.76 + 1.77 + var type = this.getA11yEventType(aEventType); 1.78 + for (var i = 1; i < aTargets.length; i++) { 1.79 + var checker = new invokerChecker(type, aTargets[i]); 1.80 + this.getEventSeq().splice(++targetIdx, 0, checker); 1.81 + } 1.82 + } 1.83 + 1.84 + // Implementation 1.85 + this.getA11yEventType = function mutateA11yTree_getA11yEventType(aEventType) 1.86 + { 1.87 + if (aEventType == kReorderEvent) 1.88 + return nsIAccessibleEvent.EVENT_REORDER; 1.89 + 1.90 + if (aEventType == kHideEvent) 1.91 + return nsIAccessibleEvent.EVENT_HIDE; 1.92 + 1.93 + if (aEventType == kShowEvent) 1.94 + return nsIAccessibleEvent.EVENT_SHOW; 1.95 + } 1.96 + 1.97 + this.getEventSeq = function mutateA11yTree_getEventSeq() 1.98 + { 1.99 + return this.doNotExpectEvents ? this.unexpectedEventSeq : this.eventSeq; 1.100 + } 1.101 + 1.102 + if (aEventTypes & kHideEvent) { 1.103 + var checker = new invokerChecker(this.getA11yEventType(kHideEvent), 1.104 + this.DOMNode); 1.105 + this.getEventSeq().push(checker); 1.106 + } 1.107 + 1.108 + if (aEventTypes & kShowEvent) { 1.109 + var checker = new invokerChecker(this.getA11yEventType(kShowEvent), 1.110 + this.DOMNode); 1.111 + this.getEventSeq().push(checker); 1.112 + } 1.113 + 1.114 + if (aEventTypes & kReorderEvent) { 1.115 + var checker = new invokerChecker(this.getA11yEventType(kReorderEvent), 1.116 + this.DOMNode.parentNode); 1.117 + this.getEventSeq().push(checker); 1.118 + } 1.119 + } 1.120 + 1.121 + /** 1.122 + * Change CSS style for the given node. 1.123 + */ 1.124 + function changeStyle(aNodeOrID, aProp, aValue, aEventTypes) 1.125 + { 1.126 + this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false); 1.127 + 1.128 + this.invoke = function changeStyle_invoke() 1.129 + { 1.130 + this.DOMNode.style[aProp] = aValue; 1.131 + } 1.132 + 1.133 + this.getID = function changeStyle_getID() 1.134 + { 1.135 + return aNodeOrID + " change style " + aProp + " on value " + aValue; 1.136 + } 1.137 + } 1.138 + 1.139 + /** 1.140 + * Change class name for the given node. 1.141 + */ 1.142 + function changeClass(aParentNodeOrID, aNodeOrID, aClassName, aEventTypes) 1.143 + { 1.144 + this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false); 1.145 + 1.146 + this.invoke = function changeClass_invoke() 1.147 + { 1.148 + this.parentDOMNode.className = aClassName; 1.149 + } 1.150 + 1.151 + this.getID = function changeClass_getID() 1.152 + { 1.153 + return aNodeOrID + " change class " + aClassName; 1.154 + } 1.155 + 1.156 + this.parentDOMNode = getNode(aParentNodeOrID); 1.157 + } 1.158 + 1.159 + /** 1.160 + * Clone the node and append it to its parent. 1.161 + */ 1.162 + function cloneAndAppendToDOM(aNodeOrID, aEventTypes, 1.163 + aTargetsFunc, aReorderTargetFunc) 1.164 + { 1.165 + var eventTypes = aEventTypes || kShowEvents; 1.166 + var doNotExpectEvents = (aEventTypes == kNoEvents); 1.167 + 1.168 + this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes, 1.169 + doNotExpectEvents); 1.170 + 1.171 + this.invoke = function cloneAndAppendToDOM_invoke() 1.172 + { 1.173 + var newElm = this.DOMNode.cloneNode(true); 1.174 + newElm.removeAttribute('id'); 1.175 + 1.176 + var targets = aTargetsFunc ? 1.177 + aTargetsFunc.call(null, newElm) : [newElm]; 1.178 + this.setTargets(kShowEvent, targets); 1.179 + 1.180 + if (aReorderTargetFunc) { 1.181 + var reorderTarget = aReorderTargetFunc.call(null, this.DOMNode); 1.182 + this.setTarget(kReorderEvent, reorderTarget); 1.183 + } 1.184 + 1.185 + this.DOMNode.parentNode.appendChild(newElm); 1.186 + } 1.187 + 1.188 + this.getID = function cloneAndAppendToDOM_getID() 1.189 + { 1.190 + return aNodeOrID + " clone and append to DOM."; 1.191 + } 1.192 + } 1.193 + 1.194 + /** 1.195 + * Removes the node from DOM. 1.196 + */ 1.197 + function removeFromDOM(aNodeOrID, aEventTypes, 1.198 + aTargetsFunc, aReorderTargetFunc) 1.199 + { 1.200 + var eventTypes = aEventTypes || kHideEvents; 1.201 + var doNotExpectEvents = (aEventTypes == kNoEvents); 1.202 + 1.203 + this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes, 1.204 + doNotExpectEvents); 1.205 + 1.206 + this.invoke = function removeFromDOM_invoke() 1.207 + { 1.208 + this.DOMNode.parentNode.removeChild(this.DOMNode); 1.209 + } 1.210 + 1.211 + this.getID = function removeFromDOM_getID() 1.212 + { 1.213 + return prettyName(aNodeOrID) + " remove from DOM."; 1.214 + } 1.215 + 1.216 + if (aTargetsFunc && (eventTypes & kHideEvent)) 1.217 + this.setTargets(kHideEvent, aTargetsFunc.call(null, this.DOMNode)); 1.218 + 1.219 + if (aReorderTargetFunc && (eventTypes & kReorderEvent)) 1.220 + this.setTarget(kReorderEvent, 1.221 + aReorderTargetFunc.call(null, this.DOMNode)); 1.222 + } 1.223 + 1.224 + /** 1.225 + * Clone the node and replace the original node by cloned one. 1.226 + */ 1.227 + function cloneAndReplaceInDOM(aNodeOrID) 1.228 + { 1.229 + this.__proto__ = new mutateA11yTree(aNodeOrID, kHideAndShowEvents, 1.230 + false); 1.231 + 1.232 + this.invoke = function cloneAndReplaceInDOM_invoke() 1.233 + { 1.234 + this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode); 1.235 + } 1.236 + 1.237 + this.getID = function cloneAndReplaceInDOM_getID() 1.238 + { 1.239 + return aNodeOrID + " clone and replace in DOM."; 1.240 + } 1.241 + 1.242 + this.newElm = this.DOMNode.cloneNode(true); 1.243 + this.newElm.removeAttribute('id'); 1.244 + this.setTarget(kShowEvent, this.newElm); 1.245 + } 1.246 + 1.247 + /** 1.248 + * Trigger content insertion (flush layout), removal and insertion of 1.249 + * the same element for the same parent. 1.250 + */ 1.251 + function test1(aContainerID) 1.252 + { 1.253 + this.divNode = document.createElement("div"); 1.254 + this.divNode.setAttribute("id", "div-test1"); 1.255 + this.containerNode = getNode(aContainerID); 1.256 + 1.257 + this.eventSeq = [ 1.258 + new invokerChecker(EVENT_SHOW, this.divNode), 1.259 + new invokerChecker(EVENT_REORDER, this.containerNode) 1.260 + ]; 1.261 + 1.262 + this.invoke = function test1_invoke() 1.263 + { 1.264 + this.containerNode.appendChild(this.divNode); 1.265 + getComputedStyle(this.divNode, "").color; 1.266 + this.containerNode.removeChild(this.divNode); 1.267 + this.containerNode.appendChild(this.divNode); 1.268 + } 1.269 + 1.270 + this.getID = function test1_getID() 1.271 + { 1.272 + return "fuzzy test #1: content insertion (flush layout), removal and" + 1.273 + "reinsertion"; 1.274 + } 1.275 + } 1.276 + 1.277 + /** 1.278 + * Trigger content insertion (flush layout), removal and insertion of 1.279 + * the same element for the different parents. 1.280 + */ 1.281 + function test2(aContainerID, aTmpContainerID) 1.282 + { 1.283 + this.divNode = document.createElement("div"); 1.284 + this.divNode.setAttribute("id", "div-test2"); 1.285 + this.containerNode = getNode(aContainerID); 1.286 + this.tmpContainerNode = getNode(aTmpContainerID); 1.287 + this.container = getAccessible(this.containerNode); 1.288 + this.tmpContainer = getAccessible(this.tmpContainerNode); 1.289 + 1.290 + this.eventSeq = [ 1.291 + new invokerChecker(EVENT_SHOW, this.divNode), 1.292 + new invokerChecker(EVENT_REORDER, this.containerNode) 1.293 + ]; 1.294 + 1.295 + this.unexpectedEventSeq = [ 1.296 + new invokerChecker(EVENT_REORDER, this.tmpContainerNode) 1.297 + ]; 1.298 + 1.299 + this.invoke = function test2_invoke() 1.300 + { 1.301 + this.tmpContainerNode.appendChild(this.divNode); 1.302 + getComputedStyle(this.divNode, "").color; 1.303 + this.tmpContainerNode.removeChild(this.divNode); 1.304 + this.containerNode.appendChild(this.divNode); 1.305 + } 1.306 + 1.307 + this.getID = function test2_getID() 1.308 + { 1.309 + return "fuzzy test #2: content insertion (flush layout), removal and" + 1.310 + "reinsertion under another container"; 1.311 + } 1.312 + } 1.313 + 1.314 + /** 1.315 + * Content insertion (flush layout) and then removal (nothing was changed). 1.316 + */ 1.317 + function test3(aContainerID) 1.318 + { 1.319 + this.divNode = document.createElement("div"); 1.320 + this.divNode.setAttribute("id", "div-test3"); 1.321 + this.containerNode = getNode(aContainerID); 1.322 + 1.323 + this.unexpectedEventSeq = [ 1.324 + new invokerChecker(EVENT_SHOW, this.divNode), 1.325 + new invokerChecker(EVENT_HIDE, this.divNode), 1.326 + new invokerChecker(EVENT_REORDER, this.containerNode) 1.327 + ]; 1.328 + 1.329 + this.invoke = function test3_invoke() 1.330 + { 1.331 + this.containerNode.appendChild(this.divNode); 1.332 + getComputedStyle(this.divNode, "").color; 1.333 + this.containerNode.removeChild(this.divNode); 1.334 + } 1.335 + 1.336 + this.getID = function test3_getID() 1.337 + { 1.338 + return "fuzzy test #3: content insertion (flush layout) and removal"; 1.339 + } 1.340 + } 1.341 + 1.342 + /** 1.343 + * Target getters. 1.344 + */ 1.345 + function getFirstChild(aNode) 1.346 + { 1.347 + return [aNode.firstChild]; 1.348 + } 1.349 + 1.350 + function getNEnsureFirstChild(aNode) 1.351 + { 1.352 + var node = aNode.firstChild; 1.353 + getAccessible(node); 1.354 + return [node]; 1.355 + } 1.356 + 1.357 + function getNEnsureChildren(aNode) 1.358 + { 1.359 + var children = []; 1.360 + var node = aNode.firstChild; 1.361 + do { 1.362 + children.push(node); 1.363 + getAccessible(node); 1.364 + node = node.nextSibling; 1.365 + } while (node); 1.366 + 1.367 + return children; 1.368 + } 1.369 + 1.370 + function getParent(aNode) 1.371 + { 1.372 + return aNode.parentNode; 1.373 + } 1.374 + 1.375 + /** 1.376 + * Do tests. 1.377 + */ 1.378 + var gQueue = null; 1.379 + //gA11yEventDumpToConsole = true; // debug stuff 1.380 + 1.381 + function doTests() 1.382 + { 1.383 + gQueue = new eventQueue(); 1.384 + 1.385 + // Show/hide events by changing of display style of accessible DOM node 1.386 + // from 'inline' to 'none', 'none' to 'inline'. 1.387 + var id = "link1"; 1.388 + getAccessible(id); // ensure accessible is created 1.389 + gQueue.push(new changeStyle(id, "display", "none", kHideEvents)); 1.390 + gQueue.push(new changeStyle(id, "display", "inline", kShowEvents)); 1.391 + 1.392 + // Show/hide events by changing of visibility style of accessible DOM node 1.393 + // from 'visible' to 'hidden', 'hidden' to 'visible'. 1.394 + var id = "link2"; 1.395 + getAccessible(id); 1.396 + gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents)); 1.397 + gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents)); 1.398 + 1.399 + // Show/hide events by changing of display style of accessible DOM node 1.400 + // from 'inline' to 'block', 'block' to 'inline'. 1.401 + var id = "link3"; 1.402 + getAccessible(id); // ensure accessible is created 1.403 + gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents)); 1.404 + gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents)); 1.405 + 1.406 + // Show/hide events by changing of visibility style of accessible DOM node 1.407 + // from 'collapse' to 'visible', 'visible' to 'collapse'. 1.408 + var id = "link4"; 1.409 + gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents)); 1.410 + gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents)); 1.411 + 1.412 + // Show/hide events by adding new accessible DOM node and removing old one. 1.413 + var id = "link5"; 1.414 + gQueue.push(new cloneAndAppendToDOM(id)); 1.415 + gQueue.push(new removeFromDOM(id)); 1.416 + 1.417 + // No show/hide events by adding new not accessible DOM node and removing 1.418 + // old one, no reorder event for their parent. 1.419 + var id = "child1"; 1.420 + gQueue.push(new cloneAndAppendToDOM(id, kNoEvents)); 1.421 + gQueue.push(new removeFromDOM(id, kNoEvents)); 1.422 + 1.423 + // Show/hide events by adding new accessible DOM node and removing 1.424 + // old one, there is reorder event for their parent. 1.425 + var id = "child2"; 1.426 + gQueue.push(new cloneAndAppendToDOM(id)); 1.427 + gQueue.push(new removeFromDOM(id)); 1.428 + 1.429 + // Show/hide events by adding new DOM node containing accessible DOM and 1.430 + // removing old one, there is reorder event for their parent. 1.431 + var id = "child3"; 1.432 + gQueue.push(new cloneAndAppendToDOM(id, kShowEvents, getFirstChild, 1.433 + getParent)); 1.434 + 1.435 + // Hide event for accessible child of unaccessible removed DOM node and 1.436 + // reorder event for its parent. 1.437 + gQueue.push(new removeFromDOM(id, kHideEvents, 1.438 + getNEnsureFirstChild, getParent)); 1.439 + 1.440 + // Hide events for accessible children of unaccessible removed DOM node 1.441 + // and reorder event for its parent. 1.442 + gQueue.push(new removeFromDOM("child4", kHideEvents, 1.443 + getNEnsureChildren, getParent)); 1.444 + 1.445 + // Show/hide events by creating new accessible DOM node and replacing 1.446 + // old one. 1.447 + getAccessible("link6"); // ensure accessible is created 1.448 + gQueue.push(new cloneAndReplaceInDOM("link6")); 1.449 + 1.450 + // Show/hide events by changing class name on the parent node. 1.451 + gQueue.push(new changeClass("container2", "link7", "", kShowEvents)); 1.452 + gQueue.push(new changeClass("container2", "link7", "displayNone", 1.453 + kHideEvents)); 1.454 + 1.455 + gQueue.push(new changeClass("container3", "link8", "", kShowEvents)); 1.456 + gQueue.push(new changeClass("container3", "link8", "visibilityHidden", 1.457 + kHideEvents)); 1.458 + 1.459 + gQueue.push(new test1("testContainer")); 1.460 + gQueue.push(new test2("testContainer", "testContainer2")); 1.461 + gQueue.push(new test2("testContainer", "testNestedContainer")); 1.462 + gQueue.push(new test3("testContainer")); 1.463 + 1.464 + gQueue.invoke(); // Will call SimpleTest.finish(); 1.465 + } 1.466 + 1.467 + SimpleTest.waitForExplicitFinish(); 1.468 + addA11yLoadEvent(doTests); 1.469 + </script> 1.470 +</head> 1.471 + 1.472 +<body> 1.473 + 1.474 + <a target="_blank" 1.475 + href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985" 1.476 + title=" turn the test from bug 354745 into mochitest"> 1.477 + Mozilla Bug 469985</a> 1.478 + <a target="_blank" 1.479 + href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662" 1.480 + title="no reorder event when html:link display property is changed from 'none' to 'inline'"> 1.481 + Mozilla Bug 472662</a> 1.482 + <a target="_blank" 1.483 + title="Rework accessible tree update code" 1.484 + href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> 1.485 + Mozilla Bug 570275</a> 1.486 + <a target="_blank" 1.487 + title="Develop a way to handle visibility style" 1.488 + href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125"> 1.489 + Mozilla Bug 606125</a> 1.490 + <a target="_blank" 1.491 + title="Update accessible tree on content insertion after layout" 1.492 + href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015"> 1.493 + Mozilla Bug 498015</a> 1.494 + 1.495 + <p id="display"></p> 1.496 + <div id="content" style="display: none"></div> 1.497 + <pre id="test"> 1.498 + </pre> 1.499 + <div id="eventdump"></div> 1.500 + 1.501 + <div id="testContainer"> 1.502 + <a id="link1" href="http://www.google.com">Link #1</a> 1.503 + <a id="link2" href="http://www.google.com">Link #2</a> 1.504 + <a id="link3" href="http://www.google.com">Link #3</a> 1.505 + <a id="link4" href="http://www.google.com" style="visibility:collapse">Link #4</a> 1.506 + <a id="link5" href="http://www.google.com">Link #5</a> 1.507 + 1.508 + <div id="container" role="list"> 1.509 + <span id="child1"></span> 1.510 + <span id="child2" role="listitem"></span> 1.511 + <span id="child3"><span role="listitem"></span></span> 1.512 + <span id="child4"><span id="child4_1" role="listitem"></span><span id="child4_2" role="listitem"></span></span> 1.513 + </div> 1.514 + 1.515 + <a id="link6" href="http://www.google.com">Link #6</a> 1.516 + 1.517 + <div id="container2" class="displayNone"><a id="link7">Link #7</a></div> 1.518 + <div id="container3" class="visibilityHidden"><a id="link8">Link #8</a></div> 1.519 + <div id="testNestedContainer"></div> 1.520 + </div> 1.521 + <div id="testContainer2"></div> 1.522 +</body> 1.523 +</html>