/* 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/browser/dom/webauthn/tests/browser/tab_webauthn_result.html";
add_task(async
function test_setup() {
return SpecialPowers.pushPrefEnv({
set: [
[
"security.webauth.webauthn_enable_softtoken",
false],
[
"security.webauth.webauthn_enable_usbtoken",
true],
],
});
});
add_task(test_switch_tab);
add_task(test_new_window_make);
add_task(test_new_window_get);
add_task(test_minimize_make);
add_task(test_minimize_get);
async
function assertStatus(tab, expected) {
let actual = await SpecialPowers.spawn(
tab.linkedBrowser,
[],
async
function () {
info(
"visbility state: " + content.document.visibilityState);
info(
"active: " + content.browsingContext.isActive);
return content.document.getElementById(
"status").value;
}
);
is(actual, expected,
"webauthn request " + expected);
}
async
function waitForStatus(tab, expected) {
/* eslint-disable no-shadow */
await SpecialPowers.spawn(
tab.linkedBrowser,
[[expected]],
async
function (expected) {
return ContentTaskUtils.waitForCondition(() => {
info(
"expecting " +
expected +
", visbility state: " +
content.document.visibilityState
);
info(
"expecting " +
expected +
", active: " +
content.browsingContext.isActive
);
return content.document.getElementById(
"status").value == expected;
});
}
);
/* eslint-enable no-shadow */
await assertStatus(tab, expected);
}
function startMakeCredentialRequest(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], async
function () {
const cose_alg_ECDSA_w_SHA256 = -7;
let publicKey = {
rp: { id: content.document.domain, name:
"none" },
user: {
id:
new Uint8Array(),
name:
"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 }],
};
let status = content.document.getElementById(
"status");
info(
"Attempting to create credential for origin: " +
content.document.nodePrincipal.origin
);
content.navigator.credentials
.create({ publicKey })
.then(() => {
status.value =
"completed";
})
.
catch(() => {
status.value =
"aborted";
});
status.value =
"pending";
});
}
function startGetAssertionRequest(tab) {
return SpecialPowers.spawn(tab.linkedBrowser, [], 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],
};
let status = content.document.getElementById(
"status");
info(
"Attempting to get credential for origin: " +
content.document.nodePrincipal.origin
);
content.navigator.credentials
.get({ publicKey })
.then(() => {
status.value =
"completed";
})
.
catch(ex => {
info(
"aborted: " + ex);
status.value =
"aborted";
});
status.value =
"pending";
});
}
// Test that MakeCredential() and GetAssertion() requests
// are aborted when the current tab loses its focus.
async
function test_switch_tab() {
// Create a new tab for the MakeCredential() request.
let tab_create = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
TEST_URL
);
// Start the request.
await startMakeCredentialRequest(tab_create);
await assertStatus(tab_create,
"pending");
// Open another tab and switch to it. The first will lose focus.
let tab_get = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
await assertStatus(tab_create,
"pending");
// Start a GetAssertion() request in the second tab, the first is aborted
await startGetAssertionRequest(tab_get);
await waitForStatus(tab_create,
"aborted");
await assertStatus(tab_get,
"pending");
// Start a second request in the second tab. It should abort.
await startGetAssertionRequest(tab_get);
await waitForStatus(tab_get,
"aborted");
// Close tabs.
BrowserTestUtils.removeTab(tab_create);
BrowserTestUtils.removeTab(tab_get);
}
function waitForWindowActive(win, active) {
return Promise.all([
BrowserTestUtils.waitForEvent(win, active ?
"focus" :
"blur"),
BrowserTestUtils.waitForEvent(win, active ?
"activate" :
"deactivate"),
]);
}
async
function test_new_window_make() {
// Create a new tab for the MakeCredential() request.
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
// Start a MakeCredential request.
await startMakeCredentialRequest(tab);
await assertStatus(tab,
"pending");
let windowGonePromise = waitForWindowActive(window,
false);
// Open a new window. The tab will lose focus.
let win = await BrowserTestUtils.openNewBrowserWindow();
await windowGonePromise;
await assertStatus(tab,
"pending");
let windowBackPromise = waitForWindowActive(window,
true);
await BrowserTestUtils.closeWindow(win);
await windowBackPromise;
// Close tab.
await BrowserTestUtils.removeTab(tab);
}
async
function test_new_window_get() {
// Create a new tab for the GetAssertion() request.
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
// Start a GetAssertion request.
await startGetAssertionRequest(tab);
await assertStatus(tab,
"pending");
let windowGonePromise = waitForWindowActive(window,
false);
// Open a new window. The tab will lose focus.
let win = await BrowserTestUtils.openNewBrowserWindow();
await windowGonePromise;
await assertStatus(tab,
"pending");
let windowBackPromise = waitForWindowActive(window,
true);
await BrowserTestUtils.closeWindow(win);
await windowBackPromise;
// Close tab.
BrowserTestUtils.removeTab(tab);
}
async
function test_minimize_make() {
// Minimizing windows doesn't supported in headless mode.
if (Services.env.get(
"MOZ_HEADLESS")) {
return;
}
// Create a new window for the MakeCredential() request.
let win = await BrowserTestUtils.openNewBrowserWindow();
let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
// Start a MakeCredential request.
await startMakeCredentialRequest(tab);
await assertStatus(tab,
"pending");
// Minimize the window.
let windowGonePromise = waitForWindowActive(win,
false);
win.minimize();
await assertStatus(tab,
"pending");
await windowGonePromise;
// Restore the window.
await
new Promise(resolve => SimpleTest.waitForFocus(resolve, win));
await assertStatus(tab,
"pending");
// Close window and wait for main window to be focused again.
let windowBackPromise = waitForWindowActive(window,
true);
await BrowserTestUtils.removeTab(tab);
await BrowserTestUtils.closeWindow(win);
await windowBackPromise;
}
async
function test_minimize_get() {
// Minimizing windows doesn't supported in headless mode.
if (Services.env.get(
"MOZ_HEADLESS")) {
return;
}
// Create a new window for the GetAssertion() request.
let win = await BrowserTestUtils.openNewBrowserWindow();
let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
// Start a GetAssertion request.
await startGetAssertionRequest(tab);
await assertStatus(tab,
"pending");
// Minimize the window.
let windowGonePromise = waitForWindowActive(win,
false);
win.minimize();
await assertStatus(tab,
"pending");
await windowGonePromise;
// Restore the window.
await
new Promise(resolve => SimpleTest.waitForFocus(resolve, win));
await assertStatus(tab,
"pending");
// Close window and wait for main window to be focused again.
let windowBackPromise = waitForWindowActive(window,
true);
await BrowserTestUtils.removeTab(tab);
await BrowserTestUtils.closeWindow(win);
await windowBackPromise;
}