blob: 8d1d0027df07e740324b854f72d6354f4eb5635b [file] [log] [blame]
// Copyright 2018 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 <algorithm>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/stl_util.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/password_form_generation_data.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/password_form_filling.h"
#include "components/password_manager/core/browser/password_generation_state.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_util.h"
#include "components/password_manager/core/browser/possible_username_data.h"
#include "components/password_manager/core/browser/statistics_table.h"
#include "components/password_manager/core/common/password_manager_features.h"
using autofill::FormData;
using autofill::FormFieldData;
using autofill::FormSignature;
using autofill::FormStructure;
using autofill::PasswordForm;
using autofill::ValueElementPair;
using base::TimeDelta;
using base::TimeTicks;
using Logger = autofill::SavePasswordProgressLogger;
namespace password_manager {
bool PasswordFormManager::wait_for_server_predictions_for_filling_ = true;
namespace {
constexpr TimeDelta kMaxFillingDelayForServerPredictions =
TimeDelta::FromMilliseconds(500);
// Helper to get the platform specific identifier by which autofill and password
// manager refer to a field. See http://crbug.com/896594
base::string16 GetPlatformSpecificIdentifier(const FormFieldData& field) {
#if defined(OS_IOS)
return field.unique_id;
#else
return field.name;
#endif
}
ValueElementPair PasswordToSave(const PasswordForm& form) {
if (form.new_password_value.empty()) {
DCHECK(!form.password_value.empty() || form.IsFederatedCredential());
return {form.password_value, form.password_element};
}
return {form.new_password_value, form.new_password_element};
}
// Copies field properties masks from the form |from| to the form |to|.
void CopyFieldPropertiesMasks(const FormData& from, FormData* to) {
// Skip copying if the number of fields is different.
if (from.fields.size() != to->fields.size())
return;
for (size_t i = 0; i < from.fields.size(); ++i) {
to->fields[i].properties_mask =
GetPlatformSpecificIdentifier(to->fields[i]) ==
GetPlatformSpecificIdentifier(from.fields[i])
? from.fields[i].properties_mask
: autofill::FieldPropertiesFlags::ERROR_OCCURRED;
}
}
// Filter sensitive information, duplicates and |username_value| out from
// |form->all_possible_usernames|.
void SanitizePossibleUsernames(PasswordForm* form) {
auto& usernames = form->all_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);
});
}
// Returns bit masks with differences in forms attributes which are important
// for parsing. Bits are set according to enum FormDataDifferences.
uint32_t FindFormsDifferences(const FormData& lhs, const FormData& rhs) {
if (lhs.fields.size() != rhs.fields.size())
return PasswordFormMetricsRecorder::kFieldsNumber;
size_t differences_bitmask = 0;
for (size_t i = 0; i < lhs.fields.size(); ++i) {
const FormFieldData& lhs_field = lhs.fields[i];
const FormFieldData& rhs_field = rhs.fields[i];
if (lhs_field.unique_renderer_id != rhs_field.unique_renderer_id)
differences_bitmask |= PasswordFormMetricsRecorder::kRendererFieldIDs;
if (lhs_field.form_control_type != rhs_field.form_control_type)
differences_bitmask |= PasswordFormMetricsRecorder::kFormControlTypes;
if (lhs_field.autocomplete_attribute != rhs_field.autocomplete_attribute)
differences_bitmask |=
PasswordFormMetricsRecorder::kAutocompleteAttributes;
}
return differences_bitmask;
}
// Since empty or unspecified form's action is automatically set to the page
// origin, this function checks if a form's action is empty by comparing it to
// its origin.
bool HasNonEmptyAction(const FormData& form) {
// TODO(crbug.com/1008798): The logic isn't accurate and should be fixed.
return form.action != form.url;
}
bool FormContainsFieldWithName(const FormData& form,
const base::string16& element) {
if (element.empty())
return false;
for (const auto& field : form.fields) {
if (base::EqualsCaseInsensitiveASCII(field.name, element))
return true;
}
return false;
}
bool IsUsernameFirstFlowFeatureEnabled() {
return base::FeatureList::IsEnabled(features::kUsernameFirstFlow);
}
} // namespace
PasswordFormManager::PasswordFormManager(
PasswordManagerClient* client,
const base::WeakPtr<PasswordManagerDriver>& driver,
const FormData& observed_form,
FormFetcher* form_fetcher,
std::unique_ptr<FormSaver> form_saver,
scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder)
: PasswordFormManager(client,
form_fetcher,
std::move(form_saver),
metrics_recorder,
PasswordStore::FormDigest(observed_form)) {
driver_ = driver;
observed_form_ = observed_form;
metrics_recorder_->RecordFormSignature(CalculateFormSignature(observed_form));
// Do not fetch saved credentials for Chrome sync form, since nor filling nor
// saving are supported.
if (owned_form_fetcher_ &&
!observed_form_.is_gaia_with_skip_save_password_form) {
owned_form_fetcher_->Fetch();
}
form_fetcher_->AddConsumer(this);
votes_uploader_.StoreInitialFieldValues(observed_form);
}
PasswordFormManager::PasswordFormManager(
PasswordManagerClient* client,
PasswordStore::FormDigest observed_http_auth_digest,
FormFetcher* form_fetcher,
std::unique_ptr<FormSaver> form_saver)
: PasswordFormManager(client,
form_fetcher,
std::move(form_saver),
nullptr /* metrics_recorder */,
observed_http_auth_digest) {
observed_not_web_form_digest_ = std::move(observed_http_auth_digest);
if (owned_form_fetcher_)
owned_form_fetcher_->Fetch();
form_fetcher_->AddConsumer(this);
}
PasswordFormManager::~PasswordFormManager() {
form_fetcher_->RemoveConsumer(this);
}
bool PasswordFormManager::DoesManage(
const FormData& form,
const PasswordManagerDriver* driver) const {
if (driver != driver_.get())
return false;
if (observed_form_.is_form_tag != form.is_form_tag)
return false;
// All unowned input elements are considered as one synthetic form.
if (!observed_form_.is_form_tag && !form.is_form_tag)
return true;
#if defined(OS_IOS)
// On iOS form name is used as the form identifier.
return observed_form_.name == form.name;
#else
return observed_form_.unique_renderer_id == form.unique_renderer_id;
#endif
}
bool PasswordFormManager::DoesManageAccordingToRendererId(
uint32_t form_renderer_id,
const PasswordManagerDriver* driver) const {
if (driver != driver_.get())
return false;
#if defined(OS_IOS)
NOTREACHED();
// On iOS form name is used as the form identifier.
return false;
#else
return observed_form_.unique_renderer_id == form_renderer_id;
#endif
}
bool PasswordFormManager::IsEqualToSubmittedForm(
const autofill::FormData& form) const {
if (!is_submitted_)
return false;
if (IsHttpAuth())
return false;
if (form.action.is_valid() && HasNonEmptyAction(form) &&
HasNonEmptyAction(submitted_form_) &&
submitted_form_.action == form.action) {
return true;
}
// Match the form if username and password fields are same.
if (FormContainsFieldWithName(form,
parsed_submitted_form_->username_element) &&
FormContainsFieldWithName(form,
parsed_submitted_form_->password_element)) {
return true;
}
// Match the form if the observed username field has the same value as in
// the submitted form.
if (!parsed_submitted_form_->username_value.empty()) {
for (const auto& field : form.fields) {
if (field.value == parsed_submitted_form_->username_value)
return true;
}
}
return false;
}
const GURL& PasswordFormManager::GetOrigin() const {
return observed_not_web_form_digest_ ? observed_not_web_form_digest_->origin
: observed_form_.url;
}
const std::vector<const PasswordForm*>& PasswordFormManager::GetBestMatches()
const {
return form_fetcher_->GetBestMatches();
}
std::vector<const autofill::PasswordForm*>
PasswordFormManager::GetFederatedMatches() const {
return form_fetcher_->GetFederatedMatches();
}
const PasswordForm& PasswordFormManager::GetPendingCredentials() const {
return pending_credentials_;
}
metrics_util::CredentialSourceType PasswordFormManager::GetCredentialSource()
const {
return metrics_util::CredentialSourceType::kPasswordManager;
}
PasswordFormMetricsRecorder* PasswordFormManager::GetMetricsRecorder() {
return metrics_recorder_.get();
}
base::span<const InteractionsStats> PasswordFormManager::GetInteractionsStats()
const {
return base::make_span(form_fetcher_->GetInteractionsStats());
}
bool PasswordFormManager::IsBlacklisted() const {
return form_fetcher_->IsBlacklisted() || newly_blacklisted_;
}
void PasswordFormManager::Save() {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
DCHECK(!client_->IsIncognito());
if (IsBlacklisted()) {
form_saver_->Unblacklist(ConstructObservedFormDigest());
newly_blacklisted_ = false;
}
if (password_overridden_ &&
pending_credentials_.type == PasswordForm::Type::kGenerated &&
!HasGeneratedPassword()) {
metrics_util::LogPasswordGenerationSubmissionEvent(
metrics_util::PASSWORD_OVERRIDDEN);
pending_credentials_.type = PasswordForm::Type::kManual;
}
if (is_new_login_) {
metrics_util::LogNewlySavedPasswordIsGenerated(
pending_credentials_.type == PasswordForm::Type::kGenerated);
SanitizePossibleUsernames(&pending_credentials_);
pending_credentials_.date_created = base::Time::Now();
votes_uploader_.SendVotesOnSave(observed_form_, *parsed_submitted_form_,
GetBestMatches(), &pending_credentials_);
SavePendingToStore(false /*update*/);
} else {
ProcessUpdate();
SavePendingToStore(true /*update*/);
}
if (pending_credentials_.times_used == 1 &&
pending_credentials_.type == PasswordForm::Type::kGenerated) {
// This also includes PSL matched credentials.
metrics_util::LogPasswordGenerationSubmissionEvent(
metrics_util::PASSWORD_USED);
}
client_->UpdateFormManagers();
}
void PasswordFormManager::Update(const PasswordForm& credentials_to_update) {
metrics_util::LogPasswordAcceptedSaveUpdateSubmissionIndicatorEvent(
parsed_submitted_form_->submission_event);
metrics_recorder_->SetSubmissionIndicatorEvent(
parsed_submitted_form_->submission_event);
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;
pending_credentials_.date_last_used = base::Time::Now();
is_new_login_ = false;
ProcessUpdate();
SavePendingToStore(true /*update*/);
client_->UpdateFormManagers();
}
void PasswordFormManager::UpdateUsername(const base::string16& new_username) {
DCHECK(parsed_submitted_form_);
parsed_submitted_form_->username_value = new_username;
parsed_submitted_form_->username_element.clear();
metrics_recorder_->set_username_updated_in_bubble(true);
// |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.
votes_uploader_.set_has_username_edited_vote(false);
if (!new_username.empty()) {
// |all_possible_usernames| has all possible usernames.
// TODO(crbug.com/831123): rename to |all_possible_usernames| when the old
// parser is gone.
for (const auto& possible_username :
parsed_submitted_form_->all_possible_usernames) {
if (possible_username.first == new_username) {
parsed_submitted_form_->username_element = possible_username.second;
votes_uploader_.set_has_username_edited_vote(true);
break;
}
}
}
CreatePendingCredentials();
}
void PasswordFormManager::UpdatePasswordValue(
const base::string16& new_password) {
DCHECK(parsed_submitted_form_);
parsed_submitted_form_->password_value = new_password;
parsed_submitted_form_->password_element.clear();
// The user updated a password from the prompt. It means that heuristics were
// wrong. So clear new password, since it is likely wrong.
parsed_submitted_form_->new_password_value.clear();
parsed_submitted_form_->new_password_element.clear();
for (const ValueElementPair& pair :
parsed_submitted_form_->all_possible_passwords) {
if (pair.first == new_password) {
parsed_submitted_form_->password_element = pair.second;
break;
}
}
CreatePendingCredentials();
}
void PasswordFormManager::UpdateSubmissionIndicatorEvent(
autofill::mojom::SubmissionIndicatorEvent event) {
parsed_submitted_form_->form_data.submission_event = event;
parsed_submitted_form_->submission_event = event;
}
void PasswordFormManager::OnNopeUpdateClicked() {
votes_uploader_.UploadPasswordVote(*parsed_submitted_form_,
*parsed_submitted_form_,
autofill::NOT_NEW_PASSWORD, std::string());
}
void PasswordFormManager::OnNeverClicked() {
// |UNKNOWN_TYPE| is sent in order to record that a generation popup was
// shown and ignored.
votes_uploader_.UploadPasswordVote(*parsed_submitted_form_,
*parsed_submitted_form_,
autofill::UNKNOWN_TYPE, std::string());
votes_uploader_.MaybeSendSingleUsernameVote(false /* credentials_saved */);
PermanentlyBlacklist();
}
void PasswordFormManager::OnNoInteraction(bool is_update) {
// |UNKNOWN_TYPE| is sent in order to record that a generation popup was
// shown and ignored.
votes_uploader_.UploadPasswordVote(
*parsed_submitted_form_, *parsed_submitted_form_,
is_update ? autofill::PROBABLY_NEW_PASSWORD : autofill::UNKNOWN_TYPE,
std::string());
votes_uploader_.MaybeSendSingleUsernameVote(false /* credentials_saved */);
}
void PasswordFormManager::PermanentlyBlacklist() {
DCHECK(!client_->IsIncognito());
form_saver_->PermanentlyBlacklist(ConstructObservedFormDigest());
newly_blacklisted_ = true;
}
PasswordStore::FormDigest PasswordFormManager::ConstructObservedFormDigest() {
std::string signon_realm;
GURL origin;
if (observed_not_web_form_digest_) {
origin = observed_not_web_form_digest_->origin;
// GetSignonRealm is not suitable for http auth credentials.
signon_realm = IsHttpAuth()
? observed_not_web_form_digest_->signon_realm
: GetSignonRealm(observed_not_web_form_digest_->origin);
} else {
origin = observed_form_.url;
signon_realm = GetSignonRealm(observed_form_.url);
}
return PasswordStore::FormDigest(GetScheme(), signon_realm, origin);
}
void PasswordFormManager::OnPasswordsRevealed() {
votes_uploader_.set_has_passwords_revealed_vote(true);
}
bool PasswordFormManager::IsNewLogin() const {
return is_new_login_;
}
FormFetcher* PasswordFormManager::GetFormFetcher() {
return form_fetcher_;
}
bool PasswordFormManager::IsPendingCredentialsPublicSuffixMatch() const {
return pending_credentials_.is_public_suffix_match;
}
void PasswordFormManager::PresaveGeneratedPassword(const PasswordForm& form) {
// TODO(https://crbug.com/831123): Propagate generated password independently
// of PasswordForm when PasswordForm goes away from the renderer process.
PresaveGeneratedPasswordInternal(form.form_data,
form.password_value /*generated_password*/);
}
void PasswordFormManager::PasswordNoLongerGenerated() {
if (!HasGeneratedPassword())
return;
generation_state_->PasswordNoLongerGenerated();
generation_state_.reset();
votes_uploader_.set_has_generated_password(false);
votes_uploader_.set_generated_password_changed(false);
metrics_recorder_->SetGeneratedPasswordStatus(
PasswordFormMetricsRecorder::GeneratedPasswordStatus::kPasswordDeleted);
}
bool PasswordFormManager::HasGeneratedPassword() const {
return generation_state_ && generation_state_->HasGeneratedPassword();
}
void PasswordFormManager::SetGenerationPopupWasShown(
bool is_manual_generation) {
votes_uploader_.set_generation_popup_was_shown(true);
votes_uploader_.set_is_manual_generation(is_manual_generation);
metrics_recorder_->SetPasswordGenerationPopupShown(true,
is_manual_generation);
}
void PasswordFormManager::SetGenerationElement(
const base::string16& generation_element) {
votes_uploader_.set_generation_element(generation_element);
}
bool PasswordFormManager::IsPossibleChangePasswordFormWithoutUsername() const {
return parsed_submitted_form_ &&
parsed_submitted_form_->IsPossibleChangePasswordFormWithoutUsername();
}
bool PasswordFormManager::IsPasswordUpdate() const {
return password_overridden_;
}
base::WeakPtr<PasswordManagerDriver> PasswordFormManager::GetDriver() const {
return driver_;
}
const PasswordForm* PasswordFormManager::GetSubmittedForm() const {
return parsed_submitted_form_.get();
}
#if defined(OS_IOS)
void PasswordFormManager::PresaveGeneratedPassword(
PasswordManagerDriver* driver,
const FormData& form,
const base::string16& generated_password,
const base::string16& generation_element) {
observed_form_ = form;
PresaveGeneratedPasswordInternal(form, generated_password);
votes_uploader_.set_generation_element(generation_element);
}
bool PasswordFormManager::UpdateGeneratedPasswordOnUserInput(
const base::string16& form_identifier,
const base::string16& field_identifier,
const base::string16& field_value) {
if (observed_form_.name != form_identifier || !HasGeneratedPassword()) {
// *this might not have generated password, because
// 1.This function is called before PresaveGeneratedPassword, or
// 2.There are multiple forms with the same |form_identifier|
return false;
}
bool form_data_changed = false;
for (FormFieldData& field : observed_form_.fields) {
if (field.unique_id == field_identifier) {
field.value = field_value;
form_data_changed = true;
break;
}
}
base::string16 generated_password = generation_state_->generated_password();
if (votes_uploader_.get_generation_element() == field_identifier) {
generated_password = field_value;
form_data_changed = true;
}
if (form_data_changed)
PresaveGeneratedPasswordInternal(observed_form_, generated_password);
return true;
}
#endif // defined(OS_IOS)
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 create the
// blacklisting entry if needed.
auto result = std::make_unique<PasswordFormManager>(
client_, base::WeakPtr<PasswordManagerDriver>(), observed_form_,
fetcher.get(), form_saver_->Clone(), metrics_recorder_);
// The constructor only can take a weak pointer to the fetcher, so moving the
// owning one needs to happen explicitly.
result->owned_form_fetcher_ = std::move(fetcher);
if (generation_state_) {
result->generation_state_ =
generation_state_->Clone(result->form_saver_.get());
}
// 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 OnFetchCompleted, triggered at some point
// by the
// cloned FormFetcher.
result->votes_uploader_ = votes_uploader_;
if (parser_.predictions())
result->parser_.set_predictions(*parser_.predictions());
result->pending_credentials_ = pending_credentials_;
if (parsed_submitted_form_) {
result->parsed_submitted_form_.reset(
new PasswordForm(*parsed_submitted_form_));
}
result->is_new_login_ = is_new_login_;
result->password_overridden_ = password_overridden_;
result->is_submitted_ = is_submitted_;
return result;
}
PasswordFormManager::PasswordFormManager(
PasswordManagerClient* client,
std::unique_ptr<PasswordForm> saved_form,
std::unique_ptr<FormFetcher> form_fetcher,
std::unique_ptr<FormSaver> form_saver)
: PasswordFormManager(client,
form_fetcher.get(),
std::move(form_saver),
nullptr /* metrics_recorder */,
PasswordStore::FormDigest(*saved_form)) {
observed_not_web_form_digest_ = PasswordStore::FormDigest(*saved_form);
parsed_submitted_form_ = std::move(saved_form);
is_submitted_ = true;
owned_form_fetcher_ = std::move(form_fetcher),
form_fetcher_->AddConsumer(this);
if (form_fetcher_)
form_fetcher_->Fetch();
}
void PasswordFormManager::OnFetchCompleted() {
received_stored_credentials_time_ = TimeTicks::Now();
// Copy out blacklisted matches.
newly_blacklisted_ = false;
autofills_left_ = kMaxTimesAutofill;
if (IsCredentialAPISave()) {
// This is saving with credential API, there is no form to fill, so no
// filling required.
return;
}
if (IsHttpAuth()) {
// No server prediction for http auth, so no need to wait.
FillHttpAuth();
} else if (parser_.predictions() ||
!wait_for_server_predictions_for_filling_) {
ReportTimeBetweenStoreAndServerUMA();
Fill();
} else if (!waiting_for_server_predictions_) {
waiting_for_server_predictions_ = true;
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&PasswordFormManager::Fill,
weak_ptr_factory_.GetWeakPtr()),
kMaxFillingDelayForServerPredictions);
}
}
bool PasswordFormManager::ProvisionallySave(
const FormData& submitted_form,
const PasswordManagerDriver* driver,
const PossibleUsernameData* possible_username) {
DCHECK(DoesManage(submitted_form, driver));
std::unique_ptr<PasswordForm> parsed_submitted_form =
ParseFormAndMakeLogging(submitted_form, FormDataParser::Mode::kSaving);
RecordMetricOnReadonly(parser_.readonly_status(), !!parsed_submitted_form,
FormDataParser::Mode::kSaving);
// This function might be called multiple times. Consider as success if the
// submitted form was successfully parsed on a previous call.
if (!parsed_submitted_form)
return is_submitted_;
parsed_submitted_form_ = std::move(parsed_submitted_form);
submitted_form_ = submitted_form;
is_submitted_ = true;
CalculateFillingAssistanceMetric(submitted_form);
metrics_recorder_->set_possible_username_used(false);
votes_uploader_.clear_single_username_vote_data();
if (IsUsernameFirstFlowFeatureEnabled() &&
parsed_submitted_form_->username_value.empty() && possible_username &&
IsPossibleUsernameValid(*possible_username,
parsed_submitted_form_->signon_realm,
base::Time::Now())) {
parsed_submitted_form_->username_value = possible_username->value;
metrics_recorder_->set_possible_username_used(true);
if (possible_username->form_predictions) {
votes_uploader_.set_single_username_vote_data(
possible_username->renderer_id, *possible_username->form_predictions);
}
}
CreatePendingCredentials();
return true;
}
bool PasswordFormManager::ProvisionallySaveHttpAuthForm(
const PasswordForm& submitted_form) {
if (!IsHttpAuth())
return false;
if (!(*observed_not_web_form_digest_ ==
PasswordStore::FormDigest(submitted_form)))
return false;
parsed_submitted_form_.reset(new PasswordForm(submitted_form));
is_submitted_ = true;
CreatePendingCredentials();
return true;
}
bool PasswordFormManager::IsHttpAuth() const {
return GetScheme() != PasswordForm::Scheme::kHtml;
}
bool PasswordFormManager::IsCredentialAPISave() const {
return observed_not_web_form_digest_ && !IsHttpAuth();
}
PasswordForm::Scheme PasswordFormManager::GetScheme() const {
return observed_not_web_form_digest_ ? observed_not_web_form_digest_->scheme
: PasswordForm::Scheme::kHtml;
}
void PasswordFormManager::ProcessServerPredictions(
const std::map<FormSignature, FormPredictions>& predictions) {
if (parser_.predictions()) {
// This method might be called multiple times. No need to process
// predictions again.
return;
}
FormSignature observed_form_signature =
CalculateFormSignature(observed_form_);
auto it = predictions.find(observed_form_signature);
if (it == predictions.end())
return;
ReportTimeBetweenStoreAndServerUMA();
parser_.set_predictions(it->second);
Fill();
}
void PasswordFormManager::Fill() {
if (!driver_)
return;
waiting_for_server_predictions_ = false;
if (form_fetcher_->GetState() == FormFetcher::State::WAITING)
return;
if (autofills_left_ <= 0)
return;
autofills_left_--;
// There are additional signals (server-side data) and parse results in
// filling and saving mode might be different so it is better not to cache
// parse result, but to parse each time again.
std::unique_ptr<PasswordForm> observed_password_form =
ParseFormAndMakeLogging(observed_form_, FormDataParser::Mode::kFilling);
RecordMetricOnReadonly(parser_.readonly_status(), !!observed_password_form,
FormDataParser::Mode::kFilling);
if (!observed_password_form)
return;
if (observed_password_form->is_new_password_reliable && !IsBlacklisted()) {
#if defined(OS_IOS)
driver_->FormEligibleForGenerationFound(
{/*form_name*/ observed_password_form->form_data.name,
/*new_password_element*/ observed_password_form->new_password_element,
/*confirmation_password_element*/
observed_password_form->confirmation_password_element});
#else
driver_->FormEligibleForGenerationFound(
{/*new_password_renderer_id*/
observed_password_form->new_password_element_renderer_id,
/*confirmation_password_renderer_id*/
observed_password_form->confirmation_password_element_renderer_id});
#endif
}
#if defined(OS_IOS)
// Filling on username first flow is not supported on iOS.
if (observed_password_form->IsSingleUsername())
return;
#endif
SendFillInformationToRenderer(
client_, driver_.get(), *observed_password_form.get(),
form_fetcher_->GetBestMatches(), form_fetcher_->GetFederatedMatches(),
form_fetcher_->GetPreferredMatch(), metrics_recorder_.get());
}
void PasswordFormManager::FillForm(const FormData& observed_form) {
uint32_t differences_bitmask =
FindFormsDifferences(observed_form_, observed_form);
metrics_recorder_->RecordFormChangeBitmask(differences_bitmask);
if (differences_bitmask)
observed_form_ = observed_form;
if (!waiting_for_server_predictions_)
Fill();
}
void PasswordFormManager::OnGeneratedPasswordAccepted(
FormData form_data,
uint32_t generation_element_id,
const base::string16& password) {
// Find the generating element to update its value. The parser needs a non
// empty value.
auto it = std::find_if(form_data.fields.begin(), form_data.fields.end(),
[generation_element_id](const auto& field_data) {
return generation_element_id ==
field_data.unique_renderer_id;
});
DCHECK(it != form_data.fields.end());
it->value = password;
std::unique_ptr<PasswordForm> parsed_form =
ParseFormAndMakeLogging(form_data, FormDataParser::Mode::kSaving);
if (!parsed_form) {
// Create a password form with a minimum data.
parsed_form.reset(new PasswordForm);
parsed_form->origin = form_data.url;
parsed_form->signon_realm = GetSignonRealm(form_data.url);
}
parsed_form->password_value = password;
generation_state_ =
std::make_unique<PasswordGenerationState>(form_saver_.get(), client_);
generation_state_->GeneratedPasswordAccepted(*parsed_form, *form_fetcher_,
driver_);
}
PasswordFormManager::PasswordFormManager(
PasswordManagerClient* client,
FormFetcher* form_fetcher,
std::unique_ptr<FormSaver> form_saver,
scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder,
PasswordStore::FormDigest form_digest)
: client_(client),
metrics_recorder_(metrics_recorder),
owned_form_fetcher_(form_fetcher
? nullptr
: FormFetcherImpl::CreateFormFetcherImpl(
std::move(form_digest),
client_,
true /* should_migrate_http_passwords */)),
form_fetcher_(form_fetcher ? form_fetcher : owned_form_fetcher_.get()),
form_saver_(std::move(form_saver)),
// TODO(https://crbug.com/831123): set correctly
// |is_possible_change_password_form| in |votes_uploader_| constructor
votes_uploader_(client, false /* is_possible_change_password_form */) {
if (!metrics_recorder_) {
metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
client_->IsMainFrameSecure(), client_->GetUkmSourceId());
}
}
void PasswordFormManager::RecordMetricOnReadonly(
FormDataParser::ReadonlyPasswordFields readonly_status,
bool parsing_successful,
FormDataParser::Mode mode) {
// The reported value is combined of the |readonly_status| shifted by one bit
// to the left, and the success bit put in the least significant bit. Note:
// C++ guarantees that bool->int conversions map false to 0 and true to 1.
uint64_t value = static_cast<uint64_t>(parsing_successful) +
(static_cast<uint64_t>(readonly_status) << 1);
switch (mode) {
case FormDataParser::Mode::kSaving:
metrics_recorder_->RecordReadonlyWhenSaving(value);
break;
case FormDataParser::Mode::kFilling:
metrics_recorder_->RecordReadonlyWhenFilling(value);
break;
}
}
void PasswordFormManager::ReportTimeBetweenStoreAndServerUMA() {
if (!received_stored_credentials_time_.is_null()) {
UMA_HISTOGRAM_TIMES("PasswordManager.TimeBetweenStoreAndServer",
TimeTicks::Now() - received_stored_credentials_time_);
}
}
// TODO(https://crbug.com/831123): move this function to the proper place
// corresponding to its place in the header.
void PasswordFormManager::CreatePendingCredentials() {
DCHECK(is_submitted_);
// TODO(https://crbug.com/831123): Process correctly the case when saved
// credentials are not received from the store yet.
if (!parsed_submitted_form_)
return;
// Calculate the user's action based on existing matches and the submitted
// form.
metrics_recorder_->CalculateUserAction(GetBestMatches(),
*parsed_submitted_form_);
// This function might be called multiple times so set variables that are
// changed in this function to initial states.
is_new_login_ = true;
SetPasswordOverridden(false);
ValueElementPair password_to_save(PasswordToSave(*parsed_submitted_form_));
// Look for the actually submitted credentials in the list of previously saved
// credentials that were available to autofilling.
const PasswordForm* saved_form = password_manager_util::GetMatchForUpdating(
*parsed_submitted_form_, GetBestMatches());
if (saved_form) {
// A similar credential exists in the store already.
pending_credentials_ = *saved_form;
SetPasswordOverridden(pending_credentials_.password_value !=
password_to_save.first);
// If the autofilled credentials were 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_ = pending_credentials_.is_public_suffix_match;
if (is_new_login_) {
// 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.
password_manager_util::UpdateMetadataForUsage(&pending_credentials_);
// Update |pending_credentials_| in order to be able correctly save it.
pending_credentials_.origin = parsed_submitted_form_->origin;
pending_credentials_.signon_realm = parsed_submitted_form_->signon_realm;
pending_credentials_.action = parsed_submitted_form_->action;
}
} else {
is_new_login_ = true;
// No stored credentials can be matched to the submitted form. Offer to
// save new credentials.
CreatePendingCredentialsForNewCredentials(*parsed_submitted_form_,
password_to_save.second);
// Generate username correction votes.
bool username_correction_found =
votes_uploader_.FindCorrectedUsernameElement(
form_fetcher_->GetAllRelevantMatches(),
parsed_submitted_form_->username_value,
parsed_submitted_form_->password_value);
UMA_HISTOGRAM_BOOLEAN("PasswordManager.UsernameCorrectionFound",
username_correction_found);
if (username_correction_found) {
metrics_recorder_->RecordDetailedUserAction(
password_manager::PasswordFormMetricsRecorder::DetailedUserAction::
kCorrectedUsernameInForm);
}
}
pending_credentials_.password_value =
HasGeneratedPassword() ? generation_state_->generated_password()
: password_to_save.first;
pending_credentials_.preferred = true;
pending_credentials_.date_last_used = base::Time::Now();
pending_credentials_.form_has_autofilled_value =
parsed_submitted_form_->form_has_autofilled_value;
pending_credentials_.all_possible_passwords =
parsed_submitted_form_->all_possible_passwords;
CopyFieldPropertiesMasks(submitted_form_, &pending_credentials_.form_data);
// 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 (parsed_submitted_form_->type == PasswordForm::Type::kApi) {
pending_credentials_.skip_zero_click =
parsed_submitted_form_->skip_zero_click;
pending_credentials_.display_name = parsed_submitted_form_->display_name;
pending_credentials_.federation_origin =
parsed_submitted_form_->federation_origin;
pending_credentials_.icon_url = parsed_submitted_form_->icon_url;
// It's important to override |signon_realm| for federated credentials
// because it has format "federation://" + origin_host + "/" +
// federation_host
pending_credentials_.signon_realm = parsed_submitted_form_->signon_realm;
}
if (HasGeneratedPassword())
pending_credentials_.type = PasswordForm::Type::kGenerated;
}
void PasswordFormManager::CreatePendingCredentialsForNewCredentials(
const PasswordForm& submitted_password_form,
const base::string16& password_element) {
if (IsHttpAuth() || IsCredentialAPISave()) {
pending_credentials_ = submitted_password_form;
return;
}
// TODO(https://crbug.com/831123): Replace parsing of the observed form with
// usage of already parsed submitted form.
std::unique_ptr<PasswordForm> parsed_observed_form =
ParseFormAndMakeLogging(observed_form_, FormDataParser::Mode::kFilling);
if (!parsed_observed_form)
return;
pending_credentials_ = *parsed_observed_form;
pending_credentials_.username_element =
submitted_password_form.username_element;
pending_credentials_.username_value = submitted_password_form.username_value;
pending_credentials_.all_possible_usernames =
submitted_password_form.all_possible_usernames;
pending_credentials_.all_possible_passwords =
submitted_password_form.all_possible_passwords;
// The password value will be filled in later, remove any garbage for now.
pending_credentials_.password_value.clear();
// The password element should be determined earlier in |PasswordToSave|.
pending_credentials_.password_element = password_element;
// The new password's value and element name should be empty.
pending_credentials_.new_password_value.clear();
pending_credentials_.new_password_element.clear();
}
void PasswordFormManager::ProcessUpdate() {
DCHECK_EQ(FormFetcher::State::NOT_WAITING, form_fetcher_->GetState());
DCHECK(form_fetcher_->GetPreferredMatch() ||
pending_credentials_.IsFederatedCredential());
// 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(pending_credentials_.preferred);
DCHECK(!client_->IsIncognito());
DCHECK(parsed_submitted_form_);
password_manager_util::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 (!parsed_submitted_form_->IsPossibleChangePasswordForm()) {
votes_uploader_.SendVoteOnCredentialsReuse(
observed_form_, *parsed_submitted_form_, &pending_credentials_);
}
if (IsPasswordUpdate()) {
votes_uploader_.UploadPasswordVote(
*parsed_submitted_form_, *parsed_submitted_form_,
autofill::NEW_PASSWORD,
autofill::FormStructure(pending_credentials_.form_data)
.FormSignatureAsStr());
}
if (pending_credentials_.times_used == 1) {
votes_uploader_.UploadFirstLoginVotes(
GetBestMatches(), pending_credentials_, *parsed_submitted_form_);
}
}
void PasswordFormManager::FillHttpAuth() {
DCHECK(IsHttpAuth());
if (!form_fetcher_->GetPreferredMatch())
return;
client_->AutofillHttpAuth(*form_fetcher_->GetPreferredMatch(), this);
}
std::unique_ptr<PasswordForm> PasswordFormManager::ParseFormAndMakeLogging(
const FormData& form,
FormDataParser::Mode mode) {
std::unique_ptr<PasswordForm> password_form = parser_.Parse(form, mode);
if (password_manager_util::IsLoggingActive(client_)) {
BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
logger.LogFormData(Logger::STRING_FORM_PARSING_INPUT, form);
if (password_form)
logger.LogPasswordForm(Logger::STRING_FORM_PARSING_OUTPUT,
*password_form);
}
return password_form;
}
void PasswordFormManager::PresaveGeneratedPasswordInternal(
const FormData& form,
const base::string16& generated_password) {
std::unique_ptr<PasswordForm> parsed_form =
ParseFormAndMakeLogging(form, FormDataParser::Mode::kSaving);
if (!parsed_form) {
// Create a password form with a minimum data.
parsed_form.reset(new PasswordForm());
parsed_form->origin = form.url;
parsed_form->signon_realm = GetSignonRealm(form.url);
}
if (!HasGeneratedPassword()) {
generation_state_ =
std::make_unique<PasswordGenerationState>(form_saver_.get(), client_);
votes_uploader_.set_generated_password_changed(false);
metrics_recorder_->SetGeneratedPasswordStatus(
PasswordFormMetricsRecorder::GeneratedPasswordStatus::
kPasswordAccepted);
} else {
// If the password is already generated and a new value to presave differs
// from the presaved one, then mark that the generated password was changed.
// If a user recovers the original generated password, it will be recorded
// as a password change.
if (generation_state_->generated_password() != generated_password) {
votes_uploader_.set_generated_password_changed(true);
metrics_recorder_->SetGeneratedPasswordStatus(
PasswordFormMetricsRecorder::GeneratedPasswordStatus::
kPasswordEdited);
}
}
votes_uploader_.set_has_generated_password(true);
// Set |password_value| to the generated password in order to ensure that the
// generated password is saved.
parsed_form->password_value = generated_password;
generation_state_->PresaveGeneratedPassword(
std::move(*parsed_form), form_fetcher_->GetAllRelevantMatches());
}
void PasswordFormManager::CalculateFillingAssistanceMetric(
const FormData& submitted_form) {
// TODO(https://crbug.com/918846): implement collecting all necessary data on
// iOS.
#if not defined(OS_IOS)
std::set<base::string16> saved_usernames;
std::set<base::string16> saved_passwords;
for (auto* saved_form : form_fetcher_->GetNonFederatedMatches()) {
saved_usernames.insert(saved_form->username_value);
saved_passwords.insert(saved_form->password_value);
}
// Saved credentials might have empty usernames which are not interesting for
// filling assistance metric.
saved_usernames.erase(base::string16());
metrics_recorder_->CalculateFillingAssistanceMetric(
submitted_form, saved_usernames, saved_passwords, IsBlacklisted(),
form_fetcher_->GetInteractionsStats());
#endif
}
void PasswordFormManager::SavePendingToStore(bool update) {
const PasswordForm* saved_form = password_manager_util::GetMatchForUpdating(
*parsed_submitted_form_, GetBestMatches());
if ((update || password_overridden_) &&
!pending_credentials_.IsFederatedCredential()) {
DCHECK(saved_form);
}
base::string16 old_password =
saved_form ? saved_form->password_value : base::string16();
if (HasGeneratedPassword()) {
generation_state_->CommitGeneratedPassword(
pending_credentials_, form_fetcher_->GetAllRelevantMatches(),
old_password);
} else if (update) {
form_saver_->Update(pending_credentials_,
form_fetcher_->GetAllRelevantMatches(), old_password);
} else {
form_saver_->Save(pending_credentials_,
form_fetcher_->GetAllRelevantMatches(), old_password);
}
}
} // namespace password_manager