| <!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; |
| var mutations2; |
| var div; |
| var subDiv, subDiv2, subDiv3, text; |
| var calls; |
| |
| function testBasic() { |
| var observer; |
| |
| function start() { |
| debug('Testing basic aspects of subtree observation.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| subDiv = div.appendChild(document.createElement('div')); |
| subDiv.innerHTML = 'hello, world'; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| observer.observe(div, {attributes: true, characterData: true, subtree: true}); |
| subDiv.setAttribute('foo', 'bar'); |
| subDiv.firstChild.textContent = 'goodbye!'; |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...attribute and characterData changes in subtree'); |
| |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].target', 'subDiv'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"characterData"'); |
| shouldBe('mutations[1].target', 'subDiv.firstChild'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testMultipleObservers() { |
| var observer; |
| var observer2; |
| |
| function start() { |
| debug('Testing two observers at different depths.'); |
| |
| mutations = null; |
| mutations2 = null; |
| div = document.createElement('div'); |
| subDiv = div.appendChild(document.createElement('div')); |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| observer2 = new MutationObserver(function(mutations) { |
| window.mutations2 = mutations; |
| }); |
| |
| observer.observe(div, {attributes: true, subtree: true}); |
| observer2.observe(subDiv, {attributes: true}); |
| subDiv.setAttribute('foo', 'bar'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].target', 'subDiv'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations2.length', '1'); |
| shouldBe('mutations2[0].type', '"attributes"'); |
| shouldBe('mutations2[0].target', 'subDiv'); |
| shouldBe('mutations2[0].attributeName', '"foo"'); |
| shouldBe('mutations2[0].attributeNamespace', 'null'); |
| observer.disconnect(); |
| observer2.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testMultipleObservations() { |
| var observer; |
| |
| function start() { |
| debug('Testing one observer at two different depths.'); |
| |
| mutations = null; |
| calls = 0; |
| div = document.createElement('div'); |
| subDiv = div.appendChild(document.createElement('div')); |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| ++calls; |
| }); |
| |
| observer.observe(div, {attributes: true, subtree: true}); |
| observer.observe(subDiv, {attributes: true, subtree: true}); |
| subDiv.setAttribute('foo', 'bar'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('calls', '1'); |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].target', 'subDiv'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testTransientDetachedBasic() { |
| var observer; |
| |
| function start() { |
| debug('Testing that transiently detached nodes are still observed via subtree.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| subDiv = div.appendChild(document.createElement('div')); |
| subDiv.innerHTML = 'hello, world'; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| observer.observe(div, {attributes: true, characterData: true, subtree: true}); |
| subDiv.setAttribute('foo', 'bar'); |
| div.removeChild(subDiv); |
| subDiv.setAttribute('test', 'test'); |
| setTimeout(checkDeliveredAndChangeAgain, 0); |
| } |
| |
| function checkDeliveredAndChangeAgain() { |
| debug('...both changes should be received. Change detached subDiv again.'); |
| |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].target', 'subDiv'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].target', 'subDiv'); |
| shouldBe('mutations[1].attributeName', '"test"'); |
| |
| mutations = null; |
| subDiv.setAttribute('foo', 'baz'); |
| |
| setTimeout(checkNotDeliveredAndReattach); |
| } |
| |
| function checkNotDeliveredAndReattach() { |
| debug('...transient subtree observation was stopped after delivery, so subDiv change should not be received. Reattach and change again.'); |
| |
| shouldBe('mutations', 'null'); |
| |
| mutations = null |
| div.appendChild(subDiv); |
| subDiv.setAttribute('foo', 'bat'); |
| |
| setTimeout(checkDeliveredAndReobserve); |
| } |
| |
| function checkDeliveredAndReobserve() { |
| debug('...reattached subtree should now be observable. Try detaching and re-observing.'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].target', 'subDiv'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| |
| mutations = null; |
| div.removeChild(subDiv); |
| subDiv.firstChild.textContent = 'badbye'; |
| observer.observe(div, {attributes: true, characterData: true, subtree: true}); |
| subDiv.setAttribute('foo', 'boo'); |
| |
| setTimeout(finish); |
| } |
| |
| |
| function finish() { |
| debug('...The change made before re-observing should be received, but not the one after.'); |
| |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"characterData"'); |
| shouldBe('mutations[0].target', 'subDiv.firstChild'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| start(); |
| } |
| |
| function testTransientDetachedDetailed() { |
| var observer; |
| |
| function start() { |
| debug('Testing correct behavior of transient observation with complex movement .'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| subDiv = div.appendChild(document.createElement('div')); |
| subDiv2 = subDiv.appendChild(document.createElement('div')); |
| subDiv2.innerHTML = 'hello, world'; |
| subDiv3 = document.createElement('div'); |
| |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| observer.observe(div, {attributes: true, characterData: true, subtree: true}); |
| div.removeChild(subDiv); |
| subDiv.removeChild(subDiv2); |
| text = subDiv2.removeChild(subDiv2.firstChild); |
| |
| subDiv.setAttribute('a', 'a'); |
| subDiv2.setAttribute('b', 'b'); |
| text.textContent = 'c'; |
| subDiv3.appendChild(subDiv2); |
| subDiv3.setAttribute('d', 'd'); |
| subDiv2.setAttribute('e', 'e'); |
| div.appendChild(subDiv3); |
| subDiv3.setAttribute('f', 'f'); |
| subDiv2.setAttribute('g', 'g'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...All changes should be received except for setting the "d" attribute on subDiv3 before it was reachable from div.'); |
| |
| shouldBe('mutations.length', '6'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].target', 'subDiv'); |
| shouldBe('mutations[0].attributeName', '"a"'); |
| |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].target', 'subDiv2'); |
| shouldBe('mutations[1].attributeName', '"b"'); |
| |
| shouldBe('mutations[2].type', '"characterData"'); |
| shouldBe('mutations[2].target', 'text'); |
| |
| shouldBe('mutations[3].type', '"attributes"'); |
| shouldBe('mutations[3].target', 'subDiv2'); |
| shouldBe('mutations[3].attributeName', '"e"'); |
| |
| shouldBe('mutations[4].type', '"attributes"'); |
| shouldBe('mutations[4].target', 'subDiv3'); |
| shouldBe('mutations[4].attributeName', '"f"'); |
| |
| shouldBe('mutations[5].type', '"attributes"'); |
| shouldBe('mutations[5].target', 'subDiv2'); |
| shouldBe('mutations[5].attributeName', '"g"'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| start(); |
| } |
| |
| var tests = [testBasic, testMultipleObservers, testMultipleObservations, testTransientDetachedBasic, testTransientDetachedDetailed]; |
| var testIndex = 0; |
| |
| function runNextTest() { |
| if (testIndex < tests.length) |
| tests[testIndex++](); |
| else |
| finishJSTest(); |
| } |
| |
| description('Test WebKitMutationObserver.observe on a subtree'); |
| |
| runNextTest(); |
| </script> |
| <script src="../../../resources/js-test-post.js"></script> |
| </body> |
| </html> |