Commit 6b305d1a authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

Reland "[MFill Android] Retrieve addresses for sheet on native side"

This is a reland of 2385ddd3

Original change's description:
> [MFill Android] Retrieve addresses for sheet on native side
>
> This class adds the Address controller that collects data for the manual
> fallback sheet on Android.
> It does work similar to https://crrev.com/c/1599927, and the existing
> PasswordAccessoryController. All three classes should be fairly similar
> in behavior.
>
> The interface leans on the draft implementation of the bridge which
> currently pulls data and might need changing to pushing data - I am open
> for suggestions.
>
> Bug: 962548
> Change-Id: Id4c593a7af9eda815be16a85354bcfe241bc731f
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1614200
> Commit-Queue: Friedrich [CET] <fhorschig@chromium.org>
> Reviewed-by: Tommy Martino <tmartino@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#662658}
TBR=tmartino@chromium.org

Bug: 962548
Change-Id: I67a78eb3c8097ba7783f3c6f369f491be337bef5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1628609Reviewed-by: default avatarFriedrich [CET] <fhorschig@chromium.org>
Reviewed-by: default avatarVasilii Sukhanov <vasilii@chromium.org>
Commit-Queue: Friedrich [CET] <fhorschig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#663031}
parent a4912b2b
......@@ -2612,6 +2612,9 @@ jumbo_split_static_library("browser") {
"android/widget/thumbnail_generator.cc",
"android/widget/thumbnail_generator.h",
"autofill/accessory_controller.h",
"autofill/address_accessory_controller.h",
"autofill/address_accessory_controller_impl.cc",
"autofill/address_accessory_controller_impl.h",
"autofill/android/personal_data_manager_android.cc",
"autofill/android/personal_data_manager_android.h",
"autofill/android/phone_number_util_android.cc",
......
// Copyright 2019 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.
#ifndef CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
#define CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/autofill/accessory_controller.h"
#include "content/public/browser/web_contents_user_data.h"
namespace autofill {
// Interface for address-specific keyboard accessory controller between the
// ManualFillingController and the autofill backend.
//
// There is a single instance per WebContents that can be accessed by calling:
// AddressAccessoryController::GetOrCreate(web_contents);
// On the first call, an instance is attached to |web_contents|, so it can be
// returned by subsequent calls.
class AddressAccessoryController
: public base::SupportsWeakPtr<AddressAccessoryController>,
public AccessoryController {
public:
AddressAccessoryController() = default;
~AddressAccessoryController() override = default;
// Returns true if the accessory controller may exist for |web_contents|.
// Otherwise it returns false.
static bool AllowedForWebContents(content::WebContents* web_contents);
// Returns a reference to the unique AddressAccessoryController associated
// with |web_contents|. A new instance is created if the first time this
// function is called. Only valid to be called if
// |AddressAccessoryController::AllowedForWebContents(web_contents)|.
static AddressAccessoryController* GetOrCreate(
content::WebContents* web_contents);
// Returns a reference to the unique AddressAccessoryController associated
// with |web_contents|. Returns null if no such instance exists.
static AddressAccessoryController* GetIfExisting(
content::WebContents* web_contents);
// Fetches suggestions and propagates them to the frontend.
virtual void RefreshSuggestions() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(AddressAccessoryController);
};
} // namespace autofill
#endif // CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_H_
// Copyright 2019 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/autofill/address_accessory_controller_impl.h"
#include <algorithm>
#include <utility>
#include "base/strings/utf_string_conversions.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 "chrome/browser/vr/vr_tab_helper.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/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.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[] = {
// TODO(crbug.com/965494): Possibly, the names should be in a single chip.
ServerFieldType::NAME_FIRST,
ServerFieldType::NAME_MIDDLE,
ServerFieldType::NAME_LAST,
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) {
base::string16 field = profile->GetRawInfo(type);
if (type == ServerFieldType::NAME_MIDDLE && field.empty()) {
field = profile->GetRawInfo(ServerFieldType::NAME_MIDDLE_INITIAL);
}
info->add_field(UserInfo::Field(field, field, /*is_password=*/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());
std::transform(profiles.begin(), profiles.end(), infos.begin(),
TranslateProfile);
return infos;
}
std::vector<FooterCommand> CreateManageAddressesFooter() {
return {FooterCommand(l10n_util::GetStringUTF16(
IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK))};
}
} // namespace
AddressAccessoryControllerImpl::~AddressAccessoryControllerImpl() = default;
// static
bool AddressAccessoryController::AllowedForWebContents(
content::WebContents* web_contents) {
DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
if (vr::VrTabHelper::IsInVr(web_contents)) {
return false; // TODO(crbug.com/865749): Re-Enable if possible.
}
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);
}
// static
AddressAccessoryController* AddressAccessoryController::GetIfExisting(
content::WebContents* web_contents) {
return AddressAccessoryControllerImpl::FromWebContents(web_contents);
}
void AddressAccessoryControllerImpl::OnFillingTriggered(
const UserInfo::Field& 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.
autofill::ContentAutofillDriver* driver =
autofill::ContentAutofillDriver::GetForRenderFrameHost(
web_contents_->GetFocusedFrame());
if (!driver)
return;
driver->RendererShouldFillFieldWithValue(selection.display_text());
}
void AddressAccessoryControllerImpl::RefreshSuggestions() {
std::vector<AutofillProfile*> profiles = GetProfiles();
base::string16 title_or_empty_message;
if (profiles.empty())
title_or_empty_message =
l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
GetManualFillingController()->RefreshSuggestionsForField(
mojom::FocusedFieldType::kFillableTextField,
autofill::CreateAccessorySheetData(
autofill::FallbackSheetType::ADDRESS, title_or_empty_message,
UserInfosForProfiles(profiles), CreateManageAddressesFooter()));
}
// static
void AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
content::WebContents* web_contents,
base::WeakPtr<ManualFillingController> mf_controller,
autofill::PersonalDataManager* personal_data_manager) {
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), personal_data_manager)));
}
AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
content::WebContents* web_contents)
: web_contents_(web_contents),
personal_data_manager_for_testing_(nullptr) {}
// Additional creation functions in unit tests only:
AddressAccessoryControllerImpl::AddressAccessoryControllerImpl(
content::WebContents* web_contents,
base::WeakPtr<ManualFillingController> mf_controller,
autofill::PersonalDataManager* personal_data_manager)
: web_contents_(web_contents),
mf_controller_(std::move(mf_controller)),
personal_data_manager_for_testing_(personal_data_manager) {}
std::vector<AutofillProfile*> AddressAccessoryControllerImpl::GetProfiles() {
const autofill::PersonalDataManager* data_manager =
personal_data_manager_for_testing_;
if (!data_manager)
data_manager = autofill::PersonalDataManagerFactory::GetForProfile(
Profile::FromBrowserContext(web_contents_->GetBrowserContext()));
if (!data_manager)
return {}; // No data available yet or anymore!
return data_manager->GetProfilesToSuggest();
}
base::WeakPtr<ManualFillingController>
AddressAccessoryControllerImpl::GetManualFillingController() {
if (!mf_controller_)
mf_controller_ = ManualFillingController::GetOrCreate(web_contents_);
DCHECK(mf_controller_);
return mf_controller_;
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(AddressAccessoryControllerImpl)
} // namespace autofill
// Copyright 2019 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.
#ifndef CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
#define CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
#include <vector>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/autofill/address_accessory_controller.h"
#include "content/public/browser/web_contents_user_data.h"
#include "url/gurl.h"
class ManualFillingController;
namespace autofill {
class AutofillProfile;
class PersonalDataManager;
// Use either AddressAccessoryController::GetOrCreate or
// AddressAccessoryController::GetIfExisting to obtain instances of this class.
// This class exists for every tab and should never store state based on the
// contents of one of its frames.
class AddressAccessoryControllerImpl
: public AddressAccessoryController,
public content::WebContentsUserData<AddressAccessoryControllerImpl> {
public:
~AddressAccessoryControllerImpl() override;
// AccessoryController:
void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
// AddressAccessoryController:
void RefreshSuggestions() override;
// Like |CreateForWebContents|, it creates the controller and attaches it to
// the given |web_contents|. Additionally, it allows inject a manual filling
// controller.
static void CreateForWebContentsForTesting(
content::WebContents* web_contents,
base::WeakPtr<ManualFillingController> mf_controller,
autofill::PersonalDataManager* personal_data_manager);
private:
friend class content::WebContentsUserData<AddressAccessoryControllerImpl>;
// Required for construction via |CreateForWebContents|:
explicit AddressAccessoryControllerImpl(content::WebContents* contents);
std::vector<autofill::AutofillProfile*> GetProfiles();
// Constructor that allows to inject a mock or fake view.
AddressAccessoryControllerImpl(
content::WebContents* web_contents,
base::WeakPtr<ManualFillingController> mf_controller,
autofill::PersonalDataManager* personal_data_manager);
// Lazy-initializes and returns the ManualFillingController for the current
// |web_contents_|. The lazy initialization allows injecting mocks for tests.
base::WeakPtr<ManualFillingController> GetManualFillingController();
// The tab for which this class is scoped.
content::WebContents* web_contents_;
// The password accessory controller object to forward client requests to.
base::WeakPtr<ManualFillingController> mf_controller_;
// The data manager used to retrieve the profiles in tests.
const autofill::PersonalDataManager* personal_data_manager_for_testing_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
DISALLOW_COPY_AND_ASSIGN(AddressAccessoryControllerImpl);
};
} // namespace autofill
#endif // CHROME_BROWSER_AUTOFILL_ADDRESS_ACCESSORY_CONTROLLER_IMPL_H_
// Copyright 2019 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/autofill/address_accessory_controller_impl.h"
#include <algorithm>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/autofill/mock_manual_filling_controller.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/strings/grit/components_strings.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
namespace autofill {
namespace {
using autofill::UserInfo;
using base::ASCIIToUTF16;
using testing::_;
using testing::ByMove;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
using FillingSource = ManualFillingController::FillingSource;
base::string16 addresses_empty_str() {
return l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE);
}
base::string16 manage_addresses_str() {
return l10n_util::GetStringUTF16(
IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK);
}
// Creates a AccessorySheetData::Builder with a "Manage Addresses" footer.
AccessorySheetData::Builder AddressAccessorySheetDataBuilder(
const base::string16& title) {
return AccessorySheetData::Builder(FallbackSheetType::ADDRESS, title)
.AppendFooterCommand(manage_addresses_str());
}
} // namespace
class AddressAccessoryControllerTest : public ChromeRenderViewHostTestHarness {
public:
AddressAccessoryControllerTest() {}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
personal_data_manager_.SetAutofillProfileEnabled(true);
AddressAccessoryControllerImpl::CreateForWebContentsForTesting(
web_contents(), mock_manual_filling_controller_.AsWeakPtr(),
&personal_data_manager_);
}
void TearDown() override { personal_data_manager_.ClearProfiles(); }
AddressAccessoryController* controller() {
return AddressAccessoryControllerImpl::FromWebContents(web_contents());
}
protected:
StrictMock<MockManualFillingController> mock_manual_filling_controller_;
autofill::TestPersonalDataManager personal_data_manager_;
};
TEST_F(AddressAccessoryControllerTest, IsNotRecreatedForSameWebContents) {
AddressAccessoryControllerImpl* initial_controller =
AddressAccessoryControllerImpl::FromWebContents(web_contents());
EXPECT_NE(nullptr, initial_controller);
AddressAccessoryControllerImpl::CreateForWebContents(web_contents());
EXPECT_EQ(AddressAccessoryControllerImpl::FromWebContents(web_contents()),
initial_controller);
}
TEST_F(AddressAccessoryControllerTest, RefreshSuggestionsCallsUI) {
AutofillProfile canadian = test::GetFullValidProfileForCanada();
personal_data_manager_.AddProfile(canadian);
autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
base::string16());
EXPECT_CALL(mock_manual_filling_controller_,
RefreshSuggestionsForField(
mojom::FocusedFieldType::kFillableTextField, _))
.WillOnce(SaveArg<1>(&result));
controller()->RefreshSuggestions();
ASSERT_EQ(
result,
AddressAccessorySheetDataBuilder(base::string16())
.AddUserInfo()
.AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_FIRST))
.AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_MIDDLE))
.AppendSimpleField(canadian.GetRawInfo(ServerFieldType::NAME_LAST))
.AppendSimpleField(canadian.GetRawInfo(ServerFieldType::COMPANY_NAME))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_LINE1))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_LINE2))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_ZIP))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_CITY))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_STATE))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::ADDRESS_HOME_COUNTRY))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::PHONE_HOME_WHOLE_NUMBER))
.AppendSimpleField(
canadian.GetRawInfo(ServerFieldType::EMAIL_ADDRESS))
.Build());
}
TEST_F(AddressAccessoryControllerTest, ProvidesEmptySuggestionsMessage) {
autofill::AccessorySheetData result(autofill::FallbackSheetType::PASSWORD,
base::string16());
EXPECT_CALL(mock_manual_filling_controller_,
RefreshSuggestionsForField(
mojom::FocusedFieldType::kFillableTextField, _))
.WillOnce(SaveArg<1>(&result));
controller()->RefreshSuggestions();
ASSERT_EQ(result,
AddressAccessorySheetDataBuilder(addresses_empty_str()).Build());
}
} // namespace autofill
......@@ -4,7 +4,9 @@
#include "chrome/browser/autofill/credit_card_accessory_controller_impl.h"
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>
#include "base/strings/utf_string_conversions.h"
......@@ -28,7 +30,7 @@ void AddField(const base::string16& data, UserInfo* user_info) {
/*selectable=*/true));
}
UserInfo Translate(const CreditCard* data) {
UserInfo TranslateCard(const CreditCard* data) {
DCHECK(data);
UserInfo user_info;
......@@ -63,7 +65,7 @@ void CreditCardAccessoryControllerImpl::RefreshSuggestionsForField() {
const std::vector<CreditCard*> suggestions = GetSuggestions();
std::vector<UserInfo> info_to_add;
std::transform(suggestions.begin(), suggestions.end(),
std::back_inserter(info_to_add), &Translate);
std::back_inserter(info_to_add), &TranslateCard);
// TODO(crbug.com/902425): Add "Manage payment methods" footer command
std::vector<FooterCommand> footer_commands;
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/password_manager/password_accessory_controller_impl.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
......@@ -41,8 +42,9 @@ using FillingSource = ManualFillingController::FillingSource;
namespace {
autofill::UserInfo Translate(bool current_field_is_password,
const PasswordAccessorySuggestion& data) {
autofill::UserInfo TranslateCredentials(
bool current_field_is_password,
const PasswordAccessorySuggestion& data) {
UserInfo user_info;
user_info.add_field(UserInfo::Field(
......@@ -198,7 +200,7 @@ void PasswordAccessoryControllerImpl::RefreshSuggestionsForField(
info_to_add.resize(suggestions.size());
std::transform(suggestions.begin(), suggestions.end(), info_to_add.begin(),
[is_password_field](const auto& x) {
return Translate(is_password_field, x);
return TranslateCredentials(is_password_field, x);
});
}
......
......@@ -3324,6 +3324,7 @@ test("unit_tests") {
sources += [
"../browser/android/customtabs/dynamicmodule/module_metrics_unittest.cc",
"../browser/android/search_permissions/search_permissions_service_unittest.cc",
"../browser/autofill/address_accessory_controller_impl_unittest.cc",
"../browser/autofill/autofill_credit_card_filling_infobar_delegate_mobile_unittest.cc",
"../browser/autofill/autofill_keyboard_accessory_adapter_unittest.cc",
"../browser/autofill/autofill_save_card_infobar_delegate_mobile_unittest.cc",
......
......@@ -256,6 +256,12 @@
<message name="IDS_MANUAL_FILLING_CREDIT_CARD_SHEET_TITLE" desc="Title shown at the top of a sheet where users can select indiviual data pieces (e.g., the cardholder name) related to their stored payment methods (e.g., credit cards) to fill that data piece into the focused form field. Sentence-cased.">
Payment methods
</message>
<message name="IDS_AUTOFILL_ADDRESS_SHEET_EMPTY_MESSAGE" desc="Message indicating that no addresses are available for filling.">
No saved addresses
</message>
<message name="IDS_AUTOFILL_ADDRESS_SHEET_ALL_ADDRESSES_LINK" desc="The text of the link in the address sheet that opens the address management section.">
Manage addresses
</message>
</if>
</grit-part>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment