<!DOCTYPE html>
<html>
<body>
<p>This test ensures WebKit does not fire DOM mutation events while execCommand is in progress.</p>
<div id="test" contenteditable></div>
<pre>
<script>

if (window.testRunner)
    testRunner.dumpAsText();

var commands = [
    {name: 'BackColor', value: 'blue'},
    {name: 'CreateLink', value: 'about:blank'},
    {name: 'Delete', value: null},
    {name: 'FontName', value: 'Arial'},
    {name: 'FontSize', value: '5'},
    {name: 'FontSizeDelta', value: '5'},
    {name: 'ForeColor', value: 'blue'},
    {name: 'FormatBlock', value: 'pre'},
    {name: 'ForwardDelete', value: null},
    {name: 'HiliteColor', value: 'red'},
    {name: 'Indent', value: null},
    {name: 'InsertHTML', value: "<i>hello</i>"},
    {name: 'InsertHorizontalRule', value: null},
    {name: 'InsertImage', value: '../resources/abe.png'},
    {name: 'InsertLineBreak', value: null},
    {name: 'InsertNewlineInQuotedContent', value: null},
    {name: 'InsertOrderedList', value: null},
    {name: 'InsertParagraph', value: null},
    {name: 'InsertText', value: 'webkit'},
    {name: 'InsertUnorderedList', value: null},
    {name: 'Italic', value: null},
    {name: 'JustifyCenter', value: null},
    {name: 'JustifyFull', value: null},
    {name: 'JustifyLeft', value: null},
    {name: 'JustifyNone', value: null},
    {name: 'JustifyRight', value: null},
    {name: 'Outdent', value: null},
    {name: 'RemoveFormat', value: null},
    {name: 'Strikethrough', value: null},
    {name: 'Subscript', value: null},
    {name: 'Superscript', value: null},
    {name: 'Transpose', value: null, selector: function (test) { window.getSelection().setPosition(test.firstChild, 1); }},
    {name: 'Underline', value: null},
    {name: 'Unlink', value: null},
];

var events = {
    'DOMCharacterDataModified': false,
    'DOMSubtreeModified': false,
    'DOMNodeInserted': false,
    'DOMNodeRemoved': false,
    'DOMNodeRemovedFromDocument': false,
    'DOMNodeInsertedIntoDocument': true, // this event can never be observed.
    'focusin': false,
    'focusout': false,
};

var log = [];
var test = document.getElementById('test');

function addEventListeners(node) {
    for (var e in events) {
        node.addEventListener(e, function (event) {
            log.push(test.innerHTML);
            events[event.type] = true;
        }, false);
    }
}

function isLogConsistent() {
    for (var i= 1; i < log.length; i++) {
        if (log[0] != log[i]) {
            console.log(log);
            return false;
        }
    }
    return true;
}

addEventListeners(test);

var initial = 'hello, <input type="text"><blockquote align="right"><u><a href="about:blank">world</a></u></blockquote>';
for (var i = 0; i < commands.length; i++) {
    test.innerHTML = initial;
    if (i)
        document.write("\n");

    if (test.innerHTML != initial) {
        document.write("FAIL: initial innerHTML didn't match");
        continue;
    }

    if (commands[i].selector)
        commands[i].selector(test);
    else {    
        document.getElementsByTagName('input')[0].focus();
        window.getSelection().selectAllChildren(test);
    }
    addEventListeners(test.childNodes[2]);
    log = []; // clear log
    document.execCommand(commands[i].name, false, commands[i].value);

    var quotedValue = commands[i].value ? "'" + commands[i].value.replace('<', '&lt;') + "'" : null;
    var action = "execCommand('" + commands[i].name + "', false, " + quotedValue + ")";

    if (test.innerHTML == initial || log.length <= 0)
        document.write('FAIL: ' + action + ' made no change to the DOM.');
    else if (!isLogConsistent())
        document.write('FAIL: ' + action + ' dispatched events before finalizing the DOM tree.');
    else
        document.write('PASS: ' + action);
}
test.style.display = 'none';

document.write('\n');
for (var e in events) {
    if (!events[e])
        document.write('\nWARNING: ' + e + ' was never observed.');
}

document.write('\n\nDONE');

</script>
</pre>
</body>
</html>
