| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <script src="../../../resources/js-test-pre.js"></script> |
| <title></title> |
| </head> |
| <body> |
| <p id=description></p> |
| <div id="console"></div> |
| <script> |
| |
| window.jsTestIsAsync = true; |
| var mutations, mutations2; |
| var calls; |
| var div, removedDiv1, removedDiv2, addedDiv1, addedDiv2, addedDiv3; |
| |
| function testBasic() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing basic aspects of childList observation.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { childList: true }); |
| removedDiv1 = div.appendChild(document.createElement('div')); |
| setTimeout(checkDisconnectAndMutate, 0); |
| } |
| |
| function checkDisconnectAndMutate() { |
| debug('...can childList changes be observed at all'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '1'); |
| shouldBe('mutations[0].addedNodes[0]', 'removedDiv1'); |
| |
| mutations = null; |
| observer.disconnect(); |
| removedDiv1 = div.appendChild(document.createElement('div')); |
| setTimeout(checkNotDeliveredAndMutateMultiple, 0); |
| } |
| |
| function checkNotDeliveredAndMutateMultiple() { |
| debug('...observer.disconnect() should prevent further delivery of mutations.'); |
| |
| shouldBe('mutations', 'null'); |
| observer.observe(div, { childList: true }); |
| removedDiv1 = div.removeChild(div.firstChild); |
| removedDiv2 = div.removeChild(div.firstChild); |
| setTimeout(finish); |
| } |
| |
| function finish() { |
| debug('...re-observing after disconnect works with the same observer.'); |
| |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].removedNodes.length', '1'); |
| shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); |
| shouldBe('mutations[1].type', '"childList"'); |
| shouldBe('mutations[1].removedNodes.length', '1'); |
| shouldBe('mutations[1].removedNodes[0]', 'removedDiv2'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testWrongType() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing that observing without specifying "childList" does not result in hearing about childList changes.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { attributes: true, characterData: true }); |
| div.appendChild(document.createElement('div')); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations', 'null'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testMultipleRegistration() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing that re-observing the same node with the same observer has the effect of resetting the options.'); |
| |
| calls = 0; |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| calls++; |
| }); |
| |
| observer.observe(div, { childList: true, characterData: true }); |
| observer.observe(div, { childList: true }); |
| div.appendChild(document.createElement('div')); |
| setTimeout(checkDisconnectAndMutate, 0); |
| } |
| |
| function checkDisconnectAndMutate() { |
| shouldBe('calls', '1'); |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| mutations = null; |
| observer.observe(div, { childList: true, characterData: true }); |
| observer.observe(div, { attributes: true }); |
| div.appendChild(document.createElement('div')); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations', 'null'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testMultipleObservers() { |
| var div; |
| var observer; |
| var observer2; |
| |
| function start() { |
| debug('Testing that multiple observers can be registered to a given node and both receive mutations.'); |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| observer2 = new MutationObserver(function(m) { |
| mutations2 = m; |
| }); |
| observer.observe(div, { childList: true }); |
| observer2.observe(div, { childList: true }); |
| div.appendChild(document.createElement('div')); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations2.length', '1'); |
| shouldBe('mutations2[0].type', '"childList"'); |
| observer.disconnect(); |
| observer2.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testInnerHTMLAndInnerText() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing that innerText and innerHTML always result in a single childList mutation.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| div.innerHTML = '<span>Foo</span><div>Bar</div>'; |
| removedDiv1 = div.firstChild; |
| removedDiv2 = removedDiv1.nextSibling; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { childList: true }); |
| div.innerHTML = 'foo<img src="bar.png"><p>Baz</p>'; |
| addedDiv1 = div.childNodes[0]; |
| addedDiv2 = div.childNodes[1]; |
| addedDiv3 = div.childNodes[2]; |
| setTimeout(checkInnerHTML, 0); |
| } |
| |
| function checkInnerHTML() { |
| debug('...innerHTML'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '3'); |
| shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); |
| shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); |
| shouldBe('mutations[0].addedNodes[2]', 'addedDiv3'); |
| shouldBe('mutations[0].removedNodes.length', '2'); |
| shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); |
| shouldBe('mutations[0].removedNodes[1]', 'removedDiv2'); |
| |
| mutations = null; |
| div.innerText = 'foo'; |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...innerText'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '1'); |
| shouldBe('mutations[0].removedNodes.length', '3'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testReplaceChild() { |
| var div; |
| var observer; |
| var fragment; |
| |
| function start() { |
| debug('Testing that replaceChild results in minimal childList mutations.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| div.innerHTML = '<span>Foo</span><div>Bar</div>'; |
| removedDiv1 = div.firstChild; |
| |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { childList: true }); |
| addedDiv1 = document.createElement('div'); |
| div.replaceChild(addedDiv1, div.firstChild); |
| setTimeout(checkReplaceWithNode, 0); |
| } |
| |
| function checkReplaceWithNode() { |
| debug('...simple replace child'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '1'); |
| shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); |
| shouldBe('mutations[0].removedNodes.length', '1'); |
| shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); |
| |
| mutations = null; |
| fragment = document.createDocumentFragment(); |
| addedDiv1 = fragment.appendChild(document.createElement('div')); |
| addedDiv2 = fragment.appendChild(document.createElement('div')); |
| removedDiv1 = div.firstChild; |
| |
| div.replaceChild(fragment, removedDiv1); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...replace with DocumentFragment'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '2'); |
| shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); |
| shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); |
| shouldBe('mutations[0].removedNodes.length', '1'); |
| shouldBe('mutations[0].removedNodes[0]', 'removedDiv1'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testInsertBefore() { |
| var div; |
| var observer; |
| var fragment; |
| |
| function start() { |
| debug('Testing that insertBefore results in minimal childList mutations.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| div.innerHTML = '<span>Foo</span>'; |
| fragment = document.createDocumentFragment(); |
| addedDiv1 = fragment.appendChild(document.createElement('div')); |
| addedDiv2 = fragment.appendChild(document.createElement('div')); |
| |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { childList: true }); |
| div.insertBefore(fragment, div.firstChild); |
| setTimeout(finish, 0); |
| } |
| |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '2'); |
| shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); |
| shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); |
| shouldBe('mutations[0].removedNodes.length', '0'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testAppendChild() { |
| var div; |
| var observer; |
| var fragment; |
| |
| function start() { |
| debug('Testing that appendChild results in minimal childList mutations.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| div.innerHTML = '<span>Foo</span>'; |
| fragment = document.createDocumentFragment(); |
| addedDiv1 = fragment.appendChild(document.createElement('div')); |
| addedDiv2 = fragment.appendChild(document.createElement('div')); |
| |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { childList: true }); |
| div.appendChild(fragment); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"childList"'); |
| shouldBe('mutations[0].addedNodes.length', '2'); |
| shouldBe('mutations[0].addedNodes[0]', 'addedDiv1'); |
| shouldBe('mutations[0].addedNodes[1]', 'addedDiv2'); |
| shouldBe('mutations[0].removedNodes.length', '0'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testInnerHTMLEmpty() { |
| function start() { |
| debug('Setting an empty childlist to the empty string with innerHTML should not assert.'); |
| |
| var div = document.createElement('div'); |
| mutations = null; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| observer.observe(div, {childList: true}); |
| div.innerHTML = ''; |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBeNull('mutations'); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| var tests = [testBasic, testWrongType, testMultipleRegistration, testMultipleObservers, testInnerHTMLAndInnerText, testReplaceChild, testInsertBefore, testAppendChild, testInnerHTMLEmpty]; |
| var testIndex = 0; |
| |
| function runNextTest() { |
| if (testIndex < tests.length) |
| tests[testIndex++](); |
| else |
| finishJSTest(); |
| } |
| |
| description('Test WebKitMutationObserver.observe on attributes.'); |
| |
| runNextTest(); |
| </script> |
| <script src="../../../resources/js-test-post.js"></script> |
| </body> |
| </html> |