970 lines
29 KiB
HTML
970 lines
29 KiB
HTML
<!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>
|