[WK2] selection does not disappear after coping the text
[framework/web/webkit-efl.git] / LayoutTests / fast / mutation / observe-attributes.html
1 <!DOCTYPE html>
2 <script src="../js/resources/js-test-pre.js"></script>
3 <script>
4
5 window.jsTestIsAsync = true;
6 var mutations, mutations2, mutationsWithOldValue;
7 var calls;
8 var div;
9
10 function testBasic() {
11     var div;
12     var observer;
13
14     function start() {
15         debug('Testing basic aspects of attribute observation.');
16
17         mutations = null;
18         div = document.createElement('div');
19         div.setAttribute('bar', 'foo');
20
21         observer = new WebKitMutationObserver(function(m) {
22             mutations = m;
23         });
24
25         observer.observe(div, { attributes: true, characterData: true });
26         div.setAttribute('foo', 'bar');
27         div.removeAttribute('bar');
28         setTimeout(checkDisconnectAndMutate, 0);
29     }
30
31     function checkDisconnectAndMutate() {
32         debug('...can attribute changes be observed at all');
33
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');
41
42         mutations = null;
43         observer.disconnect();
44         div.setAttribute('foo', 'baz');
45         setTimeout(checkNotDeliveredAndMutateMultiple, 0);
46     }
47
48     function checkNotDeliveredAndMutateMultiple() {
49         debug('...observer.disconnect() should prevent further delivery of mutations.');
50
51         shouldBe('mutations', 'null');
52         observer.observe(div, { attributes: true });
53         div.setAttribute('foo', 'bat');
54         div.setAttribute('bar', 'foo');
55         setTimeout(finish);
56     }
57
58     function finish() {
59         debug('...re-observing after disconnect works with the same observer.');
60
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();
69         debug('');
70         runNextTest();
71     }
72
73     start();
74 }
75
76 function testWrongType() {
77     var div;
78     var observer;
79
80     function start() {
81         debug('Testing that observing without specifying "attributes" does not result in hearing about attribute changes.');
82
83         mutations = null;
84         div = document.createElement('div');
85         observer = new WebKitMutationObserver(function(m) {
86             mutations = m;
87         });
88
89         observer.observe(div, { childList: true, characterData: true });
90         div.setAttribute('foo', 'bar');
91         setTimeout(finish, 0);
92     }
93
94     function finish() {
95         shouldBe('mutations', 'null');
96         observer.disconnect();
97         debug('');
98         runNextTest();
99     }
100
101     start();
102 }
103
104 function testMultipleRegistration() {
105     var div;
106     var observer;
107
108     function start() {
109         debug('Testing that re-observing the same node with the same observer has the effect of resetting the options.');
110
111                 calls = 0;
112         mutations = null;
113         div = document.createElement('div');
114         observer = new WebKitMutationObserver(function(m) {
115             mutations = m;
116                         calls++;
117         });
118
119         observer.observe(div, { attributes: true, characterData: true });
120         observer.observe(div, { attributes: true });
121         div.setAttribute('foo', 'bar');
122         setTimeout(checkDisconnectAndMutate, 0);
123     }
124
125     function checkDisconnectAndMutate() {
126         shouldBe('calls', '1');
127         shouldBe('mutations.length', '1');
128         shouldBe('mutations[0].type', '"attributes"');
129         shouldBe('mutations[0].attributeName', '"foo"');
130         mutations = null;
131         observer.observe(div, { attributes: true, characterData: true });
132         observer.observe(div, { childList: true });
133         div.setAttribute('foo', 'baz');
134         setTimeout(finish, 0);
135     }
136
137     function finish() {
138         shouldBe('mutations', 'null');
139         observer.disconnect();
140         debug('');
141         runNextTest();
142     }
143
144     start();
145 }
146
147 function testMultipleObservers() {
148     var div;
149     var observer;
150     var observer2;
151
152     function start() {
153         debug('Testing that multiple observers can be registered to a given node and both receive mutations.');
154         mutations = null;
155         div = document.createElement('div');
156         observer = new WebKitMutationObserver(function(m) {
157             mutations = m;
158         });
159         observer2 = new WebKitMutationObserver(function(m) {
160             mutations2 = m;
161         });
162         observer.observe(div, { attributes: true });
163         observer2.observe(div, { attributes: true });
164         div.setAttribute('foo', 'bar');
165         setTimeout(finish, 0);
166     }
167
168     function finish() {
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();
177         debug('');
178         runNextTest();
179     }
180
181     start();
182 }
183
184 function testNamespaceURI() {
185     var div;
186     var observer;
187
188     function start() {
189         debug('Testing that "attributeNamespace" value is delivered properly.');
190         mutations = null;
191         div = document.createElement('div');
192         observer = new WebKitMutationObserver(function(m) {
193             mutations = m;
194         });
195
196         observer.observe(div, { attributes: true, childList: true });
197         div.setAttributeNS('http://www.foo.com/bar', 'foo', 'bar');
198         setTimeout(finish, 0);    
199     }
200
201     function finish() {
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();
207         debug('');
208         runNextTest();
209     }
210
211     start();
212 }
213
214 function testPropertyAccess() {
215     var img, a;
216     var observer;
217
218     function start() {
219         debug('Testing that modifications to node properties which delegate to attribute storage deliver mutations.');
220         mutations = null;
221         img = document.createElement('img');
222         a = document.createElement('a');
223
224         observer = new WebKitMutationObserver(function(m) {
225             mutations = m;
226         });
227
228         observer.observe(img, { attributes: true });
229         observer.observe(a, { attributes: true });
230
231         img.src = 'baz.png';
232         a.href = 'foo.html';
233
234         setTimeout(finish, 0);
235     }
236
237     function finish() {
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();
244         debug('');
245         runNextTest();
246     }
247
248     start();
249 }
250
251 function testOrderingWrtDOMSubtreeModified() {
252     var div, div2, subDiv;
253     var observer;
254     var listener;
255
256     function start() {
257         debug('Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched.');
258
259         mutations = null;
260         div = document.body.appendChild(document.createElement('div'));
261         div2 = document.body.appendChild(document.createElement('div'));
262
263         subDiv = div.appendChild(document.createElement('div'));
264
265         observer = new WebKitMutationObserver(function(m) {
266             mutations = m;
267         });
268
269         listener = function(e) {
270             div2.setAttribute('baz', 'bat');
271         }
272
273         div.addEventListener('DOMSubtreeModified', listener);
274         observer.observe(subDiv, { attributes: true });
275         observer.observe(div2, { attributes: true });
276
277         subDiv.setAttribute('foo', 'bar');
278
279         setTimeout(finish, 0);
280     }
281
282     function finish() {
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();
291         debug('');
292         runNextTest();
293     }
294
295     start();
296 }
297
298 function testOldValue() {
299     var div;
300     var observer;
301
302     function start() {
303         debug('Testing basic oldValue delivery.');
304         mutations = null;
305         div = document.createElement('div');
306         div.setAttribute('bar', 'boo');
307         
308         observer = new WebKitMutationObserver(function(mutations) {
309             window.mutations = mutations;
310         });
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);
317     }
318
319     function finish() {
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();
331         debug('');
332         runNextTest();
333     }
334
335     start();
336 }
337
338 function testOldValueAsRequested() {
339     var div;
340     var observerWithOldValue;
341     var observer;
342
343     function start() {
344         debug('Testing that oldValue is delivered as requested (or not).');
345         mutationsWithOldValue = null;
346         mutations = null;
347         div = document.createElement('div');
348         div.setAttribute('foo', 'bar');
349         observerWithOldValue = new WebKitMutationObserver(function(mutations) {
350             window.mutationsWithOldValue = mutations;
351         });
352         observer = new WebKitMutationObserver(function(mutations) {
353             window.mutations = mutations;
354         });
355         observerWithOldValue.observe(div, { attributes: true, attributeOldValue: true });
356         observer.observe(div, { attributes: true });
357         div.setAttribute('foo', 'baz');
358         setTimeout(finish, 0);
359     }
360
361     function finish() {
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();
372         debug('');
373         runNextTest();
374     }
375
376     start();
377 }
378
379 function testOldValueUnionMultipleObservations() {
380     var div;
381     var span;
382     var observer;
383
384     function start() {
385         debug('An observer with multiple observations will get attributeOldValue if any entries request it.');
386         mutations = null;
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;
392         });
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);
397     }
398
399     function finish() {
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();
405         debug('');
406         runNextTest();
407     }
408
409     start();
410 }
411
412 function testIDLAttribute() {
413     var div;
414     var observer;
415
416     function start() {
417         debug('Testing setting an attribute via reflected IDL attribute.');
418         mutations = null;
419         div = document.createElement('div');
420         observer = new WebKitMutationObserver(function(mutations) {
421             window.mutations = mutations;
422         });
423         observer.observe(div, { attributes: true, attributeOldValue: true });
424         div.id = 'foo';
425         div.id = 'bar';
426         div.id = null;
427         setTimeout(finish, 0);
428     }
429
430     function finish() {
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();
442         debug('');
443         runNextTest();
444     }
445
446     start();
447 }
448
449 function testAttributeFilter() {
450     var div, path;
451     var observer;
452
453     function start() {
454         debug('Testing that attributeFilter works as expected and observes case with HTML elements.');
455
456         mutations = null;
457         observer = new WebKitMutationObserver(function(m) {
458             mutations = m;
459         });
460
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');
467
468         setTimeout(finish, 0);
469     }
470
471     function finish() {
472         debug('...only foo and bar should be received.');
473
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();
482         debug('');
483         runNextTest();
484     }
485
486     start();
487 }
488
489 function testAttributeFilterSubtree() {
490     var div, div2, div3;
491     var observer;
492
493     function start() {
494         debug('Testing the behavior of attributeFilter when the same observer observes at multiple nodes in a subtree with different filter options.');
495
496         mutations = null;
497         observer = new WebKitMutationObserver(function(m) {
498             mutations = m;
499         });
500
501         div = document.createElement('div');
502         div2 = div.appendChild(document.createElement('div'));
503         div3 = div2.appendChild(document.createElement('div'));
504
505         observer.observe(div, { attributes: true, subtree: true, attributeFilter: ['foo', 'bar'] });
506         observer.observe(div2, { attributes: true, subtree: true, attributeFilter: ['bar', 'bat'] });
507
508         div3.setAttribute('foo', 'foo');
509         div3.setAttribute('bar', 'bar');
510         div3.setAttribute('bat', 'bat');
511         div3.setAttribute('baz', 'baz');
512
513         setTimeout(checkAndObserveAll, 0);
514     }
515
516     function checkAndObserveAll() {
517         debug('...only foo, bar & bat should be received.');
518
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');
529
530         observer.observe(div2, { attributes: true, subtree: true });
531         div3.setAttribute('bar', 'bar');
532         div3.setAttribute('bat', 'bat');
533         div3.setAttribute('baz', 'baz');
534
535         setTimeout(finish, 0);
536     }
537
538     function finish() {
539         debug('...bar, bat & baz should all be received.');
540
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');
551
552         observer.disconnect();
553         debug('');
554         runNextTest();
555     }
556
557     start();
558 }
559
560 function testAttributeFilterNonHTMLElement() {
561     var path;
562     var observer;
563
564     function start() {
565         debug('Testing that setting an attributeFilter filters out namespaced attributes.');
566
567         mutations = null;
568         observer = new WebKitMutationObserver(function(m) {
569             mutations = m;
570         });
571
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');
575
576         setTimeout(finish, 0);
577     }
578
579     function finish() {
580         debug('...pathLength should not be received.');
581
582         shouldBeNull('mutations');
583         observer.disconnect();
584         debug('');
585         runNextTest();
586     }
587
588     start();
589 }
590
591 function testAttributeFilterNonHTMLDocument() {
592     var svgDoc, div, path;
593     var observer;
594
595     function start() {
596         debug('Testing that attributeFilter respects case with non-HTML elements.');
597
598         svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', 'svg');
599         mutations = null;
600         observer = new WebKitMutationObserver(function(m) {
601             mutations = m;
602         });
603
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');
611
612         setTimeout(finish, 0);
613     }
614
615     function finish() {
616         debug('...only ID, id, booM should be received.');
617
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');
628
629         observer.disconnect();
630         debug('');
631         runNextTest();
632     }
633
634     start();
635 }
636
637 function testStyleAttributePropertyAccess() {
638     var div, path;
639     var observer;
640
641     function start() {
642         debug('Testing that modifying an elements style property dispatches Mutation Records.');
643
644         mutations = null;
645         observer = new WebKitMutationObserver(function(m) {
646             mutations = m;
647         });
648
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';
655
656         setTimeout(checkAndContinue, 0);
657     }
658
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');
670
671         mutations = null;
672         div.getAttribute('style');
673         setTimeout(finish, 0);
674     }
675
676     function finish() {
677         debug('...mutation record created.');
678
679         shouldBe('mutations', 'null');
680
681         observer.disconnect();
682         debug('');
683         runNextTest();
684     }
685
686     start();
687 }
688
689 function testStyleAttributePropertyAccessOldValue() {
690     var div, path;
691     var observer;
692
693     function start() {
694         debug('Testing that modifying an elements style property dispatches Mutation Records with correct oldValues.');
695
696         mutations = null;
697         observer = new WebKitMutationObserver(function(m) {
698             mutations = m;
699         });
700
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';
707
708         setTimeout(checkAndContinue, 0);
709     }
710
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; "');
722
723         mutations = null;
724         div.getAttribute('style');
725         setTimeout(finish, 0);
726     }
727
728     function finish() {
729         debug('...mutation record created.');
730
731         shouldBe('mutations', 'null');
732
733         observer.disconnect();
734         debug('');
735         runNextTest();
736     }
737
738     start();
739 }
740
741 function testStyleAttributePropertyAccessIgnoreNoop() {
742     var div, path;
743     var observer;
744
745     function start() {
746         debug('Testing that a no-op style property mutation does not create Mutation Records.');
747
748         mutations = null;
749         observer = new WebKitMutationObserver(function(m) {
750             mutations = m;
751         });
752
753         div = document.createElement('div');
754         div.setAttribute('style', 'color: yellow; width: 100px; ');
755         observer.observe(div, { attributes: true });
756         div.style.removeProperty('height');
757
758         setTimeout(finish, 0);
759     }
760
761     function finish() {
762         shouldBe('mutations', 'null');
763
764         observer.disconnect();
765         debug('');
766         runNextTest();
767     }
768
769     start();
770 }
771
772 function testMutateThroughAttrNodeValue() {
773     var observer;
774
775     function start() {
776         debug('Test that mutating an attribute through an attr node delivers mutation records');
777
778         mutations = null;
779         observer = new WebKitMutationObserver(function(mutations) {
780             window.mutations = mutations;
781         });
782
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';
787
788         setTimeout(finish, 0);
789     }
790
791     function finish() {
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"');
797
798         observer.disconnect();
799         debug('');
800         runNextTest();
801     }
802
803     start();
804 }
805
806 function testMutateThroughAttrNodeChild() {
807     var observer;
808
809     function start() {
810         debug('Test that mutating an attribute by attaching a child to an attr node delivers mutation records');
811
812         mutations = null;
813         observer = new WebKitMutationObserver(function(mutations) {
814             window.mutations = mutations;
815         });
816
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'));
821
822         setTimeout(finish, 0);
823     }
824
825     function finish() {
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"');
831
832         observer.disconnect();
833         debug('');
834         runNextTest();
835     }
836
837     start();
838 }
839
840 function testSetAndRemoveAttributeNode() {
841     var observer;
842
843     function start() {
844         debug('Test that mutating via setAttributeNode delivers mutation records');
845
846         mutations = null;
847         observer = new WebKitMutationObserver(function(mutations) {
848             window.mutations = mutations;
849         });
850
851         div = document.createElement('div');
852         div.id = 'myId';
853         div.setAttribute('data-test', 'foo');
854         observer.observe(div, { attributes: true, attributeOldValue: true });
855         var attr = document.createAttribute('data-test');
856         attr.value = 'bar';
857         div.setAttributeNode(attr);
858         attr = document.createAttribute('data-other');
859         attr.value = 'baz';
860         div.setAttributeNode(attr);
861         div.removeAttributeNode(div.attributes['id']);
862
863         setTimeout(finish, 0);
864     }
865
866     function finish() {
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"');
880
881         observer.disconnect();
882         debug('');
883         runNextTest();
884     }
885
886     start();
887 }
888
889 function testMixedNodeAndElementOperations() {
890     var observer;
891
892     function start() {
893         debug('Test that setAttribute on an attribute with an existing Attr delivers mutation records');
894
895         mutations = null;
896         observer = new WebKitMutationObserver(function(mutations) {
897             window.mutations = mutations;
898         });
899
900         div = document.createElement('div');
901         var attr = document.createAttribute('data-test');
902         attr.value = 'foo';
903         div.setAttributeNode(attr);
904         observer.observe(div, { attributes: true, attributeOldValue: true });
905         div.setAttribute('data-test', 'bar');
906
907         setTimeout(finish, 0);
908     }
909
910     function finish() {
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"');
916
917         observer.disconnect();
918         debug('');
919         runNextTest();
920     }
921
922     start();
923 }
924
925 function testNamedNodeMapOperations() {
926     var observer;
927
928     function start() {
929         debug('Test that setNamedItem and removeNamedItem deliver mutation records');
930
931         mutations = null;
932         observer = new WebKitMutationObserver(function(mutations) {
933             window.mutations = mutations;
934         });
935
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');
940         attr.value = 'bar';
941         div.attributes.setNamedItem(attr);
942         div.attributes.removeNamedItem('data-test');
943
944         setTimeout(finish, 0);
945     }
946
947     function finish() {
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"');
957
958         observer.disconnect();
959         debug('');
960         runNextTest();
961     }
962
963     start();
964 }
965
966 var tests = [
967     testBasic,
968     testWrongType,
969     testMultipleRegistration,
970     testMultipleObservers,
971     testNamespaceURI,
972     testPropertyAccess,
973     testOrderingWrtDOMSubtreeModified,
974     testOldValue,
975     testOldValueAsRequested,
976     testOldValueUnionMultipleObservations,
977     testIDLAttribute,
978     testAttributeFilter,
979     testAttributeFilterSubtree,
980     testAttributeFilterNonHTMLElement,
981     testAttributeFilterNonHTMLDocument,
982     testStyleAttributePropertyAccess,
983     testStyleAttributePropertyAccessOldValue,
984     testStyleAttributePropertyAccessIgnoreNoop,
985     testMutateThroughAttrNodeValue,
986     testMutateThroughAttrNodeChild,
987     testSetAndRemoveAttributeNode,
988     testMixedNodeAndElementOperations,
989     testNamedNodeMapOperations
990 ];
991 var testIndex = 0;
992
993 function runNextTest() {
994     if (testIndex < tests.length)
995         tests[testIndex++]();
996     else
997         finishJSTest();
998 }
999
1000 description('Test WebKitMutationObserver.observe on attributes');
1001
1002 if (!window.WebKitMutationObserver)
1003     testFailed('This test requires ENABLE(MUTATION_OBSERVERS)');
1004 else
1005     runNextTest();
1006
1007 </script>
1008 <script src="../js/resources/js-test-post.js"></script>