blob: a2156657f435cc0cdc420eec8c22222b6d285c29 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/password_manager/core/browser/password_form_manager.h"
#include <stddef.h>
#include <algorithm>
#include <map>
#include <memory>
#include <utility>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/proto/server.pb.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/password_form.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/form_fetcher_impl.h"
#include "components/password_manager/core/browser/form_saver.h"
#include "components/password_manager/core/browser/log_manager.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_driver.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/browser/password_store.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/common/password_manager_features.h"
using autofill::FormStructure;
using autofill::PasswordForm;
using autofill::ValueElementPair;
using base::Time;
// Shorten the name to spare line breaks. The code provides enough context
// already.
typedef autofill::SavePasswordProgressLogger Logger;
namespace password_manager {
namespace {
std::vector<std::string> SplitPathToSegments(const std::string& path) {
return base::SplitString(path, "/", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
}
bool DoesStringContainOnlyDigits(const base::string16& s) {
for (auto c : s) {
if (!base::IsAsciiDigit(c))
return false;
}
return true;
}
// Heuristics to determine that a string is very unlikely to be a username.
bool IsProbablyNotUsername(const base::string16& s) {
return !s.empty() && DoesStringContainOnlyDigits(s) && s.size() < 3;
}
bool ShouldShowInitialPasswordAccountSuggestions() {
return base::FeatureList::IsEnabled(
password_manager::features::kFillOnAccountSelect);
}
// Update |credential| to reflect usage.
void UpdateMetadataForUsage(PasswordForm* credential) {
++credential->times_used;
// Remove alternate usernames. At this point we assume that we have found
// the right username.
credential->other_possible_usernames.clear();
}
// Returns true iff |best_matches| contain a preferred credential with a
// username other than |preferred_username|.
bool DidPreferenceChange(
const std::map<base::string16, const PasswordForm*>& best_matches,
const base::string16& preferred_username) {
for (const auto& key_value_pair : best_matches) {
const PasswordForm& form = *key_value_pair.second;
if (form.preferred && !form.is_public_suffix_match &&
form.username_value != preferred_username) {
return true;
}
}
return false;
}
// Filter sensitive information, duplicates and |username_value| out from
// |form->other_possible_usernames|.
void SanitizePossibleUsernames(PasswordForm* form) {
auto& usernames = form->other_possible_usernames;
// Deduplicate.
std::sort(usernames.begin(), usernames.end());
usernames.erase(std::unique(usernames.begin(), usernames.end()),
usernames.end());
// Filter out |form->username_value| and sensitive information.
const base::string16& username_value = form->username_value;
base::EraseIf(usernames, [&username_value](const ValueElementPair& pair) {
return pair.first == username_value ||
autofill::IsValidCreditCardNumber(pair.first) ||
autofill::IsSSN(pair.first);
});
}
// Copies field properties masks from the form |from| to the form |to|.
void CopyFieldPropertiesMasks(const PasswordForm& from, PasswordForm* to) {
// Skip copying if the number of fields is different.
if (from.form_data.fields.size() != to->form_data.fields.size())
return;
for (size_t i = 0; i < from.form_data.fields.size(); ++i) {
to->form_data.fields[i].properties_mask =
to->form_data.fields[i].name == from.form_data.fields[i].name
? from.form_data.fields[i].properties_mask
: autofill::FieldPropertiesFlags::ERROR_OCCURRED;
}
}
// Sets autofill types of password and new password fields in |field_types|.
// |password_type| (the autofill type of new password field) should be equal to
// NEW_PASSWORD, PROBABLY_NEW_PASSWORD or NOT_NEW_PASSWORD. These values
// correspond to cases when the user confirmed password update, did nothing or
// declined to update password respectively.
void SetFieldLabelsOnUpdate(const autofill::ServerFieldType password_type,
const autofill::PasswordForm& submitted_form,
FieldTypeMap* field_types) {
DCHECK(password_type == autofill::NEW_PASSWORD ||
password_type == autofill::PROBABLY_NEW_PASSWORD ||
password_type == autofill::NOT_NEW_PASSWORD)
<< password_type;
DCHECK(!submitted_form.new_password_element.empty());
(*field_types)[submitted_form.password_element] = autofill::PASSWORD;
(*field_types)[submitted_form.new_password_element] = password_type;
}
// Sets the autofill type of the password field stored in |submitted_form| to
// |password_type| in |field_types| map.
void SetFieldLabelsOnSave(const autofill::ServerFieldType password_type,
const autofill::PasswordForm& submitted_form,
FieldTypeMap* field_types) {
DCHECK(password_type == autofill::PASSWORD ||
password_type == autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD ||
password_type == autofill::ACCOUNT_CREATION_PASSWORD ||
password_type == autofill::NOT_ACCOUNT_CREATION_PASSWORD)
<< password_type;
if (!submitted_form.new_password_element.empty()) {
(*field_types)[submitted_form.new_password_element] = password_type;
} else {
DCHECK(!submitted_form.password_element.empty());
(*field_types)[submitted_form.password_element] = password_type;
}
}
// Label username and password fields with autofill types in |form_structure|
// based on |field_types|. The function also adds the types to
// |available_field_types|. For field of |USERNAME| type, the username vote
// type will be set to |username_vote_type|.
void LabelFields(const FieldTypeMap& field_types,
FormStructure* form_structure,
autofill::ServerFieldTypeSet* available_field_types,
autofill::AutofillUploadContents::Field::UsernameVoteType
username_vote_type) {
for (size_t i = 0; i < form_structure->field_count(); ++i) {
autofill::AutofillField* field = form_structure->field(i);
autofill::ServerFieldType type = autofill::UNKNOWN_TYPE;
if (!field->name.empty()) {
auto iter = field_types.find(field->name);
if (iter != field_types.end()) {
type = iter->second;
available_field_types->insert(type);
if (type == autofill::USERNAME) {
field->set_username_vote_type(username_vote_type);
DCHECK_NE(autofill::AutofillUploadContents::Field::NO_INFORMATION,
username_vote_type);
}
}
}
autofill::ServerFieldTypeSet types;
types.insert(type);
field->set_possible_types(types);
}
}
} // namespace
PasswordFormManager::PasswordFormManager(
PasswordManager* password_manager,
PasswordManagerClient* client,
const base::WeakPtr<PasswordManagerDriver>& driver,
const PasswordForm& observed_form,
std::unique_ptr<FormSaver> form_saver,
FormFetcher* form_fetcher)
: observed_form_(observed_form),
observed_form_signature_(CalculateFormSignature(observed_form.form_data)),
other_possible_username_action_(
PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES),
form_path_segments_(
observed_form_.origin.is_valid()
? SplitPathToSegments(observed_form_.origin.path())
: std::vector<std::string>()),
is_new_login_(true),
has_autofilled_(false),
has_generated_password_(false),
generated_password_changed_(false),
is_manual_generation_(false),
generation_popup_was_shown_(false),
form_classifier_outcome_(kNoOutcome),
password_overridden_(false),
retry_password_form_password_update_(false),
password_manager_(password_manager),
preferred_match_(nullptr),
is_possible_change_password_form_without_username_(
observed_form.IsPossibleChangePasswordFormWithoutUsername()),
client_(client),
user_action_(UserAction::kNone),
form_saver_(std::move(form_saver)),
owned_form_fetcher_(
form_fetcher ? nullptr
: std::make_unique<FormFetcherImpl>(
PasswordStore::FormDigest(observed_form),
client,
true /* should_migrate_http_passwords */,
true /* should_query_suppressed_https_forms */)),
form_fetcher_(form_fetcher ? form_fetcher : owned_form_fetcher_.get()),
is_main_frame_secure_(client->IsMainFrameSecure()) {
// Non-HTML forms should not need any interaction with the renderer, and hence
// no driver. Note that cloned PasswordFormManager instances can have HTML
// forms without drivers as well.
DCHECK((observed_form.scheme == PasswordForm::SCHEME_HTML) ||
(driver == nullptr))
<< observed_form.scheme;
if (driver)
drivers_.push_back(driver);
}
void PasswordFormManager::Init(
scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder) {
DCHECK(!metrics_recorder_) << "Do not call Init twice.";
metrics_recorder_ = std::move(metrics_recorder);
if (!metrics_recorder_) {
metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
client_->IsMainFrameSecure(), client_->GetUkmSourceId());
}
if (owned_form_fetcher_)
owned_form_fetcher_->Fetch();
form_fetcher_->AddConsumer(this);
}
PasswordFormManager::~PasswordFormManager() {
form_fetcher_->RemoveConsumer(this);
metrics_recorder_->RecordHistogramsOnSuppressedAccounts(
observed_form_.origin.SchemeIsCryptographic(), *form_fetcher_,
pending_credentials_);
}
// static
base::string16 PasswordFormManager::PasswordToSave(const PasswordForm& form) {
if (form.new_password_element.empty() || form.new_password_value.empty())
return form.password_value;
return form.new_password_value;
}
// TODO(crbug.com/700420): Refactor this function, to make comparison more
// reliable.
PasswordFormManager::MatchResultMask PasswordFormManager::DoesManage(
const PasswordForm& form,
const password_manager::PasswordManagerDriver* driver) const {
// Non-HTML form case.
if (observed_form_.scheme != PasswordForm::SCHEME_HTML ||
form.scheme != PasswordForm::SCHEME_HTML) {
const bool forms_match = observed_form_.signon_realm == form.signon_realm &&
observed_form_.scheme == form.scheme;
return forms_match ? RESULT_COMPLETE_MATCH : RESULT_NO_MATCH;
}
// HTML form case.
MatchResultMask result = RESULT_NO_MATCH;
if (observed_form_.signon_realm != form.signon_realm)
return result;
// Easiest case of matching origins.
bool origins_match = form.origin == observed_form_.origin;
// If this is a replay of the same form in the case a user entered an invalid
// password, the origin of the new form may equal the action of the "first"
// form instead.
origins_match = origins_match || (form.origin == observed_form_.action);
// Otherwise, if action hosts are the same, the old URL scheme is HTTP while
// the new one is HTTPS, and the new path equals to or extends the old path,
// we also consider the actions a match. This is to accommodate cases where
// the original login form is on an HTTP page, but a failed login attempt
// redirects to HTTPS (as in http://example.org -> https://example.org/auth).
if (!origins_match && !observed_form_.origin.SchemeIsCryptographic() &&
form.origin.SchemeIsCryptographic()) {
const base::StringPiece& old_path = observed_form_.origin.path_piece();
const base::StringPiece& new_path = form.origin.path_piece();
origins_match =
observed_form_.origin.host_piece() == form.origin.host_piece() &&
observed_form_.origin.port() == form.origin.port() &&
base::StartsWith(new_path, old_path, base::CompareCase::SENSITIVE);
}
if (driver)
origins_match =
origins_match ||
std::any_of(drivers_.begin(), drivers_.end(),
[driver](const base::WeakPtr<PasswordManagerDriver>& d) {
return d.get() == driver;
});
if (!origins_match)
return result;
result |= RESULT_ORIGINS_OR_FRAMES_MATCH;
if (CalculateFormSignature(form.form_data) == observed_form_signature_)
result |= RESULT_SIGNATURE_MATCH;
if (form.form_data.name == observed_form_.form_data.name)
result |= RESULT_FORM_NAME_MATCH;
// Note: although saved password forms might actually have an empty action
// URL if they were imported (see bug 1107719), the |form| we see here comes
// never from the password store, and should have an exactly matching action.
if (form.action == observed_form_.action)
result |= RESULT_ACTION_MATCH;
return result;
}
bool PasswordFormManager::IsBlacklisted() const {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
return !blacklisted_matches_.empty();
}
void PasswordFormManager::PermanentlyBlacklist() {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
DCHECK(!client_->IsIncognito());
if (!new_blacklisted_) {
new_blacklisted_ = std::make_unique<PasswordForm>(observed_form_);
blacklisted_matches_.push_back(new_blacklisted_.get());
}
form_saver_->PermanentlyBlacklist(new_blacklisted_.get());
}
bool PasswordFormManager::IsNewLogin() const {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
return is_new_login_;
}
bool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() const {
return pending_credentials_.is_public_suffix_match;
}
void PasswordFormManager::ProvisionallySave(
const PasswordForm& credentials,
OtherPossibleUsernamesAction action) {
std::unique_ptr<autofill::PasswordForm> mutable_submitted_form(
new PasswordForm(credentials));
if (credentials.IsPossibleChangePasswordForm() &&
!credentials.username_value.empty() &&
IsProbablyNotUsername(credentials.username_value)) {
mutable_submitted_form->username_value.clear();
mutable_submitted_form->username_element.clear();
is_possible_change_password_form_without_username_ = true;
}
submitted_form_ = std::move(mutable_submitted_form);
other_possible_username_action_ = action;
if (form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING)
CreatePendingCredentials();
}
void PasswordFormManager::Save() {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
DCHECK(!client_->IsIncognito());
metrics_util::LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
submitted_form_->submission_event);
if ((user_action_ == UserAction::kNone) &&
DidPreferenceChange(best_matches_, pending_credentials_.username_value)) {
SetUserAction(UserAction::kChoose);
}
if (is_new_login_) {
SanitizePossibleUsernames(&pending_credentials_);
pending_credentials_.date_created = base::Time::Now();
SendVotesOnSave();
form_saver_->Save(pending_credentials_, best_matches_);
} else {
ProcessUpdate();
std::vector<PasswordForm> credentials_to_update;
base::Optional<PasswordForm> old_primary_key =
UpdatePendingAndGetOldKey(&credentials_to_update);
form_saver_->Update(pending_credentials_, best_matches_,
&credentials_to_update,
old_primary_key ? &old_primary_key.value() : nullptr);
}
// This is not in ProcessUpdate() to catch PSL matched credentials.
if (pending_credentials_.times_used != 0 &&
pending_credentials_.type == PasswordForm::TYPE_GENERATED) {
metrics_util::LogPasswordGenerationSubmissionEvent(
metrics_util::PASSWORD_USED);
}
password_manager_->UpdateFormManagers();
}
void PasswordFormManager::Update(
const autofill::PasswordForm& credentials_to_update) {
metrics_util::LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
submitted_form_->submission_event);
if (observed_form_.IsPossibleChangePasswordForm()) {
FormStructure form_structure(credentials_to_update.form_data);
UploadPasswordVote(observed_form_, autofill::NEW_PASSWORD,
form_structure.FormSignatureAsStr());
}
base::string16 password_to_save = pending_credentials_.password_value;
bool skip_zero_click = pending_credentials_.skip_zero_click;
pending_credentials_ = credentials_to_update;
pending_credentials_.password_value = password_to_save;
pending_credentials_.skip_zero_click = skip_zero_click;
pending_credentials_.preferred = true;
is_new_login_ = false;
ProcessUpdate();
std::vector<PasswordForm> more_credentials_to_update;
base::Optional<PasswordForm> old_primary_key =
UpdatePendingAndGetOldKey(&more_credentials_to_update);
form_saver_->Update(pending_credentials_, best_matches_,
&more_credentials_to_update,
old_primary_key ? &old_primary_key.value() : nullptr);
password_manager_->UpdateFormManagers();
}
void PasswordFormManager::UpdateUsername(const base::string16& new_username) {
PasswordForm credential(*submitted_form_);
credential.username_value = new_username;
// If |new_username| is not found in |other_possible_usernames|, store empty
// |username_element|.
credential.username_element.clear();
// |has_username_edited_vote_| is true iff |new_username| was typed in another
// field. Otherwise, |has_username_edited_vote_| is false and no vote will be
// uploaded.
has_username_edited_vote_ = false;
if (!new_username.empty()) {
for (size_t i = 0; i < credential.other_possible_usernames.size(); ++i) {
if (credential.other_possible_usernames[i].first == new_username) {
credential.username_element =
credential.other_possible_usernames[i].second;
credential.other_possible_usernames.erase(
credential.other_possible_usernames.begin() + i);
// Set |corrected_username_element_| to upload a username vote.
has_username_edited_vote_ = true;
break;
}
}
}
// A user may make a mistake and remove the correct username. So, save
// |username_value| and |username_element| of the submitted form. When the
// user has to override the username, Chrome will send a username vote.
if (!submitted_form_->username_value.empty()) {
credential.other_possible_usernames.push_back(autofill::ValueElementPair(
submitted_form_->username_value, submitted_form_->username_element));
}
ProvisionallySave(credential, IGNORE_OTHER_POSSIBLE_USERNAMES);
}
void PasswordFormManager::UpdatePasswordValue(
const base::string16& new_password) {
pending_credentials_.password_value = new_password;
}
void PasswordFormManager::PresaveGeneratedPassword(
const autofill::PasswordForm& form) {
form_saver()->PresaveGeneratedPassword(form);
metrics_recorder_->SetHasGeneratedPassword(true);
if (has_generated_password_) {
generated_password_changed_ = true;
} else {
SetHasGeneratedPassword(true);
generated_password_changed_ = false;
}
}
void PasswordFormManager::PasswordNoLongerGenerated() {
DCHECK(has_generated_password_);
form_saver()->RemovePresavedPassword();
SetHasGeneratedPassword(false);
generated_password_changed_ = false;
}
void PasswordFormManager::SaveSubmittedFormTypeForMetrics(
const autofill::PasswordForm& form) {
bool is_change_password_form =
!form.new_password_value.empty() && !form.password_value.empty();
bool is_signup_form =
!form.new_password_value.empty() && form.password_value.empty();
bool no_username = form.username_value.empty();
PasswordFormMetricsRecorder::SubmittedFormType type =
PasswordFormMetricsRecorder::kSubmittedFormTypeUnspecified;
if (form.layout == PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP) {
type = PasswordFormMetricsRecorder::kSubmittedFormTypeLoginAndSignup;
} else if (is_change_password_form) {
type = PasswordFormMetricsRecorder::kSubmittedFormTypeChangePasswordEnabled;
} else if (is_signup_form) {
if (no_username)
type = PasswordFormMetricsRecorder::kSubmittedFormTypeSignupNoUsername;
else
type = PasswordFormMetricsRecorder::kSubmittedFormTypeSignup;
} else if (no_username) {
type = PasswordFormMetricsRecorder::kSubmittedFormTypeLoginNoUsername;
} else {
type = PasswordFormMetricsRecorder::kSubmittedFormTypeLogin;
}
metrics_recorder_->SetSubmittedFormType(type);
}
void PasswordFormManager::ScoreMatches(
const std::vector<const PasswordForm*>& matches) {
DCHECK(std::all_of(
matches.begin(), matches.end(),
[](const PasswordForm* match) { return !match->blacklisted_by_user; }));
preferred_match_ = nullptr;
best_matches_.clear();
not_best_matches_.clear();
if (matches.empty())
return;
// Compute scores.
std::vector<uint32_t> credential_scores(matches.size());
for (size_t i = 0; i < matches.size(); ++i)
credential_scores[i] = ScoreResult(*matches[i]);
const uint32_t best_score =
*std::max_element(credential_scores.begin(), credential_scores.end());
// Compute best score for each username.
std::map<base::string16, uint32_t> best_scores;
for (size_t i = 0; i < matches.size(); ++i) {
uint32_t& score = best_scores[matches[i]->username_value];
score = std::max(score, credential_scores[i]);
}
// Find the best match for each username, move the rest to
// |non_best_matches_|. Also assign the overall best match to
// |preferred_match_|.
not_best_matches_.reserve(matches.size() - best_scores.size());
// Fill |best_matches_| with the best-scoring credentials for each username.
for (size_t i = 0; i < matches.size(); ++i) {
const PasswordForm* const match = matches[i];
const base::string16& username = match->username_value;
if (credential_scores[i] < best_scores[username]) {
not_best_matches_.push_back(match);
continue;
}
if (!preferred_match_ && credential_scores[i] == best_score)
preferred_match_ = match;
// If there is already another best-score match for the same username, leave
// it and add the current form to |not_best_matches_|.
if (best_matches_.find(username) != best_matches_.end())
not_best_matches_.push_back(match);
else
best_matches_.insert(std::make_pair(username, match));
}
}
void PasswordFormManager::ProcessMatches(
const std::vector<const PasswordForm*>& non_federated,
size_t filtered_count) {
blacklisted_matches_.clear();
new_blacklisted_.reset();
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_PROCESS_MATCHES_METHOD);
}
// Copy out and score non-blacklisted matches.
std::vector<const PasswordForm*> matches(std::count_if(
non_federated.begin(), non_federated.end(),
[this](const PasswordForm* form) { return IsMatch(*form); }));
std::copy_if(non_federated.begin(), non_federated.end(), matches.begin(),
[this](const PasswordForm* form) { return IsMatch(*form); });
ScoreMatches(matches);
// Copy out blacklisted matches.
blacklisted_matches_.resize(std::count_if(
non_federated.begin(), non_federated.end(),
[this](const PasswordForm* form) { return IsBlacklistMatch(*form); }));
std::copy_if(
non_federated.begin(), non_federated.end(), blacklisted_matches_.begin(),
[this](const PasswordForm* form) { return IsBlacklistMatch(*form); });
UMA_HISTOGRAM_COUNTS(
"PasswordManager.NumPasswordsNotShown",
non_federated.size() + filtered_count - best_matches_.size());
// If password store was slow and provisionally saved form is already here
// then create pending credentials (see http://crbug.com/470322).
if (submitted_form_)
CreatePendingCredentials();
for (auto const& driver : drivers_)
ProcessFrameInternal(driver);
if (observed_form_.scheme != PasswordForm::SCHEME_HTML)
ProcessLoginPrompt();
}
void PasswordFormManager::ProcessFrame(
const base::WeakPtr<PasswordManagerDriver>& driver) {
DCHECK_EQ(PasswordForm::SCHEME_HTML, observed_form_.scheme);
// Don't keep processing the same form.
if (autofills_left_ <= 0)
return;
autofills_left_--;
if (form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING)
ProcessFrameInternal(driver);
for (auto const& old_driver : drivers_) {
// |drivers_| is not a set because WeakPtr has no good candidate for a key
// (the address may change to null). So let's weed out duplicates in O(N).
if (old_driver.get() == driver.get())
return;
}
drivers_.push_back(driver);
}
void PasswordFormManager::ProcessFrameInternal(
const base::WeakPtr<PasswordManagerDriver>& driver) {
DCHECK_EQ(PasswordForm::SCHEME_HTML, observed_form_.scheme);
if (!driver)
return;
if (IsBlacklisted())
driver->MatchingBlacklistedFormFound();
driver->AllowPasswordGenerationForForm(observed_form_);
if (best_matches_.empty()) {
driver->InformNoSavedCredentials();
metrics_recorder_->RecordFillEvent(
PasswordFormMetricsRecorder::kManagerFillEventNoCredential);
return;
}
// Proceed to autofill.
// Note that we provide the choices but don't actually prefill a value if:
// (1) we are in Incognito mode, or
// (2) if it matched using public suffix domain matching, or
// (3) the form is change password form.
bool wait_for_username = client_->IsIncognito() ||
preferred_match_->is_public_suffix_match ||
observed_form_.IsPossibleChangePasswordForm();
if (wait_for_username) {
metrics_recorder_->SetManagerAction(
PasswordFormMetricsRecorder::kManagerActionNone);
metrics_recorder_->RecordFillEvent(
PasswordFormMetricsRecorder::kManagerFillEventBlockedOnInteraction);
} else {
has_autofilled_ = true;
metrics_recorder_->SetManagerAction(
PasswordFormMetricsRecorder::kManagerActionAutofilled);
metrics_recorder_->RecordFillEvent(
PasswordFormMetricsRecorder::kManagerFillEventAutofilled);
base::RecordAction(base::UserMetricsAction("PasswordManager_Autofilled"));
}
if (ShouldShowInitialPasswordAccountSuggestions()) {
// This is for the fill-on-account-select experiment. Instead of autofilling
// found usernames and passwords on load, this instructs the renderer to
// return with any found password forms so a list of password account
// suggestions can be drawn.
password_manager_->ShowInitialPasswordAccountSuggestions(
driver.get(), observed_form_, best_matches_, *preferred_match_,
wait_for_username);
} else {
// If fill-on-account-select is not enabled, continue with autofilling any
// password forms as traditionally has been done.
password_manager_->Autofill(driver.get(), observed_form_, best_matches_,
form_fetcher_->GetFederatedMatches(),
*preferred_match_, wait_for_username);
}
}
void PasswordFormManager::ProcessLoginPrompt() {
DCHECK_NE(PasswordForm::SCHEME_HTML, observed_form_.scheme);
if (!preferred_match_) {
DCHECK(best_matches_.empty());
metrics_recorder_->RecordFillEvent(
PasswordFormMetricsRecorder::kManagerFillEventNoCredential);
return;
}
has_autofilled_ = true;
metrics_recorder_->SetManagerAction(
PasswordFormMetricsRecorder::kManagerActionAutofilled);
metrics_recorder_->RecordFillEvent(
PasswordFormMetricsRecorder::kManagerFillEventAutofilled);
password_manager_->AutofillHttpAuth(best_matches_, *preferred_match_);
}
void PasswordFormManager::ProcessUpdate() {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
DCHECK(preferred_match_ || !pending_credentials_.federation_origin.unique());
// If we're doing an Update, we either autofilled correctly and need to
// update the stats, or the user typed in a new password for autofilled
// username, or the user selected one of the non-preferred matches,
// thus requiring a swap of preferred bits.
DCHECK(!IsNewLogin() && pending_credentials_.preferred);
DCHECK(!client_->IsIncognito());
UpdateMetadataForUsage(&pending_credentials_);
base::RecordAction(
base::UserMetricsAction("PasswordManager_LoginFollowingAutofill"));
// Check to see if this form is a candidate for password generation.
// Do not send votes on change password forms, since they were already sent in
// Update() method.
if (!observed_form_.IsPossibleChangePasswordForm())
SendVoteOnCredentialsReuse(observed_form_, &pending_credentials_);
}
bool PasswordFormManager::UpdatePendingCredentialsIfOtherPossibleUsername(
const base::string16& username) {
for (const auto& key_value : best_matches_) {
const PasswordForm& match = *key_value.second;
for (size_t i = 0; i < match.other_possible_usernames.size(); ++i) {
if (match.other_possible_usernames[i].first == username) {
pending_credentials_ = match;
return true;
}
}
}
return false;
}
bool PasswordFormManager::FindUsernameInOtherPossibleUsernames(
const autofill::PasswordForm& match,
const base::string16& username) {
DCHECK(!username_correction_vote_);
for (const ValueElementPair& pair : match.other_possible_usernames) {
if (pair.first == username) {
username_correction_vote_.reset(new autofill::PasswordForm(match));
username_correction_vote_->username_element = pair.second;
return true;
}
}
return false;
}
bool PasswordFormManager::FindCorrectedUsernameElement(
const base::string16& username,
const base::string16& password) {
if (username.empty())
return false;
for (const auto& key_value : best_matches_) {
const PasswordForm* match = key_value.second;
if ((match->password_value == password) &&
FindUsernameInOtherPossibleUsernames(*match, username))
return true;
}
for (const autofill::PasswordForm* match : not_best_matches_) {
if ((match->password_value == password) &&
FindUsernameInOtherPossibleUsernames(*match, username))
return true;
}
return false;
}
void PasswordFormManager::SendVoteOnCredentialsReuse(
const PasswordForm& observed,
PasswordForm* pending) {
// Ignore |pending_structure| if its FormData has no fields. This is to
// weed out those credentials that were saved before FormData was added
// to PasswordForm. Even without this check, these FormStructure's won't
// be uploaded, but it makes it hard to see if we are encountering
// unexpected errors.
if (pending->form_data.fields.empty())
return;
FormStructure pending_structure(pending->form_data);
FormStructure observed_structure(observed.form_data);
if (pending_structure.form_signature() !=
observed_structure.form_signature()) {
// Only upload if this is the first time the password has been used.
// Otherwise the credentials have been used on the same field before so
// they aren't from an account creation form.
// Also bypass uploading if the username was edited. Offering generation
// in cases where we currently save the wrong username isn't great.
// TODO(gcasto): Determine if generation should be offered in this case.
if (pending->times_used == 1 && selected_username_.empty()) {
if (UploadPasswordVote(*pending, autofill::ACCOUNT_CREATION_PASSWORD,
observed_structure.FormSignatureAsStr())) {
pending->generation_upload_status =
autofill::PasswordForm::POSITIVE_SIGNAL_SENT;
}
}
} else if (pending->generation_upload_status ==
autofill::PasswordForm::POSITIVE_SIGNAL_SENT) {
// A signal was sent that this was an account creation form, but the
// credential is now being used on the same form again. This cancels out
// the previous vote.
if (UploadPasswordVote(*pending, autofill::NOT_ACCOUNT_CREATION_PASSWORD,
std::string())) {
pending->generation_upload_status =
autofill::PasswordForm::NEGATIVE_SIGNAL_SENT;
}
} else if (generation_popup_was_shown_) {
// Even if there is no autofill vote to be sent, send the vote about the
// usage of the generation popup.
UploadPasswordVote(*pending, autofill::UNKNOWN_TYPE, std::string());
}
}
bool PasswordFormManager::UploadPasswordVote(
const autofill::PasswordForm& form_to_upload,
const autofill::ServerFieldType& autofill_type,
const std::string& login_form_signature) {
// Check if there is any vote to be sent.
bool has_autofill_vote = autofill_type != autofill::UNKNOWN_TYPE;
bool has_password_generation_vote = generation_popup_was_shown_;
if (!has_autofill_vote && !has_password_generation_vote)
return false;
autofill::AutofillManager* autofill_manager =
client_->GetAutofillManagerForMainFrame();
if (!autofill_manager || !autofill_manager->download_manager())
return false;
// If this is an update, a vote about the observed form is sent. If the user
// re-uses credentials, a vote about the saved form is sent. If the user saves
// credentials, the observed and pending forms are the same.
FormStructure form_structure(form_to_upload.form_data);
if (!autofill_manager->ShouldUploadForm(form_structure)) {
UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", false);
return false;
}
autofill::ServerFieldTypeSet available_field_types;
// A map from field names to field types.
FieldTypeMap field_types;
autofill::AutofillUploadContents::Field::UsernameVoteType username_vote_type =
autofill::AutofillUploadContents::Field::NO_INFORMATION;
if (autofill_type != autofill::USERNAME) {
if (has_autofill_vote) {
DCHECK(submitted_form_);
bool is_update = autofill_type == autofill::NEW_PASSWORD ||
autofill_type == autofill::PROBABLY_NEW_PASSWORD ||
autofill_type == autofill::NOT_NEW_PASSWORD;
if (is_update) {
if (submitted_form_->new_password_element.empty())
return false;
SetFieldLabelsOnUpdate(autofill_type, *submitted_form_, &field_types);
} else { // Saving.
SetFieldLabelsOnSave(autofill_type, *submitted_form_, &field_types);
}
field_types[submitted_form_->confirmation_password_element] =
autofill::CONFIRMATION_PASSWORD;
}
if (autofill_type != autofill::ACCOUNT_CREATION_PASSWORD) {
if (generation_popup_was_shown_)
AddGeneratedVote(&form_structure);
if (form_classifier_outcome_ != kNoOutcome)
AddFormClassifierVote(&form_structure);
if (has_username_edited_vote_) {
field_types[form_to_upload.username_element] = autofill::USERNAME;
username_vote_type =
autofill::AutofillUploadContents::Field::USERNAME_EDITED;
}
} else { // User reuses credentials.
// If the saved username value was used, then send a confirmation vote for
// username.
if (!submitted_form_->username_value.empty()) {
DCHECK(submitted_form_->username_value ==
form_to_upload.username_value);
field_types[form_to_upload.username_element] = autofill::USERNAME;
username_vote_type =
autofill::AutofillUploadContents::Field::CREDENTIALS_REUSED;
}
}
} else { // User overwrites username.
field_types[form_to_upload.username_element] = autofill::USERNAME;
field_types[form_to_upload.password_element] =
autofill::ACCOUNT_CREATION_PASSWORD;
username_vote_type =
autofill::AutofillUploadContents::Field::USERNAME_OVERWRITTEN;
}
LabelFields(field_types, &form_structure, &available_field_types,
username_vote_type);
// Force uploading as these events are relatively rare and we want to make
// sure to receive them.
form_structure.set_upload_required(UPLOAD_REQUIRED);
if (password_manager_util::IsLoggingActive(client_)) {
BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
logger.LogFormStructure(Logger::STRING_FORM_VOTES, form_structure);
}
bool success = autofill_manager->download_manager()->StartUploadRequest(
form_structure, false /* was_autofilled */, available_field_types,
login_form_signature, true /* observed_submission */);
UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success);
return success;
}
void PasswordFormManager::AddGeneratedVote(
autofill::FormStructure* form_structure) {
DCHECK(form_structure);
DCHECK(generation_popup_was_shown_);
if (generation_element_.empty())
return;
autofill::AutofillUploadContents::Field::PasswordGenerationType type =
autofill::AutofillUploadContents::Field::NO_GENERATION;
if (has_generated_password_) {
if (is_manual_generation_) {
type = observed_form_.IsPossibleChangePasswordForm()
? autofill::AutofillUploadContents::Field::
MANUALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
: autofill::AutofillUploadContents::Field::
MANUALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
} else {
type =
observed_form_.IsPossibleChangePasswordForm()
? autofill::AutofillUploadContents::Field::
AUTOMATICALLY_TRIGGERED_GENERATION_ON_CHANGE_PASSWORD_FORM
: autofill::AutofillUploadContents::Field::
AUTOMATICALLY_TRIGGERED_GENERATION_ON_SIGN_UP_FORM;
}
} else
type = autofill::AutofillUploadContents::Field::IGNORED_GENERATION_POPUP;
for (size_t i = 0; i < form_structure->field_count(); ++i) {
autofill::AutofillField* field = form_structure->field(i);
if (field->name == generation_element_) {
field->set_generation_type(type);
field->set_generated_password_changed(generated_password_changed_);
break;
}
}
}
void PasswordFormManager::AddFormClassifierVote(
autofill::FormStructure* form_structure) {
DCHECK(form_structure);
DCHECK(form_classifier_outcome_ != kNoOutcome);
for (size_t i = 0; i < form_structure->field_count(); ++i) {
autofill::AutofillField* field = form_structure->field(i);
if (form_classifier_outcome_ == kFoundGenerationElement &&
field->name == generation_element_detected_by_classifier_) {
field->set_form_classifier_outcome(
autofill::AutofillUploadContents::Field::GENERATION_ELEMENT);
} else {
field->set_form_classifier_outcome(
autofill::AutofillUploadContents::Field::NON_GENERATION_ELEMENT);
}
}
}
void PasswordFormManager::CreatePendingCredentials() {
DCHECK(submitted_form_);
base::string16 password_to_save(PasswordToSave(*submitted_form_));
// Look for the actually submitted credentials in the list of previously saved
// credentials that were available to autofilling.
// This first match via FindBestSavedMatch focuses on matches by username and
// falls back to password based matches if |submitted_form_| has no username
// filled.
const PasswordForm* saved_form = FindBestSavedMatch(submitted_form_.get());
if (saved_form != nullptr) {
// The user signed in with a login we autofilled.
pending_credentials_ = *saved_form;
password_overridden_ =
pending_credentials_.password_value != password_to_save;
if (IsPendingCredentialsPublicSuffixMatch()) {
// If the autofilled credentials were a PSL match or credentials stored
// from Android apps, 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;
SetUserAction(password_overridden_ ? UserAction::kOverridePassword
: UserAction::kChoosePslMatch);
// Since this credential will not overwrite a previously saved credential,
// username_value can be updated now.
if (!selected_username_.empty())
pending_credentials_.username_value = selected_username_;
// 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_);
// Update |pending_credentials_| in order to be able correctly save it.
pending_credentials_.origin = submitted_form_->origin;
pending_credentials_.signon_realm = submitted_form_->signon_realm;
// 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 |submitted_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 setting
// |password_overriden_| on |pending_credentials_| to false and setting
// |origin| and |signon_realm| to correct values.
//
// There is still the edge case when the autofilled credentials represent
// the same account as |submitted_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 |submitted_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_overridden_) {
pending_credentials_.is_public_suffix_match = false;
password_overridden_ = false;
}
} else { // Not a PSL match but a match of an already stored credential.
is_new_login_ = false;
if (password_overridden_) {
// Stored credential matched by username but with mismatching password.
// This means the user has overridden the password.
SetUserAction(UserAction::kOverridePassword);
}
}
} else if (other_possible_username_action_ ==
ALLOW_OTHER_POSSIBLE_USERNAMES &&
UpdatePendingCredentialsIfOtherPossibleUsername(
submitted_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_ = submitted_form_->username_value;
is_new_login_ = false;
} else if (!best_matches_.empty() &&
submitted_form_->type != autofill::PasswordForm::TYPE_API &&
submitted_form_->username_value.empty()) {
// This branch deals with the case that the submitted form has no username
// element and needs to decide whether to offer to update any credentials.
// In that case, the user can select any previously stored credential as
// the one to update, but we still try to find the best candidate.
// Find the best candidate to select by default in the password update
// bubble. If no best candidate is found, any one can be offered.
const PasswordForm* best_update_match =
FindBestMatchForUpdatePassword(submitted_form_->password_value);
// A retry password form is one that consists of only an "old password"
// field, i.e. one that is not a "new password".
retry_password_form_password_update_ =
submitted_form_->username_value.empty() &&
submitted_form_->new_password_value.empty();
is_new_login_ = false;
if (best_update_match) {
// Chose |best_update_match| to be updated.
pending_credentials_ = *best_update_match;
} else if (has_generated_password_) {
// If a password was generated and we didn't find a match, we have to save
// it in a separate entry since we have to store it but we don't know
// where.
CreatePendingCredentialsForNewCredentials();
is_new_login_ = true;
} else {
// We don't have a good candidate to choose as the default credential for
// the update bubble and the user has to pick one.
// We set |pending_credentials_| to the bare minimum, which is the correct
// origin.
pending_credentials_.origin = submitted_form_->origin;
}
} else {
is_new_login_ = true;
// No stored credentials can be matched to the submitted form. Offer to
// save new credentials.
CreatePendingCredentialsForNewCredentials();
// Generate username correction votes.
bool username_correction_found = FindCorrectedUsernameElement(
submitted_form_->username_value, submitted_form_->password_value);
UMA_HISTOGRAM_BOOLEAN("PasswordManager.UsernameCorrectionFound",
username_correction_found);
if (username_correction_found) {
metrics_recorder_->RecordDetailedUserAction(
password_manager::PasswordFormMetricsRecorder::DetailedUserAction::
kCorrectedUsernameInForm);
}
}
if (!IsValidAndroidFacetURI(pending_credentials_.signon_realm)) {
pending_credentials_.action = submitted_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 b/1107719.
if (pending_credentials_.action.is_empty())
pending_credentials_.action = observed_form_.action;
}
pending_credentials_.password_value = password_to_save;
pending_credentials_.preferred = submitted_form_->preferred;
pending_credentials_.form_has_autofilled_value =
submitted_form_->form_has_autofilled_value;
pending_credentials_.all_possible_passwords =
submitted_form_->all_possible_passwords;
CopyFieldPropertiesMasks(*submitted_form_, &pending_credentials_);
// If we're dealing with an API-driven provisionally saved form, then take
// the server provided values. We don't do this for non-API forms, as
// those will never have those members set.
if (submitted_form_->type == autofill::PasswordForm::TYPE_API) {
pending_credentials_.skip_zero_click = submitted_form_->skip_zero_click;
pending_credentials_.display_name = submitted_form_->display_name;
pending_credentials_.federation_origin = submitted_form_->federation_origin;
pending_credentials_.icon_url = submitted_form_->icon_url;
// Take the correct signon_realm for federated credentials.
pending_credentials_.signon_realm = submitted_form_->signon_realm;
}
if (user_action_ == UserAction::kOverridePassword &&
pending_credentials_.type == PasswordForm::TYPE_GENERATED &&
!has_generated_password_) {
metrics_util::LogPasswordGenerationSubmissionEvent(
metrics_util::PASSWORD_OVERRIDDEN);
pending_credentials_.type = PasswordForm::TYPE_MANUAL;
}
if (has_generated_password_)
pending_credentials_.type = PasswordForm::TYPE_GENERATED;
}
uint32_t PasswordFormManager::ScoreResult(const PasswordForm& candidate) const {
DCHECK(!candidate.blacklisted_by_user);
// For scoring of candidate login data:
// The most important element that should match is the signon_realm followed
// by the origin, the action, the password name, the submit button name, and
// finally the username input field name.
// If public suffix origin match was not used, it gives an addition of
// 128 (1 << 7).
// Exact origin match gives an addition of 64 (1 << 6) + # of matching url
// dirs.
// Partial match gives an addition of 32 (1 << 5) + # matching url dirs
// That way, a partial match cannot trump an exact match even if
// the partial one matches all other attributes (action, elements) (and
// regardless of the matching depth in the URL path).
// When comparing path segments, only consider at most 63 of them, so that the
// potential gain from shared path prefix is not more than from an exact
// origin match.
const size_t kSegmentCountCap = 63;
const size_t capped_form_path_segment_count =
std::min(form_path_segments_.size(), kSegmentCountCap);
uint32_t score = 0u;
if (!candidate.is_public_suffix_match) {
score += 1u << 8;
}
if (candidate.preferred)
score += 1u << 7;
if (candidate.origin == observed_form_.origin) {
// This check is here for the most common case which
// is we have a single match in the db for the given host,
// so we don't generally need to walk the entire URL path (the else
// clause).
score += (1u << 6) + static_cast<uint32_t>(capped_form_path_segment_count);
} else {
// Walk the origin URL paths one directory at a time to see how
// deep the two match.
std::vector<std::string> candidate_path_segments =
SplitPathToSegments(candidate.origin.path());
size_t depth = 0u;
const size_t max_dirs = std::min(capped_form_path_segment_count,
candidate_path_segments.size());
while ((depth < max_dirs) &&
(form_path_segments_[depth] == candidate_path_segments[depth])) {
depth++;
score++;
}
// do we have a partial match?
score += (depth > 0u) ? 1u << 5 : 0u;
}
if (observed_form_.scheme == PasswordForm::SCHEME_HTML) {
if (candidate.action == observed_form_.action)
score += 1u << 3;
if (candidate.password_element == observed_form_.password_element)
score += 1u << 2;
if (candidate.submit_element == observed_form_.submit_element)
score += 1u << 1;
if (candidate.username_element == observed_form_.username_element)
score += 1u << 0;
}
return score;
}
bool PasswordFormManager::IsMatch(const autofill::PasswordForm& form) const {
return !form.blacklisted_by_user && form.scheme == observed_form_.scheme;
}
bool PasswordFormManager::IsBlacklistMatch(
const autofill::PasswordForm& blacklisted_form) const {
return blacklisted_form.blacklisted_by_user &&
!blacklisted_form.is_public_suffix_match &&
blacklisted_form.scheme == observed_form_.scheme &&
blacklisted_form.origin.GetOrigin() ==
observed_form_.origin.GetOrigin();
}
const PasswordForm* PasswordFormManager::FindBestMatchForUpdatePassword(
const base::string16& password) const {
// This function is called for forms that do not contain a username field.
// This means that we cannot update credentials based on a matching username
// and that we may need to show an update prompt.
if (best_matches_.size() == 1 && !has_generated_password_) {
// In case the submitted form contained no username but a password, and if
// the user has only one credential stored, return it as the one that should
// be updated.
return best_matches_.begin()->second;
}
if (password.empty())
return nullptr;
// Return any existing credential that has the same |password| saved already.
for (const auto& key_value : best_matches_) {
if (key_value.second->password_value == password)
return key_value.second;
}
return nullptr;
}
const PasswordForm* PasswordFormManager::FindBestSavedMatch(
const PasswordForm* submitted_form) const {
if (!submitted_form->federation_origin.unique())
return nullptr;
// Return form with matching |username_value|.
auto it = best_matches_.find(submitted_form->username_value);
if (it != best_matches_.end())
return it->second;
// Match Credential API forms only by username. Stop here if nothing was found
// above.
if (submitted_form->type == autofill::PasswordForm::TYPE_API)
return nullptr;
// Verify that the submitted form has no username and no "new password"
// and bail out with a nullptr otherwise.
bool submitted_form_has_username = !submitted_form->username_value.empty();
bool submitted_form_has_new_password_element =
!submitted_form->new_password_value.empty();
if (submitted_form_has_username || submitted_form_has_new_password_element)
return nullptr;
// At this line we are certain that the submitted form contains only a
// password field that is not a "new password". Now we can check whether we
// have a match by password of an already saved credential.
for (const auto& stored_match : best_matches_) {
if (stored_match.second->password_value == submitted_form->password_value)
return stored_match.second;
}
return nullptr;
}
void PasswordFormManager::CreatePendingCredentialsForNewCredentials() {
// User typed in a new, unknown username.
SetUserAction(UserAction::kOverrideUsernameAndPassword);
pending_credentials_ = observed_form_;
pending_credentials_.username_element = submitted_form_->username_element;
pending_credentials_.username_value = submitted_form_->username_value;
pending_credentials_.other_possible_usernames =
submitted_form_->other_possible_usernames;
pending_credentials_.all_possible_passwords =
submitted_form_->all_possible_passwords;
// 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 during update when the user
// goes onto a real login form for the first time.
if (!submitted_form_->new_password_element.empty()) {
pending_credentials_.password_element.clear();
}
}
void PasswordFormManager::OnNopeUpdateClicked() {
UploadPasswordVote(observed_form_, autofill::NOT_NEW_PASSWORD, std::string());
}
void PasswordFormManager::OnNeverClicked() {
UploadPasswordVote(pending_credentials_, autofill::UNKNOWN_TYPE,
std::string());
PermanentlyBlacklist();
}
void PasswordFormManager::OnNoInteraction(bool is_update) {
if (is_update) {
UploadPasswordVote(observed_form_, autofill::PROBABLY_NEW_PASSWORD,
std::string());
} else {
UploadPasswordVote(pending_credentials_, autofill::UNKNOWN_TYPE,
std::string());
}
}
void PasswordFormManager::SetHasGeneratedPassword(bool generated_password) {
has_generated_password_ = generated_password;
metrics_recorder_->SetHasGeneratedPassword(generated_password);
}
void PasswordFormManager::LogSubmitPassed() {
metrics_recorder_->LogSubmitPassed();
}
void PasswordFormManager::LogSubmitFailed() {
metrics_recorder_->LogSubmitFailed();
}
void PasswordFormManager::MarkGenerationAvailable() {
metrics_recorder_->MarkGenerationAvailable();
}
void PasswordFormManager::WipeStoreCopyIfOutdated() {
UMA_HISTOGRAM_BOOLEAN(
"PasswordManager.StoreReadyWhenWiping",
form_fetcher_->GetState() == FormFetcher::State::NOT_WAITING);
form_saver_->WipeOutdatedCopies(pending_credentials_, &best_matches_,
&preferred_match_);
}
void PasswordFormManager::SaveGenerationFieldDetectedByClassifier(
const base::string16& generation_field) {
form_classifier_outcome_ =
generation_field.empty() ? kNoGenerationElement : kFoundGenerationElement;
generation_element_detected_by_classifier_ = generation_field;
}
void PasswordFormManager::ResetStoredMatches() {
preferred_match_ = nullptr;
best_matches_.clear();
not_best_matches_.clear();
blacklisted_matches_.clear();
new_blacklisted_.reset();
}
void PasswordFormManager::GrabFetcher(std::unique_ptr<FormFetcher> fetcher) {
DCHECK(!owned_form_fetcher_);
owned_form_fetcher_ = std::move(fetcher);
if (owned_form_fetcher_.get() == form_fetcher_)
return;
ResetStoredMatches();
form_fetcher_->RemoveConsumer(this);
form_fetcher_ = owned_form_fetcher_.get();
form_fetcher_->AddConsumer(this);
}
std::unique_ptr<PasswordFormManager> PasswordFormManager::Clone() {
// Fetcher is cloned to avoid re-fetching data from PasswordStore.
std::unique_ptr<FormFetcher> fetcher = form_fetcher_->Clone();
// Some data is filled through the constructor. No PasswordManagerDriver is
// needed, because the UI does not need any functionality related to the
// renderer process, to which the driver serves as an interface. The full
// |observed_form_| needs to be copied, because it is used to created the
// blacklisting entry if needed.
auto result = std::make_unique<PasswordFormManager>(
password_manager_, client_, base::WeakPtr<PasswordManagerDriver>(),
observed_form_, form_saver_->Clone(), fetcher.get());
result->Init(metrics_recorder_);
// The constructor only can take a weak pointer to the fetcher, so moving the
// owning one needs to happen explicitly.
result->GrabFetcher(std::move(fetcher));
// |best_matches_| are skipped, because those are regenerated from the new
// fetcher automatically.
// These data members all satisfy:
// (1) They could have been changed by |*this| between its construction and
// calling Clone().
// (2) They are potentially used in the clone as the clone is used in the UI
// code.
// (3) They are not changed during ProcessMatches, triggered at some point
// by the cloned FormFetcher.
if (submitted_form_)
result->submitted_form_ = std::make_unique<PasswordForm>(*submitted_form_);
result->other_possible_username_action_ = other_possible_username_action_;
if (username_correction_vote_) {
result->username_correction_vote_ =
std::make_unique<PasswordForm>(*username_correction_vote_);
}
result->pending_credentials_ = pending_credentials_;
result->is_new_login_ = is_new_login_;
result->has_autofilled_ = has_autofilled_;
result->has_generated_password_ = has_generated_password_;
result->generated_password_changed_ = generated_password_changed_;
result->is_manual_generation_ = is_manual_generation_;
result->generation_element_ = generation_element_;
result->generation_popup_was_shown_ = generation_popup_was_shown_;
result->form_classifier_outcome_ = form_classifier_outcome_;
result->generation_element_detected_by_classifier_ =
generation_element_detected_by_classifier_;
result->password_overridden_ = password_overridden_;
result->retry_password_form_password_update_ =
retry_password_form_password_update_;
result->selected_username_ = selected_username_;
result->is_possible_change_password_form_without_username_ =
is_possible_change_password_form_without_username_;
result->user_action_ = user_action_;
return result;
}
metrics_util::CredentialSourceType PasswordFormManager::GetCredentialSource() {
return metrics_util::CredentialSourceType::kPasswordManager;
}
void PasswordFormManager::SendVotesOnSave() {
if (observed_form_.IsPossibleChangePasswordFormWithoutUsername())
return;
// Send votes for sign-in form.
autofill::FormData& form_data = pending_credentials_.form_data;
if (form_data.fields.size() == 2 &&
form_data.fields[0].form_control_type == "text" &&
form_data.fields[1].form_control_type == "password") {
// |form_data| is received from the renderer and does not contain field
// values. Fill username field value with username to allow AutofillManager
// to detect username autofill type.
form_data.fields[0].value = pending_credentials_.username_value;
SendSignInVote(form_data);
}
// Upload credentials the first time they are saved. This data is used
// by password generation to help determine account creation sites.
// Credentials that have been previously used (e.g., PSL matches) are checked
// to see if they are valid account creation forms.
if (pending_credentials_.times_used == 0) {
autofill::ServerFieldType password_type = autofill::PASSWORD;
if (submitted_form_->does_look_like_signup_form)
password_type = autofill::PROBABLY_ACCOUNT_CREATION_PASSWORD;
UploadPasswordVote(pending_credentials_, password_type, std::string());
if (username_correction_vote_) {
UploadPasswordVote(
*username_correction_vote_, autofill::USERNAME,
FormStructure(observed_form_.form_data).FormSignatureAsStr());
}
} else {
SendVoteOnCredentialsReuse(observed_form_, &pending_credentials_);
}
}
void PasswordFormManager::SendSignInVote(const FormData& form_data) {
autofill::AutofillManager* autofill_manager =
client_->GetAutofillManagerForMainFrame();
if (!autofill_manager)
return;
std::unique_ptr<FormStructure> form_structure(new FormStructure(form_data));
form_structure->set_is_signin_upload(true);
DCHECK(form_structure->ShouldBeUploaded());
DCHECK_EQ(2u, form_structure->field_count());
form_structure->field(1)->set_possible_types({autofill::PASSWORD});
autofill_manager->StartUploadProcess(std::move(form_structure),
base::TimeTicks::Now(), true);
}
void PasswordFormManager::SetUserAction(UserAction user_action) {
user_action_ = user_action;
metrics_recorder_->SetUserAction(user_action);
}
base::Optional<PasswordForm> PasswordFormManager::UpdatePendingAndGetOldKey(
std::vector<PasswordForm>* credentials_to_update) {
base::Optional<PasswordForm> old_primary_key;
bool update_related_credentials = false;
if (!selected_username_.empty()) {
// Username has changed. We set this selected username as the real
// username. Given that |username_value| is part of the Sync and
// PasswordStore primary key, the old primary key must be supplied.
old_primary_key = pending_credentials_;
pending_credentials_.username_value = selected_username_;
// TODO(crbug.com/188908) This branch currently never executes (bound to
// the other usernames experiment). Updating related credentials would be
// complicated, so we skip that, given it influences no users.
update_related_credentials = false;
} else if (observed_form_.new_password_element.empty() &&
pending_credentials_.federation_origin.unique() &&
!IsValidAndroidFacetURI(pending_credentials_.signon_realm) &&
(pending_credentials_.password_element.empty() ||
pending_credentials_.username_element.empty() ||
pending_credentials_.submit_element.empty())) {
// If |observed_form_| is a sign-in form and some of the element names are
// empty, it is likely the first time a credential saved on a
// sign-up/change password form is used. Given that |password_element| and
// |username_element| are part of Sync and PasswordStore primary key, the
// old primary key must be used if the new names shal be saved.
old_primary_key = pending_credentials_;
pending_credentials_.password_element = observed_form_.password_element;
pending_credentials_.username_element = observed_form_.username_element;
pending_credentials_.submit_element = observed_form_.submit_element;
update_related_credentials = true;
} else {
update_related_credentials =
pending_credentials_.federation_origin.unique();
}
// If this was a password update, then update all non-best matches entries
// with the same username and the same old password.
if (update_related_credentials) {
auto updated_password_it =
best_matches_.find(pending_credentials_.username_value);
DCHECK(best_matches_.end() != updated_password_it);
const base::string16& old_password =
updated_password_it->second->password_value;
for (auto* not_best_match : not_best_matches_) {
if (not_best_match->username_value ==
pending_credentials_.username_value &&
not_best_match->password_value == old_password) {
credentials_to_update->push_back(*not_best_match);
credentials_to_update->back().password_value =
pending_credentials_.password_value;
}
}
}
return old_primary_key;
}
} // namespace password_manager