<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=687787
-->
<head>
  <title>Test for Bug 687787</title>
  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=687787">Mozilla Bug 687787</a>
<p id="display"></p>
<div id="content" style="display: none">

</div>

<pre id="test">
<script class="testbody" type="text/javascript">

var eventStack = [];

function _callback(e){
    var event = {'type' : e.type, 'target' : e.target, 'relatedTarget' : e.relatedTarget }
    eventStack.push(event);
}

function clearEventStack(){
    eventStack = [];
}

window.addEventListener("focus", _callback, true);
window.addEventListener("focusin", _callback, true);
window.addEventListener("focusout", _callback, true);
window.addEventListener("blur", _callback, true);

function CompareEventToExpected(e, expected) {
    if (expected == null || e == null)
        return false;
    if (e.type == expected.type && e.target == expected.target && e.relatedTarget == expected.relatedTarget)
        return true;
    return false;
}

function TestEventOrderNormal() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var input3 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    input3.setAttribute('id', 'input3');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');
    input3.setAttribute('type', 'text');

    content.appendChild(input1);
    content.appendChild(input2);
    content.appendChild(input3);
    content.style.display = 'block'

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focus',
        'target' : input2,
        'relatedTarget' : input1},
        {'type' : 'focusin',
        'target' : input2,
        'relatedTarget' : input1},
        {'type' : 'blur',
        'target' : input2,
        'relatedTarget' : input3},
        {'type' : 'focusout',
        'target' : input2,
        'relatedTarget' : input3},
        {'type' : 'focus',
        'target' : input3,
        'relatedTarget' : input2},
        {'type' : 'focusin',
        'target' : input3,
        'relatedTarget' : input2},
    ]

    input1.focus();
    clearEventStack();

    input2.focus();
    input3.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length ; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestEventOrderNormalFiresAtRightTime() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var input3 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    input3.setAttribute('id', 'input3');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');
    input3.setAttribute('type', 'text');

    input1.onblur = function(e)
    {
        ok(document.activeElement == document.body, 'input1: not focused when blur fires')
    }

    input1.addEventListener('focusout', function(e)
    {
        ok(document.activeElement == document.body, 'input1: not focused when focusout fires')
    });

    input2.onfocus = function(e)
    {
        ok(document.activeElement == input2, 'input2: focused when focus fires')
    }

    input2.addEventListener('focusin', function(e)
    {
        ok(document.activeElement == input2, 'input2: focused when focusin fires')
    });

    content.appendChild(input1);
    content.appendChild(input2);
    content.style.display = 'block'

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focus',
        'target' : input2,
        'relatedTarget' : input1},
        {'type' : 'focusin',
        'target' : input2,
        'relatedTarget' : input1},
    ]

    input1.focus();
    clearEventStack();

    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length ; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestFocusOutRedirectsFocus() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var input3 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    input3.setAttribute('id', 'input3');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');
    input3.setAttribute('type', 'text');
    input1.addEventListener('focusout', function () {
        input3.focus();
    });

    content.appendChild(input1);
    content.appendChild(input2);
    content.appendChild(input3);
    content.style.display = 'block'

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focus',
        'target' : input3,
        'relatedTarget' : null},
        {'type' : 'focusin',
        'target' : input3,
        'relatedTarget' : null},
    ]

    input1.focus();
    clearEventStack();
    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestFocusInRedirectsFocus() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var input3 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    input3.setAttribute('id', 'input3');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');
    input3.setAttribute('type', 'text');
    input2.addEventListener('focusin', function () {
        input3.focus();
    });

    content.appendChild(input1);
    content.appendChild(input2);
    content.appendChild(input3);
    content.style.display = 'block'

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focus',
        'target' : input2,
        'relatedTarget' : input1},
        {'type' : 'focusin',
        'target' : input2,
        'relatedTarget' : input1},
        {'type' : 'blur',
        'target' : input2,
        'relatedTarget' : input3},
        {'type' : 'focusout',
        'target' : input2,
        'relatedTarget' : input3},
        {'type' : 'focus',
        'target' : input3,
        'relatedTarget' : input2},
        {'type' : 'focusin',
        'target' : input3,
        'relatedTarget' : input2},
    ]

    input1.focus();
    clearEventStack();
    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestBlurRedirectsFocus() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var input3 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    input3.setAttribute('id', 'input3');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');
    input3.setAttribute('type', 'text');
    input1.onblur = function () {
        input3.focus();
    }

    content.appendChild(input1);
    content.appendChild(input2);
    content.appendChild(input3);
    content.style.display = 'block'

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focus',
        'target' : input3,
        'relatedTarget' : null},
        {'type' : 'focusin',
        'target' : input3,
        'relatedTarget' : null},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
    ]

    input1.focus();
    clearEventStack();
    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestFocusRedirectsFocus() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var input3 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    input3.setAttribute('id', 'input3');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');
    input3.setAttribute('type', 'text');
    input2.onfocus = function () {
        input3.focus();
    }

    content.appendChild(input1);
    content.appendChild(input2);
    content.appendChild(input3);
    content.style.display = 'block'

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focus',
        'target' : input2,
        'relatedTarget' : input1},
        {'type' : 'blur',
        'target' : input2,
        'relatedTarget' : input3},
        {'type' : 'focusout',
        'target' : input2,
        'relatedTarget' : input3},
        {'type' : 'focus',
        'target' : input3,
        'relatedTarget' : input2},
        {'type' : 'focusin',
        'target' : input3,
        'relatedTarget' : input2},
    ]

    input1.focus();
    clearEventStack();
    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestEventOrderDifferentDocument() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var iframe1 = document.createElement('iframe');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    iframe1.setAttribute('id', 'iframe1');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');

    content.appendChild(input1);
    content.appendChild(iframe1);
    iframe1.contentDocument.body.appendChild(input2);
    content.style.display = 'block'

    iframe1.contentDocument.addEventListener("focus", _callback, true);
    iframe1.contentDocument.addEventListener("focusin", _callback, true);
    iframe1.contentDocument.addEventListener("focusout", _callback, true);
    iframe1.contentDocument.addEventListener("blur", _callback, true);

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : null},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : null},
        {'type' : 'blur',
        'target' : document,
        'relatedTarget' : null},
        {'type' : 'blur',
        'target' : window,
        'relatedTarget' : null},
        {'type' : 'focus',
        'target' : iframe1.contentDocument,
        'relatedTarget' : null},
        {'type' : 'focus',
        'target' : input2,
        'relatedTarget' : null},
        {'type' : 'focusin',
        'target' : input2,
        'relatedTarget' : null},
    ]

    input1.focus();
    clearEventStack();
    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}


