blob: b3362a2a03576ccd9e338faed1f628d8a20b4914 [file] [log] [blame]
// Copyright (c) 2012 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/ui/webui/options/autofill_options_handler.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/guid.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/autofill/core/browser/autofill_address_util.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/country_combobox_model.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/phone_number_i18n.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_ui.h"
#include "third_party/libaddressinput/messages.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/webui/web_ui_util.h"
using autofill::AutofillType;
using autofill::AutofillProfile;
using autofill::CreditCard;
using autofill::PersonalDataManager;
namespace {
static const char kComponents[] = "components";
static const char kLanguageCode[] = "languageCode";
std::unique_ptr<base::DictionaryValue> CreditCardToDictionary(
const CreditCard& card) {
std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
value->SetString("guid", card.guid());
std::pair<base::string16, base::string16> label_pieces = card.LabelPieces();
value->SetString("label", label_pieces.first);
value->SetString("sublabel", label_pieces.second);
value->SetBoolean("isLocal", card.record_type() == CreditCard::LOCAL_CARD);
value->SetBoolean("isCached",
card.record_type() == CreditCard::FULL_SERVER_CARD);
return value;
}
} // namespace
namespace options {
AutofillOptionsHandler::AutofillOptionsHandler() : personal_data_(nullptr) {}
AutofillOptionsHandler::~AutofillOptionsHandler() {
if (personal_data_)
personal_data_->RemoveObserver(this);
}
/////////////////////////////////////////////////////////////////////////////
// OptionsPageUIHandler implementation:
void AutofillOptionsHandler::GetLocalizedValues(
base::DictionaryValue* localized_strings) {
DCHECK(localized_strings);
static OptionsStringResource resources[] = {
{ "autofillAddresses", IDS_AUTOFILL_ADDRESSES_GROUP_NAME },
{ "autofillCreditCards", IDS_AUTOFILL_CREDITCARDS_GROUP_NAME },
{ "autofillAddAddress", IDS_AUTOFILL_ADD_ADDRESS_BUTTON },
{ "autofillAddCreditCard", IDS_AUTOFILL_ADD_CREDITCARD_BUTTON },
{ "autofillEditProfileButton", IDS_AUTOFILL_EDIT_PROFILE_BUTTON },
{ "autofillFromGoogleAccount", IDS_AUTOFILL_FROM_GOOGLE_ACCOUNT },
{ "autofillDescribeLocalCopy", IDS_AUTOFILL_DESCRIBE_LOCAL_COPY },
{ "autofillClearLocalCopyButton", IDS_AUTOFILL_CLEAR_LOCAL_COPY_BUTTON },
{ "helpButton", IDS_AUTOFILL_HELP_LABEL },
{ "addAddressTitle", IDS_AUTOFILL_ADD_ADDRESS_CAPTION },
{ "editAddressTitle", IDS_AUTOFILL_EDIT_ADDRESS_CAPTION },
{ "addCreditCardTitle", IDS_AUTOFILL_ADD_CREDITCARD_CAPTION },
{ "editCreditCardTitle", IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION },
};
RegisterStrings(localized_strings, resources, arraysize(resources));
RegisterTitle(localized_strings, "autofillOptionsPage",
IDS_AUTOFILL_OPTIONS_TITLE);
localized_strings->SetString("helpUrl", autofill::kHelpURL);
personal_data_ = autofill::PersonalDataManagerFactory::GetForProfile(
Profile::FromWebUI(web_ui()));
SetAddressOverlayStrings(localized_strings);
SetCreditCardOverlayStrings(localized_strings);
localized_strings->SetString(
"paymentsManageAddressesUrl",
autofill::payments::GetManageAddressesUrl(0).spec());
localized_strings->SetString(
"paymentsManageInstrumentsUrl",
autofill::payments::GetManageInstrumentsUrl(0).spec());
}
void AutofillOptionsHandler::InitializeHandler() {
// personal_data_ is NULL in guest mode on Chrome OS.
if (personal_data_)
personal_data_->AddObserver(this);
}
void AutofillOptionsHandler::InitializePage() {
if (personal_data_)
LoadAutofillData();
}
void AutofillOptionsHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"removeData",
base::Bind(&AutofillOptionsHandler::RemoveData,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"loadAddressEditor",
base::Bind(&AutofillOptionsHandler::LoadAddressEditor,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"loadAddressEditorComponents",
base::Bind(&AutofillOptionsHandler::LoadAddressEditorComponents,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"loadCreditCardEditor",
base::Bind(&AutofillOptionsHandler::LoadCreditCardEditor,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"setAddress",
base::Bind(&AutofillOptionsHandler::SetAddress, base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"setCreditCard",
base::Bind(&AutofillOptionsHandler::SetCreditCard,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"clearLocalCardCopy",
base::Bind(&AutofillOptionsHandler::RemaskServerCard,
base::Unretained(this)));
}
/////////////////////////////////////////////////////////////////////////////
// PersonalDataManagerObserver implementation:
void AutofillOptionsHandler::OnPersonalDataChanged() {
LoadAutofillData();
}
void AutofillOptionsHandler::SetAddressOverlayStrings(
base::DictionaryValue* localized_strings) {
localized_strings->SetString("autofillEditAddressTitle",
l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_ADDRESS_CAPTION));
localized_strings->SetString("autofillCountryLabel",
l10n_util::GetStringUTF16(IDS_LIBADDRESSINPUT_COUNTRY_OR_REGION_LABEL));
localized_strings->SetString("autofillPhoneLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE));
localized_strings->SetString("autofillEmailLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EMAIL));
SetCountryData(*personal_data_, localized_strings,
g_browser_process->GetApplicationLocale());
}
void AutofillOptionsHandler::SetCreditCardOverlayStrings(
base::DictionaryValue* localized_strings) {
localized_strings->SetString("autofillEditCreditCardTitle",
l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION));
localized_strings->SetString("nameOnCardLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_NAME_ON_CARD));
localized_strings->SetString("creditCardNumberLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_CREDIT_CARD_NUMBER));
localized_strings->SetString("creditCardExpirationDateLabel",
l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EXPIRATION_DATE));
}
void AutofillOptionsHandler::LoadAutofillData() {
if (!IsPersonalDataLoaded())
return;
const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
std::vector<base::string16> labels;
AutofillProfile::CreateDifferentiatingLabels(
profiles,
g_browser_process->GetApplicationLocale(),
&labels);
DCHECK_EQ(labels.size(), profiles.size());
base::ListValue addresses;
for (size_t i = 0; i < profiles.size(); ++i) {
// Skip showing auxiliary profiles (e.g. Mac Contacts) for now.
if (profiles[i]->record_type() == AutofillProfile::AUXILIARY_PROFILE)
continue;
base::string16 separator =
l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR);
std::vector<base::string16> label_parts = base::SplitStringUsingSubstr(
labels[i], separator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
value->SetString("guid", profiles[i]->guid());
value->SetString("label", label_parts[0]);
value->SetString("sublabel", labels[i].substr(label_parts[0].size()));
value->SetBoolean("isLocal", profiles[i]->record_type() ==
AutofillProfile::LOCAL_PROFILE);
addresses.Append(std::move(value));
}
web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.setAddressList",
addresses);
base::ListValue credit_cards;
const std::vector<CreditCard*>& cards = personal_data_->GetCreditCards();
for (const CreditCard* card : cards) {
credit_cards.Append(CreditCardToDictionary(*card));
}
web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.setCreditCardList",
credit_cards);
}
void AutofillOptionsHandler::RemoveData(const base::ListValue* args) {
DCHECK(IsPersonalDataLoaded());
std::string guid;
if (!args->GetString(0, &guid)) {
NOTREACHED();
return;
}
personal_data_->RemoveByGUID(guid);
}
void AutofillOptionsHandler::LoadAddressEditor(const base::ListValue* args) {
DCHECK(IsPersonalDataLoaded());
std::string guid;
if (!args->GetString(0, &guid)) {
NOTREACHED();
return;
}
const AutofillProfile* prior_profile = personal_data_->GetProfileByGUID(guid);
if (!prior_profile) {
// There is a race where a user can click once on the close button and
// quickly click again on the list item before the item is removed (since
// the list is not updated until the model tells the list an item has been
// removed). This will activate the editor for a profile that has been
// removed. Do nothing in that case.
return;
}
base::DictionaryValue address;
AutofillProfileToDictionary(*prior_profile, &address);
web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.editAddress",
address);
}
void AutofillOptionsHandler::LoadAddressEditorComponents(
const base::ListValue* args) {
std::string country_code;
if (!args->GetString(0, &country_code)) {
NOTREACHED();
return;
}
base::DictionaryValue input;
std::unique_ptr<base::ListValue> components(new base::ListValue);
std::string language_code;
autofill::GetAddressComponents(country_code,
g_browser_process->GetApplicationLocale(),
components.get(), &language_code);
input.Set(kComponents, components.release());
input.SetString(kLanguageCode, language_code);
web_ui()->CallJavascriptFunctionUnsafe(
"AutofillEditAddressOverlay.loadAddressComponents", input);
}
void AutofillOptionsHandler::LoadCreditCardEditor(const base::ListValue* args) {
DCHECK(IsPersonalDataLoaded());
std::string guid;
if (!args->GetString(0, &guid)) {
NOTREACHED();
return;
}
CreditCard* credit_card = personal_data_->GetCreditCardByGUID(guid);
if (!credit_card) {
// There is a race where a user can click once on the close button and
// quickly click again on the list item before the item is removed (since
// the list is not updated until the model tells the list an item has been
// removed). This will activate the editor for a profile that has been
// removed. Do nothing in that case.
return;
}
base::DictionaryValue credit_card_data;
credit_card_data.SetString("guid", credit_card->guid());
credit_card_data.SetString(
"nameOnCard", credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME_FULL));
credit_card_data.SetString(
"creditCardNumber",
credit_card->GetRawInfo(autofill::CREDIT_CARD_NUMBER));
credit_card_data.SetString(
"expirationMonth",
credit_card->GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH));
credit_card_data.SetString(
"expirationYear",
credit_card->GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR));
web_ui()->CallJavascriptFunctionUnsafe("AutofillOptions.editCreditCard",
credit_card_data);
}
void AutofillOptionsHandler::SetAddress(const base::ListValue* args) {
if (!IsPersonalDataLoaded())
return;
int arg_counter = 0;
std::string guid;
if (!args->GetString(arg_counter++, &guid)) {
NOTREACHED();
return;
}
AutofillProfile profile(guid, autofill::kSettingsOrigin);
base::string16 full_name;
if (args->GetString(arg_counter++, &full_name)) {
// Although First/Middle/Last are not displayed on the form, we transfer
// this information when they match the full name in the old version of the
// profile, if one exists. This is because it may not be possible later to
// correctly tokenize the concatenated full name; e.g., when the last name
// contains a space, the first word would be treated as a middle name.
const AutofillProfile* prior_profile =
base::IsValidGUID(profile.guid())
? personal_data_->GetProfileByGUID(guid)
: nullptr;
if (prior_profile && autofill::data_util::ProfileMatchesFullName(
full_name, *prior_profile)) {
profile.SetRawInfo(autofill::NAME_FULL, full_name);
profile.SetRawInfo(autofill::NAME_FIRST,
prior_profile->GetRawInfo(autofill::NAME_FIRST));
profile.SetRawInfo(autofill::NAME_MIDDLE,
prior_profile->GetRawInfo(autofill::NAME_MIDDLE));
profile.SetRawInfo(autofill::NAME_LAST,
prior_profile->GetRawInfo(autofill::NAME_LAST));
} else {
// In contrast to SetRawInfo, SetInfo will naively attempt to populate the
// First/Middle/Last fields by tokenization.
profile.SetInfo(AutofillType(autofill::NAME_FULL), full_name,
g_browser_process->GetApplicationLocale());
}
}
base::string16 value;
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::COMPANY_NAME, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_CITY, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_STATE, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_ZIP, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::ADDRESS_HOME_COUNTRY, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER, value);
if (args->GetString(arg_counter++, &value))
profile.SetRawInfo(autofill::EMAIL_ADDRESS, value);
if (args->GetString(arg_counter++, &value))
profile.set_language_code(base::UTF16ToUTF8(value));
if (!base::IsValidGUID(profile.guid())) {
profile.set_guid(base::GenerateGUID());
personal_data_->AddProfile(profile);
} else {
personal_data_->UpdateProfile(profile);
}
}
void AutofillOptionsHandler::SetCreditCard(const base::ListValue* args) {
if (!IsPersonalDataLoaded())
return;
std::string guid;
if (!args->GetString(0, &guid)) {
NOTREACHED();
return;
}
CreditCard credit_card(guid, autofill::kSettingsOrigin);
base::string16 value;
if (args->GetString(1, &value))
credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL, value);
if (args->GetString(2, &value))
credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER, value);
if (args->GetString(3, &value))
credit_card.SetRawInfo(autofill::CREDIT_CARD_EXP_MONTH, value);
if (args->GetString(4, &value))
credit_card.SetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR, value);
if (!base::IsValidGUID(credit_card.guid())) {
credit_card.set_guid(base::GenerateGUID());
personal_data_->AddCreditCard(credit_card);
} else {
personal_data_->UpdateCreditCard(credit_card);
}
}
void AutofillOptionsHandler::RemaskServerCard(const base::ListValue* args) {
std::string guid;
if (!args->GetString(0, &guid)) {
NOTREACHED();
return;
}
personal_data_->ResetFullServerCard(guid);
}
bool AutofillOptionsHandler::IsPersonalDataLoaded() const {
return personal_data_ && personal_data_->IsDataLoaded();
}
// static
void AutofillOptionsHandler::AutofillProfileToDictionary(
const autofill::AutofillProfile& profile,
base::DictionaryValue* address) {
address->SetString("guid", profile.guid());
address->SetString(
autofill::kFullNameField,
profile.GetInfo(AutofillType(autofill::NAME_FULL),
g_browser_process->GetApplicationLocale()));
address->SetString(autofill::kCompanyNameField,
profile.GetRawInfo(autofill::COMPANY_NAME));
address->SetString(autofill::kAddressLineField,
profile.GetRawInfo(autofill::ADDRESS_HOME_STREET_ADDRESS));
address->SetString(autofill::kCityField,
profile.GetRawInfo(autofill::ADDRESS_HOME_CITY));
address->SetString(autofill::kStateField,
profile.GetRawInfo(autofill::ADDRESS_HOME_STATE));
address->SetString(
autofill::kDependentLocalityField,
profile.GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY));
address->SetString(autofill::kSortingCodeField,
profile.GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE));
address->SetString(autofill::kPostalCodeField,
profile.GetRawInfo(autofill::ADDRESS_HOME_ZIP));
address->SetString(autofill::kCountryField,
profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
address->SetString("phone",
profile.GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER));
address->SetString("email", profile.GetRawInfo(autofill::EMAIL_ADDRESS));
address->SetString(kLanguageCode, profile.language_code());
std::unique_ptr<base::ListValue> components(new base::ListValue);
autofill::GetAddressComponents(
base::UTF16ToUTF8(profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY)),
profile.language_code(), components.get(), nullptr);
address->Set(kComponents, components.release());
}
} // namespace options