<!DOCTYPE HTML>
<html>
<!--
  Test that an unresolved respondWith promise will reset the channel when
  the service worker is terminated due to idling, and that appropriate error
  messages are logged for both the termination of the serice worker and the
  resetting of the channel.
  -->
<head>
  <title>Test for Bug 1188545</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="error_reporting_helpers.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188545">Mozilla Bug 118845</a>
<p id="display"></p>
<div id="content" style="display: none">

</div>
<pre id="test">
</pre>

<script src="utils.js"></script>
<script class="testbody" type="text/javascript">
// (This doesn't really need to be its own task, but it allows the actual test
// case to be self-contained.)
add_task(function setupPrefs() {
  return SpecialPowers.pushPrefEnv({"set": [
    ["dom.serviceWorkers.enabled", true],
    ["dom.serviceWorkers.testing.enabled", true],
  ]});
});

add_task(async function grace_timeout_termination_with_interrupted_intercept() {
  // Setup timeouts so that the service worker will go into grace timeout after
  // a zero-length idle timeout.
  await SpecialPowers.pushPrefEnv({"set": [
    ["dom.serviceWorkers.idle_timeout", 0],
    ["dom.serviceWorkers.idle_extended_timeout", 299999]]});

  let registration = await navigator.serviceWorker.register(
    "unresolved_fetch_worker.js", { scope: "./"} );
  await waitForState(registration.installing, "activated");
  ok(navigator.serviceWorker.controller, "Controlled"); // double check!

  // We want to make sure the SW is active and processing the fetch before we
  // try and kill it.  It sends us a message when it has done so.
  let waitForFetchActive = new Promise((resolve) => {
    navigator.serviceWorker.onmessage = resolve;
  });

  // Issue a fetch which the SW will respondWith() a never resolved promise.
  // The fetch, however, will terminate when the SW is killed, so check that.
  let hangingFetch = fetch("does_not_exist.html")
    .then(() => { ok(false, "should have rejected "); },
          () => { ok(true, "hung fetch terminates when worker dies"); });

  await waitForFetchActive;

  let expectedMessage = expect_console_message(
    // Termination error
    "ServiceWorkerGraceTimeoutTermination",
    [make_absolute_url("./")],
    // The interception failure error generated by the RespondWithHandler
    // destructor when it notices it didn't get a response before being
    // destroyed.  It logs via the intercepted channel nsIConsoleReportCollector
    // that is eventually flushed to our document and its console.
    "InterceptionFailedWithURL",
    [make_absolute_url("does_not_exist.html")]
  );

  // Zero out the grace timeout too so the worker will get terminated after two
  // zero-length timer firings.  Note that we need to do something to get the
  // SW to renew its keepalive for this to actually cause the timers to be
  // rescheduled...
  await SpecialPowers.pushPrefEnv({"set": [
    ["dom.serviceWorkers.idle_extended_timeout", 0]]});
  // ...which we do by postMessaging it.
  navigator.serviceWorker.controller.postMessage("doomity doom doom");

  // Now wait for signs that the worker was terminated by the fetch failing.
  await hangingFetch;

  // The worker should now be dead and the error logged, wait/assert.
  await wait_for_expected_message(expectedMessage);

  // roll back all of our test case specific preferences and otherwise cleanup
  await SpecialPowers.popPrefEnv();
  await SpecialPowers.popPrefEnv();
  await registration.unregister();
});
</script>
</body>
</html>
