/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This tests that the basic auth dialog can not be used for DOS attacks
// and that the protections are reset on user-initiated navigation/reload.

let promptModalType = Services.prefs.getIntPref("prompts.modalType.httpAuth");

function promiseAuthWindowShown() {
  return PromptTestUtils.handleNextPrompt(
    window,
    { modalType: promptModalType, promptType: "promptUserAndPass" },
    { buttonNumClick: 1 }
  );
}

add_task(async function test() {
  await BrowserTestUtils.withNewTab("https://example.com", async function(
    browser
  ) {
    let cancelDialogLimit = Services.prefs.getIntPref(
      "prompts.authentication_dialog_abuse_limit"
    );

    let authShown = promiseAuthWindowShown();
    let browserLoaded = BrowserTestUtils.browserLoaded(browser);
    BrowserTestUtils.loadURI(
      browser,
      "https://example.com/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs"
    );
    await authShown;
    ok(true, "Seen dialog number 1");
    await browserLoaded;
    ok(true, "Loaded document number 1");

    // Reload the document a bit more often than should be allowed.
    // As long as we're in the acceptable range we should receive
    // auth prompts, otherwise we should not receive them and the
    // page should just load.
    // We've already seen the dialog once, hence we start the loop at 1.
    for (let i = 1; i < cancelDialogLimit + 2; i++) {
      if (i < cancelDialogLimit) {
        authShown = promiseAuthWindowShown();
      }
      browserLoaded = BrowserTestUtils.browserLoaded(browser);
      SpecialPowers.spawn(browser, [], function() {
        content.document.location.reload();
      });
      if (i < cancelDialogLimit) {
        await authShown;
        ok(true, `Seen dialog number ${i + 1}`);
      }
      await browserLoaded;
      ok(true, `Loaded document number ${i + 1}`);
    }

    let reloadButton = document.getElementById("reload-button");
    await TestUtils.waitForCondition(
      () => !reloadButton.hasAttribute("disabled")
    );

    // Verify that we can click the reload button to reset the counter.
    authShown = promiseAuthWindowShown();
    browserLoaded = BrowserTestUtils.browserLoaded(browser);
    reloadButton.click();
    await authShown;
    ok(true, "Seen dialog number 1");
    await browserLoaded;
    ok(true, "Loaded document number 1");

    // Now check loading subresources with auth on the page.
    browserLoaded = BrowserTestUtils.browserLoaded(browser);
    BrowserTestUtils.loadURI(browser, "https://example.com");
    await browserLoaded;

    // We've already seen the dialog once, hence we start the loop at 1.
    for (let i = 1; i < cancelDialogLimit + 2; i++) {
      if (i < cancelDialogLimit) {
        authShown = promiseAuthWindowShown();
      }

      let iframeLoaded = SpecialPowers.spawn(browser, [], async function() {
        let doc = content.document;
        let iframe = doc.createElement("iframe");
        doc.body.appendChild(iframe);
        let loaded = new Promise(resolve => {
          iframe.addEventListener(
            "load",
            function(e) {
              resolve();
            },
            { once: true }
          );
        });
        iframe.src =
          "https://example.com/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs";
        await loaded;
      });

      if (i < cancelDialogLimit) {
        await authShown;
        ok(true, `Seen dialog number ${i + 1}`);
      }

      await iframeLoaded;
      ok(true, `Loaded iframe number ${i + 1}`);
    }

    // Verify that third party subresources can not spawn new auth dialogs.
    let iframeLoaded = SpecialPowers.spawn(browser, [], async function() {
      let doc = content.document;
      let iframe = doc.createElement("iframe");
      doc.body.appendChild(iframe);
      let loaded = new Promise(resolve => {
        iframe.addEventListener(
          "load",
          function(e) {
            resolve();
          },
          { once: true }
        );
      });
      iframe.src =
        "https://example.org/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs";
      await loaded;
    });

    await iframeLoaded;
    ok(true, "Loaded a third party iframe without showing the auth dialog");

    // Verify that pressing enter in the urlbar also resets the counter.
    authShown = promiseAuthWindowShown();
    browserLoaded = BrowserTestUtils.browserLoaded(browser);
    gURLBar.value =
      "https://example.com/browser/toolkit/components/passwordmgr/test/browser/authenticate.sjs";
    gURLBar.focus();
    EventUtils.synthesizeKey("KEY_Enter");
    await authShown;
    await browserLoaded;
  });
});
