"use strict";

// Testing if 2 child processes are correctly managed when they both try to do
// an SW update.

const BASE_URI =
  "http://mochi.test:8888/browser/dom/serviceworkers/test/isolated/multi-e10s-update/";

add_task(async function test_update() {
  info("Setting the prefs to having multi-e10s enabled");
  await SpecialPowers.pushPrefEnv({
    set: [
      ["dom.ipc.processCount", 4],
      ["dom.serviceWorkers.enabled", true],
      ["dom.serviceWorkers.testing.enabled", true],
    ],
  });

  let url = BASE_URI + "file_multie10s_update.html";

  info("Creating the first tab...");
  let tab1 = BrowserTestUtils.addTab(gBrowser, url);
  let browser1 = gBrowser.getBrowserForTab(tab1);
  await BrowserTestUtils.browserLoaded(browser1);

  info("Creating the second tab...");
  let tab2 = BrowserTestUtils.addTab(gBrowser, url);
  let browser2 = gBrowser.getBrowserForTab(tab2);
  await BrowserTestUtils.browserLoaded(browser2);

  let sw = BASE_URI + "server_multie10s_update.sjs";

  info("Let's make sure there are no existing registrations...");
  let existingCount = await SpecialPowers.spawn(browser1, [], async function() {
    const regs = await content.navigator.serviceWorker.getRegistrations();
    return regs.length;
  });
  is(existingCount, 0, "Previous tests should have cleaned up!");

  info("Let's start the test...");
  /* eslint-disable no-shadow */
  let status = await SpecialPowers.spawn(browser1, [sw], function(url) {
    // Let the SW be served immediately once by triggering a relase immediately.
    // We don't need to await this.  We do this from a frame script because
    // it has fetch.
    content.fetch(url + "?release");

    // Registration of the SW
    return (
      content.navigator.serviceWorker
        .register(url)

        // Activation
        .then(function(r) {
          content.registration = r;
          return new content.window.Promise(resolve => {
            let worker = r.installing;
            worker.addEventListener("statechange", () => {
              if (worker.state === "installed") {
                resolve(true);
              }
            });
          });
        })

        // Waiting for the result.
        .then(() => {
          return new content.window.Promise(resolveResults => {
            // Once both updates have been issued and a single update has failed, we
            // can tell the .sjs to release a single copy of the SW script.
            let updateCount = 0;
            const uc = new content.window.BroadcastChannel("update");
            // This promise tracks the updates tally.
            const updatesIssued = new Promise(resolveUpdatesIssued => {
              uc.onmessage = function(e) {
                updateCount++;
                console.log("got update() number", updateCount);
                if (updateCount === 2) {
                  resolveUpdatesIssued();
                }
              };
            });

            let results = [];
            const rc = new content.window.BroadcastChannel("result");
            // This promise resolves when an update has failed.
            const oneFailed = new Promise(resolveOneFailed => {
              rc.onmessage = function(e) {
                console.log("got result", e.data);
                results.push(e.data);
                if (e.data === 1) {
                  resolveOneFailed();
                }
                if (results.length != 2) {
                  return;
                }

                resolveResults(results[0] + results[1]);
              };
            });

            Promise.all([updatesIssued, oneFailed]).then(() => {
              console.log("releasing update");
              content.fetch(url + "?release").catch(ex => {
                console.error("problem releasing:", ex);
              });
            });

            // Let's inform the tabs.
            const sc = new content.window.BroadcastChannel("start");
            sc.postMessage("go");
          });
        })
    );
  });
  /* eslint-enable no-shadow */

  if (status == 0) {
    ok(false, "both succeeded. This is wrong.");
  } else if (status == 1) {
    ok(true, "one succeded, one failed. This is good.");
  } else {
    ok(false, "both failed. This is definitely wrong.");
  }

  // let's clean up the registration and get the fetch count.  The count
  // should be 1 for the initial fetch and 1 for the update.
  /* eslint-disable no-shadow */
  const count = await SpecialPowers.spawn(browser1, [sw], async function(url) {
    // We stored the registration on the frame script's wrapper, hence directly
    // accesss content without using wrappedJSObject.
    await content.registration.unregister();
    const { count } = await content
      .fetch(url + "?get-and-clear-count")
      .then(r => r.json());
    return count;
  });
  /* eslint-enable no-shadow */
  is(count, 2, "SW should have been fetched only twice");

  BrowserTestUtils.removeTab(tab1);
  BrowserTestUtils.removeTab(tab2);
});
