| <!DOCTYPE html> |
| <script src="../../../resources/js-test-pre.js"></script> |
| <script> |
| |
| window.jsTestIsAsync = true; |
| var mutations, mutations2, mutationsWithOldValue; |
| var calls; |
| var div; |
| |
| function testBasic() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing basic aspects of attribute observation.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| div.setAttribute('bar', 'foo'); |
| |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { attributes: true, characterData: true }); |
| div.setAttribute('foo', 'bar'); |
| div.removeAttribute('bar'); |
| setTimeout(checkDisconnectAndMutate, 0); |
| } |
| |
| function checkDisconnectAndMutate() { |
| debug('...can attribute changes be observed at all'); |
| |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"bar"'); |
| shouldBe('mutations[1].attributeNamespace', 'null'); |
| |
| mutations = null; |
| observer.disconnect(); |
| div.setAttribute('foo', 'baz'); |
| setTimeout(checkNotDeliveredAndMutateMultiple, 0); |
| } |
| |
| function checkNotDeliveredAndMutateMultiple() { |
| debug('...observer.disconnect() should prevent further delivery of mutations.'); |
| |
| shouldBe('mutations', 'null'); |
| observer.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'bat'); |
| div.setAttribute('bar', 'foo'); |
| setTimeout(finish); |
| } |
| |
| function finish() { |
| debug('...re-observing after disconnect works with the same observer.'); |
| |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"bar"'); |
| shouldBe('mutations[1].attributeNamespace', 'null'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testWrongType() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing that observing without specifying "attributes" does not result in hearing about attribute changes.'); |
| |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { childList: true, characterData: true }); |
| div.setAttribute('foo', 'bar'); |
| 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, { attributes: true, characterData: true }); |
| observer.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'bar'); |
| setTimeout(checkDisconnectAndMutate, 0); |
| } |
| |
| function checkDisconnectAndMutate() { |
| shouldBe('calls', '1'); |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| mutations = null; |
| observer.observe(div, { attributes: true, characterData: true }); |
| observer.observe(div, { childList: true }); |
| div.setAttribute('foo', 'baz'); |
| 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, { attributes: true }); |
| observer2.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'bar'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations2.length', '1'); |
| shouldBe('mutations2[0].type', '"attributes"'); |
| shouldBe('mutations2[0].attributeName', '"foo"'); |
| observer.disconnect(); |
| observer2.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testNamespaceURI() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing that "attributeNamespace" value is delivered properly.'); |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(div, { attributes: true, childList: true }); |
| div.setAttributeNS('http://www.foo.com/bar', 'foo', 'bar'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', '"http://www.foo.com/bar"'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testPropertyAccess() { |
| var img, a; |
| var observer; |
| |
| function start() { |
| debug('Testing that modifications to node properties which delegate to attribute storage deliver mutations.'); |
| mutations = null; |
| img = document.createElement('img'); |
| a = document.createElement('a'); |
| |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| observer.observe(img, { attributes: true }); |
| observer.observe(a, { attributes: true }); |
| |
| img.src = 'baz.png'; |
| a.href = 'foo.html'; |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"src"'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"href"'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testOrderingWrtDOMSubtreeModified() { |
| var div, div2, subDiv; |
| var observer; |
| var listener; |
| |
| function start() { |
| debug('Testing mutation records are enqueued for attributes before DOMSubtreeModified is dispatched.'); |
| |
| mutations = null; |
| div = document.body.appendChild(document.createElement('div')); |
| div2 = document.body.appendChild(document.createElement('div')); |
| |
| subDiv = div.appendChild(document.createElement('div')); |
| |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| listener = function(e) { |
| div2.setAttribute('baz', 'bat'); |
| } |
| |
| div.addEventListener('DOMSubtreeModified', listener); |
| observer.observe(subDiv, { attributes: true }); |
| observer.observe(div2, { attributes: true }); |
| |
| subDiv.setAttribute('foo', 'bar'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"baz"'); |
| div.removeEventListener('DOMSubtreeModified', listener); |
| document.body.removeChild(div); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testOldValue() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing basic oldValue delivery.'); |
| mutations = null; |
| div = document.createElement('div'); |
| div.setAttribute('bar', 'boo'); |
| |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| div.setAttribute('foo', 'bar'); |
| div.setAttribute('foo', 'baz'); |
| div.removeAttribute('bar'); |
| div.removeAttribute('non-existant'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].oldValue', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"foo"'); |
| shouldBe('mutations[1].oldValue', '"bar"'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"bar"'); |
| shouldBe('mutations[2].oldValue', '"boo"'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testOldValueAsRequested() { |
| var div; |
| var observerWithOldValue; |
| var observer; |
| |
| function start() { |
| debug('Testing that oldValue is delivered as requested (or not).'); |
| mutationsWithOldValue = null; |
| mutations = null; |
| div = document.createElement('div'); |
| div.setAttribute('foo', 'bar'); |
| observerWithOldValue = new MutationObserver(function(mutations) { |
| window.mutationsWithOldValue = mutations; |
| }); |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| observerWithOldValue.observe(div, { attributes: true, attributeOldValue: true }); |
| observer.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'baz'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutationsWithOldValue.length', '1'); |
| shouldBe('mutationsWithOldValue[0].type', '"attributes"'); |
| shouldBe('mutationsWithOldValue[0].attributeName', '"foo"'); |
| shouldBe('mutationsWithOldValue[0].oldValue', '"bar"'); |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].oldValue', 'null'); |
| observerWithOldValue.disconnect(); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testOldValueUnionMultipleObservations() { |
| var div; |
| var span; |
| var observer; |
| |
| function start() { |
| debug('An observer with multiple observations will get attributeOldValue if any entries request it.'); |
| mutations = null; |
| div = document.createElement('div'); |
| span = div.appendChild(document.createElement('span')); |
| span.setAttribute('foo', 'bar'); |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| observer.observe(div, { attributes: true, attributeOldValue: true, subtree: true }); |
| observer.observe(span, { attributes: true }); |
| span.setAttribute('foo', 'baz'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].oldValue', '"bar"'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testIDLAttribute() { |
| var div; |
| var observer; |
| |
| function start() { |
| debug('Testing setting an attribute via reflected IDL attribute.'); |
| mutations = null; |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| div.id = 'foo'; |
| div.id = 'bar'; |
| div.id = null; |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"id"'); |
| shouldBe('mutations[0].oldValue', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"id"'); |
| shouldBe('mutations[1].oldValue', '"foo"'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"id"'); |
| shouldBe('mutations[2].oldValue', '"bar"'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testAttributeFilter() { |
| var div, path; |
| var observer; |
| |
| function start() { |
| debug('Testing that attributeFilter works as expected and observes case with HTML elements.'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| div = document.createElement('div'); |
| observer.observe(div, { attributes: true, attributeFilter: ['foo', 'bar', 'booM'] }); |
| div.setAttribute('foo', 'foo'); |
| div.setAttribute('bar', 'bar'); |
| div.setAttribute('baz', 'baz'); |
| div.setAttribute('BOOm', 'boom'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...only foo and bar should be received.'); |
| |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"bar"'); |
| shouldBe('mutations[1].attributeNamespace', 'null'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testAttributeFilterSubtree() { |
| var div, div2, div3; |
| var observer; |
| |
| function start() { |
| debug('Testing the behavior of attributeFilter when the same observer observes at multiple nodes in a subtree with different filter options.'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| div = document.createElement('div'); |
| div2 = div.appendChild(document.createElement('div')); |
| div3 = div2.appendChild(document.createElement('div')); |
| |
| observer.observe(div, { attributes: true, subtree: true, attributeFilter: ['foo', 'bar'] }); |
| observer.observe(div2, { attributes: true, subtree: true, attributeFilter: ['bar', 'bat'] }); |
| |
| div3.setAttribute('foo', 'foo'); |
| div3.setAttribute('bar', 'bar'); |
| div3.setAttribute('bat', 'bat'); |
| div3.setAttribute('baz', 'baz'); |
| |
| setTimeout(checkAndObserveAll, 0); |
| } |
| |
| function checkAndObserveAll() { |
| debug('...only foo, bar & bat should be received.'); |
| |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"foo"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"bar"'); |
| shouldBe('mutations[1].attributeNamespace', 'null'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"bat"'); |
| shouldBe('mutations[2].attributeNamespace', 'null'); |
| |
| observer.observe(div2, { attributes: true, subtree: true }); |
| div3.setAttribute('bar', 'bar'); |
| div3.setAttribute('bat', 'bat'); |
| div3.setAttribute('baz', 'baz'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...bar, bat & baz should all be received.'); |
| |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"bar"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"bat"'); |
| shouldBe('mutations[1].attributeNamespace', 'null'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"baz"'); |
| shouldBe('mutations[2].attributeNamespace', 'null'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testAttributeFilterNonHTMLElement() { |
| var path; |
| var observer; |
| |
| function start() { |
| debug('Testing that setting an attributeFilter filters out namespaced attributes.'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| observer.observe(path, { attributes: true, attributeFilter: ['pathLength'] }); |
| path.setAttributeNS('http://www.w3.org/2000/svg', 'pathLength', '200'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...pathLength should not be received.'); |
| |
| shouldBeNull('mutations'); |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testAttributeFilterNonHTMLDocument() { |
| var svgDoc, div, path; |
| var observer; |
| |
| function start() { |
| debug('Testing that attributeFilter respects case with non-HTML elements.'); |
| |
| svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', null); |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| div = svgDoc.createElement('div'); |
| observer.observe(div, { attributes: true, attributeFilter: ['ID', 'id', 'booM'] }); |
| div.setAttribute('ID', 'ID'); |
| div.setAttribute('id', 'id'); |
| div.setAttribute('baz', 'baz'); |
| div.setAttribute('booM', 'boom'); |
| div.setAttribute('BOOm', 'boom'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...only ID, id, booM should be received.'); |
| |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"ID"'); |
| shouldBe('mutations[0].attributeNamespace', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"id"'); |
| shouldBe('mutations[1].attributeNamespace', 'null'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"booM"'); |
| shouldBe('mutations[2].attributeNamespace', 'null'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testStyleAttributePropertyAccess() { |
| var div, path; |
| var observer; |
| |
| function start() { |
| debug('Testing that modifying an elements style property dispatches Mutation Records.'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| div = document.createElement('div'); |
| div.setAttribute('style', 'color: yellow; width: 100px;'); |
| observer.observe(div, { attributes: true }); |
| div.style.color = 'red'; |
| div.style.width = '200px'; |
| div.style.color = 'blue'; |
| |
| setTimeout(checkAndContinue, 0); |
| } |
| |
| function checkAndContinue() { |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"style"'); |
| shouldBe('mutations[0].oldValue', 'null'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"style"'); |
| shouldBe('mutations[1].oldValue', 'null'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"style"'); |
| shouldBe('mutations[2].oldValue', 'null'); |
| |
| mutations = null; |
| div.getAttribute('style'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...mutation record created.'); |
| |
| shouldBe('mutations', 'null'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testStyleAttributePropertyAccessOldValue() { |
| var div, path; |
| var observer; |
| |
| function start() { |
| debug('Testing that modifying an elements style property dispatches Mutation Records with correct oldValues.'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| div = document.createElement('div'); |
| div.setAttribute('style', 'color: yellow; width: 100px;'); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| div.style.color = 'red'; |
| div.style.width = '200px'; |
| div.style.color = 'blue'; |
| |
| setTimeout(checkAndContinue, 0); |
| } |
| |
| function checkAndContinue() { |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"style"'); |
| shouldBe('mutations[0].oldValue', '"color: yellow; width: 100px;"'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"style"'); |
| shouldBe('mutations[1].oldValue', '"color: red; width: 100px;"'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"style"'); |
| shouldBe('mutations[2].oldValue', '"color: red; width: 200px;"'); |
| |
| mutations = null; |
| div.getAttribute('style'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| debug('...mutation record created.'); |
| |
| shouldBe('mutations', 'null'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testStyleAttributePropertyAccessIgnoreNoop() { |
| var div, path; |
| var observer; |
| |
| function start() { |
| debug('Testing that a no-op style property mutation does not create Mutation Records.'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(m) { |
| mutations = m; |
| }); |
| |
| div = document.createElement('div'); |
| div.setAttribute('style', 'color: yellow; width: 100px;'); |
| observer.observe(div, { attributes: true }); |
| div.style.removeProperty('height'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations', 'null'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testMutateThroughAttrNodeValue() { |
| var observer; |
| |
| function start() { |
| debug('Test that mutating an attribute through an attr node delivers mutation records'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| div = document.createElement('div'); |
| div.setAttribute('data-test', 'foo'); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| div.attributes['data-test'].value = 'bar'; |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].target', 'div'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"data-test"'); |
| shouldBe('mutations[0].oldValue', '"foo"'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testSetAndRemoveAttributeNode() { |
| var observer; |
| |
| function start() { |
| debug('Test that mutating via setAttributeNode delivers mutation records'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| div = document.createElement('div'); |
| div.id = 'myId'; |
| div.setAttribute('data-test', 'foo'); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| var attr = document.createAttribute('data-test'); |
| attr.value = 'bar'; |
| div.setAttributeNode(attr); |
| attr = document.createAttribute('data-other'); |
| attr.value = 'baz'; |
| div.setAttributeNode(attr); |
| div.removeAttributeNode(div.attributes['id']); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '3'); |
| shouldBe('mutations[0].target', 'div'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"data-test"'); |
| shouldBe('mutations[0].oldValue', '"foo"'); |
| shouldBe('mutations[1].target', 'div'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"data-other"'); |
| shouldBe('mutations[1].oldValue', 'null'); |
| shouldBe('mutations[2].target', 'div'); |
| shouldBe('mutations[2].type', '"attributes"'); |
| shouldBe('mutations[2].attributeName', '"id"'); |
| shouldBe('mutations[2].oldValue', '"myId"'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testMixedNodeAndElementOperations() { |
| var observer; |
| |
| function start() { |
| debug('Test that setAttribute on an attribute with an existing Attr delivers mutation records'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| div = document.createElement('div'); |
| var attr = document.createAttribute('data-test'); |
| attr.value = 'foo'; |
| div.setAttributeNode(attr); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| div.setAttribute('data-test', 'bar'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '1'); |
| shouldBe('mutations[0].target', 'div'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"data-test"'); |
| shouldBe('mutations[0].oldValue', '"foo"'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| function testNamedNodeMapOperations() { |
| var observer; |
| |
| function start() { |
| debug('Test that setNamedItem and removeNamedItem deliver mutation records'); |
| |
| mutations = null; |
| observer = new MutationObserver(function(mutations) { |
| window.mutations = mutations; |
| }); |
| |
| div = document.createElement('div'); |
| div.setAttribute('data-test', 'foo'); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| var attr = document.createAttribute('data-test'); |
| attr.value = 'bar'; |
| div.attributes.setNamedItem(attr); |
| div.attributes.removeNamedItem('data-test'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| shouldBe('mutations.length', '2'); |
| shouldBe('mutations[0].target', 'div'); |
| shouldBe('mutations[0].type', '"attributes"'); |
| shouldBe('mutations[0].attributeName', '"data-test"'); |
| shouldBe('mutations[0].oldValue', '"foo"'); |
| shouldBe('mutations[1].target', 'div'); |
| shouldBe('mutations[1].type', '"attributes"'); |
| shouldBe('mutations[1].attributeName', '"data-test"'); |
| shouldBe('mutations[1].oldValue', '"bar"'); |
| |
| observer.disconnect(); |
| debug(''); |
| runNextTest(); |
| } |
| |
| start(); |
| } |
| |
| var tests = [ |
| testBasic, |
| testWrongType, |
| testMultipleRegistration, |
| testMultipleObservers, |
| testNamespaceURI, |
| testPropertyAccess, |
| testOrderingWrtDOMSubtreeModified, |
| testOldValue, |
| testOldValueAsRequested, |
| testOldValueUnionMultipleObservations, |
| testIDLAttribute, |
| testAttributeFilter, |
| testAttributeFilterSubtree, |
| testAttributeFilterNonHTMLElement, |
| testAttributeFilterNonHTMLDocument, |
| testStyleAttributePropertyAccess, |
| testStyleAttributePropertyAccessOldValue, |
| testStyleAttributePropertyAccessIgnoreNoop, |
| testMutateThroughAttrNodeValue, |
| testSetAndRemoveAttributeNode, |
| testMixedNodeAndElementOperations, |
| testNamedNodeMapOperations |
| ]; |
| 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> |