blob: 8dc18d229defb1fdb8aa7ace3060fa1ee6de6f4c [file] [log] [blame]
// Copyright 2019 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/autofill/address_accessory_controller_impl.h"
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
#include "chrome/browser/autofill/manual_filling_controller.h"
#include "chrome/browser/autofill/manual_filling_utils.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
namespace {
// Defines which types to load from the Personal data manager and add as field
// to the address sheet. Order matters.
constexpr ServerFieldType kTypesToInclude[] = {
ServerFieldType::NAME_FULL,
ServerFieldType::COMPANY_NAME,
ServerFieldType::ADDRESS_HOME_LINE1,
ServerFieldType::ADDRESS_HOME_LINE2,
ServerFieldType::ADDRESS_HOME_ZIP,
ServerFieldType::ADDRESS_HOME_CITY,
ServerFieldType::ADDRESS_HOME_STATE,
ServerFieldType::ADDRESS_HOME_COUNTRY,
ServerFieldType::PHONE_HOME_WHOLE_NUMBER,
ServerFieldType::EMAIL_ADDRESS,
};
void AddProfileInfoAsSelectableField(UserInfo* info,
const AutofillProfile* profile,
ServerFieldType type) {
std::u16string field = profile->GetRawInfo(type);
if (type == ServerFieldType::NAME_MIDDLE && field.empty()) {
field = profile->GetRawInfo(ServerFieldType::NAME_MIDDLE_INITIAL);
}
info->add_field(AccessorySheetField(
/*display_text=*/field, /*text_to_fill=*/field,
/*a11y_description=*/field, /*id=*/std::string(), /*is_obfuscated=*/false,
/*selectable=*/true));
}
UserInfo TranslateProfile(const AutofillProfile* profile) {
UserInfo info;
for (ServerFieldType server_field_type : kTypesToInclude) {
AddProfileInfoAsSelectableField(&info, profile, server_field_type);
}
return info;
}
std::vector<UserInfo> UserInfosForProfiles(
const std::vector<AutofillProfile*>& profiles) {
std::vector<UserInfo> infos(profiles.size());
base::ranges::transform(profiles, infos.begin(), TranslateProfile);
return infos;
}
std::vector<FooterCommand> CreateManageAddressesFooter() {
return {FooterCommand(
l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK),
AccessoryAction::MANAGE_ADDRESSES)};
}
} // namespace
AddressAccessoryControllerImpl::~AddressAccessoryControllerImpl() {
if (personal_data_manager_)
personal_data_manager_->RemoveObserver(this);
}
// static
bool AddressAccessoryController::AllowedForWebContents(
content::WebContents* web_contents) {
DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
return base::FeatureList::IsEnabled(
autofill::features::kAutofillKeyboardAccessory);
}
// static
AddressAccessoryController* AddressAccessoryController::GetOrCreate(
content::WebContents* web_contents) {
DCHECK(AddressAccessoryController::AllowedForWebContents(web_contents));
AddressAccessoryControllerImpl::CreateForWebContents(web_contents);
return AddressAccessoryControllerImpl::FromWebContents(web_contents);
}
void AddressAccessoryControllerImpl::RegisterFillingSourceObserver(
FillingSourceObserver observer) {
source_observer_ = std::move(observer);
}
absl::optional<autofill::AccessorySheetData>
AddressAccessoryControllerImpl::GetSheetData() const {
if (!personal_data_manager_) {
return absl::nullopt;
}
std::vector<AutofillProfile*> profiles =
personal_data_manager_->GetProfilesToSuggest();
std::u16string title_or_empty_message;
if (profiles.empty()) {
title_or_empty_message =
l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
}
return autofill::CreateAccessorySheetData(
autofill::AccessoryTabType::ADDRESSES, title_or_empty_message,
UserInfosForProfiles(profiles), CreateManageAddressesFooter());
}
void AddressAccessoryControllerImpl::OnFillingTriggered(
FieldGlobalId focused_field_id,
const AccessorySheetField& selection) {
// Since the data we fill is scoped to the profile and not to a frame, we can
// fill the focused frame - we basically behave like a keyboard here.
content::RenderFrameHost* rfh = GetWebContents().GetFocusedFrame();
if (!rfh)
return;
autofill::ContentAutofillDriver* driver =
autofill::ContentAutofillDriver::GetForRenderFrameHost(rfh);
if (!driver)
return;
driver->browser_events().RendererShouldFillFieldWithValue(
focused_field_id, selection.display_text());
}
void AddressAccessoryControllerImpl::OnOptionSelected(
AccessoryAction selected_action) {
if (selected_action == AccessoryAction::MANAGE_ADDRESSES) {
autofill::ShowAutofillProfileSettings(&GetWebContents());
return;
}
NOTREACHED() << "Unhandled selected action: "
<< static_cast<int>(selected_action);
}
void AddressAccessoryControllerImpl::OnToggleChanged(
AccessoryAction toggled_action,
bool enabled) {
NOTREACHED() << "Unhandled toggled action: "
<< static_cast<int>(toggled_action);
}
void AddressAccessoryControllerImpl::RefreshSuggestions() {
TRACE_EVENT0("passwords",
"AddressAccessoryControllerImpl::RefreshSuggestions");
if (!personal_data_manager_) {
personal_data_manager_ =
autofill::PersonalDataManagerFactory::GetForProfile(
Profile::FromBrowserContext(GetWebContents().GetBrowserContext()));
personal_data_manager_->AddObserver(this);
}
if (source_observer_) {
source_observer_.Run(
this, IsFillingSourceAvailable(
personal_data_manager_ &&
!personal_data_manager_->GetProfilesToSuggest().empty()));
} else {
// TODO(crbug.com/1169167): Remove once filling controller pulls this
// information instead of waiting to get it pushed.
absl::optional<AccessorySheetData> data = GetSheetData();
DCHECK(data.has_value());
GetManualFillingController()->RefreshSuggestions(std::move(data).value());
}
}
void AddressAccessoryControllerImpl::OnPersonalDataChanged() {
RefreshSuggestions();
}
// static
void AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
content::WebContents* web_contents,
base::WeakPtr<ManualFillingController> mf_controller) {
DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
DCHECK(!FromWebContents(web_contents)) << "Controller already attached!";
DCHECK(mf_controller);
web_contents->SetUserData(UserDataKey(),
base::WrapUnique(new AddressAccessoryControllerImpl(
web_contents, std::move(mf_controller))));
}
AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
content::WebContents* web_contents)
: AddressAccessoryControllerImpl(web_contents, nullptr) {}
// Additional creation functions in unit tests only:
AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
content::WebContents* web_contents,
base::WeakPtr<ManualFillingController> mf_controller)
: content::WebContentsUserData<AddressAccessoryControllerImpl>(
*web_contents),
mf_controller_(std::move(mf_controller)),
personal_data_manager_(nullptr) {}
base::WeakPtr<ManualFillingController>
AddressAccessoryControllerImpl::GetManualFillingController() {
if (!mf_controller_)
mf_controller_ = ManualFillingController::GetOrCreate(&GetWebContents());
DCHECK(mf_controller_);
return mf_controller_;
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(AddressAccessoryControllerImpl);
} // namespace autofill