"use strict";

const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

const { AddonTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/AddonTestUtils.sys.mjs"
);

const { ExtensionTestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
);

const { createHttpServer } = AddonTestUtils;

// Force ServiceWorkerRegistrar to init by calling do_get_profile.
// (This has to be called before AddonTestUtils.init, because it does
// also call do_get_profile internally but it doesn't notify
// profile-after-change).
do_get_profile(true);

AddonTestUtils.init(this);
ExtensionTestUtils.init(this);

const server = createHttpServer({ hosts: ["example.com"] });
server.registerPathHandler("/dummy", (request, response) => {
  response.setStatusLine(request.httpVersion, 200, "OK");
  response.setHeader("Content-Type", "text/html", false);
  response.write("dummy page");
});

add_setup(() => {
  info("Making sure Services.io.offline is true");
  // Explicitly setting Services.io.offline to true makes this test able
  // to hit on Desktop builds the same issue that test_ext_cache_api.js
  // was hitting on Android builds (Bug 1844825).
  Services.io.offline = true;
});

// Regression test derived from Bug 1844825.
add_task(async function test_fetch_request_from_ext_shared_worker() {
  if (!WebExtensionPolicy.useRemoteWebExtensions) {
    // Ensure RemoteWorkerService has been initialized in the main
    // process.
    Services.obs.notifyObservers(null, "profile-after-change");
  }

  const background = async function () {
    const testUrl = `http://example.com/dummy`;
    const worker = new SharedWorker("worker.js");
    const { data: result } = await new Promise(resolve => {
      worker.port.onmessage = resolve;
      worker.port.postMessage(["worker-fetch-test", testUrl]);
    });

    browser.test.sendMessage("test-sharedworker-fetch:done", result);
  };

  const extension = ExtensionTestUtils.loadExtension({
    background,
    manifest: { permissions: ["http://example.com/*"] },
    files: {
      "worker.js": function () {
        self.onconnect = evt => {
          const port = evt.ports[0];
          port.onmessage = async evt => {
            let result = {};
            let message;
            try {
              const [msg, url] = evt.data;
              message = msg;
              const response = await fetch(url);
              dump(`fetch call resolved: ${response}\n`);
              result.fetchResolvesTo = `${response}`;
            } catch (err) {
              dump(`fetch call rejected: ${err}\n`);
              result.error = err.name;
              throw err;
            } finally {
              port.postMessage([`${message}:result`, result]);
            }
          };
        };
      },
    },
  });

  await extension.startup();
  const result = await extension.awaitMessage("test-sharedworker-fetch:done");
  if (Services.io.offline && WebExtensionPolicy.useRemoteWebExtensions) {
    // If the network is offline and the extensions are running in the
    // child extension process, expect the fetch call to be rejected
    // with an TypeError.
    Assert.deepEqual(
      ["worker-fetch-test:result", { error: "TypeError" }],
      result,
      "fetch should have been rejected with an TypeError"
    );
  } else {
    // If the network is not offline or the extension are running in the
    // parent process, we expect the fetch call to resolve to a Response.
    Assert.deepEqual(
      ["worker-fetch-test:result", { fetchResolvesTo: "[object Response]" }],
      result,
      "fetch should have been resolved to a Response instance"
    );
  }
  await extension.unload();
});
