// Copyright (c) 2006-2008 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 #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/platform_thread.h" #include "base/stl_util-inl.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/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.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); } // 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 PlatformThreadId initial_thread_id = PlatformThread::CurrentId(); DCHECK(initial_thread_id == 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(Delegate* delegate) : 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::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::PasswordFormsFound( const std::vector& forms) { if (!delegate_->GetProfileForPasswordManager()) return; if (!*password_manager_enabled_) return; // Ask the SSLManager for current security. bool had_ssl_error = delegate_->DidLastPageLoadEncounterSSLErrors(); std::vector::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::PasswordFormsVisible( const std::vector& visible_forms) { if (!provisional_save_manager_.get()) return; std::vector::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::PasswordFormDomManager::FillData 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)); } } }