/*
 * Test the password manager context menu.
 */

"use strict";

const TEST_ORIGIN = "https://example.com";

// Test with a page that only has a form within an iframe, not in the top-level document
const IFRAME_PAGE_PATH =
  "/browser/toolkit/components/passwordmgr/test/browser/form_basic_iframe.html";

/**
 * Initialize logins needed for the tests and disable autofill
 * for login forms for easier testing of manual fill.
 */
add_task(async function test_initialize() {
  Services.prefs.setBoolPref("signon.autofillForms", false);
  Services.prefs.setBoolPref("signon.schemeUpgrades", true);
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("signon.autofillForms");
    Services.prefs.clearUserPref("signon.schemeUpgrades");
  });
  await Services.logins.addLogins(loginList());
});

/**
 * Check if the password field is correctly filled when it's in an iframe.
 */
add_task(async function test_context_menu_iframe_fill() {
  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: TEST_ORIGIN + IFRAME_PAGE_PATH,
    },
    async function (browser) {
      await openPasswordContextMenu(
        browser,
        "#form-basic-password",
        () => true,
        browser.browsingContext.children[0],
        true
      );

      let popupMenu = document.getElementById("fill-login-popup");

      // Stores the original value of username
      function promiseFrameInputValue(name) {
        return SpecialPowers.spawn(
          browser.browsingContext.children[0],
          [name],
          function (inputname) {
            return content.document.getElementById(inputname).value;
          }
        );
      }
      let usernameOriginalValue = await promiseFrameInputValue(
        "form-basic-username"
      );

      // Execute the command of the first login menuitem found at the context menu.
      let firstLoginItem =
        popupMenu.getElementsByClassName("context-login-item")[0];
      Assert.ok(firstLoginItem, "Found the first login item");

      await TestUtils.waitForTick();

      Assert.ok(
        BrowserTestUtils.isVisible(firstLoginItem),
        "First login menuitem is visible"
      );

      info("Clicking on the firstLoginItem");
      // click on the login item to fill the password field, triggering an "input" event
      popupMenu.activateItem(firstLoginItem);

      let passwordValue = await TestUtils.waitForCondition(async () => {
        let value = await promiseFrameInputValue("form-basic-password");
        return value;
      });

      // Find the used login by it's username.
      let login = getLoginFromUsername(firstLoginItem.label);
      Assert.equal(
        login.password,
        passwordValue,
        "Password filled and correct."
      );

      let usernameNewValue = await promiseFrameInputValue(
        "form-basic-username"
      );
      Assert.equal(
        usernameOriginalValue,
        usernameNewValue,
        "Username value was not changed."
      );

      let contextMenu = document.getElementById("contentAreaContextMenu");
      contextMenu.hidePopup();

      await cleanupDoorhanger();
      await cleanupPasswordNotifications();
    }
  );
});

/**
 * Check that the login context menu items don't appear on an opaque origin.
 */
add_task(async function test_context_menu_iframe_sandbox() {
  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: TEST_ORIGIN + IFRAME_PAGE_PATH,
    },
    async function (browser) {
      info("Opening context menu for test_context_menu_iframe_sandbox");
      await openPasswordContextMenu(
        browser,
        "#form-basic-password",
        function checkDisabled() {
          info("checkDisabled for test_context_menu_iframe_sandbox");
          let popupHeader = document.getElementById("fill-login");
          Assert.ok(
            popupHeader.hidden,
            "Check that the Fill Login menu item is hidden"
          );
          return false;
        },
        browser.browsingContext.children[1]
      );
      let contextMenu = document.getElementById("contentAreaContextMenu");
      contextMenu.hidePopup();
    }
  );
});

/**
 * Check that the login context menu item appears for sandbox="allow-same-origin"
 */
add_task(async function test_context_menu_iframe_sandbox_same_origin() {
  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: TEST_ORIGIN + IFRAME_PAGE_PATH,
    },
    async function (browser) {
      await openPasswordContextMenu(
        browser,
        "#form-basic-password",
        function checkDisabled() {
          let popupHeader = document.getElementById("fill-login");
          Assert.ok(
            !popupHeader.hidden,
            "Check that the Fill Login menu item is visible"
          );
          Assert.ok(
            !popupHeader.disabled,
            "Check that the Fill Login menu item is disabled"
          );
          return false;
        },
        browser.browsingContext.children[2]
      );

      let contextMenu = document.getElementById("contentAreaContextMenu");
      contextMenu.hidePopup();
    }
  );
});

/**
 * Search for a login by it's username.
 *
 * Only unique login/origin combinations should be used at this test.
 */
function getLoginFromUsername(username) {
  return loginList().find(login => login.username == username);
}

/**
 * List of logins used for the test.
 *
 * We should only use unique usernames in this test,
 * because we need to search logins by username. There is one duplicate u+p combo
 * in order to test de-duping in the menu.
 */
function loginList() {
  return [
    LoginTestUtils.testData.formLogin({
      origin: "https://example.com",
      formActionOrigin: "https://example.com",
      username: "username",
      password: "password",
    }),
    // Same as above but HTTP in order to test de-duping.
    LoginTestUtils.testData.formLogin({
      origin: "http://example.com",
      formActionOrigin: "http://example.com",
      username: "username",
      password: "password",
    }),
    LoginTestUtils.testData.formLogin({
      origin: "http://example.com",
      formActionOrigin: "http://example.com",
      username: "username1",
      password: "password1",
    }),
    LoginTestUtils.testData.formLogin({
      origin: "https://example.com",
      formActionOrigin: "https://example.com",
      username: "username2",
      password: "password2",
    }),
    LoginTestUtils.testData.formLogin({
      origin: "http://example.org",
      formActionOrigin: "http://example.org",
      username: "username-cross-origin",
      password: "password-cross-origin",
    }),
  ];
}
