2 <script src="../js/resources/js-test-pre.js"></script>
5 window.jsTestIsAsync = true;
6 var mutations, mutations2, mutationsWithOldValue;
10 function testBasic() {
15 debug('Testing basic aspects of attribute observation.');
18 div = document.createElement('div');
19 div.setAttribute('bar', 'foo');
21 observer = new WebKitMutationObserver(function(m) {
25 observer.observe(div, { attributes: true, characterData: true });
26 div.setAttribute('foo', 'bar');
27 div.removeAttribute('bar');
28 setTimeout(checkDisconnectAndMutate, 0);
31 function checkDisconnectAndMutate() {
32 debug('...can attribute changes be observed at all');
34 shouldBe('mutations.length', '2');
35 shouldBe('mutations[0].type', '"attributes"');
36 shouldBe('mutations[0].attributeName', '"foo"');
37 shouldBe('mutations[0].attributeNamespace', 'null');
38 shouldBe('mutations[1].type', '"attributes"');
39 shouldBe('mutations[1].attributeName', '"bar"');
40 shouldBe('mutations[1].attributeNamespace', 'null');
43 observer.disconnect();
44 div.setAttribute('foo', 'baz');
45 setTimeout(checkNotDeliveredAndMutateMultiple, 0);
48 function checkNotDeliveredAndMutateMultiple() {
49 debug('...observer.disconnect() should prevent further delivery of mutations.');
51 shouldBe('mutations', 'null');
52 observer.observe(div, { attributes: true });
53 div.setAttribute('foo', 'bat');
54 div.setAttribute('bar', 'foo');
59 debug('...re-observing after disconnect works with the same observer.');
61 shouldBe('mutations.length', '2');
62 shouldBe('mutations[0].type', '"attributes"');
63 shouldBe('mutations[0].attributeName', '"foo"');
64 shouldBe('mutations[0].attributeNamespace', 'null');
65 shouldBe('mutations[1].type', '"attributes"');
66 shouldBe('mutations[1].attributeName', '"bar"');
67 shouldBe('mutations[1].attributeNamespace', 'null');
68 observer.disconnect();
76 function testWrongType() {
81 debug('Testing that observing without specifying "attributes" does not result in hearing about attribute changes.');
84 div = document.createElement('div');
85 observer = new WebKitMutationObserver(function(m) {
89 observer.observe(div, { childList: true, characterData: true });
90 div.setAttribute('foo', 'bar');
91 setTimeout(finish, 0);
95 shouldBe('mutations', 'null');
96 observer.disconnect();
104 function testMultipleRegistration() {
109 debug('Testing that re-observing the same node with the same observer has the effect of resetting the options.');
113 div = document.createElement('div');
114 observer = new WebKitMutationObserver(function(m) {
119 observer.observe(div, { attributes: true, characterData: true });
120 observer.observe(div, { attributes: true });
121 div.setAttribute('foo', 'bar');
122 setTimeout(checkDisconnectAndMutate, 0);
125 function checkDisconnectAndMutate() {
126 shouldBe('calls', '1');
127 shouldBe('mutations.length', '1');
128 shouldBe('mutations[0].type', '"attributes"');
129 shouldBe('mutations[0].attributeName', '"foo"');
131 observer.observe(div, { attributes: true, characterData: true });
132 observer.observe(div, { childList: true });
133 div.setAttribute('foo', 'baz');
134 setTimeout(finish, 0);
138 shouldBe('mutations', 'null');
139 observer.disconnect();
147 function testMultipleObservers() {
153 debug('Testing that multiple observers can be registered to a given node and both receive mutations.');
155 div = document.createElement('div');
156 observer = new WebKitMutationObserver(function(m) {
159 observer2 = new WebKitMutationObserver(function(m) {
162 observer.observe(div, { attributes: true });
163 observer2.observe(div, { attributes: true });
164 div.setAttribute('foo', 'bar');
165 setTimeout(finish, 0);
169 shouldBe('mutations.length', '1');
170 shouldBe('mutations[0].type', '"attributes"');
171 shouldBe('mutations[0].attributeName', '"foo"');
172 shouldBe('mutations2.length', '1');
173 shouldBe('mutations2[0].type', '"attributes"');
174 shouldBe('mutations2[0].attributeName', '"foo"');
175 observer.disconnect();
176 observer2.disconnect();
184 function testNamespaceURI() {
189 debug('Testing that "attributeNamespace" value is delivered properly.');
191 div = document.createElement('div');
192 observer = new WebKitMutationObserver(function(m) {
196 observer.observe(div, { attributes: true, childList: true });
197 div.setAttributeNS('http://www.foo.com/bar', 'foo', 'bar');
198 setTimeout(finish, 0);
202 shouldBe('mutations.length', '1');
203 shouldBe('mutations[0].type', '"attributes"');
204 shouldBe('mutations[0].attributeName', '"foo"');
205 shouldBe('mutations[0].attributeNamespace', '"http://www.foo.com/bar"');
206 observer.disconnect();
214 function testPropertyAccess() {
219 debug('Testing that modifications to node properties which delegate to attribute storage deliver mutations.');
221 img = document.createElement('img');
222 a = document.createElement('a');
224 observer = new WebKitMutationObserver(function(m) {
228 observer.observe(img, { attributes: true });
229 observer.observe(a, { attributes: true });
234 setTimeout(finish, 0);
238 shouldBe('mutations.length', '2');
239 shouldBe('mutations[0].type', '"attributes"');
240 shouldBe('mutations[0].attributeName', '"src"');
241 shouldBe('mutations[1].type', '"attributes"');
242 shouldBe('mutations[1].attributeName', '"href"');
243 observer.disconnect();
251 function testOrderingWrtDOMSubtreeModified() {
252 var div, div2, subDiv;
257 debug('Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched.');
260 div = document.body.appendChild(document.createElement('div'));
261 div2 = document.body.appendChild(document.createElement('div'));
263 subDiv = div.appendChild(document.createElement('div'));
265 observer = new WebKitMutationObserver(function(m) {
269 listener = function(e) {
270 div2.setAttribute('baz', 'bat');
273 div.addEventListener('DOMSubtreeModified', listener);
274 observer.observe(subDiv, { attributes: true });
275 observer.observe(div2, { attributes: true });
277 subDiv.setAttribute('foo', 'bar');
279 setTimeout(finish, 0);
283 shouldBe('mutations.length', '2');
284 shouldBe('mutations[0].type', '"attributes"');
285 shouldBe('mutations[0].attributeName', '"foo"');
286 shouldBe('mutations[1].type', '"attributes"');
287 shouldBe('mutations[1].attributeName', '"baz"');
288 div.removeEventListener('DOMSubtreeModified', listener);
289 document.body.removeChild(div);
290 observer.disconnect();
298 function testOldValue() {
303 debug('Testing basic oldValue delivery.');
305 div = document.createElement('div');
306 div.setAttribute('bar', 'boo');
308 observer = new WebKitMutationObserver(function(mutations) {
309 window.mutations = mutations;
311 observer.observe(div, { attributes: true, attributeOldValue: true });
312 div.setAttribute('foo', 'bar');
313 div.setAttribute('foo', 'baz');
314 div.removeAttribute('bar');
315 div.removeAttribute('non-existant');
316 setTimeout(finish, 0);
320 shouldBe('mutations.length', '3');
321 shouldBe('mutations[0].type', '"attributes"');
322 shouldBe('mutations[0].attributeName', '"foo"');
323 shouldBe('mutations[0].oldValue', 'null');
324 shouldBe('mutations[1].type', '"attributes"');
325 shouldBe('mutations[1].attributeName', '"foo"');
326 shouldBe('mutations[1].oldValue', '"bar"');
327 shouldBe('mutations[2].type', '"attributes"');
328 shouldBe('mutations[2].attributeName', '"bar"');
329 shouldBe('mutations[2].oldValue', '"boo"');
330 observer.disconnect();
338 function testOldValueAsRequested() {
340 var observerWithOldValue;
344 debug('Testing that oldValue is delivered as requested (or not).');
345 mutationsWithOldValue = null;
347 div = document.createElement('div');
348 div.setAttribute('foo', 'bar');
349 observerWithOldValue = new WebKitMutationObserver(function(mutations) {
350 window.mutationsWithOldValue = mutations;
352 observer = new WebKitMutationObserver(function(mutations) {
353 window.mutations = mutations;
355 observerWithOldValue.observe(div, { attributes: true, attributeOldValue: true });
356 observer.observe(div, { attributes: true });
357 div.setAttribute('foo', 'baz');
358 setTimeout(finish, 0);
362 shouldBe('mutationsWithOldValue.length', '1');
363 shouldBe('mutationsWithOldValue[0].type', '"attributes"');
364 shouldBe('mutationsWithOldValue[0].attributeName', '"foo"');
365 shouldBe('mutationsWithOldValue[0].oldValue', '"bar"');
366 shouldBe('mutations.length', '1');
367 shouldBe('mutations[0].type', '"attributes"');
368 shouldBe('mutations[0].attributeName', '"foo"');
369 shouldBe('mutations[0].oldValue', 'null');
370 observerWithOldValue.disconnect();
371 observer.disconnect();
379 function testOldValueUnionMultipleObservations() {
385 debug('An observer with multiple observations will get attributeOldValue if any entries request it.');
387 div = document.createElement('div');
388 span = div.appendChild(document.createElement('span'));
389 span.setAttribute('foo', 'bar');
390 observer = new WebKitMutationObserver(function(mutations) {
391 window.mutations = mutations;
393 observer.observe(div, { attributes: true, attributeOldValue: true, subtree: true });
394 observer.observe(span, { attributes: true });
395 span.setAttribute('foo', 'baz');
396 setTimeout(finish, 0);
400 shouldBe('mutations.length', '1');
401 shouldBe('mutations[0].type', '"attributes"');
402 shouldBe('mutations[0].attributeName', '"foo"');
403 shouldBe('mutations[0].oldValue', '"bar"');
404 observer.disconnect();
412 function testIDLAttribute() {
417 debug('Testing setting an attribute via reflected IDL attribute.');
419 div = document.createElement('div');
420 observer = new WebKitMutationObserver(function(mutations) {
421 window.mutations = mutations;
423 observer.observe(div, { attributes: true, attributeOldValue: true });
427 setTimeout(finish, 0);
431 shouldBe('mutations.length', '3');
432 shouldBe('mutations[0].type', '"attributes"');
433 shouldBe('mutations[0].attributeName', '"id"');
434 shouldBe('mutations[0].oldValue', 'null');
435 shouldBe('mutations[1].type', '"attributes"');
436 shouldBe('mutations[1].attributeName', '"id"');
437 shouldBe('mutations[1].oldValue', '"foo"');
438 shouldBe('mutations[2].type', '"attributes"');
439 shouldBe('mutations[2].attributeName', '"id"');
440 shouldBe('mutations[2].oldValue', '"bar"');
441 observer.disconnect();
449 function testAttributeFilter() {
454 debug('Testing that attributeFilter works as expected and observes case with HTML elements.');
457 observer = new WebKitMutationObserver(function(m) {
461 div = document.createElement('div');
462 observer.observe(div, { attributes: true, attributeFilter: ['foo', 'bar', 'booM'] });
463 div.setAttribute('foo', 'foo');
464 div.setAttribute('bar', 'bar');
465 div.setAttribute('baz', 'baz');
466 div.setAttribute('BOOm', 'boom');
468 setTimeout(finish, 0);
472 debug('...only foo and bar should be received.');
474 shouldBe('mutations.length', '2');
475 shouldBe('mutations[0].type', '"attributes"');
476 shouldBe('mutations[0].attributeName', '"foo"');
477 shouldBe('mutations[0].attributeNamespace', 'null');
478 shouldBe('mutations[1].type', '"attributes"');
479 shouldBe('mutations[1].attributeName', '"bar"');
480 shouldBe('mutations[1].attributeNamespace', 'null');
481 observer.disconnect();
489 function testAttributeFilterSubtree() {
494 debug('Testing the behavior of attributeFilter when the same observer observes at multiple nodes in a subtree with different filter options.');
497 observer = new WebKitMutationObserver(function(m) {
501 div = document.createElement('div');
502 div2 = div.appendChild(document.createElement('div'));
503 div3 = div2.appendChild(document.createElement('div'));
505 observer.observe(div, { attributes: true, subtree: true, attributeFilter: ['foo', 'bar'] });
506 observer.observe(div2, { attributes: true, subtree: true, attributeFilter: ['bar', 'bat'] });
508 div3.setAttribute('foo', 'foo');
509 div3.setAttribute('bar', 'bar');
510 div3.setAttribute('bat', 'bat');
511 div3.setAttribute('baz', 'baz');
513 setTimeout(checkAndObserveAll, 0);
516 function checkAndObserveAll() {
517 debug('...only foo, bar & bat should be received.');
519 shouldBe('mutations.length', '3');
520 shouldBe('mutations[0].type', '"attributes"');
521 shouldBe('mutations[0].attributeName', '"foo"');
522 shouldBe('mutations[0].attributeNamespace', 'null');
523 shouldBe('mutations[1].type', '"attributes"');
524 shouldBe('mutations[1].attributeName', '"bar"');
525 shouldBe('mutations[1].attributeNamespace', 'null');
526 shouldBe('mutations[2].type', '"attributes"');
527 shouldBe('mutations[2].attributeName', '"bat"');
528 shouldBe('mutations[2].attributeNamespace', 'null');
530 observer.observe(div2, { attributes: true, subtree: true });
531 div3.setAttribute('bar', 'bar');
532 div3.setAttribute('bat', 'bat');
533 div3.setAttribute('baz', 'baz');
535 setTimeout(finish, 0);
539 debug('...bar, bat & baz should all be received.');
541 shouldBe('mutations.length', '3');
542 shouldBe('mutations[0].type', '"attributes"');
543 shouldBe('mutations[0].attributeName', '"bar"');
544 shouldBe('mutations[0].attributeNamespace', 'null');
545 shouldBe('mutations[1].type', '"attributes"');
546 shouldBe('mutations[1].attributeName', '"bat"');
547 shouldBe('mutations[1].attributeNamespace', 'null');
548 shouldBe('mutations[2].type', '"attributes"');
549 shouldBe('mutations[2].attributeName', '"baz"');
550 shouldBe('mutations[2].attributeNamespace', 'null');
552 observer.disconnect();
560 function testAttributeFilterNonHTMLElement() {
565 debug('Testing that setting an attributeFilter filters out namespaced attributes.');
568 observer = new WebKitMutationObserver(function(m) {
572 path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
573 observer.observe(path, { attributes: true, attributeFilter: ['pathLength'] });
574 path.setAttributeNS('http://www.w3.org/2000/svg', 'pathLength', '200');
576 setTimeout(finish, 0);
580 debug('...pathLength should not be received.');
582 shouldBeNull('mutations');
583 observer.disconnect();
591 function testAttributeFilterNonHTMLDocument() {
592 var svgDoc, div, path;
596 debug('Testing that attributeFilter respects case with non-HTML elements.');
598 svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', 'svg');
600 observer = new WebKitMutationObserver(function(m) {
604 div = svgDoc.createElement('div');
605 observer.observe(div, { attributes: true, attributeFilter: ['ID', 'id', 'booM'] });
606 div.setAttribute('ID', 'ID');
607 div.setAttribute('id', 'id');
608 div.setAttribute('baz', 'baz');
609 div.setAttribute('booM', 'boom');
610 div.setAttribute('BOOm', 'boom');
612 setTimeout(finish, 0);
616 debug('...only ID, id, booM should be received.');
618 shouldBe('mutations.length', '3');
619 shouldBe('mutations[0].type', '"attributes"');
620 shouldBe('mutations[0].attributeName', '"ID"');
621 shouldBe('mutations[0].attributeNamespace', 'null');
622 shouldBe('mutations[1].type', '"attributes"');
623 shouldBe('mutations[1].attributeName', '"id"');
624 shouldBe('mutations[1].attributeNamespace', 'null');
625 shouldBe('mutations[2].type', '"attributes"');
626 shouldBe('mutations[2].attributeName', '"booM"');
627 shouldBe('mutations[2].attributeNamespace', 'null');
629 observer.disconnect();
637 function testStyleAttributePropertyAccess() {
642 debug('Testing that modifying an elements style property dispatches Mutation Records.');
645 observer = new WebKitMutationObserver(function(m) {
649 div = document.createElement('div');
650 div.setAttribute('style', 'color: yellow; width: 100px; ');
651 observer.observe(div, { attributes: true });
652 div.style.color = 'red';
653 div.style.width = '200px';
654 div.style.color = 'blue';
656 setTimeout(checkAndContinue, 0);
659 function checkAndContinue() {
660 shouldBe('mutations.length', '3');
661 shouldBe('mutations[0].type', '"attributes"');
662 shouldBe('mutations[0].attributeName', '"style"');
663 shouldBe('mutations[0].oldValue', 'null');
664 shouldBe('mutations[1].type', '"attributes"');
665 shouldBe('mutations[1].attributeName', '"style"');
666 shouldBe('mutations[1].oldValue', 'null');
667 shouldBe('mutations[2].type', '"attributes"');
668 shouldBe('mutations[2].attributeName', '"style"');
669 shouldBe('mutations[2].oldValue', 'null');
672 div.getAttribute('style');
673 setTimeout(finish, 0);
677 debug('...mutation record created.');
679 shouldBe('mutations', 'null');
681 observer.disconnect();
689 function testStyleAttributePropertyAccessOldValue() {
694 debug('Testing that modifying an elements style property dispatches Mutation Records with correct oldValues.');
697 observer = new WebKitMutationObserver(function(m) {
701 div = document.createElement('div');
702 div.setAttribute('style', 'color: yellow; width: 100px; ');
703 observer.observe(div, { attributes: true, attributeOldValue: true });
704 div.style.color = 'red';
705 div.style.width = '200px';
706 div.style.color = 'blue';
708 setTimeout(checkAndContinue, 0);
711 function checkAndContinue() {
712 shouldBe('mutations.length', '3');
713 shouldBe('mutations[0].type', '"attributes"');
714 shouldBe('mutations[0].attributeName', '"style"');
715 shouldBe('mutations[0].oldValue', '"color: yellow; width: 100px; "');
716 shouldBe('mutations[1].type', '"attributes"');
717 shouldBe('mutations[1].attributeName', '"style"');
718 shouldBe('mutations[1].oldValue', '"color: red; width: 100px; "');
719 shouldBe('mutations[2].type', '"attributes"');
720 shouldBe('mutations[2].attributeName', '"style"');
721 shouldBe('mutations[2].oldValue', '"color: red; width: 200px; "');
724 div.getAttribute('style');
725 setTimeout(finish, 0);
729 debug('...mutation record created.');
731 shouldBe('mutations', 'null');
733 observer.disconnect();
741 function testStyleAttributePropertyAccessIgnoreNoop() {
746 debug('Testing that a no-op style property mutation does not create Mutation Records.');
749 observer = new WebKitMutationObserver(function(m) {
753 div = document.createElement('div');
754 div.setAttribute('style', 'color: yellow; width: 100px; ');
755 observer.observe(div, { attributes: true });
756 div.style.removeProperty('height');
758 setTimeout(finish, 0);
762 shouldBe('mutations', 'null');
764 observer.disconnect();
772 function testMutateThroughAttrNodeValue() {
776 debug('Test that mutating an attribute through an attr node delivers mutation records');
779 observer = new WebKitMutationObserver(function(mutations) {
780 window.mutations = mutations;
783 div = document.createElement('div');
784 div.setAttribute('data-test', 'foo');
785 observer.observe(div, { attributes: true, attributeOldValue: true });
786 div.attributes['data-test'].value = 'bar';
788 setTimeout(finish, 0);
792 shouldBe('mutations.length', '1');
793 shouldBe('mutations[0].target', 'div');
794 shouldBe('mutations[0].type', '"attributes"');
795 shouldBe('mutations[0].attributeName', '"data-test"');
796 shouldBe('mutations[0].oldValue', '"foo"');
798 observer.disconnect();
806 function testMutateThroughAttrNodeChild() {
810 debug('Test that mutating an attribute by attaching a child to an attr node delivers mutation records');
813 observer = new WebKitMutationObserver(function(mutations) {
814 window.mutations = mutations;
817 div = document.createElement('div');
818 div.setAttribute('data-test', 'foo');
819 observer.observe(div, { attributes: true, attributeOldValue: true });
820 div.attributes['data-test'].appendChild(document.createTextNode('bar'));
822 setTimeout(finish, 0);
826 shouldBe('mutations.length', '1');
827 shouldBe('mutations[0].target', 'div');
828 shouldBe('mutations[0].type', '"attributes"');
829 shouldBe('mutations[0].attributeName', '"data-test"');
830 shouldBe('mutations[0].oldValue', '"foo"');
832 observer.disconnect();
840 function testSetAndRemoveAttributeNode() {
844 debug('Test that mutating via setAttributeNode delivers mutation records');
847 observer = new WebKitMutationObserver(function(mutations) {
848 window.mutations = mutations;
851 div = document.createElement('div');
853 div.setAttribute('data-test', 'foo');
854 observer.observe(div, { attributes: true, attributeOldValue: true });
855 var attr = document.createAttribute('data-test');
857 div.setAttributeNode(attr);
858 attr = document.createAttribute('data-other');
860 div.setAttributeNode(attr);
861 div.removeAttributeNode(div.attributes['id']);
863 setTimeout(finish, 0);
867 shouldBe('mutations.length', '3');
868 shouldBe('mutations[0].target', 'div');
869 shouldBe('mutations[0].type', '"attributes"');
870 shouldBe('mutations[0].attributeName', '"data-test"');
871 shouldBe('mutations[0].oldValue', '"foo"');
872 shouldBe('mutations[1].target', 'div');
873 shouldBe('mutations[1].type', '"attributes"');
874 shouldBe('mutations[1].attributeName', '"data-other"');
875 shouldBe('mutations[1].oldValue', 'null');
876 shouldBe('mutations[2].target', 'div');
877 shouldBe('mutations[2].type', '"attributes"');
878 shouldBe('mutations[2].attributeName', '"id"');
879 shouldBe('mutations[2].oldValue', '"myId"');
881 observer.disconnect();
889 function testMixedNodeAndElementOperations() {
893 debug('Test that setAttribute on an attribute with an existing Attr delivers mutation records');
896 observer = new WebKitMutationObserver(function(mutations) {
897 window.mutations = mutations;
900 div = document.createElement('div');
901 var attr = document.createAttribute('data-test');
903 div.setAttributeNode(attr);
904 observer.observe(div, { attributes: true, attributeOldValue: true });
905 div.setAttribute('data-test', 'bar');
907 setTimeout(finish, 0);
911 shouldBe('mutations.length', '1');
912 shouldBe('mutations[0].target', 'div');
913 shouldBe('mutations[0].type', '"attributes"');
914 shouldBe('mutations[0].attributeName', '"data-test"');
915 shouldBe('mutations[0].oldValue', '"foo"');
917 observer.disconnect();
925 function testNamedNodeMapOperations() {
929 debug('Test that setNamedItem and removeNamedItem deliver mutation records');
932 observer = new WebKitMutationObserver(function(mutations) {
933 window.mutations = mutations;
936 div = document.createElement('div');
937 div.setAttribute('data-test', 'foo');
938 observer.observe(div, { attributes: true, attributeOldValue: true });
939 var attr = document.createAttribute('data-test');
941 div.attributes.setNamedItem(attr);
942 div.attributes.removeNamedItem('data-test');
944 setTimeout(finish, 0);
948 shouldBe('mutations.length', '2');
949 shouldBe('mutations[0].target', 'div');
950 shouldBe('mutations[0].type', '"attributes"');
951 shouldBe('mutations[0].attributeName', '"data-test"');
952 shouldBe('mutations[0].oldValue', '"foo"');
953 shouldBe('mutations[1].target', 'div');
954 shouldBe('mutations[1].type', '"attributes"');
955 shouldBe('mutations[1].attributeName', '"data-test"');
956 shouldBe('mutations[1].oldValue', '"bar"');
958 observer.disconnect();
969 testMultipleRegistration,
970 testMultipleObservers,
973 testOrderingWrtDOMSubtreeModified,
975 testOldValueAsRequested,
976 testOldValueUnionMultipleObservations,
979 testAttributeFilterSubtree,
980 testAttributeFilterNonHTMLElement,
981 testAttributeFilterNonHTMLDocument,
982 testStyleAttributePropertyAccess,
983 testStyleAttributePropertyAccessOldValue,
984 testStyleAttributePropertyAccessIgnoreNoop,
985 testMutateThroughAttrNodeValue,
986 testMutateThroughAttrNodeChild,
987 testSetAndRemoveAttributeNode,
988 testMixedNodeAndElementOperations,
989 testNamedNodeMapOperations
993 function runNextTest() {
994 if (testIndex < tests.length)
995 tests[testIndex++]();
1000 description('Test WebKitMutationObserver.observe on attributes');
1002 if (!window.WebKitMutationObserver)
1003 testFailed('This test requires ENABLE(MUTATION_OBSERVERS)');
1008 <script src="../js/resources/js-test-post.js"></script>