/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

let gServer;
let gServerURL;
let gRedirectedURL;
let gExpectedText;
let gNavigated = false;
let gExpectedURL;

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

const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService(
  Ci.nsINativeDNSResolverOverride
);

let gCaptivePortalState = "captive-replace";

add_setup(async function () {
  gOverride.addIPOverride("test1.example.com", "127.0.0.1");
  gOverride.addIPOverride("example.net", "127.0.0.1");
  gServer = new HttpServer();
  gServer.start(-1);
  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
  gServerURL = `http://test1.example.com:${gServer.identity.primaryPort}/`;
  // eslint-disable-next-line @microsoft/sdl/no-insecure-url
  gRedirectedURL = `http://example.net:${gServer.identity.primaryPort}/redir`;
  gServer.identity.add(
    "http",
    "test1.example.com",
    gServer.identity.primaryPort
  );
  gServer.identity.add("http", "example.net", gServer.identity.primaryPort);
  gServer.registerPathHandler("/", (request, response) => {
    if (gCaptivePortalState == "captive-replace") {
      response.setStatusLine(request.httpVersion, 200, "OK");
      response.setHeader("Content-Type", "text/html");
      const BODY = `captive muahahah`;
      response.bodyOutputStream.write(BODY, BODY.length);
      return;
    }

    if (gCaptivePortalState == "captive-redirect") {
      response.setStatusLine(request.httpVersion, 302, "Moved Temporarily");
      // eslint-disable-next-line @microsoft/sdl/no-insecure-url
      response.setHeader("Location", gRedirectedURL);
      response.write("redirecting");
    }
  });
  gServer.registerPathHandler("/redir", (request, response) => {
    response.setStatusLine(request.httpVersion, 200, "OK");
    response.setHeader("Content-Type", "text/html");
    const BODY = `redirected`;
    response.bodyOutputStream.write(BODY, BODY.length);
  });
  registerCleanupFunction(async () => {
    await gServer.stop();
    gOverride.clearOverrides();
  });

  await SpecialPowers.pushPrefEnv({
    set: [
      ["test.wait300msAfterTabSwitch", true],
      ["captivedetect.canonicalURL", gServerURL],
      ["captivedetect.canonicalContent", "stuff"],
      ["network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY],
      ["network.proxy.no_proxies_on", "test1.example.com,example.net"],
    ],
  });
});

async function openAndCheck() {
  // Open a second window in the background. Later, we'll check that
  // when we click the button to open the captive portal tab, the tab
  // only opens in the active window and not in the background one.
  let secondWindow = await openWindowAndWaitForFocus();
  await SimpleTest.promiseFocus(window);

  await portalDetected();

  // Check that we didn't open anything in the background window.
  ensureNoPortalTab(secondWindow);

  let tab = await openCaptivePortalErrorTab();
  let browser = tab.linkedBrowser;
  let portalTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, gExpectedURL);

  await SpecialPowers.spawn(browser, [], async () => {
    let doc = content.document;
    let loginButton = doc.getElementById("openPortalLoginPageButton");
    await ContentTaskUtils.waitForCondition(
      () => ContentTaskUtils.isVisible(loginButton),
      "Captive portal error page UI is visible"
    );

    if (!Services.focus.focusedElement == loginButton) {
      await ContentTaskUtils.waitForEvent(loginButton, "focus");
    }

    Assert.ok(true, "openPortalLoginPageButton has focus");
    info("Clicking the Open Login Page button");
    await EventUtils.synthesizeMouseAtCenter(loginButton, {}, content);
  });

  let portalTab = await portalTabPromise;
  await SpecialPowers.spawn(
    portalTab.linkedBrowser,
    [gExpectedText],
    async text => {
      Assert.equal(content.document.body.innerText, text);
    }
  );

  is(
    gBrowser.selectedTab,
    portalTab,
    "Login page should be open in a new foreground tab."
  );

  // Check that we didn't open anything in the background window.
  ensureNoPortalTab(secondWindow);

  // Make sure clicking the "Open Login Page" button again focuses the existing portal tab.
  await BrowserTestUtils.switchTab(gBrowser, tab);
  // Passing an empty function to BrowserTestUtils.switchTab lets us wait for an arbitrary
  // tab switch.
  portalTabPromise = BrowserTestUtils.switchTab(gBrowser, () => {});
  await SpecialPowers.spawn(browser, [], async () => {
    info("Clicking the Open Login Page button.");
    let loginButton = content.document.getElementById(
      "openPortalLoginPageButton"
    );
    await EventUtils.synthesizeMouseAtCenter(loginButton, {}, content);
  });

  info("Opening captive portal login page");
  let portalTab2 = await portalTabPromise;
  is(portalTab2, portalTab, "The existing portal tab should be focused.");

  // Check that we didn't open anything in the background window.
  ensureNoPortalTab(secondWindow);

  let portalTabClosing = BrowserTestUtils.waitForTabClosing(portalTab);
  let errorTabReloaded = BrowserTestUtils.waitForErrorPage(browser);

  Services.obs.notifyObservers(null, "captive-portal-login-success");
  info("closing page");
  if (gNavigated) {
    // Since it navigated, the tab won't be automatically closed.
    await BrowserTestUtils.removeTab(portalTab);
  }

  await portalTabClosing;

  info(
    "Waiting for error tab to be reloaded after the captive portal was freed."
  );
  await errorTabReloaded;
  await SpecialPowers.spawn(browser, [], () => {
    let doc = content.document;
    ok(
      !doc.body.classList.contains("captiveportal"),
      "Captive portal error page UI is not visible."
    );
  });

  await BrowserTestUtils.removeTab(tab);
  await BrowserTestUtils.closeWindow(secondWindow);
}

add_task(async function checkNoTRRForTab() {
  info("Check that the captive portal login tab does not use TRR");
  gExpectedText = `captive muahahah`;
  gExpectedURL = gServerURL;

  await openAndCheck();
});

add_task(async function checkNoTRRForTabWithRedirect() {
  gCaptivePortalState = "captive-redirect";
  gExpectedText = "redirected";
  gExpectedURL = gRedirectedURL;
  gNavigated = true;
  info("Check that the captive portal login tab does not use TRR");

  await openAndCheck();
});
