<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>Test for primary password</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript" src="pwmgr_common.js"></script>
  <script type="text/javascript" src="../../../prompts/test/prompt_common.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Login Manager test: primary password.

<p id="display"></p>

<div id="content" style="display: none">
</div>

<pre id="test">
<script class="testbody" type="text/javascript">
"use strict";

// Force parent to not look for tab-modal prompts, as they're not used for auth prompts.
modalType = Ci.nsIPrompt.MODAL_TYPE_WINDOW;

const exampleCom = "https://example.com/tests/toolkit/components/passwordmgr/test/mochitest/";
const exampleOrg = "https://example.org/tests/toolkit/components/passwordmgr/test/mochitest/";

const win = window.open("about:blank");
SimpleTest.registerCleanupFunction(() => win.close());

add_setup(async () => {
  await setStoredLoginsAsync(
    ["http://mochi.test:8888", "http://mochi.test:8888", null, "testuser", "testpass", "uname", "pword"],
    ["https://example.com", "https://example.com", null, "user1", "pass1", "uname", "pword"],
    ["https://example.org", "https://example.org", null, "user2", "pass2", "uname", "pword"]
  );
  ok(await isLoggedIn(), "should be initially logged in (no PP)");
  enablePrimaryPassword();
  ok(!await isLoggedIn(), "should be logged out after setting PP");
});

add_task(async function test_1() {
  // Trigger a MP prompt via the API
  const state = {
    msg: "Please enter your Primary Password.",
    title: "the title",
    textValue: "",
    passValue: "",
    iconClass: "authentication-icon question-icon",
    titleHidden: true,
    textHidden: true,
    passHidden: false,
    checkHidden: true,
    checkMsg: "",
    checked: false,
    focused: "passField",
    defButton: "button0",
  };
  const action = {
    buttonClick: "ok",
    passField: LoginTestUtils.primaryPassword.primaryPassword,
  };
  const promptDone = handlePrompt(state, action);

  const logins = await LoginManager.getAllLogins();

  await promptDone;
  is(logins.length, 3, "expected number of logins");

  ok(await isLoggedIn(), "should be logged in after MP prompt");
  logoutPrimaryPassword();
  ok(!await isLoggedIn(), "should be logged out");
});

add_task(async function test_2() {
  // Try again but click cancel.
  const state = {
    msg: "Please enter your Primary Password.",
    title: "the title",
    textValue: "",
    passValue: "",
    iconClass: "authentication-icon question-icon",
    titleHidden: true,
    textHidden: true,
    passHidden: false,
    checkHidden: true,
    checkMsg: "",
    checked: false,
    focused: "passField",
    defButton: "button0",
  };
  const action = {
    buttonClick: "cancel",
  };
  const promptDone = handlePrompt(state, action);

  const logins = await LoginManager.getAllLogins().catch(() => {});
  await promptDone;
  is(logins, undefined, "shouldn't have gotten logins");
  ok(!await isLoggedIn(), "should still be logged out");
});

add_task(async function test_3() {
  const state = {
    msg: "Please enter your Primary Password.",
    title: "the title",
    textValue: "",
    passValue: "",
    iconClass: "authentication-icon question-icon",
    titleHidden: true,
    textHidden: true,
    passHidden: false,
    checkHidden: true,
    checkMsg: "",
    checked: false,
    focused: "passField",
    defButton: "button0",
  };
  const action = {
    buttonClick: "ok",
    passField: LoginTestUtils.primaryPassword.primaryPassword,
  };
  const promptDone = handlePrompt(state, action);

  const fillPromise = promiseFormsProcessed();

  info("Load a single window to trigger a MP");
  await SimpleTest.promiseFocus(win, true);
  win.location = exampleCom + "subtst_primary_pass.html";

  await promptDone;
  info("promptDone");
  await fillPromise;
  info("filled");

  // check contents of win fields

  await SpecialPowers.spawn(win, [], function() {
    const u = this.content.document.getElementById("userfield");
    const p = this.content.document.getElementById("passfield");
    Assert.equal(u.value, "user1", "checking expected user to have been filled in");
    Assert.equal(p.value, "pass1", "checking expected pass to have been filled in");
    u.value = "";
    p.value = "";
  });

  ok(await isLoggedIn(), "should be logged in");
  logoutPrimaryPassword();
  ok(!await isLoggedIn(), "should be logged out");
});

