1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/webui/options/autofill_options_handler.h"
10 #include "base/bind_helpers.h"
11 #include "base/guid.h"
12 #include "base/logging.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/autofill/personal_data_manager_factory.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/autofill/country_combobox_model.h"
21 #include "chrome/common/url_constants.h"
22 #include "components/autofill/core/browser/autofill_country.h"
23 #include "components/autofill/core/browser/autofill_profile.h"
24 #include "components/autofill/core/browser/credit_card.h"
25 #include "components/autofill/core/browser/personal_data_manager.h"
26 #include "components/autofill/core/browser/phone_number_i18n.h"
27 #include "components/autofill/core/common/autofill_constants.h"
28 #include "content/public/browser/web_ui.h"
29 #include "grit/component_strings.h"
30 #include "grit/generated_resources.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/base/webui/web_ui_util.h"
34 using autofill::AutofillCountry;
35 using autofill::ServerFieldType;
36 using autofill::AutofillProfile;
37 using autofill::CreditCard;
38 using autofill::PersonalDataManager;
42 const char kSettingsOrigin[] = "Chrome settings";
44 // Sets data related to the country <select>.
45 void SetCountryData(const PersonalDataManager& manager,
46 DictionaryValue* localized_strings) {
47 autofill::CountryComboboxModel model(manager);
48 const std::vector<AutofillCountry*>& countries = model.countries();
49 localized_strings->SetString("defaultCountryCode",
50 countries.front()->country_code());
52 // An ordered list of options to show in the <select>.
53 scoped_ptr<ListValue> country_list(new ListValue());
54 // A dictionary of postal code and state info, keyed on country code.
55 scoped_ptr<DictionaryValue> country_data(new DictionaryValue());
56 for (size_t i = 0; i < countries.size(); ++i) {
57 scoped_ptr<DictionaryValue> option_details(new DictionaryValue());
58 option_details->SetString("name", model.GetItemAt(i));
59 option_details->SetString(
61 countries[i] ? countries[i]->country_code() : "separator");
62 country_list->Append(option_details.release());
67 scoped_ptr<DictionaryValue> details(new DictionaryValue());
68 details->SetString("postalCodeLabel", countries[i]->postal_code_label());
69 details->SetString("stateLabel", countries[i]->state_label());
70 country_data->Set(countries[i]->country_code(), details.release());
73 localized_strings->Set("autofillCountrySelectList", country_list.release());
74 localized_strings->Set("autofillCountryData", country_data.release());
77 // Get the multi-valued element for |type| and return it in |ListValue| form.
78 void GetValueList(const AutofillProfile& profile,
80 scoped_ptr<ListValue>* list) {
81 list->reset(new ListValue);
83 std::vector<string16> values;
84 profile.GetRawMultiInfo(type, &values);
86 // |GetRawMultiInfo()| always returns at least one, potentially empty, item.
87 if (values.size() == 1 && values.front().empty())
90 for (size_t i = 0; i < values.size(); ++i) {
91 (*list)->Set(i, new base::StringValue(values[i]));
95 // Set the multi-valued element for |type| from input |list| values.
96 void SetValueList(const ListValue* list,
98 AutofillProfile* profile) {
99 std::vector<string16> values(list->GetSize());
100 for (size_t i = 0; i < list->GetSize(); ++i) {
102 if (list->GetString(i, &value))
105 profile->SetRawMultiInfo(type, values);
108 // Get the multi-valued element for |type| and return it in |ListValue| form.
109 void GetNameList(const AutofillProfile& profile,
110 scoped_ptr<ListValue>* names) {
111 names->reset(new ListValue);
113 std::vector<string16> first_names;
114 std::vector<string16> middle_names;
115 std::vector<string16> last_names;
116 profile.GetRawMultiInfo(autofill::NAME_FIRST, &first_names);
117 profile.GetRawMultiInfo(autofill::NAME_MIDDLE, &middle_names);
118 profile.GetRawMultiInfo(autofill::NAME_LAST, &last_names);
119 DCHECK_EQ(first_names.size(), middle_names.size());
120 DCHECK_EQ(first_names.size(), last_names.size());
122 // |GetRawMultiInfo()| always returns at least one, potentially empty, item.
123 if (first_names.size() == 1 && first_names.front().empty() &&
124 middle_names.front().empty() && last_names.front().empty()) {
128 for (size_t i = 0; i < first_names.size(); ++i) {
129 ListValue* name = new ListValue; // owned by |list|
130 name->Set(0, new base::StringValue(first_names[i]));
131 name->Set(1, new base::StringValue(middle_names[i]));
132 name->Set(2, new base::StringValue(last_names[i]));
133 (*names)->Set(i, name);
137 // Set the multi-valued element for |type| from input |list| values.
138 void SetNameList(const ListValue* names, AutofillProfile* profile) {
139 const size_t size = names->GetSize();
140 std::vector<string16> first_names(size);
141 std::vector<string16> middle_names(size);
142 std::vector<string16> last_names(size);
144 for (size_t i = 0; i < size; ++i) {
145 const ListValue* name;
146 bool success = names->GetList(i, &name);
150 success = name->GetString(0, &first_name);
152 first_names[i] = first_name;
154 string16 middle_name;
155 success = name->GetString(1, &middle_name);
157 middle_names[i] = middle_name;
160 success = name->GetString(2, &last_name);
162 last_names[i] = last_name;
165 profile->SetRawMultiInfo(autofill::NAME_FIRST, first_names);
166 profile->SetRawMultiInfo(autofill::NAME_MIDDLE, middle_names);
167 profile->SetRawMultiInfo(autofill::NAME_LAST, last_names);
170 // Pulls the phone number |index|, |phone_number_list|, and |country_code| from
172 void ExtractPhoneNumberInformation(const ListValue* args,
174 const ListValue** phone_number_list,
175 std::string* country_code) {
176 // Retrieve index as a |double|, as that is how it comes across from
179 if (!args->GetDouble(0, &number)) {
185 if (!args->GetList(1, phone_number_list)) {
190 if (!args->GetString(2, country_code)) {
196 // Searches the |list| for the value at |index|. If this value is present
197 // in any of the rest of the list, then the item (at |index|) is removed.
198 // The comparison of phone number values is done on normalized versions of the
199 // phone number values.
200 void RemoveDuplicatePhoneNumberAtIndex(size_t index,
201 const std::string& country_code,
204 if (!list->GetString(index, &new_value)) {
205 NOTREACHED() << "List should have a value at index " << index;
209 bool is_duplicate = false;
210 std::string app_locale = g_browser_process->GetApplicationLocale();
211 for (size_t i = 0; i < list->GetSize() && !is_duplicate; ++i) {
215 string16 existing_value;
216 if (!list->GetString(i, &existing_value)) {
217 NOTREACHED() << "List should have a value at index " << i;
220 is_duplicate = autofill::i18n::PhoneNumbersMatch(
221 new_value, existing_value, country_code, app_locale);
225 list->Remove(index, NULL);
228 scoped_ptr<ListValue> ValidatePhoneArguments(const ListValue* args) {
230 std::string country_code;
231 const ListValue* extracted_list = NULL;
232 ExtractPhoneNumberInformation(args, &index, &extracted_list, &country_code);
234 scoped_ptr<ListValue> list(extracted_list->DeepCopy());
235 RemoveDuplicatePhoneNumberAtIndex(index, country_code, list.get());
243 AutofillOptionsHandler::AutofillOptionsHandler()
244 : personal_data_(NULL) {}
246 AutofillOptionsHandler::~AutofillOptionsHandler() {
248 personal_data_->RemoveObserver(this);
251 /////////////////////////////////////////////////////////////////////////////
252 // OptionsPageUIHandler implementation:
253 void AutofillOptionsHandler::GetLocalizedValues(
254 DictionaryValue* localized_strings) {
255 DCHECK(localized_strings);
257 static OptionsStringResource resources[] = {
258 { "autofillAddresses", IDS_AUTOFILL_ADDRESSES_GROUP_NAME },
259 { "autofillCreditCards", IDS_AUTOFILL_CREDITCARDS_GROUP_NAME },
260 { "autofillAddAddress", IDS_AUTOFILL_ADD_ADDRESS_BUTTON },
261 { "autofillAddCreditCard", IDS_AUTOFILL_ADD_CREDITCARD_BUTTON },
262 { "autofillEditProfileButton", IDS_AUTOFILL_EDIT_PROFILE_BUTTON },
263 { "helpButton", IDS_AUTOFILL_HELP_LABEL },
264 { "addAddressTitle", IDS_AUTOFILL_ADD_ADDRESS_CAPTION },
265 { "editAddressTitle", IDS_AUTOFILL_EDIT_ADDRESS_CAPTION },
266 { "addCreditCardTitle", IDS_AUTOFILL_ADD_CREDITCARD_CAPTION },
267 { "editCreditCardTitle", IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION },
268 #if defined(OS_MACOSX)
269 { "auxiliaryProfilesEnabled", IDS_AUTOFILL_USE_MAC_ADDRESS_BOOK },
270 #endif // defined(OS_MACOSX)
273 RegisterStrings(localized_strings, resources, arraysize(resources));
274 RegisterTitle(localized_strings, "autofillOptionsPage",
275 IDS_AUTOFILL_OPTIONS_TITLE);
277 localized_strings->SetString("helpUrl", autofill::kHelpURL);
278 SetAddressOverlayStrings(localized_strings);
279 SetCreditCardOverlayStrings(localized_strings);
282 void AutofillOptionsHandler::InitializeHandler() {
283 // personal_data_ is NULL in guest mode on Chrome OS.
285 personal_data_->AddObserver(this);
288 void AutofillOptionsHandler::InitializePage() {
293 void AutofillOptionsHandler::RegisterMessages() {
294 personal_data_ = autofill::PersonalDataManagerFactory::GetForProfile(
295 Profile::FromWebUI(web_ui()));
297 web_ui()->RegisterMessageCallback(
299 base::Bind(&AutofillOptionsHandler::RemoveData,
300 base::Unretained(this)));
301 web_ui()->RegisterMessageCallback(
303 base::Bind(&AutofillOptionsHandler::LoadAddressEditor,
304 base::Unretained(this)));
305 web_ui()->RegisterMessageCallback(
306 "loadCreditCardEditor",
307 base::Bind(&AutofillOptionsHandler::LoadCreditCardEditor,
308 base::Unretained(this)));
309 web_ui()->RegisterMessageCallback(
311 base::Bind(&AutofillOptionsHandler::SetAddress, base::Unretained(this)));
312 web_ui()->RegisterMessageCallback(
314 base::Bind(&AutofillOptionsHandler::SetCreditCard,
315 base::Unretained(this)));
316 web_ui()->RegisterMessageCallback(
317 "validatePhoneNumbers",
318 base::Bind(&AutofillOptionsHandler::ValidatePhoneNumbers,
319 base::Unretained(this)));
322 /////////////////////////////////////////////////////////////////////////////
323 // PersonalDataManagerObserver implementation:
324 void AutofillOptionsHandler::OnPersonalDataChanged() {
328 void AutofillOptionsHandler::SetAddressOverlayStrings(
329 DictionaryValue* localized_strings) {
330 localized_strings->SetString("autofillEditAddressTitle",
331 l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_ADDRESS_CAPTION));
332 localized_strings->SetString("autofillFirstNameLabel",
333 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_FIRST_NAME));
334 localized_strings->SetString("autofillMiddleNameLabel",
335 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_MIDDLE_NAME));
336 localized_strings->SetString("autofillLastNameLabel",
337 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_LAST_NAME));
338 localized_strings->SetString("autofillCompanyNameLabel",
339 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_COMPANY_NAME));
340 localized_strings->SetString("autofillAddrLine1Label",
341 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADDRESS_LINE_1));
342 localized_strings->SetString("autofillAddrLine2Label",
343 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADDRESS_LINE_2));
344 localized_strings->SetString("autofillCityLabel",
345 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_CITY));
346 localized_strings->SetString("autofillCountryLabel",
347 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_COUNTRY));
348 localized_strings->SetString("autofillPhoneLabel",
349 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_PHONE));
350 localized_strings->SetString("autofillEmailLabel",
351 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EMAIL));
352 localized_strings->SetString("autofillAddFirstNamePlaceholder",
353 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_FIRST_NAME));
354 localized_strings->SetString("autofillAddMiddleNamePlaceholder",
355 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_MIDDLE_NAME));
356 localized_strings->SetString("autofillAddLastNamePlaceholder",
357 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_LAST_NAME));
358 localized_strings->SetString("autofillAddPhonePlaceholder",
359 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_PHONE));
360 localized_strings->SetString("autofillAddEmailPlaceholder",
361 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_ADD_EMAIL));
362 SetCountryData(*personal_data_, localized_strings);
365 void AutofillOptionsHandler::SetCreditCardOverlayStrings(
366 DictionaryValue* localized_strings) {
367 localized_strings->SetString("autofillEditCreditCardTitle",
368 l10n_util::GetStringUTF16(IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION));
369 localized_strings->SetString("nameOnCardLabel",
370 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_NAME_ON_CARD));
371 localized_strings->SetString("creditCardNumberLabel",
372 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_CREDIT_CARD_NUMBER));
373 localized_strings->SetString("creditCardExpirationDateLabel",
374 l10n_util::GetStringUTF16(IDS_AUTOFILL_FIELD_LABEL_EXPIRATION_DATE));
377 void AutofillOptionsHandler::LoadAutofillData() {
378 if (!IsPersonalDataLoaded())
382 for (std::vector<AutofillProfile*>::const_iterator i =
383 personal_data_->web_profiles().begin();
384 i != personal_data_->web_profiles().end(); ++i) {
385 ListValue* entry = new ListValue();
386 entry->Append(new StringValue((*i)->guid()));
387 entry->Append(new StringValue((*i)->Label()));
388 addresses.Append(entry);
391 web_ui()->CallJavascriptFunction("AutofillOptions.setAddressList", addresses);
393 ListValue credit_cards;
394 const std::vector<CreditCard*>& cards = personal_data_->GetCreditCards();
395 for (std::vector<CreditCard*>::const_iterator iter = cards.begin();
396 iter != cards.end(); ++iter) {
397 const CreditCard* card = *iter;
398 // TODO(estade): this should be a dictionary.
399 ListValue* entry = new ListValue();
400 entry->Append(new StringValue(card->guid()));
401 entry->Append(new StringValue(card->Label()));
402 entry->Append(new StringValue(
403 webui::GetBitmapDataUrlFromResource(
404 CreditCard::IconResourceId(card->type()))));
405 entry->Append(new StringValue(card->TypeForDisplay()));
406 credit_cards.Append(entry);
409 web_ui()->CallJavascriptFunction("AutofillOptions.setCreditCardList",
413 void AutofillOptionsHandler::RemoveData(const ListValue* args) {
414 DCHECK(IsPersonalDataLoaded());
417 if (!args->GetString(0, &guid)) {
422 personal_data_->RemoveByGUID(guid);
425 void AutofillOptionsHandler::LoadAddressEditor(const ListValue* args) {
426 DCHECK(IsPersonalDataLoaded());
429 if (!args->GetString(0, &guid)) {
434 AutofillProfile* profile = personal_data_->GetProfileByGUID(guid);
436 // There is a race where a user can click once on the close button and
437 // quickly click again on the list item before the item is removed (since
438 // the list is not updated until the model tells the list an item has been
439 // removed). This will activate the editor for a profile that has been
440 // removed. Do nothing in that case.
444 DictionaryValue address;
445 address.SetString("guid", profile->guid());
446 scoped_ptr<ListValue> list;
447 GetNameList(*profile, &list);
448 address.Set("fullName", list.release());
449 address.SetString("companyName", profile->GetRawInfo(autofill::COMPANY_NAME));
450 address.SetString("addrLine1",
451 profile->GetRawInfo(autofill::ADDRESS_HOME_LINE1));
452 address.SetString("addrLine2",
453 profile->GetRawInfo(autofill::ADDRESS_HOME_LINE2));
454 address.SetString("city", profile->GetRawInfo(autofill::ADDRESS_HOME_CITY));
455 address.SetString("state", profile->GetRawInfo(autofill::ADDRESS_HOME_STATE));
456 address.SetString("postalCode",
457 profile->GetRawInfo(autofill::ADDRESS_HOME_ZIP));
458 address.SetString("country",
459 profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
460 GetValueList(*profile, autofill::PHONE_HOME_WHOLE_NUMBER, &list);
461 address.Set("phone", list.release());
462 GetValueList(*profile, autofill::EMAIL_ADDRESS, &list);
463 address.Set("email", list.release());
465 web_ui()->CallJavascriptFunction("AutofillOptions.editAddress", address);
468 void AutofillOptionsHandler::LoadCreditCardEditor(const ListValue* args) {
469 DCHECK(IsPersonalDataLoaded());
472 if (!args->GetString(0, &guid)) {
477 CreditCard* credit_card = personal_data_->GetCreditCardByGUID(guid);
479 // There is a race where a user can click once on the close button and
480 // quickly click again on the list item before the item is removed (since
481 // the list is not updated until the model tells the list an item has been
482 // removed). This will activate the editor for a profile that has been
483 // removed. Do nothing in that case.
487 DictionaryValue credit_card_data;
488 credit_card_data.SetString("guid", credit_card->guid());
489 credit_card_data.SetString(
491 credit_card->GetRawInfo(autofill::CREDIT_CARD_NAME));
492 credit_card_data.SetString(
494 credit_card->GetRawInfo(autofill::CREDIT_CARD_NUMBER));
495 credit_card_data.SetString(
497 credit_card->GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH));
498 credit_card_data.SetString(
500 credit_card->GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR));
502 web_ui()->CallJavascriptFunction("AutofillOptions.editCreditCard",
506 void AutofillOptionsHandler::SetAddress(const ListValue* args) {
507 if (!IsPersonalDataLoaded())
511 if (!args->GetString(0, &guid)) {
516 AutofillProfile profile(guid, kSettingsOrigin);
518 std::string country_code;
520 const ListValue* list_value;
521 if (args->GetList(1, &list_value))
522 SetNameList(list_value, &profile);
524 if (args->GetString(2, &value))
525 profile.SetRawInfo(autofill::COMPANY_NAME, value);
527 if (args->GetString(3, &value))
528 profile.SetRawInfo(autofill::ADDRESS_HOME_LINE1, value);
530 if (args->GetString(4, &value))
531 profile.SetRawInfo(autofill::ADDRESS_HOME_LINE2, value);
533 if (args->GetString(5, &value))
534 profile.SetRawInfo(autofill::ADDRESS_HOME_CITY, value);
536 if (args->GetString(6, &value))
537 profile.SetRawInfo(autofill::ADDRESS_HOME_STATE, value);
539 if (args->GetString(7, &value))
540 profile.SetRawInfo(autofill::ADDRESS_HOME_ZIP, value);
542 if (args->GetString(8, &country_code))
543 profile.SetRawInfo(autofill::ADDRESS_HOME_COUNTRY,
544 ASCIIToUTF16(country_code));
546 if (args->GetList(9, &list_value))
547 SetValueList(list_value, autofill::PHONE_HOME_WHOLE_NUMBER, &profile);
549 if (args->GetList(10, &list_value))
550 SetValueList(list_value, autofill::EMAIL_ADDRESS, &profile);
552 if (!base::IsValidGUID(profile.guid())) {
553 profile.set_guid(base::GenerateGUID());
554 personal_data_->AddProfile(profile);
556 personal_data_->UpdateProfile(profile);
560 void AutofillOptionsHandler::SetCreditCard(const ListValue* args) {
561 if (!IsPersonalDataLoaded())
565 if (!args->GetString(0, &guid)) {
570 CreditCard credit_card(guid, kSettingsOrigin);
573 if (args->GetString(1, &value))
574 credit_card.SetRawInfo(autofill::CREDIT_CARD_NAME, value);
576 if (args->GetString(2, &value))
577 credit_card.SetRawInfo(autofill::CREDIT_CARD_NUMBER, value);
579 if (args->GetString(3, &value))
580 credit_card.SetRawInfo(autofill::CREDIT_CARD_EXP_MONTH, value);
582 if (args->GetString(4, &value))
583 credit_card.SetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR, value);
585 if (!base::IsValidGUID(credit_card.guid())) {
586 credit_card.set_guid(base::GenerateGUID());
587 personal_data_->AddCreditCard(credit_card);
589 personal_data_->UpdateCreditCard(credit_card);
593 void AutofillOptionsHandler::ValidatePhoneNumbers(const ListValue* args) {
594 if (!IsPersonalDataLoaded())
597 scoped_ptr<ListValue> list_value = ValidatePhoneArguments(args);
599 web_ui()->CallJavascriptFunction(
600 "AutofillEditAddressOverlay.setValidatedPhoneNumbers", *list_value);
603 bool AutofillOptionsHandler::IsPersonalDataLoaded() const {
604 return personal_data_ && personal_data_->IsDataLoaded();
607 } // namespace options