function TestFocusOutMovesTarget() {

    var input1 = document.createElement('input');
    var input2 = document.createElement('input');
    var iframe1 = document.createElement('iframe');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input2.setAttribute('id', 'input2');
    iframe1.setAttribute('id', 'iframe1');
    input1.setAttribute('type', 'text');
    input2.setAttribute('type', 'text');

    input1.addEventListener('focusout', function () {
        iframe1.contentDocument.body.appendChild(input2);
    });

    content.appendChild(input1);
    content.appendChild(input2);
    content.appendChild(iframe1);
    content.style.display = 'block'

    iframe1.contentDocument.addEventListener("focus", _callback, true);
    iframe1.contentDocument.addEventListener("focusin", _callback, true);
    iframe1.contentDocument.addEventListener("focusout", _callback, true);
    iframe1.contentDocument.addEventListener("blur", _callback, true);

    let expectedEventOrder = [
        {'type' : 'blur',
        'target' : input1,
        'relatedTarget' : input2},
        {'type' : 'focusout',
        'target' : input1,
        'relatedTarget' : input2},
    ]

    input1.focus();
    clearEventStack();
    input2.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

function TestBlurWindowAndRefocusInputOnlyFiresFocusInOnInput() {

    var input1 = document.createElement('input');
    var content = document.getElementById('content');

    input1.setAttribute('id', 'input1');
    input1.setAttribute('type', 'text');

    content.appendChild(input1);

    let expectedEventOrder = [
        {'type' : 'focus',
        'target' : document,
        'relatedTarget' : null},
        {'type' : 'focus',
        'target' : window,
        'relatedTarget' : null},
        {'type' : 'focus',
        'target' : input1,
        'relatedTarget' : null},
        {'type' : 'focusin',
        'target' : input1,
        'relatedTarget' : null},
    ]

    window.blur();
    clearEventStack();
    input1.focus();

    for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
        ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
                + 'Expected ('
                    + expectedEventOrder[i].type + ','
                    + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
                    + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
                + 'Actual ('
                    + eventStack[i].type + ','
                    + (eventStack[i].target ? eventStack[i].target.id : null) + ','
                    + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
    }

    content.innerHTML = '';
}

TestEventOrderNormal();
TestEventOrderNormalFiresAtRightTime();
TestFocusOutRedirectsFocus();
TestFocusInRedirectsFocus();
TestBlurRedirectsFocus();
TestFocusRedirectsFocus();
TestFocusOutMovesTarget();
TestEventOrderDifferentDocument();
TestBlurWindowAndRefocusInputOnlyFiresFocusInOnInput();

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