<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="../../../resources/js-test.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>
</body>
</html>