blob: eca34dc09468032c9a120d19904169dceaba395e [file] [log] [blame]
// Copyright 2024 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/save_address_bubble_controller.h"
#include <array>
#include <optional>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/autofill/ui/ui_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/global_features.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/theme_resources.h"
#include "components/application_locale_storage/application_locale_storage.h"
#include "components/autofill/content/browser/content_autofill_client.h"
#include "components/autofill/core/browser/data_manager/personal_data_manager.h"
#include "components/autofill/core/browser/foundations/autofill_client.h"
#include "components/autofill/core/browser/ui/addresses/autofill_address_util.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/image_model.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/color/color_id.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia.h"
namespace autofill {
SaveAddressBubbleController::SaveAddressBubbleController(
base::WeakPtr<AddressBubbleControllerDelegate> delegate,
content::WebContents* web_contents,
const AutofillProfile& address_profile,
AutofillClient::SaveAddressBubbleType save_address_bubble_type)
: content::WebContentsObserver(web_contents),
delegate_(delegate),
address_profile_(address_profile),
save_address_bubble_type_(save_address_bubble_type) {}
SaveAddressBubbleController::~SaveAddressBubbleController() = default;
std::u16string SaveAddressBubbleController::GetWindowTitle() const {
return l10n_util::GetStringUTF16([this] {
switch (save_address_bubble_type_) {
case AutofillClient::SaveAddressBubbleType::kSave:
return IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_TITLE;
case autofill::AutofillClient::SaveAddressBubbleType::kMigrateToAccount:
return IDS_AUTOFILL_ACCOUNT_MIGRATE_ADDRESS_PROMPT_TITLE;
case autofill::AutofillClient::SaveAddressBubbleType::
kHomeWorkNameEmailMerge:
return IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_NAME_EMAIL_HOME_WORK_MERGE_PROMPT_TITLE;
}
}());
}
std::optional<SaveAddressBubbleController::HeaderImages>
SaveAddressBubbleController::GetHeaderImages() const {
if (IsMigrationToAccount() && web_contents()) {
std::optional<AccountInfo> account =
GetPrimaryAccountInfoFromBrowserContext(
web_contents()->GetBrowserContext());
if (account) {
// The position and size must match the implied one in the image,
// so these numbers are exclusively for ..._AVATAR50_X135_Y54.
static constexpr gfx::Point kAvatarPosition{135, 54};
static constexpr size_t kAvatarSize{50};
return HeaderImages{
.light = profiles::EmbedAvatarOntoImage(
IDR_MIGRATE_ADDRESS_AVATAR50_X135_Y54, account->account_image,
kAvatarPosition, kAvatarSize),
.dark = profiles::EmbedAvatarOntoImage(
IDR_MIGRATE_ADDRESS_AVATAR50_X135_Y54_DARK,
account->account_image, kAvatarPosition, kAvatarSize)};
}
}
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
return HeaderImages{.lottie = bundle.GetThemedLottieImageNamed(
IDR_AUTOFILL_SAVE_ADDRESS_LOTTIE)};
}
std::u16string SaveAddressBubbleController::GetBodyText() const {
if (IsMigrationToAccount() && web_contents()) {
PersonalDataManager& pdm =
ContentAutofillClient::FromWebContents(web_contents())
->GetPersonalDataManager();
std::optional<AccountInfo> account =
GetPrimaryAccountInfoFromBrowserContext(
web_contents()->GetBrowserContext());
int string_id = pdm.address_data_manager().IsSyncFeatureEnabledForAutofill()
? IDS_AUTOFILL_SYNCABLE_PROFILE_MIGRATION_PROMPT_NOTICE
: IDS_AUTOFILL_LOCAL_PROFILE_MIGRATION_PROMPT_NOTICE;
return l10n_util::GetStringFUTF16(string_id,
base::UTF8ToUTF16(account->email));
}
return {};
}
std::u16string SaveAddressBubbleController::GetAddressSummary() const {
// Use a shorter version of the address summary for migration, it has
// a fixed set of fields and doesn't depend on libaddressinput.
if (IsMigrationToAccount()) {
static constexpr std::array fields = {
NAME_FULL, ADDRESS_HOME_LINE1, EMAIL_ADDRESS, PHONE_HOME_WHOLE_NUMBER};
std::vector<std::u16string> values;
for (FieldType field : fields) {
std::u16string value =
address_profile_.GetInfo(field, g_browser_process->GetFeatures()
->application_locale_storage()
->Get());
if (!value.empty()) {
values.push_back(value);
}
}
if (values.empty()) {
return {};
}
return base::JoinString(values, u"\n");
}
return GetEnvelopeStyleAddress(
address_profile_,
g_browser_process->GetFeatures()->application_locale_storage()->Get(),
/*include_recipient=*/true, /*include_country=*/true);
}
std::u16string SaveAddressBubbleController::GetProfileEmail() const {
// Email is not shown as a separate field in the migration flow,
// it is included in the address summary, see GetAddressSummary().
if (IsMigrationToAccount()) {
return {};
}
return address_profile_.GetInfo(
EMAIL_ADDRESS,
g_browser_process->GetFeatures()->application_locale_storage()->Get());
}
std::u16string SaveAddressBubbleController::GetProfilePhone() const {
// Phone is not shown as a separate field in the migration flow,
// it is included in the address summary, see GetAddressSummary().
if (IsMigrationToAccount()) {
return {};
}
return autofill::i18n::GetFormattedPhoneNumberForDisplay(
address_profile_,
g_browser_process->GetFeatures()->application_locale_storage()->Get());
}
std::u16string SaveAddressBubbleController::GetOkButtonLabel() const {
return l10n_util::GetStringUTF16([this] {
switch (save_address_bubble_type_) {
case AutofillClient::SaveAddressBubbleType::kSave:
return IDS_AUTOFILL_EDIT_ADDRESS_DIALOG_OK_BUTTON_LABEL_SAVE;
case autofill::AutofillClient::SaveAddressBubbleType::kMigrateToAccount:
return IDS_AUTOFILL_MIGRATE_ADDRESS_DIALOG_OK_BUTTON_LABEL_SAVE;
case autofill::AutofillClient::SaveAddressBubbleType::
kHomeWorkNameEmailMerge:
return IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_NAME_EMAIL_HOME_WORK_MERGE_OK_BUTTON_LABEL;
}
}());
}
std::u16string SaveAddressBubbleController::GetNegativeButtonLabel() const {
return l10n_util::GetStringUTF16([this] {
switch (save_address_bubble_type_) {
case AutofillClient::SaveAddressBubbleType::kSave:
case autofill::AutofillClient::SaveAddressBubbleType::kMigrateToAccount:
return IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_CANCEL_BUTTON_LABEL;
case autofill::AutofillClient::SaveAddressBubbleType::
kHomeWorkNameEmailMerge:
return IDS_AUTOFILL_SAVE_ADDRESS_PROMPT_NAME_EMAIL_HOME_WORK_MERGE_CANCEL_BUTTON_LABEL;
}
}());
}
AutofillClient::AddressPromptUserDecision
SaveAddressBubbleController::GetCancelCallbackValue() const {
// The migration prompt should not be shown again if the user explicitly
// rejects it (for a particular address, due to legal and privacy
// requirements). In other cases it is acceptable to show it a few times more.
return IsMigrationToAccount()
? AutofillClient::AddressPromptUserDecision::kNever
: AutofillClient::AddressPromptUserDecision::kDeclined;
}
std::u16string SaveAddressBubbleController::GetFooterMessage() const {
if (address_profile_.IsAccountProfile() && web_contents()) {
std::optional<AccountInfo> account =
GetPrimaryAccountInfoFromBrowserContext(
web_contents()->GetBrowserContext());
return l10n_util::GetStringFUTF16(
IDS_AUTOFILL_SAVE_IN_ACCOUNT_PROMPT_ADDRESS_SOURCE_NOTICE,
base::UTF8ToUTF16(account->email));
}
return {};
}
std::u16string SaveAddressBubbleController::GetEditorFooterMessage() const {
if (IsMigrationToAccount() && web_contents()) {
std::optional<AccountInfo> account =
GetPrimaryAccountInfoFromBrowserContext(
web_contents()->GetBrowserContext());
return l10n_util::GetStringFUTF16(
IDS_AUTOFILL_SAVE_IN_ACCOUNT_PROMPT_ADDRESS_SOURCE_NOTICE,
base::UTF8ToUTF16(account->email));
}
return GetFooterMessage();
}
void SaveAddressBubbleController::OnUserDecision(
AutofillClient::AddressPromptUserDecision decision,
base::optional_ref<const AutofillProfile> profile) {
if (delegate_) {
delegate_->OnUserDecision(decision, profile);
}
}
void SaveAddressBubbleController::OnEditButtonClicked() {
if (delegate_) {
delegate_->ShowEditor(address_profile_, /*title_override=*/u"",
GetEditorFooterMessage(),
/*is_editing_existing_address=*/false);
}
}
void SaveAddressBubbleController::OnBubbleClosed() {
if (delegate_) {
delegate_->OnBubbleClosed();
}
}
bool SaveAddressBubbleController::IsMigrationToAccount() const {
return save_address_bubble_type_ ==
AutofillClient::SaveAddressBubbleType::kMigrateToAccount;
}
} // namespace autofill