mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 09:15:35 +00:00
Backed out 3 changesets (bug 1388674) for mochitest fission failures on test_submit_without_field_modifications. CLOSED TREE
Backed out changeset 2bbba46b221c (bug 1388674) Backed out changeset 74fb61b252bb (bug 1388674) Backed out changeset b723ff1791cd (bug 1388674)
This commit is contained in:
parent
cf3a4562ea
commit
31672fe924
@ -3895,7 +3895,6 @@ pref("signon.generation.available", false);
|
||||
pref("signon.generation.enabled", false);
|
||||
pref("signon.privateBrowsingCapture.enabled", false);
|
||||
pref("signon.storeWhenAutocompleteOff", true);
|
||||
pref("signon.userInputRequiredToCapture.enabled", true);
|
||||
pref("signon.debug", false);
|
||||
pref("signon.recipes.path", "chrome://passwordmgr/content/recipes.json");
|
||||
pref("signon.schemeUpgrades", false);
|
||||
|
@ -82,9 +82,6 @@ this.LoginHelper = {
|
||||
this.storeWhenAutocompleteOff = Services.prefs.getBoolPref(
|
||||
"signon.storeWhenAutocompleteOff"
|
||||
);
|
||||
this.userInputRequiredToCapture = Services.prefs.getBoolPref(
|
||||
"signon.userInputRequiredToCapture.enabled"
|
||||
);
|
||||
},
|
||||
|
||||
createLogger(aLogPrefix) {
|
||||
|
@ -216,51 +216,32 @@ const observer = {
|
||||
return;
|
||||
}
|
||||
|
||||
let ownerDocument = aEvent.target.ownerDocument;
|
||||
let window = ownerDocument.defaultView;
|
||||
let docState = LoginManagerChild.forWindow(window).stateForDocument(
|
||||
ownerDocument
|
||||
);
|
||||
let window = aEvent.target.ownerDocument.defaultView;
|
||||
|
||||
switch (aEvent.type) {
|
||||
// Used to mask fields with filled generated passwords when blurred.
|
||||
case "blur": {
|
||||
if (docState.generatedPasswordFields.has(aEvent.target)) {
|
||||
let unmask = false;
|
||||
LoginManagerChild.forWindow(window)._togglePasswordFieldMasking(
|
||||
aEvent.target,
|
||||
unmask
|
||||
);
|
||||
}
|
||||
let unmask = false;
|
||||
LoginManagerChild.forWindow(window)._togglePasswordFieldMasking(
|
||||
aEvent.target,
|
||||
unmask
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Used to watch for changes to fields filled with generated passwords.
|
||||
case "change": {
|
||||
if (docState.generatedPasswordFields.has(aEvent.target)) {
|
||||
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(
|
||||
aEvent.target
|
||||
);
|
||||
}
|
||||
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(
|
||||
aEvent.target
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Used to watch for changes to fields filled with generated passwords.
|
||||
case "input": {
|
||||
let field = aEvent.target;
|
||||
if (docState.generatedPasswordFields.has(field)) {
|
||||
LoginManagerChild.forWindow(
|
||||
window
|
||||
)._maybeStopTreatingAsGeneratedPasswordField(aEvent);
|
||||
}
|
||||
if (
|
||||
field.hasBeenTypePassword ||
|
||||
LoginHelper.isUsernameFieldType(field)
|
||||
) {
|
||||
// flag this form as user-modified
|
||||
let formLikeRoot = FormLikeFactory.findRootForField(field);
|
||||
docState.fieldModificationsByRootElement.set(formLikeRoot, true);
|
||||
}
|
||||
LoginManagerChild.forWindow(
|
||||
window
|
||||
)._maybeStopTreatingAsGeneratedPasswordField(aEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -277,10 +258,7 @@ const observer = {
|
||||
}
|
||||
|
||||
case "focus": {
|
||||
if (
|
||||
aEvent.target.type == "password" &&
|
||||
docState.generatedPasswordFields.has(aEvent.target)
|
||||
) {
|
||||
if (aEvent.target.type == "password") {
|
||||
// Used to unmask fields with filled generated passwords when focused.
|
||||
let unmask = true;
|
||||
LoginManagerChild.forWindow(window)._togglePasswordFieldMasking(
|
||||
@ -879,12 +857,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
return;
|
||||
}
|
||||
|
||||
// set up input event listeners so we know if the user has interacted with these fields
|
||||
form.rootElement.addEventListener("input", observer, {
|
||||
capture: true,
|
||||
mozSystemGroup: true,
|
||||
});
|
||||
|
||||
this._getLoginDataFromParent(form, { showMasterPassword: true })
|
||||
.then(this.loginsFound.bind(this))
|
||||
.catch(Cu.reportError);
|
||||
@ -902,15 +874,10 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
* Keeps track of filled fields and values.
|
||||
*/
|
||||
fillsByRootElement: new WeakMap(),
|
||||
/**
|
||||
* Keeps track of fields we've filled with generated passwords
|
||||
*/
|
||||
generatedPasswordFields: new WeakSet(),
|
||||
/**
|
||||
* Keeps track of logins that were last submitted.
|
||||
*/
|
||||
lastSubmittedValuesByRootElement: new WeakMap(),
|
||||
fieldModificationsByRootElement: new WeakMap(),
|
||||
loginFormRootElements: new WeakSet(),
|
||||
};
|
||||
this._loginFormStateByDocument.set(document, loginFormState);
|
||||
@ -1573,17 +1540,9 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
return;
|
||||
}
|
||||
|
||||
let docState = this.stateForDocument(doc);
|
||||
let fieldsModified = this._formHasModifiedFields(formLikeRoot);
|
||||
if (!fieldsModified && LoginHelper.userInputRequiredToCapture) {
|
||||
// we know no fields in this form had user modifications, so don't prompt
|
||||
log(
|
||||
"(form submission ignored -- submitting values that are not changed by the user)"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let autoFilledLogin = docState.fillsByRootElement.get(form.rootElement);
|
||||
let autoFilledLogin = this.stateForDocument(doc).fillsByRootElement.get(
|
||||
form.rootElement
|
||||
);
|
||||
|
||||
let detail = {
|
||||
origin,
|
||||
@ -1616,10 +1575,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
_stopTreatingAsGeneratedPasswordField(passwordField) {
|
||||
log("_stopTreatingAsGeneratedPasswordField");
|
||||
|
||||
let fields = this.stateForDocument(passwordField.ownerDocument)
|
||||
.generatedPasswordFields;
|
||||
fields.delete(passwordField);
|
||||
|
||||
// Remove all the event listeners added in _generatedPasswordFilledOrEdited
|
||||
for (let eventType of ["blur", "change", "focus", "input"]) {
|
||||
passwordField.removeEventListener(eventType, observer, {
|
||||
@ -1648,8 +1603,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
|
||||
let win = passwordField.ownerGlobal;
|
||||
let formLikeRoot = FormLikeFactory.findRootForField(passwordField);
|
||||
let docState = this.stateForDocument(passwordField.ownerDocument);
|
||||
docState.generatedPasswordFields.add(passwordField);
|
||||
|
||||
this._highlightFilledField(passwordField);
|
||||
|
||||
@ -1840,6 +1793,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
}
|
||||
|
||||
log("_fillForm", form.elements);
|
||||
let usernameField;
|
||||
// Will be set to one of AUTOFILL_RESULT in the `try` block.
|
||||
let autofillResult = -1;
|
||||
const AUTOFILL_RESULT = {
|
||||
@ -1857,16 +1811,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
PASSWORD_AUTOCOMPLETE_NEW_PASSWORD: 11,
|
||||
};
|
||||
|
||||
// Heuristically determine what the user/pass fields are
|
||||
// We do this before checking to see if logins are stored,
|
||||
// so that the user isn't prompted for a master password
|
||||
// without need.
|
||||
let [usernameField, passwordField] = this._getFormFields(
|
||||
form,
|
||||
false,
|
||||
recipes
|
||||
);
|
||||
|
||||
try {
|
||||
// Nothing to do if we have no matching (excluding form action
|
||||
// checks) logins available, and there isn't a need to show
|
||||
@ -1881,6 +1825,17 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
return;
|
||||
}
|
||||
|
||||
// Heuristically determine what the user/pass fields are
|
||||
// We do this before checking to see if logins are stored,
|
||||
// so that the user isn't prompted for a master password
|
||||
// without need.
|
||||
let passwordField;
|
||||
[usernameField, passwordField] = this._getFormFields(
|
||||
form,
|
||||
false,
|
||||
recipes
|
||||
);
|
||||
|
||||
// If we have a password inputElement parameter and it's not
|
||||
// the same as the one heuristically found, use the parameter
|
||||
// one instead.
|
||||
@ -2173,14 +2128,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
|
||||
}
|
||||
}
|
||||
|
||||
_formHasModifiedFields(formLikeRoot) {
|
||||
let state = this.stateForDocument(formLikeRoot.ownerDocument);
|
||||
let fieldsModified = state.fieldModificationsByRootElement.get(
|
||||
formLikeRoot
|
||||
);
|
||||
return fieldsModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a field, determine whether that field was last filled as a username
|
||||
* field AND whether the username is still filled in with the username AND
|
||||
|
@ -96,14 +96,14 @@ async function submitSomeCrossSiteFrames(locationMode) {
|
||||
|
||||
await SpecialPowers.spawn(outerFrameBC, [], () => {
|
||||
let doc = content.document;
|
||||
doc.getElementById("outer-username").setUserInput("outer");
|
||||
doc.getElementById("outer-password").setUserInput("outerpass");
|
||||
doc.getElementById("outer-username").value = "outer";
|
||||
doc.getElementById("outer-password").value = "outerpass";
|
||||
});
|
||||
|
||||
await SpecialPowers.spawn(innerFrameBC, [locationMode], doClick => {
|
||||
let doc = content.document;
|
||||
doc.getElementById("inner-username").setUserInput("inner");
|
||||
doc.getElementById("inner-password").setUserInput("innerpass");
|
||||
doc.getElementById("inner-username").value = "inner";
|
||||
doc.getElementById("inner-password").value = "innerpass";
|
||||
if (doClick) {
|
||||
doc.getElementById("inner-gobutton").click();
|
||||
} else {
|
||||
@ -138,8 +138,8 @@ async function submitSomeCrossSiteFrames(locationMode) {
|
||||
|
||||
await SpecialPowers.spawn(outerFrameBC2, [locationMode], doClick => {
|
||||
let doc = content.document;
|
||||
doc.getElementById("outer-username").setUserInput("outer2");
|
||||
doc.getElementById("outer-password").setUserInput("outerpass2");
|
||||
doc.getElementById("outer-username").value = "outer2";
|
||||
doc.getElementById("outer-password").value = "outerpass2";
|
||||
if (doClick) {
|
||||
doc.getElementById("outer-gobutton").click();
|
||||
} else {
|
||||
|
@ -85,12 +85,10 @@ add_task(async function test_doorhanger_shown_on_un_with_invalid_ccnumber() {
|
||||
|
||||
let processedPromise = listenForTestNotification("FormSubmit");
|
||||
await ContentTask.spawn(browser, null, async () => {
|
||||
content.document
|
||||
.getElementById("form-basic-username")
|
||||
.setUserInput("1234123412341234");
|
||||
content.document
|
||||
.getElementById("form-basic-password")
|
||||
.setUserInput("411");
|
||||
content.document.getElementById("form-basic-username").value =
|
||||
"1234123412341234";
|
||||
content.document.getElementById("form-basic-password").value = "411";
|
||||
|
||||
content.document.getElementById("form-basic-submit").click();
|
||||
});
|
||||
await processedPromise;
|
||||
|
@ -22,8 +22,8 @@ add_task(async function test_empty_password() {
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").setUserInput("username");
|
||||
doc.getElementById("form-basic-password").setUserInput("pw");
|
||||
doc.getElementById("form-basic-username").value = "username";
|
||||
doc.getElementById("form-basic-password").value = "pw";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
|
||||
|
@ -14,14 +14,12 @@
|
||||
add_task(async function test_edit_password() {
|
||||
let testCases = [
|
||||
{
|
||||
description: "No saved logins, update password in doorhanger",
|
||||
usernameInPage: "username",
|
||||
passwordInPage: "password",
|
||||
passwordChangedTo: "newPassword",
|
||||
timesUsed: 1,
|
||||
},
|
||||
{
|
||||
description: "Login is saved, update password in doorhanger",
|
||||
usernameInPage: "username",
|
||||
usernameInPageExists: true,
|
||||
passwordInPage: "password",
|
||||
@ -30,8 +28,6 @@ add_task(async function test_edit_password() {
|
||||
timesUsed: 2,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Change username in doorhanger to match saved login, update password in doorhanger",
|
||||
usernameInPage: "username",
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
@ -40,8 +36,6 @@ add_task(async function test_edit_password() {
|
||||
timesUsed: 2,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Change username in doorhanger to match saved login, dont update password in doorhanger",
|
||||
usernameInPage: "username",
|
||||
usernameChangedTo: "newUsername",
|
||||
usernameChangedToExists: true,
|
||||
@ -51,8 +45,6 @@ add_task(async function test_edit_password() {
|
||||
checkPasswordNotUpdated: true,
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Change username and password in doorhanger to match saved empty-username login",
|
||||
usernameInPage: "newUsername",
|
||||
usernameChangedTo: "",
|
||||
usernameChangedToExists: true,
|
||||
@ -107,22 +99,31 @@ add_task(async function test_edit_password() {
|
||||
contentTestCase
|
||||
) {
|
||||
let doc = content.document;
|
||||
doc
|
||||
.getElementById("form-basic-username")
|
||||
.setUserInput(contentTestCase.usernameInPage);
|
||||
doc
|
||||
.getElementById("form-basic-password")
|
||||
.setUserInput(contentTestCase.passwordInPage);
|
||||
doc.getElementById("form-basic-username").value =
|
||||
contentTestCase.usernameInPage;
|
||||
doc.getElementById("form-basic-password").value =
|
||||
contentTestCase.passwordInPage;
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
await promiseShown;
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
// Style flush to make sure binding is attached
|
||||
notificationElement.querySelector("#password-notification-password")
|
||||
.clientTop;
|
||||
|
||||
// Modify the username & password in the dialog if requested.
|
||||
await updateDoorhangerInputValues({
|
||||
username: testCase.usernameChangedTo,
|
||||
password: testCase.passwordChangedTo,
|
||||
});
|
||||
// Modify the username in the dialog if requested.
|
||||
if (testCase.usernameChangedTo) {
|
||||
notificationElement.querySelector(
|
||||
"#password-notification-username"
|
||||
).value = testCase.usernameChangedTo;
|
||||
}
|
||||
|
||||
// Modify the password in the dialog if requested.
|
||||
if (testCase.passwordChangedTo) {
|
||||
notificationElement.querySelector(
|
||||
"#password-notification-password"
|
||||
).value = testCase.passwordChangedTo;
|
||||
}
|
||||
|
||||
// We expect a modifyLogin notification if the final username used by the
|
||||
// dialog exists in the logins database, otherwise an addLogin one.
|
||||
@ -141,15 +142,8 @@ add_task(async function test_edit_password() {
|
||||
"passwordmgr-storage-changed",
|
||||
(_, data) => data == expectedNotification
|
||||
);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popuphidden"
|
||||
);
|
||||
notificationElement.button.doCommand();
|
||||
|
||||
let [result] = await promiseLogin;
|
||||
await promiseHidden;
|
||||
|
||||
// Check that the values in the database match the expected values.
|
||||
let login = expectModifyLogin
|
||||
@ -157,27 +151,24 @@ add_task(async function test_edit_password() {
|
||||
.QueryInterface(Ci.nsIArray)
|
||||
.queryElementAt(1, Ci.nsILoginInfo)
|
||||
: result.QueryInterface(Ci.nsILoginInfo);
|
||||
let meta = login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
|
||||
let expectedLogin = {
|
||||
username:
|
||||
"usernameChangedTo" in testCase
|
||||
? testCase.usernameChangedTo
|
||||
: testCase.usernameInPage,
|
||||
password:
|
||||
"passwordChangedTo" in testCase
|
||||
? testCase.passwordChangedTo
|
||||
: testCase.passwordInPage,
|
||||
timesUsed: testCase.timesUsed,
|
||||
};
|
||||
Assert.equal(
|
||||
login.username,
|
||||
testCase.usernameChangedTo || testCase.usernameInPage
|
||||
);
|
||||
Assert.equal(
|
||||
login.password,
|
||||
testCase.passwordChangedTo || testCase.passwordInPage
|
||||
);
|
||||
|
||||
let meta = login.QueryInterface(Ci.nsILoginMetaInfo);
|
||||
Assert.equal(meta.timesUsed, testCase.timesUsed);
|
||||
|
||||
// Check that the password was not updated if the user is empty
|
||||
if (testCase.checkPasswordNotUpdated) {
|
||||
expectedLogin.usedSince = meta.timeCreated;
|
||||
expectedLogin.timeCreated = meta.timePasswordChanged;
|
||||
Assert.ok(meta.timeLastUsed > meta.timeCreated);
|
||||
Assert.ok(meta.timeCreated == meta.timePasswordChanged);
|
||||
}
|
||||
verifyLogins([expectedLogin]);
|
||||
|
||||
await cleanupDoorhanger();
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -93,48 +93,55 @@ add_task(async function test_save_change() {
|
||||
async function(browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown",
|
||||
event => event.target == PopupNotifications.panel
|
||||
);
|
||||
await ContentTask.spawn(browser, [username, password], async function([
|
||||
contentUsername,
|
||||
contentPassword,
|
||||
]) {
|
||||
let doc = content.document;
|
||||
doc
|
||||
.getElementById("form-basic-username")
|
||||
.setUserInput(contentUsername);
|
||||
doc
|
||||
.getElementById("form-basic-password")
|
||||
.setUserInput(contentPassword);
|
||||
doc.getElementById("form-basic-username").value = contentUsername;
|
||||
doc.getElementById("form-basic-password").value = contentPassword;
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
await promiseShown;
|
||||
let notif = PopupNotifications.getNotification("password", browser);
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
// Style flush to make sure binding is attached
|
||||
notificationElement.querySelector("#password-notification-password")
|
||||
.clientTop;
|
||||
|
||||
// Check the actual content of the popup notification.
|
||||
Assert.equal(
|
||||
notificationElement.querySelector("#password-notification-username")
|
||||
.value,
|
||||
username
|
||||
);
|
||||
Assert.equal(
|
||||
notificationElement.querySelector("#password-notification-password")
|
||||
.value,
|
||||
password
|
||||
);
|
||||
|
||||
// Simulate the action on the notification to request the login to be
|
||||
// saved, and wait for the data to be updated or saved based on the type
|
||||
// of operation we expect.
|
||||
let expectedNotification, expectedDoorhanger;
|
||||
let expectedNotification;
|
||||
if (oldPassword !== undefined && oldUsername !== undefined) {
|
||||
expectedNotification = "addLogin";
|
||||
expectedDoorhanger = "password-save";
|
||||
} else if (oldPassword !== undefined) {
|
||||
expectedNotification = "modifyLogin";
|
||||
expectedDoorhanger = "password-change";
|
||||
} else {
|
||||
expectedNotification = "addLogin";
|
||||
expectedDoorhanger = "password-save";
|
||||
}
|
||||
|
||||
let notif = getCaptureDoorhanger(
|
||||
expectedDoorhanger,
|
||||
PopupNotifications,
|
||||
browser
|
||||
);
|
||||
// Check the actual content of the popup notification.
|
||||
await checkDoorhangerUsernamePassword(username, password);
|
||||
|
||||
let promiseLogin = TestUtils.topicObserved(
|
||||
"passwordmgr-storage-changed",
|
||||
(_, data) => data == expectedNotification
|
||||
);
|
||||
await clickDoorhangerButton(notif, REMEMBER_BUTTON);
|
||||
notificationElement.button.doCommand();
|
||||
await promiseLogin;
|
||||
await cleanupDoorhanger(notif); // clean slate for the next test
|
||||
|
||||
|
@ -18,17 +18,18 @@ add_task(async function test_toggle_password() {
|
||||
async function(browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown",
|
||||
event => event.target == PopupNotifications.panel
|
||||
);
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").setUserInput("username");
|
||||
doc.getElementById("form-basic-password").setUserInput("pw");
|
||||
doc.getElementById("form-basic-username").value = "username";
|
||||
doc.getElementById("form-basic-password").value = "pw";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
// Check the actual content of the popup notification.
|
||||
await checkDoorhangerUsernamePassword("username", "pw");
|
||||
await promiseShown;
|
||||
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
let passwordTextbox = notificationElement.querySelector(
|
||||
@ -39,7 +40,7 @@ add_task(async function test_toggle_password() {
|
||||
);
|
||||
|
||||
await EventUtils.synthesizeMouseAtCenter(toggleCheckbox, {});
|
||||
Assert.ok(toggleCheckbox.checked, "Toggle is checked");
|
||||
Assert.ok(toggleCheckbox.checked);
|
||||
Assert.equal(
|
||||
passwordTextbox.type,
|
||||
"text",
|
||||
@ -53,7 +54,6 @@ add_task(async function test_toggle_password() {
|
||||
"password",
|
||||
"Password textbox changed to * text"
|
||||
);
|
||||
await cleanupDoorhanger(notif);
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -72,19 +72,21 @@ add_task(async function test_checkbox_disabled_if_has_master_password() {
|
||||
async function(browser) {
|
||||
// Submit the form in the content page with the credentials from the test
|
||||
// case. This will cause the doorhanger notification to be displayed.
|
||||
let promiseShown = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popupshown",
|
||||
event => event.target == PopupNotifications.panel
|
||||
);
|
||||
|
||||
LoginTestUtils.masterPassword.enable();
|
||||
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let doc = content.document;
|
||||
doc.getElementById("form-basic-username").setUserInput("username");
|
||||
doc.getElementById("form-basic-password").setUserInput("pass");
|
||||
doc.getElementById("form-basic-username").value = "username";
|
||||
doc.getElementById("form-basic-password").value = "pass";
|
||||
doc.getElementById("form-basic").submit();
|
||||
});
|
||||
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
|
||||
ok(notif, "got notification popup");
|
||||
|
||||
// Check the actual content of the popup notification.
|
||||
await checkDoorhangerUsernamePassword("username", "pass");
|
||||
await promiseShown;
|
||||
|
||||
let notificationElement = PopupNotifications.panel.childNodes[0];
|
||||
let passwordTextbox = notificationElement.querySelector(
|
||||
@ -103,7 +105,6 @@ add_task(async function test_checkbox_disabled_if_has_master_password() {
|
||||
toggleCheckbox.getAttribute("hidden"),
|
||||
"checkbox is hidden when master password is set"
|
||||
);
|
||||
await cleanupDoorhanger(notif);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -94,10 +94,8 @@ add_task(async function test_edit_username() {
|
||||
testCase.usernameInPage,
|
||||
async function(usernameInPage) {
|
||||
let doc = content.document;
|
||||
doc
|
||||
.getElementById("form-basic-username")
|
||||
.setUserInput(usernameInPage);
|
||||
doc.getElementById("form-basic-password").setUserInput("password");
|
||||
doc.getElementById("form-basic-username").value = usernameInPage;
|
||||
doc.getElementById("form-basic-password").value = "password";
|
||||
doc.getElementById("form-basic").submit();
|
||||
}
|
||||
);
|
||||
@ -109,9 +107,9 @@ add_task(async function test_edit_username() {
|
||||
|
||||
// Modify the username in the dialog if requested.
|
||||
if (testCase.usernameChangedTo) {
|
||||
await updateDoorhangerInputValues({
|
||||
username: testCase.usernameChangedTo,
|
||||
});
|
||||
notificationElement.querySelector(
|
||||
"#password-notification-username"
|
||||
).value = testCase.usernameChangedTo;
|
||||
}
|
||||
|
||||
// We expect a modifyLogin notification if the final username used by the
|
||||
@ -130,13 +128,8 @@ add_task(async function test_edit_username() {
|
||||
"passwordmgr-storage-changed",
|
||||
(_, data) => data == expectedNotification
|
||||
);
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
PopupNotifications.panel,
|
||||
"popuphidden"
|
||||
);
|
||||
notificationElement.button.doCommand();
|
||||
let [result] = await promiseLogin;
|
||||
await promiseHidden;
|
||||
|
||||
// Check that the values in the database match the expected values.
|
||||
let login = expectModifyLogin
|
||||
|
@ -6,12 +6,10 @@
|
||||
|
||||
async function fillTestPage(aBrowser) {
|
||||
await ContentTask.spawn(aBrowser, null, async function() {
|
||||
content.document
|
||||
.getElementById("form-basic-username")
|
||||
.setUserInput("my_username");
|
||||
content.document
|
||||
.getElementById("form-basic-password")
|
||||
.setUserInput("my_password");
|
||||
content.document.getElementById("form-basic-username").value =
|
||||
"my_username";
|
||||
content.document.getElementById("form-basic-password").value =
|
||||
"my_password";
|
||||
});
|
||||
info("fields filled");
|
||||
}
|
||||
|
@ -88,9 +88,6 @@ function verifyLogins(expectedLogins = []) {
|
||||
"Check timePasswordChanged"
|
||||
);
|
||||
}
|
||||
if (typeof expected.timeCreated !== "undefined") {
|
||||
is(login.timeCreated, expected.timeCreated, "Check timeCreated");
|
||||
}
|
||||
}
|
||||
}
|
||||
return allLogins;
|
||||
@ -111,11 +108,7 @@ async function submitFormAndGetResults(
|
||||
selectorValues,
|
||||
responseSelectors
|
||||
) {
|
||||
async function contentSubmitForm([contentFormAction, contentSelectorValues]) {
|
||||
const { WrapPrivileged } = ChromeUtils.import(
|
||||
"resource://specialpowers/WrapPrivileged.jsm",
|
||||
this
|
||||
);
|
||||
function contentSubmitForm([contentFormAction, contentSelectorValues]) {
|
||||
let doc = content.document;
|
||||
let form = doc.querySelector("form");
|
||||
if (contentFormAction) {
|
||||
@ -123,20 +116,9 @@ async function submitFormAndGetResults(
|
||||
}
|
||||
for (let [sel, value] of Object.entries(contentSelectorValues)) {
|
||||
try {
|
||||
let field = doc.querySelector(sel);
|
||||
let gotInput = ContentTaskUtils.waitForEvent(
|
||||
field,
|
||||
"input",
|
||||
"Got input event on " + sel
|
||||
);
|
||||
// we don't get an input event if the new value == the old
|
||||
field.value = "###";
|
||||
WrapPrivileged.wrap(field).setUserInput(value);
|
||||
await gotInput;
|
||||
doc.querySelector(sel).setUserInput(value);
|
||||
} catch (ex) {
|
||||
throw new Error(
|
||||
`submitForm: Couldn't set value of field at: ${sel}: ${ex.message}`
|
||||
);
|
||||
throw new Error(`submitForm: Couldn't set value of field at: ${sel}`);
|
||||
}
|
||||
}
|
||||
form.submit();
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
function submitForm() {
|
||||
// Get the password from the query string (exclude '?').
|
||||
let [username, password] = window.location.search.substring(1).split("|");
|
||||
SpecialPowers.wrap(userField).setUserInput(username);
|
||||
SpecialPowers.wrap(passField).setUserInput(password);
|
||||
userField.value = username;
|
||||
passField.value = password;
|
||||
form.submit();
|
||||
window.opener.formSubmitted();
|
||||
}
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(pass).setUserInput("notifyp1");
|
||||
pass.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
userField.value = "notifyu1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(passField).setUserInput("notifyp1");
|
||||
passField.value = "notifyp1";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("notifyu1");
|
||||
SpecialPowers.wrap(passField).setUserInput("pass2");
|
||||
userField.value = "notifyu1";
|
||||
passField.value = "pass2";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(userField).setUserInput("");
|
||||
SpecialPowers.wrap(passField).setUserInput("pass2");
|
||||
userField.value = "";
|
||||
passField.value = "pass2";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,8 @@
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
SpecialPowers.wrap(passField).setUserInput("pass2");
|
||||
SpecialPowers.wrap(passConfirmField).setUserInput("pass2");
|
||||
passField.value = "pass2";
|
||||
passConfirmField.value = "pass2";
|
||||
|
||||
form.submit();
|
||||
}
|
||||
|
@ -160,7 +160,6 @@ skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_prompt_promptAuth_proxy.html]
|
||||
skip-if = e10s || os == "linux" || toolkit == 'android' # Tests desktop prompts
|
||||
[test_recipe_login_fields.html]
|
||||
[test_submit_without_field_modifications.html]
|
||||
[test_username_focus.html]
|
||||
skip-if = toolkit == 'android' # android:autocomplete.
|
||||
[test_xhr.html]
|
||||
|
@ -113,57 +113,6 @@ function getIframeBrowsingContext(window, iframeNumber = 0) {
|
||||
return SpecialPowers.unwrap(bc.getChildren()[iframeNumber]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input values via setUserInput to emulate user input
|
||||
* and distinguish them from declarative or script-assigned values
|
||||
*/
|
||||
function setUserInputValues(parentNode, selectorValues) {
|
||||
for (let [selector, newValue] of Object.entries(selectorValues)) {
|
||||
info(`setUserInputValues, selector: ${selector}`);
|
||||
try {
|
||||
let field = SpecialPowers.wrap(parentNode.querySelector(selector));
|
||||
if (field.value == newValue) {
|
||||
// we don't get an input event if the new value == the old
|
||||
field.value += "#";
|
||||
}
|
||||
field.setUserInput(newValue);
|
||||
} catch (ex) {
|
||||
info(ex.message);
|
||||
info(ex.stack);
|
||||
ok(
|
||||
false,
|
||||
`setUserInputValues: Couldn't set value of field: ${ex.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function} [aFilterFn = undefined] Function to filter out irrelevant submissions.
|
||||
* @return {Promise} resolving when a relevant form submission was processed.
|
||||
*/
|
||||
function getSubmitMessage(aFilterFn = undefined) {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
PWMGR_COMMON_PARENT.addMessageListener(
|
||||
"formSubmissionProcessed",
|
||||
function processed(...args) {
|
||||
if (aFilterFn && !aFilterFn(...args)) {
|
||||
// This submission isn't the one we're waiting for.
|
||||
return;
|
||||
}
|
||||
|
||||
info("got formSubmissionProcessed");
|
||||
PWMGR_COMMON_PARENT.removeMessageListener(
|
||||
"formSubmissionProcessed",
|
||||
processed
|
||||
);
|
||||
resolve(...args);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for expected username/password in form.
|
||||
* @see `checkForm` below for a similar function.
|
||||
|
@ -36,9 +36,9 @@ function startTest() {
|
||||
|
||||
// Fill in the username and password fields, for account creation.
|
||||
// Form 1
|
||||
SpecialPowers.wrap($_(1, "uname")).setUserInput("newuser1");
|
||||
SpecialPowers.wrap($_(1, "pword")).setUserInput("newpass1");
|
||||
SpecialPowers.wrap($_(1, "qword")).setUserInput("newpass1");
|
||||
$_(1, "uname").value = "newuser1";
|
||||
$_(1, "pword").value = "newpass1";
|
||||
$_(1, "qword").value = "newpass1";
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var button = getFormSubmitButton(1);
|
||||
|
@ -46,10 +46,7 @@ const DEFAULT_ORIGIN = "http://mochi.test:8888";
|
||||
const TESTCASES = [
|
||||
{
|
||||
// Inputs
|
||||
document: `<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"[type=password]": "pass1",
|
||||
},
|
||||
document: `<input type=password value="pass1">`,
|
||||
inputIndexForFormLike: 0,
|
||||
|
||||
// Expected outputs similar to PasswordManager:onFormSubmit
|
||||
@ -60,12 +57,8 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"[type=password]": "pass1",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">`,
|
||||
inputIndexForFormLike: 0,
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -74,12 +67,8 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"[type=password]": "pass1",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">`,
|
||||
inputIndexForFormLike: 1,
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -88,14 +77,9 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input id="p1" type=password value="">
|
||||
<input id="p2" type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "pass1",
|
||||
"#p2": "pass2",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">`,
|
||||
inputIndexForFormLike: 2,
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -104,16 +88,10 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input id="p1" type=password value="">
|
||||
<input id="p2" type=password value="">
|
||||
<input id="p3" type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "pass1",
|
||||
"#p2": "pass2",
|
||||
"#p3": "pass2",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">
|
||||
<input type=password value="pass2">`,
|
||||
inputIndexForFormLike: 3,
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -122,20 +100,13 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input id="p1" type=password value="" form="form1">
|
||||
<input id="p2" type=password value="">
|
||||
document: `<input value="user1">
|
||||
<input type=password value="user2" form="form1">
|
||||
<input type=password value="pass1">
|
||||
<form id="form1">
|
||||
<input id="u2" value="">
|
||||
<input id="p3" type=password value="">
|
||||
<input value="user3">
|
||||
<input type=password value="pass2">
|
||||
</form>`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "user2",
|
||||
"#p2": "pass1",
|
||||
"#u2": "user3",
|
||||
"#p3": "pass2",
|
||||
},
|
||||
inputIndexForFormLike: 2,
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -145,16 +116,10 @@ const TESTCASES = [
|
||||
},
|
||||
{
|
||||
document: `<!-- recipe field override -->
|
||||
<input name="recipeuname" value="">
|
||||
<input id="u1" value="">
|
||||
<input id="p1" type=password value="">
|
||||
<input name="recipepword" type=password value="">`,
|
||||
selectorValues: {
|
||||
"[name='recipeuname']": "username from recipe",
|
||||
"#u1": "default field username",
|
||||
"#p1": "pass1",
|
||||
"[name='recipepword']": "pass2",
|
||||
},
|
||||
<input name="recipeuname" value="username from recipe">
|
||||
<input value="default field username">
|
||||
<input type=password value="pass1">
|
||||
<input name="recipepword" type=password value="pass2">`,
|
||||
inputIndexForFormLike: 2,
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -164,30 +129,36 @@ const TESTCASES = [
|
||||
},
|
||||
];
|
||||
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test() {
|
||||
let count = 0;
|
||||
let loginFrame = document.getElementById("loginFrame");
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
let frameDoc = SpecialPowers.wrap(loginFrame.contentWindow).document;
|
||||
let frameDoc = loginFrame.contentWindow.document;
|
||||
info("Starting testcase: " + JSON.stringify(tc));
|
||||
|
||||
let formProcessed = promiseFormsProcessed();
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
frameDoc.documentElement.innerHTML = tc.document;
|
||||
await formProcessed;
|
||||
// We eliminate no user input as a reason for not capturing by modifying the value
|
||||
setUserInputValues(frameDoc.documentElement, tc.selectorValues);
|
||||
|
||||
let inputForFormLike = frameDoc.querySelectorAll("input")[tc.inputIndexForFormLike];
|
||||
|
||||
let formLike = LoginFormFactory.createFromField(inputForFormLike);
|
||||
|
||||
info("Calling _onFormSubmit with FormLike");
|
||||
let submitProcessed = getSubmitMessage();
|
||||
let processedPromise = getSubmitMessage();
|
||||
LoginManagerChild.forWindow(frameDoc.defaultView)._onFormSubmit(formLike);
|
||||
|
||||
let submittedResult = await submitProcessed;
|
||||
let submittedResult = await processedPromise;
|
||||
|
||||
// Check data sent via PasswordManager:onFormSubmit
|
||||
is(submittedResult.origin, tc.origin, "Check origin");
|
||||
|
@ -47,10 +47,7 @@ const SCRIPTS = {
|
||||
const TESTCASES = [
|
||||
{
|
||||
// Inputs
|
||||
document: `<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"[type=password]": "pass1",
|
||||
},
|
||||
document: `<input type=password value="pass1">`,
|
||||
|
||||
// Expected outputs similar to PasswordManager:onFormSubmit
|
||||
origin: DEFAULT_ORIGIN,
|
||||
@ -60,12 +57,8 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"[type=password]": "pass1",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">`,
|
||||
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -74,14 +67,9 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input id="p1" type=password value="">
|
||||
<input id="p2" type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "pass1",
|
||||
"#p2": "pass2",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">`,
|
||||
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -90,16 +78,10 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: "pass1",
|
||||
},
|
||||
{
|
||||
document: `<input id="u1" value="">
|
||||
<input id="p1" type=password value="">
|
||||
<input id="p2" type=password value="">
|
||||
<input id="p3" type=password value="">`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "pass1",
|
||||
"#p2": "pass2",
|
||||
"#p3": "pass2",
|
||||
},
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass1">
|
||||
<input type=password value="pass2">
|
||||
<input type=password value="pass2">`,
|
||||
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -112,20 +94,13 @@ const TESTCASES = [
|
||||
// one FormLike's password fields with a magic "ignore-form-submission"
|
||||
// value so we can just focus on the other form. We then repeat the testcase
|
||||
// below with the other FormLike ignored.
|
||||
document: `<input id="u1" value="">
|
||||
<input type=password id="p1" value="" form="form1">
|
||||
<input type=password id="p2" value="">
|
||||
document: `<input value="user1">
|
||||
<input type=password value="ignore-form-submission" form="form1">
|
||||
<input type=password value="pass1">
|
||||
<form id="form1">
|
||||
<input id="u2" value="">
|
||||
<input id="p3" type=password value="">
|
||||
<input value="user3">
|
||||
<input type=password value="ignore-form-submission">
|
||||
</form>`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "ignore-form-submission",
|
||||
"#p2": "pass1",
|
||||
"#u2": "user3",
|
||||
"#p3": "ignore-form-submission",
|
||||
},
|
||||
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -134,20 +109,13 @@ const TESTCASES = [
|
||||
oldPasswordFieldValue: null,
|
||||
},
|
||||
{ // Same as above but with the other form ignored.
|
||||
document: `<input id="u1" value="">
|
||||
<input id="p1" type=password value="" form="form1">
|
||||
<input id="p2" type=password value="">
|
||||
document: `<input value="user1">
|
||||
<input type=password value="pass2" form="form1">
|
||||
<input type=password value="ignore-form-submission">
|
||||
<form id="form1">
|
||||
<input id="u2" value="">
|
||||
<input id="p3" type=password value="">
|
||||
<input value="user3">
|
||||
<input type=password value="pass2">
|
||||
</form>`,
|
||||
selectorValues: {
|
||||
"#u1": "user1",
|
||||
"#p1": "pass2",
|
||||
"#p2": "ignore-form-submission",
|
||||
"#u2": "user3",
|
||||
"#p3": "pass2",
|
||||
},
|
||||
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -157,16 +125,10 @@ const TESTCASES = [
|
||||
},
|
||||
{
|
||||
document: `<!-- recipe field override -->
|
||||
<input name="recipeuname" value="">
|
||||
<input id="u1" value="">
|
||||
<input id="p1" type=password value="">
|
||||
<input name="recipepword" type=password value="">`,
|
||||
selectorValues: {
|
||||
"[name='recipeuname']": "username from recipe",
|
||||
"#u1": "default field username",
|
||||
"#p1": "pass1",
|
||||
"[name='recipepword']": "pass2",
|
||||
},
|
||||
<input name="recipeuname" value="username from recipe">
|
||||
<input value="default field username">
|
||||
<input type=password value="pass1">
|
||||
<input name="recipepword" type=password value="pass2">`,
|
||||
|
||||
origin: DEFAULT_ORIGIN,
|
||||
formActionOrigin: DEFAULT_ORIGIN,
|
||||
@ -176,6 +138,26 @@ const TESTCASES = [
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {Function} [aFilterFn = undefined] Function to filter out irrelevant submissions.
|
||||
* @return {Promise} resolving when a relevant form submission was processed.
|
||||
*/
|
||||
function getSubmitMessage(aFilterFn = undefined) {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
if (aFilterFn && !aFilterFn(...args)) {
|
||||
// This submission isn't the one we're waiting for.
|
||||
return;
|
||||
}
|
||||
|
||||
info("got formSubmissionProcessed");
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function filterFormSubmissions(data) {
|
||||
return data.newPasswordField.value != "ignore-form-submission";
|
||||
}
|
||||
@ -207,20 +189,15 @@ add_task(async function test() {
|
||||
testDoc = "<form>" + testDoc + "</form>";
|
||||
}
|
||||
|
||||
let formProcessed = promiseFormsProcessed();
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
frameDoc.documentElement.innerHTML = testDoc;
|
||||
await formProcessed;
|
||||
// We eliminate no user input as a reason for not capturing by modifying the value
|
||||
setUserInputValues(frameDoc.documentElement, tc.selectorValues)
|
||||
|
||||
let submitProcessed = getSubmitMessage(filterFormSubmissions);
|
||||
// Wait for the form to be processed before trying to submit.
|
||||
await promiseFormsProcessed();
|
||||
let processedPromise = getSubmitMessage(filterFormSubmissions);
|
||||
info("Running " + scriptName + " script to cause a submission");
|
||||
frameDoc.defaultView.eval(SCRIPTS[scriptName]);
|
||||
|
||||
info("Waiting for formSubmissionProcsssed message");
|
||||
let submittedResult = await submitProcessed;
|
||||
info("Got for formSubmissionProcsssed message");
|
||||
let submittedResult = await processedPromise;
|
||||
|
||||
// Check data sent via PasswordManager:onFormSubmit
|
||||
is(submittedResult.origin, tc.origin, "Check origin");
|
||||
|
@ -53,34 +53,22 @@ const TESTCASES = [
|
||||
// Begin test cases that shouldn't trigger capture.
|
||||
{
|
||||
// Empty password field in a form
|
||||
document: `<form><input type=password value="xxx"></form>`,
|
||||
selectorValues: {
|
||||
"[type=password]": "",
|
||||
},
|
||||
document: `<form><input type=password value=""></form>`,
|
||||
},
|
||||
{
|
||||
// Empty password field
|
||||
document: `<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"[type=password]": "",
|
||||
},
|
||||
},
|
||||
{
|
||||
// Test with an input that would normally be captured but with SCRIPTS that
|
||||
// shouldn't trigger capture.
|
||||
document: `<input type=password value="">`,
|
||||
selectorValues: {
|
||||
"[type=password]": "pass2",
|
||||
},
|
||||
document: `<input type=password value="pass2">`,
|
||||
wouldCapture: true,
|
||||
},
|
||||
{
|
||||
// Test with an input that would normally be captured but with SCRIPTS that
|
||||
// shouldn't trigger capture.
|
||||
document: `<form><input type=password value=""></form>`,
|
||||
selectorValues: {
|
||||
"[type=password]": "pass2",
|
||||
},
|
||||
document: `<form><input type=password value="pass2"></form>`,
|
||||
wouldCapture: true,
|
||||
},
|
||||
];
|
||||
@ -116,8 +104,9 @@ add_task(async function test() {
|
||||
let frameDoc = SpecialPowers.wrap(loginFrame.contentWindow).document;
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
frameDoc.documentElement.innerHTML = tc.document;
|
||||
// We eliminate no user input as a reason for not capturing by modifying the value
|
||||
setUserInputValues(frameDoc.documentElement, tc.selectorValues);
|
||||
|
||||
// Wait for the form to be processed before trying to submit.
|
||||
await promiseFormsProcessed();
|
||||
|
||||
info("Running " + scriptName + " script to check for a submission");
|
||||
frameDoc.defaultView.eval(SCRIPTS[scriptName]);
|
||||
|
@ -63,15 +63,6 @@ async function loadFormIntoIframe(origin, html) {
|
||||
|
||||
// Wait for the form to be processed before trying to submit.
|
||||
await promiseFormsProcessed();
|
||||
|
||||
await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [html], function(contentHtml) {
|
||||
let doc = this.content.document;
|
||||
for (let field of doc.querySelectorAll("input")) {
|
||||
let actualValue = field.value;
|
||||
field.value = "";
|
||||
SpecialPowers.wrap(field).setUserInput(actualValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
@ -136,6 +127,20 @@ const TESTCASES = [
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* @return {Promise} resolving when form submission was processed.
|
||||
*/
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
PWMGR_COMMON_PARENT.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
PWMGR_COMMON_PARENT.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_new_logins() {
|
||||
for (let tc of TESTCASES) {
|
||||
info("Starting testcase: " + JSON.stringify(tc));
|
||||
@ -188,8 +193,8 @@ add_task(async function test_no_autofill_munged_username_matching_password() {
|
||||
await SpecialPowers.spawn(getIframeBrowsingContext(window, 0), [], function() {
|
||||
let doc = this.content.document;
|
||||
Assert.equal(doc.querySelector("[name='uname']").value, "", "Check username didn't get autofilled");
|
||||
SpecialPowers.wrap(doc.querySelector("[name='uname']")).setUserInput("real••••user");
|
||||
SpecialPowers.wrap(doc.querySelector("[name='pword']")).setUserInput("pass1");
|
||||
doc.querySelector("[name='uname']").setUserInput("real••••user");
|
||||
doc.querySelector("[name='pword']").setUserInput("pass1");
|
||||
});
|
||||
|
||||
// we shouldn't get the save password doorhanger...
|
||||
|
@ -12,6 +12,17 @@
|
||||
<script>
|
||||
let chromeScript = runChecksAfterCommonInit();
|
||||
|
||||
function getSubmitMessage() {
|
||||
info("getSubmitMessage");
|
||||
return new Promise((resolve, reject) => {
|
||||
chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) {
|
||||
info("got formSubmissionProcessed");
|
||||
chromeScript.removeMessageListener("formSubmissionProcessed", processed);
|
||||
resolve(...args);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.requestFlakyTimeout("Giving a chance for the unexpected popupshown to occur");
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
@ -34,8 +45,8 @@
|
||||
let password = document.getElementById("pfield");
|
||||
let submitButton = document.getElementById("submitBtn");
|
||||
|
||||
SpecialPowers.wrap(username).setUserInput("user");
|
||||
SpecialPowers.wrap(password).setUserInput("pass");
|
||||
username.value = "user";
|
||||
password.value = "pass";
|
||||
|
||||
let processedPromise = getSubmitMessage();
|
||||
let promptShownPromise = promisePromptShown("passwordmgr-prompt-save");
|
||||
|
@ -40,15 +40,12 @@ Login Manager test: input value change right after onsubmit event
|
||||
<script>
|
||||
/** Test for Login Manager: input value change right after onsubmit event **/
|
||||
add_task(async function checkFormValues() {
|
||||
SpecialPowers.wrap(document.getElementById("ufield")).setUserInput("testuser");
|
||||
SpecialPowers.wrap(document.getElementById("pfield")).setUserInput("testpass");
|
||||
document.getElementById("ufield").value = "testuser";
|
||||
document.getElementById("pfield").value = "testpass";
|
||||
is($_(1, "uname").value, "testuser", "Checking for filled username");
|
||||
is($_(1, "pword").value, "testpass", "Checking for filled password");
|
||||
|
||||
document.getElementById("form1").addEventListener("submit", () => {
|
||||
// deliberately assign to .value rather than setUserInput:
|
||||
// the scenario under test here is that script is changing/populating
|
||||
// fields after the user has clicked the submit button
|
||||
document.getElementById("ufield").value = "newuser";
|
||||
document.getElementById("pfield").value = "newpass";
|
||||
}, true);
|
||||
|
@ -1,233 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Don't send onFormSubmit message on navigation if the user did not interact
|
||||
with the login fields</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/AddTask.js"></script>
|
||||
<script type="text/javascript" src="pwmgr_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
|
||||
<div id="content">
|
||||
<iframe id="loginFrame">
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
<pre id="test"></pre>
|
||||
<script>
|
||||
const { TestUtils } = SpecialPowers.Cu.import("resource://testing-common/TestUtils.jsm");
|
||||
SimpleTest.requestFlakyTimeout("Giving a chance for the unexpected popup to show");
|
||||
let iframe = document.getElementById("loginFrame");
|
||||
|
||||
function waitForLoad() {
|
||||
return new Promise(resolve => {
|
||||
function handleLoad() {
|
||||
iframe.removeEventListener("load", handleLoad);
|
||||
resolve();
|
||||
}
|
||||
iframe.addEventListener("load", handleLoad);
|
||||
});
|
||||
}
|
||||
|
||||
async function setup(pageUrl) {
|
||||
let loadPromise = waitForLoad();
|
||||
iframe.src = pageUrl || "https://example.org/tests/toolkit/components/passwordmgr/test/mochitest/formless_basic.html";
|
||||
|
||||
await loadPromise;
|
||||
let win = SpecialPowers.wrap(iframe.contentWindow);
|
||||
let iframeDoc = win.document;
|
||||
|
||||
let link = document.createElement("a");
|
||||
link.setAttribute("href", "http://mochi.test:8888");
|
||||
iframeDoc.body.appendChild(link);
|
||||
|
||||
return {iframeDoc, link};
|
||||
}
|
||||
|
||||
function setValue(doc) {
|
||||
// assign to .value directly, we deliberately don't emulate user input
|
||||
doc.getElementById("form-basic-username").value = "user";
|
||||
doc.getElementById("form-basic-password").value = "pass";
|
||||
}
|
||||
|
||||
add_task(async function test_init() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
["signon.userInputRequiredToCapture.enabled", true],
|
||||
]});
|
||||
});
|
||||
|
||||
add_task(async function test_no_message_on_navigation() {
|
||||
// If login field values were set by the website, we don't message to save the
|
||||
// login values if the user did not interact with the fields before submiting.
|
||||
let elements = await setup();
|
||||
setValue(elements.iframeDoc);
|
||||
await promiseFormsProcessed();
|
||||
|
||||
let submitMessageSent = false;
|
||||
getSubmitMessage().then(value => {
|
||||
submitMessageSent = true;
|
||||
});
|
||||
|
||||
let loadPromise = waitForLoad();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
await loadPromise;
|
||||
|
||||
// allow time to pass before concluding no onFormSubmit message was sent
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
ok(!submitMessageSent, "onFormSubmit message is not sent on navigation since the login fields were not modified");
|
||||
});
|
||||
|
||||
add_task(async function test_prefd_off_message_on_navigation() {
|
||||
// Confirm the pref controls capture behavior with non-user-set field values.
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
["signon.userInputRequiredToCapture.enabled", false],
|
||||
]});
|
||||
|
||||
let elements = await setup();
|
||||
setValue(elements.iframeDoc);
|
||||
await promiseFormsProcessed();
|
||||
|
||||
let promiseSubmitMessage = getSubmitMessage();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
await promiseSubmitMessage;
|
||||
info("onFormSubmit message was sent as expected after navigation");
|
||||
|
||||
SpecialPowers.popPrefEnv();
|
||||
});
|
||||
|
||||
add_task(async function test_message_with_user_interaction_on_navigation() {
|
||||
let elements = await setup();
|
||||
setValue(elements.iframeDoc);
|
||||
await promiseFormsProcessed();
|
||||
|
||||
let username = elements.iframeDoc.getElementById("form-basic-username");
|
||||
username.setUserInput("foo");
|
||||
let promiseSubmitMessage = getSubmitMessage();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
await promiseSubmitMessage;
|
||||
info("onFormSubmit message was sent as expected after user interaction");
|
||||
});
|
||||
|
||||
add_task(async function test_empty_form_with_input_handler() {
|
||||
let elements = await setup();
|
||||
await promiseFormsProcessed();
|
||||
|
||||
elements.iframeDoc.getElementById("form-basic-username").setUserInput("user");
|
||||
elements.iframeDoc.getElementById("form-basic-password").setUserInput("pass");
|
||||
|
||||
let promiseSubmitMessage = getSubmitMessage();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
await promiseSubmitMessage;
|
||||
info("onFormSubmit message was sent as expected after user interaction");
|
||||
});
|
||||
|
||||
add_task(async function test_message_on_autofill_without_user_interaction() {
|
||||
runInParent(function addLogin() {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
let login = Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
.createInstance(Ci.nsILoginInfo);
|
||||
login.init("https://example.org", "https://example.org", null,
|
||||
"user1", "pass1", "", "");
|
||||
|
||||
Services.logins.addLogin(login);
|
||||
});
|
||||
|
||||
let elements = await setup();
|
||||
await promiseFormsProcessed();
|
||||
|
||||
// Check for autofilled values.
|
||||
let doc = elements.iframeDoc;
|
||||
let uname = doc.getElementById("form-basic-username");
|
||||
let pword = doc.getElementById("form-basic-password");
|
||||
is(uname.value, "user1", "Username was autofilled correctly");
|
||||
is(pword.value, "pass1", "Password was autofilled correctly");
|
||||
|
||||
let promiseSubmitMessage = getSubmitMessage();
|
||||
let loadPromise = waitForLoad();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
await loadPromise;
|
||||
|
||||
let messageData = await promiseSubmitMessage;
|
||||
ok(messageData.autoFilledLoginGuid, "Message was sent with autoFilledLoginGuid");
|
||||
});
|
||||
|
||||
add_task(async function test_message_on_autofill_with_user_interaction() {
|
||||
runInParent(function addLogin() {
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
let login = Cc["@mozilla.org/login-manager/loginInfo;1"]
|
||||
.createInstance(Ci.nsILoginInfo);
|
||||
login.init("https://example.org", "https://example.org", null,
|
||||
"user1", "pass1", "", "");
|
||||
|
||||
Services.logins.addLogin(login);
|
||||
});
|
||||
|
||||
let elements = await setup();
|
||||
await promiseFormsProcessed();
|
||||
// Check for autofilled values.
|
||||
let doc = elements.iframeDoc;
|
||||
let uname = doc.getElementById("form-basic-username");
|
||||
let pword = doc.getElementById("form-basic-password");
|
||||
is(uname.value, "user1", "Username was autofilled correctly");
|
||||
is(pword.value, "pass1", "Password was autofilled correctly");
|
||||
|
||||
elements.iframeDoc.getElementById("form-basic-username").setUserInput("newuser");
|
||||
let promiseSubmitMessage = getSubmitMessage();
|
||||
let loadPromise = waitForLoad();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
await loadPromise;
|
||||
|
||||
let messageData = await promiseSubmitMessage;
|
||||
ok(messageData.autoFilledLoginGuid, "Message was sent with autoFilledLoginGuid");
|
||||
is(messageData.usernameField.value, "newuser", "Message was sent with correct usernameField.value");
|
||||
info("Message was sent as expected after user interaction");
|
||||
});
|
||||
|
||||
add_task(async function test_no_message_on_user_input_from_other_form() {
|
||||
// ensure input into unrelated fields on the page don't change login form modified-ness
|
||||
let elements = await setup("http://example.com/tests/toolkit/components/passwordmgr/test/mochitest/form_basic.html");
|
||||
await promiseFormsProcessed();
|
||||
info("initial form processed");
|
||||
// Add a form which will not be submitted and an input associated with that form
|
||||
let doc = SpecialPowers.wrap(elements.iframeDoc);
|
||||
let loginForm = doc.querySelector("form");
|
||||
let fragment = doc.createDocumentFragment();
|
||||
let otherForm = doc.createElement("form");
|
||||
otherForm.id ="otherForm";
|
||||
fragment.appendChild(otherForm);
|
||||
|
||||
let alienField = doc.createElement("input");
|
||||
alienField.type = "text"; // not a password field
|
||||
alienField.setAttribute("form", "otherForm");
|
||||
// new field is child of the login, but a member of different non-login form via its .form property
|
||||
loginForm.appendChild(alienField);
|
||||
doc.body.appendChild(fragment);
|
||||
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
SpecialPowers.wrap(alienField).setUserInput("something");
|
||||
|
||||
let submitMessageSent = false;
|
||||
getSubmitMessage().then(data => {
|
||||
info("submit mesage data: " + JSON.stringify(data));
|
||||
submitMessageSent = true;
|
||||
});
|
||||
|
||||
info("submitting the form");
|
||||
let loadPromise = waitForLoad();
|
||||
SpecialPowers.wrap(elements.link).click();
|
||||
info("waiting for response to load");
|
||||
await loadPromise;
|
||||
|
||||
// allow time to pass before concluding no onFormSubmit message was sent
|
||||
await new Promise(res => setTimeout(res, 1000));
|
||||
ok(!submitMessageSent, "onFormSubmit message is not sent on navigation since no login fields were modified");
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user