<!DOCTYPE html>
<meta charset=utf-8>
<title>EventTarget.dispatchEvent</title>
<link rel="author" title="Olli Pettay" href="mailto:Olli.Pettay@gmail.com">
<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
<link rel="help" href="https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/dom/nodes/Document-createEvent.js"></script>
<div id="log"></div>
<script>
setup({
  "allow_uncaught_exception": true,
})

test(function() {
  assert_throws(new TypeError(), function() { document.dispatchEvent(null) })
}, "Calling dispatchEvent(null).")

for (var alias in aliases) {
  test(function() {
    var e = document.createEvent(alias)
    assert_equals(e.type, "", "Event type should be empty string before initialization")
    assert_throws("InvalidStateError", function() { document.dispatchEvent(e) })
  }, "If the event's initialized flag is not set, an InvalidStateError must be thrown (" + alias + ").")
}

var dispatch_dispatch = async_test("If the event's dispatch flag is set, an InvalidStateError must be thrown.")
dispatch_dispatch.step(function() {
  var e = document.createEvent("Event")
  e.initEvent("type", false, false)

  var target = document.createElement("div")
  target.addEventListener("type", dispatch_dispatch.step_func(function() {
    assert_throws("InvalidStateError", function() {
      target.dispatchEvent(e)
    })
    assert_throws("InvalidStateError", function() {
      document.dispatchEvent(e)
    })
  }), false)

  assert_equals(target.dispatchEvent(e), true, "dispatchEvent must return true")

  dispatch_dispatch.done()
})

test(function() {
  // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17713
  // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17714

  var e = document.createEvent("Event")
  e.initEvent("type", false, false)

  var called = []

  var target = document.createElement("div")
  target.addEventListener("type", function() {
    called.push("First")
    throw new Error()
  }, false)

  target.addEventListener("type", function() {
    called.push("Second")
  }, false)

  assert_equals(target.dispatchEvent(e), true, "dispatchEvent must return true")
  assert_array_equals(called, ["First", "Second"],
                      "Should have continued to call other event listeners")
}, "Exceptions from event listeners must not be propagated.")

async_test(function() {
  var results = []
  var outerb = document.createElement("b")
  var middleb = outerb.appendChild(document.createElement("b"))
  var innerb = middleb.appendChild(document.createElement("b"))
  outerb.addEventListener("x", this.step_func(function() {
    middleb.addEventListener("x", this.step_func(function() {
      results.push("middle")
    }), true)
    results.push("outer")
  }), true)
  innerb.dispatchEvent(new Event("x"))
  assert_array_equals(results, ["outer", "middle"])
  this.done()
}, "Event listeners added during dispatch should be called");

async_test(function() {
  var results = []
  var b = document.createElement("b")
  b.addEventListener("x", this.step_func(function() {
    results.push(1)
  }), true)
  b.addEventListener("x", this.step_func(function() {
    results.push(2)
  }), false)
  b.addEventListener("x", this.step_func(function() {
    results.push(3)
  }), true)
  b.dispatchEvent(new Event("x"))
  assert_array_equals(results, [1, 2, 3])
  this.done()
}, "Event listeners should be called in order of addition")
</script>