blob: 66b8a6d7ba300d4741cad54a713de61db2242f63 [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 "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.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/affiliation_utils.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/keychain_migration_status_mac.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_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/common/password_manager_pref_names.h"
#include "components/password_manager/core/common/password_manager_switches.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#if defined(OS_WIN)
#include "base/prefs/pref_registry_simple.h"
#endif
using autofill::PasswordForm;
using autofill::PasswordFormMap;
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 ShouldDropSyncCredential() {
std::string group_name =
base::FieldTrialList::FindFullName("PasswordManagerDropSyncCredential");
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kEnableDropSyncCredential))
return true;
if (command_line->HasSwitch(switches::kDisableDropSyncCredential))
return false;
// Default to not saving.
return group_name != "Disabled";
}
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;
}
// 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();
}
bool ServerTypeToPrediction(autofill::ServerFieldType server_field_type,
autofill::PasswordFormFieldPredictionType* type) {
switch (server_field_type) {
case autofill::USERNAME:
case autofill::USERNAME_AND_EMAIL_ADDRESS:
*type = autofill::PREDICTION_USERNAME;
break;
case autofill::PASSWORD:
*type = autofill::PREDICTION_CURRENT_PASSWORD;
break;
case autofill::ACCOUNT_CREATION_PASSWORD:
*type = autofill::PREDICTION_NEW_PASSWORD;
break;
default:
return false;
}
return true;
}
bool PreferredRealmIsFromAndroid(
const autofill::PasswordFormFillData& fill_data) {
return FacetURI::FromPotentiallyInvalidSpec(
fill_data.preferred_realm).IsValidAndroidFacetURI();
}
bool ContainsAndroidCredentials(
const autofill::PasswordFormFillData& fill_data) {
for (const auto& login : fill_data.additional_logins) {
if (FacetURI::FromPotentiallyInvalidSpec(
login.second.realm).IsValidAndroidFacetURI()) {
return true;
}
}
return PreferredRealmIsFromAndroid(fill_data);
}
} // namespace
// static
void PasswordManager::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(
prefs::kPasswordManagerSavingEnabled,
true,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterBooleanPref(prefs::kPasswordManagerAutoSignin, true);
registry->RegisterBooleanPref(prefs::kPasswordManagerAllowShowPasswords,
true);
registry->RegisterListPref(prefs::kPasswordManagerGroupsForDomains);
#if defined(OS_MACOSX)
registry->RegisterIntegerPref(prefs::kKeychainMigrationStatus,
static_cast<int>(MigrationStatus::NOT_STARTED));
#endif
}
#if defined(OS_WIN)
// static
void PasswordManager::RegisterLocalPrefs(PrefRegistrySimple* registry) {
registry->RegisterInt64Pref(prefs::kOsPasswordLastChanged, 0);
registry->RegisterBooleanPref(prefs::kOsPasswordBlank, false);
}
#endif
PasswordManager::PasswordManager(PasswordManagerClient* client)
: client_(client) {
DCHECK(client_);
}
PasswordManager::~PasswordManager() {
FOR_EACH_OBSERVER(LoginModelObserver, observers_, OnLoginModelDestroying());
}
void PasswordManager::SetHasGeneratedPasswordForForm(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& form,
bool password_is_generated) {
DCHECK(client_->IsSavingEnabledForCurrentPage());
if (!password_is_generated) {
// Since this password was provisionally saved it was removed from
// |pending_login_managers_| and needs to be re-added.
provisional_save_manager_->set_has_generated_password(false);
pending_login_managers_.push_back(provisional_save_manager_.Pass());
return;
}
ScopedVector<PasswordFormManager>::iterator matched_manager_it =
pending_login_managers_.end();
PasswordFormManager::MatchResultMask current_match_result =
PasswordFormManager::RESULT_NO_MATCH;
for (ScopedVector<PasswordFormManager>::iterator iter =
pending_login_managers_.begin();
iter != pending_login_managers_.end(); ++iter) {
PasswordFormManager::MatchResultMask result = (*iter)->DoesManage(form);
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_it = iter;
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_it = iter;
current_match_result = result;
} else if (result > current_match_result) {
matched_manager_it = iter;
current_match_result = result;
}
}
if (matched_manager_it != pending_login_managers_.end()) {
(*matched_manager_it)->set_has_generated_password(true);
// Provisionally save generated passwords now, as they should always be
// saved.
ProvisionallySavePassword(form);
return;
}
UMA_HISTOGRAM_BOOLEAN("PasswordManager.GeneratedFormHasNoFormManager", true);
// 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.
bool ssl_valid = form.origin.SchemeIsCryptographic();
PasswordFormManager* manager = new PasswordFormManager(
this, client_, driver->AsWeakPtr(), form, ssl_valid);
pending_login_managers_.push_back(manager);
manager->set_has_generated_password(true);
ProvisionallySavePassword(form);
}
void PasswordManager::ProvisionallySavePassword(const PasswordForm& form) {
bool is_saving_enabled = client_->IsSavingEnabledForCurrentPage();
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_PROVISIONALLY_SAVE_PASSWORD_METHOD);
logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVE_PASSWORD_FORM,
form);
}
if (!is_saving_enabled) {
RecordFailure(SAVING_DISABLED, form.origin, logger.get());
return;
}
// No password to save? Then don't.
if (PasswordFormManager::PasswordToSave(form).empty()) {
RecordFailure(EMPTY_PASSWORD, form.origin, logger.get());
return;
}
scoped_ptr<PasswordFormManager> manager;
ScopedVector<PasswordFormManager>::iterator 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
// !HasCompletedMatching sense. We keep track of such PasswordFormManager
// instances for UMA.
for (ScopedVector<PasswordFormManager>::iterator iter =
pending_login_managers_.begin();
iter != pending_login_managers_.end(); ++iter) {
PasswordFormManager::MatchResultMask result = (*iter)->DoesManage(form);
if (result == PasswordFormManager::RESULT_NO_MATCH)
continue;
(*iter)->SetSubmittedForm(form);
if ((*iter)->is_ignorable_change_password_form()) {
if (logger)
logger->LogMessage(Logger::STRING_CHANGE_PASSWORD_FORM);
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.
if (logger)
logger->LogMessage(Logger::STRING_EXACT_MATCH);
matched_manager_it = iter;
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.
if (logger)
logger->LogMessage(Logger::STRING_MATCH_WITHOUT_ACTION);
matched_manager_it = iter;
current_match_result = result;
} else if (IsSignupForm(form) && result > current_match_result) {
// Signup forms don't require HTML attributes to match because we don't
// need to fill these saved passwords on the same form in the future.
// Prefer the best possible match (e.g. action and origins match instead
// or just origin matching). Don't break in case there exists a better
// match.
// TODO(gcasto): Matching in this way is very imprecise. Having some
// better way to match the same form when the HTML elements change (e.g.
// text element changed to password element) would be useful.
if (logger)
logger->LogMessage(Logger::STRING_ORIGINS_MATCH);
matched_manager_it = iter;
current_match_result = result;
}
}
// 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.
if (matched_manager_it != pending_login_managers_.end()) {
// Transfer ownership of the manager from |pending_login_managers_| to
// |manager|.
manager.reset(*matched_manager_it);
pending_login_managers_.weak_erase(matched_manager_it);
} else {
RecordFailure(NO_MATCHING_FORM, form.origin, logger.get());
return;
}
// Bail if we're missing any of the necessary form components.
if (!manager->HasValidPasswordForm()) {
RecordFailure(INVALID_FORM, form.origin, logger.get());
return;
}
// Don't save credentials for the syncing account. See crbug.com/365832 for
// background.
if (ShouldDropSyncCredential() &&
client_->IsSyncAccountCredential(base::UTF16ToUTF8(form.username_value),
form.signon_realm)) {
RecordFailure(SYNC_CREDENTIAL, form.origin, logger.get());
return;
}
PasswordForm provisionally_saved_form(form);
provisionally_saved_form.ssl_valid =
form.origin.SchemeIsCryptographic() &&
!client_->DidLastPageLoadEncounterSSLErrors();
provisionally_saved_form.preferred = true;
if (logger) {
logger->LogPasswordForm(Logger::STRING_PROVISIONALLY_SAVED_FORM,
provisionally_saved_form);
}
PasswordFormManager::OtherPossibleUsernamesAction action =
PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES;
if (OtherPossibleUsernamesEnabled())
action = PasswordFormManager::ALLOW_OTHER_POSSIBLE_USERNAMES;
if (logger) {
logger->LogBoolean(
Logger::STRING_IGNORE_POSSIBLE_USERNAMES,
action == PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
}
manager->ProvisionallySave(provisionally_saved_form, action);
provisional_save_manager_.swap(manager);
// 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();
}
void PasswordManager::RecordFailure(ProvisionalSaveFailure failure,
const GURL& form_origin,
BrowserSavePasswordProgressLogger* logger) {
UMA_HISTOGRAM_ENUMERATION(
"PasswordManager.ProvisionalSaveFailure", failure, MAX_FAILURE_VALUE);
std::string group_name =
metrics_util::GroupIdToString(metrics_util::MonitoredDomainGroupId(
form_origin.host(), client_->GetPrefs()));
if (!group_name.empty()) {
metrics_util::LogUMAHistogramEnumeration(
"PasswordManager.ProvisionalSaveFailure_" + group_name,
failure,
MAX_FAILURE_VALUE);
}
if (logger) {
switch (failure) {
case SAVING_DISABLED:
logger->LogMessage(Logger::STRING_SAVING_DISABLED);
break;
case EMPTY_PASSWORD:
logger->LogMessage(Logger::STRING_EMPTY_PASSWORD);
break;
case MATCHING_NOT_COMPLETE:
logger->LogMessage(Logger::STRING_MATCHING_NOT_COMPLETE);
break;
case NO_MATCHING_FORM:
logger->LogMessage(Logger::STRING_NO_MATCHING_FORM);
break;
case FORM_BLACKLISTED:
logger->LogMessage(Logger::STRING_FORM_BLACKLISTED);
break;
case INVALID_FORM:
logger->LogMessage(Logger::STRING_INVALID_FORM);
break;
case SYNC_CREDENTIAL:
logger->LogMessage(Logger::STRING_SYNC_CREDENTIAL);
break;
case MAX_FAILURE_VALUE:
NOTREACHED();
return;
}
logger->LogMessage(Logger::STRING_DECISION_DROP);
}
}
void PasswordManager::AddSubmissionCallback(
const PasswordSubmittedCallback& callback) {
submission_callbacks_.push_back(callback);
}
void PasswordManager::AddObserver(LoginModelObserver* observer) {
observers_.AddObserver(observer);
}
void PasswordManager::RemoveObserver(LoginModelObserver* observer) {
observers_.RemoveObserver(observer);
}
void PasswordManager::DidNavigateMainFrame() {
pending_login_managers_.clear();
}
void PasswordManager::OnPasswordFormSubmitted(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& password_form) {
ProvisionallySavePassword(password_form);
for (size_t i = 0; i < submission_callbacks_.size(); ++i) {
submission_callbacks_[i].Run(password_form);
}
pending_login_managers_.clear();
}
void PasswordManager::OnPasswordFormForceSaveRequested(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& password_form) {
// TODO(msramek): This is just a sketch. We will need to show a custom bubble,
// mark the form as force saved, and recreate the pending login managers,
// because the password store might have changed.
ProvisionallySavePassword(password_form);
AskUserOrSavePassword();
}
void PasswordManager::OnPasswordFormsParsed(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& forms) {
CreatePendingLoginManagers(driver, forms);
}
void PasswordManager::CreatePendingLoginManagers(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& forms) {
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_CREATE_LOGIN_MANAGERS_METHOD);
}
if (client_->DidLastPageLoadEncounterSSLErrors() ||
!client_->IsPasswordManagementEnabledForCurrentPage())
return;
if (logger) {
logger->LogNumber(Logger::STRING_OLD_NUMBER_LOGIN_MANAGERS,
pending_login_managers_.size());
}
// Copy the weak pointers to the currently known login managers for comparison
// against the newly added.
std::vector<PasswordFormManager*> old_login_managers(
pending_login_managers_.get());
for (std::vector<PasswordForm>::const_iterator iter = forms.begin();
iter != forms.end(); ++iter) {
// Don't involve the password manager if this form corresponds to
// SpdyProxy authentication, as indicated by the realm.
if (base::EndsWith(iter->signon_realm, kSpdyProxyRealm, true))
continue;
bool old_manager_found = false;
for (const auto& old_manager : old_login_managers) {
if (old_manager->DoesManage(*iter) !=
PasswordFormManager::RESULT_COMPLETE_MATCH) {
continue;
}
old_manager_found = true;
if (old_manager->HasCompletedMatching())
old_manager->ProcessFrame(driver->AsWeakPtr());
break;
}
if (old_manager_found)
continue; // The current form is already managed.
UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.ParsedUsernameField",
iter->username_element.empty());
// Out of the forms not containing a username field, determine how many
// are password change forms.
if (iter->username_element.empty()) {
UMA_HISTOGRAM_BOOLEAN(
"PasswordManager.EmptyUsernames."
"FormWithoutUsernameFieldIsPasswordChangeForm",
!iter->new_password_element.empty());
}
if (logger)
logger->LogFormSignatures(Logger::STRING_ADDING_SIGNATURE, *iter);
bool ssl_valid = iter->origin.SchemeIsCryptographic();
PasswordFormManager* manager = new PasswordFormManager(
this, client_, driver->AsWeakPtr(), *iter, ssl_valid);
pending_login_managers_.push_back(manager);
PasswordStore::AuthorizationPromptPolicy prompt_policy =
client_->GetAuthorizationPromptPolicy(*iter);
manager->FetchMatchingLoginsFromPasswordStore(prompt_policy);
}
if (logger) {
logger->LogNumber(Logger::STRING_NEW_NUMBER_LOGIN_MANAGERS,
pending_login_managers_.size());
}
}
bool PasswordManager::CanProvisionalManagerSave() {
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_CAN_PROVISIONAL_MANAGER_SAVE_METHOD);
}
if (!provisional_save_manager_.get()) {
if (logger) {
logger->LogMessage(Logger::STRING_NO_PROVISIONAL_SAVE_MANAGER);
}
return false;
}
if (!provisional_save_manager_->HasCompletedMatching()) {
// We have a provisional save manager, but it didn't finish matching yet.
// We just give up.
RecordFailure(MATCHING_NOT_COMPLETE,
provisional_save_manager_->observed_form().origin,
logger.get());
provisional_save_manager_.reset();
return false;
}
// Also get out of here if the user told us to 'never remember' passwords for
// this form.
if (provisional_save_manager_->IsBlacklisted()) {
RecordFailure(FORM_BLACKLISTED,
provisional_save_manager_->observed_form().origin,
logger.get());
provisional_save_manager_.reset();
return false;
}
return true;
}
bool PasswordManager::ShouldPromptUserToSavePassword() const {
return !client_->IsAutomaticPasswordSavingEnabled() &&
provisional_save_manager_->IsNewLogin() &&
!provisional_save_manager_->has_generated_password() &&
!provisional_save_manager_->IsPendingCredentialsPublicSuffixMatch();
}
void PasswordManager::OnPasswordFormsRendered(
password_manager::PasswordManagerDriver* driver,
const std::vector<PasswordForm>& visible_forms,
bool did_stop_loading) {
CreatePendingLoginManagers(driver, visible_forms);
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_ON_PASSWORD_FORMS_RENDERED_METHOD);
}
if (!CanProvisionalManagerSave())
return;
DCHECK(client_->IsSavingEnabledForCurrentPage());
// 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);
provisional_save_manager_->SubmitFailed();
provisional_save_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 we see the login form again, then the login failed.
if (did_stop_loading) {
if (provisional_save_manager_->pending_credentials().scheme ==
PasswordForm::SCHEME_HTML) {
// Generated passwords should always be saved.
if (provisional_save_manager_->has_generated_password())
all_visible_forms_.clear();
for (size_t i = 0; i < all_visible_forms_.size(); ++i) {
// TODO(vabr): The similarity check is just action equality up to
// HTTP<->HTTPS substitution for now. If it becomes more complex, it may
// make sense to consider modifying and using
// PasswordFormManager::DoesManage for it.
if (all_visible_forms_[i].action.is_valid() &&
URLsEqualUpToHttpHttpsSubstitution(
provisional_save_manager_->pending_credentials().action,
all_visible_forms_[i].action)) {
if (logger) {
logger->LogPasswordForm(Logger::STRING_PASSWORD_FORM_REAPPEARED,
visible_forms[i]);
logger->LogMessage(Logger::STRING_DECISION_DROP);
}
provisional_save_manager_->SubmitFailed();
provisional_save_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.
AskUserOrSavePassword();
}
}
void PasswordManager::OnInPageNavigation(
password_manager::PasswordManagerDriver* driver,
const PasswordForm& password_form) {
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_ON_IN_PAGE_NAVIGATION);
}
ProvisionallySavePassword(password_form);
if (!CanProvisionalManagerSave())
return;
AskUserOrSavePassword();
}
void PasswordManager::AskUserOrSavePassword() {
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_ON_ASK_USER_OR_SAVE_PASSWORD);
}
provisional_save_manager_->SubmitPassed();
RecordWhetherTargetDomainDiffers(main_frame_url_, client_->GetMainFrameURL());
if (ShouldPromptUserToSavePassword()) {
bool empty_password =
provisional_save_manager_->pending_credentials().username_value.empty();
UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.OfferedToSave",
empty_password);
if (logger)
logger->LogMessage(Logger::STRING_DECISION_ASK);
if (client_->PromptUserToSavePassword(
provisional_save_manager_.Pass(),
CredentialSourceType::CREDENTIAL_SOURCE_PASSWORD_MANAGER)) {
if (logger)
logger->LogMessage(Logger::STRING_SHOW_PASSWORD_PROMPT);
}
} else {
if (logger)
logger->LogMessage(Logger::STRING_DECISION_SAVE);
provisional_save_manager_->Save();
if (provisional_save_manager_->has_generated_password()) {
client_->AutomaticPasswordSave(provisional_save_manager_.Pass());
} else {
provisional_save_manager_.reset();
}
}
}
bool PasswordManager::OtherPossibleUsernamesEnabled() const {
return false;
}
void PasswordManager::Autofill(password_manager::PasswordManagerDriver* driver,
const PasswordForm& form_for_autofill,
const PasswordFormMap& best_matches,
const PasswordForm& preferred_match,
bool wait_for_username) const {
scoped_ptr<BrowserSavePasswordProgressLogger> logger;
if (client_->IsLoggingActive()) {
logger.reset(new BrowserSavePasswordProgressLogger(client_));
logger->LogMessage(Logger::STRING_PASSWORDMANAGER_AUTOFILL);
}
switch (form_for_autofill.scheme) {
case PasswordForm::SCHEME_HTML: {
// Note the check above is required because the observers_ for a non-HTML
// schemed password form may have been freed, so we need to distinguish.
autofill::PasswordFormFillData fill_data;
InitPasswordFormFillData(form_for_autofill,
best_matches,
&preferred_match,
wait_for_username,
OtherPossibleUsernamesEnabled(),
&fill_data);
if (logger)
logger->LogBoolean(Logger::STRING_WAIT_FOR_USERNAME, wait_for_username);
UMA_HISTOGRAM_BOOLEAN(
"PasswordManager.FillSuggestionsIncludeAndroidAppCredentials",
ContainsAndroidCredentials(fill_data));
metrics_util::LogFilledCredentialIsFromAndroidApp(
PreferredRealmIsFromAndroid(fill_data));
driver->FillPasswordForm(fill_data);
break;
}
default:
if (logger) {
logger->LogBoolean(Logger::STRING_LOGINMODELOBSERVER_PRESENT,
observers_.might_have_observers());
}
FOR_EACH_OBSERVER(
LoginModelObserver,
observers_,
OnAutofillDataAvailable(preferred_match.username_value,
preferred_match.password_value));
break;
}
client_->PasswordWasAutofilled(best_matches);
}
void PasswordManager::ProcessAutofillPredictions(
password_manager::PasswordManagerDriver* driver,
const std::vector<autofill::FormStructure*>& forms) {
// Leave only forms that contain fields that are useful for password manager.
std::map<autofill::FormData, autofill::PasswordFormFieldPredictionMap>
predictions;
for (autofill::FormStructure* form : forms) {
for (std::vector<autofill::AutofillField*>::const_iterator field =
form->begin();
field != form->end(); ++field) {
autofill::PasswordFormFieldPredictionType prediction_type;
if (ServerTypeToPrediction((*field)->server_type(), &prediction_type))
predictions[form->ToFormData()][prediction_type] = *(*field);
}
}
if (predictions.empty())
return;
driver->AutofillDataReceived(predictions);
}
} // namespace password_manager