| // 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.h" |
| |
| #include "base/string_util.h" |
| #include "chrome/app/theme/theme_resources.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/browser/web_contents.h" |
| #include "chrome/browser/web_contents_view.h" |
| #include "chrome/common/l10n_util.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/pref_service.h" |
| #include "chrome/common/resource_bundle.h" |
| #include "chrome/common/stl_util-inl.h" |
| |
| #include "chromium_strings.h" |
| #include "generated_resources.h" |
| |
| // static |
| void PasswordManager::RegisterUserPrefs(PrefService* prefs) { |
| prefs->RegisterBooleanPref(prefs::kPasswordManagerEnabled, true); |
| } |
| |
| PasswordManager::PasswordManager(WebContents* web_contents) |
| : web_contents_(web_contents), |
| current_bar_(NULL), |
| observer_(NULL), |
| login_managers_deleter_(&pending_login_managers_) { |
| password_manager_enabled_.Init(prefs::kPasswordManagerEnabled, |
| web_contents->profile()->GetPrefs(), NULL); |
| } |
| |
| PasswordManager::~PasswordManager() { |
| CloseBars(); |
| } |
| |
| void PasswordManager::ProvisionallySavePassword(PasswordForm form) { |
| if (!web_contents_->controller() || !web_contents_->profile() || |
| web_contents_->profile()->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() && |
| !web_contents_->controller()->ssl_manager()-> |
| ProcessedSSLErrorFromRequest(); |
| form.preferred = true; |
| manager->ProvisionallySave(form); |
| pending_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_); |
| pending_login_managers_.clear(); |
| } |
| |
| 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 (!pending_save_manager_.get() && !pending_login_managers_.empty()) { |
| STLDeleteElements(&pending_login_managers_); |
| pending_login_managers_.clear(); |
| } |
| } |
| |
| void PasswordManager::ClearProvisionalSave() { |
| pending_save_manager_.reset(); |
| } |
| |
| void PasswordManager::DidStopLoading() { |
| if (!pending_save_manager_.get()) |
| return; |
| |
| DCHECK(!web_contents_->profile()->IsOffTheRecord()); |
| DCHECK(!pending_save_manager_->IsBlacklisted()); |
| |
| if (!web_contents_->profile() || |
| !web_contents_->profile()->GetWebDataService(Profile::IMPLICIT_ACCESS)) |
| return; |
| if (!web_contents_->controller()) |
| return; |
| |
| if (pending_save_manager_->IsNewLogin()) { |
| // Transfer ownership of the pending_save_manager_ to the PasswordBar. |
| ReplaceInfoBar(new SavePasswordBar(pending_save_manager_.release(), this)); |
| } 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. |
| pending_save_manager_->Save(); |
| pending_save_manager_.reset(); |
| } |
| } |
| |
| void PasswordManager::PasswordFormsSeen(const std::vector<PasswordForm>& forms) { |
| if (!web_contents_->profile() || |
| !web_contents_->profile()->GetWebDataService(Profile::EXPLICIT_ACCESS)) |
| return; |
| if (!web_contents_->controller()) |
| return; |
| if (!*password_manager_enabled_) |
| return; |
| |
| // Ask the SSLManager for current security. |
| bool had_ssl_error = web_contents_->controller()->ssl_manager()-> |
| ProcessedSSLErrorFromRequest(); |
| |
| std::vector<PasswordForm>::const_iterator iter; |
| for (iter = forms.begin(); iter != forms.end(); iter++) { |
| if (pending_save_manager_.get() && |
| pending_save_manager_->DoesManage(*iter)) { |
| // The form trying to be saved has immediately re-appeared. Assume |
| // login failure and abort this save. Fallback to pending login state |
| // since the user may try again. |
| pending_login_managers_.push_back(pending_save_manager_.release()); |
| // Don't delete the login managers since the user may try again |
| // and we want to be able to save in that case. |
| break; |
| } else { |
| bool ssl_valid = iter->origin.SchemeIsSecure() && !had_ssl_error; |
| PasswordFormManager* manager = |
| new PasswordFormManager(web_contents_->profile(), |
| this, *iter, ssl_valid); |
| pending_login_managers_.push_back(manager); |
| manager->FetchMatchingLoginsFromWebDatabase(); |
| } |
| } |
| } |
| |
| void PasswordManager::Autofill(const PasswordForm& form_for_autofill, |
| const PasswordFormMap& best_matches, |
| const PasswordForm* const preferred_match) const { |
| DCHECK(web_contents_); |
| 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. |
| bool action_mismatch = form_for_autofill.action.GetWithEmptyPath() != |
| preferred_match->action.GetWithEmptyPath(); |
| scoped_ptr<PasswordFormDomManager::FillData> fill_data( |
| PasswordFormDomManager::CreateFillData(form_for_autofill, |
| best_matches, preferred_match, |
| action_mismatch)); |
| web_contents_->render_view_host()->FillPasswordForm(*fill_data); |
| return; |
| } |
| default: |
| if (observer_) |
| observer_->OnAutofillDataAvailable(preferred_match->username_value, |
| preferred_match->password_value); |
| } |
| } |
| |
| void PasswordManager::CloseBars() { |
| if (current_bar_) |
| current_bar_->Close(); |
| } |
| |
| void PasswordManager::ReplaceInfoBar(InfoBarItemView* bar) { |
| // TODO(brettw) The password manager should not have to know about info bars. |
| CloseBars(); |
| InfoBarView* view = web_contents_->view()->GetInfoBarView(); |
| view->AddChildView(bar); |
| current_bar_ = bar; |
| } |
| |
| PasswordManager::SavePasswordBar |
| ::SavePasswordBar(PasswordFormManager* form_manager, |
| PasswordManager* password_manager) |
| : form_manager_(form_manager), |
| password_manager_(password_manager), |
| InfoBarConfirmView( |
| l10n_util::GetString(IDS_PASSWORD_MANAGER_SAVE_PASSWORD_PROMPT)) { |
| SetOKButtonLabel(l10n_util::GetString(IDS_PASSWORD_MANAGER_SAVE_BUTTON)); |
| SetCancelButtonLabel(l10n_util::GetString( |
| IDS_PASSWORD_MANAGER_BLACKLIST_BUTTON)); |
| ResourceBundle &rb = ResourceBundle::GetSharedInstance(); |
| SetIcon(*rb.GetBitmapNamed(IDR_INFOBAR_SAVE_PASSWORD)); |
| } |
| |
| PasswordManager::SavePasswordBar::~SavePasswordBar() { |
| password_manager_->current_bar_ = NULL; |
| if (form_manager_) |
| delete form_manager_; |
| } |
| |
| void PasswordManager::SavePasswordBar::OKButtonPressed() { |
| form_manager_->Save(); |
| BeginClose(); |
| } |
| |
| void PasswordManager::SavePasswordBar::CancelButtonPressed() { |
| form_manager_->PermanentlyBlacklist(); |
| BeginClose(); |
| } |