Bug 1641412 - Pass all possible UNs/PWs to doorhanger;r=MattN,sfoster

Differential Revision: https://phabricator.services.mozilla.com/D77190
This commit is contained in:
Severin 2020-06-16 02:43:43 +00:00
Родитель 15284e839e
Коммит c26d4965f3
5 изменённых файлов: 190 добавлений и 122 удалений

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

@ -3751,6 +3751,7 @@ pref("signon.autofillForms", true);
pref("signon.autofillForms.autocompleteOff", true);
pref("signon.autofillForms.http", false);
pref("signon.autologin.proxy", false);
pref("signon.capture.inputChanges.enabled", true);
pref("signon.formlessCapture.enabled", true);
pref("signon.generation.available", true);
// A value of "-1" disables new-password heuristics. Can be updated once Bug 1618058 is resolved.

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

@ -14,6 +14,9 @@
const EXPORTED_SYMBOLS = ["LoginManagerChild"];
const PASSWORD_INPUT_ADDED_COALESCING_THRESHOLD_MS = 1;
// The amount of time a context menu event supresses showing a
// popup from a focus event in ms. This matches the threshold in
// toolkit/components/satchel/nsFormFillController.cpp
const AUTOCOMPLETE_AFTER_RIGHT_CLICK_THRESHOLD_MS = 400;
const AUTOFILL_STATE = "-moz-autofill";
@ -236,6 +239,9 @@ const observer = {
log("Ignoring change event on form that hasn't been user-modified");
break;
}
this._storeUserInput(docState, aEvent.target);
if (aEvent.target.hasBeenTypePassword) {
let triggeredByFillingGenerated = docState.generatedPasswordFields.has(
aEvent.target
@ -392,6 +398,20 @@ const observer = {
}
}
},
_storeUserInput(docState, field) {
if (
!Services.prefs.getBoolPref("signon.capture.inputChanges.enabled", false)
) {
return;
}
if (LoginHelper.isPasswordFieldType(field)) {
docState.possiblePasswords.add(field.value);
} else if (LoginHelper.isUsernameFieldType(field)) {
docState.possibleUsernames.add(field.value);
}
},
};
// Add this observer once for the process.
@ -906,6 +926,14 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
lastSubmittedValuesByRootElement: new WeakMap(),
fieldModificationsByRootElement: new WeakMap(),
loginFormRootElements: new WeakSet(),
/**
* Anything entered into an <input> that we think might be a username
*/
possibleUsernames: new Set(),
/**
* Anything entered into an <input> that we think might be a password
*/
possiblePasswords: new Set(),
};
this._loginFormStateByDocument.set(document, loginFormState);
}
@ -1702,6 +1730,10 @@ this.LoginManagerChild = class LoginManagerChild extends JSWindowActorChild {
oldPasswordField: mockOldPassword,
dismissedPrompt,
triggeredByFillingGenerated,
possibleValues: {
usernames: docState.possibleUsernames,
passwords: docState.possiblePasswords,
},
messageSent: true,
};

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

