accessible/tests/mochitest/events/test_mutation.html

branch
TOR_BUG_9701
changeset 14
925c144e1f1f
equal deleted inserted replaced
-1:000000000000 0:c857391fabab
1 <html>
2
3 <head>
4 <title>Accessible mutation events testing</title>
5
6 <link rel="stylesheet" type="text/css"
7 href="chrome://mochikit/content/tests/SimpleTest/test.css" />
8
9 <style>
10 div.displayNone a { display:none; }
11 div.visibilityHidden a { visibility:hidden; }
12 </style>
13
14 <script type="application/javascript"
15 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
16 <script type="application/javascript"
17 src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
18
19 <script type="application/javascript"
20 src="../common.js"></script>
21 <script type="application/javascript"
22 src="../events.js"></script>
23
24 <script type="application/javascript">
25 /**
26 * Invokers.
27 */
28 var kNoEvents = 0;
29
30 var kShowEvent = 1;
31 var kHideEvent = 2;
32 var kReorderEvent = 4;
33 var kShowEvents = kShowEvent | kReorderEvent;
34 var kHideEvents = kHideEvent | kReorderEvent;
35 var kHideAndShowEvents = kHideEvents | kShowEvent;
36
37 /**
38 * Base class to test mutation a11y events.
39 *
40 * @param aNodeOrID [in] node invoker's action is executed for
41 * @param aEventTypes [in] events to register (see constants above)
42 * @param aDoNotExpectEvents [in] boolean indicates if events are expected
43 */
44 function mutateA11yTree(aNodeOrID, aEventTypes, aDoNotExpectEvents)
45 {
46 // Interface
47 this.DOMNode = getNode(aNodeOrID);
48 this.doNotExpectEvents = aDoNotExpectEvents;
49 this.eventSeq = [];
50 this.unexpectedEventSeq = [];
51
52 /**
53 * Change default target (aNodeOrID) registered for the given event type.
54 */
55 this.setTarget = function mutateA11yTree_setTarget(aEventType, aTarget)
56 {
57 var type = this.getA11yEventType(aEventType);
58 for (var idx = 0; idx < this.getEventSeq().length; idx++) {
59 if (this.getEventSeq()[idx].type == type) {
60 this.getEventSeq()[idx].target = aTarget;
61 return idx;
62 }
63 }
64 return -1;
65 }
66
67 /**
68 * Replace the default target currently registered for a given event type
69 * with the nodes in the targets array.
70 */
71 this.setTargets = function mutateA11yTree_setTargets(aEventType, aTargets) {
72 var targetIdx = this.setTarget(aEventType, aTargets[0]);
73
74 var type = this.getA11yEventType(aEventType);
75 for (var i = 1; i < aTargets.length; i++) {
76 var checker = new invokerChecker(type, aTargets[i]);
77 this.getEventSeq().splice(++targetIdx, 0, checker);
78 }
79 }
80
81 // Implementation
82 this.getA11yEventType = function mutateA11yTree_getA11yEventType(aEventType)
83 {
84 if (aEventType == kReorderEvent)
85 return nsIAccessibleEvent.EVENT_REORDER;
86
87 if (aEventType == kHideEvent)
88 return nsIAccessibleEvent.EVENT_HIDE;
89
90 if (aEventType == kShowEvent)
91 return nsIAccessibleEvent.EVENT_SHOW;
92 }
93
94 this.getEventSeq = function mutateA11yTree_getEventSeq()
95 {
96 return this.doNotExpectEvents ? this.unexpectedEventSeq : this.eventSeq;
97 }
98
99 if (aEventTypes & kHideEvent) {
100 var checker = new invokerChecker(this.getA11yEventType(kHideEvent),
101 this.DOMNode);
102 this.getEventSeq().push(checker);
103 }
104
105 if (aEventTypes & kShowEvent) {
106 var checker = new invokerChecker(this.getA11yEventType(kShowEvent),
107 this.DOMNode);
108 this.getEventSeq().push(checker);
109 }
110
111 if (aEventTypes & kReorderEvent) {
112 var checker = new invokerChecker(this.getA11yEventType(kReorderEvent),
113 this.DOMNode.parentNode);
114 this.getEventSeq().push(checker);
115 }
116 }
117
118 /**
119 * Change CSS style for the given node.
120 */
121 function changeStyle(aNodeOrID, aProp, aValue, aEventTypes)
122 {
123 this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
124
125 this.invoke = function changeStyle_invoke()
126 {
127 this.DOMNode.style[aProp] = aValue;
128 }
129
130 this.getID = function changeStyle_getID()
131 {
132 return aNodeOrID + " change style " + aProp + " on value " + aValue;
133 }
134 }
135
136 /**
137 * Change class name for the given node.
138 */
139 function changeClass(aParentNodeOrID, aNodeOrID, aClassName, aEventTypes)
140 {
141 this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
142
143 this.invoke = function changeClass_invoke()
144 {
145 this.parentDOMNode.className = aClassName;
146 }
147
148 this.getID = function changeClass_getID()
149 {
150 return aNodeOrID + " change class " + aClassName;
151 }
152
153 this.parentDOMNode = getNode(aParentNodeOrID);
154 }
155
156 /**
157 * Clone the node and append it to its parent.
158 */
159 function cloneAndAppendToDOM(aNodeOrID, aEventTypes,
160 aTargetsFunc, aReorderTargetFunc)
161 {
162 var eventTypes = aEventTypes || kShowEvents;
163 var doNotExpectEvents = (aEventTypes == kNoEvents);
164
165 this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
166 doNotExpectEvents);
167
168 this.invoke = function cloneAndAppendToDOM_invoke()
169 {
170 var newElm = this.DOMNode.cloneNode(true);
171 newElm.removeAttribute('id');
172
173 var targets = aTargetsFunc ?
174 aTargetsFunc.call(null, newElm) : [newElm];
175 this.setTargets(kShowEvent, targets);
176
177 if (aReorderTargetFunc) {
178 var reorderTarget = aReorderTargetFunc.call(null, this.DOMNode);
179 this.setTarget(kReorderEvent, reorderTarget);
180 }
181
182 this.DOMNode.parentNode.appendChild(newElm);
183 }
184
185 this.getID = function cloneAndAppendToDOM_getID()
186 {
187 return aNodeOrID + " clone and append to DOM.";
188 }
189 }
190
191 /**
192 * Removes the node from DOM.
193 */
194 function removeFromDOM(aNodeOrID, aEventTypes,
195 aTargetsFunc, aReorderTargetFunc)
196 {
197 var eventTypes = aEventTypes || kHideEvents;
198 var doNotExpectEvents = (aEventTypes == kNoEvents);
199
200 this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
201 doNotExpectEvents);
202
203 this.invoke = function removeFromDOM_invoke()
204 {
205 this.DOMNode.parentNode.removeChild(this.DOMNode);
206 }
207
208 this.getID = function removeFromDOM_getID()
209 {
210 return prettyName(aNodeOrID) + " remove from DOM.";
211 }
212
213 if (aTargetsFunc && (eventTypes & kHideEvent))
214 this.setTargets(kHideEvent, aTargetsFunc.call(null, this.DOMNode));
215
216 if (aReorderTargetFunc && (eventTypes & kReorderEvent))
217 this.setTarget(kReorderEvent,
218 aReorderTargetFunc.call(null, this.DOMNode));
219 }
220
221 /**
222 * Clone the node and replace the original node by cloned one.
223 */
224 function cloneAndReplaceInDOM(aNodeOrID)
225 {
226 this.__proto__ = new mutateA11yTree(aNodeOrID, kHideAndShowEvents,
227 false);
228
229 this.invoke = function cloneAndReplaceInDOM_invoke()
230 {
231 this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode);
232 }
233
234 this.getID = function cloneAndReplaceInDOM_getID()
235 {
236 return aNodeOrID + " clone and replace in DOM.";
237 }
238
239 this.newElm = this.DOMNode.cloneNode(true);
240 this.newElm.removeAttribute('id');
241 this.setTarget(kShowEvent, this.newElm);
242 }
243
244 /**
245 * Trigger content insertion (flush layout), removal and insertion of
246 * the same element for the same parent.
247 */
248 function test1(aContainerID)
249 {
250 this.divNode = document.createElement("div");
251 this.divNode.setAttribute("id", "div-test1");
252 this.containerNode = getNode(aContainerID);
253
254 this.eventSeq = [
255 new invokerChecker(EVENT_SHOW, this.divNode),
256 new invokerChecker(EVENT_REORDER, this.containerNode)
257 ];
258
259 this.invoke = function test1_invoke()
260 {
261 this.containerNode.appendChild(this.divNode);
262 getComputedStyle(this.divNode, "").color;
263 this.containerNode.removeChild(this.divNode);
264 this.containerNode.appendChild(this.divNode);
265 }
266
267 this.getID = function test1_getID()
268 {
269 return "fuzzy test #1: content insertion (flush layout), removal and" +
270 "reinsertion";
271 }
272 }
273
274 /**
275 * Trigger content insertion (flush layout), removal and insertion of
276 * the same element for the different parents.
277 */
278 function test2(aContainerID, aTmpContainerID)
279 {
280 this.divNode = document.createElement("div");
281 this.divNode.setAttribute("id", "div-test2");
282 this.containerNode = getNode(aContainerID);
283 this.tmpContainerNode = getNode(aTmpContainerID);
284 this.container = getAccessible(this.containerNode);
285 this.tmpContainer = getAccessible(this.tmpContainerNode);
286
287 this.eventSeq = [
288 new invokerChecker(EVENT_SHOW, this.divNode),
289 new invokerChecker(EVENT_REORDER, this.containerNode)
290 ];
291
292 this.unexpectedEventSeq = [
293 new invokerChecker(EVENT_REORDER, this.tmpContainerNode)
294 ];
295
296 this.invoke = function test2_invoke()
297 {
298 this.tmpContainerNode.appendChild(this.divNode);
299 getComputedStyle(this.divNode, "").color;
300 this.tmpContainerNode.removeChild(this.divNode);
301 this.containerNode.appendChild(this.divNode);
302 }
303
304 this.getID = function test2_getID()
305 {
306 return "fuzzy test #2: content insertion (flush layout), removal and" +
307 "reinsertion under another container";
308 }
309 }
310
311 /**
312 * Content insertion (flush layout) and then removal (nothing was changed).
313 */
314 function test3(aContainerID)
315 {
316 this.divNode = document.createElement("div");
317 this.divNode.setAttribute("id", "div-test3");
318 this.containerNode = getNode(aContainerID);
319
320 this.unexpectedEventSeq = [
321 new invokerChecker(EVENT_SHOW, this.divNode),
322 new invokerChecker(EVENT_HIDE, this.divNode),
323 new invokerChecker(EVENT_REORDER, this.containerNode)
324 ];
325
326 this.invoke = function test3_invoke()
327 {
328 this.containerNode.appendChild(this.divNode);
329 getComputedStyle(this.divNode, "").color;
330 this.containerNode.removeChild(this.divNode);
331 }
332
333 this.getID = function test3_getID()
334 {
335 return "fuzzy test #3: content insertion (flush layout) and removal";
336 }
337 }
338
339 /**
340 * Target getters.
341 */
342 function getFirstChild(aNode)
343 {
344 return [aNode.firstChild];
345 }
346
347 function getNEnsureFirstChild(aNode)
348 {
349 var node = aNode.firstChild;
350 getAccessible(node);
351 return [node];
352 }
353
354 function getNEnsureChildren(aNode)
355 {
356 var children = [];
357 var node = aNode.firstChild;
358 do {
359 children.push(node);
360 getAccessible(node);
361 node = node.nextSibling;
362 } while (node);
363
364 return children;
365 }
366
367 function getParent(aNode)
368 {
369 return aNode.parentNode;
370 }
371
372 /**
373 * Do tests.
374 */
375 var gQueue = null;
376 //gA11yEventDumpToConsole = true; // debug stuff
377
378 function doTests()
379 {
380 gQueue = new eventQueue();
381
382 // Show/hide events by changing of display style of accessible DOM node
383 // from 'inline' to 'none', 'none' to 'inline'.
384 var id = "link1";
385 getAccessible(id); // ensure accessible is created
386 gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
387 gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
388
389 // Show/hide events by changing of visibility style of accessible DOM node
390 // from 'visible' to 'hidden', 'hidden' to 'visible'.
391 var id = "link2";
392 getAccessible(id);
393 gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
394 gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
395
396 // Show/hide events by changing of display style of accessible DOM node
397 // from 'inline' to 'block', 'block' to 'inline'.
398 var id = "link3";
399 getAccessible(id); // ensure accessible is created
400 gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
401 gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
402
403 // Show/hide events by changing of visibility style of accessible DOM node
404 // from 'collapse' to 'visible', 'visible' to 'collapse'.
405 var id = "link4";
406 gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
407 gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
408
409 // Show/hide events by adding new accessible DOM node and removing old one.
410 var id = "link5";
411 gQueue.push(new cloneAndAppendToDOM(id));
412 gQueue.push(new removeFromDOM(id));
413
414 // No show/hide events by adding new not accessible DOM node and removing
415 // old one, no reorder event for their parent.
416 var id = "child1";
417 gQueue.push(new cloneAndAppendToDOM(id, kNoEvents));
418 gQueue.push(new removeFromDOM(id, kNoEvents));
419
420 // Show/hide events by adding new accessible DOM node and removing
421 // old one, there is reorder event for their parent.
422 var id = "child2";
423 gQueue.push(new cloneAndAppendToDOM(id));
424 gQueue.push(new removeFromDOM(id));
425
426 // Show/hide events by adding new DOM node containing accessible DOM and
427 // removing old one, there is reorder event for their parent.
428 var id = "child3";
429 gQueue.push(new cloneAndAppendToDOM(id, kShowEvents, getFirstChild,
430 getParent));
431
432 // Hide event for accessible child of unaccessible removed DOM node and
433 // reorder event for its parent.
434 gQueue.push(new removeFromDOM(id, kHideEvents,
435 getNEnsureFirstChild, getParent));
436
437 // Hide events for accessible children of unaccessible removed DOM node
438 // and reorder event for its parent.
439 gQueue.push(new removeFromDOM("child4", kHideEvents,
440 getNEnsureChildren, getParent));
441
442 // Show/hide events by creating new accessible DOM node and replacing
443 // old one.
444 getAccessible("link6"); // ensure accessible is created
445 gQueue.push(new cloneAndReplaceInDOM("link6"));
446
447 // Show/hide events by changing class name on the parent node.
448 gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
449 gQueue.push(new changeClass("container2", "link7", "displayNone",
450 kHideEvents));
451
452 gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
453 gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
454 kHideEvents));
455
456 gQueue.push(new test1("testContainer"));
457 gQueue.push(new test2("testContainer", "testContainer2"));
458 gQueue.push(new test2("testContainer", "testNestedContainer"));
459 gQueue.push(new test3("testContainer"));
460
461 gQueue.invoke(); // Will call SimpleTest.finish();
462 }
463
464 SimpleTest.waitForExplicitFinish();
465 addA11yLoadEvent(doTests);
466 </script>
467 </head>
468
469 <body>
470
471 <a target="_blank"
472 href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985"
473 title=" turn the test from bug 354745 into mochitest">
474 Mozilla Bug 469985</a>
475 <a target="_blank"
476 href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
477 title="no reorder event when html:link display property is changed from 'none' to 'inline'">
478 Mozilla Bug 472662</a>
479 <a target="_blank"
480 title="Rework accessible tree update code"
481 href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
482 Mozilla Bug 570275</a>
483 <a target="_blank"
484 title="Develop a way to handle visibility style"
485 href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
486 Mozilla Bug 606125</a>
487 <a target="_blank"
488 title="Update accessible tree on content insertion after layout"
489 href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015">
490 Mozilla Bug 498015</a>
491
492 <p id="display"></p>
493 <div id="content" style="display: none"></div>
494 <pre id="test">
495 </pre>
496 <div id="eventdump"></div>
497
498 <div id="testContainer">
499 <a id="link1" href="http://www.google.com">Link #1</a>
500 <a id="link2" href="http://www.google.com">Link #2</a>
501 <a id="link3" href="http://www.google.com">Link #3</a>
502 <a id="link4" href="http://www.google.com" style="visibility:collapse">Link #4</a>
503 <a id="link5" href="http://www.google.com">Link #5</a>
504
505 <div id="container" role="list">
506 <span id="child1"></span>
507 <span id="child2" role="listitem"></span>
508 <span id="child3"><span role="listitem"></span></span>
509 <span id="child4"><span id="child4_1" role="listitem"></span><span id="child4_2" role="listitem"></span></span>
510 </div>
511
512 <a id="link6" href="http://www.google.com">Link #6</a>
513
514 <div id="container2" class="displayNone"><a id="link7">Link #7</a></div>
515 <div id="container3" class="visibilityHidden"><a id="link8">Link #8</a></div>
516 <div id="testNestedContainer"></div>
517 </div>
518 <div id="testContainer2"></div>
519 </body>
520 </html>

mercurial