blob: d25c5f1e389d5bba5e04825c930a0673f56346d0 [file] [log] [blame]
// Copyright 2023 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/autofill/payments/iban_bubble_controller_impl.h"
#include <string>
#include <string_view>
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
#include "chrome/browser/ui/autofill/autofill_bubble_handler.h"
#include "chrome/browser/ui/autofill/payments/save_iban_ui.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/page_action/page_action_icon_type.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/autofill/core/browser/data_manager/personal_data_manager.h"
#include "components/autofill/core/browser/data_model/payments/iban.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "components/autofill/core/browser/metrics/payments/iban_metrics.h"
#include "components/autofill/core/browser/ui/payments/payments_ui_closed_reasons.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
// static
IbanBubbleController* IbanBubbleController::GetOrCreate(
content::WebContents* web_contents) {
if (!web_contents) {
return nullptr;
}
IbanBubbleControllerImpl::CreateForWebContents(web_contents);
return IbanBubbleControllerImpl::FromWebContents(web_contents);
}
IbanBubbleControllerImpl::~IbanBubbleControllerImpl() = default;
void IbanBubbleControllerImpl::OfferLocalSave(
const Iban& iban,
bool should_show_prompt,
payments::PaymentsAutofillClient::SaveIbanPromptCallback
save_iban_prompt_callback) {
// Don't show the bubble if it's already visible.
if (bubble_view() || !MaySetUpBubble()) {
return;
}
SetupLocalSave(iban, std::move(save_iban_prompt_callback));
if (should_show_prompt) {
QueueOrShowBubble();
} else {
ShowIconOnly();
}
}
void IbanBubbleControllerImpl::OfferUploadSave(
const Iban& iban,
LegalMessageLines legal_message_lines,
bool should_show_prompt,
payments::PaymentsAutofillClient::SaveIbanPromptCallback
save_iban_prompt_callback) {
// Don't show the bubble if it's already visible.
if (bubble_view() || !MaySetUpBubble()) {
return;
}
SetupUploadSave(iban, std::move(legal_message_lines),
std::move(save_iban_prompt_callback));
if (should_show_prompt) {
QueueOrShowBubble();
} else {
ShowIconOnly();
}
}
void IbanBubbleControllerImpl::SetupLocalSave(
Iban iban,
payments::PaymentsAutofillClient::SaveIbanPromptCallback
save_iban_prompt_callback) {
was_bubble_shown_ = false;
iban_ = std::move(iban);
is_reshow_ = false;
is_upload_save_ = false;
legal_message_lines_.clear();
save_iban_prompt_callback_ = std::move(save_iban_prompt_callback);
current_bubble_type_ = IbanBubbleType::kLocalSave;
// Save callback should not be null for IBAN save.
CHECK(!save_iban_prompt_callback_.is_null());
}
void IbanBubbleControllerImpl::SetupUploadSave(
Iban iban,
LegalMessageLines legal_message_lines,
payments::PaymentsAutofillClient::SaveIbanPromptCallback
save_iban_prompt_callback) {
was_bubble_shown_ = false;
iban_ = std::move(iban);
is_reshow_ = false;
is_upload_save_ = true;
legal_message_lines_ = std::move(legal_message_lines);
save_iban_prompt_callback_ = std::move(save_iban_prompt_callback);
current_bubble_type_ = IbanBubbleType::kUploadSave;
// Save callback should not be null for IBAN save.
CHECK(!save_iban_prompt_callback_.is_null());
}
void IbanBubbleControllerImpl::ReshowBubble() {
// Don't show the bubble if it's already visible.
if (bubble_view()) {
return;
}
is_reshow_ = true;
CHECK(current_bubble_type_ != IbanBubbleType::kInactive);
if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
current_bubble_type_ == IbanBubbleType::kUploadSave) {
CHECK(!save_iban_prompt_callback_.is_null());
} else {
CHECK(current_bubble_type_ == IbanBubbleType::kManageSavedIban ||
current_bubble_type_ == IbanBubbleType::kUploadInProgress);
}
QueueOrShowBubble(/*force_show=*/true);
}
void IbanBubbleControllerImpl::ShowConfirmationBubbleView(
bool iban_saved,
bool hit_max_strikes) {
DoNotShowNextQueuedBubbleGuard guard = DoNotShowNextQueuedBubble();
// Hide the current bubble if still showing.
HideBubble(/*initiated_by_bubble_manager=*/false);
is_reshow_ = false;
current_bubble_type_ = IbanBubbleType::kUploadCompleted;
confirmation_ui_params_ =
iban_saved ? SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams::
CreateForSaveIbanSuccess()
: SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams::
CreateForSaveIbanFailure(hit_max_strikes);
// Show upload confirmation bubble.
AutofillBubbleHandler* autofill_bubble_handler =
chrome::FindBrowserWithTab(web_contents())
->window()
->GetAutofillBubbleHandler();
SetBubbleView(*autofill_bubble_handler->ShowSaveIbanConfirmationBubble(
web_contents(), this));
// Auto close confirmation bubble when IBAN saved is successful.
if (iban_saved) {
auto_close_confirmation_timer_.Start(
FROM_HERE, kAutoCloseConfirmationBubbleWaitSec,
base::BindOnce(&IbanBubbleControllerImpl::HideBubble,
base::Unretained(this),
/*initiated_by_bubble_manager=*/false));
}
}
std::u16string IbanBubbleControllerImpl::GetWindowTitle() const {
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_IBAN_PROMPT_TITLE_LOCAL);
case IbanBubbleType::kUploadSave:
case IbanBubbleType::kUploadInProgress:
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_IBAN_PROMPT_TITLE_SERVER);
case IbanBubbleType::kManageSavedIban:
return l10n_util::GetStringUTF16(IDS_AUTOFILL_IBAN_SAVED);
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kInactive:
NOTREACHED();
}
}
std::u16string IbanBubbleControllerImpl::GetExplanatoryMessage() const {
if (current_bubble_type_ == IbanBubbleType::kUploadSave) {
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_UPLOAD_IBAN_PROMPT_EXPLANATION);
}
return std::u16string();
}
std::u16string IbanBubbleControllerImpl::GetAcceptButtonText() const {
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
case IbanBubbleType::kUploadSave:
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_IBAN_BUBBLE_SAVE_ACCEPT);
case IbanBubbleType::kManageSavedIban:
return l10n_util::GetStringUTF16(IDS_AUTOFILL_DONE);
case IbanBubbleType::kUploadInProgress:
return std::u16string();
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kInactive:
NOTREACHED();
}
}
std::u16string IbanBubbleControllerImpl::GetDeclineButtonText() const {
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
case IbanBubbleType::kUploadSave:
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_SAVE_IBAN_BUBBLE_SAVE_NO_THANKS);
case IbanBubbleType::kUploadInProgress:
return std::u16string();
case IbanBubbleType::kManageSavedIban:
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kInactive:
NOTREACHED();
}
}
AccountInfo IbanBubbleControllerImpl::GetAccountInfo() {
// The results of this call should not be cached because the user can update
// their account info at any time.
Profile* profile = GetProfile();
if (!profile) {
return AccountInfo();
}
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile);
if (!identity_manager) {
return AccountInfo();
}
PersonalDataManager* personal_data_manager =
PersonalDataManagerFactory::GetForBrowserContext(profile);
if (!personal_data_manager) {
return AccountInfo();
}
return identity_manager->FindExtendedAccountInfo(
personal_data_manager->payments_data_manager()
.GetAccountInfoForPaymentsServer());
}
const Iban& IbanBubbleControllerImpl::GetIban() const {
return iban_;
}
base::OnceCallback<void(PaymentsUiClosedReason)>
IbanBubbleControllerImpl::GetOnBubbleClosedCallback() {
return base::BindOnce(&IbanBubbleControllerImpl::OnBubbleClosed,
weak_ptr_factory_.GetWeakPtr());
}
void IbanBubbleControllerImpl::OnAcceptButton(std::u16string_view nickname) {
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
CHECK(!save_iban_prompt_callback_.is_null());
// Show an animated IBAN saved confirmation message next time
// UpdatePageActionIcon() is called.
should_show_iban_saved_label_animation_ = true;
autofill_metrics::LogSaveIbanPromptResultSavedWithNicknameMetric(
!nickname.empty(), /*is_upload_save=*/false);
iban_.set_nickname(std::u16string(nickname));
std::move(save_iban_prompt_callback_)
.Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
kAccepted,
nickname);
return;
case IbanBubbleType::kUploadSave:
CHECK(!save_iban_prompt_callback_.is_null());
autofill_metrics::LogSaveIbanPromptResultSavedWithNicknameMetric(
!nickname.empty(), /*is_upload_save=*/true);
// Log metrics now for the upload save IBAN. The upload case is special
// because we don't immediately close the bubble (at which time the other
// metrics are logged) after OnAcceptButton() and logging now aligns the
// timing of the log with the other cases.
autofill_metrics::LogSaveIbanPromptResultMetric(
autofill_metrics::SaveIbanPromptResult::kAccepted, is_reshow_,
/*is_upload_save=*/true);
iban_.set_nickname(std::u16string(nickname));
current_bubble_type_ = IbanBubbleType::kUploadInProgress;
std::move(save_iban_prompt_callback_)
.Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
kAccepted,
nickname);
return;
case IbanBubbleType::kManageSavedIban:
return;
case IbanBubbleType::kUploadInProgress:
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kInactive:
NOTREACHED();
}
}
void IbanBubbleControllerImpl::OnLegalMessageLinkClicked(const GURL& url) {
web_contents()->OpenURL(
content::OpenURLParams(url, content::Referrer(),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_LINK, false),
/*navigation_handle_callback=*/{});
}
void IbanBubbleControllerImpl::OnManageSavedIbanExtraButtonClicked() {
CHECK(current_bubble_type_ == IbanBubbleType::kManageSavedIban);
chrome::ShowSettingsSubPage(chrome::FindBrowserWithTab(web_contents()),
chrome::kPaymentsSubPage);
OnBubbleClosed(PaymentsUiClosedReason::kClosed);
}
void IbanBubbleControllerImpl::OnBubbleDiscarded() {
LogBubbleCloseMetrics(was_bubble_shown_
? PaymentsUiClosedReason::kNotInteracted
: PaymentsUiClosedReason::kUnknown);
}
void IbanBubbleControllerImpl::LogBubbleCloseMetrics(
PaymentsUiClosedReason closed_reason) {
auto get_metric = [](PaymentsUiClosedReason reason) {
switch (reason) {
case PaymentsUiClosedReason::kAccepted:
return autofill_metrics::SaveIbanPromptResult::kAccepted;
case PaymentsUiClosedReason::kCancelled:
return autofill_metrics::SaveIbanPromptResult::kCancelled;
case PaymentsUiClosedReason::kClosed:
return autofill_metrics::SaveIbanPromptResult::kClosed;
case PaymentsUiClosedReason::kNotInteracted:
return autofill_metrics::SaveIbanPromptResult::kNotInteracted;
case PaymentsUiClosedReason::kLostFocus:
return autofill_metrics::SaveIbanPromptResult::kLostFocus;
case PaymentsUiClosedReason::kUnknown:
return autofill_metrics::SaveIbanPromptResult::kUnknown;
}
};
// Log save IBAN prompt result according to the closed reason.
if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
current_bubble_type_ == IbanBubbleType::kUploadSave ||
current_bubble_type_ == IbanBubbleType::kUploadInProgress) {
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
case IbanBubbleType::kUploadSave:
autofill_metrics::LogSaveIbanPromptResultMetric(
get_metric(closed_reason), is_reshow_,
/*is_upload_save=*/
(current_bubble_type_ == IbanBubbleType::kUploadSave));
break;
case IbanBubbleType::kInactive:
case IbanBubbleType::kManageSavedIban:
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kUploadInProgress:
break;
}
}
}
void IbanBubbleControllerImpl::OnBubbleClosed(
PaymentsUiClosedReason closed_reason) {
if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
current_bubble_type_ == IbanBubbleType::kUploadSave) {
if (closed_reason == PaymentsUiClosedReason::kCancelled) {
std::move(save_iban_prompt_callback_)
.Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
kDeclined,
/*nickname=*/u"");
} else if (closed_reason == PaymentsUiClosedReason::kClosed) {
std::move(save_iban_prompt_callback_)
.Run(payments::PaymentsAutofillClient::SaveIbanOfferUserDecision::
kIgnored,
/*nickname=*/u"");
}
}
ResetBubbleViewAndInformBubbleManager();
if (!bubble_hide_initiated_by_bubble_manager_) {
LogBubbleCloseMetrics(closed_reason);
}
if (current_bubble_type_ == IbanBubbleType::kUploadCompleted) {
current_bubble_type_ = IbanBubbleType::kInactive;
UpdatePageActionIcon();
confirmation_ui_params_.reset();
return;
}
// Handles `current_bubble_type_` change according to its current type and the
// `closed_reason`.
if (closed_reason == PaymentsUiClosedReason::kAccepted) {
if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
current_bubble_type_ == IbanBubbleType::kUploadSave) {
current_bubble_type_ = IbanBubbleType::kManageSavedIban;
} else {
current_bubble_type_ = IbanBubbleType::kInactive;
}
} else if (closed_reason == PaymentsUiClosedReason::kCancelled ||
closed_reason == PaymentsUiClosedReason::kClosed) {
if (current_bubble_type_ != IbanBubbleType::kManageSavedIban) {
current_bubble_type_ = IbanBubbleType::kInactive;
}
}
UpdatePageActionIcon();
confirmation_ui_params_.reset();
}
IbanBubbleControllerImpl::IbanBubbleControllerImpl(
content::WebContents* web_contents)
: AutofillBubbleControllerBase(web_contents),
content::WebContentsUserData<IbanBubbleControllerImpl>(*web_contents) {}
IbanBubbleType IbanBubbleControllerImpl::GetIbanBubbleType() const {
return current_bubble_type_;
}
std::u16string IbanBubbleControllerImpl::GetSavePaymentIconTooltipText() const {
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
case IbanBubbleType::kUploadSave:
case IbanBubbleType::kManageSavedIban:
return l10n_util::GetStringUTF16(IDS_TOOLTIP_SAVE_IBAN);
case IbanBubbleType::kUploadInProgress:
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kInactive:
return std::u16string();
}
}
bool IbanBubbleControllerImpl::ShouldShowSavingPaymentAnimation() const {
return false;
}
bool IbanBubbleControllerImpl::ShouldShowPaymentSavedLabelAnimation() const {
return should_show_iban_saved_label_animation_;
}
void IbanBubbleControllerImpl::OnAnimationEnded() {
// Do not repeat the animation next time UpdatePageActionIcon() is called,
// unless explicitly set somewhere else.
should_show_iban_saved_label_animation_ = false;
}
bool IbanBubbleControllerImpl::IsIconVisible() const {
// If there is no bubble to show, then there should be no icon.
return current_bubble_type_ != IbanBubbleType::kInactive;
}
AutofillBubbleBase* IbanBubbleControllerImpl::GetPaymentBubbleView() const {
return bubble_view();
}
int IbanBubbleControllerImpl::GetSaveSuccessAnimationStringId() const {
return IDS_AUTOFILL_IBAN_SAVED;
}
const SavePaymentMethodAndVirtualCardEnrollConfirmationUiParams&
IbanBubbleControllerImpl::GetConfirmationUiParams() const {
CHECK(confirmation_ui_params_.has_value());
return confirmation_ui_params_.value();
}
std::optional<PageActionIconType>
IbanBubbleControllerImpl::GetPageActionIconType() {
return PageActionIconType::kSaveIban;
}
void IbanBubbleControllerImpl::DoShowBubble() {
Browser* browser = chrome::FindBrowserWithTab(web_contents());
AutofillBubbleHandler* autofill_bubble_handler =
browser->window()->GetAutofillBubbleHandler();
SetBubbleView(*autofill_bubble_handler->ShowIbanBubble(
web_contents(), this,
/*is_user_gesture=*/is_reshow_, current_bubble_type_));
CHECK(bubble_view());
CHECK(current_bubble_type_ != IbanBubbleType::kInactive);
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
autofill_metrics::LogSaveIbanPromptOfferMetric(
autofill_metrics::SaveIbanPromptOffer::kShown, is_reshow_,
/*is_upload_save=*/false);
break;
case IbanBubbleType::kUploadSave:
autofill_metrics::LogSaveIbanPromptOfferMetric(
autofill_metrics::SaveIbanPromptOffer::kShown, is_reshow_,
/*is_upload_save=*/true);
break;
case IbanBubbleType::kManageSavedIban:
// TODO(crbug.com/40233611): Add metrics for manage saved IBAN mode.
break;
case IbanBubbleType::kUploadInProgress:
break;
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kInactive:
NOTREACHED();
}
if (observer_for_testing_) {
observer_for_testing_->OnBubbleShown();
}
}
bool IbanBubbleControllerImpl::CanBeReshown() const {
return current_bubble_type_ != IbanBubbleType::kUploadCompleted;
}
BubbleType IbanBubbleControllerImpl::GetBubbleType() const {
return BubbleType::kSaveIban;
}
base::WeakPtr<BubbleControllerBase>
IbanBubbleControllerImpl::GetBubbleControllerBaseWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
Profile* IbanBubbleControllerImpl::GetProfile() {
if (!web_contents()) {
return nullptr;
}
return Profile::FromBrowserContext(web_contents()->GetBrowserContext());
}
void IbanBubbleControllerImpl::ShowIconOnly() {
CHECK(current_bubble_type_ != IbanBubbleType::kInactive);
if (current_bubble_type_ == IbanBubbleType::kLocalSave ||
current_bubble_type_ == IbanBubbleType::kUploadSave) {
CHECK(!save_iban_prompt_callback_.is_null());
} else {
CHECK(current_bubble_type_ == IbanBubbleType::kManageSavedIban);
}
CHECK(!bubble_view());
// Show the icon only. The bubble can still be displayed if the user
// explicitly clicks the icon.
UpdatePageActionIcon();
switch (current_bubble_type_) {
case IbanBubbleType::kLocalSave:
autofill_metrics::LogSaveIbanPromptOfferMetric(
autofill_metrics::SaveIbanPromptOffer::kNotShownMaxStrikesReached,
is_reshow_, /*is_upload_save=*/false);
break;
case IbanBubbleType::kUploadSave:
autofill_metrics::LogSaveIbanPromptOfferMetric(
autofill_metrics::SaveIbanPromptOffer::kNotShownMaxStrikesReached,
is_reshow_, /*is_upload_save=*/true);
break;
case IbanBubbleType::kManageSavedIban:
case IbanBubbleType::kUploadCompleted:
case IbanBubbleType::kUploadInProgress:
break;
case IbanBubbleType::kInactive:
NOTREACHED();
}
if (observer_for_testing_) {
observer_for_testing_->OnIconShown();
}
}
bool IbanBubbleControllerImpl::IsUploadSave() const {
return is_upload_save_;
}
const LegalMessageLines& IbanBubbleControllerImpl::GetLegalMessageLines()
const {
return legal_message_lines_;
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(IbanBubbleControllerImpl);
} // namespace autofill