add_task(async function test_4() {
  const state = {
    msg: "Please enter your Primary Password.",
    title: "the title",
    textValue: "",
    passValue: "",
    iconClass: "authentication-icon question-icon",
    titleHidden: true,
    textHidden: true,
    passHidden: false,
    checkHidden: true,
    checkMsg: "",
    checked: false,
    focused: "passField",
    defButton: "button0",
  };
  let action = {
    buttonClick: "none",
  };
  const promptDone = handlePrompt(state, action);

  // first part of loading 2 MP-triggering windows
  await SimpleTest.promiseFocus(win);
  win.location = exampleOrg + "subtst_primary_pass.html";
  // The MP prompt is open but don't take any action yet.
  await promptDone;

  // check contents of win fields
  await SpecialPowers.spawn(win, [], function() {
    const u = this.content.document.getElementById("userfield");
    const p = this.content.document.getElementById("passfield");
    Assert.equal(u.value, "", "checking expected empty user");
    Assert.equal(p.value, "", "checking expected empty pass");
  });

  ok(!await isLoggedIn(), "should be logged out");

  // XXX check that there's 1 MP window open

  // Load a second login form in an iframe
  // This should detect that there's already a pending MP prompt, and not
  // put up a second one.

  // Since the Primary Password prompt is open, we can't focus another tab
  // to load the second form. Instead, we load the same form into an iframe.
  const url = exampleOrg + "subtst_primary_pass.html";
  await SpecialPowers.spawn(win, [url], async function(urlF) {
    const iframe = this.content.document.querySelector("iframe");
    const loadPromise = new Promise(resolve => {
      iframe.addEventListener("load", function onload() {
        resolve();
      }, { once: true });
    });
    // Use the same origin as the top level to ensure we would autofill
    // if we could (we don't fill in cross-origin iframes).
    iframe.src = urlF;
    await loadPromise;
  });

  // We can't use promiseFormsProcessed* here, because _fillForm doesn't
  // run if Primary Password is locked.
  await new Promise(resolve => {
    // Testing a negative, wait a little to give the login manager a chance to
    // (incorrectly) fill in the form.  Note, we cannot use setTimeout()
    // here because the modal window suspends all window timers.  Instead we
    // must use a chrome script to use nsITimer directly.
    const chromeURL = SimpleTest.getTestFileURL("chrome_timeout.js");
    const script = SpecialPowers.loadChromeScript(chromeURL);
    script.addMessageListener("ready", _ => {
      script.sendAsyncMessage("setTimeout", { delay: 500 });
    });
    script.addMessageListener("timeout", resolve);
  });

  // iframe should load without having triggered a MP prompt (because one
  // is already waiting)

  // check contents of iframe fields
  await SpecialPowers.spawn(win, [], function() {
    const iframe = this.content.document.querySelector("iframe");
    const frameDoc = iframe.contentDocument;
    const u = frameDoc.getElementById("userfield");
    const p = frameDoc.getElementById("passfield");
    Assert.equal(u.value, "", "checking expected empty user");
    Assert.equal(p.value, "", "checking expected empty pass");
  });

  // XXX check that there's 1 MP window open
  ok(!await isLoggedIn(), "should be logged out");

  // Ok, now enter the MP. The MP prompt is already up.
  const fillPromise = promiseFormsProcessed(2);

  // fill existing MP dialog with MP.
  action = {
    buttonClick: "ok",
    passField: LoginTestUtils.primaryPassword.primaryPassword,
  };
  await handlePrompt(state, action);
  await fillPromise;

  // We shouldn't have to worry about win's load event racing with
  // filling of the iframe's data. We notify observers synchronously, so
  // the iframe's observer will process the iframe before win even finishes
  // processing the form.
  ok(await isLoggedIn(), "should be logged in");

  // check contents of win fields
  await SpecialPowers.spawn(win, [], function() {
    const u = this.content.document.getElementById("userfield");
    const p = this.content.document.getElementById("passfield");
    Assert.equal(u.value, "user2", "checking expected user to have been filled in");
    Assert.equal(p.value, "pass2", "checking expected pass to have been filled in");

    // clearing fields to not cause a submission when the next document is loaded
    u.value = "";
    p.value = "";
  });

  // check contents of iframe fields
  await SpecialPowers.spawn(win, [], function() {
    const iframe = this.content.document.querySelector("iframe");
    const frameDoc = iframe.contentDocument;
    const u = frameDoc.getElementById("userfield");
    const p = frameDoc.getElementById("passfield");
    Assert.equal(u.value, "user2", "checking expected user to have been filled in");
    Assert.equal(p.value, "pass2", "checking expected pass to have been filled in");

    // clearing fields to not cause a submission when the next document is loaded
    u.value = "";
    p.value = "";
  });
});

// XXX do a test5ABC with clicking cancel?

SimpleTest.registerCleanupFunction(function finishTest() {
  disablePrimaryPassword();
});

</script>
</pre>
</body>
</html>
