blob: f7aa9829c50a947fd83526155f39b552aded484c [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_manager.h"
#include <stddef.h>
#include <map>
#include <memory>
#include <utility>
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/password_form_field_prediction_map.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/form_saver_impl.h"
#include "components/password_manager/core/browser/keychain_migration_status_mac.h"
#include "components/password_manager/core/browser/log_manager.h"
#include "components/password_manager/core/browser/new_password_form_manager.h"
#include "components/password_manager/core/browser/password_autofill_manager.h"
#include "components/password_manager/core/browser/password_form_manager.h"
#include "components/password_manager/core/browser/password_generation_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/common/password_manager_features.h"
#include "components/password_manager/core/common/password_manager_pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#if defined(OS_WIN)
#include "components/prefs/pref_registry_simple.h"
#endif
using autofill::FormData;
using autofill::PasswordForm;
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
using password_manager::metrics_util::SyncPasswordHashChange;
#endif // SYNC_PASSWORD_REUSE_DETECTION_ENABLED
namespace password_manager {
namespace {
const char kSpdyProxyRealm[] = "/SpdyProxy";
// Shorten the name to spare line breaks. The code provides enough context
// already.
typedef autofill::SavePasswordProgressLogger Logger;
bool URLsEqualUpToScheme(const GURL& a, const GURL& b) {
return (a.GetContent() == b.GetContent());
}
bool URLsEqualUpToHttpHttpsSubstitution(const GURL& a, const GURL& b) {
if (a == b)
return true;
// The first-time and retry login forms action URLs sometimes differ in
// switching from HTTP to HTTPS, see http://crbug.com/400769.
if (a.SchemeIsHTTPOrHTTPS() && b.SchemeIsHTTPOrHTTPS())
return URLsEqualUpToScheme(a, b);
return false;
}
// 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 PasswordForm& form) {
return form.action != form.origin;
}
// Checks if the observed form looks like the submitted one to handle "Invalid
// password entered" case so we don't offer a password save when we shouldn't.
bool IsPasswordFormReappeared(const PasswordForm& observed_form,
const PasswordForm& submitted_form) {
if (observed_form.action.is_valid() && HasNonEmptyAction(observed_form) &&
HasNonEmptyAction(submitted_form) &&
URLsEqualUpToHttpHttpsSubstitution(submitted_form.action,
observed_form.action)) {
return true;
}
// Match the form if username and password fields are same.
if (base::EqualsCaseInsensitiveASCII(observed_form.username_element,
submitted_form.username_element) &&
base::EqualsCaseInsensitiveASCII(observed_form.password_element,
submitted_form.password_element)) {
return true;
}
// Match the form if the observed username field has the same value as in
// the submitted form.
if (!submitted_form.username_value.empty() &&
observed_form.username_value == submitted_form.username_value) {
return true;
}
return false;
}
// Helper UMA reporting function for differences in URLs during form submission.
void RecordWhetherTargetDomainDiffers(const GURL& src, const GURL& target) {
bool target_domain_differs =
!net::registry_controlled_domains::SameDomainOrHost(
src, target,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
UMA_HISTOGRAM_BOOLEAN("PasswordManager.SubmitNavigatesToDifferentDomain",
target_domain_differs);
}
bool IsSignupForm(const PasswordForm& form) {
return !form.new_password_element.empty() && form.password_element.empty();
}
// Tries to find if at least one of the values from |server_field_predictions|
// can be converted from AutofillQueryResponseContents::Field::FieldPrediction
// to a PasswordFormFieldPredictionType stored in |type|. Returns true if the
// conversion was made.
bool ServerPredictionsToPasswordFormPrediction(
std::vector<autofill::AutofillQueryResponseContents::Field::FieldPrediction>
server_field_predictions,
autofill::PasswordFormFieldPredictionType* type) {
for (auto const& server_field_prediction : server_field_predictions) {
switch (server_field_prediction.type()) {
case autofill::USERNAME:
case autofill::USERNAME_AND_EMAIL_ADDRESS:
*type = autofill::PREDICTION_USERNAME;
return true;
case autofill::PASSWORD:
*type = autofill::PREDICTION_CURRENT_PASSWORD;
return true;
case autofill::ACCOUNT_CREATION_PASSWORD:
*type = autofill::PREDICTION_NEW_PASSWORD;
return true;
default:
break;
}
}
return false;
}
// Returns true if the |field_type| is known to be possibly
// misinterpreted as a password by the Password Manager.
bool IsPredictedTypeNotPasswordPrediction(
autofill::ServerFieldType field_type) {
return field_type == autofill::CREDIT_CARD_NUMBER ||
field_type == autofill::CREDIT_CARD_VERIFICATION_CODE;
}
bool AreAllFieldsEmpty(const PasswordForm& form) {
return form.username_value.empty() && form.password_value.empty() &&
form.new_password_value.empty();
}
// Finds the matched form manager for |form| in |pending_login_managers|.
PasswordFormManager* FindMatchedManager(
const PasswordForm& form,
const std::vector<std::unique_ptr<PasswordFormManager>>&
pending_login_managers,
const password_manager::PasswordManagerDriver* driver,
BrowserSavePasswordProgressLogger* logger) {
auto matched_manager_it = pending_login_managers.end();
PasswordFormManager::MatchResultMask current_match_result =
PasswordFormManager::RESULT_NO_MATCH;
// Below, "matching" is in DoesManage-sense and "not ready" in the sense of
// FormFetcher being ready. We keep track of such PasswordFormManager
// instances for UMA.
for (auto iter = pending_login_managers.begin();
iter != pending_login_managers.end(); ++iter) {
PasswordFormManager::MatchResultMask result =
(*iter)->DoesManage(form, driver);
if (result == PasswordFormManager::RESULT_COMPLETE_MATCH) {
// If we find a manager that exactly matches the submitted form including
// the action URL, exit the loop.
if (logger)
logger->LogMessage(Logger::STRING_EXACT_MATCH);
matched_manager_it = iter;
break;
}
if (result > current_match_result) {
current_match_result = result;
matched_manager_it = iter;
if (logger) {
if (result == (PasswordFormManager::RESULT_COMPLETE_MATCH &
~PasswordFormManager::RESULT_ACTION_MATCH))
logger->LogMessage(Logger::STRING_MATCH_WITHOUT_ACTION);
if (IsSignupForm(form))
logger->LogMessage(Logger::STRING_ORIGINS_MATCH);
}
}
}
return matched_manager_it == pending_login_managers.end()
? nullptr
: matched_manager_it->get();
}
std::unique_ptr<PasswordFormManager> FindAndCloneMatchedPasswordFormManager(
const PasswordForm& password_form,
const std::vector<std::unique_ptr<PasswordFormManager>>&
pending_login_managers,
const password_manager::PasswordManagerDriver* driver) {
PasswordFormManager* matched_manager = FindMatchedManager(
password_form, pending_login_managers, driver, nullptr);
if (!matched_manager)
return nullptr;
// TODO(crbug.com/741537): Process manual saving request even if there is
// still no response from the store.
if (matched_manager->GetFormFetcher()->GetState() ==
FormFetcher::State::WAITING) {
return nullptr;
}
std::unique_ptr<PasswordFormManager> manager = matched_manager->Clone();
PasswordForm form(password_form);
form.preferred = true;
manager->ProvisionallySave(form);
return manager;
}
// Returns true if the user needs to be prompted before a password can be
// saved (instead of automatically saving the password), based on inspecting
// the state of |manager|.
bool ShouldPromptUserToSavePassword(
const PasswordFormManagerInterface& manager) {
if (manager.IsPasswordUpdate()) {
// Updating a credential might erase a useful stored value by accident.
// Always ask the user to confirm.
return true;
}
// User successfully signed-in with PSL match credentials. These credentials
// should be automatically saved in order to be autofilled on next login.
if (manager.IsPendingCredentialsPublicSuffixMatch())
return false;
if (manager.HasGeneratedPassword())
return false;
return manager.IsNewLogin();
}
// Checks that |form| has visible password fields. It should be used only for
// GAIA forms.
bool IsThereVisiblePasswordField(const FormData& form) {
for (const autofill::FormFieldData& field : form.fields) {
if (field.form_control_type == "password" && field.is_focusable)
return true;
}
return false;
}
// Finds the matched form manager for |form| in |form_managers|.
NewPasswordFormManager* FindMatchedManager(
const FormData& form,
const std::vector<std::unique_ptr<NewPasswordFormManager>>& form_managers,
const PasswordManagerDriver* driver) {
for (const auto& form_manager : form_managers) {
if (form_manager->DoesManage(form, driver))
return form_manager.get();
}
return nullptr;
}
// Finds the matched form manager with id |form_renderer_id| in |form_managers|.
NewPasswordFormManager* FindMatchedManagerByRendererId(
uint32_t form_renderer_id,
const std::vector<std::unique_ptr<NewPasswordFormManager>>& form_managers,
const PasswordManagerDriver* driver) {
for (const auto& form_manager : form_managers) {
if (form_manager->DoesManageAccordingToRendererId(form_renderer_id, driver))
return form_manager.get();
}
return nullptr;
}
// Records the difference between how |old_manager| and |new_manager| understood
// the pending credentials.
void RecordParsingOnSavingDifference(
const PasswordFormManagerInterface& old_manager,
const PasswordFormManagerInterface& new_manager,
PasswordFormMetricsRecorder* metrics_recorder) {
const PasswordForm& old_form = old_manager.GetPendingCredentials();
const PasswordForm& new_form = new_manager.GetPendingCredentials();
uint64_t result = 0;
if (old_form.username_element != new_form.username_element ||
old_form.username_value != new_form.username_value ||
old_form.password_element != new_form.password_element ||
old_form.password_value != new_form.password_value) {
result |= static_cast<int>(
PasswordFormMetricsRecorder::ParsingOnSavingDifference::kFields);
}
if (old_form.signon_realm != new_form.signon_realm) {
result |= static_cast<int>(
PasswordFormMetricsRecorder::ParsingOnSavingDifference::kSignonRealm);
}
if (old_manager.IsNewLogin() != new_manager.IsNewLogin()) {
result |= static_cast<int>(PasswordFormMetricsRecorder::
ParsingOnSavingDifference::kNewLoginStatus);
}
if (old_manager.HasGeneratedPassword() !=
new_manager.HasGeneratedPassword()) {
result |= static_cast<int>(
PasswordFormMetricsRecorder::ParsingOnSavingDifference::kGenerated);
}
metrics_recorder->RecordParsingOnSavingDifference(result);
}
} // namespace
// static
void PasswordManager::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(
prefs::kCredentialsEnableService, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
registry->RegisterBooleanPref(
prefs::kCredentialsEnableAutosignin, true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
registry->RegisterStringPref(prefs::kSyncPasswordHash, std::string(),
PrefRegistry::NO_REGISTRATION_FLAGS);
registry->RegisterStringPref(prefs::kSyncPasswordLengthAndHashSalt,
std::string(),
PrefRegistry::NO_REGISTRATION_FLAGS);
registry->RegisterBooleanPref(
prefs::kWasAutoSignInFirstRunExperienceShown, false,
user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
registry->RegisterDoublePref(prefs::kLastTimeObsoleteHttpCredentialsRemoved,
0.0);
#if defined(OS_MACOSX)
registry->RegisterIntegerPref(
prefs::kKeychainMigrationStatus,
static_cast<int>(MigrationStatus::MIGRATED_DELETED));
#endif
registry->RegisterListPref(prefs::kPasswordHashDataList,
PrefRegistry::NO_REGISTRATION_FLAGS);
}
// static
void PasswordManager::RegisterLocalPrefs(PrefRegistrySimple* registry) {
#if defined(OS_WIN)
registry->RegisterInt64Pref(prefs::kOsPasswordLastChanged, 0);
registry->RegisterBooleanPref(prefs::kOsPasswordBlank, false);
#endif
#if defined(OS_MACOSX) && !defined(OS_IOS)
registry->RegisterTimePref(prefs::kPasswordRecovery, base::Time());
#endif
}
PasswordManager::PasswordManager(PasswordManagerClient* client)
: client_(client),
is_new_form_parsing_for_saving_enabled_(
base::FeatureList::IsEnabled(
features::kNewPasswordFormParsingForSaving) &&
base::FeatureList::IsEnabled(features::kNewPasswordFormParsing)),
is_only_new_parser_enabled_(
base::FeatureList::IsEnabled(
features::kNewPasswordFormParsingForSaving) &&
base::FeatureList::IsEnabled(features::kNewPasswordFormParsing) &&
base::FeatureList::IsEnabled(features::kOnlyNewParser)) {
DCHECK(client_);
}
PasswordManager::~PasswordManager() {
for (LoginModelObserver& observer : observers_)
observer.OnLoginModelDestroying();
}
void PasswordManager::GenerationAvailableForForm(const PasswordForm& form) {
DCHECK(client_->IsSavingAndFillingEnabled(form.origin));
PasswordFormManager* form_manager = GetMatchingPendingManager(form);
if (form_manager) {
form_manager->MarkGenerationAvailable();
return;
}
}
void PasswordManager::OnPresaveGeneratedPassword(PasswordManagerDriver* driver,
const PasswordForm& form) {
DCHECK(client_->IsSavingAndFillingEnabled(form.origin));
PasswordFormManagerInterface* form_manager = GetMatchedManager(driver, form);
if (form_manager) {
form_manager->PresaveGeneratedPassword(form);
UMA_HISTOGRAM_BOOLEAN("PasswordManager.GeneratedFormHasNoFormManager",
false);
return;
}
UMA_HISTOGRAM_BOOLEAN("PasswordManager.GeneratedFormHasNoFormManager", true);
}
void PasswordManager::OnPasswordNoLongerGenerated(PasswordManagerDriver* driver,
const PasswordForm& form) {
DCHECK(client_->IsSavingAndFillingEnabled(form.origin));
PasswordFormManagerInterface* form_manager = GetMatchedManager(driver, form);
if (form_manager)
form_manager->PasswordNoLongerGenerated();
}
void PasswordManager::SetGenerationElementAndReasonForForm(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& form,
const base::string16& generation_element,
bool is_manually_triggered) {
DCHECK(client_->IsSavingAndFillingEnabled(form.origin));
PasswordFormManagerInterface* form_manager = GetMatchedManager(driver, form);
if (form_manager) {
form_manager->SetGenerationElement(generation_element);
form_manager->SetGenerationPopupWasShown(true, is_manually_triggered);
return;
}
// If there is no corresponding PasswordFormManager, we create one. This is
// not the common case, and should only happen when there is a bug in our
// ability to detect forms. No matched |NewPasswordFormManager| is unlikely.
if (!is_new_form_parsing_for_saving_enabled_) {
auto manager = std::make_unique<PasswordFormManager>(
this, client_, driver->AsWeakPtr(), form,
std::make_unique<FormSaverImpl>(client_->GetPasswordStore()), nullptr);
manager->Init(nullptr);
pending_login_managers_.push_back(std::move(manager));
}
}
void PasswordManager::ProvisionallySavePassword(
const PasswordForm& form,
const password_manager::PasswordManagerDriver* driver) {
// If the form was declined by some heuristics, don't show automatic bubble
// for it, only fallback saving should be available.
if (form.only_for_fallback_saving)
return;
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_PROVISIONALLY_SAVE_PASSWORD_METHOD);
logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVE_PASSWORD_FORM,
form);
}
if (!client_->IsSavingAndFillingEnabled(form.origin)) {
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::SAVING_DISABLED, form.origin,
logger.get());
return;
}
// No password value to save? Then don't.
if (PasswordFormManager::PasswordToSave(form).first.empty()) {
RecordProvisionalSaveFailure(PasswordManagerMetricsRecorder::EMPTY_PASSWORD,
form.origin, logger.get());
return;
}
bool should_block = ShouldBlockPasswordForSameOriginButDifferentScheme(form);
metrics_util::LogShouldBlockPasswordForSameOriginButDifferentScheme(
should_block);
if (should_block) {
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::SAVING_ON_HTTP_AFTER_HTTPS, form.origin,
logger.get());
return;
}
PasswordFormManager* matched_manager =
FindMatchedManager(form, pending_login_managers_, driver, logger.get());
// If we didn't find a manager, this means a form was submitted without
// first loading the page containing the form. Don't offer to save
// passwords in this case.
auto availability =
matched_manager
? PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess
: PasswordManagerMetricsRecorder::FormManagerAvailable::
kMissingProvisionallySave;
if (client_ && client_->GetMetricsRecorder())
client_->GetMetricsRecorder()->RecordFormManagerAvailable(availability);
if (!matched_manager) {
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::NO_MATCHING_FORM, form.origin,
logger.get());
return;
}
matched_manager->SaveSubmittedFormTypeForMetrics(form);
ProvisionallySaveManager(form, matched_manager, logger.get());
// Cache the user-visible URL (i.e., the one seen in the omnibox). Once the
// post-submit navigation concludes, we compare the landing URL against the
// cached and report the difference through UMA.
main_frame_url_ = client_->GetMainFrameURL();
// Report SubmittedFormFrame metric.
if (driver) {
metrics_util::SubmittedFormFrame frame;
if (driver->IsMainFrame()) {
frame = metrics_util::SubmittedFormFrame::MAIN_FRAME;
} else if (form.origin == main_frame_url_) {
frame =
metrics_util::SubmittedFormFrame::IFRAME_WITH_SAME_URL_AS_MAIN_FRAME;
} else {
GURL::Replacements rep;
rep.SetPathStr("");
std::string main_frame_signon_realm =
main_frame_url_.ReplaceComponents(rep).spec();
frame =
(main_frame_signon_realm == form.signon_realm)
? metrics_util::SubmittedFormFrame::
IFRAME_WITH_DIFFERENT_URL_SAME_SIGNON_REALM_AS_MAIN_FRAME
: metrics_util::SubmittedFormFrame::
IFRAME_WITH_DIFFERENT_SIGNON_REALM;
}
metrics_util::LogSubmittedFormFrame(frame);
}
}
void PasswordManager::DidNavigateMainFrame(bool form_may_be_submitted) {
pending_login_managers_.clear();
if (form_may_be_submitted) {
for (std::unique_ptr<NewPasswordFormManager>& manager : form_managers_) {
if (manager->is_submitted()) {
owned_submitted_form_manager_ = std::move(manager);
break;
}
}
}
form_managers_.clear();
predictions_.clear();
}
void PasswordManager::UpdateFormManagers() {
std::vector<PasswordFormManagerInterface*> form_managers;
for (const auto& form_manager : form_managers_)
form_managers.push_back(form_manager.get());
for (const auto& form_manager : pending_login_managers_)
form_managers.push_back(form_manager.get());
// Get the fetchers and all the drivers.
std::vector<FormFetcher*> fetchers;
std::vector<PasswordManagerDriver*> drivers;
for (PasswordFormManagerInterface* form_manager : form_managers) {
fetchers.push_back(form_manager->GetFormFetcher());
for (const auto& driver : form_manager->GetDrivers()) {
if (driver)
drivers.push_back(driver.get());
}
}
// Remove the duplicates.
std::sort(fetchers.begin(), fetchers.end());
fetchers.erase(std::unique(fetchers.begin(), fetchers.end()), fetchers.end());
std::sort(drivers.begin(), drivers.end());
drivers.erase(std::unique(drivers.begin(), drivers.end()), drivers.end());
// Refetch credentials for all the forms and update the drivers.
for (FormFetcher* fetcher : fetchers)
fetcher->Fetch();
// The autofill manager will be repopulated again when the credentials
// are retrieved.
for (PasswordManagerDriver* driver : drivers) {
// GetPasswordAutofillManager() is returning nullptr in iOS Chrome, since
// PasswordAutofillManager is not instantiated on iOS Chrome.
// See //ios/chrome/browser/passwords/ios_chrome_password_manager_driver.mm
if (driver->GetPasswordAutofillManager()) {
driver->GetPasswordAutofillManager()->DeleteFillData();
}
}
}
void PasswordManager::DropFormManagers() {
pending_login_managers_.clear();
form_managers_.clear();
owned_submitted_form_manager_.reset();
provisional_save_manager_.reset();
all_visible_forms_.clear();
predictions_.clear();
}
bool PasswordManager::IsPasswordFieldDetectedOnPage() {
return !pending_login_managers_.empty();
}
void PasswordManager::AddObserverAndDeliverCredentials(
LoginModelObserver* observer,
const PasswordForm& observed_form) {
observers_.AddObserver(observer);
// The observers are responsible for filtering notifications by the observer
// signon_realm. Each notification is broadcasted to every observer.
observer->set_signon_realm(observed_form.signon_realm);
std::vector<PasswordForm> observed_forms;
observed_forms.push_back(observed_form);
OnPasswordFormsParsed(nullptr, observed_forms);
}
void PasswordManager::RemoveObserver(LoginModelObserver* observer) {
observers_.RemoveObserver(observer);
}
void PasswordManager::OnPasswordFormSubmitted(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& password_form) {
if (is_new_form_parsing_for_saving_enabled_)
ProvisionallySaveForm(password_form.form_data, driver);
ProvisionallySavePassword(password_form, driver);
}
void PasswordManager::OnPasswordFormSubmittedNoChecks(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& password_form) {
if (password_manager_util::IsLoggingActive(client_)) {
BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
logger.LogMessage(Logger::STRING_ON_SAME_DOCUMENT_NAVIGATION);
}
if (gaia::IsGaiaSignonRealm(GURL(password_form.signon_realm)) &&
!IsThereVisiblePasswordField(password_form.form_data)) {
// Gaia form without visible password fields is found.
// It might happen only when Password Manager autofilled a username
// (visible) and a password (invisible) fields. Then the user typed a new
// username. A page removed the form. As result a form is inconsistent - the
// username from one account, the password from another. Skip such form.
return;
}
if (is_new_form_parsing_for_saving_enabled_)
ProvisionallySaveForm(password_form.form_data, driver);
ProvisionallySavePassword(password_form, driver);
if (IsAutomaticSavePromptAvailable())
OnLoginSuccessful();
}
void PasswordManager::ShowManualFallbackForSaving(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& password_form) {
if (!client_->GetPasswordStore()->IsAbleToSavePasswords() ||
!client_->IsSavingAndFillingEnabled(password_form.origin) ||
ShouldBlockPasswordForSameOriginButDifferentScheme(password_form) ||
!client_->GetStoreResultFilter()->ShouldSave(password_form))
return;
std::unique_ptr<PasswordFormManagerInterface> manager = nullptr;
if (is_new_form_parsing_for_saving_enabled_) {
NewPasswordFormManager* matched_manager =
ProvisionallySaveForm(password_form.form_data, driver);
manager = matched_manager ? matched_manager->Clone() : nullptr;
} else {
manager = FindAndCloneMatchedPasswordFormManager(
password_form, pending_login_managers_, driver);
}
auto availability =
manager ? PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess
: PasswordManagerMetricsRecorder::FormManagerAvailable::
kMissingManual;
if (client_ && client_->GetMetricsRecorder())
client_->GetMetricsRecorder()->RecordFormManagerAvailable(availability);
if (!manager)
return;
// Show the fallback if a prompt or a confirmation bubble should be available.
bool has_generated_password = manager->HasGeneratedPassword();
if (ShouldPromptUserToSavePassword(*manager) || has_generated_password) {
bool is_update = manager->IsPasswordUpdate();
manager->GetMetricsRecorder()->RecordShowManualFallbackForSaving(
has_generated_password, is_update);
client_->ShowManualFallbackForSaving(std::move(manager),
has_generated_password, is_update);
} else {
HideManualFallbackForSaving();
}
}
void PasswordManager::HideManualFallbackForSaving() {
client_->HideManualFallbackForSaving();
}
void PasswordManager::OnPasswordFormsParsed(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& forms) {
CreatePendingLoginManagers(driver, forms);
PasswordGenerationManager* password_generation_manager =
driver ? driver->GetPasswordGenerationManager() : nullptr;
if (password_generation_manager) {
password_generation_manager->PrefetchSpec(
client_->GetLastCommittedEntryURL().GetOrigin());
}
}
void PasswordManager::CreatePendingLoginManagers(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& forms) {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_CREATE_LOGIN_MANAGERS_METHOD);
}
if (base::FeatureList::IsEnabled(features::kNewPasswordFormParsing)) {
CreateFormManagers(driver, forms);
}
// Record whether or not this top-level URL has at least one password field.
client_->AnnotateNavigationEntry(!forms.empty());
// Only report SSL error status for cases where there are potentially forms to
// fill or save from.
if (!forms.empty()) {
metrics_util::CertificateError cert_error =
metrics_util::CertificateError::NONE;
const net::CertStatus cert_status = client_->GetMainFrameCertStatus();
// The order of the if statements matters -- if the status involves multiple
// errors, Chrome should report the one highest up in the list below.
if (cert_status & net::CERT_STATUS_AUTHORITY_INVALID)
cert_error = metrics_util::CertificateError::AUTHORITY_INVALID;
else if (cert_status & net::CERT_STATUS_COMMON_NAME_INVALID)
cert_error = metrics_util::CertificateError::COMMON_NAME_INVALID;
else if (cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM)
cert_error = metrics_util::CertificateError::WEAK_SIGNATURE_ALGORITHM;
else if (cert_status & net::CERT_STATUS_DATE_INVALID)
cert_error = metrics_util::CertificateError::DATE_INVALID;
else if (net::IsCertStatusError(cert_status))
cert_error = metrics_util::CertificateError::OTHER;
UMA_HISTOGRAM_ENUMERATION(
"PasswordManager.CertificateErrorsWhileSeeingForms", cert_error,
metrics_util::CertificateError::COUNT);
}
if (logger) {
logger->LogNumber(Logger::STRING_OLD_NUMBER_LOGIN_MANAGERS,
pending_login_managers_.size());
}
bool html_scheme =
!forms.empty() && forms[0].scheme == PasswordForm::SCHEME_HTML;
// Create PasswordFormManager for non-html scheme even if
// |is_only_new_parser_enabled_|.
// TODO(https://crbug.com/915161) Implement support of non-html schemes in
// NewPasswordFormManager.
if (html_scheme && is_only_new_parser_enabled_)
return;
for (const PasswordForm& form : forms) {
// Don't involve the password manager if this form corresponds to
// SpdyProxy authentication, as indicated by the realm.
if (base::EndsWith(form.signon_realm, kSpdyProxyRealm,
base::CompareCase::SENSITIVE))
continue;
if (!client_->IsFillingEnabled(form.origin))
continue;
bool old_manager_found = false;
for (const auto& old_manager : pending_login_managers_) {
if (old_manager->DoesManage(form, driver) !=
PasswordFormManager::RESULT_COMPLETE_MATCH) {
continue;
}
old_manager_found = true;
if (driver)
old_manager->ProcessFrame(driver->AsWeakPtr());
break;
}
if (old_manager_found)
continue; // The current form is already managed.
UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.ParsedUsernameField",
form.username_element.empty());
// Out of the forms not containing a username field, determine how many
// are password change forms.
if (form.username_element.empty()) {
UMA_HISTOGRAM_BOOLEAN(
"PasswordManager.EmptyUsernames."
"FormWithoutUsernameFieldIsPasswordChangeForm",
form.new_password_element.empty());
}
if (logger)
logger->LogFormSignatures(Logger::STRING_ADDING_SIGNATURE, form);
auto manager = std::make_unique<PasswordFormManager>(
this, client_,
(driver ? driver->AsWeakPtr() : base::WeakPtr<PasswordManagerDriver>()),
form, std::make_unique<FormSaverImpl>(client_->GetPasswordStore()),
nullptr);
manager->Init(
GetMetricRecorderFromNewPasswordFormManager(form.form_data, driver));
pending_login_managers_.push_back(std::move(manager));
}
if (logger) {
logger->LogNumber(Logger::STRING_NEW_NUMBER_LOGIN_MANAGERS,
pending_login_managers_.size());
}
}
void PasswordManager::CreateFormManagers(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& forms) {
// Find new forms.
std::vector<const PasswordForm*> new_forms;
for (const PasswordForm& form : forms) {
// TODO(https://crbug.com/831123): Implement inside NewPasswordFormManger
// not-filling Gaia forms that should be ignored instead of non-creating
// NewPasswordFormManger instance.
if (form.is_gaia_with_skip_save_password_form)
continue;
if (!client_->IsFillingEnabled(form.origin))
continue;
NewPasswordFormManager* manager =
FindMatchedManager(form.form_data, form_managers_, driver);
if (manager) {
// This extra filling is just duplicating redundancy that was in
// PasswordFormManager, that helps to fix cases when the site overrides
// filled values.
// TODO(https://crbug.com/831123): Implement more robust filling and
// remove the next line.
manager->FillForm(form.form_data);
} else {
new_forms.push_back(&form);
}
}
// Create form manager for new forms.
for (const PasswordForm* new_form : new_forms) {
auto* manager = CreateFormManager(driver, new_form->form_data);
manager->set_old_parsing_result(*new_form);
}
}
NewPasswordFormManager* PasswordManager::CreateFormManager(
PasswordManagerDriver* driver,
const autofill::FormData& form) {
form_managers_.push_back(std::make_unique<NewPasswordFormManager>(
client_,
driver ? driver->AsWeakPtr() : base::WeakPtr<PasswordManagerDriver>(),
form, nullptr,
std::make_unique<FormSaverImpl>(client_->GetPasswordStore()), nullptr));
form_managers_.back()->ProcessServerPredictions(predictions_);
return form_managers_.back().get();
}
NewPasswordFormManager* PasswordManager::ProvisionallySaveForm(
const FormData& submitted_form,
PasswordManagerDriver* driver) {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
}
if (!client_->IsSavingAndFillingEnabled(submitted_form.origin)) {
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::SAVING_DISABLED, submitted_form.origin,
logger.get());
return nullptr;
}
// No need to report PasswordManagerMetricsRecorder::EMPTY_PASSWORD, because
// PasswordToSave in NewPasswordFormManager DCHECKs that the password is never
// empty.
// TODO(https://crbug.com/831123): Add the
// ShouldBlockPasswordForSameOriginButDifferentScheme check.
NewPasswordFormManager* matching_form_manager = nullptr;
for (const auto& manager : form_managers_) {
if (manager->ProvisionallySaveIfIsManaged(submitted_form, driver)) {
matching_form_manager = manager.get();
break;
}
}
auto availability =
matching_form_manager
? PasswordManagerMetricsRecorder::FormManagerAvailable::kSuccess
: PasswordManagerMetricsRecorder::FormManagerAvailable::
kMissingProvisionallySave;
if (client_ && client_->GetMetricsRecorder())
client_->GetMetricsRecorder()->RecordFormManagerAvailable(availability);
if (!matching_form_manager) {
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::NO_MATCHING_FORM, submitted_form.origin,
logger.get());
matching_form_manager = CreateFormManager(driver, submitted_form);
matching_form_manager->ProvisionallySaveIfIsManaged(submitted_form, driver);
}
// Set all other form managers to no submission state.
for (const auto& manager : form_managers_) {
if (manager.get() != matching_form_manager)
manager->set_not_submitted();
}
return matching_form_manager;
}
void PasswordManager::ReportSpecPriorityForGeneratedPassword(
const PasswordForm& password_form,
uint32_t spec_priority) {
PasswordFormManager* form_manager = GetMatchingPendingManager(password_form);
if (form_manager && form_manager->GetMetricsRecorder()) {
form_manager->GetMetricsRecorder()->ReportSpecPriorityForGeneratedPassword(
spec_priority);
}
}
void PasswordManager::LogFirstFillingResult(PasswordManagerDriver* driver,
uint32_t form_renderer_id,
int32_t result) {
NewPasswordFormManager* matching_manager =
FindMatchedManagerByRendererId(form_renderer_id, form_managers_, driver);
if (!matching_manager)
return;
matching_manager->GetMetricsRecorder()->RecordFirstFillingResult(result);
}
void PasswordManager::ProvisionallySaveManager(
const PasswordForm& form,
PasswordFormManager* matched_manager,
BrowserSavePasswordProgressLogger* logger) {
DCHECK(matched_manager);
std::unique_ptr<PasswordFormManager> manager = matched_manager->Clone();
PasswordForm submitted_form(form);
submitted_form.preferred = true;
if (logger) {
logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVED_FORM,
submitted_form);
}
manager->ProvisionallySave(submitted_form);
provisional_save_manager_.swap(manager);
}
bool PasswordManager::IsAutomaticSavePromptAvailable() {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_CAN_PROVISIONAL_MANAGER_SAVE_METHOD);
}
PasswordFormManagerInterface* submitted_manager = GetSubmittedManager();
if (!submitted_manager) {
if (logger) {
logger->LogMessage(Logger::STRING_NO_PROVISIONAL_SAVE_MANAGER);
}
return false;
}
if (submitted_manager->GetFormFetcher()->GetState() ==
FormFetcher::State::WAITING) {
// We have a provisional save manager, but it didn't finish matching yet.
// We just give up.
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::MATCHING_NOT_COMPLETE,
submitted_manager->GetOrigin(), logger.get());
return false;
}
return !submitted_manager->GetPendingCredentials().only_for_fallback_saving;
}
bool PasswordManager::ShouldBlockPasswordForSameOriginButDifferentScheme(
const PasswordForm& form) const {
const GURL& old_origin = main_frame_url_.GetOrigin();
const GURL& new_origin = form.origin.GetOrigin();
return old_origin.host_piece() == new_origin.host_piece() &&
old_origin.SchemeIsCryptographic() &&
!new_origin.SchemeIsCryptographic();
}
void PasswordManager::OnPasswordFormsRendered(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& visible_forms,
bool did_stop_loading) {
CreatePendingLoginManagers(driver, visible_forms);
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD);
}
if (!IsAutomaticSavePromptAvailable())
return;
PasswordFormManagerInterface* submitted_manager = GetSubmittedManager();
// If the server throws an internal error, access denied page, page not
// found etc. after a login attempt, we do not save the credentials.
if (client_->WasLastNavigationHTTPError()) {
if (logger)
logger->LogMessage(Logger::STRING_DECISION_DROP);
submitted_manager->GetMetricsRecorder()->LogSubmitFailed();
provisional_save_manager_.reset();
owned_submitted_form_manager_.reset();
return;
}
if (logger) {
logger->LogNumber(Logger::STRING_NUMBER_OF_VISIBLE_FORMS,
visible_forms.size());
}
// Record all visible forms from the frame.
all_visible_forms_.insert(all_visible_forms_.end(),
visible_forms.begin(),
visible_forms.end());
if (!did_stop_loading)
return;
// If we see the login form again, then the login failed.
if (submitted_manager->GetPendingCredentials().scheme ==
PasswordForm::SCHEME_HTML) {
for (const PasswordForm& form : all_visible_forms_) {
if (IsPasswordFormReappeared(
form, submitted_manager->GetPendingCredentials())) {
if (submitted_manager->IsPossibleChangePasswordFormWithoutUsername() &&
AreAllFieldsEmpty(form)) {
continue;
}
submitted_manager->GetMetricsRecorder()->LogSubmitFailed();
if (logger) {
logger->LogPasswordForm(Logger::STRING_PASSWORD_FORM_REAPPEARED,
form);
logger->LogMessage(Logger::STRING_DECISION_DROP);
}
provisional_save_manager_.reset();
owned_submitted_form_manager_.reset();
// Clear all_visible_forms_ once we found the match.
all_visible_forms_.clear();
return;
}
}
} else {
if (logger)
logger->LogMessage(Logger::STRING_PROVISIONALLY_SAVED_FORM_IS_NOT_HTML);
}
// Clear all_visible_forms_ after checking all the visible forms.
all_visible_forms_.clear();
// Looks like a successful login attempt. Either show an infobar or
// automatically save the login data. We prompt when the user hasn't
// already given consent, either through previously accepting the infobar
// or by having the browser generate the password.
OnLoginSuccessful();
}
void PasswordManager::OnLoginSuccessful() {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_ON_ASK_USER_OR_SAVE_PASSWORD);
}
PasswordFormManagerInterface* submitted_manager = GetSubmittedManager();
DCHECK(submitted_manager);
DCHECK(submitted_manager->GetSubmittedForm());
client_->GetStoreResultFilter()->ReportFormLoginSuccess(*submitted_manager);
auto submission_event =
submitted_manager->GetSubmittedForm()->submission_event;
metrics_util::LogPasswordSuccessfulSubmissionIndicatorEvent(submission_event);
if (logger)
logger->LogSuccessfulSubmissionIndicatorEvent(submission_event);
bool able_to_save_passwords =
client_->GetPasswordStore()->IsAbleToSavePasswords();
UMA_HISTOGRAM_BOOLEAN("PasswordManager.AbleToSavePasswordsOnSuccessfulLogin",
able_to_save_passwords);
if (!able_to_save_passwords)
return;
MaybeSavePasswordHash(*submitted_manager);
// TODO(https://crbug.com/831123): Implement checking whether to save with
// NewPasswordFormManager.
if (!client_->GetStoreResultFilter()->ShouldSave(
*submitted_manager->GetSubmittedForm())) {
RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::SYNC_CREDENTIAL,
submitted_manager->GetOrigin(), logger.get());
provisional_save_manager_.reset();
owned_submitted_form_manager_.reset();
return;
}
submitted_manager->GetMetricsRecorder()->LogSubmitPassed();
RecordWhetherTargetDomainDiffers(main_frame_url_, client_->GetMainFrameURL());
UMA_HISTOGRAM_BOOLEAN(
"PasswordManager.SuccessfulLoginHappened",
submitted_manager->GetSubmittedForm()->origin.SchemeIsCryptographic());
// If the form is eligible only for saving fallback, it shouldn't go here.
DCHECK(!submitted_manager->GetPendingCredentials().only_for_fallback_saving);
// TODO(https://crbug.com/831123): Remove logging when the old form parsing is
// removed.
if (is_new_form_parsing_for_saving_enabled_) {
// In this case, |submitted_manager| points to a NewPasswordFormManager and
// |provisional_save_manager_| to a PasswordFormManager. They use the new
// and the old FormData parser, respectively. Log the differences using UKM
// to be alerted of regressions early.
if (provisional_save_manager_) {
RecordParsingOnSavingDifference(*provisional_save_manager_,
*submitted_manager,
submitted_manager->GetMetricsRecorder());
}
}
if (ShouldPromptUserToSavePassword(*submitted_manager)) {
bool empty_password =
submitted_manager->GetPendingCredentials().username_value.empty();
UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.OfferedToSave",
empty_password);
if (logger)
logger->LogMessage(Logger::STRING_DECISION_ASK);
bool update_password = submitted_manager->IsPasswordUpdate();
if (client_->PromptUserToSaveOrUpdatePassword(MoveOwnedSubmittedManager(),
update_password)) {
if (logger)
logger->LogMessage(Logger::STRING_SHOW_PASSWORD_PROMPT);
}
} else {
if (logger)
logger->LogMessage(Logger::STRING_DECISION_SAVE);
submitted_manager->Save();
if (!submitted_manager->IsNewLogin()) {
client_->NotifySuccessfulLoginWithExistingPassword(
submitted_manager->GetPendingCredentials());
}
if (submitted_manager->HasGeneratedPassword()) {
client_->AutomaticPasswordSave(MoveOwnedSubmittedManager());
} else {
provisional_save_manager_.reset();
owned_submitted_form_manager_.reset();
}
}
}
void PasswordManager::MaybeSavePasswordHash(
const PasswordFormManagerInterface& submitted_manager) {
#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
// When |username_value| is empty, it's not clear whether the submitted
// credentials are really Gaia or enterprise credentials. Don't save
// password hash in that case.
std::string username =
base::UTF16ToUTF8(submitted_manager.GetSubmittedForm()->username_value);
if (username.empty())
return;
password_manager::PasswordStore* store = client_->GetPasswordStore();
// May be null in tests.
if (!store)
return;
const PasswordForm* password_form = submitted_manager.GetSubmittedForm();
bool should_save_enterprise_pw =
client_->GetStoreResultFilter()->ShouldSaveEnterprisePasswordHash(
*password_form);
bool should_save_gaia_pw =
client_->GetStoreResultFilter()->ShouldSaveGaiaPasswordHash(
*password_form);
if (!should_save_enterprise_pw && !should_save_gaia_pw)
return;
// Canonicalizes username if it is an email.
if (username.find('@') != std::string::npos)
username = gaia::CanonicalizeEmail(username);
bool is_password_change = !password_form->new_password_element.empty();
const base::string16 password = is_password_change
? password_form->new_password_value
: password_form->password_value;
if (should_save_enterprise_pw) {
store->SaveEnterprisePasswordHash(username, password);
return;
}
DCHECK(should_save_gaia_pw);
SyncPasswordHashChange event =
client_->GetStoreResultFilter()->IsSyncAccountEmail(username)
? (is_password_change
? SyncPasswordHashChange::CHANGED_IN_CONTENT_AREA
: SyncPasswordHashChange::SAVED_IN_CONTENT_AREA)
: SyncPasswordHashChange::NOT_SYNC_PASSWORD_CHANGE;
store->SaveGaiaPasswordHash(username, password, event);
#endif
}
void PasswordManager::AutofillHttpAuth(
const std::map<base::string16, const PasswordForm*>& best_matches,
const PasswordForm& preferred_match) const {
DCHECK_NE(PasswordForm::SCHEME_HTML, preferred_match.scheme);
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_)) {
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
logger->LogMessage(Logger::STRING_PASSWORDMANAGER_AUTOFILLHTTPAUTH);
logger->LogBoolean(Logger::STRING_LOGINMODELOBSERVER_PRESENT,
observers_.might_have_observers());
}
for (LoginModelObserver& observer : observers_)
observer.OnAutofillDataAvailable(preferred_match);
DCHECK(!best_matches.empty());
client_->PasswordWasAutofilled(best_matches,
best_matches.begin()->second->origin, nullptr);
}
void PasswordManager::ProcessAutofillPredictions(
password_manager::PasswordManagerDriver* driver,
const std::vector<autofill::FormStructure*>& forms) {
std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
if (password_manager_util::IsLoggingActive(client_))
logger.reset(
new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
if (base::FeatureList::IsEnabled(
password_manager::features::kNewPasswordFormParsing)) {
for (const autofill::FormStructure* form : forms)
predictions_[form->form_signature()] = ConvertToFormPredictions(*form);
for (auto& manager : form_managers_)
manager->ProcessServerPredictions(predictions_);
}
// Leave only forms that contain fields that are useful for password manager.
std::map<FormData, autofill::PasswordFormFieldPredictionMap> predictions;
for (const autofill::FormStructure* form : forms) {
if (logger)
logger->LogFormStructure(Logger::STRING_SERVER_PREDICTIONS, *form);
for (const auto& field : *form) {
autofill::PasswordFormFieldPredictionType prediction_type;
if (ServerPredictionsToPasswordFormPrediction(field->server_predictions(),
&prediction_type)) {
predictions[form->ToFormData()][*field] = prediction_type;
}
// Certain fields are annotated by the browsers as "not passwords" i.e.
// they should not be treated as passwords by the Password Manager.
if (field->form_control_type == "password" &&
IsPredictedTypeNotPasswordPrediction(
field->Type().GetStorableType())) {
predictions[form->ToFormData()][*field] =
autofill::PREDICTION_NOT_PASSWORD;
}
}
}
if (predictions.empty())
return;
driver->AutofillDataReceived(predictions);
}
PasswordFormManager* PasswordManager::GetMatchingPendingManager(
const PasswordForm& form) {
PasswordFormManager* matched_manager = nullptr;
PasswordFormManager::MatchResultMask current_match_result =
PasswordFormManager::RESULT_NO_MATCH;
for (auto& login_manager : pending_login_managers_) {
PasswordFormManager::MatchResultMask result =
login_manager->DoesManage(form, nullptr);
if (result == PasswordFormManager::RESULT_NO_MATCH)
continue;
if (result == PasswordFormManager::RESULT_COMPLETE_MATCH) {
// If we find a manager that exactly matches the submitted form including
// the action URL, exit the loop.
matched_manager = login_manager.get();
break;
} else if (result == (PasswordFormManager::RESULT_COMPLETE_MATCH &
~PasswordFormManager::RESULT_ACTION_MATCH) &&
result > current_match_result) {
// If the current manager matches the submitted form excluding the action
// URL, remember it as a candidate and continue searching for an exact
// match. See http://crbug.com/27246 for an example where actions can
// change.
matched_manager = login_manager.get();
current_match_result = result;
} else if (result > current_match_result) {
matched_manager = login_manager.get();
current_match_result = result;
}
}
return matched_manager;
}
PasswordFormManagerInterface* PasswordManager::GetSubmittedManager() const {
// TODO(https://crbug.com/915161) Implement support of non-html schemes in
// NewPasswordFormManager remove using PasswordFormManager for such forms.
if (provisional_save_manager_ &&
provisional_save_manager_->GetPendingCredentials().scheme !=
PasswordForm::SCHEME_HTML)
return provisional_save_manager_.get();
if (!is_new_form_parsing_for_saving_enabled_)
return provisional_save_manager_.get();
if (owned_submitted_form_manager_)
return owned_submitted_form_manager_.get();
for (const std::unique_ptr<NewPasswordFormManager>& manager :
form_managers_) {
if (manager->is_submitted())
return manager.get();
}
return nullptr;
}
std::unique_ptr<PasswordFormManagerForUI>
PasswordManager::MoveOwnedSubmittedManager() {
// TODO(https://crbug.com/915161) Implement support of non-html schemes in
// NewPasswordFormManager remove using PasswordFormManager for such forms.
if (provisional_save_manager_ &&
provisional_save_manager_->GetPendingCredentials().scheme !=
PasswordForm::SCHEME_HTML)
return std::move(provisional_save_manager_);
if (!is_new_form_parsing_for_saving_enabled_)
return std::move(provisional_save_manager_);
if (owned_submitted_form_manager_)
return std::move(owned_submitted_form_manager_);
for (auto iter = form_managers_.begin(); iter != form_managers_.end();
++iter) {
if ((*iter)->is_submitted()) {
std::unique_ptr<NewPasswordFormManager> submitted_manager =
std::move(*iter);
form_managers_.erase(iter);
return std::move(submitted_manager);
}
}
NOTREACHED();
return nullptr;
}
void PasswordManager::RecordProvisionalSaveFailure(
PasswordManagerMetricsRecorder::ProvisionalSaveFailure failure,
const GURL& form_origin,
BrowserSavePasswordProgressLogger* logger) {
if (client_ && client_->GetMetricsRecorder()) {
client_->GetMetricsRecorder()->RecordProvisionalSaveFailure(
failure, main_frame_url_, form_origin, logger);
}
}
scoped_refptr<PasswordFormMetricsRecorder>
PasswordManager::GetMetricRecorderFromNewPasswordFormManager(
const FormData& form,
const PasswordManagerDriver* driver) {
for (auto& form_manager : form_managers_) {
if (form_manager->DoesManage(form, driver))
return form_manager->metrics_recorder();
}
return nullptr;
}
// TODO(https://crbug.com/831123): Implement creating missing
// NewPasswordFormManager when PasswordFormManager is gone.
PasswordFormManagerInterface* PasswordManager::GetMatchedManager(
const PasswordManagerDriver* driver,
const PasswordForm& form) {
if (!is_new_form_parsing_for_saving_enabled_)
return GetMatchingPendingManager(form);
for (auto& form_manager : form_managers_) {
if (form_manager->DoesManage(form.form_data, driver))
return form_manager.get();
}
return nullptr;
}
} // namespace password_manager