Index: components/password_manager/core/browser/password_form_manager.cc |
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc |
index 8f2305531eea7e9b3dbbe966ab35076a33f987d7..64ade29c67b883917d197e43fe1fa73a289c97d1 100644 |
--- a/components/password_manager/core/browser/password_form_manager.cc |
+++ b/components/password_manager/core/browser/password_form_manager.cc |
@@ -92,6 +92,9 @@ PasswordFormManager::PasswordFormManager( |
bool ssl_valid) |
: best_matches_deleter_(&best_matches_), |
observed_form_(CopyAndModifySSLValidity(observed_form, ssl_valid)), |
+ provisionally_saved_form_(nullptr), |
+ other_possible_username_action_( |
+ PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES), |
is_new_login_(true), |
has_generated_password_(false), |
password_manager_(password_manager), |
@@ -258,7 +261,7 @@ bool PasswordFormManager::HasGeneratedPassword() const { |
} |
bool PasswordFormManager::HasValidPasswordForm() const { |
- DCHECK_EQ(state_, POST_MATCHING_PHASE); |
+ DCHECK(state_ == MATCHING_PHASE || state_ == POST_MATCHING_PHASE) << state_; |
// Non-HTML password forms (primarily HTTP and FTP autentication) |
// do not contain username_element and password_element values. |
if (observed_form_.scheme != PasswordForm::SCHEME_HTML) |
@@ -270,120 +273,13 @@ bool PasswordFormManager::HasValidPasswordForm() const { |
void PasswordFormManager::ProvisionallySave( |
const PasswordForm& credentials, |
OtherPossibleUsernamesAction action) { |
- DCHECK_EQ(state_, POST_MATCHING_PHASE); |
+ DCHECK(state_ == MATCHING_PHASE || state_ == POST_MATCHING_PHASE) << state_; |
DCHECK_NE(RESULT_NO_MATCH, DoesManage(credentials)); |
+ provisionally_saved_form_.reset(new PasswordForm(credentials)); |
+ other_possible_username_action_ = action; |
- base::string16 password_to_save(PasswordToSave(credentials)); |
- |
- // Make sure the important fields stay the same as the initially observed or |
- // autofilled ones, as they may have changed if the user experienced a login |
- // failure. |
- // Look for these credentials in the list containing auto-fill entries. |
- PasswordFormMap::const_iterator it = |
- best_matches_.find(credentials.username_value); |
- if (it != best_matches_.end()) { |
- // The user signed in with a login we autofilled. |
- pending_credentials_ = *it->second; |
- bool password_changed = |
- pending_credentials_.password_value != password_to_save; |
- if (IsPendingCredentialsPublicSuffixMatch()) { |
- // If the autofilled credentials were only a PSL match, store a copy with |
- // the current origin and signon realm. This ensures that on the next |
- // visit, a precise match is found. |
- is_new_login_ = true; |
- user_action_ = password_changed ? kUserActionChoosePslMatch |
- : kUserActionOverridePassword; |
- |
- // Update credential to reflect that it has been used for submission. |
- // If this isn't updated, then password generation uploads are off for |
- // sites where PSL matching is required to fill the login form, as two |
- // PASSWORD votes are uploaded per saved password instead of one. |
- // |
- // TODO(gcasto): It would be nice if other state were shared such that if |
- // say a password was updated on one match it would update on all related |
- // passwords. This is a much larger change. |
- UpdateMetadataForUsage(pending_credentials_); |
- |
- // Normally, the copy of the PSL matched credentials, adapted for the |
- // current domain, is saved automatically without asking the user, because |
- // the copy likely represents the same account, i.e., the one for which |
- // the user already agreed to store a password. |
- // |
- // However, if the user changes the suggested password, it might indicate |
- // that the autofilled credentials and |credentials| actually correspond |
- // to two different accounts (see http://crbug.com/385619). In that case |
- // the user should be asked again before saving the password. This is |
- // ensured by clearing |original_signon_realm| on |pending_credentials_|, |
- // which unmarks it as a PSL match. |
- // |
- // There is still the edge case when the autofilled credentials represent |
- // the same account as |credentials| but the stored password was out of |
- // date. In that case, the user just had to manually enter the new |
- // password, which is now in |credentials|. The best thing would be to |
- // save automatically, and also update the original credentials. However, |
- // we have no way to tell if this is the case. This will likely happen |
- // infrequently, and the inconvenience put on the user by asking them is |
- // not significant, so we are fine with asking here again. |
- if (password_changed) { |
- pending_credentials_.original_signon_realm.clear(); |
- DCHECK(!IsPendingCredentialsPublicSuffixMatch()); |
- } |
- } else { // Not a PSL match. |
- is_new_login_ = false; |
- if (password_changed) |
- user_action_ = kUserActionOverridePassword; |
- } |
- } else if (action == ALLOW_OTHER_POSSIBLE_USERNAMES && |
- UpdatePendingCredentialsIfOtherPossibleUsername( |
- credentials.username_value)) { |
- // |pending_credentials_| is now set. Note we don't update |
- // |pending_credentials_.username_value| to |credentials.username_value| |
- // yet because we need to keep the original username to modify the stored |
- // credential. |
- selected_username_ = credentials.username_value; |
- is_new_login_ = false; |
- } else { |
- // User typed in a new, unknown username. |
- user_action_ = kUserActionOverrideUsernameAndPassword; |
- pending_credentials_ = observed_form_; |
- if (credentials.was_parsed_using_autofill_predictions) |
- pending_credentials_.username_element = credentials.username_element; |
- pending_credentials_.username_value = credentials.username_value; |
- pending_credentials_.other_possible_usernames = |
- credentials.other_possible_usernames; |
- |
- // The password value will be filled in later, remove any garbage for now. |
- pending_credentials_.password_value.clear(); |
- pending_credentials_.new_password_value.clear(); |
- |
- // If this was a sign-up or change password form, the names of the elements |
- // are likely different than those on a login form, so do not bother saving |
- // them. We will fill them with meaningful values in UpdateLogin() when the |
- // user goes onto a real login form for the first time. |
- if (!credentials.new_password_element.empty()) { |
- pending_credentials_.password_element.clear(); |
- pending_credentials_.new_password_element.clear(); |
- } |
- } |
- |
- pending_credentials_.action = credentials.action; |
- // If the user selected credentials we autofilled from a PasswordForm |
- // that contained no action URL (IE6/7 imported passwords, for example), |
- // bless it with the action URL from the observed form. See bug 1107719. |
- if (pending_credentials_.action.is_empty()) |
- pending_credentials_.action = observed_form_.action; |
- |
- pending_credentials_.password_value = password_to_save; |
- pending_credentials_.preferred = credentials.preferred; |
- |
- if (user_action_ == kUserActionOverridePassword && |
- pending_credentials_.type == PasswordForm::TYPE_GENERATED && |
- !has_generated_password_) { |
- LogPasswordGenerationSubmissionEvent(PASSWORD_OVERRIDDEN); |
- } |
- |
- if (has_generated_password_) |
- pending_credentials_.type = PasswordForm::TYPE_GENERATED; |
+ if (HasCompletedMatching()) |
+ CreatePendingCredentials(); |
} |
void PasswordFormManager::Save() { |
@@ -623,6 +519,11 @@ void PasswordFormManager::OnGetPasswordStoreResults( |
OnRequestDone(results.Pass()); |
state_ = POST_MATCHING_PHASE; |
+ // If password store was slow and provisionally saved form is already here |
+ // then create pending credentials (see http://crbug.com/470322). |
+ if (provisionally_saved_form_) |
+ CreatePendingCredentials(); |
battre
2017/07/13 13:30:51
Do you remember why you decided not to return here
vabr (Chromium)
2017/07/13 14:01:26
The PasswordFormManager still needs to ensure save
|
+ |
if (manager_action_ != kManagerActionBlacklisted) { |
for (auto const& driver : drivers_) |
ProcessFrame(driver); |
@@ -869,6 +770,128 @@ bool PasswordFormManager::UploadPasswordForm( |
return success; |
} |
+void PasswordFormManager::CreatePendingCredentials() { |
+ DCHECK(provisionally_saved_form_); |
+ base::string16 password_to_save(PasswordToSave(*provisionally_saved_form_)); |
+ |
+ // Make sure the important fields stay the same as the initially observed or |
+ // autofilled ones, as they may have changed if the user experienced a login |
+ // failure. |
+ // Look for these credentials in the list containing auto-fill entries. |
+ PasswordFormMap::const_iterator it = |
+ best_matches_.find(provisionally_saved_form_->username_value); |
+ if (it != best_matches_.end()) { |
+ // The user signed in with a login we autofilled. |
+ pending_credentials_ = *it->second; |
+ bool password_changed = |
+ pending_credentials_.password_value != password_to_save; |
+ if (IsPendingCredentialsPublicSuffixMatch()) { |
+ // If the autofilled credentials were only a PSL match, store a copy with |
+ // the current origin and signon realm. This ensures that on the next |
+ // visit, a precise match is found. |
+ is_new_login_ = true; |
+ user_action_ = password_changed ? kUserActionChoosePslMatch |
+ : kUserActionOverridePassword; |
+ |
+ // Update credential to reflect that it has been used for submission. |
+ // If this isn't updated, then password generation uploads are off for |
+ // sites where PSL matching is required to fill the login form, as two |
+ // PASSWORD votes are uploaded per saved password instead of one. |
+ // |
+ // TODO(gcasto): It would be nice if other state were shared such that if |
+ // say a password was updated on one match it would update on all related |
+ // passwords. This is a much larger change. |
+ UpdateMetadataForUsage(pending_credentials_); |
+ |
+ // Normally, the copy of the PSL matched credentials, adapted for the |
+ // current domain, is saved automatically without asking the user, because |
+ // the copy likely represents the same account, i.e., the one for which |
+ // the user already agreed to store a password. |
+ // |
+ // However, if the user changes the suggested password, it might indicate |
+ // that the autofilled credentials and |provisionally_saved_form_| |
+ // actually correspond to two different accounts (see |
+ // http://crbug.com/385619). In that case the user should be asked again |
+ // before saving the password. This is ensured by clearing |
+ // |original_signon_realm| on |pending_credentials_|, which unmarks it as |
+ // a PSL match. |
+ // |
+ // There is still the edge case when the autofilled credentials represent |
+ // the same account as |provisionally_saved_form_| but the stored password |
+ // was out of date. In that case, the user just had to manually enter the |
+ // new password, which is now in |provisionally_saved_form_|. The best |
+ // thing would be to save automatically, and also update the original |
+ // credentials. However, we have no way to tell if this is the case. |
+ // This will likely happen infrequently, and the inconvenience put on the |
+ // user by asking them is not significant, so we are fine with asking |
+ // here again. |
+ if (password_changed) { |
+ pending_credentials_.original_signon_realm.clear(); |
+ DCHECK(!IsPendingCredentialsPublicSuffixMatch()); |
+ } |
+ } else { // Not a PSL match. |
+ is_new_login_ = false; |
+ if (password_changed) |
+ user_action_ = kUserActionOverridePassword; |
+ } |
+ } else if (other_possible_username_action_ == |
+ ALLOW_OTHER_POSSIBLE_USERNAMES && |
+ UpdatePendingCredentialsIfOtherPossibleUsername( |
+ provisionally_saved_form_->username_value)) { |
+ // |pending_credentials_| is now set. Note we don't update |
+ // |pending_credentials_.username_value| to |credentials.username_value| |
+ // yet because we need to keep the original username to modify the stored |
+ // credential. |
+ selected_username_ = provisionally_saved_form_->username_value; |
+ is_new_login_ = false; |
+ } else { |
+ // User typed in a new, unknown username. |
+ user_action_ = kUserActionOverrideUsernameAndPassword; |
+ pending_credentials_ = observed_form_; |
+ if (provisionally_saved_form_->was_parsed_using_autofill_predictions) |
+ pending_credentials_.username_element = |
+ provisionally_saved_form_->username_element; |
+ pending_credentials_.username_value = |
+ provisionally_saved_form_->username_value; |
+ pending_credentials_.other_possible_usernames = |
+ provisionally_saved_form_->other_possible_usernames; |
+ |
+ // The password value will be filled in later, remove any garbage for now. |
+ pending_credentials_.password_value.clear(); |
+ pending_credentials_.new_password_value.clear(); |
+ |
+ // If this was a sign-up or change password form, the names of the elements |
+ // are likely different than those on a login form, so do not bother saving |
+ // them. We will fill them with meaningful values in UpdateLogin() when the |
+ // user goes onto a real login form for the first time. |
+ if (!provisionally_saved_form_->new_password_element.empty()) { |
+ pending_credentials_.password_element.clear(); |
+ pending_credentials_.new_password_element.clear(); |
+ } |
+ } |
+ |
+ pending_credentials_.action = provisionally_saved_form_->action; |
+ // If the user selected credentials we autofilled from a PasswordForm |
+ // that contained no action URL (IE6/7 imported passwords, for example), |
+ // bless it with the action URL from the observed form. See bug 1107719. |
+ if (pending_credentials_.action.is_empty()) |
+ pending_credentials_.action = observed_form_.action; |
+ |
+ pending_credentials_.password_value = password_to_save; |
+ pending_credentials_.preferred = provisionally_saved_form_->preferred; |
+ |
+ if (user_action_ == kUserActionOverridePassword && |
+ pending_credentials_.type == PasswordForm::TYPE_GENERATED && |
+ !has_generated_password_) { |
+ LogPasswordGenerationSubmissionEvent(PASSWORD_OVERRIDDEN); |
+ } |
+ |
+ if (has_generated_password_) |
+ pending_credentials_.type = PasswordForm::TYPE_GENERATED; |
+ |
+ provisionally_saved_form_.reset(); |
+} |
+ |
int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { |
DCHECK_EQ(state_, MATCHING_PHASE); |
// For scoring of candidate login data: |