Backed out 5 changesets (bug 1536728) for failing browser_context_menu_iframe.js on a CLOSED TREE

Backed out changeset 3c6419676ae1 (bug 1536728)
Backed out changeset dc86932a5454 (bug 1536728)
Backed out changeset 155ff3fe94c7 (bug 1536728)
Backed out changeset fe18718bfeb3 (bug 1536728)
Backed out changeset 80ffc576da4a (bug 1536728)

--HG--
rename : toolkit/components/passwordmgr/test/mochitest/test_LoginManagerContent_passwordEditedOrGenerated.html => toolkit/components/passwordmgr/test/mochitest/test_LoginManagerContent_generatedPasswordFilledOrEdited.html
rename : toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_onPasswordEditedOrGenerated.js => toolkit/components/passwordmgr/test/unit/test_LoginManagerParent_onGeneratedPasswordFilledOrEdited.js
This commit is contained in:
Andreea Pavel 2020-02-13 07:13:50 +02:00
Родитель 1fb4eedd2a
Коммит 94249cc894
22 изменённых файлов: 640 добавлений и 1297 удалений

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

@ -3938,7 +3938,6 @@ pref("signon.autologin.proxy", false);
pref("signon.formlessCapture.enabled", true);
pref("signon.generation.available", true);
pref("signon.generation.enabled", true);
pref("signon.passwordEditCapture.enabled", false);
pref("signon.privateBrowsingCapture.enabled", true);
pref("signon.storeWhenAutocompleteOff", true);
pref("signon.userInputRequiredToCapture.enabled", true);

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

