/* 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/. */

"use strict";

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

function arrivingHereIsBad(aResult) {
  ok(false, "Bad result! Received a: " + aResult);
}

function expectNotAllowedError(aResult) {
  let expected = "NotAllowedError";
  is(aResult.slice(0, expected.length), expected, `Expecting a ${expected}`);
}

function promiseMakeCredential(tab) {
  return ContentTask.spawn(tab.linkedBrowser, null, async function () {
    const cose_alg_ECDSA_w_SHA256 = -7;

    let publicKey = {
      rp: { id: content.document.domain, name: "none", icon: "none" },
      user: {
        id: new Uint8Array(),
        name: "none",
        icon: "none",
        displayName: "none",
      },
      challenge: content.crypto.getRandomValues(new Uint8Array(16)),
      timeout: 5000, // the minimum timeout is actually 15 seconds
      pubKeyCredParams: [{ type: "public-key", alg: cose_alg_ECDSA_w_SHA256 }],
    };

    return content.navigator.credentials.create({ publicKey });
  });
}

function promiseGetAssertion(tab) {
  return ContentTask.spawn(tab.linkedBrowser, null, async function () {
    let newCredential = {
      type: "public-key",
      id: content.crypto.getRandomValues(new Uint8Array(16)),
      transports: ["usb"],
    };

    let publicKey = {
      challenge: content.crypto.getRandomValues(new Uint8Array(16)),
      timeout: 5000, // the minimum timeout is actually 15 seconds
      rpId: content.document.domain,
      allowCredentials: [newCredential],
    };

    return content.navigator.credentials.get({ publicKey });
  });
}

add_task(async function test_setup() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["security.webauth.webauthn", true],
      ["security.webauth.webauthn_enable_softtoken", true],
      ["security.webauth.webauthn_enable_usbtoken", false],
    ],
  });
});

add_task(async function test_background_tab() {
  // Open two tabs, the last one will selected.
  let tab_bg = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
  let tab_fg = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

  // Requests from background tabs must fail.
  await promiseMakeCredential(tab_bg)
    .then(arrivingHereIsBad)
    .catch(expectNotAllowedError);

  // Requests from background tabs must fail.
  await promiseGetAssertion(tab_bg)
    .then(arrivingHereIsBad)
    .catch(expectNotAllowedError);

  // Close tabs.
  await BrowserTestUtils.removeTab(tab_bg);
  await BrowserTestUtils.removeTab(tab_fg);
});

add_task(async function test_background_window() {
  // Open a tab, then a new window.
  let tab_bg = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
  let win = await BrowserTestUtils.openNewBrowserWindow();

  // Wait until the new window is really focused.
  await new Promise(resolve => SimpleTest.waitForFocus(resolve, win));

  // Requests from selected tabs not in the active window must fail.
  await promiseMakeCredential(tab_bg)
    .then(arrivingHereIsBad)
    .catch(expectNotAllowedError);

  // Requests from selected tabs not in the active window must fail.
  await promiseGetAssertion(tab_bg)
    .then(arrivingHereIsBad)
    .catch(expectNotAllowedError);

  // Close tab and window.
  await BrowserTestUtils.closeWindow(win);
  await BrowserTestUtils.removeTab(tab_bg);
});

add_task(async function test_minimized() {
  // Minimizing windows doesn't supported in headless mode.
  if (Services.env.get("MOZ_HEADLESS")) {
    return;
  }

  // Open a window with a tab.
  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);

  // Minimize the window.
  window.minimize();
  await TestUtils.waitForCondition(() => !tab.linkedBrowser.docShellIsActive);

  // Requests from minimized windows must fail.
  await promiseMakeCredential(tab)
    .then(arrivingHereIsBad)
    .catch(expectNotAllowedError);

  // Requests from minimized windows must fail.
  await promiseGetAssertion(tab)
    .then(arrivingHereIsBad)
    .catch(expectNotAllowedError);

  // Restore the window.
  await new Promise(resolve => SimpleTest.waitForFocus(resolve, window));

  // Close tab.
  await BrowserTestUtils.removeTab(tab);
});
