diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index 92fb784fe2482e..ab3ce940cbff06 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn @@ -68,6 +68,7 @@ source_set("extensions") { "//third_party/cacheinvalidation", "//third_party/icu", "//third_party/leveldatabase", + "//third_party/libaddressinput:util", "//third_party/re2", "//third_party/webrtc/modules/desktop_capture", "//ui/accessibility:ax_gen", diff --git a/chrome/browser/extensions/api/autofill_private/DEPS b/chrome/browser/extensions/api/autofill_private/DEPS new file mode 100644 index 00000000000000..5e04926880183c --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/libaddressinput/src/cpp/include/libaddressinput", +] diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc index 6dcd5eaa2892c6..0ff52958577503 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.cc @@ -4,15 +4,181 @@ #include "chrome/browser/extensions/api/autofill_private/autofill_private_api.h" +#include "base/guid.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/common/extensions/api/autofill_private.h" +#include "chrome/grit/generated_resources.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function_registry.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui_component.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/localization.h" +#include "ui/base/l10n/l10n_util.h" + +namespace autofill_private = extensions::api::autofill_private; +namespace addressinput = i18n::addressinput; + +namespace { + +static const char kSettingsOrigin[] = "Chrome settings"; +static const char kErrorDataUnavailable[] = "Autofill data unavailable."; + +// Converts the UTF-8 strings in |input| to UTF-16 strings and adds them to +// |output|. +void UTF8VectorToUTF16Vector( + const std::vector& input, + std::vector* output) { + // Ensure output is clear. + output->clear(); + + for (const std::string& s : input) + output->push_back(base::UTF8ToUTF16(s)); +} + +// Fills |components| with the address UI components that should be used to +// input an address for |country_code| when UI BCP 47 language code is +// |ui_language_code|. +void PopulateAddressComponents( + const std::string& country_code, + const std::string& ui_language_code, + autofill_private::AddressComponents* address_components) { + DCHECK(address_components); + + i18n::addressinput::Localization localization; + localization.SetGetter(l10n_util::GetStringUTF8); + std::string best_address_language_code; + std::vector components = + i18n::addressinput::BuildComponents( + country_code, + localization, + ui_language_code, + &best_address_language_code); + if (components.empty()) { + static const char kDefaultCountryCode[] = "US"; + components = i18n::addressinput::BuildComponents( + kDefaultCountryCode, + localization, + ui_language_code, + &best_address_language_code); + } + address_components->language_code = best_address_language_code; + DCHECK(!components.empty()); + + autofill_private::AddressComponentRow* row = nullptr; + for (size_t i = 0; i < components.size(); ++i) { + if (!row || + components[i - 1].length_hint == + addressinput::AddressUiComponent::HINT_LONG || + components[i].length_hint == + addressinput::AddressUiComponent::HINT_LONG) { + row = new autofill_private::AddressComponentRow; + address_components->components.push_back(make_linked_ptr(row)); + } + + scoped_ptr + component(new autofill_private::AddressComponent); + component->field_name = components[i].name; + + switch (components[i].field) { + case i18n::addressinput::COUNTRY: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_COUNTRY_CODE; + break; + case i18n::addressinput::ADMIN_AREA: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_ADDRESS_LEVEL_1; + break; + case i18n::addressinput::LOCALITY: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_ADDRESS_LEVEL_2; + break; + case i18n::addressinput::DEPENDENT_LOCALITY: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_ADDRESS_LEVEL_3; + break; + case i18n::addressinput::SORTING_CODE: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_SORTING_CODE; + break; + case i18n::addressinput::POSTAL_CODE: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_POSTAL_CODE; + break; + case i18n::addressinput::STREET_ADDRESS: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_ADDRESS_LINES; + break; + case i18n::addressinput::ORGANIZATION: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_COMPANY_NAME; + break; + case i18n::addressinput::RECIPIENT: + component->field = + autofill_private::AddressField::ADDRESS_FIELD_FULL_NAME; + component->placeholder.reset(new std::string( + l10n_util::GetStringUTF8(IDS_AUTOFILL_FIELD_LABEL_ADD_NAME))); + break; + } + + switch (components[i].length_hint) { + case addressinput::AddressUiComponent::HINT_LONG: + component->is_long_field = true; + break; + case addressinput::AddressUiComponent::HINT_SHORT: + component->is_long_field = false; + break; + } + + row->row.push_back(make_linked_ptr(component.release())); + } +} + +// Searches the |list| for the value at |index|. If this value is present in +// any of the rest of the list, then the item (at |index|) is removed. The +// comparison of phone number values is done on normalized versions of the phone +// number values. +void RemoveDuplicatePhoneNumberAtIndex( + size_t index, const std::string& country_code, base::ListValue* list) { + base::string16 new_value; + if (!list->GetString(index, &new_value)) { + NOTREACHED() << "List should have a value at index " << index; + return; + } + + bool is_duplicate = false; + std::string app_locale = g_browser_process->GetApplicationLocale(); + for (size_t i = 0; i < list->GetSize() && !is_duplicate; ++i) { + if (i == index) + continue; + + base::string16 existing_value; + if (!list->GetString(i, &existing_value)) { + NOTREACHED() << "List should have a value at index " << i; + continue; + } + is_duplicate = autofill::i18n::PhoneNumbersMatch( + new_value, existing_value, country_code, app_locale); + } + + if (is_duplicate) + list->Remove(index, nullptr); +} + +} // namespace namespace extensions { //////////////////////////////////////////////////////////////////////////////// // AutofillPrivateSaveAddressFunction +AutofillPrivateSaveAddressFunction::AutofillPrivateSaveAddressFunction() + : chrome_details_(this) {} + AutofillPrivateSaveAddressFunction::~AutofillPrivateSaveAddressFunction() {} ExtensionFunction::ResponseAction AutofillPrivateSaveAddressFunction::Run() { @@ -20,13 +186,107 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveAddressFunction::Run() { api::autofill_private::SaveAddress::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + autofill::PersonalDataManager* personal_data = + autofill::PersonalDataManagerFactory::GetForProfile( + chrome_details_.GetProfile()); + if (!personal_data || !personal_data->IsDataLoaded()) { + error_ = kErrorDataUnavailable; + return RespondNow(NoArguments()); + } + + api::autofill_private::AddressEntry* address = ¶meters->address; + + std::string guid = address->guid ? *address->guid : ""; + autofill::AutofillProfile profile(guid, kSettingsOrigin); + + // Strings from JavaScript use UTF-8 encoding. This container is used as an + // intermediate container for functions which require UTF-16 strings. + std::vector string16Container; + + if (address->full_names) { + autofill::AutofillProfile* old_profile = + personal_data->GetProfileByGUID(guid); + UTF8VectorToUTF16Vector(*address->full_names, &string16Container); + profile.CopyAndUpdateNameList( + string16Container, + old_profile, + g_browser_process->GetApplicationLocale()); + } + + if (address->company_name) { + profile.SetRawInfo( + autofill::COMPANY_NAME, + base::UTF8ToUTF16(*address->company_name)); + } + + if (address->address_lines) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_STREET_ADDRESS, + base::UTF8ToUTF16(*address->address_lines)); + } + + if (address->address_level1) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_CITY, + base::UTF8ToUTF16(*address->address_level1)); + } + + if (address->address_level2) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_STATE, + base::UTF8ToUTF16(*address->address_level2)); + } + + if (address->address_level3) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_DEPENDENT_LOCALITY, + base::UTF8ToUTF16(*address->address_level3)); + } + + if (address->postal_code) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_ZIP, + base::UTF8ToUTF16(*address->postal_code)); + } + + if (address->sorting_code) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_SORTING_CODE, + base::UTF8ToUTF16(*address->sorting_code)); + } + + if (address->country_code) { + profile.SetRawInfo( + autofill::ADDRESS_HOME_COUNTRY, + base::UTF8ToUTF16(*address->country_code)); + } + + if (address->phone_numbers) { + UTF8VectorToUTF16Vector(*address->phone_numbers, &string16Container); + profile.SetRawMultiInfo( + autofill::PHONE_HOME_WHOLE_NUMBER, string16Container); + } + + if (address->email_addresses) { + UTF8VectorToUTF16Vector(*address->email_addresses, &string16Container); + profile.SetRawMultiInfo(autofill::EMAIL_ADDRESS, string16Container); + } + + if (address->language_code) + profile.set_language_code(*address->language_code); + + if (!base::IsValidGUID(profile.guid())) { + profile.set_guid(base::GenerateGUID()); + personal_data->AddProfile(profile); + } else { + personal_data->UpdateProfile(profile); + } return RespondNow(NoArguments()); } //////////////////////////////////////////////////////////////////////////////// -// AutofillPrivate*Function +// AutofillPrivateGetAddressComponentsFunction AutofillPrivateGetAddressComponentsFunction:: ~AutofillPrivateGetAddressComponentsFunction() {} @@ -37,14 +297,21 @@ ExtensionFunction::ResponseAction api::autofill_private::GetAddressComponents::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + autofill_private::AddressComponents components; + PopulateAddressComponents( + parameters->country_code, + g_browser_process->GetApplicationLocale(), + &components); - return RespondNow(NoArguments()); + return RespondNow(OneArgument(components.ToValue().release())); } //////////////////////////////////////////////////////////////////////////////// // AutofillPrivateSaveCreditCardFunction +AutofillPrivateSaveCreditCardFunction::AutofillPrivateSaveCreditCardFunction() + : chrome_details_(this) {} + AutofillPrivateSaveCreditCardFunction:: ~AutofillPrivateSaveCreditCardFunction() {} @@ -53,7 +320,49 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveCreditCardFunction::Run() { api::autofill_private::SaveCreditCard::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + autofill::PersonalDataManager* personal_data = + autofill::PersonalDataManagerFactory::GetForProfile( + chrome_details_.GetProfile()); + if (!personal_data || !personal_data->IsDataLoaded()) { + error_ = kErrorDataUnavailable; + return RespondNow(NoArguments()); + } + + api::autofill_private::CreditCardEntry* card = ¶meters->card; + + std::string guid = card->guid ? *card->guid : ""; + autofill::CreditCard credit_card(guid, kSettingsOrigin); + + if (card->name) { + credit_card.SetRawInfo( + autofill::CREDIT_CARD_NAME, + base::UTF8ToUTF16(*card->name)); + } + + if (card->card_number) { + credit_card.SetRawInfo( + autofill::CREDIT_CARD_NUMBER, + base::UTF8ToUTF16(*card->card_number)); + } + + if (card->expiration_month) { + credit_card.SetRawInfo( + autofill::CREDIT_CARD_EXP_MONTH, + base::UTF8ToUTF16(*card->expiration_month)); + } + + if (card->expiration_year) { + credit_card.SetRawInfo( + autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR, + base::UTF8ToUTF16(*card->expiration_year)); + } + + if (!base::IsValidGUID(credit_card.guid())) { + credit_card.set_guid(base::GenerateGUID()); + personal_data->AddCreditCard(credit_card); + } else { + personal_data->UpdateCreditCard(credit_card); + } return RespondNow(NoArguments()); } @@ -61,6 +370,9 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveCreditCardFunction::Run() { //////////////////////////////////////////////////////////////////////////////// // AutofillPrivateRemoveEntryFunction +AutofillPrivateRemoveEntryFunction::AutofillPrivateRemoveEntryFunction() + : chrome_details_(this) {} + AutofillPrivateRemoveEntryFunction::~AutofillPrivateRemoveEntryFunction() {} ExtensionFunction::ResponseAction AutofillPrivateRemoveEntryFunction::Run() { @@ -68,7 +380,15 @@ ExtensionFunction::ResponseAction AutofillPrivateRemoveEntryFunction::Run() { api::autofill_private::RemoveEntry::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + autofill::PersonalDataManager* personal_data = + autofill::PersonalDataManagerFactory::GetForProfile( + chrome_details_.GetProfile()); + if (!personal_data || !personal_data->IsDataLoaded()) { + error_ = kErrorDataUnavailable; + return RespondNow(NoArguments()); + } + + personal_data->RemoveByGUID(parameters->guid); return RespondNow(NoArguments()); } @@ -85,14 +405,24 @@ ExtensionFunction::ResponseAction api::autofill_private::ValidatePhoneNumbers::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + api::autofill_private::ValidatePhoneParams* params = ¶meters->params; - return RespondNow(NoArguments()); + // Extract the phone numbers into a ListValue. + scoped_ptr phoneNumbers(new base::ListValue); + phoneNumbers->AppendStrings(params->phone_numbers); + + RemoveDuplicatePhoneNumberAtIndex( + params->index_of_new_number, params->country_code, phoneNumbers.get()); + + return RespondNow(OneArgument(phoneNumbers.Pass())); } //////////////////////////////////////////////////////////////////////////////// // AutofillPrivateMaskCreditCardFunction +AutofillPrivateMaskCreditCardFunction::AutofillPrivateMaskCreditCardFunction() + : chrome_details_(this) {} + AutofillPrivateMaskCreditCardFunction:: ~AutofillPrivateMaskCreditCardFunction() {} @@ -101,7 +431,15 @@ ExtensionFunction::ResponseAction AutofillPrivateMaskCreditCardFunction::Run() { api::autofill_private::MaskCreditCard::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + autofill::PersonalDataManager* personal_data = + autofill::PersonalDataManagerFactory::GetForProfile( + chrome_details_.GetProfile()); + if (!personal_data || !personal_data->IsDataLoaded()) { + error_ = kErrorDataUnavailable; + return RespondNow(NoArguments()); + } + + personal_data->ResetFullServerCard(parameters->guid); return RespondNow(NoArguments()); } diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_api.h b/chrome/browser/extensions/api/autofill_private/autofill_private_api.h index 776bd3ab678a4c..819881a2c3ab0a 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_private_api.h +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_api.h @@ -8,13 +8,14 @@ #include #include "base/macros.h" +#include "chrome/browser/extensions/chrome_extension_function_details.h" #include "extensions/browser/extension_function.h" namespace extensions { class AutofillPrivateSaveAddressFunction : public UIThreadExtensionFunction { public: - AutofillPrivateSaveAddressFunction() {} + AutofillPrivateSaveAddressFunction(); DECLARE_EXTENSION_FUNCTION("autofillPrivate.saveAddress", AUTOFILLPRIVATE_SAVEADDRESS); @@ -25,6 +26,8 @@ class AutofillPrivateSaveAddressFunction : public UIThreadExtensionFunction { ResponseAction Run() override; private: + ChromeExtensionFunctionDetails chrome_details_; + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateSaveAddressFunction); }; @@ -47,7 +50,7 @@ class AutofillPrivateGetAddressComponentsFunction : class AutofillPrivateSaveCreditCardFunction : public UIThreadExtensionFunction { public: - AutofillPrivateSaveCreditCardFunction() {} + AutofillPrivateSaveCreditCardFunction(); DECLARE_EXTENSION_FUNCTION("autofillPrivate.saveCreditCard", AUTOFILLPRIVATE_SAVECREDITCARD); @@ -58,12 +61,14 @@ class AutofillPrivateSaveCreditCardFunction : public UIThreadExtensionFunction { ResponseAction Run() override; private: + ChromeExtensionFunctionDetails chrome_details_; + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateSaveCreditCardFunction); }; class AutofillPrivateRemoveEntryFunction : public UIThreadExtensionFunction { public: - AutofillPrivateRemoveEntryFunction() {} + AutofillPrivateRemoveEntryFunction(); DECLARE_EXTENSION_FUNCTION("autofillPrivate.removeEntry", AUTOFILLPRIVATE_REMOVEENTRY); @@ -74,6 +79,8 @@ class AutofillPrivateRemoveEntryFunction : public UIThreadExtensionFunction { ResponseAction Run() override; private: + ChromeExtensionFunctionDetails chrome_details_; + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateRemoveEntryFunction); }; @@ -96,7 +103,7 @@ class AutofillPrivateValidatePhoneNumbersFunction : class AutofillPrivateMaskCreditCardFunction : public UIThreadExtensionFunction { public: - AutofillPrivateMaskCreditCardFunction() {} + AutofillPrivateMaskCreditCardFunction(); DECLARE_EXTENSION_FUNCTION("autofillPrivate.maskCreditCard", AUTOFILLPRIVATE_MASKCREDITCARD); @@ -107,6 +114,8 @@ class AutofillPrivateMaskCreditCardFunction : public UIThreadExtensionFunction { ResponseAction Run() override; private: + ChromeExtensionFunctionDetails chrome_details_; + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateMaskCreditCardFunction); }; diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc new file mode 100644 index 00000000000000..9c09abea5201d4 --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_apitest.cc @@ -0,0 +1,65 @@ +// Copyright 2015 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 "base/command_line.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/extensions/api/autofill_private.h" +#include "components/keyed_service/core/keyed_service.h" +#include "content/public/test/test_utils.h" +#include "extensions/common/switches.h" + +namespace extensions { + +namespace { + +class AutofillPrivateApiTest : public ExtensionApiTest { + public: + AutofillPrivateApiTest() {} + ~AutofillPrivateApiTest() override {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + ExtensionApiTest::SetUpCommandLine(command_line); + } + + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + content::RunAllPendingInMessageLoop(); + } + + protected: + bool RunAutofillSubtest(const std::string& subtest) { + return RunExtensionSubtest("autofill_private", + "main.html?" + subtest, + kFlagLoadAsComponent); + } + + private: + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateApiTest); +}; + +} // namespace + +IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, SaveAddress) { + EXPECT_TRUE(RunAutofillSubtest("saveAddress")) << message_; +} + +IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, GetAddressComponents) { + EXPECT_TRUE(RunAutofillSubtest("getAddressComponents")) << message_; +} + +IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, SaveCreditCard) { + EXPECT_TRUE(RunAutofillSubtest("saveCreditCard")) << message_; +} + +IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, RemoveEntry) { + EXPECT_TRUE(RunAutofillSubtest("removeEntry")) << message_; +} + +IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, ValidatePhoneNumbers) { + EXPECT_TRUE(RunAutofillSubtest("ValidatePhoneNumbers")) << message_; +} + +} // namespace extensions + diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc new file mode 100644 index 00000000000000..e4f737a59eeb83 --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.cc @@ -0,0 +1,124 @@ +// Copyright 2015 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/extensions/api/autofill_private/autofill_private_event_router.h" + +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/autofill/personal_data_manager_factory.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/autofill_private/autofill_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/search_engines/template_url_service_factory.h" +#include "chrome/common/extensions/api/autofill_private.h" +#include "components/autofill/core/browser/personal_data_manager.h" +#include "content/public/browser/browser_context.h" + +using AddressEntryList = std::vector >; +using CreditCardEntryList = std::vector >; + +namespace extensions { + +AutofillPrivateEventRouter::AutofillPrivateEventRouter( + content::BrowserContext* context) + : context_(context), + event_router_(nullptr), + personal_data_(nullptr), + listening_(false) { + // Register with the event router so we know when renderers are listening to + // our events. We first check and see if there *is* an event router, because + // some unit tests try to create all context services, but don't initialize + // the event router first. + event_router_ = EventRouter::Get(context_); + if (!event_router_) + return; + + personal_data_ = autofill::PersonalDataManagerFactory::GetForProfile( + Profile::FromBrowserContext(context_)); + if (!personal_data_) + return; + + event_router_->RegisterObserver( + this, + api::autofill_private::OnAddressListChanged::kEventName); + event_router_->RegisterObserver( + this, + api::autofill_private::OnCreditCardListChanged::kEventName); + StartOrStopListeningForChanges(); +} + +AutofillPrivateEventRouter::~AutofillPrivateEventRouter() { +} + +void AutofillPrivateEventRouter::Shutdown() { + if (event_router_) + event_router_->UnregisterObserver(this); +} + +void AutofillPrivateEventRouter::OnListenerAdded( + const EventListenerInfo& details) { + // Start listening to change events and propagate the original lists to + // listeners. + StartOrStopListeningForChanges(); + OnPersonalDataChanged(); +} + +void AutofillPrivateEventRouter::OnListenerRemoved( + const EventListenerInfo& details) { + // Stop listening to events if there are no more listeners. + StartOrStopListeningForChanges(); +} + +void AutofillPrivateEventRouter::OnPersonalDataChanged() { + DCHECK(personal_data_ && personal_data_->IsDataLoaded()); + + scoped_ptr addressList = + extensions::autofill_util::GenerateAddressList(*personal_data_); + scoped_ptr args( + api::autofill_private::OnAddressListChanged::Create(*addressList) + .release()); + scoped_ptr extension_event(new Event( + api::autofill_private::OnAddressListChanged::kEventName, args.Pass())); + event_router_->BroadcastEvent(extension_event.Pass()); + + scoped_ptr creditCardList = + extensions::autofill_util::GenerateCreditCardList(*personal_data_); + args.reset( + api::autofill_private::OnCreditCardListChanged::Create(*creditCardList) + .release()); + extension_event.reset(new Event( + api::autofill_private::OnCreditCardListChanged::kEventName, args.Pass())); + event_router_->BroadcastEvent(extension_event.Pass()); +} + +void AutofillPrivateEventRouter::StartOrStopListeningForChanges() { + if (!personal_data_ || !personal_data_->IsDataLoaded()) + return; + + bool should_listen_for_address_changes = event_router_->HasEventListener( + api::autofill_private::OnAddressListChanged::kEventName); + bool should_listen_for_credit_card_changes = event_router_->HasEventListener( + api::autofill_private::OnCreditCardListChanged::kEventName); + bool should_listen = should_listen_for_address_changes || + should_listen_for_credit_card_changes; + + if (should_listen && !listening_) + personal_data_->AddObserver(this); + else if (!should_listen && listening_) + personal_data_->RemoveObserver(this); + + listening_ = should_listen; +} + +AutofillPrivateEventRouter* AutofillPrivateEventRouter::Create( + content::BrowserContext* context) { + return new AutofillPrivateEventRouter(context); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h new file mode 100644 index 00000000000000..65be92081c567a --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h @@ -0,0 +1,66 @@ +// Copyright 2015 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_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_EVENT_ROUTER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_EVENT_ROUTER_H_ + +#include "components/autofill/core/browser/personal_data_manager_observer.h" +#include "components/keyed_service/core/keyed_service.h" +#include "extensions/browser/event_router.h" + +namespace autofill { +class PersonalDataManager; +} + +namespace content { +class BrowserContext; +} + +namespace extensions { + +// An event router that observes changes to autofill addresses and credit cards +// and notifies listeners to the onAddressListChanged and +// onCreditCardListChanged events of changes. +class AutofillPrivateEventRouter : + public KeyedService, + public EventRouter::Observer, + public autofill::PersonalDataManagerObserver { + public: + static AutofillPrivateEventRouter* Create( + content::BrowserContext* browser_context); + ~AutofillPrivateEventRouter() override; + + protected: + explicit AutofillPrivateEventRouter(content::BrowserContext* context); + + // KeyedService overrides: + void Shutdown() override; + + // EventRouter::Observer overrides: + void OnListenerAdded(const EventListenerInfo& details) override; + void OnListenerRemoved(const EventListenerInfo& details) override; + + // PersonalDataManagerObserver implementation. + void OnPersonalDataChanged() override; + + private: + // Either listens or unlistens for changes to |personal_data_|, depending on + // whether clients are listening to the autofillPrivate API events. + void StartOrStopListeningForChanges(); + + content::BrowserContext* context_; + + EventRouter* event_router_; + + autofill::PersonalDataManager* personal_data_; + + // Whether this class is currently listening for changes to |personal_data_|. + bool listening_; + + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateEventRouter); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_EVENT_ROUTER_H_ diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc new file mode 100644 index 00000000000000..a39f6b655b2d4f --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc @@ -0,0 +1,60 @@ +// Copyright 2015 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/extensions/api/autofill_private/autofill_private_event_router_factory.h" + +#include "chrome/browser/extensions/api/autofill_private/autofill_private_event_router.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/extension_system_provider.h" +#include "extensions/browser/extensions_browser_client.h" + +namespace extensions { + +// static +AutofillPrivateEventRouter* +AutofillPrivateEventRouterFactory::GetForProfile( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +AutofillPrivateEventRouterFactory* +AutofillPrivateEventRouterFactory::GetInstance() { + return Singleton::get(); +} + +AutofillPrivateEventRouterFactory::AutofillPrivateEventRouterFactory() + : BrowserContextKeyedServiceFactory( + "AutofillPrivateEventRouter", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); +} + +AutofillPrivateEventRouterFactory:: + ~AutofillPrivateEventRouterFactory() { +} + +KeyedService* AutofillPrivateEventRouterFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return AutofillPrivateEventRouter::Create(context); +} + +content::BrowserContext* +AutofillPrivateEventRouterFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +bool AutofillPrivateEventRouterFactory:: + ServiceIsCreatedWithBrowserContext() const { + return true; +} + +bool AutofillPrivateEventRouterFactory::ServiceIsNULLWhileTesting() const { + return false; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h new file mode 100644 index 00000000000000..f7238ad7c9168b --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h @@ -0,0 +1,51 @@ +// Copyright 2015 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_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_EVENT_ROUTER_FACTORY_H_ +#define CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_EVENT_ROUTER_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace extensions { + +class AutofillPrivateEventRouter; + +// This is a factory class used by the BrowserContextDependencyManager +// to instantiate the autofillPrivate event router per profile (since the +// extension event router is per profile). +class AutofillPrivateEventRouterFactory + : public BrowserContextKeyedServiceFactory { + public: + // Returns the AutofillPrivateEventRouter for |profile|, creating it if + // it is not yet created. + static AutofillPrivateEventRouter* GetForProfile( + content::BrowserContext* context); + + // Returns the AutofillPrivateEventRouterFactory instance. + static AutofillPrivateEventRouterFactory* GetInstance(); + + protected: + // BrowserContextKeyedBaseFactory overrides: + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + bool ServiceIsNULLWhileTesting() const override; + + private: + friend struct DefaultSingletonTraits; + + AutofillPrivateEventRouterFactory(); + ~AutofillPrivateEventRouterFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateEventRouterFactory); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_EVENT_ROUTER_FACTORY_H_ diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc new file mode 100644 index 00000000000000..391ca1f4efe1c7 --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc @@ -0,0 +1,187 @@ +// Copyright 2015 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/extensions/api/autofill_private/autofill_util.h" + +#include "base/prefs/pref_service.h" +#include "base/strings/string_split.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/settings_private/prefs_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/autofill_private.h" +#include "chrome/common/pref_names.h" +#include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/autofill_type.h" +#include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/field_types.h" +#include "grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" + +namespace autofill_private = extensions::api::autofill_private; + +namespace { + +// Get the multi-valued element for |type| and return it as a |vector|. +scoped_ptr> GetValueList( + const autofill::AutofillProfile& profile, autofill::ServerFieldType type) { + scoped_ptr> list(new std::vector); + + std::vector values; + if (autofill::AutofillType(type).group() == autofill::NAME) { + profile.GetMultiInfo( + autofill::AutofillType(type), + g_browser_process->GetApplicationLocale(), + &values); + } else { + profile.GetRawMultiInfo(type, &values); + } + + // |Get[Raw]MultiInfo()| always returns at least one, potentially empty, item. + // If this is the case, there is no info to return, so return an empty vector. + if (values.size() == 1 && values.front().empty()) + return list.Pass(); + + for (const base::string16& value16 : values) + list->push_back(base::UTF16ToUTF8(value16)); + + return list.Pass(); +} + +// Gets the string corresponding to |type| from |profile|. +scoped_ptr GetStringFromProfile( + const autofill::AutofillProfile& profile, + const autofill::ServerFieldType& type) { + return make_scoped_ptr( + new std::string(base::UTF16ToUTF8(profile.GetRawInfo(type)))); +} + +scoped_ptr ProfileToAddressEntry( + const autofill::AutofillProfile& profile, + const base::string16& label) { + scoped_ptr + address(new autofill_private::AddressEntry); + + // Add all address fields to the entry. + address->guid.reset(new std::string(profile.guid())); + address->full_names = GetValueList(profile, autofill::NAME_FULL); + address->company_name.reset(GetStringFromProfile( + profile, autofill::COMPANY_NAME).release()); + address->address_lines.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_STREET_ADDRESS).release()); + address->address_level1.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_STATE).release()); + address->address_level2.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_CITY).release()); + address->address_level3.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_DEPENDENT_LOCALITY).release()); + address->postal_code.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_ZIP).release()); + address->sorting_code.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_SORTING_CODE).release()); + address->country_code.reset(GetStringFromProfile( + profile, autofill::ADDRESS_HOME_COUNTRY).release()); + address->phone_numbers = + GetValueList(profile, autofill::PHONE_HOME_WHOLE_NUMBER); + address->email_addresses = + GetValueList(profile, autofill::EMAIL_ADDRESS); + address->language_code.reset(new std::string(profile.language_code())); + + // Parse |label| so that it can be used to create address metadata. + base::string16 separator = + l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR); + std::vector label_pieces; + base::SplitStringUsingSubstr(label, separator, &label_pieces); + + // Create address metadata and add it to |address|. + scoped_ptr + metadata(new autofill_private::AutofillMetadata); + metadata->summary_label = base::UTF16ToUTF8(label_pieces[0]); + metadata->summary_sublabel.reset(new std::string(base::UTF16ToUTF8( + label.substr(label_pieces[0].size())))); + metadata->is_local.reset(new bool( + profile.record_type() == autofill::AutofillProfile::LOCAL_PROFILE)); + address->metadata = metadata.Pass(); + + return address.Pass(); +} + +scoped_ptr CreditCardToCreditCardEntry( + const autofill::CreditCard& credit_card) { + scoped_ptr + card(new autofill_private::CreditCardEntry); + + // Add all credit card fields to the entry. + card->guid.reset(new std::string(credit_card.guid())); + card->name.reset(new std::string(base::UTF16ToUTF8( + credit_card.GetRawInfo(autofill::CREDIT_CARD_NAME)))); + card->card_number.reset(new std::string(base::UTF16ToUTF8( + credit_card.GetRawInfo(autofill::CREDIT_CARD_NUMBER)))); + card->expiration_month.reset(new std::string(base::UTF16ToUTF8( + credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH)))); + card->expiration_year.reset(new std::string(base::UTF16ToUTF8( + credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR)))); + + // Create address metadata and add it to |address|. + scoped_ptr + metadata(new autofill_private::AutofillMetadata); + std::pair label_pieces = + credit_card.LabelPieces(); + metadata->summary_label = base::UTF16ToUTF8(label_pieces.first); + metadata->summary_sublabel.reset(new std::string(base::UTF16ToUTF8( + label_pieces.second))); + metadata->is_local.reset(new bool( + credit_card.record_type() == autofill::CreditCard::LOCAL_CARD)); + metadata->is_cached.reset(new bool( + credit_card.record_type() == autofill::CreditCard::FULL_SERVER_CARD)); + card->metadata = metadata.Pass(); + + return card.Pass(); +} + +} // namespace + +namespace extensions { + +namespace autofill_util { + +scoped_ptr GenerateAddressList( + const autofill::PersonalDataManager& personal_data) { + const std::vector& profiles = + personal_data.GetProfiles(); + std::vector labels; + autofill::AutofillProfile::CreateDifferentiatingLabels( + profiles, + g_browser_process->GetApplicationLocale(), + &labels); + DCHECK_EQ(labels.size(), profiles.size()); + + scoped_ptr list(new AddressEntryList); + for (size_t i = 0; i < profiles.size(); ++i) { + autofill_private::AddressEntry* entry = + ProfileToAddressEntry(*profiles[i], labels[i]).release(); + list->push_back(linked_ptr(entry)); + } + + return list.Pass(); +} + +scoped_ptr GenerateCreditCardList( + const autofill::PersonalDataManager& personal_data) { + const std::vector& cards = + personal_data.GetCreditCards(); + + scoped_ptr list(new CreditCardEntryList); + for (const autofill::CreditCard* card : cards) { + autofill_private::CreditCardEntry* entry = + CreditCardToCreditCardEntry(*card).release(); + list->push_back(linked_ptr(entry)); + } + + return list.Pass(); +} + +} // namespace autofill_util + +} // namespace extensions diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.h b/chrome/browser/extensions/api/autofill_private/autofill_util.h new file mode 100644 index 00000000000000..5d47e650140957 --- /dev/null +++ b/chrome/browser/extensions/api/autofill_private/autofill_util.h @@ -0,0 +1,43 @@ +// Copyright 2015 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_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_UTIL_H_ +#define CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_UTIL_H_ + +#include +#include + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/common/extensions/api/autofill_private.h" +#include "components/autofill/core/browser/personal_data_manager.h" + +namespace autofill { +class AutofillProfile; +class CreditCard; +} + +namespace extensions { + +namespace autofill_util { + +using AddressEntryList = std::vector >; +using CreditCardEntryList = std::vector >; + +// Uses |personal_data| to generate a list of up-to-date AddressEntry objects. +scoped_ptr GenerateAddressList( + const autofill::PersonalDataManager& personal_data); + +// Uses |personal_data| to generate a list of up-to-date CreditCardEntry +// objects. +scoped_ptr GenerateCreditCardList( + const autofill::PersonalDataManager& personal_data); + +} // namespace autofill_util + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_UTIL_H_ diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc index 4f55a320635f4d..bbe0cee917ba9b 100644 --- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc +++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc @@ -6,6 +6,7 @@ #include "chrome/browser/extensions/activity_log/activity_log.h" #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h" +#include "chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h" #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h" #include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" #include "chrome/browser/extensions/api/braille_display_private/braille_display_private_api.h" @@ -80,6 +81,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { extensions::ActivityLogAPI::GetFactoryInstance(); extensions::ApiResourceManager< extensions::UsbDeviceResource>::GetFactoryInstance(); + extensions::AutofillPrivateEventRouterFactory::GetInstance(); extensions::BookmarksAPI::GetFactoryInstance(); extensions::BookmarkManagerPrivateAPI::GetFactoryInstance(); extensions::BluetoothAPI::GetFactoryInstance(); diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 111b8a1febfe11..8d734275b6480d 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -123,6 +123,12 @@ 'browser/extensions/api/audio_modem/audio_modem_api.h', 'browser/extensions/api/autofill_private/autofill_private_api.cc', 'browser/extensions/api/autofill_private/autofill_private_api.h', + 'browser/extensions/api/autofill_private/autofill_private_event_router.cc', + 'browser/extensions/api/autofill_private/autofill_private_event_router.h', + 'browser/extensions/api/autofill_private/autofill_private_event_router_factory.cc', + 'browser/extensions/api/autofill_private/autofill_private_event_router_factory.h', + 'browser/extensions/api/autofill_private/autofill_util.cc', + 'browser/extensions/api/autofill_private/autofill_util.h', 'browser/extensions/api/automation_internal/automation_action_adapter.h', 'browser/extensions/api/automation_internal/automation_internal_api.cc', 'browser/extensions/api/automation_internal/automation_internal_api.h', @@ -916,6 +922,7 @@ '../third_party/icu/icu.gyp:icuuc', '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase', '../third_party/re2/re2.gyp:re2', + '../third_party/libaddressinput/libaddressinput.gyp:libaddressinput_util', '../third_party/webrtc/modules/modules.gyp:desktop_capture', '../ui/accessibility/accessibility.gyp:ax_gen', '../ui/base/ui_base.gyp:ui_base', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 4298b8e25e6c25..7125fa747fdc66 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -119,6 +119,7 @@ 'browser/extensions/alert_apitest.cc', 'browser/extensions/all_urls_apitest.cc', 'browser/extensions/api/activity_log_private/activity_log_private_apitest.cc', + 'browser/extensions/api/autofill_private/autofill_private_apitest.cc', 'browser/extensions/api/automation/automation_apitest.cc', 'browser/extensions/api/autotest_private/autotest_private_apitest.cc', 'browser/extensions/api/bookmark_manager_private/bookmark_manager_private_apitest.cc', diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl index 51124d99b18008..43edb3d0d28b0d 100644 --- a/chrome/common/extensions/api/autofill_private.idl +++ b/chrome/common/extensions/api/autofill_private.idl @@ -80,7 +80,9 @@ namespace autofillPrivate { // a sorting code system is CEDEX in France. DOMString? sortingCode; - DOMString? country; + // A two-character string representing the address' country. See + // autofill_country.cc for a list of valid codes. + DOMString? countryCode; DOMString[]? phoneNumbers; @@ -94,7 +96,7 @@ namespace autofillPrivate { // A component to be shown in an address editor. Different countries have // different components to their addresses. dictionary AddressComponent { - // The type of field that this is. + // The field type. AddressField field; // The name of the field. @@ -102,6 +104,17 @@ namespace autofillPrivate { // A hint for the UI regarding whether the input is likely to be long. boolean isLongField; + + // A placeholder for the text field to be used when the user has not yet + // input a value for the field. + DOMString? placeholder; + }; + + // A row of address components. Each component in a row should be shown in the + // same row in the UI. For example, city, state, and zip code are all included + // on the same line for US addresses. + dictionary AddressComponentRow { + AddressComponent[] row; }; // The address components for a given country code. Each entry in |components| @@ -111,7 +124,7 @@ namespace autofillPrivate { // includes the associated language code. dictionary AddressComponents { // The components. - AddressComponent[][] components; + AddressComponentRow[] components; // The language code. DOMString languageCode; @@ -147,7 +160,8 @@ namespace autofillPrivate { // number resides. long indexOfNewNumber; - // The country code for the numbers. + // A two-character string representing the address' country. See + // autofill_country.cc for a list of valid codes. DOMString countryCode; }; @@ -165,7 +179,9 @@ namespace autofillPrivate { // Gets the address components for a given country code. // - // |countryCode|: The country code for which to fetch the components. + // |countryCode|: A two-character string representing the address' country + // whose components should be returned. See autofill_country.cc for a + // list of valid codes. // |callback|: Callback which will be called with components. static void getAddressComponents(DOMString countryCode, GetAddressComponentsCallback callback); @@ -187,7 +203,7 @@ namespace autofillPrivate { // // |params|: The parameters to this function. // |callback|: Callback which will be called with validated phone numbers. - static DOMString[] validatePhoneNumbers(ValidatePhoneParams params, + static void validatePhoneNumbers(ValidatePhoneParams params, ValidatePhoneNumbersCallback callback); // Clears the data associated with a wallet card which was saved diff --git a/chrome/test/data/extensions/api_test/autofill_private/main.html b/chrome/test/data/extensions/api_test/autofill_private/main.html new file mode 100644 index 00000000000000..7df6dbb9e543f1 --- /dev/null +++ b/chrome/test/data/extensions/api_test/autofill_private/main.html @@ -0,0 +1,11 @@ + + + + +autofillPrivate component API interface test +

chrome.autofillPrivate.* tests

+ \ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/autofill_private/manifest.json b/chrome/test/data/extensions/api_test/autofill_private/manifest.json new file mode 100644 index 00000000000000..5a2f254b6c8a31 --- /dev/null +++ b/chrome/test/data/extensions/api_test/autofill_private/manifest.json @@ -0,0 +1,10 @@ +{ + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC74Vbx3EbhPc/FOvn6+HxCjMSml0HdPMiuRjj5a3b+MnRML1iJ9OAgbKUYJ/u3s25/cGq8pNB0NbyupHGEqvqAE7TcNr1mdgs0PWxh2IOI1GKrxlzxpqzQuFmxq5WHKr5RrwZ4/Xq0t/+e8JkvhZdW0jarz/28Jom0gkM5lorsewIDAQAB", + "name": "autofillPrivate API interface test", + "version": "0.1", + "manifest_version": 2, + "description": "Test of chrome.autofillPrivate interface", + "permissions": [ + "autofillPrivate" + ] +} \ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/autofill_private/test.js b/chrome/test/data/extensions/api_test/autofill_private/test.js new file mode 100644 index 00000000000000..3097ba0790f4cc --- /dev/null +++ b/chrome/test/data/extensions/api_test/autofill_private/test.js @@ -0,0 +1,146 @@ +// Copyright 2015 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. + +// This just tests the interface. It does not test for specific results, only +// that callbacks are correctly invoked, expected parameters are correct, +// and failures are detected. + +var availableTests = [ + function saveAddress() { + var NAME = 'Name'; + + var numCalls = 0; + var handler = function(addressList) { + numCalls++; + + if (numCalls == 1) { + chrome.test.assertEq(addressList.length, 0); + } else { + chrome.test.assertEq(addressList.length, 1); + var address = addressList[0]; + chrome.test.assertEq(address.fullNames[0], NAME); + chrome.test.succeed(); + } + } + + chrome.autofillPrivate.onAddressListChanged.addListener(handler); + chrome.autofillPrivate.saveAddress({fullNames: [NAME]}); + }, + + function getAddressComponents() { + var COUNTRY_CODE = 'US'; + + var handler = function(components) { + chrome.test.assertTrue(!!components.components); + chrome.test.assertTrue(!!components.components[0]); + chrome.test.assertTrue(!!components.components[0].row); + chrome.test.assertTrue(!!components.components[0].row[0]); + chrome.test.assertTrue(!!components.components[0].row[0].field); + chrome.test.assertTrue(!!components.components[0].row[0].fieldName); + chrome.test.assertTrue(!!components.languageCode); + chrome.test.succeed(); + } + + chrome.autofillPrivate.getAddressComponents(COUNTRY_CODE, handler); + }, + + function saveCreditCard() { + var NAME = 'Name'; + + var numCalls = 0; + var handler = function(creditCardList) { + numCalls++; + + if (numCalls == 1) { + chrome.test.assertEq(creditCardList.length, 0); + } else { + chrome.test.assertEq(creditCardList.length, 1); + var creditCard = creditCardList[0]; + chrome.test.assertEq(creditCard.name, NAME); + chrome.test.succeed(); + } + } + + chrome.autofillPrivate.onCreditCardListChanged.addListener(handler); + chrome.autofillPrivate.saveCreditCard({name: NAME}); + }, + + function removeEntry() { + var NAME = 'Name'; + var guid; + + var numCalls = 0; + var handler = function(creditCardList) { + numCalls++; + + if (numCalls == 1) { + chrome.test.assertEq(creditCardList.length, 0); + } else if (numCalls == 2) { + chrome.test.assertEq(creditCardList.length, 1); + var creditCard = creditCardList[0]; + chrome.test.assertEq(creditCard.name, NAME); + + guid = creditCard.guid; + chrome.autofillPrivate.removeEntry(guid); + } else { + chrome.test.assertEq(creditCardList.length, 0); + chrome.test.succeed(); + } + } + + chrome.autofillPrivate.onCreditCardListChanged.addListener(handler); + chrome.autofillPrivate.saveCreditCard({name: NAME}); + }, + + function validatePhoneNumbers() { + var COUNTRY_CODE = 'US'; + var ORIGINAL_NUMBERS = ['1-800-123-4567']; + var FIRST_NUMBER_TO_ADD = '1-800-234-5768'; + // Same as original number, but without formatting. + var SECOND_NUMBER_TO_ADD = '18001234567'; + + var handler1 = function(validateNumbers) { + chrome.test.assertEq(validateNumbers.length, 1); + chrome.test.assertEq('1-800-123-4567', validateNumbers[0]); + + chrome.autofillPrivate.validatePhoneNumbers({ + phoneNumbers: validatedNumbers.concat(FIRST_NUMBER_TO_ADD), + indexOfNewNumber: 0, + countryCode: COUNTRY_CODE + }, handler2); + } + + var handler2 = function(validatedNumbers) { + chrome.test.assertEq(validateNumbers.length, 2); + chrome.test.assertEq('1-800-123-4567', validateNumbers[0]); + chrome.test.assertEq('1-800-234-5678', validateNumbers[1]); + + chrome.autofillPrivate.validatePhoneNumbers({ + phoneNumbers: validatedNumbers.concat(SECOND_NUMBER_TO_ADD), + indexOfNewNumber: 0, + countryCode: COUNTRY_CODE + }, handler3); + }; + + var handler3 = function(validateNumbers) { + // Newly-added number should not appear since it was the same as an + // existing number. + chrome.test.assertEq(validateNumbers.length, 2); + chrome.test.assertEq('1-800-123-4567', validateNumbers[0]); + chrome.test.assertEq('1-800-234-5678', validateNumbers[1]); + chrome.test.succeed(); + } + + chrome.autofillPrivate.validatePhoneNumbers({ + phoneNumbers: ORIGINAL_NUMBERS, + indexOfNewNumber: 0, + countryCode: COUNTRY_CODE + }, handler1); + }, +]; + +var testToRun = window.location.search.substring(1); +chrome.test.runTests(availableTests.filter(function(op) { + return op.name == testToRun; +})); diff --git a/third_party/closure_compiler/externs/autofill_private.js b/third_party/closure_compiler/externs/autofill_private.js index 84d25216705a0f..05ea05e2c6fbde 100644 --- a/third_party/closure_compiler/externs/autofill_private.js +++ b/third_party/closure_compiler/externs/autofill_private.js @@ -47,7 +47,7 @@ var AutofillMetadata; * addressLevel3: (string|undefined), * postalCode: (string|undefined), * sortingCode: (string|undefined), - * country: (string|undefined), + * countryCode: (string|undefined), * phoneNumbers: (!Array|undefined), * emailAddresses: (!Array|undefined), * languageCode: (string|undefined), @@ -59,9 +59,10 @@ var AddressEntry; /** * @typedef {{ - * field: !chrome.autofillPrivate.AddressField, + * field: number, * fieldName: string, - * isLongField: boolean + * isLongField: boolean, + * placeholder: (string|undefined) * }} * @see https://developer.chrome.com/extensions/autofillPrivate#type-AddressComponent */ @@ -69,7 +70,15 @@ var AddressComponent; /** * @typedef {{ - * components: !Array, + * row: !Array + * }} + * @see https://developer.chrome.com/extensions/autofillPrivate#type-AddressComponentRow + */ +var AddressComponentRow; + +/** + * @typedef {{ + * components: !Array, * languageCode: string * }} * @see https://developer.chrome.com/extensions/autofillPrivate#type-AddressComponents @@ -109,8 +118,9 @@ chrome.autofillPrivate.saveAddress = function(address) {}; /** * Gets the address components for a given country code. - * @param {string} countryCode The country code for which to fetch the - * components. + * @param {string} countryCode A two-character string representing the address' + * country whose components should be returned. See autofill_country.cc + * for a list of valid codes. * @param {function(AddressComponents):void} callback Callback which will be * called with components. * @see https://developer.chrome.com/extensions/autofillPrivate#method-getAddressComponents @@ -139,7 +149,6 @@ chrome.autofillPrivate.removeEntry = function(guid) {}; * @param {ValidatePhoneParams} params The parameters to this function. * @param {function(!Array):void} callback Callback which will be called * with validated phone numbers. - * @return {!Array} * @see https://developer.chrome.com/extensions/autofillPrivate#method-validatePhoneNumbers */ chrome.autofillPrivate.validatePhoneNumbers = function(params, callback) {};