| <html> |
| <link rel="import" href="../resources/chai.html" /> |
| <link rel="import" href="../resources/mocha.html" /> |
| <script> |
| describe('MutationObserver.observe on attributes', function() { |
| it('should handle basic aspects of attribute observation', function(done) { |
| var div; |
| var observer; |
| var mutations; |
| |
| function start() { |
| div = document.createElement('div'); |
| div.setAttribute('bar', 'foo'); |
| |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| observer.observe(div, { attributes: true, characterData: true }); |
| div.setAttribute('foo', 'bar'); |
| div.removeAttribute('bar'); |
| setTimeout(checkDisconnectAndMutate, 0); |
| } |
| |
| function checkDisconnectAndMutate() { |
| // ...can attribute changes be observed at all |
| |
| assert.equal(mutations.length, 2); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "bar"); |
| |
| mutations = null; |
| observer.disconnect(); |
| div.setAttribute('foo', 'baz'); |
| setTimeout(checkNotDeliveredAndMutateMultiple, 0); |
| } |
| |
| function checkNotDeliveredAndMutateMultiple() { |
| // ...observer.disconnect() should prevent further delivery of mutations. |
| |
| assert.equal(mutations, null); |
| observer.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'bat'); |
| div.setAttribute('bar', 'foo'); |
| setTimeout(finish); |
| } |
| |
| function finish() { |
| // ...re-observing after disconnect works with the same observer. |
| |
| assert.equal(mutations.length, 2); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "bar"); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should not notify of attribute changes without asking', function(done) { |
| var div; |
| var observer; |
| var mutations; |
| |
| function start() { |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| observer.observe(div, { childList: true, characterData: true }); |
| div.setAttribute('foo', 'bar'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| assert.equal(mutations, null); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('re-observing the same node with the same observer has the effect of resetting the options', function(done) { |
| var div; |
| var observer; |
| var mutations; |
| var calls = 0; |
| |
| function start() { |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| calls++; |
| }); |
| |
| observer.observe(div, { attributes: true, characterData: true }); |
| observer.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'bar'); |
| setTimeout(checkDisconnectAndMutate, 0); |
| } |
| |
| function checkDisconnectAndMutate() { |
| assert.equal(calls, 1); |
| assert.equal(mutations.length, 1); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(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() { |
| assert.equal(mutations, null); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('multiple observers can be registered to a given node and both receive mutations', function(done) { |
| var div; |
| var observer; |
| var observer2; |
| var mutations; |
| var mutations2; |
| |
| function start() { |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| observer2 = new MutationObserver(function(records) { |
| mutations2 = records; |
| }); |
| observer.observe(div, { attributes: true }); |
| observer2.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'bar'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| assert.equal(mutations.length, 1); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations2.length, 1); |
| assert.equal(mutations2[0].type, "attributes"); |
| assert.equal(mutations2[0].attributeName, "foo"); |
| observer.disconnect(); |
| observer2.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should deliver mutations on modifications to node properties which delegate to attribute storage', function(done) { |
| var img, a; |
| var observer; |
| var mutations; |
| |
| function start() { |
| img = document.createElement('img'); |
| a = document.createElement('a'); |
| |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| observer.observe(img, { attributes: true }); |
| observer.observe(a, { attributes: true }); |
| |
| img.src = 'baz.png'; |
| a.href = 'foo.html'; |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| assert.equal(mutations.length, 2); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "src"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "href"); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should handle basic oldValue delivery', function(done) { |
| var div; |
| var observer; |
| var mutations; |
| |
| function start() { |
| div = document.createElement('div'); |
| div.setAttribute('bar', 'boo'); |
| |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| 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() { |
| assert.equal(mutations.length, 3); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[0].oldValue, null); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "foo"); |
| assert.equal(mutations[1].oldValue, "bar"); |
| assert.equal(mutations[2].type, "attributes"); |
| assert.equal(mutations[2].attributeName, "bar"); |
| assert.equal(mutations[2].oldValue, "boo"); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should deliver oldValue when needed', function(done) { |
| var div; |
| var observerWithOldValue; |
| var observer; |
| var mutationsWithOldValue; |
| var mutations; |
| |
| function start() { |
| div = document.createElement('div'); |
| div.setAttribute('foo', 'bar'); |
| observerWithOldValue = new MutationObserver(function(records) { |
| mutationsWithOldValue = records; |
| }); |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| observerWithOldValue.observe(div, { attributes: true, attributeOldValue: true }); |
| observer.observe(div, { attributes: true }); |
| div.setAttribute('foo', 'baz'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| assert.equal(mutationsWithOldValue.length, 1); |
| assert.equal(mutationsWithOldValue[0].type, "attributes"); |
| assert.equal(mutationsWithOldValue[0].attributeName, "foo"); |
| assert.equal(mutationsWithOldValue[0].oldValue, "bar"); |
| assert.equal(mutations.length, 1); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[0].oldValue, null); |
| observerWithOldValue.disconnect(); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should give attributeOldValue if any entries request it with multiple observers', function(done) { |
| var div; |
| var span; |
| var observer; |
| var mutations; |
| |
| function start() { |
| div = document.createElement('div'); |
| span = div.appendChild(document.createElement('span')); |
| span.setAttribute('foo', 'bar'); |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| observer.observe(div, { attributes: true, attributeOldValue: true, subtree: true }); |
| observer.observe(span, { attributes: true }); |
| span.setAttribute('foo', 'baz'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| assert.equal(mutations.length, 1); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[0].oldValue, "bar"); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should handle setting an attribute via reflected IDL attribute', function(done) { |
| var div; |
| var observer; |
| var mutations; |
| |
| function start() { |
| div = document.createElement('div'); |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| observer.observe(div, { attributes: true, attributeOldValue: true }); |
| div.id = 'foo'; |
| div.id = 'bar'; |
| div.id = null; |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| assert.equal(mutations.length, 3); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "id"); |
| assert.equal(mutations[0].oldValue, null); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "id"); |
| assert.equal(mutations[1].oldValue, "foo"); |
| assert.equal(mutations[2].type, "attributes"); |
| assert.equal(mutations[2].attributeName, "id"); |
| assert.equal(mutations[2].oldValue, "bar"); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should respect attributeFilter on HTML elements', function(done) { |
| var div, path; |
| var observer; |
| var mutations; |
| |
| function start() { |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| 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() { |
| // ...only foo and bar should be received. |
| |
| assert.equal(mutations.length, 2); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "bar"); |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should respect different attributeFilters when observing multiple subtree nodes', function(done) { |
| var div, div2, div3; |
| var observer; |
| var mutations; |
| |
| function start() { |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| 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() { |
| // ...only foo, bar & bat should be received. |
| |
| assert.equal(mutations.length, 3); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "foo"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "bar"); |
| assert.equal(mutations[2].type, "attributes"); |
| assert.equal(mutations[2].attributeName, "bat"); |
| |
| observer.observe(div2, { attributes: true, subtree: true }); |
| div3.setAttribute('bar', 'bar'); |
| div3.setAttribute('bat', 'bat'); |
| div3.setAttribute('baz', 'baz'); |
| |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| // ...bar, bat & baz should all be received. |
| |
| assert.equal(mutations.length, 3); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "bar"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "bat"); |
| assert.equal(mutations[2].type, "attributes"); |
| assert.equal(mutations[2].attributeName, "baz"); |
| |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should create records for the style property', function(done) { |
| var div, path; |
| var observer; |
| var mutations; |
| |
| function start() { |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| 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() { |
| assert.equal(mutations.length, 3); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "style"); |
| assert.equal(mutations[0].oldValue, null); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "style"); |
| assert.equal(mutations[1].oldValue, null); |
| assert.equal(mutations[2].type, "attributes"); |
| assert.equal(mutations[2].attributeName, "style"); |
| assert.equal(mutations[2].oldValue, null); |
| |
| mutations = null; |
| div.getAttribute('style'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| // ...mutation record created. |
| |
| assert.equal(mutations, null); |
| |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should have oldValue for style property mutations', function(done) { |
| var div, path; |
| var observer; |
| var mutations; |
| |
| function start() { |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| 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() { |
| assert.equal(mutations.length, 3); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "style"); |
| assert.equal(mutations[0].oldValue, "color: yellow; width: 100px;"); |
| assert.equal(mutations[1].type, "attributes"); |
| assert.equal(mutations[1].attributeName, "style"); |
| assert.equal(mutations[1].oldValue, "color: rgb(255, 0, 0); width: 100px;"); |
| assert.equal(mutations[2].type, "attributes"); |
| assert.equal(mutations[2].attributeName, "style"); |
| assert.equal(mutations[2].oldValue, "color: rgb(255, 0, 0); width: 200px;"); |
| |
| mutations = null; |
| div.getAttribute('style'); |
| setTimeout(finish, 0); |
| } |
| |
| function finish() { |
| // ...mutation record created. |
| |
| assert.equal(mutations, null); |
| |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should not create records for noop style property mutation', function(done) { |
| var div, path; |
| var observer; |
| var mutations; |
| |
| function start() { |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| 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() { |
| assert.equal(mutations, null); |
| |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| |
| it('should create records when mutating through the attribute collection', function(done) { |
| var observer; |
| var mutations; |
| var div; |
| |
| function start() { |
| observer = new MutationObserver(function(records) { |
| mutations = records; |
| }); |
| |
| 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() { |
| assert.equal(mutations.length, 1); |
| assert.equal(mutations[0].target, div); |
| assert.equal(mutations[0].type, "attributes"); |
| assert.equal(mutations[0].attributeName, "data-test"); |
| assert.equal(mutations[0].oldValue, "foo"); |
| |
| observer.disconnect(); |
| done(); |
| } |
| |
| start(); |
| }); |
| }); |
| </script> |
| </body> |
| </html> |