blob: 167d600a3bd0fa2af7b77f4ae750a3c76b954782 [file] [log] [blame]
Ryosuke Niwa3801ab52016-09-21 14:33:291<!DOCTYPE html>
2<html>
3<head>
4<title>Shadow DOM: slotchange event</title>
5<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
Hayato Itobbdd6aa2016-12-07 09:28:496<meta name="author" title="Hayato Ito" href="mailto:hayato@google.com">
Ryosuke Niwa3801ab52016-09-21 14:33:297<link rel="help" href="https://dom.spec.whatwg.org/#signaling-slot-change">
8<script src="/resources/testharness.js"></script>
9<script src="/resources/testharnessreport.js"></script>
10</head>
11<body>
12<div id="log"></div>
13<script>
Ryosuke Niwa3801ab52016-09-21 14:33:2914function treeName(mode, connectedToDocument)
15{
16 return (mode == 'open' ? 'an ' : 'a ') + mode + ' shadow root '
17 + (connectedToDocument ? '' : ' not') + ' in a document';
18}
19
20function testAppendingSpanToShadowRootWithDefaultSlot(mode, connectedToDocument)
21{
22 var test = async_test('slotchange event must fire on a default slot element inside '
23 + treeName(mode, connectedToDocument));
24
25 var host;
26 var slot;
27 var eventCount = 0;
28
29 test.step(function () {
30 host = document.createElement('div');
31 if (connectedToDocument)
32 document.body.appendChild(host);
33
34 var shadowRoot = host.attachShadow({'mode': mode});
35 slot = document.createElement('slot');
36
37 slot.addEventListener('slotchange', function (event) {
38 if (event.isFakeEvent)
39 return;
40
41 test.step(function () {
42 assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
43 assert_equals(event.target, slot, 'slotchange event\'s target must be the slot element');
44 assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget');
45 });
46 eventCount++;
47 });
48
49 shadowRoot.appendChild(slot);
50
51 host.appendChild(document.createElement('span'));
52 host.appendChild(document.createElement('b'));
53
54 assert_equals(eventCount, 0, 'slotchange event must not be fired synchronously');
55 });
56
57 setTimeout(function () {
58 test.step(function () {
59 assert_equals(eventCount, 1, 'slotchange must be fired exactly once after the assigned nodes changed');
60
61 host.appendChild(document.createElement('i'));
62 });
63
64 setTimeout(function () {
65 test.step(function () {
66 assert_equals(eventCount, 2, 'slotchange must be fired exactly once after the assigned nodes changed');
67
68 host.appendChild(document.createTextNode('hello'));
69
70 var fakeEvent = new Event('slotchange');
71 fakeEvent.isFakeEvent = true;
72 slot.dispatchEvent(fakeEvent);
73 });
74
75 setTimeout(function () {
76 test.step(function () {
77 assert_equals(eventCount, 3, 'slotchange must be fired exactly once after the assigned nodes changed'
78 + ' event if there was a synthetic slotchange event fired');
79 });
80 test.done();
81 }, 1);
82 }, 1);
83 }, 1);
84}
85
86testAppendingSpanToShadowRootWithDefaultSlot('open', true);
87testAppendingSpanToShadowRootWithDefaultSlot('closed', true);
88testAppendingSpanToShadowRootWithDefaultSlot('open', false);
89testAppendingSpanToShadowRootWithDefaultSlot('closed', false);
90
91function testAppendingSpanToShadowRootWithNamedSlot(mode, connectedToDocument)
92{
93 var test = async_test('slotchange event must fire on a named slot element inside'
94 + treeName(mode, connectedToDocument));
95
96 var host;
97 var slot;
98 var eventCount = 0;
99
100 test.step(function () {
101 host = document.createElement('div');
102 if (connectedToDocument)
103 document.body.appendChild(host);
104
105 var shadowRoot = host.attachShadow({'mode': mode});
106 slot = document.createElement('slot');
107 slot.name = 'someSlot';
108
109 slot.addEventListener('slotchange', function (event) {
110 if (event.isFakeEvent)
111 return;
112
113 test.step(function () {
114 assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
115 assert_equals(event.target, slot, 'slotchange event\'s target must be the slot element');
116 assert_equals(event.relatedTarget, undefined, 'slotchange must not set relatedTarget');
117 });
118 eventCount++;
119 });
120
121 shadowRoot.appendChild(slot);
122
123 var span = document.createElement('span');
124 span.slot = 'someSlot';
125 host.appendChild(span);
126
127 var b = document.createElement('b');
128 b.slot = 'someSlot';
129 host.appendChild(b);
130
131 assert_equals(eventCount, 0, 'slotchange event must not be fired synchronously');
132 });
133
134 setTimeout(function () {
135 test.step(function () {
136 assert_equals(eventCount, 1, 'slotchange must be fired exactly once after the assigned nodes changed');
137
138 var i = document.createElement('i');
139 i.slot = 'someSlot';
140 host.appendChild(i);
141 });
142
143 setTimeout(function () {
144 test.step(function () {
145 assert_equals(eventCount, 2, 'slotchange must be fired exactly once after the assigned nodes changed');
146
147 var em = document.createElement('em');
148 em.slot = 'someSlot';
149 host.appendChild(em);
150
151 var fakeEvent = new Event('slotchange');
152 fakeEvent.isFakeEvent = true;
153 slot.dispatchEvent(fakeEvent);
154 });
155
156 setTimeout(function () {
157 test.step(function () {
158 assert_equals(eventCount, 3, 'slotchange must be fired exactly once after the assigned nodes changed'
159 + ' event if there was a synthetic slotchange event fired');
160 });
161 test.done();
162 }, 1);
163
164 }, 1);
165 }, 1);
166}
167
168testAppendingSpanToShadowRootWithNamedSlot('open', true);
169testAppendingSpanToShadowRootWithNamedSlot('closed', true);
170testAppendingSpanToShadowRootWithNamedSlot('open', false);
171testAppendingSpanToShadowRootWithNamedSlot('closed', false);
172
173function testSlotchangeDoesNotFireWhenOtherSlotsChange(mode, connectedToDocument)
174{
175 var test = async_test('slotchange event must not fire on a slot element inside '
176 + treeName(mode, connectedToDocument)
177 + ' when another slot\'s assigned nodes change');
178
179 var host;
180 var defaultSlotEventCount = 0;
181 var namedSlotEventCount = 0;
182
183 test.step(function () {
184 host = document.createElement('div');
185 if (connectedToDocument)
186 document.body.appendChild(host);
187
188 var shadowRoot = host.attachShadow({'mode': mode});
189 var defaultSlot = document.createElement('slot');
190 defaultSlot.addEventListener('slotchange', function (event) {
191 test.step(function () {
192 assert_equals(event.target, defaultSlot, 'slotchange event\'s target must be the slot element');
193 });
194 defaultSlotEventCount++;
195 });
196
197 var namedSlot = document.createElement('slot');
198 namedSlot.name = 'slotName';
199 namedSlot.addEventListener('slotchange', function (event) {
200 test.step(function () {
201 assert_equals(event.target, namedSlot, 'slotchange event\'s target must be the slot element');
202 });
203 namedSlotEventCount++;
204 });
205
206 shadowRoot.appendChild(defaultSlot);
207 shadowRoot.appendChild(namedSlot);
208
209 host.appendChild(document.createElement('span'));
210
211 assert_equals(defaultSlotEventCount, 0, 'slotchange event must not be fired synchronously');
212 assert_equals(namedSlotEventCount, 0, 'slotchange event must not be fired synchronously');
213 });
214
215 setTimeout(function () {
216 test.step(function () {
217 assert_equals(defaultSlotEventCount, 1,
218 'slotchange must be fired exactly once after the assigned nodes change on a default slot');
219 assert_equals(namedSlotEventCount, 0,
220 'slotchange must not be fired on a named slot after the assigned nodes change on a default slot');
221
222 var span = document.createElement('span');
223 span.slot = 'slotName';
224 host.appendChild(span);
225 });
226
227 setTimeout(function () {
228 test.step(function () {
229 assert_equals(defaultSlotEventCount, 1,
230 'slotchange must not be fired on a default slot after the assigned nodes change on a named slot');
231 assert_equals(namedSlotEventCount, 1,
232 'slotchange must be fired exactly once after the assigned nodes change on a default slot');
233 });
234 test.done();
235 }, 1);
236 }, 1);
237}
238
239testSlotchangeDoesNotFireWhenOtherSlotsChange('open', true);
240testSlotchangeDoesNotFireWhenOtherSlotsChange('closed', true);
241testSlotchangeDoesNotFireWhenOtherSlotsChange('open', false);
242testSlotchangeDoesNotFireWhenOtherSlotsChange('closed', false);
243
Hayato Itoce333252017-06-09 03:31:55244function testSlotchangeDoesFireAtInsertedAndDoesNotFireForMutationAfterRemoved(mode, connectedToDocument)
Ryosuke Niwa3801ab52016-09-21 14:33:29245{
Hayato Itoce333252017-06-09 03:31:55246 var test = async_test('slotchange event must fire on a slot element when a shadow host has a slotable and the slot was inserted'
247 + ' and must not fire when the shadow host was mutated after the slot was removed inside '
248 + treeName(mode, connectedToDocument));
Ryosuke Niwa3801ab52016-09-21 14:33:29249
250 var host;
251 var slot;
252 var eventCount = 0;
253
254 test.step(function () {
255 host = document.createElement('div');
256 if (connectedToDocument)
257 document.body.appendChild(host);
258
259 var shadowRoot = host.attachShadow({'mode': mode});
260 slot = document.createElement('slot');
261 slot.addEventListener('slotchange', function (event) {
262 test.step(function () {
263 assert_equals(event.target, slot, 'slotchange event\'s target must be the slot element');
264 });
265 eventCount++;
266 });
267
268 host.appendChild(document.createElement('span'));
269 shadowRoot.appendChild(slot);
270
271 assert_equals(eventCount, 0, 'slotchange event must not be fired synchronously');
272 });
273
274 setTimeout(function () {
275 test.step(function () {
Hayato Itoce333252017-06-09 03:31:55276 assert_equals(eventCount, 1,
277 'slotchange must be fired on a slot element if there is assigned nodes when the slot was inserted');
Ryosuke Niwa3801ab52016-09-21 14:33:29278 host.removeChild(host.firstChild);
279 });
280
281 setTimeout(function () {
282 test.step(function () {
Hayato Itoce333252017-06-09 03:31:55283 assert_equals(eventCount, 2,
284 'slotchange must be fired after the assigned nodes change on a slot while the slot element was in the tree');
Ryosuke Niwa3801ab52016-09-21 14:33:29285 slot.parentNode.removeChild(slot);
286 host.appendChild(document.createElement('span'));
287 });
288
289 setTimeout(function () {
Hayato Itoce333252017-06-09 03:31:55290 assert_equals(eventCount, 2,
291 'slotchange must not be fired on a slot element if the assigned nodes changed after the slot was removed');
Ryosuke Niwa3801ab52016-09-21 14:33:29292 test.done();
293 }, 1);
294 }, 1);
295 }, 1);
296}
297
Hayato Itoce333252017-06-09 03:31:55298testSlotchangeDoesFireAtInsertedAndDoesNotFireForMutationAfterRemoved('open', true);
299testSlotchangeDoesFireAtInsertedAndDoesNotFireForMutationAfterRemoved('closed', true);
300testSlotchangeDoesFireAtInsertedAndDoesNotFireForMutationAfterRemoved('open', false);
301testSlotchangeDoesFireAtInsertedAndDoesNotFireForMutationAfterRemoved('closed', false);
Ryosuke Niwa3801ab52016-09-21 14:33:29302
303function testSlotchangeFiresOnTransientlyPresentSlot(mode, connectedToDocument)
304{
305 var test = async_test('slotchange event must fire on a slot element inside '
306 + treeName(mode, connectedToDocument)
307 + ' even if the slot was removed immediately after the assigned nodes were mutated');
308
309 var host;
310 var slot;
311 var anotherSlot;
312 var slotEventCount = 0;
313 var anotherSlotEventCount = 0;
314
315 test.step(function () {
316 host = document.createElement('div');
317 if (connectedToDocument)
318 document.body.appendChild(host);
319
320 var shadowRoot = host.attachShadow({'mode': mode});
321 slot = document.createElement('slot');
322 slot.name = 'someSlot';
323 slot.addEventListener('slotchange', function (event) {
324 test.step(function () {
325 assert_equals(event.target, slot, 'slotchange event\'s target must be the slot element');
326 });
327 slotEventCount++;
328 });
329
330 anotherSlot = document.createElement('slot');
331 anotherSlot.name = 'someSlot';
332 anotherSlot.addEventListener('slotchange', function (event) {
333 test.step(function () {
334 assert_equals(event.target, anotherSlot, 'slotchange event\'s target must be the slot element');
335 });
336 anotherSlotEventCount++;
337 });
338
339 shadowRoot.appendChild(slot);
340
341 var span = document.createElement('span');
342 span.slot = 'someSlot';
343 host.appendChild(span);
344
345 shadowRoot.removeChild(slot);
346 shadowRoot.appendChild(anotherSlot);
347
348 var span = document.createElement('span');
349 span.slot = 'someSlot';
350 host.appendChild(span);
351
352 shadowRoot.removeChild(anotherSlot);
353
354 assert_equals(slotEventCount, 0, 'slotchange event must not be fired synchronously');
355 assert_equals(anotherSlotEventCount, 0, 'slotchange event must not be fired synchronously');
356 });
357
358 setTimeout(function () {
359 test.step(function () {
360 assert_equals(slotEventCount, 1,
361 'slotchange must be fired on a slot element if the assigned nodes changed while the slot was present');
362 assert_equals(anotherSlotEventCount, 1,
363 'slotchange must be fired on a slot element if the assigned nodes changed while the slot was present');
364 });
365 test.done();
366 }, 1);
367}
368
369testSlotchangeFiresOnTransientlyPresentSlot('open', true);
370testSlotchangeFiresOnTransientlyPresentSlot('closed', true);
371testSlotchangeFiresOnTransientlyPresentSlot('open', false);
372testSlotchangeFiresOnTransientlyPresentSlot('closed', false);
373
374function testSlotchangeFiresOnInnerHTML(mode, connectedToDocument)
375{
376 var test = async_test('slotchange event must fire on a slot element inside '
377 + treeName(mode, connectedToDocument)
378 + ' when innerHTML modifies the children of the shadow host');
379
380 var host;
381 var defaultSlot;
382 var namedSlot;
383 var defaultSlotEventCount = 0;
384 var namedSlotEventCount = 0;
385
386 test.step(function () {
387 host = document.createElement('div');
388 if (connectedToDocument)
389 document.body.appendChild(host);
390
391 var shadowRoot = host.attachShadow({'mode': mode});
392 defaultSlot = document.createElement('slot');
393 defaultSlot.addEventListener('slotchange', function (event) {
394 test.step(function () {
395 assert_equals(event.target, defaultSlot, 'slotchange event\'s target must be the slot element');
396 });
397 defaultSlotEventCount++;
398 });
399
400 namedSlot = document.createElement('slot');
401 namedSlot.name = 'someSlot';
402 namedSlot.addEventListener('slotchange', function (event) {
403 test.step(function () {
404 assert_equals(event.target, namedSlot, 'slotchange event\'s target must be the slot element');
405 });
406 namedSlotEventCount++;
407 });
408 shadowRoot.appendChild(namedSlot);
409 shadowRoot.appendChild(defaultSlot);
410 host.innerHTML = 'foo <b>bar</b>';
411
412 assert_equals(defaultSlotEventCount, 0, 'slotchange event must not be fired synchronously');
413 assert_equals(namedSlotEventCount, 0, 'slotchange event must not be fired synchronously');
414 });
415
416 setTimeout(function () {
417 test.step(function () {
418 assert_equals(defaultSlotEventCount, 1,
419 'slotchange must be fired on a slot element if the assigned nodes are changed by innerHTML');
420 assert_equals(namedSlotEventCount, 0,
421 'slotchange must not be fired on a slot element if the assigned nodes are not changed by innerHTML');
422 host.innerHTML = 'baz';
423 });
424 setTimeout(function () {
425 test.step(function () {
426 assert_equals(defaultSlotEventCount, 2,
427 'slotchange must be fired on a slot element if the assigned nodes are changed by innerHTML');
428 assert_equals(namedSlotEventCount, 0,
429 'slotchange must not be fired on a slot element if the assigned nodes are not changed by innerHTML');
430 host.innerHTML = '';
431 });
432 setTimeout(function () {
433 test.step(function () {
434 assert_equals(defaultSlotEventCount, 3,
435 'slotchange must be fired on a slot element if the assigned nodes are changed by innerHTML');
436 assert_equals(namedSlotEventCount, 0,
437 'slotchange must not be fired on a slot element if the assigned nodes are not changed by innerHTML');
438 host.innerHTML = '<b slot="someSlot">content</b>';
439 });
440 setTimeout(function () {
441 test.step(function () {
442 assert_equals(defaultSlotEventCount, 3,
443 'slotchange must not be fired on a slot element if the assigned nodes are not changed by innerHTML');
444 assert_equals(namedSlotEventCount, 1,
445 'slotchange must not be fired on a slot element if the assigned nodes are changed by innerHTML');
446 host.innerHTML = '';
447 });
448 setTimeout(function () {
449 test.step(function () {
450 // FIXME: This test would fail in the current implementation because we can't tell
451 // whether a text node was removed in AllChildrenRemoved or not.
452// assert_equals(defaultSlotEventCount, 3,
453// 'slotchange must not be fired on a slot element if the assigned nodes are not changed by innerHTML');
454 assert_equals(namedSlotEventCount, 2,
455 'slotchange must not be fired on a slot element if the assigned nodes are changed by innerHTML');
456 });
457 test.done();
458 }, 1);
459 }, 1);
460 }, 1);
461 }, 1);
462 }, 1);
463}
464
465testSlotchangeFiresOnInnerHTML('open', true);
466testSlotchangeFiresOnInnerHTML('closed', true);
467testSlotchangeFiresOnInnerHTML('open', false);
468testSlotchangeFiresOnInnerHTML('closed', false);
469
470function testSlotchangeFiresWhenNestedSlotChange(mode, connectedToDocument)
471{
472 var test = async_test('slotchange event must fire on a slot element inside '
473 + treeName(mode, connectedToDocument)
474 + ' when nested slots\'s contents change');
475
476 var outerHost;
477 var innerHost;
478 var outerSlot;
479 var innerSlot;
480 var outerSlotEventCount = 0;
481 var innerSlotEventCount = 0;
482
483 test.step(function () {
484 outerHost = document.createElement('div');
485 if (connectedToDocument)
486 document.body.appendChild(outerHost);
487
488 var outerShadow = outerHost.attachShadow({'mode': mode});
489 outerShadow.appendChild(document.createElement('span'));
490 outerSlot = document.createElement('slot');
491 outerSlot.addEventListener('slotchange', function (event) {
Ryosuke Niwa3801ab52016-09-21 14:33:29492 test.step(function () {
493 assert_equals(event.target, outerSlot, 'slotchange event\'s target must be the slot element');
494 });
495 outerSlotEventCount++;
496 });
497
498 innerHost = document.createElement('div');
499 innerHost.appendChild(outerSlot);
500 outerShadow.appendChild(innerHost);
501
502 var innerShadow = innerHost.attachShadow({'mode': mode});
503 innerShadow.appendChild(document.createElement('span'));
504 innerSlot = document.createElement('slot');
505 innerSlot.addEventListener('slotchange', function (event) {
506 event.stopPropagation();
507 test.step(function () {
Hayato Itoce333252017-06-09 03:31:55508 if (innerSlotEventCount === 0) {
509 assert_equals(event.target, innerSlot, 'slotchange event\'s target must be the inner slot element at 1st slotchange');
510 } else if (innerSlotEventCount === 1) {
511 assert_equals(event.target, outerSlot, 'slotchange event\'s target must be the outer slot element at 2nd sltochange');
512 }
Ryosuke Niwa3801ab52016-09-21 14:33:29513 });
514 innerSlotEventCount++;
515 });
516 innerShadow.appendChild(innerSlot);
517
518 outerHost.appendChild(document.createElement('span'));
519
520 assert_equals(innerSlotEventCount, 0, 'slotchange event must not be fired synchronously');
521 assert_equals(outerSlotEventCount, 0, 'slotchange event must not be fired synchronously');
522 });
523
524 setTimeout(function () {
525 test.step(function () {
Ryosuke Niwa3801ab52016-09-21 14:33:29526 assert_equals(outerSlotEventCount, 1,
Hayato Itobbdd6aa2016-12-07 09:28:49527 'slotchange must be fired on a slot element if the assigned nodes changed');
Hayato Itoce333252017-06-09 03:31:55528 assert_equals(innerSlotEventCount, 2,
Hayato Itobbdd6aa2016-12-07 09:28:49529 'slotchange must be fired on a slot element and must bubble');
Ryosuke Niwa3801ab52016-09-21 14:33:29530 });
531 test.done();
532 }, 1);
533}
534
535testSlotchangeFiresWhenNestedSlotChange('open', true);
536testSlotchangeFiresWhenNestedSlotChange('closed', true);
537testSlotchangeFiresWhenNestedSlotChange('open', false);
538testSlotchangeFiresWhenNestedSlotChange('closed', false);
539
540function testSlotchangeFiresAtEndOfMicroTask(mode, connectedToDocument)
541{
542 var test = async_test('slotchange event must fire at the end of current microtask after mutation observers are invoked inside '
543 + treeName(mode, connectedToDocument) + ' when slots\'s contents change');
544
Hayato Itobbdd6aa2016-12-07 09:28:49545 var host;
546 var slot;
547 var eventCount = 0;
Ryosuke Niwa3801ab52016-09-21 14:33:29548
549 test.step(function () {
Hayato Itobbdd6aa2016-12-07 09:28:49550 host = document.createElement('div');
Ryosuke Niwa3801ab52016-09-21 14:33:29551 if (connectedToDocument)
Hayato Itobbdd6aa2016-12-07 09:28:49552 document.body.appendChild(host);
Ryosuke Niwa3801ab52016-09-21 14:33:29553
Hayato Itobbdd6aa2016-12-07 09:28:49554 var shadowRoot = host.attachShadow({'mode': mode});
555 slot = document.createElement('slot');
556
557 slot.addEventListener('slotchange', function (event) {
Ryosuke Niwa3801ab52016-09-21 14:33:29558 test.step(function () {
Hayato Itobbdd6aa2016-12-07 09:28:49559 assert_equals(event.type, 'slotchange', 'slotchange event\'s type must be "slotchange"');
560 assert_equals(event.target, slot, 'slotchange event\'s target must be the slot element');
Ryosuke Niwa3801ab52016-09-21 14:33:29561 });
Hayato Itobbdd6aa2016-12-07 09:28:49562 eventCount++;
Ryosuke Niwa3801ab52016-09-21 14:33:29563 });
564
Hayato Itobbdd6aa2016-12-07 09:28:49565 shadowRoot.appendChild(slot);
Ryosuke Niwa3801ab52016-09-21 14:33:29566 });
567
568 var element = document.createElement('div');
569
570 new MutationObserver(function () {
571 test.step(function () {
Hayato Itobbdd6aa2016-12-07 09:28:49572 assert_equals(eventCount, 0, 'slotchange event must not be fired before mutation records are delivered');
Ryosuke Niwa3801ab52016-09-21 14:33:29573 });
Hayato Itobbdd6aa2016-12-07 09:28:49574 host.appendChild(document.createElement('span'));
Ryosuke Niwa3801ab52016-09-21 14:33:29575 element.setAttribute('title', 'bar');
Ryosuke Niwa3801ab52016-09-21 14:33:29576 }).observe(element, {attributes: true, attributeFilter: ['id']});
577
578 new MutationObserver(function () {
579 test.step(function () {
Hayato Itobbdd6aa2016-12-07 09:28:49580 assert_equals(eventCount, 1, 'slotchange event must be fired during a single compound microtask');
Ryosuke Niwa3801ab52016-09-21 14:33:29581 });
582 }).observe(element, {attributes: true, attributeFilter: ['title']});
583
584 element.setAttribute('id', 'foo');
Hayato Itobbdd6aa2016-12-07 09:28:49585 host.appendChild(document.createElement('div'));
Ryosuke Niwa3801ab52016-09-21 14:33:29586
587 setTimeout(function () {
588 test.step(function () {
Hayato Itobbdd6aa2016-12-07 09:28:49589 assert_equals(eventCount, 2,
Ryosuke Niwa3801ab52016-09-21 14:33:29590 'a distinct slotchange event must be enqueued for changes made during a mutation observer delivery');
591 });
592 test.done();
593 }, 0);
594}
595
596testSlotchangeFiresAtEndOfMicroTask('open', true);
597testSlotchangeFiresAtEndOfMicroTask('closed', true);
598testSlotchangeFiresAtEndOfMicroTask('open', false);
599testSlotchangeFiresAtEndOfMicroTask('closed', false);
Ryosuke Niwa3801ab52016-09-21 14:33:29600</script>
601</body>
602</html>