Bug 1642570 - capture only user input for Form History r=dimi

We can skip checks for `input.value != input.defaultValue` by using `lastInteractiveValue` which tells us what user typed in the field.

Differential Revision: https://phabricator.services.mozilla.com/D150990
This commit is contained in:
Sergey Galich 2022-07-21 21:00:11 +00:00
Родитель 1d4fdff5ec
Коммит a9e99bef15
18 изменённых файлов: 478 добавлений и 367 удалений

Просмотреть файл

@ -51,9 +51,9 @@ function $_(formNum, name) {
return null;
}
var element = form.children.namedItem(name);
var element = form.querySelector(`:is([name="${name}"], [id="${name}"])`);
if (!element) {
ok(false, "$_ couldn't find requested element " + name);
ok(false, `$_ couldn't find requested element "${name}"`);
return null;
}

Просмотреть файл

@ -95,10 +95,17 @@ class FormHistoryChild extends JSWindowActorChild {
continue;
}
let value = input.value.trim();
const value = input.lastInteractiveValue?.trim();
// Don't save empty or unchanged values.
if (!value || value == input.defaultValue.trim()) {
// Only save user entered values even if they match the default value.
// Any script input is ignored.
// See Bug 1642570 for details.
if (!value) {
continue;
}
// Save only when user input was last.
if (value != input.value.trim()) {
continue;
}

Просмотреть файл

@ -18,9 +18,6 @@ skip-if = (verify && debug && (os == 'win')) || (os == 'mac') # Bug 1514249
[test_form_autocomplete_validation_at_input_event.html]
[test_form_autocomplete_with_list.html]
[test_form_submission.html]
skip-if =
os == "mac" && bits == 64 && debug # Bug 1481802
os == "linux" && bits == 64 # Bug 1481802
[test_form_submission_cap.html]
[test_form_submission_cap2.html]
[test_input_valid_state_with_autocomplete.html]

Просмотреть файл

@ -25,16 +25,16 @@ const TelemetryFilterPropsAC = Object.freeze({
/*
* Returns the element with the specified |name| attribute.
*/
function $_(formNum, name) {
function getFormElementByName(formNum, name) {
let form = document.getElementById("form" + formNum);
if (!form) {
ok(false, "$_ couldn't find requested form " + formNum);
ok(false, "getFormElementByName couldn't find requested form " + formNum);
return null;
}
let element = form.elements.namedItem(name);
if (!element) {
ok(false, "$_ couldn't find requested element " + name);
ok(false, "getFormElementByName couldn't find requested element " + name);
return null;
}
@ -43,11 +43,11 @@ function $_(formNum, name) {
// the element.
if (element.hasAttribute("name") && element.getAttribute("name") != name) {
ok(false, "$_ got confused.");
ok(false, "getFormElementByName got confused.");
return null;
}
return element;
return SpecialPowers.wrap(element);
}
function registerPopupShownListener(listener) {
@ -79,7 +79,7 @@ function checkArrayValues(actualValues, expectedValues, msg) {
}
}
var checkObserver = {
var gCheckObserver = {
verifyStack: [],
callback: null,
@ -122,17 +122,41 @@ var checkObserver = {
countEntries(expected.name, expected.value, function(num) {
ok(num > 0, expected.message);
if (!checkObserver.verifyStack.length) {
let callback = checkObserver.callback;
checkObserver.callback = null;
if (!gCheckObserver.verifyStack.length) {
let callback = gCheckObserver.callback;
gCheckObserver.callback = null;
callback();
}
});
},
};
class StorageEventsObserver {
promisesToResolve = [];
constructor() {
gChromeScript.sendAsyncMessage("addObserver");
gChromeScript.addMessageListener(
"satchel-storage-changed",
this.observe.bind(this)
);
}
cleanup() {
gChromeScript.sendAsyncMessage("removeObserver");
}
observe({ subject, topic, data }) {
this.promisesToResolve.shift()?.({ subject, topic, data });
}
promiseNextStorageEvent() {
return new Promise(resolve => this.promisesToResolve.push(resolve));
}
}
function checkForSave(name, value, message) {
checkObserver.verifyStack.push({ name, value, message });
gCheckObserver.verifyStack.push({ name, value, message });
}
function getFormSubmitButton(formNum) {
@ -297,6 +321,12 @@ function checkACTelemetryEvent(actualEvent, input, augmentedExtra) {
isDeeply(actualEvent[5], expectedExtra, "Check event extra object");
}
let gStorageEventsObserver;
function promiseNextStorageEvent() {
return gStorageEventsObserver.promiseNextStorageEvent();
}
function satchelCommonSetup() {
let chromeURL = SimpleTest.getTestFileURL("parent_utils.js");
gChromeScript = SpecialPowers.loadChromeScript(chromeURL);
@ -307,7 +337,10 @@ function satchelCommonSetup() {
}
});
gStorageEventsObserver = new StorageEventsObserver();
SimpleTest.registerCleanupFunction(() => {
gStorageEventsObserver.cleanup();
gChromeScript.sendAsyncMessage("cleanup");
return new Promise(resolve => {
gChromeScript.addMessageListener("cleanup-done", function done() {

Просмотреть файл

@ -6,15 +6,10 @@
<body>
<form id="subform2" onsubmit="return false">
<form>
<input id="subtest2" type="text" name="subtest2">
<button type="submit">Submit</button>
</form>
<script>
// set the input's value (can't use a default value, as satchel will ignore it)
document.getElementById("subtest2").value = "subtestValue";
</script>
</body>
</html>

Просмотреть файл

@ -7,8 +7,8 @@
function submitForm() {
if (!location.search.includes("field")) {
let form = document.getElementById("form");
let field = document.getElementById("field");
field.value = "value";
let field = SpecialPowers.wrap(document.getElementById("field"));
field.setUserInput("value");
form.submit();
}
}

Просмотреть файл

@ -76,7 +76,9 @@ function checkSelectedIndexAfterResponseTime(expectedIndex) {
});
}
let input = $_(1, "field1");
// getFormElementByName() returns wrapped input,
// but in this test we want to test untrusted events, so we must unwrap
const input = SpecialPowers.unwrap(getFormElementByName(1, "field1"));
function doKeyUnprivileged(key) {
let keyName = "DOM_VK_" + key.toUpperCase();

Просмотреть файл

@ -60,7 +60,7 @@ function waitForNextPopup() {
}
add_task(async function test_popup_not_move_input() {
let input = $_(1, "field1");
let input = getFormElementByName(1, "field1");
let rect = input.getBoundingClientRect();
await new Promise(resolve => updateFormHistory([

Просмотреть файл

@ -29,7 +29,7 @@ Form History test: form field autocomplete
<pre id="test">
<script class="testbody" type="text/javascript">
let input = $_(1, "field1");
let input = getFormElementByName(1, "field1");
function setupFormHistory(aCallback) {
updateFormHistory([

Просмотреть файл

@ -144,7 +144,7 @@ Form History test: form field autocomplete
SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal", true]]});
var kSetUserInputCancelable = SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input");
var input = $_(1, "field1");
var input = getFormElementByName(1, "field1");
function setupFormHistory(aCallback) {
updateFormHistory([
@ -580,7 +580,7 @@ async function runTest() { // eslint-disable-line complexity
case 64:
// Look at form 2, trigger autocomplete popup
input = $_(2, "field2");
input = getFormElementByName(2, "field2");
testNum = 99;
expectPopup();
restoreForm();
@ -596,7 +596,7 @@ async function runTest() { // eslint-disable-line complexity
checkForm("value1");
// Look at form 3, try to trigger autocomplete popup
input = $_(3, "field2");
input = getFormElementByName(3, "field2");
restoreForm();
// Sometimes, this will fail if scrollTo(0, 0) is called, so that doesn't
// happen here. Fortunately, a different input is used from the last test,
@ -612,7 +612,7 @@ async function runTest() { // eslint-disable-line complexity
checkForm("");
// Look at form 4, try to trigger autocomplete popup
input = $_(4, "field2");
input = getFormElementByName(4, "field2");
restoreForm();
synthesizeKey("KEY_ArrowDown");
waitForMenuChange(0);
@ -625,7 +625,7 @@ async function runTest() { // eslint-disable-line complexity
checkForm("");
// Look at form 5, try to trigger autocomplete popup
input = $_(5, "field3");
input = getFormElementByName(5, "field3");
restoreForm();
testNum = 199;
expectPopup();
@ -709,7 +709,7 @@ async function runTest() { // eslint-disable-line complexity
synthesizeKey("KEY_Escape");
// Look at form 6, try to trigger autocomplete popup
input = $_(6, "field4");
input = getFormElementByName(6, "field4");
restoreForm();
testNum = 249;
expectPopup();
@ -775,7 +775,7 @@ async function runTest() { // eslint-disable-line complexity
checkMenuEntries([], testNum);
// Look at form 7, try to trigger autocomplete popup
input = $_(7, "field5");
input = getFormElementByName(7, "field5");
testNum = 299;
expectPopup();
restoreForm();
@ -869,7 +869,7 @@ async function runTest() { // eslint-disable-line complexity
case 310:
checkMenuEntries([], testNum);
input = $_(8, "field6");
input = getFormElementByName(8, "field6");
testNum = 399;
expectPopup();
restoreForm();
@ -886,14 +886,14 @@ async function runTest() { // eslint-disable-line complexity
checkForm("value");
if (testNum == 400) {
input = $_(9, "field7");
input = getFormElementByName(9, "field7");
} else if (testNum == 401) {
input = $_(10, "field8");
input = getFormElementByName(10, "field8");
} else if (testNum == 402) {
input = $_(11, "field9");
input = getFormElementByName(11, "field9");
} else if (testNum == 403) {
todo(false, "Fix input type=number");
input = $_(12, "field10");
input = getFormElementByName(12, "field10");
}
expectPopup();
@ -907,7 +907,7 @@ async function runTest() { // eslint-disable-line complexity
synthesizeKey("KEY_Enter");
checkForm("42");
input = $_(14, "field11");
input = getFormElementByName(14, "field11");
restoreForm();
waitForMenuChange(0);
break;
@ -917,7 +917,7 @@ async function runTest() { // eslint-disable-line complexity
// have a drop down menu for now
checkForm("");
input = $_(15, "field12");
input = getFormElementByName(15, "field12");
restoreForm();
waitForMenuChange(0);
break;
@ -927,7 +927,7 @@ async function runTest() { // eslint-disable-line complexity
// have a drop down menu for now
checkForm("");
input = $_(16, "field13");
input = getFormElementByName(16, "field13");
restoreForm();
synthesizeKey("KEY_ArrowDown");
waitForMenuChange(0);
@ -939,7 +939,7 @@ async function runTest() { // eslint-disable-line complexity
synthesizeKey("KEY_Enter");
checkForm("30"); // default (midway between minimum (0) and maximum (64)) - step
input = $_(17, "field14");
input = getFormElementByName(17, "field14");
restoreForm();
waitForMenuChange(0);
break;
@ -948,7 +948,7 @@ async function runTest() { // eslint-disable-line complexity
checkMenuEntries([]); // type=color does not have a drop down menu
checkForm("#000000"); // default color value
input = $_(18, "field15");
input = getFormElementByName(18, "field15");
restoreForm();
expectPopup();
synthesizeKey("KEY_ArrowDown");
@ -960,7 +960,7 @@ async function runTest() { // eslint-disable-line complexity
synthesizeKey("KEY_Enter");
checkForm("2016-08");
input = $_(19, "field16");
input = getFormElementByName(19, "field16");
restoreForm();
expectPopup();
synthesizeKey("KEY_ArrowDown");
@ -972,7 +972,7 @@ async function runTest() { // eslint-disable-line complexity
synthesizeKey("KEY_Enter");
checkForm("2016-W32");
input = $_(20, "field17");
input = getFormElementByName(20, "field17");
restoreForm();
waitForMenuChange(0);
synthesizeKey("KEY_ArrowDown");
@ -987,7 +987,7 @@ async function runTest() { // eslint-disable-line complexity
break;
case 412:
input = $_(1, "field1");
input = getFormElementByName(1, "field1");
// Go to test 500.
testNum = 499;
@ -1073,7 +1073,7 @@ async function runTest() { // eslint-disable-line complexity
}
case 600:
// check we don't show autocomplete for searchbar-history
input = $_(13, "searchbar-history");
input = getFormElementByName(13, "searchbar-history");
// Trigger autocomplete popup
checkForm("");

Просмотреть файл

@ -51,7 +51,7 @@ Form History test: form field autocomplete
/** Test for Form History autocomplete **/
var input = $_(1, "field1");
var input = getFormElementByName(1, "field1");
function setupFormHistory(aCallback) {
updateFormHistory([
@ -238,7 +238,7 @@ async function runTest() {
// Trigger autocomplete popup
// Look at form 3, try to trigger autocomplete popup
input.value = "";
input = $_(3, "field2");
input = getFormElementByName(3, "field2");
testNum = 99;
expectPopup();
restoreForm();
@ -527,7 +527,7 @@ async function runTest() {
});
testNum = 499;
input = $_(5, "field3");
input = getFormElementByName(5, "field3");
checkForm("");
expectPopup();
restoreForm();

Просмотреть файл

@ -13,239 +13,245 @@
<!-- ===== Things that should not be saved. ===== -->
<!-- autocomplete=off (case-insensitive token) for input -->
<form id="form1" onsubmit="return checkSubmit(1)">
<form purpose="nothing stored for input autocomplete=off (case-insensitive token)"
id="form1">
<input type="text" name="test1" autocomplete=" oFf ">
<button type="submit">Submit</button>
</form>
<!-- autocomplete=off for form -->
<form id="form2" onsubmit="return checkSubmit(2)" autocomplete="oFf">
<form purpose="nothing stored for form autocomplete=off"
id="form2" autocomplete="oFf">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- don't save type=hidden -->
<form id="form3" onsubmit="return checkSubmit(3)">
<form purpose="nothing stored for type=hidden"
id="form3">
<input type="hidden" name="test1">
<button type="submit">Submit</button>
</form>
<!-- don't save type=checkbox -->
<form id="form4" onsubmit="return checkSubmit(4)">
<form purpose="nothing stored for type=checkbox"
id="form4">
<input type="checkbox" name="test1">
<button type="submit">Submit</button>
</form>
<!-- Don't save empty values. -->
<form id="form5" onsubmit="return checkSubmit(5)">
<form purpose="nothing stored for empty values."
id="form5">
<input type="text" name="test1" value="originalValue">
<button type="submit">Submit</button>
</form>
<!-- Don't save unchanged values. -->
<form id="form6" onsubmit="return checkSubmit(6)">
<form purpose="nothing stored for unchanged values when set by a script."
id="form6">
<input type="text" name="test1" value="dontSaveThis">
<button type="submit">Submit</button>
</form>
<!-- Don't save unchanged values. (.value not touched) -->
<form id="form7" onsubmit="return checkSubmit(7)">
<form purpose="nothing stored for unchanged values. (.value not touched)"
id="form7">
<input type="text" name="test1" value="dontSaveThis">
<button type="submit">Submit</button>
</form>
<!-- No field name or ID. -->
<form id="form8" onsubmit="return checkSubmit(8)">
<form purpose="nothing stored for no field name or ID"
id="form8">
<input type="text">
<button type="submit">Submit</button>
</form>
<!-- Nothing to save! -->
<form id="form9" onsubmit="return checkSubmit(9)">
<form purpose="nothing stored for nothing to save"
id="form9">
<button type="submit">Submit</button>
</form>
<!-- input with name too long (300 chars.) -->
<form id="form10" onsubmit="return checkSubmit(10)">
<form purpose="nothing stored for input with name too long (300 chars.)"
id="form10">
<input type="text" name="12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890">
<button type="submit">Submit</button>
</form>
<!-- input with value too long (300 chars.) -->
<form id="form11" onsubmit="return checkSubmit(11)">
<form purpose="nothing stored for input with value too long (300 chars.)"
id="form11">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- input with value of one space (which should be trimmed) -->
<form id="form12" onsubmit="return checkSubmit(12)">
<form purpose="nothing stored for input with value of one space (which should be trimmed)"
id="form12">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- password field -->
<form id="form13" onsubmit="return checkSubmit(13)">
<form purpose="nothing stored for password field"
id="form13">
<input type="password" name="test1">
<button type="submit">Submit</button>
</form>
<!-- password field (type toggled to password and back after pageload) -->
<form id="form14" onsubmit="return checkSubmit(14)">
<form purpose="nothing stored for password field (type toggled to password and back after pageload)"
id="form14">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- input with sensitive data (16 digit credit card number) -->
<form id="form15" onsubmit="return checkSubmit(15)">
<form purpose="nothing stored for input with sensitive data (16 digit credit card number)"
id="form15">
<script type="text/javascript">
var form = document.getElementById("form15");
for (let i = 0; i != 10; i++) {
let input = document.createElement("input");
let form = document.getElementById("form15");
for (let i = 0; i < 10; i++) {
const input = form.appendChild(document.createElement("input"));
input.type = "text";
input.name = "test" + (i + 1);
form.appendChild(input);
}
</script>
<button type="submit">Submit</button>
</form>
<!-- input with sensitive data (15 digit credit card number) -->
<form id="form16" onsubmit="return checkSubmit(16)">
<form purpose="nothing stored for input with sensitive data (15 digit credit card number)"
id="form16">
<script type="text/javascript">
form = document.getElementById("form16");
for (let i = 0; i != 10; i++) {
let input = document.createElement("input");
for (let i = 0; i < 10; i++) {
const input = form.appendChild(document.createElement("input"));
input.type = "text";
input.name = "test" + (i + 1);
form.appendChild(input);
}
</script>
<button type="submit">Submit</button>
</form>
<!-- input with sensitive data (19 digit credit card number) -->
<form id="form17" onsubmit="return checkSubmit(17)">
<form purpose="nothing stored for input with sensitive data (19 digit credit card number)"
id="form17">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- input with sensitive data (16 digit hyphenated credit card number) -->
<form id="form18" onsubmit="return checkSubmit(18)">
<form purpose="nothing stored for input with sensitive data (16 digit hyphenated credit card number)"
id="form18">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- input with sensitive data (15 digit whitespace-separated credit card number) -->
<form id="form19" onsubmit="return checkSubmit(19)">
<form purpose="nothing stored for input with sensitive data (15 digit whitespace-separated credit card number)"
id="form19">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- Don't save values if the form is invalid. -->
<form id="form20" onsubmit="return checkSubmit(20);">
<input type='email' name='test1' oninvalid="return checkSubmit(20);">
<form purpose="nothing stored for the invalid form"
id="form20">
<input type='email' name='test1'>
<button type='submit'>Submit</button>
</form>
<!-- Don't save values if the form is invalid. -->
<form id="form21" onsubmit="return checkSubmit(21);">
<input type='email' value='foo' oninvalid="return checkSubmit(21);">
<form purpose="nothing stored for the invalid form"
id="form21">
<input type='email' value='foo'>
<input type='text' name='test1'>
<button type='submit'>Submit</button>
</form>
<!-- Don't save values if the input name is 'searchbar-history' -->
<form id="form22" onsubmit="return checkSubmit(22);">
<form purpose="nothing stored for the input with name 'searchbar-history'"
id="form22">
<input type='text' name='searchbar-history'>
<button type='submit'>Submit</button>
</form>
<!-- autocomplete=cc-csc (case-insensitive token) for input -->
<form id="form23" onsubmit="return checkSubmit(23)">
<form purpose="nothing stored for input autocomplete=cc-csc (case-insensitive token)"
id="form23">
<input type="text" name="test1" autocomplete=" cc-CSC ">
<button type="submit">Submit</button>
</form>
<!-- autocomplete=new-password (case-insensitive token) for input -->
<form id="form24" onsubmit="return checkSubmit(24)">
<form purpose="nothing stored for input autocomplete=new-password (case-insensitive token)"
id="form24">
<input type="text" name="test1" autocomplete=" NEW-password ">
<button type="submit">Submit</button>
</form>
<form purpose="nothing stored after user input followed by reset button click"
id="form25">
<input type="text" name="test1" defaultValue="do not save me" value="do not save me either">
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
<form purpose="nothing stored after user input changed by a script"
id="form26">
<input type="text" name="test1" defaultValue="do not save me" value="do not save me either">
<button type="submit">Submit</button>
</form>
<!-- ===== Things that should be saved ===== -->
<!-- Form 100 is submitted into an iframe, not declared here. -->
<!-- input with no default value -->
<form id="form101" onsubmit="return checkSubmit(101)">
<form purpose="saved input with no default value"
id="form101">
<input type="text" name="test1">
<button type="submit">Submit</button>
</form>
<!-- input with a default value -->
<form id="form102" onsubmit="return checkSubmit(102)">
<form purpose="saved input with a default value"
id="form102">
<input type="text" name="test2" value="originalValue">
<button type="submit">Submit</button>
</form>
<!-- input uses id but not name -->
<form id="form103" onsubmit="return checkSubmit(103)">
<form purpose="saved input with id and not name"
id="form103">
<input type="text" id="test3">
<button type="submit">Submit</button>
</form>
<!-- input with leading and trailing space -->
<form id="form104" onsubmit="return checkSubmit(104)">
<form purpose="saved input with leading and trailing space"
id="form104">
<input type="text" name="test4">
<button type="submit">Submit</button>
</form>
<!-- input with leading and trailing whitespace -->
<form id="form105" onsubmit="return checkSubmit(105)">
<form purpose="saved input with leading and trailing whitespace"
id="form105">
<input type="text" name="test5">
<button type="submit">Submit</button>
</form>
<!-- input that looks like sensitive data but doesn't
satisfy the requirements (incorrect length) -->
<form id="form106" onsubmit="return checkSubmit(106)">
<form purpose="saved input that looks like sensitive data but doesn't satisfy the requirements (incorrect length)"
id="form106">
<input type="text" name="test6">
<button type="submit">Submit</button>
</form>
<!-- input that looks like sensitive data but doesn't
satisfy the requirements (Luhn check fails for 16 chars) -->
<form id="form107" onsubmit="return checkSubmit(107)">
<form purpose="input that looks like sensitive data but doesn't satisfy the requirements (Luhn check fails for 16 chars)"
id="form107">
<script type="text/javascript">
form = document.getElementById("form107");
for (let i = 0; i != 10; i++) {
let input = document.createElement("input");
for (let i = 0; i < 10; i++) {
let input = form.appendChild(document.createElement("input"));
input.type = "text";
input.name = "test7_" + (i + 1);
form.appendChild(input);
}
</script>
<button type="submit">Submit</button>
</form>
<!-- input that looks like sensitive data but doesn't
satisfy the requirements (Luhn check fails for 15 chars) -->
<form id="form108" onsubmit="return checkSubmit(108)">
<form purpose="input that looks like sensitive data but doesn't satisfy the requirements (Luhn check fails for 15 chars)"
id="form108">
<script type="text/javascript">
form = document.getElementById("form108");
for (let i = 0; i != 10; i++) {
let input = document.createElement("input");
let input = form.appendChild(document.createElement("input"));
input.type = "text";
input.name = "test8_" + (i + 1);
form.appendChild(input);
}
</script>
<button type="submit">Submit</button>
</form>
<!-- form data submitted through HTTPS, when browser.formfill.saveHttpsForms is true -->
<form id="form109" action="https://www.example.com/" onsubmit="return checkSubmit(109)">
<form purpose="form data submitted through HTTPS, when browser.formfill.saveHttpsForms is true"
id="form109" action="https://www.example.com/">
<input type="text" name="test9">
<button type="submit">Submit</button>
</form>
@ -255,9 +261,7 @@
<script class="testbody" type="text/javascript">
/* eslint-disable complexity */
var numSubmittedForms = 0;
var ccNumbers = {
const ccNumbers = {
valid15: [
"930771457288760", "474915027480942",
"924894781317325", "714816113937185",
@ -288,235 +292,308 @@ var ccNumbers = {
],
};
function checkInitialState() {
countEntries(null, null,
function(num) {
ok(!num, "checking for initially empty storage");
startTest();
function setUserInput(formNumber, inputName, value) {
const input = SpecialPowers.wrap(getFormElementByName(formNumber, inputName));
input.setUserInput(value);
}
function setScriptInput(formNumber, inputName, value) {
getFormElementByName(formNumber, inputName).value = value;
}
function checkSubmitDoesNotSave(formNumber, inputName, value) {
return new Promise((resolve, reject) => {
const form = document.getElementById("form" + formNumber);
form.addEventListener("submit", function listener(e) {
countEntries(null, null, historyEntriesCount => {
ok(!historyEntriesCount, form.getAttribute("purpose"));
form.removeEventListener("submit", listener);
resolve();
});
});
getFormSubmitButton(formNumber).click();
});
}
function startTest() {
// Fill in values for the various fields. We could just set the <input>'s
// value attribute, but we don't save default form values (and we want to
// ensure unsaved values are because of autocomplete=off or whatever).
$_(1, "test1").value = "dontSaveThis";
$_(2, "test1").value = "dontSaveThis";
$_(3, "test1").value = "dontSaveThis";
$_(4, "test1").value = "dontSaveThis";
$_(5, "test1").value = "";
$_(6, "test1").value = "dontSaveThis";
function checkInvalidFirstInputDoesNotSave(formNumber, value) {
return new Promise((resolve, reject) => {
const form = document.getElementById("form" + formNumber);
const input = form.querySelector("input");
input.addEventListener("invalid", function listener(e) {
countEntries(null, null, historyEntriesCount => {
ok(!historyEntriesCount, form.getAttribute("purpose"));
input.removeEventListener("invalid", listener);
resolve();
});
});
getFormSubmitButton(formNumber).click();
});
}
async function checkSubmitSaves(formNumber, inputName, interactiveValue, savedValue, storageEventData = "formhistory-add") {
setUserInput(formNumber, inputName, interactiveValue);
const form = document.getElementById("form" + formNumber);
const storageEventPromise = promiseNextStorageEvent();
getFormSubmitButton(formNumber).click();
const storageEvent = await storageEventPromise;
isDeeply(storageEvent, {
subject: null,
topic: "satchel-storage-changed",
data: storageEventData
}, "expected storage event");
await new Promise((resolve, reject) => {
countEntries(inputName, savedValue, historyEntriesCount => {
ok(historyEntriesCount == 1, form.getAttribute("purpose"));
resolve();
});
});
}
// This will prevent endless loop of tests
for (const form of document.forms) {
form.addEventListener("submit", e => e.preventDefault());
}
add_task(async function checkInitialState() {
await new Promise((resolve)=>{
countEntries(null, null,
historyEntriesCount => {
ok(!historyEntriesCount, "checking for initially empty storage");
resolve();
});
});
})
add_task(async function form1_does_not_save() {
setUserInput(1, "test1", "dontSaveThis");
await checkSubmitDoesNotSave(1);
});
add_task(async function form2_does_not_save() {
setUserInput(2, "test1", "dontSaveThis");
await checkSubmitDoesNotSave(2);
});
add_task(async function form3_does_not_save() {
setUserInput(3, "test1", "dontSaveThis");
await checkSubmitDoesNotSave(3);
});
add_task(async function form4_does_not_save() {
setUserInput(4, "test1", "dontSaveThis");
await checkSubmitDoesNotSave(4);
});
add_task(async function form5_does_not_save() {
setUserInput(5, "test1", "");
await checkSubmitDoesNotSave(5);
});
add_task(async function form6_does_not_save() {
setScriptInput(6, "test1", "dontSaveThis");
await checkSubmitDoesNotSave(6);
});
add_task(async function form7_does_not_save() {
// Form 7 deliberately left untouched.
await checkSubmitDoesNotSave(7);
});
add_task(async function form8_does_not_save() {
// Form 8 has an input with no name or input attribute.
let input = document.getElementById("form8").elements[0];
let input = SpecialPowers.wrap(document.getElementById("form8").elements[0]);
is(input.type, "text", "checking we got unidentified input");
input.value = "dontSaveThis";
input.setUserInput("dontSaveThis");
await checkSubmitDoesNotSave(8);
});
add_task(async function form9_does_not_save() {
// Form 9 has nothing to modify.
$_(10, "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456" +
await checkSubmitDoesNotSave(9);
});
add_task(async function form10_does_not_save() {
setUserInput(10,
"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456" +
"789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456" +
"789012345678901234567890123456789012345678901234567890123456789012345678901234567890").value
= "dontSaveThis";
$_(11, "test1").value = "123456789012345678901234567890123456789012345678901234567890123456789" +
"789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
"dontSaveThis");
await checkSubmitDoesNotSave(10);
});
add_task(async function form11_does_not_save() {
setUserInput(11, "test1",
"123456789012345678901234567890123456789012345678901234567890123456789" +
"012345678901234567890123456789012345678901234567890123456789012345678" +
"901234567890123456789012345678901234567890123456789012345678901234567" +
"89012345678901234567890123456789012345678901234567890";
$_(12, "test1").value = " ";
$_(13, "test1").value = "dontSaveThis";
$_(14, "test1").type = "password";
$_(14, "test1").value = "dontSaveThis";
"89012345678901234567890123456789012345678901234567890");
await checkSubmitDoesNotSave(11);
});
add_task(async function form12_does_not_save() {
setUserInput(12, "test1", " ");
await checkSubmitDoesNotSave(12);
});
add_task(async function form13_does_not_save() {
setUserInput(13, "test1", "dontSaveThis");
await checkSubmitDoesNotSave(13);
});
add_task(async function form14_does_not_save() {
let input = SpecialPowers.wrap(document.getElementById("form14").elements[0]);
input.type = "password";
input.setUserInput("dontSaveThis");
// Set it back to type=text to simulate a password visibility toggle.
$_(14, "test1").type = "text";
input.type = "text";
await checkSubmitDoesNotSave(14);
});
let testData = ccNumbers.valid16;
for (let i = 0; i != testData.length; i++) {
$_(15, "test" + (i + 1)).value = testData[i];
add_task(async function form15_does_not_save() {
const testData = ccNumbers.valid16;
for (let i = 0; i < testData.length; i++) {
setUserInput(15, "test" + (i + 1), testData[i]);
}
await checkSubmitDoesNotSave(15);
});
testData = ccNumbers.valid15;
for (let i = 0; i != testData.length; i++) {
$_(16, "test" + (i + 1)).value = testData[i];
add_task(async function form16_does_not_save() {
const testData = ccNumbers.valid15;
for (let i = 0; i < testData.length; i++) {
setUserInput(16, "test" + (i + 1), testData[i]);
}
$_(17, "test1").value = "6799990100000000019";
$_(18, "test1").value = "0000-0000-0080-4609";
$_(19, "test1").value = "0000 0000 0222 331";
$_(20, "test1").value = "dontSaveThis";
$_(21, "test1").value = "dontSaveThis";
$_(22, "searchbar-history").value = "dontSaveThis";
$_(23, "test1").value = "987";
$_(24, "test1").value = "s3cr3t";
await checkSubmitDoesNotSave(16);
});
$_(101, "test1").value = "savedValue";
$_(102, "test2").value = "savedValue";
$_(103, "test3").value = "savedValue";
$_(104, "test4").value = " trimTrailingAndLeadingSpace ";
$_(105, "test5").value = "\t trimTrailingAndLeadingWhitespace\t ";
$_(106, "test6").value = "55555555555544445553"; // passes luhn but too long
add_task(async function form17_does_not_save() {
setUserInput(17, "test1", "6799990100000000019");
await checkSubmitDoesNotSave(17);
});
testData = ccNumbers.invalid16;
for (let i = 0; i != testData.length; i++) {
$_(107, "test7_" + (i + 1)).value = testData[i];
}
add_task(async function form18_does_not_save() {
setUserInput(18, "test1", "0000-0000-0080-4609");
await checkSubmitDoesNotSave(18);
});
testData = ccNumbers.invalid15;
for (let i = 0; i != testData.length; i++) {
$_(108, "test8_" + (i + 1)).value = testData[i];
}
add_task(async function form19_does_not_save() {
setUserInput(19, "test1", "0000 0000 0222 331");
await checkSubmitDoesNotSave(19);
});
$_(109, "test9").value = "savedValue";
add_task(async function form20_does_not_save() {
setUserInput(20, "test1", "dontSaveThis");
await checkInvalidFirstInputDoesNotSave(20, "invalid");
});
// submit the first form.
let button = getFormSubmitButton(1);
button.click();
}
add_task(async function form21_does_not_save() {
setUserInput(21, "test1", "dontSaveThis");
await checkInvalidFirstInputDoesNotSave(21, "invalid");
});
add_task(async function form22_does_not_save() {
setUserInput(22, "searchbar-history", "dontSaveThis");
await checkSubmitDoesNotSave(22);
});
/* exported checkSubmit */
// Called by each form's onsubmit handler.
function checkSubmit(formNum) {
ok(true, "form " + formNum + " submitted");
numSubmittedForms++;
add_task(async function form23_does_not_save() {
setUserInput(23, "test1", "987");
await checkSubmitDoesNotSave(23);
});
// Check for expected storage state.
switch (formNum) {
// Test 1-24 should not save anything.
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
countEntries(null, null,
function(num) {
ok(!num, "checking for empty storage");
submitForm(formNum);
});
return false;
case 100:
checkForSave("subtest2", "subtestValue", "checking saved subtest value");
break;
case 101:
checkForSave("test1", "savedValue", "checking saved value");
break;
case 102:
checkForSave("test2", "savedValue", "checking saved value");
break;
case 103:
checkForSave("test3", "savedValue", "checking saved value");
break;
case 104:
checkForSave("test4", "trimTrailingAndLeadingSpace",
"checking saved value is trimmed on both sides");
break;
case 105:
checkForSave("test5", "trimTrailingAndLeadingWhitespace",
"checking saved value is trimmed on both sides");
break;
case 106:
checkForSave("test6", "55555555555544445553", "checking saved value");
break;
case 107:
for (let i = 0; i != ccNumbers.invalid16.length; i++) {
checkForSave("test7_" + (i + 1), ccNumbers.invalid16[i], "checking saved value");
}
break;
case 108:
for (let i = 0; i != ccNumbers.invalid15.length; i++) {
checkForSave("test8_" + (i + 1), ccNumbers.invalid15[i], "checking saved value");
}
break;
case 109:
checkForSave("test9", "savedValue", "checking saved value");
break;
default:
ok(false, "Unexpected form submission");
break;
}
add_task(async function form24_does_not_save() {
setUserInput(24, "test1", "s3cr3t");
await checkSubmitDoesNotSave(24);
});
return submitForm(formNum);
}
add_task(async function form25_does_not_save() {
setUserInput(25, "test1", "s3cr3t");
document.querySelector("form[id=form25] button[type=reset]").click();
await checkSubmitDoesNotSave(25);
});
function submitForm(formNum) {
// End the test now on SeaMonkey.
if (formNum == 20 && navigator.userAgent.match(/ SeaMonkey\//)) {
checkObserver.uninit();
is(numSubmittedForms, 20, "Ensuring all forms were submitted.");
add_task(async function form26_does_not_save() {
setUserInput(26, "test1", "s3cr3t");
document.querySelector("form[id=form26] input[name=test1]").value = "script changed me";
await checkSubmitDoesNotSave(26);
});
todo(false, "Skipping remaining checks on SeaMonkey ftb. (Bug 589471)");
// finish(), yet let the test actually end first, to be safe.
SimpleTest.executeSoon(SimpleTest.finish);
return false; // return false to cancel current form submission
}
// End the test at the last form.
if (formNum == 109) {
is(numSubmittedForms, 34, "Ensuring all forms were submitted.");
checkObserver.uninit();
SimpleTest.executeSoon(SimpleTest.finish);
return false; // return false to cancel current form submission
}
// This timeout is here so that button.click() is never called before this
// function returns. If button.click() is called before returning, a long
// chain of submits will happen recursively since the submit is dispatched
// immediately.
//
// This in itself is fine, but if there are errors in the code, mochitests
// will in some cases give you "server too busy", which is hard to debug!
//
setTimeout(function() {
checkObserver.waitForChecks(function() {
let nextFormNum = formNum == 24 ? 100 : (formNum + 1);
// Submit the next form. Special cases are Forms 21 and 100, which happen
// from an HTTPS domain in an iframe.
if (nextFormNum == 100) {
ok(true, "submitting iframe test " + nextFormNum);
// Need to call checkSubmit first, as the iframe's document can be in another
// process and won't be able to notify back before the submit occurs.
checkSubmit(100);
let browsingContext = SpecialPowers.unwrap(
SpecialPowers.wrap(document.getElementById("iframe")).browsingContext);
SpecialPowers.spawn(browsingContext, [], () => {
add_task(async function form100_saves() {
const iframe = SpecialPowers.wrap(document.getElementById("iframe"));
const browsingContext = SpecialPowers.unwrap(iframe.browsingContext);
const storageEventPromise = promiseNextStorageEvent();
await SpecialPowers.spawn(browsingContext, [], () => {
/* eslint-disable no-undef */
content.document.querySelectorAll("button")[0].click();
const input = SpecialPowers.wrap(content.document.getElementById("subtest2"));
input.setUserInput("subtestValue");
content.document.querySelector("button").click();
/* eslint-enable no-undef */
});
} else {
let button = getFormSubmitButton(nextFormNum);
button.click();
}
const storageEvent = await storageEventPromise;
isDeeply(storageEvent, {
subject: null,
topic: "satchel-storage-changed",
data: "formhistory-add"
}, "expected storage event");
await new Promise((resolve, reject) => {
countEntries("subtest2", "subtestValue", historyEntriesCount => {
ok(historyEntriesCount == 1, "saved from iframe");
resolve();
});
}, 0);
});
});
return false; // cancel current form submission
}
add_task(async function form101_saves() {
await checkSubmitSaves(101, "test1", "savedValue", "savedValue");
});
checkObserver.init();
add_task(async function form102_saves() {
await checkSubmitSaves(102, "test2", "savedValue", "savedValue");
});
window.onload = checkInitialState;
add_task(async function form103_saves() {
await checkSubmitSaves(103, "test3", "savedValue", "savedValue");
});
SimpleTest.waitForExplicitFinish();
add_task(async function form104_saves() {
await checkSubmitSaves(104, "test4", " trimTrailingAndLeadingSpace ", "trimTrailingAndLeadingSpace");
});
add_task(async function form105_saves() {
await checkSubmitSaves(105, "test5", "\t trimTrailingAndLeadingWhitespace\t ", "trimTrailingAndLeadingWhitespace");
});
add_task(async function form106_saves() {
// passes luhn but too long
await checkSubmitSaves(106, "test6", "55555555555544445553", "55555555555544445553");
});
add_task(async function form107_saves() {
for (let i = 0; i != ccNumbers.invalid16.length; i++) {
const name = "test7_" + (i + 1);
const value = ccNumbers.invalid16[i];
await checkSubmitSaves(107, name, value, value, i != 0 ? "formhistory-update" : "formhistory-add");
}
});
add_task(async function form108_saves() {
for (let i = 0; i != ccNumbers.invalid15.length; i++) {
const name = "test8_" + (i + 1);
const value = ccNumbers.invalid15[i];
await checkSubmitSaves(108, name, value, value, i != 0 ? "formhistory-update" : "formhistory-add");
}
});
add_task(async function form109_saves() {
setUserInput(109, "test9", "savedValue");
await checkSubmitSaves(109, "test9", "savedValue", "savedValue");
});
</script>
</pre>

Просмотреть файл

@ -48,7 +48,7 @@ function startTest() {
// value attribute, but we don't save default form values (and we want to
// ensure unsaved values are because of autocomplete=off or whatever).
for (let i = 1; i <= numInputFields; i++) {
$_(1, "test" + i).value = i;
getFormElementByName(1, "test" + i).value = i;
}
// submit the first form.

Просмотреть файл

@ -137,7 +137,7 @@ function startTest() {
// Fill in values for the various fields. We could just set the <input>'s
// value attribute, but we don't save default form values (and we want to
// ensure unsaved values are because of autocomplete=off or whatever).
$_(1, "test" + numInputFields).value = numInputFields + " changed";
getFormElementByName(1, "test" + numInputFields).value = numInputFields + " changed";
// submit the first form.
let button = getFormSubmitButton(1);

Просмотреть файл

@ -79,8 +79,8 @@ add_task(async function test_initialize() {
is(window.location.protocol, "https:", "This test must run on HTTPS");
// Now that rememberSignons is false, create the password fields.
$_(1, "field1").type = "password";
$_(2, "field1").type = "password";
getFormElementByName(1, "field1").type = "password";
getFormElementByName(2, "field1").type = "password";
await new Promise(resolve => updateFormHistory([
{ op: "remove" },
@ -97,7 +97,7 @@ add_task(async function test_initialize() {
});
add_task(async function test_secure_noFormHistoryOrWarning() {
let input = $_(1, "field1");
let input = getFormElementByName(1, "field1");
// The autocomplete popup should not open under any circumstances on
// type=password with password manager disabled.
@ -122,7 +122,7 @@ add_task(async function test_secure_noFormHistoryOrWarning() {
add_task(async function test_insecure_focusWarning() {
// Form 2 has an insecure action so should show the warning even if password manager is disabled.
let input = $_(2, "field1");
let input = getFormElementByName(2, "field1");
let shownPromise = waitForNextPopup();
input.focus();
await shownPromise;

Просмотреть файл

@ -30,7 +30,7 @@ function waitForNextPopup() {
}
add_task(async function test_popup_direction() {
let input = $_(1, "field1");
let input = getFormElementByName(1, "field1");
await new Promise(resolve => updateFormHistory([
{ op: "remove" },

Просмотреть файл

@ -22,7 +22,7 @@ Form History test: Test for events while the form history popup is open
<pre id="test">
<script class="testbody">
var form = document.getElementById("form1");
var input = $_(1, "field1");
var input = getFormElementByName(1, "field1");
var expectedValue = "value1";
function setupFormHistory(aCallback) {

Просмотреть файл

@ -22,7 +22,7 @@ Form History test: Test for keydown handler submitting the form
<pre id="test">
<script class="testbody">
var form = document.getElementById("form1");
var input = $_(1, "field1");
var input = getFormElementByName(1, "field1");
var expectedValue = "value1";
var beforeInputFired = false;