blob: 08c0a344afafd04eebd87757ff7fcfd4534be99a [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/passwords/manage_passwords_state.h"
#include <algorithm>
#include <utility>
#include "base/memory/raw_ptr.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/browser/logging/log_router.h"
#include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
#include "components/password_manager/core/browser/password_form_manager_for_ui.h"
#include "components/password_manager/core/browser/password_manager.h"
#include "components/password_manager/core/browser/password_manager_client.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_util.h"
#include "components/password_manager/core/common/password_manager_ui.h"
#include "url/gurl.h"
#include "url/origin.h"
using password_manager::PasswordForm;
using password_manager::PasswordFormManagerForUI;
using password_manager_util::GetMatchType;
namespace {
std::vector<std::unique_ptr<PasswordForm>> DeepCopyMatchingCredentials(
base::span<const PasswordForm> password_forms) {
std::vector<std::unique_ptr<PasswordForm>> result;
result.reserve(password_forms.size());
for (const PasswordForm& form : password_forms) {
result.push_back(std::make_unique<PasswordForm>(form));
}
return result;
}
void AppendDeepCopyVector(base::span<const PasswordForm> forms,
std::vector<std::unique_ptr<PasswordForm>>* result) {
result->reserve(result->size() + forms.size());
for (const password_manager::PasswordForm& form : forms) {
result->push_back(std::make_unique<PasswordForm>(form));
}
}
// Updates one form in |forms| that has the same unique key as |updated_form|.
// Returns true if the form was found and updated.
bool UpdateFormInVector(const PasswordForm& updated_form,
std::vector<std::unique_ptr<PasswordForm>>* forms) {
auto it = std::ranges::find_if(
*forms, [&updated_form](const std::unique_ptr<PasswordForm>& form) {
return ArePasswordFormUniqueKeysEqual(*form, updated_form);
});
if (it != forms->end()) {
**it = updated_form;
return true;
}
return false;
}
// Removes a form from |forms| that has the same unique key as |form_to_delete|.
// Returns true iff the form was deleted.
bool RemoveFormFromVector(const PasswordForm& form_to_delete,
std::vector<std::unique_ptr<PasswordForm>>* forms) {
auto it = std::ranges::find_if(
*forms, [&form_to_delete](const std::unique_ptr<PasswordForm>& form) {
return ArePasswordFormUniqueKeysEqual(*form, form_to_delete);
});
if (it != forms->end()) {
forms->erase(it);
return true;
}
return false;
}
} // namespace
ManagePasswordsState::ManagePasswordsState()
: state_(password_manager::ui::INACTIVE_STATE), client_(nullptr) {}
ManagePasswordsState::~ManagePasswordsState() = default;
void ManagePasswordsState::OnPendingPassword(
std::unique_ptr<PasswordFormManagerForUI> form_manager) {
ClearData();
form_manager_ = std::move(form_manager);
local_credentials_forms_ =
DeepCopyMatchingCredentials(form_manager_->GetBestMatches());
AppendDeepCopyVector(form_manager_->GetFederatedMatches(),
&local_credentials_forms_);
origin_ = url::Origin::Create(form_manager_->GetURL());
SetState(password_manager::ui::PENDING_PASSWORD_STATE);
}
void ManagePasswordsState::OnUpdatePassword(
std::unique_ptr<password_manager::PasswordFormManagerForUI> form_manager) {
ClearData();
form_manager_ = std::move(form_manager);
local_credentials_forms_ =
DeepCopyMatchingCredentials(form_manager_->GetBestMatches());
AppendDeepCopyVector(form_manager_->GetFederatedMatches(),
&local_credentials_forms_);
origin_ = url::Origin::Create(form_manager_->GetURL());
SetState(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
}
void ManagePasswordsState::OnRequestCredentials(
std::vector<std::unique_ptr<PasswordForm>> local_credentials,
const url::Origin& origin) {
ClearData();
local_credentials_forms_ = std::move(local_credentials);
origin_ = origin;
SetState(password_manager::ui::CREDENTIAL_REQUEST_STATE);
}
void ManagePasswordsState::OnAutoSignin(
std::vector<std::unique_ptr<PasswordForm>> local_forms,
const url::Origin& origin) {
DCHECK(!local_forms.empty());
ClearData();
local_credentials_forms_ = std::move(local_forms);
origin_ = origin;
SetState(password_manager::ui::AUTO_SIGNIN_STATE);
}
void ManagePasswordsState::OnAutomaticPasswordSave(
std::unique_ptr<PasswordFormManagerForUI> form_manager) {
ClearData();
form_manager_ = std::move(form_manager);
local_credentials_forms_ =
DeepCopyMatchingCredentials(form_manager_->GetBestMatches());
AppendDeepCopyVector(form_manager_->GetFederatedMatches(),
&local_credentials_forms_);
origin_ = url::Origin::Create(form_manager_->GetURL());
SetState(password_manager::ui::SAVE_CONFIRMATION_STATE);
}
void ManagePasswordsState::OnSubmittedGeneratedPassword(
password_manager::ui::State state,
std::unique_ptr<password_manager::PasswordFormManagerForUI> form_manager,
password_manager::PasswordForm form_to_update) {
CHECK(state == password_manager::ui::SAVE_CONFIRMATION_STATE ||
state == password_manager::ui::UPDATE_CONFIRMATION_STATE ||
state == password_manager::ui::GENERATED_PASSWORD_CONFIRMATION_STATE);
if (form_manager) {
ClearData();
form_manager_ = std::move(form_manager);
}
local_credentials_forms_ =
DeepCopyMatchingCredentials(form_manager_->GetBestMatches());
AppendDeepCopyVector(form_manager_->GetFederatedMatches(),
&local_credentials_forms_);
// When confirmation bubble for the added username is shown, the old
// credential(without the added username) can be still present in
// best_matches, if FormFetcher didn't finish fetching passwords yet. It needs
// to be removed before showing the confirmation bubble.
std::erase_if(local_credentials_forms_,
[form_to_update](const std::unique_ptr<PasswordForm>& form) {
return ArePasswordFormUniqueKeysEqual(form_to_update, *form);
});
// In the confirmation state, a list of saved passwords is displayed, and that
// list should contain the just added one. This step should be skipped when
// pending password is already present in the `local_credentials_forms_`. That
// can happen when this is a confirmation of a password update done via
// CredentialManager.
auto it = std::ranges::find_if(
local_credentials_forms_,
[this](const std::unique_ptr<PasswordForm>& form) {
return ArePasswordFormUniqueKeysEqual(
*form, form_manager_->GetPendingCredentials());
});
if (it == local_credentials_forms_.end()) {
local_credentials_forms_.push_back(
std::make_unique<PasswordForm>(form_manager_->GetPendingCredentials()));
}
origin_ = url::Origin::Create(form_manager_->GetURL());
SetState(state);
}
void ManagePasswordsState::OnPasswordAutofilled(
base::span<const PasswordForm> password_forms,
url::Origin origin,
base::span<const PasswordForm> federated_matches) {
CHECK(!password_forms.empty() || !federated_matches.empty());
auto local_credentials_forms = DeepCopyMatchingCredentials(password_forms);
AppendDeepCopyVector(federated_matches, &local_credentials_forms);
// Delete |form_manager_| only when the parameters are processed. They may be
// coming from |form_manager_|.
ClearData();
if (local_credentials_forms.empty()) {
OnInactive();
} else {
origin_ = std::move(origin);
local_credentials_forms_ = std::move(local_credentials_forms);
SetState(password_manager::ui::MANAGE_STATE);
}
}
void ManagePasswordsState::OnInactive() {
ClearData();
origin_ = url::Origin();
SetState(password_manager::ui::INACTIVE_STATE);
}
void ManagePasswordsState::OnPasswordMovable(
std::unique_ptr<PasswordFormManagerForUI> form_to_move) {
ClearData();
form_manager_ = std::move(form_to_move);
local_credentials_forms_ =
DeepCopyMatchingCredentials(form_manager_->GetBestMatches());
AppendDeepCopyVector(form_manager_->GetFederatedMatches(),
&local_credentials_forms_);
origin_ = url::Origin::Create(form_manager_->GetURL());
SetState(password_manager::ui::MOVE_CREDENTIAL_AFTER_LOG_IN_STATE);
}
void ManagePasswordsState::OnKeychainError() {
ClearData();
SetState(password_manager::ui::KEYCHAIN_ERROR_STATE);
}
void ManagePasswordsState::OnPasskeySaved(bool gpm_pin_created,
std::string passkey_rp_id) {
ClearData();
gpm_pin_created_during_recent_passkey_creation_ = gpm_pin_created;
passkey_rp_id_ = std::move(passkey_rp_id);
SetState(password_manager::ui::PASSKEY_SAVED_CONFIRMATION_STATE);
}
void ManagePasswordsState::OnPasskeyDeleted() {
ClearData();
SetState(password_manager::ui::PASSKEY_DELETED_CONFIRMATION_STATE);
}
void ManagePasswordsState::OnPasskeyUpdated(std::string passkey_rp_id) {
ClearData();
passkey_rp_id_ = std::move(passkey_rp_id);
SetState(password_manager::ui::PASSKEY_UPDATED_CONFIRMATION_STATE);
}
void ManagePasswordsState::OnPasskeyNotAccepted(std::string passkey_rp_id) {
ClearData();
passkey_rp_id_ = std::move(passkey_rp_id);
SetState(password_manager::ui::PASSKEY_NOT_ACCEPTED_STATE);
}
void ManagePasswordsState::OnPasskeyUpgrade(std::string passkey_rp_id) {
ClearData();
passkey_rp_id_ = std::move(passkey_rp_id);
SetState(password_manager::ui::PASSKEY_UPGRADE_STATE);
}
void ManagePasswordsState::TransitionToState(
password_manager::ui::State state) {
CHECK_NE(password_manager::ui::INACTIVE_STATE, state_);
CHECK(state == password_manager::ui::MANAGE_STATE ||
state == password_manager::ui::PENDING_PASSWORD_STATE ||
state == password_manager::ui::PASSWORD_UPDATED_SAFE_STATE ||
state == password_manager::ui::PASSWORD_UPDATED_MORE_TO_FIX ||
state ==
password_manager::ui::BIOMETRIC_AUTHENTICATION_FOR_FILLING_STATE ||
state ==
password_manager::ui::BIOMETRIC_AUTHENTICATION_CONFIRMATION_STATE ||
state == password_manager::ui::NOTIFY_RECEIVED_SHARED_CREDENTIALS ||
state == password_manager::ui::MOVE_CREDENTIAL_FROM_MANAGE_BUBBLE_STATE)
<< state_;
if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
if (!credentials_callback_.is_null()) {
std::move(credentials_callback_).Run(nullptr);
}
}
SetState(state);
}
void ManagePasswordsState::ProcessLoginsChanged(
const password_manager::PasswordStoreChangeList& changes) {
if (state() == password_manager::ui::INACTIVE_STATE) {
return;
}
bool applied_delete = false;
bool all_changes_are_deletion = true;
for (const password_manager::PasswordStoreChange& change : changes) {
if (change.type() != password_manager::PasswordStoreChange::REMOVE) {
all_changes_are_deletion = false;
}
const PasswordForm& changed_form = change.form();
if (changed_form.blocked_by_user) {
continue;
}
if (change.type() == password_manager::PasswordStoreChange::REMOVE) {
if (RemoveFormFromVector(changed_form, &local_credentials_forms_)) {
applied_delete = true;
}
} else if (change.type() == password_manager::PasswordStoreChange::UPDATE) {
UpdateFormInVector(changed_form, &local_credentials_forms_);
} else {
DCHECK_EQ(password_manager::PasswordStoreChange::ADD, change.type());
AddForm(changed_form);
}
}
// Let the password manager know that it should update the list of the
// credentials. We react only to deletion because in case the password manager
// itself adds a credential, they should not be refetched. The password
// generation can be confused as the generated password will be refetched and
// autofilled immediately.
if (applied_delete && all_changes_are_deletion) {
client_->UpdateFormManagers();
}
}
void ManagePasswordsState::ChooseCredential(const PasswordForm* form) {
DCHECK_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, state());
DCHECK(!credentials_callback_.is_null());
std::move(credentials_callback_).Run(form);
}
void ManagePasswordsState::OpenPasswordDetailsBubble(
const password_manager::PasswordForm& form) {
single_credential_mode_credential_ = form;
SetState(password_manager::ui::State::MANAGE_STATE);
}
void ManagePasswordsState::OpenPasswordChangedBubble(
const std::u16string& username,
const std::u16string& new_password) {
password_change_username_ = username;
password_change_new_password_ = new_password;
SetState(password_manager::ui::State::PASSWORD_CHANGE_STATE);
}
void ManagePasswordsState::ClearData() {
form_manager_.reset();
clear_selected_password();
local_credentials_forms_.clear();
credentials_callback_.Reset();
single_credential_mode_credential_.reset();
gpm_pin_created_during_recent_passkey_creation_ = false;
passkey_rp_id_.clear();
password_change_username_.clear();
password_change_new_password_.clear();
}
bool ManagePasswordsState::AddForm(const PasswordForm& form) {
if (url::Origin::Create(form.url) != origin_) {
return false;
}
if (UpdateFormInVector(form, &local_credentials_forms_)) {
return true;
}
local_credentials_forms_.push_back(std::make_unique<PasswordForm>(form));
return true;
}
void ManagePasswordsState::SetState(password_manager::ui::State state) {
DCHECK(client_);
autofill::LogManager* log_manager = client_->GetCurrentLogManager();
if (log_manager && log_manager->IsLoggingActive()) {
password_manager::BrowserSavePasswordProgressLogger logger(log_manager);
logger.LogNumber(autofill::SavePasswordProgressLogger::STRING_NEW_UI_STATE,
state);
}
state_ = state;
}