// 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)); } } }