Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test telemetry on autofill and autocomplete on autocomplete=new-password fields</title>
<!-- This test assumes that telemetry events are not cleared after the `setup` task. -->
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="pwmgr_common.js"></script>
<script src="../../../satchel/test/satchel_common.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content"></div>
<pre id="test">
Login Manager test: telemetry events during autofill with password generation on `autocomplete=new-password` fields.
<template id="form1-template">
<form id="form1" action="https://autofill">
<input type="text" name="uname">
<input type="password" name="p">
<button type="submit" name="submit">Submit</button>
</form>
</template>
<template id="form2-template">
<form id="form2" action="https://autofill">
<input type="text" name="uname">
<input type="password" name="password" autocomplete="new-password">
<button type="submit" name="submit">Submit</button>
</form>
</template>
<template id="form3-template">
<form id="form3" action="https://autofill">
<input type="text" name="username">
<label>New password<input type="password" name="password"></label>
<button type="submit" name="submit">Submit</button>
</form>
</template>
<script class="testbody" type="text/javascript">
const { TestUtils } = SpecialPowers.ChromeUtils.importESModule(
);
const formTemplates = {
form1: document.getElementById("form1-template"),
form2: document.getElementById("form2-template"),
form3: document.getElementById("form3-template"),
};
const dateAndTimeFormatter = new SpecialPowers.Services.intl.DateTimeFormat(undefined, {
dateStyle: "medium",
});
const TelemetryFilterPropsUsed = Object.freeze({
category: "pwmgr",
method: "autocomplete_field",
object: "generatedpassword",
});
const TelemetryFilterPropsShown = Object.freeze({
category: "pwmgr",
method: "autocomplete_shown",
object: "generatedpassword",
});
async function waitForTelemetryEventsCondition(cond, options = {},
errorMsg = "waitForTelemetryEventsCondition timed out", maxTries = 200) {
return TestUtils.waitForCondition(async () => {
let events = await getTelemetryEvents(options);
let result;
try {
result = cond(events);
info("waitForTelemetryEventsCondition, result: " + result);
} catch (e) {
info("waitForTelemetryEventsCondition caught exception, got events: " + JSON.stringify(events));
ok(false, `${e}\n${e.stack}`);
}
return result ? events : null;
}, errorMsg, maxTries);
}
async function showACPopup(formNumber, expectedACLabels) {
const autocompleteItems = await popupByArrowDown();
checkAutoCompleteResults(autocompleteItems, expectedACLabels,
window.location.host, "Check all rows are correct");
}
async function checkTelemetryEventsPWGenShown(expectedPWGenTelemetryEvents) {
info(`showed generated password option, check there are now ${expectedPWGenTelemetryEvents} generatedpassword telemetry events`);
await waitForTelemetryEventsCondition(events => {
return events.length == expectedPWGenTelemetryEvents;
}, { process: "parent", filterProps: TelemetryFilterPropsShown }, `Wait for there to be ${expectedPWGenTelemetryEvents} shown telemetry events`);
}
async function checkTelemetryEventsPWGenUsed(expectedPWGenTelemetryEvents) {
info("filled generated password again, ensure we don't record another generatedpassword autocomplete telemetry event");
let telemetryEvents;
try {
telemetryEvents = await waitForTelemetryEventsCondition(events => events.length == expectedPWGenTelemetryEvents + 1,
{ process: "parent", filterProps: TelemetryFilterPropsUsed },
`Wait for there to be ${expectedPWGenTelemetryEvents + 1} used events`, 50);
} catch (ex) {}
ok(!telemetryEvents, `Expected to timeout waiting for there to be ${expectedPWGenTelemetryEvents + 1} events`);
}
function clearGeneratedPasswords() {
const { LoginManagerParent } = ChromeUtils.importESModule(
"resource://gre/modules/LoginManagerParent.sys.mjs"
);
if (LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin()) {
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
}
}
add_setup(async () => {
let useEvents = await getTelemetryEvents({ process: "parent", filterProps: TelemetryFilterPropsUsed, clear: true });
is(useEvents.length, 0, "Expect 0 use events");
let showEvents = await getTelemetryEvents({ process: "parent", filterProps: TelemetryFilterPropsShown, clear: true });
is(showEvents.length, 0, "Expect 0 show events");
let acEvents = await getTelemetryEvents({ process: "parent", filterProps: TelemetryFilterPropsAC, clear: true });
is(acEvents.length, 0, "Expect 0 autocomplete events");
});
add_named_task("autofill autocomplete username no generation", async () => {
await setPreferencesForTask(
["signon.generation.available", false],
["signon.generation.enabled", false],
);
await setStoredLoginsDuringTask([location.origin, "https://autofill", null, "user1", "pass1"]);
const form = setContentForTask(formTemplates.form2);
const autofillResult = await formAutofillResult(form.id);
is(autofillResult, "password_autocomplete_new_password", "form has not been filled due to password_autocomplete_new_password");
form.uname.focus();
const [acEvent] = await waitForTelemetryEventsCondition(events => {
return events.length == 1;
}, { process: "parent", filterProps: TelemetryFilterPropsAC, clear: true }, `Wait for there to be 1 autocomplete telemetry event`);
checkACTelemetryEvent(acEvent, form.uname, {
"hadPrevious": "0",
"login": "1",
"loginsFooter": "1"
});
});
add_named_task("filling out two forms one after the other", async () => {
await setPreferencesForTask(
["signon.generation.available", true],
["signon.generation.enabled", true],
);
const formsToTest = [
{
num: 2,
template: formTemplates.form2
},
{
num: 3,
template: formTemplates.form3
}
];
// Bug 1616356 and Bug 1548878: Recorded once per origin
let expectedPWGenTelemetryEvents = 0;
// Bug 1619498: Recorded once every time the autocomplete popup is shown
let expectedACShownTelemetryEvents = 0;
for (const { num: formNumber, template } of formsToTest) {
runInParent(clearGeneratedPasswords);
await setStoredLoginsAsync([location.origin, "https://autofill", null, "user1", "pass1"]);
const form = setContentForTask(template);
await promiseFormsProcessedInSameProcess();
form.reset();
form.password.focus();
await showACPopup(formNumber, [
"user1",
"Use a Securely Generated Password",
]);
expectedPWGenTelemetryEvents++;
expectedACShownTelemetryEvents++;
await checkTelemetryEventsPWGenShown(expectedPWGenTelemetryEvents);
let acEvents = await waitForTelemetryEventsCondition(events => {
return events.length == expectedACShownTelemetryEvents;
}, { process: "parent", filterProps: TelemetryFilterPropsAC }, `Wait for there to be ${expectedACShownTelemetryEvents} autocomplete telemetry event(s)`);
checkACTelemetryEvent(acEvents[expectedACShownTelemetryEvents - 1], form.password, {
"generatedPasswo": "1",
"hadPrevious": "0",
"login": "1",
"loginsFooter": "1"
});
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
// Can't use promiseFormsProcessedInSameProcess() when autocomplete fills the field directly.
await SimpleTest.promiseWaitForCondition(() => form.password.value == "pass1", "Check pw filled");
// No autocomplete results should appear for non-empty pw fields.
await noPopupByArrowDown();
info("Removing all logins to test auto-saving of generated passwords");
await LoginManager.removeAllUserFacingLogins();
while (form.password.value) {
synthesizeKey("KEY_Backspace");
}
info("This time select the generated password");
await showACPopup(formNumber, [
"Use a Securely Generated Password",
]);
expectedACShownTelemetryEvents++;
await checkTelemetryEventsPWGenShown(expectedPWGenTelemetryEvents);
acEvents = await waitForTelemetryEventsCondition(events => {
return events.length == expectedACShownTelemetryEvents;
}, { process: "parent", filterProps: TelemetryFilterPropsAC }, `Wait for there to be ${expectedACShownTelemetryEvents} autocomplete telemetry event(s)`);
checkACTelemetryEvent(acEvents[expectedACShownTelemetryEvents - 1], form.password, {
"generatedPasswo": "1",
"hadPrevious": "0",
"loginsFooter": "1"
});
synthesizeKey("KEY_ArrowDown");
let storageAddPromise = promiseStorageChanged(["addLogin"]);
synthesizeKey("KEY_Enter");
info("waiting for the password field to be filled with the generated password");
await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check generated pw filled");
info("Wait for generated password to be added to storage");
await storageAddPromise;
let logins = await LoginManager.getAllLogins();
let timePasswordChanged = logins[logins.length - 1].timePasswordChanged;
let time = dateAndTimeFormatter.format(new Date(timePasswordChanged));
const LABEL_NO_USERNAME = "No username (" + time + ")";
info("Check field is masked upon blurring");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
// Remove selection for OS where the whole value is selected upon focus.
synthesizeKey("KEY_ArrowRight");
while (form.password.value) {
synthesizeKey("KEY_Backspace");
}
info("Blur the empty field to trigger a 'change' event");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
info("Type a single character after blanking");
synthesizeKey("@");
info("Blur the single-character field to trigger a 'change' event");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
synthesizeKey("KEY_Backspace"); // Blank the field again
await showACPopup(formNumber, [
LABEL_NO_USERNAME,
"Use a Securely Generated Password",
]);
expectedACShownTelemetryEvents++;
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check generated pw filled");
await checkTelemetryEventsPWGenUsed(expectedPWGenTelemetryEvents);
info("filling the saved login to ensure the field is masked again");
while (form.password.value) {
synthesizeKey("KEY_Backspace");
}
info("Blur the field to trigger a 'change' event again");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
// Remove selection for OS where the whole value is selected upon focus.
synthesizeKey("KEY_ArrowRight");
await showACPopup(formNumber, [
LABEL_NO_USERNAME,
"Use a Securely Generated Password",
]);
expectedACShownTelemetryEvents++;
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check saved generated pw filled");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus
}
});
</script>
</pre>
</body>
</html>