@ -678,6 +678,7 @@ class LoginManagerParent extends JSWindowActorParent {
newPasswordField,
oldPasswordField,
dismissedPrompt,
possibleValues,
}
) {
function recordLoginUse(login) {
@ -841,7 +842,8 @@ class LoginManagerParent extends JSWindowActorParent {
dismissedPrompt,
notifySaved,
autoSavedStorageGUID,
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
} else if (!existingLogin.username && formLogin.username) {
log("...empty username update, prompting to change.");
@ -853,7 +855,8 @@ class LoginManagerParent extends JSWindowActorParent {
dismissedPrompt,
notifySaved,
autoSavedStorageGUID,
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
} else {
recordLoginUse(existingLogin);
@ -868,7 +871,8 @@ class LoginManagerParent extends JSWindowActorParent {
formLogin,
dismissedPrompt,
notifySaved,
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
}
@ -890,6 +894,8 @@ class LoginManagerParent extends JSWindowActorParent {
* @param {Object?} options.usernameField
* @param {Element?} options.oldPasswordField
* @param {boolean} [options.triggeredByFillingGenerated = false]
* @param {Set<String>} possibleValues.usernames
* @param {Set<String>} possibleValues.passwords
*/
async _onPasswordEditedOrGenerated(
browser,
@ -901,6 +907,10 @@ class LoginManagerParent extends JSWindowActorParent {
usernameField = null,
oldPasswordField,
triggeredByFillingGenerated = false,
possibleValues = {
usernames: new Set(),
passwords: new Set(),
},
}
) {
log(
@ -1208,7 +1218,8 @@ class LoginManagerParent extends JSWindowActorParent {
true, // dismissed prompt
notifySaved,
autoSavedStorageGUID, // autoSavedLoginGuid
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
} else if (!existingLogin.username && formLogin.username) {
log("...empty username update, prompting to change.");
@ -1219,7 +1230,8 @@ class LoginManagerParent extends JSWindowActorParent {
true, // dismissed prompt
notifySaved,
autoSavedStorageGUID, // autoSavedLoginGuid
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
} else {
log("_onPasswordEditedOrGenerated: No change to existing login");
@ -1238,7 +1250,8 @@ class LoginManagerParent extends JSWindowActorParent {
true, // dismissed prompt
notifySaved,
autoSavedStorageGUID, // autoSavedLoginGuid
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
}
}
@ -1250,7 +1263,8 @@ class LoginManagerParent extends JSWindowActorParent {
formLogin,
true, // dismissed prompt
notifySaved,
autoFilledLoginGuid
autoFilledLoginGuid,
possibleValues
);
}

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

@ -77,7 +77,11 @@ class LoginManagerPrompter {
aLogin,
dismissed = false,
notifySaved = false,
autoFilledLoginGuid = ""
autoFilledLoginGuid = "",
possibleValues = {
usernames: new Set(),
passwords: new Set(),
}
) {
log.debug("promptToSavePassword");
let inPrivateBrowsing = PrivateBrowsingUtils.isBrowserPrivate(aBrowser);
@ -501,6 +505,116 @@ class LoginManagerPrompter {
showOptions.dismissed && showOptions.extraAttr == "attention"
? ATTENTION_NOTIFICATION_TIMEOUT_MS
: NOTIFICATION_TIMEOUT_MS;
let options = Object.assign(
{
timeout: Date.now() + timeoutMs,
persistWhileVisible: true,
passwordNotificationType: type,
hideClose: true,
eventCallback(topic) {
switch (topic) {
case "showing":
log.debug("showing");
currentNotification = this;
// Record the first time this instance of the doorhanger is shown.
if (!this.timeShown) {
histogram.add(PROMPT_DISPLAYED);
Services.obs.notifyObservers(
null,
"weave:telemetry:histogram",
histogramName
);
}
chromeDoc
.getElementById("password-notification-password")
.removeAttribute("focused");
chromeDoc
.getElementById("password-notification-username")
.removeAttribute("focused");
chromeDoc
.getElementById("password-notification-username")
.addEventListener("input", onInput);
chromeDoc
.getElementById("password-notification-username")
.addEventListener("keyup", onKeyUp);
chromeDoc
.getElementById("password-notification-password")
.addEventListener("keyup", onKeyUp);
chromeDoc
.getElementById("password-notification-password")
.addEventListener("input", onInput);
let toggleBtn = chromeDoc.getElementById(
"password-notification-visibilityToggle"
);
if (
Services.prefs.getBoolPref(
"signon.rememberSignons.visibilityToggle"
)
) {
toggleBtn.addEventListener("command", onVisibilityToggle);
toggleBtn.setAttribute("label", togglePasswordLabel);
toggleBtn.setAttribute("accesskey", togglePasswordAccessKey);
let hideToggle =
LoginHelper.isMasterPasswordSet() ||
// Don't show the toggle when the login was autofilled
!!autoFilledLoginGuid ||
// Dismissed-by-default prompts should still show the toggle.
(this.timeShown && this.wasDismissed) ||
// If we are only adding a username then the password is
// one that is already saved and we don't want to reveal
// it as the submitter of this form may not be the account
// owner, they may just be using the saved password.
(messageStringID == "updateLoginMsgAddUsername" &&
login.timePasswordChanged <
Date.now() - VISIBILITY_TOGGLE_MAX_PW_AGE_MS);
toggleBtn.setAttribute("hidden", hideToggle);
}
break;
case "shown": {
log.debug("shown");
writeDataToUI();
let anchorIcon = this.anchorElement;
if (anchorIcon && this.options.extraAttr == "attention") {
anchorIcon.removeAttribute("extraAttr");
delete this.options.extraAttr;
}
break;
}
case "dismissed":
// Note that this can run after `showing` but before `shown` upon tab switch.
this.wasDismissed = true;
// Fall through.
case "removed": {
// Note that this can run after `showing` and `shown` for the
// notification it's replacing.
log.debug(topic);
currentNotification = null;
let usernameField = chromeDoc.getElementById(
"password-notification-username"
);
usernameField.removeEventListener("input", onInput);
usernameField.removeEventListener("keyup", onKeyUp);
let passwordField = chromeDoc.getElementById(
"password-notification-password"
);
passwordField.removeEventListener("input", onInput);
passwordField.removeEventListener("keyup", onKeyUp);
passwordField.removeEventListener("command", onVisibilityToggle);
break;
}
}
return false;
},
},
showOptions
);
let notification = PopupNotifications.show(
browser,
notificationID,
@ -508,117 +622,7 @@ class LoginManagerPrompter {
"password-notification-icon",
mainAction,
secondaryActions,
Object.assign(
{
timeout: Date.now() + timeoutMs,
persistWhileVisible: true,
passwordNotificationType: type,
hideClose: true,
eventCallback(topic) {
switch (topic) {
case "showing":
log.debug("showing");
currentNotification = this;
// Record the first time this instance of the doorhanger is shown.
if (!this.timeShown) {
histogram.add(PROMPT_DISPLAYED);
Services.obs.notifyObservers(
null,
"weave:telemetry:histogram",
histogramName
);
}
chromeDoc
.getElementById("password-notification-password")
.removeAttribute("focused");
chromeDoc
.getElementById("password-notification-username")
.removeAttribute("focused");
chromeDoc
.getElementById("password-notification-username")
.addEventListener("input", onInput);
chromeDoc
.getElementById("password-notification-username")
.addEventListener("keyup", onKeyUp);
chromeDoc
.getElementById("password-notification-password")
.addEventListener("keyup", onKeyUp);
chromeDoc
.getElementById("password-notification-password")
.addEventListener("input", onInput);
let toggleBtn = chromeDoc.getElementById(
"password-notification-visibilityToggle"
);
if (
Services.prefs.getBoolPref(
"signon.rememberSignons.visibilityToggle"
)
) {
toggleBtn.addEventListener("command", onVisibilityToggle);
toggleBtn.setAttribute("label", togglePasswordLabel);
toggleBtn.setAttribute("accesskey", togglePasswordAccessKey);
let hideToggle =
LoginHelper.isMasterPasswordSet() ||
// Don't show the toggle when the login was autofilled
!!autoFilledLoginGuid ||
// Dismissed-by-default prompts should still show the toggle.
(this.timeShown && this.wasDismissed) ||
// If we are only adding a username then the password is
// one that is already saved and we don't want to reveal
// it as the submitter of this form may not be the account
// owner, they may just be using the saved password.
(messageStringID == "updateLoginMsgAddUsername" &&
login.timePasswordChanged <
Date.now() - VISIBILITY_TOGGLE_MAX_PW_AGE_MS);
toggleBtn.setAttribute("hidden", hideToggle);
}
break;
case "shown": {
log.debug("shown");
writeDataToUI();
let anchorIcon = this.anchorElement;
if (anchorIcon && this.options.extraAttr == "attention") {
anchorIcon.removeAttribute("extraAttr");
delete this.options.extraAttr;
}
break;
}
case "dismissed":
// Note that this can run after `showing` but before `shown` upon tab switch.
this.wasDismissed = true;
// Fall through.
case "removed": {
// Note that this can run after `showing` and `shown` for the
// notification it's replacing.
log.debug(topic);
currentNotification = null;
let usernameField = chromeDoc.getElementById(
"password-notification-username"
);
usernameField.removeEventListener("input", onInput);
usernameField.removeEventListener("keyup", onKeyUp);
let passwordField = chromeDoc.getElementById(
"password-notification-password"
);
passwordField.removeEventListener("input", onInput);
passwordField.removeEventListener("keyup", onKeyUp);
passwordField.removeEventListener(
"command",
onVisibilityToggle
);
break;
}
}
return false;
},
},
showOptions
)
options
);
if (notifySaved) {
@ -646,6 +650,11 @@ class LoginManagerPrompter {
* @param {string} [autoSavedLoginGuid = ""]
* A guid value for the old login to be removed if the changes match it
* to a different login
* @param {object} possibleValues
* Contains values from anything that we think, but are not sure, might be
* a username or password. Has two properties, 'usernames' and 'passwords'.
* @param {Set<String>} possibleValues.usernames
* @param {Set<String>} possibleValues.passwords
*/
promptToChangePassword(
aBrowser,
@ -654,7 +663,11 @@ class LoginManagerPrompter {
dismissed = false,
notifySaved = false,
autoSavedLoginGuid = "",
autoFilledLoginGuid = ""
autoFilledLoginGuid = "",
possibleValues = {
usernames: new Set(),
passwords: new Set(),
}
) {
let login = aOldLogin.clone();
login.origin = aNewLogin.origin;

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

@ -28,12 +28,16 @@ interface nsILoginManagerPrompter : nsISupports {
* a login has been saved
* @param autoFilledLoginGuid
* A string guid value for the login which was autofilled into the form
* @param possibleValues
* Contains values from anything that we think, but are not sure, might be
* a username or password. Has two properties, 'usernames' and 'passwords'.
*/
void promptToSavePassword(in Element aBrowser,
in nsILoginInfo aLogin,
[optional] in boolean dismissed,
[optional] in boolean notifySaved,
[optional] in AString autoFilledLoginGuid);
[optional] in AString autoFilledLoginGuid,
[optional] in jsval possibleValues);
/**
* Ask the user if they want to change a login's password or username.
@ -53,6 +57,9 @@ interface nsILoginManagerPrompter : nsISupports {
* match it to a different login
* @param autoFilledLoginGuid
* A string guid value for the login which was autofilled into the form
* @param possibleValues
* Contains values from anything that we think, but are not sure, might be
* a username or password. Has two properties, 'usernames' and 'passwords'.
*/
void promptToChangePassword(in Element aBrowser,
in nsILoginInfo aOldLogin,
@ -60,7 +67,8 @@ interface nsILoginManagerPrompter : nsISupports {
[optional] in boolean dismissed,
[optional] in boolean notifySaved,
[optional] in AString autoSavedLoginGuid,
[optional] in AString autoFilledLoginGuid);
[optional] in AString autoFilledLoginGuid,
[optional] in jsval possibleValues);
/**
* Ask the user if they want to change the password for one of