@ -88,9 +88,6 @@ this.LoginHelper = {
this.userInputRequiredToCapture = Services.prefs.getBoolPref(
"signon.userInputRequiredToCapture.enabled"
);
this.passwordEditCaptureEnabled = Services.prefs.getBoolPref(
"signon.passwordEditCapture.enabled"
);
},
createLogger(aLogPrefix) {

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

@ -199,9 +199,7 @@ const observer = {
loginManagerChild.onFieldAutoComplete(focusedInput, details.guid);
} else if (style == "generatedPassword") {
loginManagerChild._highlightFilledField(focusedInput);
loginManagerChild._passwordEditedOrGenerated(focusedInput, {
triggeredByFillingGenerated: true,
});
loginManagerChild._generatedPasswordFilledOrEdited(focusedInput);
}
break;
}
@ -239,33 +237,22 @@ const observer = {
// Used to watch for changes to fields filled with generated passwords.
case "change": {
let triggeredByFillingGenerated = docState.generatedPasswordFields.has(
aEvent.target
);
if (
aEvent.target.hasBeenTypePassword &&
(triggeredByFillingGenerated ||
LoginHelper.passwordEditCaptureEnabled)
) {
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(
aEvent.target,
{
triggeredByFillingGenerated,
}
if (docState.generatedPasswordFields.has(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;
// React to input into fields filled with generated passwords.
if (docState.generatedPasswordFields.has(field)) {
LoginManagerChild.forWindow(
window
)._maybeStopTreatingAsGeneratedPasswordField(aEvent);
}
// React to input into potential username or password fields
if (
field.hasBeenTypePassword ||
LoginHelper.isUsernameFieldType(field)
@ -449,8 +436,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
formLikeRoot,
usernameValue,
passwordValue,
dismissed = false,
triggeredByFillingGenerated = false
dismissed = false
) {
let state = this.stateForDocument(formLikeRoot.ownerDocument);
const lastSentValues = state.lastSubmittedValuesByRootElement.get(
@ -464,9 +450,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
if (
lastSentValues.username == usernameValue &&
lastSentValues.password == passwordValue &&
lastSentValues.dismissed == dismissed &&
lastSentValues.triggeredByFillingGenerated ==
triggeredByFillingGenerated
lastSentValues.dismissed == dismissed
) {
log(
"_compareAndUpdatePreviouslySentValues: values are equivalent, returning true"
@ -481,7 +465,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
username: usernameValue,
password: passwordValue,
dismissed,
triggeredByFillingGenerated,
});
log(
"_compareAndUpdatePreviouslySentValues: values not equivalent, returning false"
@ -906,16 +889,10 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
}
// set up input event listeners so we know if the user has interacted with these fields
// * input: Listen for the field getting blanked (without blurring) or a paste
// * change: Listen for changes to the field filled with the generated password so we can preserve edits.
form.rootElement.addEventListener("input", observer, {
capture: true,
mozSystemGroup: true,
});
form.rootElement.addEventListener("change", observer, {
capture: true,
mozSystemGroup: true,
});
this._getLoginDataFromParent(form, { showMasterPassword: true })
.then(this.loginsFound.bind(this))
@ -1502,40 +1479,16 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
*/
_onFormSubmit(form) {
log("_onFormSubmit", form);
this._maybeSendFormInteractionMessage(
form,
"PasswordManager:onFormSubmit",
{
targetField: null,
isSubmission: true,
}
);
}
_maybeSendFormInteractionMessage(
form,
messageName,
{ targetField, isSubmission, triggeredByFillingGenerated }
) {
let doc = form.ownerDocument;
let win = doc.defaultView;
let passwordField = null;
if (targetField && targetField.hasBeenTypePassword) {
passwordField = targetField;
}
let logMessagePrefix = isSubmission ? "form submission" : "field edit";
let dismissedPrompt = !isSubmission;
// when filling a generated password, we do still want to message the parent
if (
!triggeredByFillingGenerated &&
PrivateBrowsingUtils.isContentWindowPrivate(win) &&
!LoginHelper.privateBrowsingCaptureEnabled
) {
// We won't do anything in private browsing mode anyway,
// so there's no need to perform further checks.
log(`(${logMessagePrefix} ignored in private browsing mode)`);
log("(form submission ignored in private browsing mode)");
return;
}
@ -1546,7 +1499,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
let origin = LoginHelper.getLoginOrigin(doc.documentURI);
if (!origin) {
log(`(${logMessagePrefix} ignored -- invalid origin)`);
log("(form submission ignored -- invalid origin)");
return;
}
@ -1561,15 +1514,6 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
oldPasswordField,
] = this._getFormFields(form, true, recipes);
// It's possible the field triggering this message isn't one of those found by _getFormFields' heuristics
if (
passwordField &&
passwordField != newPasswordField &&
passwordField != oldPasswordField
) {
newPasswordField = passwordField;
}
// Need at least 1 valid password field to do anything.
if (newPasswordField == null) {
return;
@ -1595,7 +1539,7 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
this._isAutocompleteDisabled(oldPasswordField)) &&
!LoginHelper.storeWhenAutocompleteOff
) {
log(`(${logMessagePrefix} ignored -- autocomplete=off found)`);
log("(form submission ignored -- autocomplete=off found)");
return;
}
@ -1612,15 +1556,16 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
: null;
let usernameValue = usernameField ? usernameField.value : null;
let formLikeRoot = FormLikeFactory.findRootForField(newPasswordField);
// Dismiss prompt if the username field is a credit card number AND
// if the password field is a three digit number. Also dismiss prompt if
// the password is a credit card number and the password field has attribute
// autocomplete="cc-number".
let dismissedPrompt = false;
let newPasswordFieldValue = newPasswordField.value;
if (
(!dismissedPrompt &&
(CreditCard.isValidNumber(usernameValue) &&
newPasswordFieldValue.trim().match(/^[0-9]{3}$/))) ||
(CreditCard.isValidNumber(usernameValue) &&
newPasswordFieldValue.trim().match(/^[0-9]{3}$/)) ||
(CreditCard.isValidNumber(newPasswordFieldValue) &&
newPasswordField.getAutocompleteInfo().fieldName == "cc-number")
) {
@ -1629,14 +1574,14 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
if (
this._compareAndUpdatePreviouslySentValues(
form.rootElement,
formLikeRoot,
usernameValue,
newPasswordField.value,
dismissedPrompt
)
) {
log(
`(${logMessagePrefix} ignored -- already submitted with the same username and password)`
"(form submission ignored -- already submitted with the same username and password)"
);
return;
}
@ -1644,12 +1589,9 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
let docState = this.stateForDocument(doc);
let fieldsModified = this._formHasModifiedFields(form);
if (!fieldsModified && LoginHelper.userInputRequiredToCapture) {
if (targetField) {
throw new Error("No user input on targetField");
}
// we know no fields in this form had user modifications, so don't prompt
log(
`(${logMessagePrefix} ignored -- submitting values that are not changed by the user)`
"(form submission ignored -- submitting values that are not changed by the user)"
);
return;
}
@ -1666,13 +1608,12 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
newPasswordField: mockPassword,
oldPasswordField: mockOldPassword,
dismissedPrompt,
triggeredByFillingGenerated,
};
this.sendAsyncMessage(messageName, detail);
this.sendAsyncMessage("PasswordManager:onFormSubmit", detail);
detail.form = form;
const evt = new CustomEvent(messageName, { detail });
const evt = new CustomEvent("PasswordManager:onFormSubmit", { detail });
win.windowRoot.dispatchEvent(evt);
}
@ -1694,8 +1635,8 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
.generatedPasswordFields;
fields.delete(passwordField);
// Remove all the event listeners added in _passwordEditedOrGenerated
for (let eventType of ["blur", "focus"]) {
// Remove all the event listeners added in _generatedPasswordFilledOrEdited
for (let eventType of ["blur", "change", "focus", "input"]) {
passwordField.removeEventListener(eventType, observer, {
capture: true,
mozSystemGroup: true,
@ -1711,50 +1652,67 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
* edited so that it can potentially be saved.
* @param {HTMLInputElement} passwordField
*/
_passwordEditedOrGenerated(
passwordField,
{ triggeredByFillingGenerated = false } = {}
) {
log("_passwordEditedOrGenerated", passwordField);
_generatedPasswordFilledOrEdited(passwordField) {
log("_generatedPasswordFilledOrEdited", passwordField);
if (!LoginHelper.enabled && triggeredByFillingGenerated) {
if (!LoginHelper.enabled) {
throw new Error(
"A generated password was filled while the password manager was disabled."
);
}
let loginForm = LoginFormFactory.createFromField(passwordField);
let win = passwordField.ownerGlobal;
let formLikeRoot = FormLikeFactory.findRootForField(passwordField);
let docState = this.stateForDocument(passwordField.ownerDocument);
docState.generatedPasswordFields.add(passwordField);
if (triggeredByFillingGenerated) {
docState.generatedPasswordFields.add(passwordField);
this._highlightFilledField(passwordField);
this._highlightFilledField(passwordField);
// blur/focus: listen for focus changes to we can mask/unmask generated passwords
for (let eventType of ["blur", "focus"]) {
passwordField.addEventListener(eventType, observer, {
capture: true,
mozSystemGroup: true,
});
}
// Unmask the password field
this._togglePasswordFieldMasking(passwordField, true);
// change: Listen for changes to the field filled with the generated password so we can preserve edits.
// input: Listen for the field getting blanked (without blurring) or a paste
for (let eventType of ["blur", "change", "focus", "input"]) {
passwordField.addEventListener(eventType, observer, {
capture: true,
mozSystemGroup: true,
});
}
// Unmask the password field
this._togglePasswordFieldMasking(passwordField, true);
// Once the generated password was filled we no longer want to autocomplete
// saved logins into a non-empty password field (see LoginAutoComplete.startSearch)
// because it is confusing.
this._fieldsWithPasswordGenerationForcedOn.delete(passwordField);
// Once the generated password was filled we no longer want to autocomplete
// saved logins into a non-empty password field (see LoginAutoComplete.startSearch)
// because it is confusing.
this._fieldsWithPasswordGenerationForcedOn.delete(passwordField);
let loginForm = LoginFormFactory.createFromField(passwordField);
let formActionOrigin = LoginHelper.getFormActionOrigin(loginForm);
let origin = LoginHelper.getLoginOrigin(
passwordField.ownerDocument.documentURI
);
let recipes = LoginRecipesContent.getRecipes(this, origin, win);
let [usernameField] = this._getFormFields(loginForm, false, recipes);
let username = (usernameField && usernameField.value) || "";
// Avoid prompting twice for the same value,
// e.g. context menu fill followed by change (blur) event
if (
this._compareAndUpdatePreviouslySentValues(
formLikeRoot,
username,
passwordField.value,
true // dismissed
)
) {
log(
"(generatedPasswordFilledOrEdited ignored -- already messaged with the same password value)"
);
return;
}
this._maybeSendFormInteractionMessage(
loginForm,
"PasswordManager:onPasswordEditedOrGenerated",
{
targetField: passwordField,
isSubmission: false,
triggeredByFillingGenerated,
}
);
this.sendAsyncMessage("PasswordManager:onGeneratedPasswordFilledOrEdited", {
formActionOrigin,
password: passwordField.value,
username: (usernameField && usernameField.value) || "",
});
}
_togglePasswordFieldMasking(passwordField, unmask) {

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

@ -216,14 +216,8 @@ class LoginManagerParent extends JSWindowActorParent {
break;
}
case "PasswordManager:onPasswordEditedOrGenerated": {
log("Received PasswordManager:onPasswordEditedOrGenerated");
if (gListenerForTests) {
log("calling gListenerForTests");
gListenerForTests("PasswordEditedOrGenerated", {});
}
let browser = this.getRootBrowser();
this._onPasswordEditedOrGenerated(browser, data);
case "PasswordManager:onGeneratedPasswordFilledOrEdited": {
this._onGeneratedPasswordFilledOrEdited(data);
break;
}
@ -635,8 +629,6 @@ class LoginManagerParent extends JSWindowActorParent {
}
}
let existingLogin = null;
let canMatchExistingLogin = true;
// Below here we have one login per hostPort + action + username with the
// matching scheme being preferred.
let logins = await LoginManagerParent.searchAndDedupeLogins(origin, {
@ -655,11 +647,13 @@ class LoginManagerParent extends JSWindowActorParent {
// password, allow the user to select from a list of applicable
// logins to update the password for.
if (!usernameField && oldPasswordField && logins.length) {
let prompter = this._getPrompter(browser);
let promptBrowser = LoginHelper.getBrowserForPrompt(browser);
if (logins.length == 1) {
existingLogin = logins[0];
let oldLogin = logins[0];
if (existingLogin.password == formLogin.password) {
recordLoginUse(existingLogin);
if (oldLogin.password == formLogin.password) {
recordLoginUse(oldLogin);
log(
"(Not prompting to save/change since we have no username and the " +
"only saved password matches the new password)"
@ -667,71 +661,76 @@ class LoginManagerParent extends JSWindowActorParent {
return;
}
formLogin.username = existingLogin.username;
formLogin.usernameField = existingLogin.usernameField;
formLogin.username = oldLogin.username;
formLogin.usernameField = oldLogin.usernameField;
prompter.promptToChangePassword(
promptBrowser,
oldLogin,
formLogin,
dismissedPrompt,
false, // notifySaved
autoSavedStorageGUID
);
return;
} else if (!generatedPW || generatedPW.value != newPasswordField.value) {
// Note: It's possible that that we already have the correct u+p saved
// but since we don't have the username, we don't know if the user is
// changing a second account to the new password so we ask anyways.
canMatchExistingLogin = false;
prompter.promptToChangePasswordWithUsernames(
promptBrowser,
logins,
formLogin
);
return;
}
}
if (canMatchExistingLogin && !existingLogin) {
// Look for an existing login that matches the form login.
for (let login of logins) {
let same;
let existingLogin = null;
// Look for an existing login that matches the form login.
for (let login of logins) {
let same;
// If one login has a username but the other doesn't, ignore
// the username when comparing and only match if they have the
// same password. Otherwise, compare the logins and match even
// if the passwords differ.
if (!login.username && formLogin.username) {
let restoreMe = formLogin.username;
formLogin.username = "";
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = restoreMe;
} else if (!formLogin.username && login.username) {
formLogin.username = login.username;
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = ""; // we know it's always blank.
} else {
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: true,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
}
// If one login has a username but the other doesn't, ignore
// the username when comparing and only match if they have the
// same password. Otherwise, compare the logins and match even
// if the passwords differ.
if (!login.username && formLogin.username) {
let restoreMe = formLogin.username;
formLogin.username = "";
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = restoreMe;
} else if (!formLogin.username && login.username) {
formLogin.username = login.username;
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = ""; // we know it's always blank.
} else {
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: true,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
}
if (same) {
existingLogin = login;
break;
}
if (same) {
existingLogin = login;
break;
}
}
let promptBrowser = LoginHelper.getBrowserForPrompt(browser);
let prompter = this._getPrompter(browser);
if (!canMatchExistingLogin) {
prompter.promptToChangePasswordWithUsernames(
promptBrowser,
logins,
formLogin
);
return;
}
if (existingLogin) {
log("Found an existing login matching this form submission");
// Change password if needed.
if (existingLogin.password != formLogin.password) {
log("...passwords differ, prompting to change.");
let prompter = this._getPrompter(browser);
prompter.promptToChangePassword(
promptBrowser,
existingLogin,
@ -759,41 +758,23 @@ class LoginManagerParent extends JSWindowActorParent {
}
// Prompt user to save login (via dialog or notification bar)
let prompter = this._getPrompter(browser);
prompter.promptToSavePassword(promptBrowser, formLogin, dismissedPrompt);
}
async _onPasswordEditedOrGenerated(
browser,
{
origin,
formActionOrigin,
autoFilledLoginGuid,
newPasswordField,
usernameField = null,
oldPasswordField,
triggeredByFillingGenerated = false,
}
) {
log(
"_onPasswordEditedOrGenerated, triggeredByFillingGenerated:",
triggeredByFillingGenerated
);
async _onGeneratedPasswordFilledOrEdited({
formActionOrigin,
password,
username = "",
}) {
log("_onGeneratedPasswordFilledOrEdited");
// If password storage is disabled, bail out.
if (!LoginHelper.storageEnabled) {
return;
if (gListenerForTests) {
gListenerForTests("PasswordFilledOrEdited", {});
}
if (!Services.logins.getLoginSavingEnabled(origin)) {
// No UI should be shown to offer generation in this case but a user may
// disable saving for the site after already filling one and they may then
// edit it.
log("_onPasswordEditedOrGenerated: saving is disabled for:", origin);
return;
}
if (!newPasswordField.value) {
log("_onPasswordEditedOrGenerated: The password field is empty");
if (!password) {
log("_onGeneratedPasswordFilledOrEdited: The password field is empty");
return;
}
@ -802,300 +783,203 @@ class LoginManagerParent extends JSWindowActorParent {
return;
}
let framePrincipalOrigin =
browsingContext.currentWindowGlobal.documentPrincipal.origin;
log(
"_onPasswordEditedOrGenerated: got framePrincipalOrigin: ",
framePrincipalOrigin
);
let {
originNoSuffix,
} = browsingContext.currentWindowGlobal.documentPrincipal;
let formOrigin = LoginHelper.getLoginOrigin(originNoSuffix);
if (formOrigin !== origin) {
if (!formOrigin) {
log(
"_onPasswordEditedOrGenerated: Invalid form origin:",
"_onGeneratedPasswordFilledOrEdited: Invalid form origin:",
browsingContext.currentWindowGlobal.documentPrincipal
);
return;
}
let formLogin = new LoginInfo(
origin,
formActionOrigin,
null,
usernameField ? usernameField.value : "",
newPasswordField.value,
usernameField ? usernameField.name : "",
newPasswordField.name
);
let existingLogin = null;
let canMatchExistingLogin = true;
let shouldAutoSaveLogin = triggeredByFillingGenerated;
let autoSavedLogin = null;
if (autoFilledLoginGuid) {
let [matchedLogin] = await Services.logins.searchLoginsAsync({
guid: autoFilledLoginGuid,
origin,
});
if (
matchedLogin &&
matchedLogin.password == formLogin.password &&
(!formLogin.username || // Also cover cases where only the password is requested.
matchedLogin.username == formLogin.username)
) {
log("The filled login matches the changed fields. Nothing to change.");
return;
}
if (!Services.logins.getLoginSavingEnabled(formOrigin)) {
// No UI should be shown to offer generation in thie case but a user may
// disable saving for the site after already filling one and they may then
// edit it.
log(
"_onGeneratedPasswordFilledOrEdited: saving is disabled for:",
formOrigin
);
return;
}
let framePrincipalOrigin =
browsingContext.currentWindowGlobal.documentPrincipal.origin;
let generatedPW = gGeneratedPasswordsByPrincipalOrigin.get(
framePrincipalOrigin
);
// Below here we have one login per hostPort + action + username with the
// matching scheme being preferred.
let logins = await LoginManagerParent.searchAndDedupeLogins(origin, {
formActionOrigin,
});
// only used in the generated pw case where we auto-save
let formLoginWithoutUsername;
let shouldAutoSaveLogin = true;
let loginToChange = null;
let autoSavedLogin = null;
if (triggeredByFillingGenerated && generatedPW) {
log("Got cached generatedPW");
formLoginWithoutUsername = new LoginInfo(
formOrigin,
formActionOrigin,
null,
"",
newPasswordField.value
);
if (password != generatedPW.value) {
// The user edited the field after generation to a non-empty value.
log("The field containing the generated password has changed");
if (newPasswordField.value != generatedPW.value) {
// The user edited the field after generation to a non-empty value.
log("The field containing the generated password has changed");
// Record telemetry for the first edit
if (!generatedPW.edited) {
Services.telemetry.recordEvent(
"pwmgr",
"filled_field_edited",
"generatedpassword"
);
log("filled_field_edited telemetry event recorded");
generatedPW.edited = true;
}
}
// This will throw if we can't look up the entry in the password/origin map
if (!generatedPW.filled) {
if (generatedPW.storageGUID) {
throw new Error(
"Generated password was saved in storage without being filled first"
);
}
// record first use of this generated password
// Record telemetry for the first edit
if (!generatedPW.edited) {
Services.telemetry.recordEvent(
"pwmgr",
"autocomplete_field",
"filled_field_edited",
"generatedpassword"
);
log("autocomplete_field telemetry event recorded");
generatedPW.filled = true;
log("filled_field_edited telemetry event recorded");
generatedPW.edited = true;
}
// We may have already autosaved this login
// The edit was to a login that was auto-saved.
// Note that it could have been saved in a totally different tab in the session.
if (generatedPW.storageGUID) {
[autoSavedLogin] = await Services.logins.searchLoginsAsync({
let existingLogins = await Services.logins.searchLoginsAsync({
guid: generatedPW.storageGUID,
origin: formOrigin,
});
if (autoSavedLogin) {
if (existingLogins.length) {
log(
"_onPasswordEditedOrGenerated: login to change is the auto-saved login"
"_onGeneratedPasswordFilledOrEdited: login to change is the auto-saved login"
);
existingLogin = autoSavedLogin;
loginToChange = existingLogins[0];
autoSavedLogin = loginToChange;
}
// The generated password login may have been deleted in the meantime.
// Proceed to maybe save a new login below.
}
generatedPW.value = newPasswordField.value;
if (!existingLogin) {
log(
"_onPasswordEditedOrGenerated: Didnt match generated-password login"
);
// Check if we already have a login saved for this site since we don't want to overwrite it in
// case the user still needs their old password to successfully complete a password change.
let matchedLogin = logins.find(login =>
formLoginWithoutUsername.matches(login, true)
);
if (matchedLogin) {
shouldAutoSaveLogin = false;
if (matchedLogin.password == formLoginWithoutUsername.password) {
// This login is already saved so show no new UI.
log("_onPasswordEditedOrGenerated: Matching login already saved");
return;
}
log(
"_onPasswordEditedOrGenerated: Login with empty username already saved for this site"
);
}
}
generatedPW.value = password;
}
// If we didn't find a username field, but seem to be changing a
// password, use the first match if there is only one
// If there's more than one we'll prompt to save with the initial formLogin
// and let the doorhanger code resolve this
if (
!triggeredByFillingGenerated &&
!existingLogin &&
!usernameField &&
oldPasswordField &&
logins.length
) {
if (logins.length == 1) {
existingLogin = logins[0];
let formLogin = new LoginInfo(
formOrigin,
formActionOrigin,
null,
username,
generatedPW.value
);
if (existingLogin.password == formLogin.password) {
let formLoginWithoutUsername = new LoginInfo(
formOrigin,
formActionOrigin,
null,
"",
generatedPW.value
);
// This will throw if we can't look up the entry in the password/origin map
if (!generatedPW.filled) {
if (generatedPW.storageGUID) {
throw new Error(
"Generated password was saved in storage without being filled first"
);
}
// record first use of this generated password
Services.telemetry.recordEvent(
"pwmgr",
"autocomplete_field",
"generatedpassword"
);
log("autocomplete_field telemetry event recorded");
generatedPW.filled = true;
}
if (!loginToChange) {
// Check if we already have a login saved for this site since we don't want to overwrite it in
// case the user still needs their old password to successfully complete a password change.
// An empty formActionOrigin is used as a wildcard to not restrict to action matches.
let logins = await LoginManagerParent.searchAndDedupeLogins(formOrigin, {
acceptDifferentSubdomains: false,
httpRealm: null,
ignoreActionAndRealm: false,
});
let matchedLogin = logins.find(login =>
formLoginWithoutUsername.matches(login, true)
);
if (matchedLogin) {
shouldAutoSaveLogin = false;
if (matchedLogin.password == formLoginWithoutUsername.password) {
// This login is already saved so show no new UI.
log(
"(Not prompting to save/change since we have no username and the " +
"only saved password matches the new password)"
"_onGeneratedPasswordFilledOrEdited: Matching login already saved"
);
return;
}
formLogin.username = existingLogin.username;
formLogin.usernameField = existingLogin.usernameField;
} else if (!generatedPW || generatedPW.value != newPasswordField.value) {
// Note: It's possible that that we already have the correct u+p saved
// but since we don't have the username, we don't know if the user is
// changing a second account to the new password so we ask anyways.
canMatchExistingLogin = false;
log(
"_onGeneratedPasswordFilledOrEdited: Login with empty username already saved for this site"
);
}
}
if (canMatchExistingLogin && !existingLogin) {
// Look for an existing login that matches the form login.
for (let login of logins) {
let same;
// If one login has a username but the other doesn't, ignore
// the username when comparing and only match if they have the
// same password. Otherwise, compare the logins and match even
// if the passwords differ.
if (!login.username && formLogin.username) {
let restoreMe = formLogin.username;
formLogin.username = "";
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = restoreMe;
} else if (!formLogin.username && login.username) {
formLogin.username = login.username;
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: false,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
formLogin.username = ""; // we know it's always blank.
} else {
same = LoginHelper.doLoginsMatch(formLogin, login, {
ignorePassword: true,
ignoreSchemes: LoginHelper.schemeUpgrades,
});
}
if (same) {
existingLogin = login;
log("_onPasswordEditedOrGenerated: matched saved login");
break;
}
if (
(matchedLogin = logins.find(login => formLogin.matches(login, true)))
) {
// We're updating a previously-saved login
loginToChange = matchedLogin;
}
}
if (shouldAutoSaveLogin) {
if (existingLogin && existingLogin == autoSavedLogin) {
if (loginToChange && loginToChange == autoSavedLogin) {
log(
"_onPasswordEditedOrGenerated: updating auto-saved login with changed password"
"_onGeneratedPasswordFilledOrEdited: updating auto-saved login with changed password"
);
Services.logins.modifyLogin(
existingLogin,
loginToChange,
LoginHelper.newPropertyBag({
password: formLogin.password,
password,
})
);
// Update `existingLogin` with the new password if modifyLogin didn't
// Update `loginToChange` with the new password if modifyLogin didn't
// throw so that the prompts later uses the new password.
existingLogin.password = formLogin.password;
loginToChange.password = password;
} else {
log(
"_onPasswordEditedOrGenerated: auto-saving new login with empty username"
"_onGeneratedPasswordFilledOrEdited: auto-saving new login with empty username"
);
existingLogin = Services.logins.addLogin(formLoginWithoutUsername);
loginToChange = Services.logins.addLogin(formLoginWithoutUsername);
// Remember the GUID where we saved the generated password so we can update
// the login if the user later edits the generated password.
generatedPW.storageGUID = existingLogin.guid;
generatedPW.storageGUID = loginToChange.guid;
}
} else {
log("_onPasswordEditedOrGenerated: not auto-saving this login");
log(
"_onGeneratedPasswordFilledOrEdited: not auto-saving/updating this login"
);
}
let browser = this.getRootBrowser();
let prompter = this._getPrompter(browser);
let promptBrowser = LoginHelper.getBrowserForPrompt(browser);
if (existingLogin) {
if (loginToChange) {
// Show a change doorhanger to allow modifying an already-saved login
// e.g. to add a username or update the password.
let autoSavedStorageGUID = "";
if (
generatedPW &&
generatedPW.value == existingLogin.password &&
generatedPW.storageGUID == existingLogin.guid
generatedPW.value == loginToChange.password &&
generatedPW.storageGUID == loginToChange.guid
) {
autoSavedStorageGUID = generatedPW.storageGUID;
}
// Change password if needed.
if (
(shouldAutoSaveLogin && !formLogin.username) ||
existingLogin.password != formLogin.password
) {
log(
"_onPasswordEditedOrGenerated: promptToChangePassword with autoSavedStorageGUID: " +
autoSavedStorageGUID
);
prompter.promptToChangePassword(
promptBrowser,
existingLogin,
formLogin,
true, // dismissed prompt
shouldAutoSaveLogin, // notifySaved
autoSavedStorageGUID // autoSavedLoginGuid
);
} else if (!existingLogin.username && formLogin.username) {
log("...empty username update, prompting to change.");
prompter.promptToChangePassword(
promptBrowser,
existingLogin,
formLogin,
true, // dismissed prompt
shouldAutoSaveLogin, // notifySaved
autoSavedStorageGUID // autoSavedLoginGuid
);
} else {
log("_onPasswordEditedOrGenerated: No change to existing login");
}
log(
"_onGeneratedPasswordFilledOrEdited: promptToChangePassword with autoSavedStorageGUID: " +
autoSavedStorageGUID
);
prompter.promptToChangePassword(
promptBrowser,
loginToChange,
formLogin,
true, // dismissed prompt
shouldAutoSaveLogin, // notifySaved
autoSavedStorageGUID // autoSavedLoginGuid
);
return;
}
log("_onPasswordEditedOrGenerated: no matching login to save/update");
log("_onGeneratedPasswordFilledOrEdited: no matching login to save/update");
prompter.promptToSavePassword(
promptBrowser,
formLogin,

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

@ -41,7 +41,6 @@ support-files =
form_crossframe_inner.html
[browser_doorhanger_dismissed_for_ccnumber.js]
[browser_doorhanger_empty_password.js]
[browser_doorhanger_form_password_edit.js]
[browser_doorhanger_generated_password.js]
support-files =
form_password_change.html

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

@ -192,14 +192,12 @@ add_task(async function fill_generated_password_nonempty_field() {
},
async function(browser) {
await SimpleTest.promiseFocus(browser.ownerGlobal);
await changeContentFormValues(browser, {
[passwordInputSelector]: "aa",
});
await SpecialPowers.spawn(
browser,
[[passwordInputSelector]],
function checkInitialFieldValue(inputSelector) {
const input = content.document.querySelector(inputSelector);
input.setUserInput("aa");
is(
content.getComputedStyle(input).filter,
"none",
@ -235,8 +233,6 @@ add_task(async function fill_generated_password_nonempty_field() {
);
}
);
LoginTestUtils.clearData();
LoginTestUtils.resetGeneratedPasswordsCache();
});
add_task(async function fill_generated_password_with_matching_logins() {
@ -471,7 +467,7 @@ add_task(async function test_edited_generated_password_in_new_tab() {
}
);
LoginTestUtils.clearData();
Services.logins.removeAllLogins();
LoginTestUtils.resetGeneratedPasswordsCache();
await SpecialPowers.popPrefEnv();
});

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

@ -39,7 +39,7 @@ add_task(async function test_context_menu_iframe_fill() {
await openPasswordContextMenu(
browser,
"#form-basic-password",
() => true,
null,
browser.browsingContext.getChildren()[0]
);
@ -60,24 +60,39 @@ add_task(async function test_context_menu_iframe_fill() {
);
// Execute the command of the first login menuitem found at the context menu.
let passwordChangedPromise = SpecialPowers.spawn(
browser.browsingContext.getChildren()[0],
[],
function(inputname) {
return new Promise(resolve => {
let passwordInput = content.document.getElementById(
"form-basic-password"
);
// Cannot pass resolve directly to the event listener, as then
// spawn will try to return the non-serializable event passed to the listener
// and generate an error.
passwordInput.addEventListener(
"input",
() => {
resolve();
},
{ once: true }
);
});
}
);
// Wait a tick for SpecialPowers.spawn to add the input listener.
await new Promise(resolve => {
SimpleTest.executeSoon(resolve);
});
let firstLoginItem = popupMenu.getElementsByClassName(
"context-login-item"
)[0];
ok(firstLoginItem, "Found the first login item");
firstLoginItem.doCommand();
await TestUtils.waitForTick();
ok(
BrowserTestUtils.is_visible(firstLoginItem),
"First login menuitem is visible"
);
info("Clicking on the firstLoginItem");
// click on the login item to fill the password field, and send tab to trigger a change event
await EventUtils.synthesizeMouseAtCenter(firstLoginItem, {});
await EventUtils.synthesizeKey("KEY_Tab");
await TestUtils.waitForTick();
await passwordChangedPromise;
// Find the used login by it's username.
let login = getLoginFromUsername(firstLoginItem.label);
@ -95,9 +110,6 @@ add_task(async function test_context_menu_iframe_fill() {
let contextMenu = document.getElementById("contentAreaContextMenu");
contextMenu.hidePopup();
await cleanupDoorhanger();
await cleanupPasswordNotifications();
}
);
});
@ -112,12 +124,10 @@ add_task(async function test_context_menu_iframe_sandbox() {
url: TEST_ORIGIN + IFRAME_PAGE_PATH,
},
async function(browser) {
info("Opening context menu for test_context_menu_iframe_sandbox");
await openPasswordContextMenu(
browser,
"#form-basic-password",
function checkDisabled() {
info("checkDisabled for test_context_menu_iframe_sandbox");
let popupHeader = document.getElementById("fill-login");
ok(
popupHeader.hidden,

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

@ -15,31 +15,17 @@ add_task(async function test_doorhanger_dismissal_un() {
// the password field is a three digit numberic value,
// we automatically dismiss the save logins prompt on submission.
let passwordFilledPromise = LoginHelper.passwordEditCaptureEnabled
? listenForTestNotification("PasswordEditedOrGenerated")
: TestUtils.waitForTick();
await SpecialPowers.spawn(browser, [], async () => {
content.document
.getElementById("form-basic-password")
.setUserInput("123");
});
info("Waiting for passwordFilledPromise");
await passwordFilledPromise;
// reset doorhanger/notifications, we're only interested in the submit outcome
await cleanupDoorhanger();
await cleanupPasswordNotifications();
let processedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async () => {
// fill the username here to avoid the caching in LoginManagerChild that will prevent
// a duplicate message being sent to the parent process. We are interested in the state
// of the doorhanger created and don't want a false positive from the password-edited handling
content.document
.getElementById("form-basic-username")
.setUserInput("4111111111111111");
content.document
.getElementById("form-basic-password")
.setUserInput("123");
content.document.getElementById("form-basic-submit").click();
});
info("Waiting for FormSubmit");
await processedPromise;
let notif = getCaptureDoorhanger("password-save");
@ -62,27 +48,18 @@ add_task(async function test_doorhanger_dismissal_pw() {
// the password field is also tagged autocomplete="cc-number",
// we automatically dismiss the save logins prompt on submission.
let passwordFilledPromise = LoginHelper.passwordEditCaptureEnabled
? listenForTestNotification("PasswordEditedOrGenerated")
: TestUtils.waitForTick();
let processedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async () => {
content.document
.getElementById("form-basic-username")
.setUserInput("aaa");
content.document
.getElementById("form-basic-password")
.setUserInput("4111111111111111");
content.document
.getElementById("form-basic-password")
.setAttribute("autocomplete", "cc-number");
});
await passwordFilledPromise;
// reset doorhanger/notifications, we're only interested in the submit outcome
await cleanupDoorhanger();
await cleanupPasswordNotifications();
let processedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async () => {
content.document
.getElementById("form-basic-username")
.setUserInput("aaa");
content.document.getElementById("form-basic-submit").click();
});
await processedPromise;
@ -106,24 +83,14 @@ add_task(async function test_doorhanger_shown_on_un_with_invalid_ccnumber() {
// If the username field has a CC number that is invalid,
// we show the doorhanger to save logins like we usually do.
let passwordFilledPromise = LoginHelper.passwordEditCaptureEnabled
? listenForTestNotification("PasswordEditedOrGenerated")
: TestUtils.waitForTick();
await SpecialPowers.spawn(browser, [], async () => {
content.document
.getElementById("form-basic-password")
.setUserInput("411");
});
await passwordFilledPromise;
// reset doorhanger/notifications, we're only interested in the submit outcome
await cleanupDoorhanger();
await cleanupPasswordNotifications();
let processedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async () => {
content.document
.getElementById("form-basic-username")
.setUserInput("1234123412341234");
content.document
.getElementById("form-basic-password")
.setUserInput("411");
content.document.getElementById("form-basic-submit").click();
});
await processedPromise;
@ -163,29 +130,11 @@ add_task(async function test_doorhanger_dismissal_on_change() {
);
Services.logins.addLogin(login);
let passwordFilledPromise = LoginHelper.passwordEditCaptureEnabled
? listenForTestNotification("PasswordEditedOrGenerated")
: TestUtils.waitForTick();
let processedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async () => {
content.document
.getElementById("form-basic-password")
.setUserInput("111");
// make a temporary username change to defeat the message caching
content.document
.getElementById("form-basic-username")
.setUserInput("changeduser");
});
await passwordFilledPromise;
// reset doorhanger/notifications, we're only interested in the submit outcome
await cleanupDoorhanger();
await cleanupPasswordNotifications();
let processedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async () => {
// restore the username
content.document
.getElementById("form-basic-username")
.setUserInput("4111111111111111");
content.document.getElementById("form-basic-submit").click();
});
await processedPromise;

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

@ -1,318 +0,0 @@
/**
* Test changed (not submitted) passwords produce the right doorhangers/notifications
*/
/* eslint no-shadow:"off" */
"use strict";
// The origin for the test URIs.
const TEST_ORIGIN = "https://example.com";
const BASIC_FORM_PAGE_PATH = DIRECTORY_PATH + "form_basic.html";
const passwordInputSelector = "#form-basic-password";
const usernameInputSelector = "#form-basic-username";
let testCases = [
{
name: "Enter password",
prefEnabled: true,
logins: [],
formDefaults: {},
formChanges: {
[passwordInputSelector]: "abcXYZ",
},
expected: {
initialForm: {
username: "",
password: "",
},
doorhanger: {
type: "password-save",
dismissed: true,
anchorExtraAttr: "",
username: "",
password: "abcXYZ",
toggle: "visible",
},
},
},
{
name: "Change password",
prefEnabled: true,
logins: [],
formDefaults: {
[passwordInputSelector]: "pass1",
},
formChanges: {
[passwordInputSelector]: "pass-changed",
},
expected: {
initialForm: {
username: "",
password: "pass1",
},
doorhanger: {
type: "password-save",
dismissed: true,
anchorExtraAttr: "",
username: "",
password: "pass-changed",
toggle: "visible",
},
},
},
{
name: "Change autofilled password",
prefEnabled: true,
logins: [{ username: "user1", password: "autopass" }],
formDefaults: {},
formChanges: {
[passwordInputSelector]: "autopass-changed",
},
expected: {
formAutofilled: true,
initialForm: {
username: "user1",
password: "autopass",
},
doorhanger: {
type: "password-change",
dismissed: true,
anchorExtraAttr: "",
username: "user1",
password: "autopass-changed",
},
},
},
{
name: "Change autofilled username and password",
prefEnabled: true,
logins: [{ username: "user1", password: "pass1" }],
formDefaults: {},
formChanges: {
[usernameInputSelector]: "user2",
[passwordInputSelector]: "pass2",
},
expected: {
formAutofilled: true,
initialForm: {
username: "user1",
password: "pass1",
},
doorhanger: {
type: "password-save",
dismissed: true,
anchorExtraAttr: "",
username: "user2",
password: "pass2",
toggle: "visible",
},
},
},
{
name: "Change password pref disabled",
prefEnabled: false,
logins: [],
formDefaults: {
[passwordInputSelector]: "pass1",
},
formChanges: {
[passwordInputSelector]: "pass-changed",
},
expected: {
initialForm: {
username: "",
password: "pass1",
},
doorhanger: null,
},
},
];
for (let testData of testCases) {
let tmp = {
async [testData.name]() {
await SpecialPowers.pushPrefEnv({
set: [["signon.passwordEditCapture.enabled", testData.prefEnabled]],
});
info("testing with: " + JSON.stringify(testData));
await testPasswordChange(testData);
await SpecialPowers.popPrefEnv();
},
};
add_task(tmp[testData.name]);
}
async function testPasswordChange({
logins = [],
formDefaults = {},
formChanges = {},
expected,
}) {
await LoginTestUtils.clearData();
await cleanupDoorhanger();
let url = TEST_ORIGIN + BASIC_FORM_PAGE_PATH;
for (let login of logins) {
await LoginTestUtils.addLogin(login);
}
for (let login of Services.logins.getAllLogins()) {
info(`Saved login: ${login.username}, ${login.password}, ${login.origin}`);
}
let formProcessedPromise = expected.formAutofilled
? listenForTestNotification("FormProcessed")
: Promise.resolve();
info("Opening tab with url: " + url);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url,
},
async function(browser) {
info(`Opened tab with url: ${url}, waiting for focus`);
await SimpleTest.promiseFocus(browser.ownerGlobal);
info("Waiting for form-processed message");
await formProcessedPromise;
await initForm(browser, formDefaults);
await checkForm(browser, expected.initialForm);
info("form checked");
let passwordEditedMessage = listenForTestNotification(
"PasswordEditedOrGenerated"
);
await changeContentFormValues(browser, formChanges);
info("form edited, waiting for change message");
let gotMessage;
passwordEditedMessage.then(() => (gotMessage = true));
try {
await TestUtils.waitForCondition(
() => {
return gotMessage;
},
`Waiting for passwordEditedMessage`,
undefined,
5
);
} catch (ex) {
ok(!expected.doorhanger, "Message not sent");
}
if (expected.doorhanger) {
is(gotMessage, !!expected.doorhanger, "Check message was sent");
} else {
let notif;
try {
await TestUtils.waitForCondition(
() => {
return (notif = PopupNotifications.getNotification(
"password",
browser
));
},
`Waiting to ensure no notification`,
undefined,
5
);
} catch (ex) {}
ok(!notif, "No doorhanger expected");
// the remainder of the test is for doorhanger-expected cases
return;
}
let notificationType = expected.doorhanger.type;
ok(
/^password-save|password-change$/.test(notificationType),
"test provided an expected notification type: " + notificationType
);
info("waiting for doorhanger");
await waitForDoorhanger(browser, notificationType);
info("verifying doorhanger");
let notif = await openAndVerifyDoorhanger(
browser,
notificationType,
expected.doorhanger
);
ok(notif, "Doorhanger was shown");
let promiseHidden = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popuphidden"
);
clickDoorhangerButton(notif, DONT_CHANGE_BUTTON);
await promiseHidden;
info("cleanup doorhanger");
await cleanupDoorhanger(notif);
}
);
}
async function initForm(browser, formDefaults) {
await ContentTask.spawn(browser, formDefaults, async function(
selectorValues
) {
for (let [sel, value] of Object.entries(selectorValues)) {
content.document.querySelector(sel).value = value;
}
});
}
async function checkForm(browser, expected) {
await ContentTask.spawn(
browser,
{
[passwordInputSelector]: expected.password,
[usernameInputSelector]: expected.username,
},
async function contentCheckForm(selectorValues) {
for (let [sel, value] of Object.entries(selectorValues)) {
let field = content.document.querySelector(sel);
is(field.value, value, sel + " has the expected initial value");
}
}
);
}
async function openAndVerifyDoorhanger(browser, type, expected) {
// check a dismissed prompt was shown with extraAttr attribute
let notif = getCaptureDoorhanger(type);
ok(notif, `${type} doorhanger was created`);
is(
notif.dismissed,
expected.dismissed,
"Check notification dismissed property"
);
is(
notif.anchorElement.getAttribute("extraAttr"),
expected.anchorExtraAttr,
"Check icon extraAttr attribute"
);
let { panel } = PopupNotifications;
// if the doorhanged is dimissed, we will open it to check panel contents
is(panel.state, "closed", "Panel is initially closed");
let promiseShown = BrowserTestUtils.waitForEvent(panel, "popupshown");
// synthesize click on anchor as this also blurs the form field triggering
// a change event
EventUtils.synthesizeMouseAtCenter(notif.anchorElement, {});
await promiseShown;
await Promise.resolve();
await checkDoorhangerUsernamePassword(expected.username, expected.password);
let notificationElement = PopupNotifications.panel.childNodes[0];
let toggleCheckboxHidden = notificationElement
.querySelector("#password-notification-visibilityToggle")
.getAttribute("hidden");
if (expected.toggle == "visible") {
todo(!toggleCheckboxHidden, "Toggle checkbox visible as expected");
} else if (expected.toggle == "hidden") {
todo(toggleCheckboxHidden, "Toggle checkbox hidden as expected");
} else {
info("Not checking toggle checkbox visibility");
}
return notif;
}

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

@ -1011,7 +1011,7 @@ add_task(
);
info("Waiting to openAndVerifyDoorhanger");
// also moves focus, producing another onPasswordEditedOrGenerated message from content
// also moves focus, producing another onGeneratedPasswordFilledOrEdited message from content
let notif = await openAndVerifyDoorhanger(browser, "password-change", {
dismissed: true,
anchorExtraAttr: "attention",

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

@ -103,19 +103,20 @@ add_task(async function test_edit_password() {
"popupshown",
event => event.target == PopupNotifications.panel
);
let formSubmittedPromise = listenForTestNotification("FormSubmit");
await changeContentFormValues(browser, {
"#form-basic-username": testCase.usernameInPage,
"#form-basic-password": testCase.passwordInPage,
await SpecialPowers.spawn(browser, [testCase], async function(
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").submit();
});
await TestUtils.waitForTick();
await SpecialPowers.spawn(browser, [], async function() {
content.document.getElementById("form-basic").submit();
});
await formSubmittedPromise;
let notif = await waitForDoorhanger(browser, "any");
await promiseShown;
let notificationElement = PopupNotifications.panel.childNodes[0];
// Modify the username & password in the dialog if requested.
await updateDoorhangerInputValues({
@ -145,7 +146,8 @@ add_task(async function test_edit_password() {
PopupNotifications.panel,
"popuphidden"
);
clickDoorhangerButton(notif, CHANGE_BUTTON);
notificationElement.button.doCommand();
let [result] = await promiseLogin;
await promiseHidden;

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

@ -73,7 +73,6 @@ add_task(async function test_remember_opens() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
});
});
@ -85,7 +84,6 @@ add_task(async function test_clickNever() {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(!notif.dismissed, "doorhanger is not dismissed");
ok(notif, "got notification popup");
is(
true,
@ -135,7 +133,6 @@ add_task(async function test_clickRemember() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(
Services.logins.getAllLogins().length,
@ -165,7 +162,6 @@ add_task(async function test_clickRemember() {
await testSubmittingLoginForm("subtst_notifications_1.html", function(
fieldValues
) {
// form login matches a saved login, we don't expect a notification on change or submit
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("password-save");
@ -219,7 +215,6 @@ add_task(async function test_rememberSignonsTrue() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
});
@ -244,7 +239,6 @@ add_task(async function test_autocompleteOffUsername() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "checking for notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
});
@ -267,7 +261,6 @@ add_task(async function test_autocompleteOffPassword() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "checking for notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
});
@ -288,7 +281,6 @@ add_task(async function test_autocompleteOffForm() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "checking for notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
});
@ -329,7 +321,6 @@ add_task(async function test_pwOnlyNewLoginMatchesUPForm() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "checking for notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(
notif.message,
"Would you like to add a username to the saved password?",
@ -384,7 +375,6 @@ add_task(async function test_pwOnlyOldLoginMatchesUPForm() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "checking for notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(
notif.message,
"Would you like to add a username to the saved password?",
@ -451,7 +441,6 @@ add_task(async function test_pwOnlyFormDoesntMatchExisting() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
});
@ -476,7 +465,6 @@ add_task(async function test_changeUPLoginOnUPForm_dont() {
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(notif.message, "Would you like to update this login?", "Check message");
await checkDoorhangerUsernamePassword("notifyu1", "pass2");
@ -504,7 +492,6 @@ add_task(async function test_changeUPLoginOnUPForm_change() {
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(notif.message, "Would you like to update this login?", "Check message");
await checkDoorhangerUsernamePassword("notifyu1", "pass2");
@ -539,7 +526,6 @@ add_task(async function test_changePLoginOnUPForm() {
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(notif.message, "Would you like to update this password?", "Check msg");
await checkDoorhangerUsernamePassword("", "pass2");
@ -568,7 +554,6 @@ add_task(async function test_changePLoginOnPForm() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(notif.message, "Would you like to update this password?", "Check msg");
await checkDoorhangerUsernamePassword("", "notifyp1");
@ -596,7 +581,6 @@ add_task(async function test_checkUPSaveText() {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(!notif.dismissed, "doorhanger is not dismissed");
ok(notif, "got notification popup");
// Check the text, which comes from the localized saveLoginMsg string.
let notificationText = notif.message;
@ -624,7 +608,6 @@ add_task(async function test_checkPSaveText() {
is(fieldValues.username, "null", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(!notif.dismissed, "doorhanger is not dismissed");
ok(notif, "got notification popup");
// Check the text, which comes from the localized saveLoginMsgNoUser string.
let notificationText = notif.message;
@ -655,7 +638,6 @@ add_task(async function test_capture2pw0un() {
is(fieldValues.username, "null", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(!notif.dismissed, "doorhanger is not dismissed");
ok(notif, "got notification popup");
await cleanupDoorhanger(notif);
}
@ -683,7 +665,6 @@ add_task(async function test_change2pw0unExistingDifferentUP() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
}
);
@ -713,7 +694,6 @@ add_task(async function test_change2pw0unExistingDifferentP() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await cleanupDoorhanger(notif);
}
);
@ -768,7 +748,6 @@ add_task(async function test_changeUPLoginOnPUpdateForm() {
is(fieldValues.password, "pass2", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-change");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
await checkDoorhangerUsernamePassword("notifyu1", "pass2");
clickDoorhangerButton(notif, CHANGE_BUTTON);
@ -804,7 +783,6 @@ add_task(async function test_recipeCaptureFields_NewLogin() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
// Sanity check, no logins should exist yet.
let logins = Services.logins.getAllLogins();
@ -867,7 +845,6 @@ add_task(async function test_saveUsingEnter() {
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(notif, "got notification popup");
ok(!notif.dismissed, "doorhanger is not dismissed");
is(
Services.logins.getAllLogins().length,
0,
@ -903,7 +880,6 @@ add_task(async function test_noShowPasswordOnDismissal() {
) {
info("Opening popup");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(!notif.dismissed, "doorhanger is not dismissed");
let { panel } = PopupNotifications;
info("Hiding popup.");
@ -936,7 +912,6 @@ add_task(async function test_showPasswordOn1stOpenOfDismissedByDefault() {
) {
info("Opening popup");
let notif = await getCaptureDoorhangerThatMayOpen("password-save");
ok(!notif.dismissed, "doorhanger is not dismissed");
let { panel } = PopupNotifications;
info("Hiding popup.");

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

@ -2,162 +2,149 @@
* Test that the doorhanger notification for password saving is populated with
* the correct values in various password capture cases.
*/
const testCases = [
{
name: "No saved logins, username and password",
username: "username",
password: "password",
expectOutcome: [
{
username: "username",
password: "password",
},
],
},
{
name: "No saved logins, password with empty username",
username: "",
password: "password",
expectOutcome: [
{
username: "",
password: "password",
},
],
},
{
name: "Saved login with username, update password",
username: "username",
oldPassword: "password",
password: "newPassword",
expectOutcome: [
{
username: "username",
password: "newPassword",
},
],
},
{
name: "Saved login with no username, update password",
username: "",
oldPassword: "password",
password: "newPassword",
expectOutcome: [
{
username: "",
password: "newPassword",
},
],
},
{
name: "Saved login with no username, add username and different password",
oldUsername: "",
username: "username",
oldPassword: "password",
password: "newPassword",
expectOutcome: [
{
username: "",
password: "password",
},
{
username: "username",
password: "newPassword",
},
],
},
];
for (let testData of testCases) {
let tmp = {
async [testData.name]() {
info("testing with: " + JSON.stringify(testData));
await test_save_change(testData);
add_task(async function test_save_change() {
let testCases = [
{
username: "username",
password: "password",
expectOutcome: [
{
username: "username",
password: "password",
},
],
},
};
add_task(tmp[testData.name]);
}
{
username: "",
password: "password",
expectOutcome: [
{
username: "",
password: "password",
},
],
},
{
username: "username",
oldPassword: "password",
password: "newPassword",
expectOutcome: [
{
username: "username",
password: "newPassword",
},
],
},
{
username: "",
oldPassword: "password",
password: "newPassword",
expectOutcome: [
{
username: "",
password: "newPassword",
},
],
},
{
oldUsername: "",
username: "username",
oldPassword: "password",
password: "newPassword",
expectOutcome: [
{
username: "",
password: "password",
},
{
username: "username",
password: "newPassword",
},
],
},
];
async function test_save_change(testData) {
let {
for (let {
oldUsername,
username,
oldPassword,
password,
expectOutcome,
} = testData;
// Add a login for the origin of the form if testing a change notification.
if (oldPassword) {
Services.logins.addLogin(
LoginTestUtils.testData.formLogin({
origin: "https://example.com",
formActionOrigin: "https://example.com",
username: typeof oldUsername !== "undefined" ? oldUsername : username,
password: oldPassword,
})
);
}
await BrowserTestUtils.withNewTab(
{
gBrowser,
url:
"https://example.com/browser/toolkit/components/" +
"passwordmgr/test/browser/form_basic.html",
},
async function(browser) {
await SimpleTest.promiseFocus(browser.ownerGlobal);
// Submit the form in the content page with the credentials from the test
// case. This will cause the doorhanger notification to be displayed.
info(`update form with username: ${username}, password: ${password}`);
await changeContentFormValues(browser, {
"#form-basic-username": username,
"#form-basic-password": password,
});
let formSubmittedPromise = listenForTestNotification("FormSubmit");
await SpecialPowers.spawn(browser, [], async function() {
let doc = this.content.document;
doc.getElementById("form-basic").submit();
});
await formSubmittedPromise;
// 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;
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";
}
info("Waiting for doorhanger of type: " + expectedDoorhanger);
let notif = await waitForDoorhanger(browser, expectedDoorhanger);
// Check the actual content of the popup notification.
await checkDoorhangerUsernamePassword(username, password);
let promiseLogin = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == expectedNotification
} of testCases) {
// Add a login for the origin of the form if testing a change notification.
if (oldPassword) {
Services.logins.addLogin(
LoginTestUtils.testData.formLogin({
origin: "https://example.com",
formActionOrigin: "https://example.com",
username: typeof oldUsername !== "undefined" ? oldUsername : username,
password: oldPassword,
})
);
await clickDoorhangerButton(notif, REMEMBER_BUTTON);
await promiseLogin;
await cleanupDoorhanger(notif); // clean slate for the next test
// Check that the values in the database match the expected values.
verifyLogins(expectOutcome);
}
);
// Clean up the database before the next test case is executed.
Services.logins.removeAllLogins();
}
await BrowserTestUtils.withNewTab(
{
gBrowser,
url:
"https://example.com/browser/toolkit/components/" +
"passwordmgr/test/browser/form_basic.html",
},
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.
await SpecialPowers.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").submit();
}
);
// 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;
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);
await promiseLogin;
await cleanupDoorhanger(notif); // clean slate for the next test
// Check that the values in the database match the expected values.
verifyLogins(expectOutcome);
}
);
// Clean up the database before the next test case is executed.
Services.logins.removeAllLogins();
}
});

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

@ -89,19 +89,23 @@ add_task(async function test_edit_username() {
"popupshown",
event => event.target == PopupNotifications.panel
);
let formSubmittedPromise = listenForTestNotification("FormSubmit");
await changeContentFormValues(browser, {
"#form-basic-username": testCase.usernameInPage,
"#form-basic-password": "password",
});
await TestUtils.waitForTick();
await SpecialPowers.spawn(browser, [], async function() {
content.document.getElementById("form-basic").submit();
});
await formSubmittedPromise;
let notif = await waitForDoorhanger(browser, "any");
await SpecialPowers.spawn(
browser,
[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").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 in the dialog if requested.
if (testCase.usernameChangedTo) {
@ -130,7 +134,7 @@ add_task(async function test_edit_username() {
PopupNotifications.panel,
"popuphidden"
);
clickDoorhangerButton(notif, CHANGE_BUTTON);
notificationElement.button.doCommand();
let [result] = await promiseLogin;
await promiseHidden;
@ -145,8 +149,6 @@ add_task(async function test_edit_username() {
testCase.usernameChangedTo || testCase.usernameInPage
);
Assert.equal(login.password, "password");
await cleanupDoorhanger();
}
);

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

@ -4,25 +4,16 @@
"use strict";
async function fillTestPage(
aBrowser,
username = "my_username",
password = "my_password"
) {
let notif = getCaptureDoorhanger("any", undefined, aBrowser);
ok(!notif, "No doorhangers should be present before filling the form");
await changeContentFormValues(aBrowser, {
"#form-basic-username": username,
"#form-basic-password": password,
async function fillTestPage(aBrowser) {
await SpecialPowers.spawn(aBrowser, [], async function() {
content.document
.getElementById("form-basic-username")
.setUserInput("my_username");
content.document
.getElementById("form-basic-password")
.setUserInput("my_password");
});
if (LoginHelper.passwordEditCaptureEnabled) {
// Filling the password will generate a dismissed doorhanger.
// Check and remove that before running the rest of the task
notif = await waitForDoorhanger(aBrowser, "any");
ok(notif.dismissed, "Only a dismissed doorhanger should be present");
await cleanupDoorhanger(notif);
}
info("fields filled");
}
function withTestPage(aTaskFn) {
@ -38,9 +29,7 @@ function withTestPage(aTaskFn) {
// Give a chance for the doorhanger to appear
await new Promise(resolve => SimpleTest.executeSoon(resolve));
let notif = getCaptureDoorhanger("any");
ok(!notif, "No doorhanger should be present");
await cleanupDoorhanger(notif);
ok(!getCaptureDoorhanger("any"), "No doorhanger should be present");
}
);
}
@ -78,7 +67,6 @@ add_task(async function test_urlbar_fragment_enter() {
add_task(async function test_backButton_forwardButton() {
await withTestPage(async function(aBrowser) {
info("Loading formless_basic.html?second");
// Load a new page in the tab so we can test going back
BrowserTestUtils.loadURI(
aBrowser,
@ -89,10 +77,8 @@ add_task(async function test_backButton_forwardButton() {
false,
"https://example.com" + DIRECTORY_PATH + "formless_basic.html?second"
);
info("Loaded formless_basic.html?second");
await fillTestPage(aBrowser, "my_username", "password_2");
await fillTestPage(aBrowser);
info("formless_basic.html?second form is filled, clicking back");
let backPromise = BrowserTestUtils.browserStopped(aBrowser);
EventUtils.synthesizeMouseAtCenter(
document.getElementById("back-button"),
@ -105,7 +91,7 @@ add_task(async function test_backButton_forwardButton() {
ok(!getCaptureDoorhanger("any"), "No doorhanger should be present");
// Now go forward again after filling
await fillTestPage(aBrowser, "my_username", "password_3");
await fillTestPage(aBrowser);
let forwardButton = document.getElementById("forward-button");
await BrowserTestUtils.waitForCondition(() => {
@ -115,7 +101,6 @@ add_task(async function test_backButton_forwardButton() {
info("click the forward button");
EventUtils.synthesizeMouseAtCenter(forwardButton, {});
await forwardPromise;
info("done");
});
});

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

@ -314,7 +314,11 @@ add_task(async function test_normal_popup_notification_3() {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("any", PopupNotifications, browser);
let notif = getCaptureDoorhanger(
"password-save",
PopupNotifications,
browser
);
ok(!notif, "got no notification popup");
if (notif) {
await cleanupDoorhanger(notif);
@ -365,8 +369,11 @@ add_task(async function test_private_popup_notification_3b() {
is(fieldValues.username, "notifyu1", "Checking submitted username");
is(fieldValues.password, "notifyp1", "Checking submitted password");
let notif = getCaptureDoorhanger("any", PopupNotifications, browser);
let notif = getCaptureDoorhanger(
"password-save",
PopupNotifications,
browser
);
ok(!notif, "got no notification popup");
if (notif) {
await cleanupDoorhanger(notif);

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

@ -34,7 +34,10 @@ registerCleanupFunction(
await recipeParent.then(recipeParentResult => recipeParentResult.reset());
await cleanupDoorhanger();
await cleanupPasswordNotifications();
let notif;
while ((notif = PopupNotifications.getNotification("password"))) {
notif.remove();
}
await closePopup(document.getElementById("contentAreaContextMenu"));
await closePopup(document.getElementById("PopupAutoComplete"));
}
@ -268,7 +271,7 @@ const DONT_CHANGE_BUTTON = "secondaryButton";
* Checks if we have a password capture popup notification
* of the right type and with the right label.
*
* @param {String} aKind The desired `passwordNotificationType` ("any" for any type)
* @param {String} aKind The desired `passwordNotificationType`
* @param {Object} [popupNotifications = PopupNotifications]
* @param {Object} [browser = null] Optional browser whose notifications should be searched.
* @return the found password popup notification.
@ -280,12 +283,7 @@ function getCaptureDoorhanger(
) {
ok(true, "Looking for " + aKind + " popup notification");
let notification = popupNotifications.getNotification("password", browser);
if (!aKind) {
throw new Error(
"getCaptureDoorhanger needs aKind to be a non-empty string"
);
}
if (aKind !== "any" && notification) {
if (notification) {
is(
notification.options.passwordNotificationType,
aKind,
@ -326,15 +324,10 @@ async function getCaptureDoorhangerThatMayOpen(
}
async function waitForDoorhanger(browser, type) {
let notif;
await TestUtils.waitForCondition(() => {
notif = PopupNotifications.getNotification("password", browser);
if (notif && type !== "any") {
return notif.options.passwordNotificationType == type;
}
return notif;
let notif = PopupNotifications.getNotification("password", browser);
return notif && notif.options.passwordNotificationType == type;
}, `Waiting for a ${type} notification`);
return notif;
}
async function hideDoorhangerPopup() {
@ -396,15 +389,6 @@ async function cleanupDoorhanger(notif) {
await promiseHidden;
}
async function cleanupPasswordNotifications(
popupNotifications = PopupNotifications
) {
let notif;
while ((notif = popupNotifications.getNotification("password"))) {
notif.remove();
}
}
/**
* Checks the doorhanger's username and password.
*
@ -415,11 +399,19 @@ async function checkDoorhangerUsernamePassword(username, password) {
await BrowserTestUtils.waitForCondition(() => {
return (
document.getElementById("password-notification-username").value ==
username &&
document.getElementById("password-notification-password").value ==
password
username
);
}, "Wait for nsLoginManagerPrompter writeDataToUI() to update to the correct username/password values");
}, "Wait for nsLoginManagerPrompter writeDataToUI()");
is(
document.getElementById("password-notification-username").value,
username,
"Check doorhanger username"
);
is(
document.getElementById("password-notification-password").value,
password,
"Check doorhanger password"
);
}
/**
@ -590,14 +582,13 @@ async function fillGeneratedPasswordFromOpenACPopup(
);
let passwordGeneratedPromise = listenForTestNotification(
"PasswordEditedOrGenerated"
"PasswordFilledOrEdited"
);
info("Clicking the generated password AC item");
EventUtils.synthesizeMouseAtCenter(item, {});
info("Waiting for the content input value to change");
await inputEventPromise;
info("Waiting for the passwordGeneratedPromise");
await passwordGeneratedPromise;
}
@ -668,7 +659,7 @@ async function openPasswordContextMenu(
* expectedMessage. Possible messages:
* FormProcessed - a form was processed after page load.
* FormSubmit - a form was just submitted.
* PasswordEditedOrGenerated - a password was filled in or modified.
* PasswordFilledOrEdited - a password was filled in or modified.
*
* The count is the number of that messages to wait for. This should
* typically be used when waiting for the FormProcessed message for a page
@ -723,40 +714,3 @@ async function doFillGeneratedPasswordContextMenuItem(browser, passwordInput) {
await promiseShown;
await fillGeneratedPasswordFromOpenACPopup(browser, passwordInput);
}
// Content form helpers
async function changeContentFormValues(browser, selectorValues) {
for (let [sel, value] of Object.entries(selectorValues)) {
info("changeContentFormValues, update: " + sel + ", to: " + value);
await changeContentInputValue(browser, sel, value);
}
}
async function changeContentInputValue(browser, selector, str) {
let oldValue = await ContentTask.spawn(browser, [selector], function(sel) {
return content.document.querySelector(sel).value;
});
if (str === oldValue) {
info("no change needed to value of " + selector + ": " + oldValue);
return;
}
let changedPromise = ContentTask.spawn(browser, [selector], async function(
sel
) {
let input = content.document.querySelector(sel);
input.focus();
input.select();
await ContentTaskUtils.waitForEvent(input, "change");
info("change event on " + sel + ": " + input.value);
});
await SimpleTest.promiseFocus(browser);
await EventUtils.synthesizeKey("KEY_Backspace");
if (str) {
await EventUtils.sendString(str);
}
info("waiting for changedPromise");
await EventUtils.synthesizeKey("KEY_Tab");
await changedPromise;
}

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

@ -124,7 +124,7 @@ skip-if = toolkit == 'android' && debug # bug 1397615
skip-if = toolkit == 'android' && debug # bug 1397615
[test_input_events.html]
[test_input_events_for_identical_values.html]
[test_LoginManagerContent_passwordEditedOrGenerated.html]
[test_LoginManagerContent_generatedPasswordFilledOrEdited.html]
scheme = https
skip-if = toolkit == 'android' # password generation
[test_master_password.html]

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

@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>Test behavior of unmasking in LMC._passwordEditedOrGenerated</title>
<title>Test behavior of unmasking in LMC.generatedPasswordFilledOrEdited</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="pwmgr_common.js"></script>
@ -62,7 +62,7 @@ add_task(async function test_preventDefaultAndStopPropagation() {
SpecialPowers.wrap(pword).setUserInput("generatedpass");
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "Before first fill");
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(pword, { triggeredByFillingGenerated: true });
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(pword);
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, false, "After first fill");
synthesizeKey("KEY_Tab"); // blur
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "After blur");
@ -80,7 +80,7 @@ add_task(async function test_fieldsMaskedAfterSavedLoginFill() {
SpecialPowers.wrap(pword).setUserInput("generatedpass");
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "Before first fill");
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(pword, { triggeredByFillingGenerated: true });
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(pword);
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, false, "After first fill");
synthesizeKey("KEY_Tab", { shiftKey: true }); // blur pw, focus un
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "After blur");
@ -105,7 +105,7 @@ add_task(async function test_fieldsMaskedAfterReplacingWholeValue() {
SpecialPowers.wrap(pword).setUserInput("generatedpass");
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "Before first fill");
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(pword, { triggeredByFillingGenerated: true });
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(pword);
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, false, "After first fill");
synthesizeKey("KEY_Tab", { shiftKey: true }); // blur pw, focus un
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "After blur");
@ -132,7 +132,7 @@ add_task(async function test_fieldsUnmaskedAfterAddingCharacter() {
SpecialPowers.wrap(pword).setUserInput("generatedpass");
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "Before first fill");
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(pword, { triggeredByFillingGenerated: true });
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(pword);
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, false, "After first fill");
synthesizeKey("KEY_Tab", { shiftKey: true }); // blur pw, focus un
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "After blur");
@ -160,7 +160,7 @@ add_task(async function test_typeNotPassword() {
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, true, "Before first fill");
SpecialPowers.wrap(pword).setUserInput("generatedpass");
LoginManagerChild.forWindow(window)._passwordEditedOrGenerated(pword, { triggeredByFillingGenerated: true });
LoginManagerChild.forWindow(window)._generatedPasswordFilledOrEdited(pword);
LOGIN_FIELD_UTILS.checkPasswordMasked(pword, false, "After first fill");
// Simulate a website doing their own unmasking and re-masking

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

@ -1,5 +1,5 @@
/**
* Test LoginManagerParent._onPasswordEditedOrGenerated()
* Test LoginManagerParent._onGeneratedPasswordFilledOrEdited()
*/
"use strict";
@ -163,8 +163,8 @@ function startTestConditions(contextId) {
LMP.useBrowsingContext(contextId);
ok(
LMP._onPasswordEditedOrGenerated,
"LMP._onPasswordEditedOrGenerated exists"
LMP._onGeneratedPasswordFilledOrEdited,
"LMP._onGeneratedPasswordFilledOrEdited exists"
);
equal(LMP.getGeneratedPassword(), null, "Null with no BrowsingContext");
equal(
@ -203,11 +203,10 @@ add_task(async function setup() {
Services.prefs.setBoolPref("signon.generation.enabled", true);
});
add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
add_task(async function test_onGeneratedPasswordFilledOrEdited() {
startTestConditions(99);
let { generatedPassword } = stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
@ -220,13 +219,11 @@ add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
"Should have no saved logins at the start of the test"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
password: generatedPassword,
username: "someusername",
});
let [login] = await storageChangedPromised;
@ -259,20 +256,17 @@ add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
username: "someusername",
password: newPassword,
});
let generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
);
ok(generatedPW.edited, "Cached edited boolean should be true");
equal(generatedPW.value, newPassword, "Cached password should be updated");
// login metadata should be updated
let [dataArray] = await storageChangedPromised;
login = dataArray.queryElementAt(1, Ci.nsILoginInfo);
expected.password = newPassword;
@ -291,13 +285,11 @@ add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newerPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
username: "someusername",
password: newerPassword,
});
generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
@ -323,92 +315,10 @@ add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
Services.telemetry.clearEvents();
});
add_task(
async function test_onPasswordEditedOrGenerated_editToEmpty_generatedPassword() {
startTestConditions(99);
let { generatedPassword } = stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "addLogin"
);
equal(
Services.logins.getAllLogins().length,
0,
"Should have no saved logins at the start of the test"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
});
let [login] = await storageChangedPromised;
let expected = new LoginInfo(
"https://www.example.com",
"https://www.mozilla.org",
null,
"", // verify we don't include the username when auto-saving a login
generatedPassword
);
ok(login.equals(expected), "Check added login");
ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
ok(
fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
info("Edit the password to be empty");
const newPassword = "";
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
});
let generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
);
ok(!generatedPW.edited, "Cached edited boolean should be false");
equal(
generatedPW.value,
generatedPassword,
"Cached password shouldn't be updated"
);
checkEditTelemetryRecorded(0, "Blanking doesn't count as an edit");
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.telemetry.clearEvents();
}
);
add_task(async function test_addUsernameBeforeAutoSaveEdit() {
add_task(async function test_onGeneratedPasswordFilledOrEdited_editToEmpty() {
startTestConditions(99);
let { generatedPassword } = stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
@ -421,13 +331,85 @@ add_task(async function test_addUsernameBeforeAutoSaveEdit() {
"Should have no saved logins at the start of the test"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
password: generatedPassword,
username: "someusername",
});
let [login] = await storageChangedPromised;
let expected = new LoginInfo(
"https://www.example.com",
"https://www.mozilla.org",
null,
"", // verify we don't include the username when auto-saving a login
generatedPassword
);
ok(login.equals(expected), "Check added login");
ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
ok(
fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
info("Edit the password to be empty");
const newPassword = "";
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
username: "someusername",
password: newPassword,
});
let generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
);
ok(!generatedPW.edited, "Cached edited boolean should be false");
equal(
generatedPW.value,
generatedPassword,
"Cached password shouldn't be updated"
);
checkEditTelemetryRecorded(0, "Blanking doesn't count as an edit");
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllLogins();
Services.telemetry.clearEvents();
});
add_task(async function test_addUsernameBeforeAutoSaveEdit() {
startTestConditions(99);
let { generatedPassword } = stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter } = stubPrompter();
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "addLogin"
);
equal(
Services.logins.getAllLogins().length,
0,
"Should have no saved logins at the start of the test"
);
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
password: generatedPassword,
username: "someusername",
});
let [login] = await storageChangedPromised;
@ -465,13 +447,11 @@ add_task(async function test_addUsernameBeforeAutoSaveEdit() {
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
username: "someusername",
password: newPassword,
});
let generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
@ -497,13 +477,11 @@ add_task(async function test_addUsernameBeforeAutoSaveEdit() {
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newerPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
username: "someusername",
password: newerPassword,
});
generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
@ -531,20 +509,17 @@ add_task(async function test_addUsernameBeforeAutoSaveEdit() {
});
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withDisabledLogin() {
async function test_onGeneratedPasswordFilledOrEdited_withDisabledLogin() {
startTestConditions(99);
let { generatedPassword } = stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
info("Disable login saving for the site");
Services.logins.setLoginSavingEnabled("https://www.example.com", false);
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
triggeredByFillingGenerated: true,
password: generatedPassword,
});
equal(
Services.logins.getAllLogins().length,
@ -563,7 +538,7 @@ add_task(
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withSavedEmptyUsername() {
async function test_onGeneratedPasswordFilledOrEdited_withSavedEmptyUsername() {
startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "",
@ -581,14 +556,11 @@ add_task(
generatedPassword: password1,
} = stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter, fakePromptToChangePassword } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: password1 },
triggeredByFillingGenerated: true,
password: password1,
});
equal(
Services.logins.getAllLogins().length,
@ -613,13 +585,11 @@ add_task(
info("Edit the password");
const newPassword = password1 + "🔥";
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
username: "someusername",
password: newPassword,
});
let generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
"https://www.example.com^userContextId=6"
@ -646,7 +616,7 @@ add_task(
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withSavedEmptyUsernameAndUsernameValue() {
async function test_onGeneratedPasswordFilledOrEdited_withSavedEmptyUsernameAndUsernameValue() {
// Save as the above task but with a non-empty username field value.
startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
@ -669,15 +639,12 @@ add_task(
fakePromptToChangePassword,
fakePromptToSavePassword,
} = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: password1 },
usernameField: { value: "non-empty-username" },
triggeredByFillingGenerated: true,
username: "non-empty-username",
password: password1,
});
equal(
Services.logins.getAllLogins().length,
@ -706,13 +673,11 @@ add_task(
info("Edit the password");
const newPassword = password1 + "🔥";
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "non-empty-username" },
triggeredByFillingGenerated: true,
username: "non-empty-username",
password: newPassword,
});
ok(
fakePromptToChangePassword.notCalled,
@ -759,7 +724,7 @@ add_task(
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withEmptyUsernameDifferentFormActionOrigin() {
async function test_onGeneratedPasswordFilledOrEdited_withEmptyUsernameDifferentFormActionOrigin() {
startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "",
@ -771,14 +736,11 @@ add_task(
generatedPassword: password1,
} = stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter, fakePromptToChangePassword } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.elsewhere.com",
newPasswordField: { value: password1 },
triggeredByFillingGenerated: true,
password: password1,
});
let savedLogins = Services.logins.getAllLogins();
@ -820,7 +782,7 @@ add_task(
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withSavedUsername() {
async function test_onGeneratedPasswordFilledOrEdited_withSavedUsername() {
startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "previoususer",
@ -832,14 +794,11 @@ add_task(
generatedPassword: password1,
} = stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter, fakePromptToChangePassword } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(rootBrowser, {
await LMP._onGeneratedPasswordFilledOrEdited({
browsingContextId: 99,
origin: "https://www.example.com",
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: password1 },
triggeredByFillingGenerated: true,
password: password1,
});
let savedLogins = Services.logins.getAllLogins();

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

@ -29,7 +29,7 @@ run-if = buildapp == "browser"
skip-if = os == "android" # Password generation not packaged/used on Android
[test_LoginManagerParent_getGeneratedPassword.js]
skip-if = os == "android" # Password generation not packaged/used on Android
[test_LoginManagerParent_onPasswordEditedOrGenerated.js]
[test_LoginManagerParent_onGeneratedPasswordFilledOrEdited.js]
skip-if = os == "android" # Password generation not packaged/used on Android
[test_LoginManagerParent_searchAndDedupeLogins.js]
skip-if = os == "android" # schemeUpgrades aren't supported

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

@ -1458,8 +1458,6 @@ PopupNotifications.prototype = {
if (notification.options.extraAttr) {
anchorElm.setAttribute("extraAttr", notification.options.extraAttr);
} else {
anchorElm.removeAttribute("extraAttr");
}
}
}