// Copyright (c) 2011 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 "chrome/browser/password_manager/password_manager.h"
#include <vector>
#include "base/stl_util-inl.h"
#include "base/threading/platform_thread.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/metrics/user_metrics.h"
#include "chrome/browser/password_manager/password_form_manager.h"
#include "chrome/browser/password_manager/password_manager_delegate.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/autofill_messages.h"
#include "chrome/common/pref_names.h"
#include "content/common/view_messages.h"
#include "grit/generated_resources.h"
using webkit_glue::PasswordForm;
using webkit_glue::PasswordFormMap;
// static
void PasswordManager::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kPasswordManagerEnabled, true);
prefs->RegisterBooleanPref(prefs::kPasswordManagerAllowShowPasswords, true);
}
// This routine is called when PasswordManagers are constructed.
//
// Currently we report metrics only once at startup. We require
// that this is only ever called from a single thread in order to
// avoid needing to lock (a static boolean flag is then sufficient to
// guarantee running only once).
static void ReportMetrics(bool password_manager_enabled) {
static base::PlatformThreadId initial_thread_id =
base::PlatformThread::CurrentId();
DCHECK(initial_thread_id == base::PlatformThread::CurrentId());
static bool ran_once = false;
if (ran_once)
return;
ran_once = true;
if (password_manager_enabled)
UserMetrics::RecordAction(UserMetricsAction("PasswordManager_Enabled"));
else
UserMetrics::RecordAction(UserMetricsAction("PasswordManager_Disabled"));
}
PasswordManager::PasswordManager(TabContents* tab_contents,
PasswordManagerDelegate* delegate)
: TabContentsObserver(tab_contents),
login_managers_deleter_(&pending_login_managers_),
delegate_(delegate),
observer_(NULL) {
DCHECK(delegate_);
password_manager_enabled_.Init(prefs::kPasswordManagerEnabled,
delegate_->GetProfileForPasswordManager()->GetPrefs(), NULL);
ReportMetrics(*password_manager_enabled_);
}
PasswordManager::~PasswordManager() {
}
void PasswordManager::ProvisionallySavePassword(PasswordForm form) {
if (!delegate_->GetProfileForPasswordManager() ||
delegate_->GetProfileForPasswordManager()->IsOffTheRecord() ||
!*password_manager_enabled_)
return;
// No password to save? Then don't.
if (form.password_value.empty())
return;
LoginManagers::iterator iter;
PasswordFormManager* manager = NULL;
for (iter = pending_login_managers_.begin();
iter != pending_login_managers_.end(); iter++) {
if ((*iter)->DoesManage(form)) {
manager = *iter;
break;
}
}
// 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 (!manager)
return;
// If we found a manager but it didn't finish matching yet, the user has
// tried to submit credentials before we had time to even find matching
// results for the given form and autofill. If this is the case, we just
// give up.
if (!manager->HasCompletedMatching())
return;
// Also get out of here if the user told us to 'never remember' passwords for
// this form.
if (manager->IsBlacklisted())
return;
form.ssl_valid = form.origin.SchemeIsSecure() &&
!delegate_->DidLastPageLoadEncounterSSLErrors();
form.preferred = true;
manager->ProvisionallySave(form);
provisional_save_manager_.reset(manager);
pending_login_managers_.erase(iter);
// We don't care about the rest of the forms on the page now that one
// was selected.
STLDeleteElements(&pending_login_managers_);
}
void PasswordManager::DidNavigate() {
// As long as this navigation isn't due to a currently pending
// password form submit, we're ready to reset and move on.
if (!provisional_save_manager_.get() && !pending_login_managers_.empty())
STLDeleteElements(&pending_login_managers_);
}
void PasswordManager::ClearProvisionalSave() {
provisional_save_manager_.reset();
}
void PasswordManager::SetObserver(LoginModelObserver* observer) {
observer_ = observer;
}
void PasswordManager::DidStopLoading() {
if (!provisional_save_manager_.get())
return;
DCHECK(!delegate_->GetProfileForPasswordManager()->IsOffTheRecord());
DCHECK(!provisional_save_manager_->IsBlacklisted());
if (!delegate_->GetProfileForPasswordManager())
return;
// Form is not completely valid - we do not support it.
if (!provisional_save_manager_->HasValidPasswordForm())
return;
provisional_save_manager_->SubmitPassed();
if (provisional_save_manager_->IsNewLogin()) {
delegate_->AddSavePasswordInfoBar(provisional_save_manager_.release());
} else {
// If the save is not a new username entry, then we just want to save this
// data (since the user already has related data saved), so don't prompt.
provisional_save_manager_->Save();
provisional_save_manager_.reset();
}
}
void PasswordManager::DidNavigateAnyFramePostCommit(
const NavigationController::LoadCommittedDetails& details,
const ViewHostMsg_FrameNavigate_Params& params) {
if (params.password_form.origin.is_valid())
ProvisionallySavePassword(params.password_form);
}
bool PasswordManager::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PasswordManager, message)
IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsFound,
OnPasswordFormsFound)
IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordFormsVisible,
OnPasswordFormsVisible)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void PasswordManager::OnPasswordFormsFound(
const std::vector<PasswordForm>& forms) {
if (!delegate_->GetProfileForPasswordManager())
return;
if (!*password_manager_enabled_)
return;
// Ask the SSLManager for current security.
bool had_ssl_error = delegate_->DidLastPageLoadEncounterSSLErrors();
std::vector<PasswordForm>::const_iterator iter;
for (iter = forms.begin(); iter != forms.end(); iter++) {
bool ssl_valid = iter->origin.SchemeIsSecure() && !had_ssl_error;
PasswordFormManager* manager =
new PasswordFormManager(delegate_->GetProfileForPasswordManager(),
this, *iter, ssl_valid);
pending_login_managers_.push_back(manager);
manager->FetchMatchingLoginsFromPasswordStore();
}
}
void PasswordManager::OnPasswordFormsVisible(
const std::vector<PasswordForm>& visible_forms) {
if (!provisional_save_manager_.get())
return;
std::vector<PasswordForm>::const_iterator iter;
for (iter = visible_forms.begin(); iter != visible_forms.end(); iter++) {
if (provisional_save_manager_->DoesManage(*iter)) {
// The form trying to be saved has immediately re-appeared. Assume login
// failure and abort this save, by clearing provisional_save_manager_.
// Don't delete the login managers since the user may try again
// and we want to be able to save in that case.
provisional_save_manager_->SubmitFailed();
ClearProvisionalSave();
break;
}
}
}
void PasswordManager::Autofill(
const PasswordForm& form_for_autofill,
const PasswordFormMap& best_matches,
const PasswordForm* const preferred_match,
bool wait_for_username) const {
DCHECK(preferred_match);
switch (form_for_autofill.scheme) {
case PasswordForm::SCHEME_HTML: {
// Note the check above is required because the observer_ for a non-HTML
// schemed password form may have been freed, so we need to distinguish.
webkit_glue::PasswordFormFillData fill_data;
webkit_glue::PasswordFormDomManager::InitFillData(form_for_autofill,
best_matches,
preferred_match,
wait_for_username,
&fill_data);
delegate_->FillPasswordForm(fill_data);
return;
}
default:
if (observer_) {
observer_->OnAutofillDataAvailable(
UTF16ToWideHack(preferred_match->username_value),
UTF16ToWideHack(preferred_match->password_value));
}
}
}