diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-12 14:07:37 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-07-17 10:29:26 +0000 |
commit | ec02ee4181c49b61fce1c8fb99292dbb8139cc90 (patch) | |
tree | 25cde714b2b71eb639d1cd53f5a22e9ba76e14ef /chromium/components/autofill/core | |
parent | bb09965444b5bb20b096a291445170876225268d (diff) | |
download | qtwebengine-chromium-ec02ee4181c49b61fce1c8fb99292dbb8139cc90.tar.gz |
BASELINE: Update Chromium to 59.0.3071.134
Change-Id: Id02ef6fb2204c5fd21668a1c3e6911c83b17585a
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/autofill/core')
93 files changed, 5998 insertions, 2725 deletions
diff --git a/chromium/components/autofill/core/browser/BUILD.gn b/chromium/components/autofill/core/browser/BUILD.gn index 4e6332c41f8..87a22f6efa1 100644 --- a/chromium/components/autofill/core/browser/BUILD.gn +++ b/chromium/components/autofill/core/browser/BUILD.gn @@ -18,6 +18,8 @@ static_library("browser") { "autocomplete_history_manager.cc", "autocomplete_history_manager.h", "autofill-inl.h", + "autofill_address_util.cc", + "autofill_address_util.h", "autofill_client.h", "autofill_country.cc", "autofill_country.h", @@ -109,6 +111,9 @@ static_library("browser") { "phone_number_i18n.cc", "phone_number_i18n.h", "popup_item_ids.h", + "region_combobox_model.cc", + "region_combobox_model.h", + "risk_data_loader.h", "server_field_types_util.cc", "server_field_types_util.h", "state_names.cc", @@ -178,12 +183,19 @@ static_library("browser") { ] } + if (!is_android) { + sources += [ + "ui/save_card_bubble_controller.h", + ] + } + configs += [ "//build/config:precompiled_headers" ] public_deps = [ "//components/autofill/core/browser/proto", "//components/autofill/core/common", "//components/resources", + "//skia", ] deps = [ "//base", @@ -206,11 +218,10 @@ static_library("browser") { "//components/webdata/common", "//google_apis", "//net", - "//skia", "//sql", "//third_party/fips181", "//third_party/icu", - "//third_party/libaddressinput:util", + "//third_party/libaddressinput", "//third_party/libphonenumber", "//third_party/re2", "//ui/base", @@ -247,6 +258,13 @@ static_library("test_support") { "test_personal_data_manager.h", ] + if (!is_android) { + sources += [ + "ui/mock_save_card_bubble_controller.cc", + "ui/mock_save_card_bubble_controller.h", + ] + } + public_deps = [ ":browser", ] @@ -336,6 +354,7 @@ source_set("unit_tests") { "phone_field_unittest.cc", "phone_number_i18n_unittest.cc", "phone_number_unittest.cc", + "region_combobox_model_unittest.cc", "ui/card_unmask_prompt_controller_impl_unittest.cc", "validation_unittest.cc", "webdata/autocomplete_sync_bridge_unittest.cc", diff --git a/chromium/components/autofill/core/browser/address_i18n_unittest.cc b/chromium/components/autofill/core/browser/address_i18n_unittest.cc index 59ddc2137a3..7b0a9a0da21 100644 --- a/chromium/components/autofill/core/browser/address_i18n_unittest.cc +++ b/chromium/components/autofill/core/browser/address_i18n_unittest.cc @@ -35,61 +35,91 @@ using ::i18n::addressinput::RECIPIENT; using ::i18n::addressinput::SORTING_CODE; using ::i18n::addressinput::STREET_ADDRESS; -TEST(AddressI18nTest, FieldTypeMirrorConversions) { - static const struct { - bool billing; - ServerFieldType server_field; - AddressField address_field; - } kTestData[] = { - {true, ADDRESS_BILLING_COUNTRY, COUNTRY}, - {true, ADDRESS_BILLING_STATE, ADMIN_AREA}, - {true, ADDRESS_BILLING_CITY, LOCALITY}, - {true, ADDRESS_BILLING_DEPENDENT_LOCALITY, DEPENDENT_LOCALITY}, - {true, ADDRESS_BILLING_SORTING_CODE, SORTING_CODE}, - {true, ADDRESS_BILLING_ZIP, POSTAL_CODE}, - {true, ADDRESS_BILLING_STREET_ADDRESS, STREET_ADDRESS}, - {true, COMPANY_NAME, ORGANIZATION}, - {true, NAME_BILLING_FULL, RECIPIENT}, - {false, ADDRESS_HOME_COUNTRY, COUNTRY}, - {false, ADDRESS_HOME_STATE, ADMIN_AREA}, - {false, ADDRESS_HOME_CITY, LOCALITY}, - {false, ADDRESS_HOME_DEPENDENT_LOCALITY, DEPENDENT_LOCALITY}, - {false, ADDRESS_HOME_SORTING_CODE, SORTING_CODE}, - {false, ADDRESS_HOME_ZIP, POSTAL_CODE}, - {false, ADDRESS_HOME_STREET_ADDRESS, STREET_ADDRESS}, - {false, COMPANY_NAME, ORGANIZATION}, - {false, NAME_FULL, RECIPIENT}, - }; - - for (const auto& test_data : kTestData) { - AddressField address_field; - EXPECT_TRUE(FieldForType(test_data.server_field, &address_field)); - EXPECT_EQ(test_data.address_field, address_field); - - ServerFieldType server_field = - TypeForField(test_data.address_field, test_data.billing); - EXPECT_EQ(test_data.server_field, server_field); - } +struct FieldTypeMirrorConversionsTestCase { + bool billing; + ServerFieldType server_field; + AddressField address_field; +}; + +class FieldTypeMirrorConversionsTest + : public testing::TestWithParam<FieldTypeMirrorConversionsTestCase> {}; + +TEST_P(FieldTypeMirrorConversionsTest, FieldTypeMirrorConversions) { + auto test_data = GetParam(); + AddressField address_field; + EXPECT_TRUE(FieldForType(test_data.server_field, &address_field)); + EXPECT_EQ(test_data.address_field, address_field); + + ServerFieldType server_field = + TypeForField(test_data.address_field, test_data.billing); + EXPECT_EQ(test_data.server_field, server_field); } -TEST(AddressI18nTest, FieldTypeUnidirectionalConversions) { - static const struct { - ServerFieldType server_field; - AddressField expected_address_field; - } kTestData[] = { - {ADDRESS_BILLING_LINE1, STREET_ADDRESS}, - {ADDRESS_BILLING_LINE2, STREET_ADDRESS}, - {ADDRESS_HOME_LINE1, STREET_ADDRESS}, - {ADDRESS_HOME_LINE2, STREET_ADDRESS}, - }; - - for (const auto& test_data : kTestData) { - AddressField actual_address_field; - FieldForType(test_data.server_field, &actual_address_field); - EXPECT_EQ(test_data.expected_address_field, actual_address_field); - } +INSTANTIATE_TEST_CASE_P( + AddressI18nTest, + FieldTypeMirrorConversionsTest, + testing::Values( + FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_COUNTRY, + COUNTRY}, + FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_STATE, + ADMIN_AREA}, + FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_CITY, + LOCALITY}, + FieldTypeMirrorConversionsTestCase{ + true, ADDRESS_BILLING_DEPENDENT_LOCALITY, DEPENDENT_LOCALITY}, + FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_SORTING_CODE, + SORTING_CODE}, + FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_ZIP, + POSTAL_CODE}, + FieldTypeMirrorConversionsTestCase{true, ADDRESS_BILLING_STREET_ADDRESS, + STREET_ADDRESS}, + FieldTypeMirrorConversionsTestCase{true, COMPANY_NAME, ORGANIZATION}, + FieldTypeMirrorConversionsTestCase{true, NAME_BILLING_FULL, RECIPIENT}, + FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_COUNTRY, + COUNTRY}, + FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_STATE, + ADMIN_AREA}, + FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_CITY, LOCALITY}, + FieldTypeMirrorConversionsTestCase{ + false, ADDRESS_HOME_DEPENDENT_LOCALITY, DEPENDENT_LOCALITY}, + FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_SORTING_CODE, + SORTING_CODE}, + FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_ZIP, + POSTAL_CODE}, + FieldTypeMirrorConversionsTestCase{false, ADDRESS_HOME_STREET_ADDRESS, + STREET_ADDRESS}, + FieldTypeMirrorConversionsTestCase{false, COMPANY_NAME, ORGANIZATION}, + FieldTypeMirrorConversionsTestCase{false, NAME_FULL, RECIPIENT})); + +struct FieldTypeUnidirectionalConversionsTestCase { + ServerFieldType server_field; + AddressField expected_address_field; +}; + +class FieldTypeUnidirectionalConversionsTest + : public testing::TestWithParam< + FieldTypeUnidirectionalConversionsTestCase> {}; + +TEST_P(FieldTypeUnidirectionalConversionsTest, + FieldTypeUnidirectionalConversions) { + auto test_data = GetParam(); + AddressField actual_address_field; + FieldForType(test_data.server_field, &actual_address_field); + EXPECT_EQ(test_data.expected_address_field, actual_address_field); } +INSTANTIATE_TEST_CASE_P(AddressI18nTest, + FieldTypeUnidirectionalConversionsTest, + testing::Values( + FieldTypeUnidirectionalConversionsTestCase{ + ADDRESS_BILLING_LINE1, STREET_ADDRESS}, + FieldTypeUnidirectionalConversionsTestCase{ + ADDRESS_BILLING_LINE2, STREET_ADDRESS}, + FieldTypeUnidirectionalConversionsTestCase{ + ADDRESS_HOME_LINE1, STREET_ADDRESS}, + FieldTypeUnidirectionalConversionsTestCase{ + ADDRESS_HOME_LINE2, STREET_ADDRESS})); + TEST(AddressI18nTest, UnconvertableServerFields) { EXPECT_FALSE(FieldForType(PHONE_HOME_NUMBER, NULL)); EXPECT_FALSE(FieldForType(EMAIL_ADDRESS, NULL)); diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc index fa4aa7ce877..ec5738c43a5 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc @@ -78,7 +78,7 @@ void AutocompleteHistoryManager::OnWillSubmitForm(const FormData& form) { if (!autofill_client_->IsAutocompleteEnabled()) return; - if (driver_->IsOffTheRecord()) + if (driver_->IsIncognito()) return; // We put the following restriction on stored FormFields: diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc index df3820d9128..56c8d274114 100644 --- a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc @@ -10,6 +10,7 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "components/autofill/core/browser/autocomplete_history_manager.h" #include "components/autofill/core/browser/autofill_external_delegate.h" @@ -78,7 +79,7 @@ class AutocompleteHistoryManagerTest : public testing::Test { void TearDown() override { autocomplete_manager_.reset(); } - base::MessageLoop message_loop_; + base::test::ScopedTaskEnvironment scoped_task_environment_; scoped_refptr<MockWebDataService> web_data_service_; std::unique_ptr<AutocompleteHistoryManager> autocomplete_manager_; std::unique_ptr<AutofillDriver> autofill_driver_; diff --git a/chromium/components/autofill/core/browser/autofill_address_util.cc b/chromium/components/autofill/core/browser/autofill_address_util.cc new file mode 100644 index 00000000000..c232e002651 --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_address_util.cc @@ -0,0 +1,165 @@ +// Copyright 2017 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 "components/autofill/core/browser/autofill_address_util.h" + +#include <memory> +#include <utility> + +#include "base/memory/ptr_util.h" +#include "base/values.h" +#include "components/autofill/core/browser/autofill_country.h" +#include "components/autofill/core/browser/country_combobox_model.h" +#include "components/autofill/core/browser/field_types.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" + +using autofill::AutofillCountry; +using autofill::ServerFieldType; +using i18n::addressinput::AddressUiComponent; + +namespace autofill { + +// Dictionary keys for address components info. +const char kFieldTypeKey[] = "field"; +const char kFieldLengthKey[] = "length"; +const char kFieldNameKey[] = "name"; + +// Field names for the address components. +const char kFullNameField[] = "fullName"; +const char kCompanyNameField[] = "companyName"; +const char kAddressLineField[] = "addrLines"; +const char kDependentLocalityField[] = "dependentLocality"; +const char kCityField[] = "city"; +const char kStateField[] = "state"; +const char kPostalCodeField[] = "postalCode"; +const char kSortingCodeField[] = "sortingCode"; +const char kCountryField[] = "country"; + +// Address field length values. +const char kShortField[] = "short"; +const char kLongField[] = "long"; + +// 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|. If |components_language_code| is not NULL, then sets it +// to the BCP 47 language code that should be used to format the address for +// display. +void GetAddressComponents(const std::string& country_code, + const std::string& ui_language_code, + base::ListValue* address_components, + std::string* components_language_code) { + DCHECK(address_components); + + i18n::addressinput::Localization localization; + localization.SetGetter(l10n_util::GetStringUTF8); + std::string not_used; + std::vector<AddressUiComponent> components = + i18n::addressinput::BuildComponents( + country_code, localization, ui_language_code, + components_language_code ? components_language_code : ¬_used); + if (components.empty()) { + static const char kDefaultCountryCode[] = "US"; + components = i18n::addressinput::BuildComponents( + kDefaultCountryCode, localization, ui_language_code, + components_language_code ? components_language_code : ¬_used); + } + DCHECK(!components.empty()); + + base::ListValue* line = nullptr; + for (size_t i = 0; i < components.size(); ++i) { + if (i == 0 || + components[i - 1].length_hint == AddressUiComponent::HINT_LONG || + components[i].length_hint == AddressUiComponent::HINT_LONG) { + line = new base::ListValue; + address_components->Append(base::WrapUnique(line)); + // |line| is invalidated at this point, so it needs to be reset. + address_components->GetList(address_components->GetSize() - 1, &line); + } + + std::unique_ptr<base::DictionaryValue> component(new base::DictionaryValue); + component->SetString(kFieldNameKey, components[i].name); + + switch (components[i].field) { + case i18n::addressinput::COUNTRY: + component->SetString(kFieldTypeKey, kCountryField); + break; + case i18n::addressinput::ADMIN_AREA: + component->SetString(kFieldTypeKey, kStateField); + break; + case i18n::addressinput::LOCALITY: + component->SetString(kFieldTypeKey, kCityField); + break; + case i18n::addressinput::DEPENDENT_LOCALITY: + component->SetString(kFieldTypeKey, kDependentLocalityField); + break; + case i18n::addressinput::SORTING_CODE: + component->SetString(kFieldTypeKey, kSortingCodeField); + break; + case i18n::addressinput::POSTAL_CODE: + component->SetString(kFieldTypeKey, kPostalCodeField); + break; + case i18n::addressinput::STREET_ADDRESS: + component->SetString(kFieldTypeKey, kAddressLineField); + break; + case i18n::addressinput::ORGANIZATION: + component->SetString(kFieldTypeKey, kCompanyNameField); + break; + case i18n::addressinput::RECIPIENT: + component->SetString(kFieldTypeKey, kFullNameField); + break; + } + + switch (components[i].length_hint) { + case AddressUiComponent::HINT_LONG: + component->SetString(kFieldLengthKey, kLongField); + break; + case AddressUiComponent::HINT_SHORT: + component->SetString(kFieldLengthKey, kShortField); + break; + } + + line->Append(std::move(component)); + } +} + +// Sets data related to the country <select>. +void SetCountryData(const PersonalDataManager& manager, + base::DictionaryValue* localized_strings, + const std::string& ui_language_code) { + autofill::CountryComboboxModel model; + model.SetCountries(manager, base::Callback<bool(const std::string&)>(), + ui_language_code); + const std::vector<std::unique_ptr<autofill::AutofillCountry>>& countries = + model.countries(); + localized_strings->SetString("defaultCountryCode", + countries.front()->country_code()); + + // An ordered list of options to show in the <select>. + std::unique_ptr<base::ListValue> country_list(new base::ListValue()); + for (size_t i = 0; i < countries.size(); ++i) { + std::unique_ptr<base::DictionaryValue> option_details( + new base::DictionaryValue()); + option_details->SetString("name", model.GetItemAt(i)); + option_details->SetString( + "value", countries[i] ? countries[i]->country_code() : "separator"); + country_list->Append(std::move(option_details)); + } + localized_strings->Set("autofillCountrySelectList", country_list.release()); + + std::unique_ptr<base::ListValue> default_country_components( + new base::ListValue); + std::string default_country_language_code; + GetAddressComponents(countries.front()->country_code(), ui_language_code, + default_country_components.get(), + &default_country_language_code); + localized_strings->Set("autofillDefaultCountryComponents", + default_country_components.release()); + localized_strings->SetString("autofillDefaultCountryLanguageCode", + default_country_language_code); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_address_util.h b/chromium/components/autofill/core/browser/autofill_address_util.h new file mode 100644 index 00000000000..e90ff5bfabe --- /dev/null +++ b/chromium/components/autofill/core/browser/autofill_address_util.h @@ -0,0 +1,78 @@ +// Copyright 2017 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 COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_UTIL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_UTIL_H_ + +#include <string> + +namespace base { +class ListValue; +class DictionaryValue; +} + +namespace autofill { + +class PersonalDataManager; + +// Dictionary key for the field type. +extern const char kFieldTypeKey[]; + +// Dictionary key for the field length. +extern const char kFieldLengthKey[]; + +// Dictionary key for the field name. +extern const char kFieldNameKey[]; + +// Field name for autofill::NAME_FULL. +extern const char kFullNameField[]; + +// Field name for autofill::COMPANY_NAME. +extern const char kCompanyNameField[]; + +// Field name for autofill::ADDRESS_HOME_STREET_ADDRESS. +extern const char kAddressLineField[]; + +// Field name for autofill::ADDRESS_HOME_DEPENDENT_LOCALITY. +extern const char kDependentLocalityField[]; + +// Field name for autofill::ADDRESS_HOME_CITY. +extern const char kCityField[]; + +// Field name for autofill::ADDRESS_HOME_STATE. +extern const char kStateField[]; + +// Field name for autofill::ADDRESS_HOME_ZIP. +extern const char kPostalCodeField[]; + +// Field name for autofill::ADDRESS_HOME_SORTING_CODE. +extern const char kSortingCodeField[]; + +// Field name for autofill::ADDRESS_HOME_COUNTRY. +extern const char kCountryField[]; + +// AddressUiComponent::HINT_SHORT. +extern const char kShortField[]; + +// AddressUiComponent::HINT_LONG. +extern const char kLongField[]; + +// 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|. If |components_language_code| is not NULL, then sets it +// to the BCP 47 language code that should be used to format the address for +// display. +void GetAddressComponents(const std::string& country_code, + const std::string& ui_language_code, + base::ListValue* address_components, + std::string* components_language_code); + +// Sets data related to the country combobox. +void SetCountryData(const PersonalDataManager& manager, + base::DictionaryValue* localized_strings, + const std::string& ui_language_code); + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_ADDRESS_UTIL_H_ diff --git a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc index 3364c6f92fd..d5b27e105b2 100644 --- a/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_assistant_unittest.cc @@ -103,7 +103,7 @@ class AutofillAssistantTest : public testing::Test { std::unique_ptr<FormStructure> CreateValidCreditCardForm() { std::unique_ptr<FormStructure> form_structure; form_structure.reset(new FormStructure(CreateValidCreditCardFormData())); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); return form_structure; } @@ -152,7 +152,7 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_Secure) { // Can be shown if the context is secure. FormData form = CreateValidCreditCardFormData(); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::move(form_structure)); @@ -169,7 +169,7 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_NotSecure) { form.origin = GURL("http://myform.com"); form.action = GURL("http://myform.com/submit"); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::move(form_structure)); @@ -184,7 +184,7 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_Javascript) { FormData form = CreateValidCreditCardFormData(); form.action = GURL("javascript:alert('hello');"); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::move(form_structure)); @@ -199,7 +199,7 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_WeirdJs) { FormData form = CreateValidCreditCardFormData(); form.action = GURL("javascript:myFunc"); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::move(form_structure)); @@ -213,7 +213,7 @@ TEST_F(AutofillAssistantTest, CanShowCreditCardAssist_FeatureOn_EmptyAction) { FormData form = CreateValidCreditCardFormData(); form.action = GURL(); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); std::vector<std::unique_ptr<FormStructure>> form_structures; form_structures.push_back(std::move(form_structure)); diff --git a/chromium/components/autofill/core/browser/autofill_client.h b/chromium/components/autofill/core/browser/autofill_client.h index d36f0026ffc..f6ff075d059 100644 --- a/chromium/components/autofill/core/browser/autofill_client.h +++ b/chromium/components/autofill/core/browser/autofill_client.h @@ -14,6 +14,7 @@ #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" #include "base/values.h" +#include "components/autofill/core/browser/risk_data_loader.h" #include "ui/base/window_open_disposition.h" #include "url/gurl.h" @@ -48,6 +49,7 @@ class CardUnmaskDelegate; class CreditCard; class FormStructure; class PersonalDataManager; +class SaveCardBubbleController; struct Suggestion; // A client interface that needs to be supplied to the Autofill component by the @@ -57,7 +59,7 @@ struct Suggestion; // AutofillManager is used (e.g. a single tab), so when we say "for the client" // below, we mean "in the execution context the client is associated with" (e.g. // for the tab the AutofillManager is attached to). -class AutofillClient { +class AutofillClient : public RiskDataLoader { public: enum PaymentsRpcResult { // Empty result. Used for initializing variables and should generally @@ -89,7 +91,7 @@ class AutofillClient { typedef base::Callback<void(const CreditCard&)> CreditCardScanCallback; - virtual ~AutofillClient() {} + ~AutofillClient() override {} // Gets the PersonalDataManager instance associated with the client. virtual PersonalDataManager* GetPersonalDataManager() = 0; @@ -109,9 +111,13 @@ class AutofillClient { // Gets the RapporServiceImpl associated with the client (for metrics). virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0; - // Gets the UKM service assiciated with this client (for metrics). + // Gets the UKM service associated with this client (for metrics). virtual ukm::UkmService* GetUkmService() = 0; + // Gets the SaveCardBubbleController instance associated with the client. + // May return nullptr if the save card bubble has not been shown yet. + virtual SaveCardBubbleController* GetSaveCardBubbleController() = 0; + // Causes the Autofill settings UI to be shown. virtual void ShowAutofillSettings() = 0; @@ -128,10 +134,12 @@ class AutofillClient { const base::Closure& callback) = 0; // Runs |callback| if the |card| should be uploaded to Payments. Displays the - // contents of |legal_message| to the user. + // contents of |legal_message| to the user. Display a CVC field in the bubble + // if |should_cvc_be_requested| is true. virtual void ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) = 0; // Will show an infobar to get user consent for Credit Card assistive filling. @@ -139,10 +147,6 @@ class AutofillClient { virtual void ConfirmCreditCardFillAssist(const CreditCard& card, const base::Closure& callback) = 0; - // Gathers risk data and provides it to |callback|. - virtual void LoadRiskData( - const base::Callback<void(const std::string&)>& callback) = 0; - // Returns true if both the platform and the device support scanning credit // cards. Should be called before ScanCreditCard(). virtual bool HasCreditCardScanFeature() = 0; @@ -183,9 +187,6 @@ class AutofillClient { const base::string16& autofilled_value, const base::string16& profile_full_name) = 0; - // Informs the client that a user gesture has been observed. - virtual void OnFirstUserGestureObserved() = 0; - // If the context is secure. virtual bool IsContextSecure() = 0; diff --git a/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc b/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc index 990217406a1..8432b105cf7 100644 --- a/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc @@ -71,53 +71,63 @@ TEST(AutofillDataModelTest, IsVerified) { EXPECT_FALSE(model.IsVerified()); } -TEST(AutofillDataModelTest, CompareFrecency) { - base::Time now = base::Time::Now(); - enum Expectation { GREATER, LESS }; - - struct { - const std::string guid_a; - const int use_count_a; - const base::Time use_date_a; - const std::string guid_b; - const int use_count_b; - const base::Time use_date_b; - Expectation expectation; - } test_cases[] = { - // Same frecency, model_a has a smaller GUID (tie breaker). - {"guid_a", 8, now, "guid_b", 8, now, LESS}, - // Same recency, model_a has a bigger frequency. - {"guid_a", 10, now, "guid_b", 8, now, GREATER}, - // Same recency, model_a has a smaller frequency. - {"guid_a", 8, now, "guid_b", 10, now, LESS}, - // Same frequency, model_a is more recent. - {"guid_a", 8, now, "guid_b", 8, now - base::TimeDelta::FromDays(1), - GREATER}, - // Same frequency, model_a is less recent. - {"guid_a", 8, now - base::TimeDelta::FromDays(1), "guid_b", 8, now, LESS}, - // Special case: occasional profiles. A profile with relatively low usage - // and used recently (model_b) should not rank higher than a more used - // profile that has been unused for a short amount of time (model_a). - {"guid_a", 300, now - base::TimeDelta::FromDays(5), "guid_b", 10, - now - base::TimeDelta::FromDays(1), GREATER}, - // Special case: moving. A new profile used frequently (model_b) should - // rank higher than a profile with more usage that has not been used for a - // while (model_a). - {"guid_a", 300, now - base::TimeDelta::FromDays(15), "guid_b", 10, - now - base::TimeDelta::FromDays(1), LESS}, - }; - - for (auto test_case : test_cases) { - TestAutofillDataModel model_a(test_case.guid_a, test_case.use_count_a, - test_case.use_date_a); - TestAutofillDataModel model_b(test_case.guid_b, test_case.use_count_b, - test_case.use_date_b); - - EXPECT_EQ(test_case.expectation == GREATER, - model_a.CompareFrecency(&model_b, now)); - EXPECT_NE(test_case.expectation == GREATER, - model_b.CompareFrecency(&model_a, now)); - } +enum Expectation { GREATER, LESS }; +struct CompareFrecencyTestCase { + const std::string guid_a; + const int use_count_a; + const base::Time use_date_a; + const std::string guid_b; + const int use_count_b; + const base::Time use_date_b; + Expectation expectation; +}; + +base::Time now = base::Time::Now(); + +class CompareFrecencyTest + : public testing::TestWithParam<CompareFrecencyTestCase> {}; + +TEST_P(CompareFrecencyTest, CompareFrecency) { + auto test_case = GetParam(); + TestAutofillDataModel model_a(test_case.guid_a, test_case.use_count_a, + test_case.use_date_a); + TestAutofillDataModel model_b(test_case.guid_b, test_case.use_count_b, + test_case.use_date_b); + + EXPECT_EQ(test_case.expectation == GREATER, + model_a.CompareFrecency(&model_b, now)); + EXPECT_NE(test_case.expectation == GREATER, + model_b.CompareFrecency(&model_a, now)); } +INSTANTIATE_TEST_CASE_P( + AutofillDataModelTest, + CompareFrecencyTest, + testing::Values( + // Same frecency, model_a has a smaller GUID (tie breaker). + CompareFrecencyTestCase{"guid_a", 8, now, "guid_b", 8, now, LESS}, + // Same recency, model_a has a bigger frequency. + CompareFrecencyTestCase{"guid_a", 10, now, "guid_b", 8, now, GREATER}, + // Same recency, model_a has a smaller frequency. + CompareFrecencyTestCase{"guid_a", 8, now, "guid_b", 10, now, LESS}, + // Same frequency, model_a is more recent. + CompareFrecencyTestCase{"guid_a", 8, now, "guid_b", 8, + now - base::TimeDelta::FromDays(1), GREATER}, + // Same frequency, model_a is less recent. + CompareFrecencyTestCase{"guid_a", 8, now - base::TimeDelta::FromDays(1), + "guid_b", 8, now, LESS}, + // Special case: occasional profiles. A profile with relatively low + // usage and used recently (model_b) should not rank higher than a more + // used profile that has been unused for a short amount of time + // (model_a). + CompareFrecencyTestCase{ + "guid_a", 300, now - base::TimeDelta::FromDays(5), "guid_b", 10, + now - base::TimeDelta::FromDays(1), GREATER}, + // Special case: moving. A new profile used frequently (model_b) should + // rank higher than a profile with more usage that has not been used for + // a while (model_a). + CompareFrecencyTestCase{"guid_a", 300, + now - base::TimeDelta::FromDays(15), "guid_b", + 10, now - base::TimeDelta::FromDays(1), LESS})); + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc b/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc index 584690d7871..9aadd58bf4f 100644 --- a/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_data_util_unittest.cc @@ -11,151 +11,166 @@ namespace autofill { namespace data_util { -TEST(AutofillDataUtilTest, IsCJKName) { - typedef struct { - const char* full_name; - bool is_cjk; - } TestCase; - - TestCase test_cases[] = { - // Non-CJK language with only ASCII characters. - {"Homer Jay Simpson", false}, - // Non-CJK language with some ASCII characters. - {"Éloïse Paré", false}, - // Non-CJK language with no ASCII characters. - {"Σωκράτης", false}, - - // (Simplified) Chinese name, Unihan. - {"刘翔", true}, - // (Simplified) Chinese name, Unihan, with an ASCII space. - {"成 龙", true}, - // Korean name, Hangul. - {"송지효", true}, - // Korean name, Hangul, with an 'IDEOGRAPHIC SPACE' (U+3000). - {"김 종국", true}, - // Japanese name, Unihan. - {"山田貴洋", true}, - // Japanese name, Katakana, with a 'KATAKANA MIDDLE DOT' (U+30FB). - {"ビル・ゲイツ", true}, - // Japanese name, Katakana, with a 'MIDDLE DOT' (U+00B7) (likely a typo). - {"ビル·ゲイツ", true}, - - // CJK names don't have a middle name, so a 3-part name is bogus to us. - {"반 기 문", false} - }; - - for (const TestCase& test_case : test_cases) { - EXPECT_EQ(test_case.is_cjk, - IsCJKName(base::UTF8ToUTF16(test_case.full_name))) - << "Failed for: " << test_case.full_name; - } +struct IsCJKNameTestCase { + const char* full_name; + bool is_cjk; +}; + +class IsCJKNameTest : public testing::TestWithParam<IsCJKNameTestCase> {}; + +TEST_P(IsCJKNameTest, IsCJKName) { + auto test_case = GetParam(); + EXPECT_EQ(test_case.is_cjk, IsCJKName(base::UTF8ToUTF16(test_case.full_name))) + << "Failed for: " << test_case.full_name; } -TEST(AutofillDataUtilTest, SplitName) { - typedef struct { - std::string full_name; - std::string given_name; - std::string middle_name; - std::string family_name; - - } TestCase; - - const TestCase test_cases[] = { - // Full name including given, middle and family names. - {"Homer Jay Simpson", "Homer", "Jay", "Simpson"}, - // No middle name. - {"Moe Szyslak", "Moe", "", "Szyslak"}, - // Common name prefixes removed. - {"Reverend Timothy Lovejoy", "Timothy", "", "Lovejoy"}, - // Common name suffixes removed. - {"John Frink Phd", "John", "", "Frink"}, - // Exception to the name suffix removal. - {"John Ma", "John", "", "Ma"}, - // Common family name prefixes not considered a middle name. - {"Milhouse Van Houten", "Milhouse", "", "Van Houten"}, - - // CJK names have reverse order (surname goes first, given name goes - // second). - {"孫 德明", "德明", "", "孫"}, // Chinese name, Unihan - {"孫 德明", "德明", "", "孫"}, // Chinese name, Unihan, 'IDEOGRAPHIC SPACE' - {"홍 길동", "길동", "", "홍"}, // Korean name, Hangul - {"山田 貴洋", "貴洋", "", "山田"}, // Japanese name, Unihan - - // In Japanese, foreign names use 'KATAKANA MIDDLE DOT' (U+30FB) as a - // separator. There is no consensus for the ordering. For now, we use the - // same ordering as regular Japanese names ("last・first"). - {"ゲイツ・ビル", "ビル", "", "ゲイツ"}, // Foreign name in Japanese, Katakana - // 'KATAKANA MIDDLE DOT' is occasionally typoed as 'MIDDLE DOT' (U+00B7). - {"ゲイツ·ビル", "ビル", "", "ゲイツ"}, // Foreign name in Japanese, Katakana - - // CJK names don't usually have a space in the middle, but most of the - // time, the surname is only one character (in Chinese & Korean). - {"최성훈", "성훈", "", "최"}, // Korean name, Hangul - {"刘翔", "翔", "", "刘"}, // (Simplified) Chinese name, Unihan - {"劉翔", "翔", "", "劉"}, // (Traditional) Chinese name, Unihan - - // There are a few exceptions. Occasionally, the surname has two - // characters. - {"남궁도", "도", "", "남궁"}, // Korean name, Hangul - {"황보혜정", "혜정", "", "황보"}, // Korean name, Hangul - {"歐陽靖", "靖", "", "歐陽"}, // (Traditional) Chinese name, Unihan - - // In Korean, some 2-character surnames are rare/ambiguous, like "강전": - // "강" is a common surname, and "전" can be part of a given name. In - // those cases, we assume it's 1/2 for 3-character names, or 2/2 for - // 4-character names. - {"강전희", "전희", "", "강"}, // Korean name, Hangul - {"황목치승", "치승", "", "황목"}, // Korean name, Hangul - - // It occasionally happens that a full name is 2 characters, 1/1. - {"이도", "도", "", "이"}, // Korean name, Hangul - {"孫文", "文", "", "孫"} // Chinese name, Unihan - }; - - for (TestCase test_case : test_cases) { - NameParts name_parts = SplitName(base::UTF8ToUTF16(test_case.full_name)); - - EXPECT_EQ(base::UTF8ToUTF16(test_case.given_name), name_parts.given); - EXPECT_EQ(base::UTF8ToUTF16(test_case.middle_name), name_parts.middle); - EXPECT_EQ(base::UTF8ToUTF16(test_case.family_name), name_parts.family); - } +INSTANTIATE_TEST_CASE_P( + AutofillDataUtilTest, + IsCJKNameTest, + testing::Values( + // Non-CJK language with only ASCII characters. + IsCJKNameTestCase{"Homer Jay Simpson", false}, + // Non-CJK language with some ASCII characters. + IsCJKNameTestCase{"Éloïse Paré", false}, + // Non-CJK language with no ASCII characters. + IsCJKNameTestCase{"Σωκράτης", false}, + + // (Simplified) Chinese name, Unihan. + IsCJKNameTestCase{"刘翔", true}, + // (Simplified) Chinese name, Unihan, with an ASCII space. + IsCJKNameTestCase{"成 龙", true}, + // Korean name, Hangul. + IsCJKNameTestCase{"송지효", true}, + // Korean name, Hangul, with an 'IDEOGRAPHIC SPACE' (U+3000). + IsCJKNameTestCase{"김 종국", true}, + // Japanese name, Unihan. + IsCJKNameTestCase{"山田貴洋", true}, + // Japanese name, Katakana, with a 'KATAKANA MIDDLE DOT' (U+30FB). + IsCJKNameTestCase{"ビル・ゲイツ", true}, + // Japanese name, Katakana, with a 'MIDDLE DOT' (U+00B7) (likely a + // typo). + IsCJKNameTestCase{"ビル·ゲイツ", true}, + + // CJK names don't have a middle name, so a 3-part name is bogus to us. + IsCJKNameTestCase{"반 기 문", false})); + +struct FullNameTestCase { + std::string full_name; + std::string given_name; + std::string middle_name; + std::string family_name; +}; + +class SplitNameTest : public testing::TestWithParam<FullNameTestCase> {}; + +TEST_P(SplitNameTest, SplitName) { + auto test_case = GetParam(); + NameParts name_parts = SplitName(base::UTF8ToUTF16(test_case.full_name)); + + EXPECT_EQ(base::UTF8ToUTF16(test_case.given_name), name_parts.given); + EXPECT_EQ(base::UTF8ToUTF16(test_case.middle_name), name_parts.middle); + EXPECT_EQ(base::UTF8ToUTF16(test_case.family_name), name_parts.family); } -TEST(AutofillDataUtilTest, JoinNameParts) { - typedef struct { - std::string given_name; - std::string middle_name; - std::string family_name; - std::string full_name; - } TestCase; - - TestCase test_cases[] = { - // Full name including given, middle and family names. - {"Homer", "Jay", "Simpson", "Homer Jay Simpson"}, - // No middle name. - {"Moe", "", "Szyslak", "Moe Szyslak"}, - - // CJK names have reversed order, no space. - {"德明", "", "孫", "孫德明"}, // Chinese name, Unihan - {"길동", "", "홍", "홍길동"}, // Korean name, Hangul - {"貴洋", "", "山田", "山田貴洋"}, // Japanese name, Unihan - - // These are no CJK names for us, they're just bogus. - {"Homer", "", "シンプソン", "Homer シンプソン"}, - {"ホーマー", "", "Simpson", "ホーマー Simpson"}, - {"반", "기", "문", "반 기 문"} // Has a middle-name, too unusual - }; - - for (const TestCase& test_case : test_cases) { - base::string16 joined = JoinNameParts( - base::UTF8ToUTF16(test_case.given_name), - base::UTF8ToUTF16(test_case.middle_name), - base::UTF8ToUTF16(test_case.family_name)); - - EXPECT_EQ(base::UTF8ToUTF16(test_case.full_name), joined); - } +INSTANTIATE_TEST_CASE_P( + AutofillDataUtil, + SplitNameTest, + testing::Values( + // Full name including given, middle and family names. + FullNameTestCase{"Homer Jay Simpson", "Homer", "Jay", "Simpson"}, + // No middle name. + FullNameTestCase{"Moe Szyslak", "Moe", "", "Szyslak"}, + // Common name prefixes removed. + FullNameTestCase{"Reverend Timothy Lovejoy", "Timothy", "", "Lovejoy"}, + // Common name suffixes removed. + FullNameTestCase{"John Frink Phd", "John", "", "Frink"}, + // Exception to the name suffix removal. + FullNameTestCase{"John Ma", "John", "", "Ma"}, + // Common family name prefixes not considered a middle name. + FullNameTestCase{"Milhouse Van Houten", "Milhouse", "", "Van Houten"}, + + // CJK names have reverse order (surname goes first, given name goes + // second). + FullNameTestCase{"孫 德明", "德明", "", "孫"}, // Chinese name, Unihan + FullNameTestCase{"孫 德明", "德明", "", + "孫"}, // Chinese name, Unihan, 'IDEOGRAPHIC SPACE' + FullNameTestCase{"홍 길동", "길동", "", "홍"}, // Korean name, Hangul + FullNameTestCase{"山田 貴洋", "貴洋", "", + "山田"}, // Japanese name, Unihan + + // In Japanese, foreign names use 'KATAKANA MIDDLE DOT' (U+30FB) as a + // separator. There is no consensus for the ordering. For now, we use + // the same ordering as regular Japanese names ("last・first"). + FullNameTestCase{"ゲイツ・ビル", "ビル", "", + "ゲイツ"}, // Foreign name in Japanese, Katakana + // 'KATAKANA MIDDLE DOT' is occasionally typoed as 'MIDDLE DOT' + // (U+00B7). + FullNameTestCase{"ゲイツ·ビル", "ビル", "", + "ゲイツ"}, // Foreign name in Japanese, Katakana + + // CJK names don't usually have a space in the middle, but most of the + // time, the surname is only one character (in Chinese & Korean). + FullNameTestCase{"최성훈", "성훈", "", "최"}, // Korean name, Hangul + FullNameTestCase{"刘翔", "翔", "", + "刘"}, // (Simplified) Chinese name, Unihan + FullNameTestCase{"劉翔", "翔", "", + "劉"}, // (Traditional) Chinese name, Unihan + + // There are a few exceptions. Occasionally, the surname has two + // characters. + FullNameTestCase{"남궁도", "도", "", "남궁"}, // Korean name, Hangul + FullNameTestCase{"황보혜정", "혜정", "", + "황보"}, // Korean name, Hangul + FullNameTestCase{"歐陽靖", "靖", "", + "歐陽"}, // (Traditional) Chinese name, Unihan + + // In Korean, some 2-character surnames are rare/ambiguous, like "강전": + // "강" is a common surname, and "전" can be part of a given name. In + // those cases, we assume it's 1/2 for 3-character names, or 2/2 for + // 4-character names. + FullNameTestCase{"강전희", "전희", "", "강"}, // Korean name, Hangul + FullNameTestCase{"황목치승", "치승", "", + "황목"}, // Korean name, Hangul + + // It occasionally happens that a full name is 2 characters, 1/1. + FullNameTestCase{"이도", "도", "", "이"}, // Korean name, Hangul + FullNameTestCase{"孫文", "文", "", "孫"} // Chinese name, Unihan + )); + +class JoinNamePartsTest : public testing::TestWithParam<FullNameTestCase> {}; + +TEST_P(JoinNamePartsTest, JoinNameParts) { + auto test_case = GetParam(); + base::string16 joined = + JoinNameParts(base::UTF8ToUTF16(test_case.given_name), + base::UTF8ToUTF16(test_case.middle_name), + base::UTF8ToUTF16(test_case.family_name)); + + EXPECT_EQ(base::UTF8ToUTF16(test_case.full_name), joined); } +INSTANTIATE_TEST_CASE_P( + AutofillDataUtil, + JoinNamePartsTest, + testing::Values( + // Full name including given, middle and family names. + FullNameTestCase{"Homer Jay Simpson", "Homer", "Jay", "Simpson"}, + // No middle name. + FullNameTestCase{"Moe Szyslak", "Moe", "", "Szyslak"}, + + // CJK names have reversed order, no space. + FullNameTestCase{"孫德明", "德明", "", "孫"}, // Chinese name, Unihan + FullNameTestCase{"홍길동", "길동", "", "홍"}, // Korean name, Hangul + FullNameTestCase{"山田貴洋", "貴洋", "", + "山田"}, // Japanese name, Unihan + + // These are no CJK names for us, they're just bogus. + FullNameTestCase{"Homer シンプソン", "Homer", "", "シンプソン"}, + FullNameTestCase{"ホーマー Simpson", "ホーマー", "", "Simpson"}, + FullNameTestCase{"반 기 문", "반", "기", "문"} + // Has a middle-name, too unusual + )); + TEST(AutofillDataUtilTest, ProfileMatchesFullName) { autofill::AutofillProfile profile; autofill::test::SetProfileInfo( diff --git a/chromium/components/autofill/core/browser/autofill_download_manager.cc b/chromium/components/autofill/core/browser/autofill_download_manager.cc index 07084c52be4..8d7d72588f4 100644 --- a/chromium/components/autofill/core/browser/autofill_download_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_download_manager.cc @@ -24,9 +24,82 @@ #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" +#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_fetcher.h" #include "url/gurl.h" +namespace { + +net::NetworkTrafficAnnotationTag GetNetworkTrafficAnnotation( + const autofill::AutofillDownloadManager::RequestType& request_type) { + if (request_type == autofill::AutofillDownloadManager::REQUEST_QUERY) { + return net::DefineNetworkTrafficAnnotation("autofill_query", R"( + semantics { + sender: "Autofill" + description: + "Chromium can automatically fill in web forms. If the feature is " + "enabled, Chromium will send a non-identifying description of the " + "form to Google's servers, which will respond with the type of " + "data required by each of the form's fields, if known. I.e., if a " + "field expects to receive a name, phone number, street address, " + "etc." + trigger: "User encounters a web form." + data: + "Hashed descriptions of the form and its fields. User data is not " + "sent." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: false + setting: + "You can enable or disable this feature via 'Enable autofill to " + "fill out web forms in a single click.' in Chromium's settings " + "under 'Passwords and forms'. The feature is enabled by default." + chrome_policy { + AutoFillEnabled { + policy_options {mode: MANDATORY} + AutoFillEnabled: false + } + } + })"); + } + + DCHECK_EQ(request_type, autofill::AutofillDownloadManager::REQUEST_UPLOAD); + return net::DefineNetworkTrafficAnnotation("autofill_upload", R"( + semantics { + sender: "Autofill" + description: + "Chromium relies on crowd-sourced field type classifications to " + "help it automatically fill in web forms. If the feature is " + "enabled, Chromium will send a non-identifying description of the " + "form to Google's servers along with the type of data Chromium " + "observed being given to the form. I.e., if you entered your first " + "name into a form field, Chromium will 'vote' for that form field " + "being a first name field." + trigger: "User submits a web form." + data: + "Hashed descriptions of the form and its fields along with type of " + "data given to each field, if recognized from the user's " + "profile(s). User data is not sent." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: false + setting: + "You can enable or disable this feature via 'Enable autofill to " + "fill out web forms in a single click.' in Chromium's settings " + "under 'Passwords and forms'. The feature is enabled by default." + chrome_policy { + AutoFillEnabled { + policy_options {mode: MANDATORY} + AutoFillEnabled: false + } + } + })"); +} + +} // namespace + namespace autofill { namespace { @@ -234,7 +307,8 @@ bool AutofillDownloadManager::StartRequest( // Id is ignored for regular chrome, in unit test id's for fake fetcher // factory will be 0, 1, 2, ... std::unique_ptr<net::URLFetcher> owned_fetcher = net::URLFetcher::Create( - fetcher_id_for_unittest_++, request_url, net::URLFetcher::POST, this); + fetcher_id_for_unittest_++, request_url, net::URLFetcher::POST, this, + GetNetworkTrafficAnnotation(request_data.request_type)); net::URLFetcher* fetcher = owned_fetcher.get(); data_use_measurement::DataUseUserData::AttachToFetcher( fetcher, data_use_measurement::DataUseUserData::AUTOFILL); @@ -251,7 +325,7 @@ bool AutofillDownloadManager::StartRequest( // not affect transmission of experiments coming from the variations server. bool is_signed_in = false; variations::AppendVariationHeaders(fetcher->GetOriginalURL(), - driver_->IsOffTheRecord(), false, + driver_->IsIncognito(), false, is_signed_in, &headers); fetcher->SetExtraRequestHeaders(headers.ToString()); fetcher->Start(); diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h index e8b95d87a02..cc1a5fd38c1 100644 --- a/chromium/components/autofill/core/browser/autofill_driver.h +++ b/chromium/components/autofill/core/browser/autofill_driver.h @@ -40,9 +40,8 @@ class AutofillDriver { virtual ~AutofillDriver() {} - // Returns whether the user is currently operating in an off-the-record - // (i.e., incognito) context. - virtual bool IsOffTheRecord() const = 0; + // Returns whether the user is currently operating in an incognito context. + virtual bool IsIncognito() const = 0; // Returns the URL request context information associated with this driver. virtual net::URLRequestContextGetter* GetURLRequestContext() = 0; @@ -103,10 +102,6 @@ class AutofillDriver { // Called when the user interacted with a credit card form, so that // the current page's security state can be updated appropriately. virtual void DidInteractWithCreditCardForm() = 0; - - // Tells the associated frame that a user gesture was observed somewhere in - // the tab (including in a different frame). - virtual void NotifyFirstUserGestureObservedInTab() {} }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory.cc b/chromium/components/autofill/core/browser/autofill_driver_factory.cc index 224c1efde47..58ec815c2ba 100644 --- a/chromium/components/autofill/core/browser/autofill_driver_factory.cc +++ b/chromium/components/autofill/core/browser/autofill_driver_factory.cc @@ -21,7 +21,6 @@ AutofillDriver* AutofillDriverFactory::DriverForKey(void* key) { } void AutofillDriverFactory::NavigationFinished() { - user_gesture_seen_ = false; client_->HideAutofillPopup(); } @@ -29,16 +28,6 @@ void AutofillDriverFactory::TabHidden() { client_->HideAutofillPopup(); } -void AutofillDriverFactory::OnFirstUserGestureObserved() { - if (user_gesture_seen_) - return; - - for (auto& driver : driver_map_) - driver.second->NotifyFirstUserGestureObservedInTab(); - - user_gesture_seen_ = true; -} - void AutofillDriverFactory::AddForKey( void* key, base::Callback<std::unique_ptr<AutofillDriver>()> factory_method) { @@ -46,8 +35,6 @@ void AutofillDriverFactory::AddForKey( // This can be called twice for the key representing the main frame. if (insertion_result.second) { insertion_result.first->second = factory_method.Run(); - if (user_gesture_seen_) - insertion_result.first->second->NotifyFirstUserGestureObservedInTab(); } } diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory.h b/chromium/components/autofill/core/browser/autofill_driver_factory.h index ff48bb67ec3..f5e21c68257 100644 --- a/chromium/components/autofill/core/browser/autofill_driver_factory.h +++ b/chromium/components/autofill/core/browser/autofill_driver_factory.h @@ -34,12 +34,6 @@ class AutofillDriverFactory { // Handles hiding of the corresponding tab. void TabHidden(); - // Call this to notify the factory that one of the frames saw a user gesture. - // The factory will distribute this information to all drivers when it comes - // for the first time since the last main frame navigation to a different - // page. It will also notify drivers added later (see AddForKey). - void OnFirstUserGestureObserved(); - AutofillClient* client() { return client_; }; protected: @@ -61,8 +55,6 @@ class AutofillDriverFactory { std::unordered_map<void*, std::unique_ptr<AutofillDriver>> driver_map_; - bool user_gesture_seen_ = false; // The state for OnFirstUserGestureObserved. - DISALLOW_COPY_AND_ASSIGN(AutofillDriverFactory); }; diff --git a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc index 8355161333c..fdc88828cb2 100644 --- a/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_driver_factory_unittest.cc @@ -36,19 +36,8 @@ class CountingAutofillDriver : public TestAutofillDriver { ~CountingAutofillDriver() override { --*instance_counter_; } - // Note that EXPECT_CALL cannot be used here, because creation and - // notification of the same driver might be done by a single AddForKey call - // from the test. Therefore tracking the "gesture_observed" flag is done - // explicitly here. - void NotifyFirstUserGestureObservedInTab() override { - gesture_observed_ = true; - } - - bool gesture_observed() { return gesture_observed_; } - private: int* const instance_counter_; - bool gesture_observed_ = false; DISALLOW_COPY_AND_ASSIGN(CountingAutofillDriver); }; @@ -187,54 +176,4 @@ TEST_F(AutofillDriverFactoryTest, TabHidden) { factory_.TabHidden(); } -// Without calling OnFirstUserGestureObserved on the factory, the factory will -// not call NotifyFirstUserGestureObservedInTab on a driver. -TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_NotCalled) { - factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); - EXPECT_FALSE(GetDriver(KeyFrom(1))->gesture_observed()); -} - -// Call OnFirstUserGestureObserved on the factory with one driver. The factory -// will call NotifyFirstUserGestureObservedInTab on that driver. -TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_CalledOld) { - factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); - factory_.OnFirstUserGestureObserved(); - EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); -} - -// Call OnFirstUserGestureObserved on the factory without drivers. Add a -// driver. The factory will call NotifyFirstUserGestureObservedInTab on that -// driver. -TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_CalledNew) { - factory_.OnFirstUserGestureObserved(); - factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); - EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); -} - -// Combining the CalledOld and CalledNew test cases into one. -TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_MultipleDrivers) { - factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); - factory_.OnFirstUserGestureObserved(); - EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); - - factory_.AddForKey(KeyFrom(7), CreateDriverCallback()); - EXPECT_TRUE(GetDriver(KeyFrom(7))->gesture_observed()); -} - -// Call OnFirstUserGestureObserved on the factory with one driver. Simulate -// navigation to a different page. Add a driver. The factory will not call -// NotifyFirstUserGestureObservedInTab on that driver. -TEST_F(AutofillDriverFactoryTest, OnFirstUserGestureObserved_CalledNavigation) { - factory_.AddForKey(KeyFrom(1), CreateDriverCallback()); - factory_.OnFirstUserGestureObserved(); - EXPECT_TRUE(GetDriver(KeyFrom(1))->gesture_observed()); - - EXPECT_CALL(client_, HideAutofillPopup()); - factory_.NavigationFinished(); - - // Adding a sub-frame - factory_.AddForKey(KeyFrom(2), CreateDriverCallback()); - EXPECT_FALSE(GetDriver(KeyFrom(2))->gesture_observed()); -} - } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_experiments.cc b/chromium/components/autofill/core/browser/autofill_experiments.cc index b27569f4d8c..ef37213e57a 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.cc +++ b/chromium/components/autofill/core/browser/autofill_experiments.cc @@ -32,8 +32,10 @@ const base::Feature kAutofillCreditCardPopupLayout{ "AutofillCreditCardPopupLayout", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kAutofillCreditCardLastUsedDateDisplay{ "AutofillCreditCardLastUsedDateDisplay", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kAutofillUkmLogging{"kAutofillUkmLogging", +const base::Feature kAutofillUkmLogging{"AutofillUkmLogging", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kAutofillUpstreamRequestCvcIfMissing{ + "AutofillUpstreamRequestCvcIfMissing", base::FEATURE_DISABLED_BY_DEFAULT}; const char kCreditCardSigninPromoImpressionLimitParamKey[] = "impression_limit"; const char kAutofillCreditCardPopupBackgroundColorKey[] = "background_color"; const char kAutofillCreditCardPopupDividerColorKey[] = "dropdown_divider_color"; @@ -229,4 +231,12 @@ bool IsUkmLoggingEnabled() { return base::FeatureList::IsEnabled(kAutofillUkmLogging); } +bool IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled() { +#if defined(OS_ANDROID) + return false; +#else + return base::FeatureList::IsEnabled(kAutofillUpstreamRequestCvcIfMissing); +#endif +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_experiments.h b/chromium/components/autofill/core/browser/autofill_experiments.h index 70c3f411c60..ba03ac21eb7 100644 --- a/chromium/components/autofill/core/browser/autofill_experiments.h +++ b/chromium/components/autofill/core/browser/autofill_experiments.h @@ -29,6 +29,7 @@ extern const base::Feature kAutofillScanCardholderName; extern const base::Feature kAutofillCreditCardPopupLayout; extern const base::Feature kAutofillCreditCardLastUsedDateDisplay; extern const base::Feature kAutofillUkmLogging; +extern const base::Feature kAutofillUpstreamRequestCvcIfMissing; extern const char kCreditCardSigninPromoImpressionLimitParamKey[]; extern const char kAutofillCreditCardPopupSettingsSuggestionValueKey[]; extern const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[]; @@ -104,6 +105,10 @@ unsigned int GetPopupMargin(); // Returns whether the feature to log UKMs is enabled. bool IsUkmLoggingEnabled(); +// Returns whether the experiment is enabled where Chrome Upstream requests CVC +// in the offer to save bubble if it was not detected during the checkout flow. +bool IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled(); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_ diff --git a/chromium/components/autofill/core/browser/autofill_field_unittest.cc b/chromium/components/autofill/core/browser/autofill_field_unittest.cc index ef4c0473f9a..5731f376ace 100644 --- a/chromium/components/autofill/core/browser/autofill_field_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_field_unittest.cc @@ -283,181 +283,240 @@ TEST_F(AutofillFieldTest, FillFormField_AutocompleteOff_CreditCardField) { EXPECT_EQ(ASCIIToUTF16("4111111111111111"), field.value); } +struct AutofillFieldTestCase { + HtmlFieldType field_type; + size_t field_max_length; + std::string value_to_fill; + std::string expected_value; +}; + +class AutofillFieldPhoneNumberTest + : public testing::TestWithParam<AutofillFieldTestCase> { + public: + AutofillFieldPhoneNumberTest() { CountryNames::SetLocaleString("en-US"); } +}; + // TODO(crbug.com/581514): Add support for filling only the prefix/suffix for // phone numbers with 10 or 11 digits. -TEST_F(AutofillFieldTest, FillPhoneNumber) { - typedef struct { - HtmlFieldType field_type; - size_t field_max_length; - std::string value_to_fill; - std::string expected_value; - } TestCase; - - TestCase test_cases[] = { - // Filling a phone type field with text should fill the text as is. - {HTML_TYPE_TEL, /* default value */ 0, "Oh hai", "Oh hai"}, - // Filling a prefix type field with a phone number of 7 digits should just - // fill the prefix. - {HTML_TYPE_TEL_LOCAL_PREFIX, /* default value */ 0, "5551234", "555"}, - // Filling a suffix type field with a phone number of 7 digits should just - // fill the suffix. - {HTML_TYPE_TEL_LOCAL_SUFFIX, /* default value */ 0, "5551234", "1234"}, - // Filling a phone type field with a max length of 3 with a phone number - // of - // 7 digits should fill only the prefix. - {HTML_TYPE_TEL, 3, "5551234", "555"}, - // Filling a phone type field with a max length of 4 with a phone number - // of - // 7 digits should fill only the suffix. - {HTML_TYPE_TEL, 4, "5551234", "1234"}, - // Filling a phone type field with a max length of 10 with a phone number - // including the country code should fill the phone number without the - // country code. - {HTML_TYPE_TEL, 10, "15141254578", "5141254578"}, - // Filling a phone type field with a max length of 5 with a phone number - // should fill with the last 5 digits of that phone number. - {HTML_TYPE_TEL, 5, "5141254578", "54578"}}; - - for (TestCase test_case : test_cases) { - AutofillField field; - field.SetHtmlType(test_case.field_type, HtmlFieldMode()); - field.max_length = test_case.field_max_length; - - AutofillField::FillFormField(field, ASCIIToUTF16(test_case.value_to_fill), - "en-US", "en-US", &field); - EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); - } +TEST_P(AutofillFieldPhoneNumberTest, FillPhoneNumber) { + auto test_case = GetParam(); + AutofillField field; + field.SetHtmlType(test_case.field_type, HtmlFieldMode()); + field.max_length = test_case.field_max_length; + + AutofillField::FillFormField(field, ASCIIToUTF16(test_case.value_to_fill), + "en-US", "en-US", &field); + EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); } -TEST_F(AutofillFieldTest, FillExpirationYearInput) { - typedef struct { - HtmlFieldType field_type; - size_t field_max_length; - std::string value_to_fill; - std::string expected_value; - } TestCase; - - TestCase test_cases[] = { - // A field predicted as a 2 digits expiration year should fill the last 2 - // digits of the expiration year if the field has an unspecified max - // length (0) or if it's greater than 1. - {HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, /* default value */ 0, "2023", - "23"}, - {HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 2, "2023", "23"}, - {HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 12, "2023", "23"}, - // A field predicted as a 2 digit expiration year should fill the last - // digit of the expiration year if the field has a max length of 1. - {HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 1, "2023", "3"}, - // A field predicted as a 4 digit expiration year should fill the 4 - // digits of the expiration year if the field has an unspecified max - // length (0) or if it's greater than 3 . - {HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, /* default value */ 0, "2023", - "2023"}, - {HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 4, "2023", "2023"}, - {HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 12, "2023", "2023"}, - // A field predicted as a 4 digits expiration year should fill the last 2 - // digits of the expiration year if the field has a max length of 2. - {HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 2, "2023", "23"}, - // A field predicted as a 4 digits expiration year should fill the last - // digit of the expiration year if the field has a max length of 1. - {HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 1, "2023", "3"}, - }; +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillFieldPhoneNumberTest, + testing::Values( + // Filling a phone type field with text should fill the text as is. + AutofillFieldTestCase{HTML_TYPE_TEL, /* default value */ 0, "Oh hai", + "Oh hai"}, + // Filling a prefix type field with a phone number of 7 digits should + // just fill the prefix. + AutofillFieldTestCase{HTML_TYPE_TEL_LOCAL_PREFIX, /* default value */ 0, + "5551234", "555"}, + // Filling a suffix type field with a phone number of 7 digits should + // just fill the suffix. + AutofillFieldTestCase{HTML_TYPE_TEL_LOCAL_SUFFIX, /* default value */ 0, + "5551234", "1234"}, + // Filling a phone type field with a max length of 3 with a phone number + // of + // 7 digits should fill only the prefix. + AutofillFieldTestCase{HTML_TYPE_TEL, 3, "5551234", "555"}, + // Filling a phone type field with a max length of 4 with a phone number + // of + // 7 digits should fill only the suffix. + AutofillFieldTestCase{HTML_TYPE_TEL, 4, "5551234", "1234"}, + // Filling a phone type field with a max length of 10 with a phone + // number including the country code should fill the phone number + // without the country code. + AutofillFieldTestCase{HTML_TYPE_TEL, 10, "15141254578", "5141254578"}, + // Filling a phone type field with a max length of 5 with a phone number + // should fill with the last 5 digits of that phone number. + AutofillFieldTestCase{HTML_TYPE_TEL, 5, "5141254578", "54578"})); + +class AutofillFieldExpirationYearTest + : public testing::TestWithParam<AutofillFieldTestCase> { + public: + AutofillFieldExpirationYearTest() { CountryNames::SetLocaleString("en-US"); } +}; - for (TestCase test_case : test_cases) { - AutofillField field; - field.form_control_type = "text"; - field.SetHtmlType(test_case.field_type, HtmlFieldMode()); - field.max_length = test_case.field_max_length; +TEST_P(AutofillFieldExpirationYearTest, FillExpirationYearInput) { + auto test_case = GetParam(); + AutofillField field; + field.form_control_type = "text"; + field.SetHtmlType(test_case.field_type, HtmlFieldMode()); + field.max_length = test_case.field_max_length; - AutofillField::FillFormField(field, ASCIIToUTF16(test_case.value_to_fill), - "en-US", "en-US", &field); - EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); - } + AutofillField::FillFormField(field, ASCIIToUTF16(test_case.value_to_fill), + "en-US", "en-US", &field); + EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); } -TEST_F(AutofillFieldTest, FillExpirationDateInput) { - typedef struct { - HtmlFieldType field_type; - size_t field_max_length; - std::string value_to_fill; - std::string expected_value; - bool expected_response; - } TestCase; - - TestCase test_cases[] = { - // Test invalid inputs - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, "1/21", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, "01-21", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, "1/2021", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, "01-2021", "", false}, - // Trim whitespace - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, "01/22 ", "01/22", - true}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, "01/2022 ", "01/2022", - true}, - // A field predicted as a expiration date w/ 2 digit year should fill - // with a format of MM/YY unless it has max-length of: - // 4: Use format MMYY - // 6: Use format MMYYYY - // 7: Use format MM/YYYY - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, /* default value */ 0, - "01/23", "01/23", true}, - // Unsupported max lengths of 1-3, fail - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 1, "01/23", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 2, "02/23", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 3, "03/23", "", false}, - // A max length of 4 indicates a format of MMYY. - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 4, "02/23", "0223", true}, - // A max length of 6 indicates a format of MMYYYY, the 21st century is - // assumed. - // Desired case of proper max length >= 5 - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 5, "03/23", "03/23", true}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 6, "04/23", "042023", true}, - // A max length of 7 indicates a format of MM/YYYY, the 21st century is - // assumed. - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 7, "05/23", "05/2023", - true}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 12, "06/23", "06/23", true}, - - // A field predicted as a expiration date w/ 4 digit year should fill - // with a format of MM/YYYY unless it has max-length of: - // 4: Use format MMYY - // 5: Use format MM/YY - // 6: Use format MMYYYY - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, /* default value */ 0, - "01/2024", "01/2024", true}, - // Unsupported max lengths of 1-3, fail - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 1, "01/2024", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 2, "02/2024", "", false}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 3, "03/2024", "", false}, - // A max length of 4 indicates a format of MMYY. - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 4, "02/2024", "0224", true}, - // A max length of 5 indicates a format of MM/YY. - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 5, "03/2024", "03/24", - true}, - // A max length of 6 indicates a format of MMYYYY. - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 6, "04/2024", "042024", - true}, - // Desired case of proper max length >= 7 - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 7, "05/2024", "05/2024", - true}, - {HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 12, "06/2024", "06/2024", - true}, - }; +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillFieldExpirationYearTest, + testing::Values( + // A field predicted as a 2 digits expiration year should fill the last + // 2 digits of the expiration year if the field has an unspecified max + // length (0) or if it's greater than 1. + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, + /* default value */ 0, "2023", "23"}, + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 2, "2023", + "23"}, + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 12, + "2023", "23"}, + // A field predicted as a 2 digit expiration year should fill the last + // digit of the expiration year if the field has a max length of 1. + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 1, "2023", + "3"}, + // A field predicted as a 4 digit expiration year should fill the 4 + // digits of the expiration year if the field has an unspecified max + // length (0) or if it's greater than 3 . + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, + /* default value */ 0, "2023", "2023"}, + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 4, "2023", + "2023"}, + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 12, + "2023", "2023"}, + // A field predicted as a 4 digits expiration year should fill the last + // 2 digits of the expiration year if the field has a max length of 2. + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 2, "2023", + "23"}, + // A field predicted as a 4 digits expiration year should fill the last + // digit of the expiration year if the field has a max length of 1. + AutofillFieldTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 1, "2023", + "3"})); + +struct AutofillFieldExpirationDateTestCase { + HtmlFieldType field_type; + size_t field_max_length; + std::string value_to_fill; + std::string expected_value; + bool expected_response; +}; - for (TestCase test_case : test_cases) { - AutofillField field; - field.form_control_type = "text"; - field.SetHtmlType(test_case.field_type, HtmlFieldMode()); - field.max_length = test_case.field_max_length; +class AutofillFieldExpirationDateTest + : public testing::TestWithParam<AutofillFieldExpirationDateTestCase> { + public: + AutofillFieldExpirationDateTest() { CountryNames::SetLocaleString("en-US"); } +}; + +TEST_P(AutofillFieldExpirationDateTest, FillExpirationDateInput) { + auto test_case = GetParam(); + AutofillField field; + field.form_control_type = "text"; + field.SetHtmlType(test_case.field_type, HtmlFieldMode()); + field.max_length = test_case.field_max_length; - bool response = AutofillField::FillFormField( + bool response = AutofillField::FillFormField( field, ASCIIToUTF16(test_case.value_to_fill), "en-US", "en-US", &field); - EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); - EXPECT_EQ(response, test_case.expected_response); - } + EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); + EXPECT_EQ(response, test_case.expected_response); } +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillFieldExpirationDateTest, + testing::Values( + // Test invalid inputs + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, "1/21", "", false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, "01-21", "", false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, "1/2021", "", + false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, "01-2021", "", + false}, + // Trim whitespace + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 0, "01/22 ", "01/22", + true}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 0, "01/2022 ", + "01/2022", true}, + // A field predicted as a expiration date w/ 2 digit year should fill + // with a format of MM/YY unless it has max-length of: + // 4: Use format MMYY + // 6: Use format MMYYYY + // 7: Use format MM/YYYY + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, /* default value */ 0, + "01/23", "01/23", true}, + // Unsupported max lengths of 1-3, fail + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 1, "01/23", "", false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 2, "02/23", "", false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 3, "03/23", "", false}, + // A max length of 4 indicates a format of MMYY. + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 4, "02/23", "0223", + true}, + // A max length of 6 indicates a format of MMYYYY, the 21st century is + // assumed. + // Desired case of proper max length >= 5 + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 5, "03/23", "03/23", + true}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 6, "04/23", "042023", + true}, + // A max length of 7 indicates a format of MM/YYYY, the 21st century is + // assumed. + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 7, "05/23", "05/2023", + true}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 12, "06/23", "06/23", + true}, + + // A field predicted as a expiration date w/ 4 digit year should fill + // with a format of MM/YYYY unless it has max-length of: + // 4: Use format MMYY + // 5: Use format MM/YY + // 6: Use format MMYYYY + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, /* default value */ 0, + "01/2024", "01/2024", true}, + // Unsupported max lengths of 1-3, fail + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 1, "01/2024", "", + false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 2, "02/2024", "", + false}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 3, "03/2024", "", + false}, + // A max length of 4 indicates a format of MMYY. + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 4, "02/2024", "0224", + true}, + // A max length of 5 indicates a format of MM/YY. + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 5, "03/2024", "03/24", + true}, + // A max length of 6 indicates a format of MMYYYY. + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 6, "04/2024", "042024", + true}, + // Desired case of proper max length >= 7 + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 7, "05/2024", + "05/2024", true}, + AutofillFieldExpirationDateTestCase{ + HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 12, "06/2024", + "06/2024", true})); + TEST_F(AutofillFieldTest, FillSelectControlByValue) { std::vector<const char*> kOptions = { "Eenie", "Meenie", "Miney", "Mo", @@ -493,131 +552,179 @@ TEST_F(AutofillFieldTest, FillSelectControlByContents) { EXPECT_EQ(ASCIIToUTF16("2"), field.value); // Corresponds to "Miney". } -TEST_F(AutofillFieldTest, FillSelectWithStates) { - typedef struct { - std::vector<const char*> select_values; - const char* input_value; - const char* expected_value; - } TestCase; - - TestCase test_cases[] = { - // Filling the abbreviation. - {{"Alabama", "California"}, "CA", "California"}, - // Attempting to fill the full name in a select full of abbreviations. - {{"AL", "CA"}, "California", "CA"}, - // Different case and diacritics. - {{"QUÉBEC", "ALBERTA"}, "Quebec", "QUÉBEC"}, - // Inexact state names. - {{"SC - South Carolina", "CA - California", "NC - North Carolina"}, - "California", - "CA - California"}, - // Don't accidentally match "Virginia" to "West Virginia". - {{"WV - West Virginia", "VA - Virginia", "NV - North Virginia"}, - "Virginia", - "VA - Virginia"}, - // Do accidentally match "Virginia" to "West Virginia". - // TODO(crbug.com/624770): This test should not pass, but it does because - // "Virginia" is a substring of "West Virginia". - {{"WV - West Virginia", "TX - Texas"}, "Virginia", "WV - West Virginia"}, - // Tests that substring matches work for full state names (a full token - // match isn't required). Also tests that matches work for states with - // whitespace in the middle. - {{"California.", "North Carolina."}, "North Carolina", "North Carolina."}, - {{"NC - North Carolina", "CA - California"}, "CA", "CA - California"}, - // These are not states. - {{"NCNCA", "SCNCA"}, "NC", ""}}; - - for (TestCase test_case : test_cases) { - AutofillField field; - test::CreateTestSelectField(test_case.select_values, &field); - field.set_heuristic_type(ADDRESS_HOME_STATE); - - AutofillField::FillFormField(field, UTF8ToUTF16(test_case.input_value), - "en-US", "en-US", &field); - EXPECT_EQ(UTF8ToUTF16(test_case.expected_value), field.value); - } -} +struct FillSelectTestCase { + std::vector<const char*> select_values; + const char* input_value; + const char* expected_value; +}; + +class AutofillSelectWithStatesTest + : public testing::TestWithParam<FillSelectTestCase> { + public: + AutofillSelectWithStatesTest() { CountryNames::SetLocaleString("en-US"); } +}; -TEST_F(AutofillFieldTest, FillSelectWithCountries) { - typedef struct { - std::vector<const char*> select_values; - const char* input_value; - const char* expected_value; - } TestCase; +TEST_P(AutofillSelectWithStatesTest, FillSelectWithStates) { + auto test_case = GetParam(); + AutofillField field; + test::CreateTestSelectField(test_case.select_values, &field); + field.set_heuristic_type(ADDRESS_HOME_STATE); - TestCase test_cases[] = {// Full country names. - {{"Albania", "Canada"}, "CA", "Canada"}, - // Abbreviations. - {{"AL", "CA"}, "Canada", "CA"}}; + AutofillField::FillFormField(field, UTF8ToUTF16(test_case.input_value), + "en-US", "en-US", &field); + EXPECT_EQ(UTF8ToUTF16(test_case.expected_value), field.value); +} - for (TestCase test_case : test_cases) { - AutofillField field; - test::CreateTestSelectField(test_case.select_values, &field); - field.set_heuristic_type(ADDRESS_HOME_COUNTRY); +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillSelectWithStatesTest, + testing::Values( + // Filling the abbreviation. + FillSelectTestCase{{"Alabama", "California"}, "CA", "California"}, + // Attempting to fill the full name in a select full of abbreviations. + FillSelectTestCase{{"AL", "CA"}, "California", "CA"}, + // Different case and diacritics. + FillSelectTestCase{{"QUÉBEC", "ALBERTA"}, "Quebec", "QUÉBEC"}, + // Inexact state names. + FillSelectTestCase{ + {"SC - South Carolina", "CA - California", "NC - North Carolina"}, + "California", + "CA - California"}, + // Don't accidentally match "Virginia" to "West Virginia". + FillSelectTestCase{ + {"WV - West Virginia", "VA - Virginia", "NV - North Virginia"}, + "Virginia", + "VA - Virginia"}, + // Do accidentally match "Virginia" to "West Virginia". + // TODO(crbug.com/624770): This test should not pass, but it does + // because "Virginia" is a substring of "West Virginia". + FillSelectTestCase{{"WV - West Virginia", "TX - Texas"}, + "Virginia", + "WV - West Virginia"}, + // Tests that substring matches work for full state names (a full token + // match isn't required). Also tests that matches work for states with + // whitespace in the middle. + FillSelectTestCase{{"California.", "North Carolina."}, + "North Carolina", + "North Carolina."}, + FillSelectTestCase{{"NC - North Carolina", "CA - California"}, + "CA", + "CA - California"}, + // These are not states. + FillSelectTestCase{{"NCNCA", "SCNCA"}, "NC", ""})); + +class AutofillSelectWithCountriesTest + : public testing::TestWithParam<FillSelectTestCase> { + public: + AutofillSelectWithCountriesTest() { CountryNames::SetLocaleString("en-US"); } +}; - AutofillField::FillFormField(field, UTF8ToUTF16(test_case.input_value), - "en-US", "en-US", &field); - EXPECT_EQ(UTF8ToUTF16(test_case.expected_value), field.value); - } +TEST_P(AutofillSelectWithCountriesTest, FillSelectWithCountries) { + auto test_case = GetParam(); + AutofillField field; + test::CreateTestSelectField(test_case.select_values, &field); + field.set_heuristic_type(ADDRESS_HOME_COUNTRY); + + AutofillField::FillFormField(field, UTF8ToUTF16(test_case.input_value), + "en-US", "en-US", &field); + EXPECT_EQ(UTF8ToUTF16(test_case.expected_value), field.value); } -TEST_F(AutofillFieldTest, FillSelectControlWithExpirationMonth) { - typedef struct { - std::vector<const char*> select_values; - std::vector<const char*> select_contents; - } TestCase; - - TestCase test_cases[] = { - // Values start at 1. - {{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, - NotNumericMonthsContentsNoPlaceholder()}, - // Values start at 1 but single digits are whitespace padded! - {{" 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "11", "12"}, - NotNumericMonthsContentsNoPlaceholder()}, - // Values start at 0. - {{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, - NotNumericMonthsContentsNoPlaceholder()}, - // Values start at 00. - {{"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11"}, - NotNumericMonthsContentsNoPlaceholder()}, - // The AngularJS framework adds a prefix to number types. Test that it is - // removed. - {{"number:1", "number:2", "number:3", "number:4", "number:5", "number:6", - "number:7", "number:8", "number:9", "number:10", "number:11", - "number:12"}, - NotNumericMonthsContentsNoPlaceholder()}, - // Values start at 0 and the first content is a placeholder. - {{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, - NotNumericMonthsContentsWithPlaceholder()}, - // Values start at 1 and the first content is a placeholder. - {{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"}, - NotNumericMonthsContentsWithPlaceholder()}, - // Values start at 01 and the first content is a placeholder. - {{"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", - "13"}, - NotNumericMonthsContentsWithPlaceholder()}, - // Values start at 0 after a placeholder. - {{"?", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, - NotNumericMonthsContentsWithPlaceholder()}, - // Values start at 1 after a placeholder. - {{"?", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, - NotNumericMonthsContentsWithPlaceholder()}, - // Values start at 0 after a negative number. - {{"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, - NotNumericMonthsContentsWithPlaceholder()}, - // Values start at 1 after a negative number. - {{"-1", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, - NotNumericMonthsContentsWithPlaceholder()}}; - - for (TestCase test_case : test_cases) { - ASSERT_EQ(test_case.select_values.size(), test_case.select_contents.size()); - - TestFillingExpirationMonth(test_case.select_values, - test_case.select_contents, - test_case.select_values.size()); +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillSelectWithCountriesTest, + testing::Values( + // Full country names. + FillSelectTestCase{{"Albania", "Canada"}, "CA", "Canada"}, + // Abbreviations. + FillSelectTestCase{{"AL", "CA"}, "Canada", "CA"})); + +struct FillWithExpirationMonthTestCase { + std::vector<const char*> select_values; + std::vector<const char*> select_contents; +}; + +class AutofillSelectWithExpirationMonthTest + : public testing::TestWithParam<FillWithExpirationMonthTestCase> { + public: + AutofillSelectWithExpirationMonthTest() { + CountryNames::SetLocaleString("en-US"); } +}; + +TEST_P(AutofillSelectWithExpirationMonthTest, + FillSelectControlWithExpirationMonth) { + auto test_case = GetParam(); + ASSERT_EQ(test_case.select_values.size(), test_case.select_contents.size()); + + TestFillingExpirationMonth(test_case.select_values, test_case.select_contents, + test_case.select_values.size()); } +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillSelectWithExpirationMonthTest, + testing::Values( + // Values start at 1. + FillWithExpirationMonthTestCase{ + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"}, + NotNumericMonthsContentsNoPlaceholder()}, + // Values start at 1 but single digits are whitespace padded! + FillWithExpirationMonthTestCase{ + {" 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "11", + "12"}, + NotNumericMonthsContentsNoPlaceholder()}, + // Values start at 0. + FillWithExpirationMonthTestCase{ + {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + NotNumericMonthsContentsNoPlaceholder()}, + // Values start at 00. + FillWithExpirationMonthTestCase{ + {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", + "11"}, + NotNumericMonthsContentsNoPlaceholder()}, + // The AngularJS framework adds a prefix to number types. Test that it + // is removed. + FillWithExpirationMonthTestCase{ + {"number:1", "number:2", "number:3", "number:4", "number:5", + "number:6", "number:7", "number:8", "number:9", "number:10", + "number:11", "number:12"}, + NotNumericMonthsContentsNoPlaceholder()}, + // Values start at 0 and the first content is a placeholder. + FillWithExpirationMonthTestCase{ + {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", + "12"}, + NotNumericMonthsContentsWithPlaceholder()}, + // Values start at 1 and the first content is a placeholder. + FillWithExpirationMonthTestCase{ + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", + "13"}, + NotNumericMonthsContentsWithPlaceholder()}, + // Values start at 01 and the first content is a placeholder. + FillWithExpirationMonthTestCase{ + {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", + "12", "13"}, + NotNumericMonthsContentsWithPlaceholder()}, + // Values start at 0 after a placeholder. + FillWithExpirationMonthTestCase{ + {"?", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}, + NotNumericMonthsContentsWithPlaceholder()}, + // Values start at 1 after a placeholder. + FillWithExpirationMonthTestCase{ + {"?", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", + "12"}, + NotNumericMonthsContentsWithPlaceholder()}, + // Values start at 0 after a negative number. + FillWithExpirationMonthTestCase{ + {"-1", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "11"}, + NotNumericMonthsContentsWithPlaceholder()}, + // Values start at 1 after a negative number. + FillWithExpirationMonthTestCase{ + {"-1", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", + "12"}, + NotNumericMonthsContentsWithPlaceholder()})); + TEST_F(AutofillFieldTest, FillSelectControlWithAbbreviatedMonthName) { std::vector<const char*> kMonthsAbbreviated = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", @@ -920,50 +1027,64 @@ TEST_F(AutofillFieldTest, FindShortestSubstringMatchInSelect) { // Tests that text state fields are filled correctly depending on their // maxlength attribute value. -TEST_F(AutofillFieldTest, FillStateText) { - typedef struct { - HtmlFieldType field_type; - size_t field_max_length; - std::string value_to_fill; - std::string expected_value; - bool should_fill; - } TestCase; - - TestCase test_cases[] = { - // Filling a state to a text field with the default maxlength value should - // fill the state value as is. - {HTML_TYPE_ADDRESS_LEVEL1, /* default value */ 0, "New York", "New York", - true}, - {HTML_TYPE_ADDRESS_LEVEL1, /* default value */ 0, "NY", "NY", true}, - // Filling a state to a text field with a maxlength value equal to the - // value's length should fill the state value as is. - {HTML_TYPE_ADDRESS_LEVEL1, 8, "New York", "New York", true}, - // Filling a state to a text field with a maxlength value lower than the - // value's length but higher than the value's abbreviation should fill the - // state abbreviation. - {HTML_TYPE_ADDRESS_LEVEL1, 2, "New York", "NY", true}, - {HTML_TYPE_ADDRESS_LEVEL1, 2, "NY", "NY", true}, - // Filling a state to a text field with a maxlength value lower than the - // value's length and the value's abbreviation should not fill at all. - {HTML_TYPE_ADDRESS_LEVEL1, 1, "New York", "", false}, - {HTML_TYPE_ADDRESS_LEVEL1, 1, "NY", "", false}, - // Filling a state to a text field with a maxlength value lower than the - // value's length and that has no associated abbreviation should not fill - // at all. - {HTML_TYPE_ADDRESS_LEVEL1, 3, "Quebec", "", false}}; - - for (const TestCase& test_case : test_cases) { - AutofillField field; - field.SetHtmlType(test_case.field_type, HtmlFieldMode()); - field.max_length = test_case.field_max_length; - - bool has_filled = AutofillField::FillFormField( - field, ASCIIToUTF16(test_case.value_to_fill), "en-US", "en-US", &field); - - EXPECT_EQ(test_case.should_fill, has_filled); - EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); - } +struct FillStateTextTestCase { + HtmlFieldType field_type; + size_t field_max_length; + std::string value_to_fill; + std::string expected_value; + bool should_fill; +}; + +class AutofillStateTextTest + : public testing::TestWithParam<FillStateTextTestCase> { + public: + AutofillStateTextTest() { CountryNames::SetLocaleString("en-US"); } +}; + +TEST_P(AutofillStateTextTest, FillStateText) { + auto test_case = GetParam(); + AutofillField field; + field.SetHtmlType(test_case.field_type, HtmlFieldMode()); + field.max_length = test_case.field_max_length; + + bool has_filled = AutofillField::FillFormField( + field, ASCIIToUTF16(test_case.value_to_fill), "en-US", "en-US", &field); + + EXPECT_EQ(test_case.should_fill, has_filled); + EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value); } +INSTANTIATE_TEST_CASE_P( + AutofillFieldTest, + AutofillStateTextTest, + testing::Values( + // Filling a state to a text field with the default maxlength value + // should + // fill the state value as is. + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, /* default value */ 0, + "New York", "New York", true}, + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, /* default value */ 0, + "NY", "NY", true}, + // Filling a state to a text field with a maxlength value equal to the + // value's length should fill the state value as is. + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 8, "New York", + "New York", true}, + // Filling a state to a text field with a maxlength value lower than the + // value's length but higher than the value's abbreviation should fill + // the state abbreviation. + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 2, "New York", "NY", + true}, + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 2, "NY", "NY", true}, + // Filling a state to a text field with a maxlength value lower than the + // value's length and the value's abbreviation should not fill at all. + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 1, "New York", "", + false}, + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 1, "NY", "", false}, + // Filling a state to a text field with a maxlength value lower than the + // value's length and that has no associated abbreviation should not + // fill at all. + FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 3, "Quebec", "", + false})); + } // namespace } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc index 056793fe09b..933b7c5dd41 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.cc +++ b/chromium/components/autofill/core/browser/autofill_manager.cc @@ -26,6 +26,7 @@ #include "base/path_service.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -50,6 +51,7 @@ #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/popup_item_ids.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" @@ -60,6 +62,7 @@ #include "components/autofill/core/common/form_data_predictions.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/password_form_fill_data.h" +#include "components/autofill/core/common/signatures_util.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" #include "components/rappor/public/rappor_utils.h" @@ -129,9 +132,9 @@ base::string16 SanitizeCreditCardFieldValue(const base::string16& value) { // want the logic of which variations of names are considered to be the same to // exactly match the logic applied on the Payments server. base::string16 RemoveMiddleInitial(const base::string16& name) { - std::vector<base::string16> parts = - base::SplitString(name, base::kWhitespaceUTF16, base::KEEP_WHITESPACE, - base::SPLIT_WANT_NONEMPTY); + std::vector<base::StringPiece16> parts = + base::SplitStringPiece(name, base::kWhitespaceUTF16, + base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); if (parts.size() == 3 && (parts[1].length() == 1 || (parts[1].length() == 2 && base::EndsWith(parts[1], base::ASCIIToUTF16("."), @@ -215,16 +218,24 @@ AutofillManager::AutofillManager( AutofillDownloadManagerState enable_download_manager) : driver_(driver), client_(client), - payments_client_( - new payments::PaymentsClient(driver->GetURLRequestContext(), this)), + payments_client_(base::MakeUnique<payments::PaymentsClient>( + driver->GetURLRequestContext(), + this)), app_locale_(app_locale), personal_data_(client->GetPersonalDataManager()), autocomplete_history_manager_( - new AutocompleteHistoryManager(driver, client)), + base::MakeUnique<AutocompleteHistoryManager>(driver, client)), + form_interactions_ukm_logger_( + base::MakeUnique<AutofillMetrics::FormInteractionsUkmLogger>( + client->GetUkmService())), address_form_event_logger_( - new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + false /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), credit_card_form_event_logger_( - new AutofillMetrics::FormEventLogger(true /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + true /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), has_logged_autofill_enabled_(false), has_logged_address_suggestions_count_(false), did_show_suggestions_(false), @@ -232,6 +243,7 @@ AutofillManager::AutofillManager( user_did_autofill_(false), user_did_edit_autofilled_field_(false), user_did_accept_upload_prompt_(false), + should_cvc_be_requested_(false), external_delegate_(NULL), test_delegate_(NULL), #if defined(OS_ANDROID) || defined(OS_IOS) @@ -245,10 +257,8 @@ AutofillManager::AutofillManager( if (personal_data_ && client_) personal_data_->OnSyncServiceInitialized(client_->GetSyncService()); -#if defined(OS_ANDROID) if (personal_data_ && driver_) personal_data_->SetURLRequestContextGetter(driver_->GetURLRequestContext()); -#endif } AutofillManager::~AutofillManager() {} @@ -493,7 +503,7 @@ void AutofillManager::ProcessPendingFormForUpload() { if (!upload_form) return; - StartUploadProcess(std::move(upload_form), base::TimeTicks::Now(), false); + StartUploadProcess(std::move(upload_form), TimeTicks::Now(), false); } void AutofillManager::OnTextFieldDidChange(const FormData& form, @@ -512,6 +522,9 @@ void AutofillManager::OnTextFieldDidChange(const FormData& form, UpdatePendingForm(form); + if (!user_did_type_ || autofill_field->is_autofilled) + form_interactions_ukm_logger_->LogTextFieldDidChange(*autofill_field); + if (!user_did_type_) { user_did_type_ = true; AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE); @@ -1033,13 +1046,16 @@ void AutofillManager::OnDidGetUploadDetails( user_did_accept_upload_prompt_ = false; client_->ConfirmSaveCreditCardToCloud( upload_request_.card, std::move(legal_message), + should_cvc_be_requested_, base::Bind(&AutofillManager::OnUserDidAcceptUpload, weak_ptr_factory_.GetWeakPtr())); client_->LoadRiskData(base::Bind(&AutofillManager::OnDidGetUploadRiskData, weak_ptr_factory_.GetWeakPtr())); - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_OFFERED); - LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); + AutofillMetrics::CardUploadDecisionMetric card_upload_decision_metric = + should_cvc_be_requested_ ? AutofillMetrics::UPLOAD_OFFERED_NO_CVC + : AutofillMetrics::UPLOAD_OFFERED; + AutofillMetrics::LogCardUploadDecisionMetric(card_upload_decision_metric); + LogCardUploadDecisionUkm(card_upload_decision_metric); } else { // If the upload details request failed, fall back to a local save. The // reasoning here is as follows: @@ -1099,6 +1115,13 @@ void AutofillManager::OnUserDidAcceptUpload() { user_did_accept_upload_prompt_ = true; if (!upload_request_.risk_data.empty()) { upload_request_.app_locale = app_locale_; + // If the upload request does not have card CVC, populate it with the + // value provided by the user: + if (upload_request_.cvc.empty()) { + DCHECK(client_->GetSaveCardBubbleController()); + upload_request_.cvc = + client_->GetSaveCardBubbleController()->GetCvcEnteredByUser(); + } payments_client_->UploadCard(upload_request_); } } @@ -1107,6 +1130,13 @@ void AutofillManager::OnDidGetUploadRiskData(const std::string& risk_data) { upload_request_.risk_data = risk_data; if (user_did_accept_upload_prompt_) { upload_request_.app_locale = app_locale_; + // If the upload request does not have card CVC, populate it with the + // value provided by the user: + if (upload_request_.cvc.empty()) { + DCHECK(client_->GetSaveCardBubbleController()); + upload_request_.cvc = + client_->GetSaveCardBubbleController()->GetCvcEnteredByUser(); + } payments_client_->UploadCard(upload_request_); } } @@ -1126,7 +1156,7 @@ bool AutofillManager::IsCreditCardUploadEnabled() { } bool AutofillManager::ShouldUploadForm(const FormStructure& form) { - return IsAutofillEnabled() && !driver_->IsOffTheRecord() && + return IsAutofillEnabled() && !driver_->IsIncognito() && form.ShouldBeParsed() && (form.active_field_count() >= kRequiredFieldsForUpload || (form.all_fields_are_passwords() && @@ -1199,9 +1229,11 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { // because if only one of the two is missing, it may be fixable. // Check for a CVC to determine whether we can prompt the user to upload - // their card. If no CVC is present, do nothing. We could fall back to a - // local save but we believe that sometimes offering upload and sometimes - // offering local save is a confusing user experience. + // their card. If no CVC is present and the experiment is off, do nothing. + // We could fall back to a local save but we believe that sometimes offering + // upload and sometimes offering local save is a confusing user experience. + // If no CVC and the experiment is on, request CVC from the user in the + // bubble and save using the provided value. for (const auto& field : submitted_form) { if (field->Type().GetStorableType() == CREDIT_CARD_VERIFICATION_CODE && IsValidCreditCardSecurityCode(field->value, @@ -1226,14 +1258,19 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) { // Both the CVC and address checks are done. Conform to the legacy order of // reporting on CVC then address. + should_cvc_be_requested_ = false; if (upload_request_.cvc.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); - LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); - pending_upload_request_url_ = GURL(); - CollectRapportSample(submitted_form.source_url(), - "Autofill.CardUploadNotOfferedNoCvc"); - return; + if (IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled()) { + should_cvc_be_requested_ = true; + } else { + AutofillMetrics::LogCardUploadDecisionMetric( + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + pending_upload_request_url_ = GURL(); + CollectRapportSample(submitted_form.source_url(), + "Autofill.CardUploadNotOfferedNoCvc"); + return; + } } if (!get_profiles_succeeded) { DCHECK(get_profiles_decision_metric != AutofillMetrics::UPLOAD_OFFERED); @@ -1378,11 +1415,10 @@ void AutofillManager::UploadFormDataAsyncCallback( const TimeTicks& interaction_time, const TimeTicks& submission_time, bool observed_submission) { - submitted_form->LogQualityMetrics(load_time, interaction_time, - submission_time, - client_->GetRapporServiceImpl(), - did_show_suggestions_, observed_submission); - + submitted_form->LogQualityMetrics( + load_time, interaction_time, submission_time, + client_->GetRapporServiceImpl(), form_interactions_ukm_logger_.get(), + did_show_suggestions_, observed_submission); if (submitted_form->ShouldBeCrowdsourced()) UploadFormData(*submitted_form, observed_submission); } @@ -1416,10 +1452,12 @@ void AutofillManager::Reset() { ProcessPendingFormForUpload(); DCHECK(!pending_form_data_); form_structures_.clear(); - address_form_event_logger_.reset( - new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)); - credit_card_form_event_logger_.reset( - new AutofillMetrics::FormEventLogger(true /* is_for_credit_card */)); + form_interactions_ukm_logger_.reset( + new AutofillMetrics::FormInteractionsUkmLogger(client_->GetUkmService())); + address_form_event_logger_.reset(new AutofillMetrics::FormEventLogger( + false /* is_for_credit_card */, form_interactions_ukm_logger_.get())); + credit_card_form_event_logger_.reset(new AutofillMetrics::FormEventLogger( + true /* is_for_credit_card */, form_interactions_ukm_logger_.get())); #if defined(OS_ANDROID) || defined(OS_IOS) autofill_assistant_.Reset(); #endif @@ -1443,16 +1481,24 @@ AutofillManager::AutofillManager(AutofillDriver* driver, PersonalDataManager* personal_data) : driver_(driver), client_(client), - payments_client_( - new payments::PaymentsClient(driver->GetURLRequestContext(), this)), + payments_client_(base::MakeUnique<payments::PaymentsClient>( + driver->GetURLRequestContext(), + this)), app_locale_("en-US"), personal_data_(personal_data), autocomplete_history_manager_( - new AutocompleteHistoryManager(driver, client)), + base::MakeUnique<AutocompleteHistoryManager>(driver, client)), + form_interactions_ukm_logger_( + base::MakeUnique<AutofillMetrics::FormInteractionsUkmLogger>( + client->GetUkmService())), address_form_event_logger_( - new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + false /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), credit_card_form_event_logger_( - new AutofillMetrics::FormEventLogger(true /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + true /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), has_logged_autofill_enabled_(false), has_logged_address_suggestions_count_(false), did_show_suggestions_(false), @@ -1483,34 +1529,34 @@ bool AutofillManager::RefreshDataModels() { // Updating the FormEventLoggers for addresses and credit cards. { - bool is_server_data_available = false; - bool is_local_data_available = false; + size_t server_record_type_count = 0; + size_t local_record_type_count = 0; for (CreditCard* credit_card : credit_cards) { if (credit_card->record_type() == CreditCard::LOCAL_CARD) - is_local_data_available = true; + local_record_type_count++; else - is_server_data_available = true; + server_record_type_count++; } - credit_card_form_event_logger_->set_is_server_data_available( - is_server_data_available); - credit_card_form_event_logger_->set_is_local_data_available( - is_local_data_available); + credit_card_form_event_logger_->set_server_record_type_count( + server_record_type_count); + credit_card_form_event_logger_->set_local_record_type_count( + local_record_type_count); credit_card_form_event_logger_->set_is_context_secure( client_->IsContextSecure()); } { - bool is_server_data_available = false; - bool is_local_data_available = false; + size_t server_record_type_count = 0; + size_t local_record_type_count = 0; for (AutofillProfile* profile : profiles) { if (profile->record_type() == AutofillProfile::LOCAL_PROFILE) - is_local_data_available = true; + local_record_type_count++; else if (profile->record_type() == AutofillProfile::SERVER_PROFILE) - is_server_data_available = true; + server_record_type_count++; } - address_form_event_logger_->set_is_server_data_available( - is_server_data_available); - address_form_event_logger_->set_is_local_data_available( - is_local_data_available); + address_form_event_logger_->set_server_record_type_count( + server_record_type_count); + address_form_event_logger_->set_local_record_type_count( + local_record_type_count); } if (profiles.empty() && credit_cards.empty()) @@ -1679,7 +1725,8 @@ void AutofillManager::FillOrPreviewDataModelForm( std::unique_ptr<FormStructure> AutofillManager::ValidateSubmittedForm( const FormData& form) { - std::unique_ptr<FormStructure> submitted_form(new FormStructure(form)); + std::unique_ptr<FormStructure> submitted_form( + base::MakeUnique<FormStructure>(form)); if (!ShouldUploadForm(*submitted_form)) return std::unique_ptr<FormStructure>(); @@ -1689,7 +1736,7 @@ std::unique_ptr<FormStructure> AutofillManager::ValidateSubmittedForm( if (!FindCachedForm(form, &cached_submitted_form)) return std::unique_ptr<FormStructure>(); - submitted_form->UpdateFromCache(*cached_submitted_form); + submitted_form->UpdateFromCache(*cached_submitted_form, false); return submitted_form; } @@ -1702,8 +1749,9 @@ bool AutofillManager::FindCachedForm(const FormData& form, // protocol with the crowdsourcing server does not permit us to discard the // original versions of the forms. *form_structure = NULL; + const auto& form_signature = autofill::CalculateFormSignature(form); for (auto& cur_form : base::Reversed(form_structures_)) { - if (*cur_form == form) { + if (cur_form->form_signature() == form_signature || *cur_form == form) { *form_structure = cur_form.get(); // The same form might be cached with multiple field counts: in some @@ -1785,41 +1833,17 @@ bool AutofillManager::UpdateCachedForm(const FormData& live_form, if (!needs_update) return true; - if (form_structures_.size() >= kMaxFormCacheSize) + // Note: We _must not_ remove the original version of the cached form from + // the list of |form_structures_|. Otherwise, we break parsing of the + // crowdsourcing server's response to our query. + if (!ParseForm(live_form, updated_form)) return false; - // Add the new or updated form to our cache. - form_structures_.push_back(base::MakeUnique<FormStructure>(live_form)); - *updated_form = form_structures_.rbegin()->get(); - (*updated_form)->DetermineHeuristicTypes(); - - // If we have cached data, propagate it to the updated form. - if (cached_form) { - std::map<base::string16, const AutofillField*> cached_fields; - for (size_t i = 0; i < cached_form->field_count(); ++i) { - const AutofillField* field = cached_form->field(i); - cached_fields[field->unique_name()] = field; - } - - for (size_t i = 0; i < (*updated_form)->field_count(); ++i) { - AutofillField* field = (*updated_form)->field(i); - auto cached_field = cached_fields.find(field->unique_name()); - if (cached_field != cached_fields.end()) { - field->set_server_type(cached_field->second->server_type()); - field->is_autofilled = cached_field->second->is_autofilled; - field->set_previously_autofilled( - cached_field->second->previously_autofilled()); - } - } - - // Note: We _must not_ remove the original version of the cached form from - // the list of |form_structures_|. Otherwise, we break parsing of the - // crowdsourcing server's response to our query. - } + if (cached_form) + (*updated_form)->UpdateFromCache(*cached_form, true); // Annotate the updated form with its predicted types. - std::vector<FormStructure*> forms(1, *updated_form); - driver_->SendAutofillTypePredictionsToRenderer(forms); + driver_->SendAutofillTypePredictionsToRenderer({*updated_form}); return true; } @@ -1878,27 +1902,21 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { std::vector<FormStructure*> non_queryable_forms; std::vector<FormStructure*> queryable_forms; for (const FormData& form : forms) { - const auto parse_form_start_time = base::TimeTicks::Now(); + const auto parse_form_start_time = TimeTicks::Now(); - std::unique_ptr<FormStructure> form_structure = - base::MakeUnique<FormStructure>(form); - form_structure->ParseFieldTypesFromAutocompleteAttributes(); - if (!form_structure->ShouldBeParsed()) + FormStructure* form_structure = nullptr; + if (!ParseForm(form, &form_structure)) continue; + DCHECK(form_structure); - form_structure->DetermineHeuristicTypes(); - - // Ownership is transferred to |form_structures_| which maintains it until - // the manager is Reset() or destroyed. It is safe to use references below - // as long as receivers don't take ownership. - form_structures_.push_back(std::move(form_structure)); - - if (form_structures_.back()->ShouldBeCrowdsourced()) - queryable_forms.push_back(form_structures_.back().get()); + // Set aside forms with method GET or author-specified types, so that they + // are not included in the query to the server. + if (form_structure->ShouldBeCrowdsourced()) + queryable_forms.push_back(form_structure); else - non_queryable_forms.push_back(form_structures_.back().get()); + non_queryable_forms.push_back(form_structure); - AutofillMetrics::LogParseFormTiming(base::TimeTicks::Now() - + AutofillMetrics::LogParseFormTiming(TimeTicks::Now() - parse_form_start_time); } @@ -1909,6 +1927,9 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { if (!queryable_forms.empty() || !non_queryable_forms.empty()) { AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED); + // Setup the url for metrics that we will collect for this form. + form_interactions_ukm_logger_->OnFormsLoaded(forms[0].origin); + #if defined(OS_IOS) // Log this from same location as AutofillMetrics::FORMS_LOADED to ensure // that KeyboardAccessoryButtonsIOS and UserHappiness UMA metrics will be @@ -1938,6 +1959,26 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) { driver_->SendAutofillTypePredictionsToRenderer(non_queryable_forms); } +bool AutofillManager::ParseForm(const FormData& form, + FormStructure** parsed_form_structure) { + DCHECK(parsed_form_structure); + if (form_structures_.size() >= kMaxFormCacheSize) + return false; + + auto form_structure = base::MakeUnique<FormStructure>(form); + form_structure->ParseFieldTypesFromAutocompleteAttributes(); + if (!form_structure->ShouldBeParsed()) + return false; + + // Ownership is transferred to |form_structures_| which maintains it until + // the manager is Reset() or destroyed. It is safe to use references below + // as long as receivers don't take ownership. + form_structures_.push_back(std::move(form_structure)); + *parsed_form_structure = form_structures_.back().get(); + (*parsed_form_structure)->DetermineHeuristicTypes(client_->GetUkmService()); + return true; +} + int AutofillManager::BackendIDToInt(const std::string& backend_id) const { if (!base::IsValidGUID(backend_id)) return 0; @@ -2016,7 +2057,7 @@ void AutofillManager::DeterminePossibleFieldTypesForUpload( // profile or credit card, identify any stored types that match the value. for (size_t i = 0; i < submitted_form->field_count(); ++i) { AutofillField* field = submitted_form->field(i); - ServerFieldTypeSet matching_types; + ServerFieldTypeSet matching_types = field->possible_types(); base::string16 value; base::TrimWhitespace(field->value, base::TRIM_ALL, &value); diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h index 290126ec108..c8597275741 100644 --- a/chromium/components/autofill/core/browser/autofill_manager.h +++ b/chromium/components/autofill/core/browser/autofill_manager.h @@ -43,8 +43,6 @@ #define ENABLE_FORM_DEBUG_DUMP #endif -class GURL; - namespace gfx { class RectF; } @@ -191,9 +189,9 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Will send an upload based on the |form_structure| data and the local // Autofill profile data. |observed_submission| is specified if the upload // follows an observed submission event. - void StartUploadProcess(std::unique_ptr<FormStructure> form_structure, - const base::TimeTicks& timestamp, - bool observed_submission); + virtual void StartUploadProcess(std::unique_ptr<FormStructure> form_structure, + const base::TimeTicks& timestamp, + bool observed_submission); // Update the pending form with |form|, possibly processing the current // pending form for upload. @@ -270,6 +268,10 @@ class AutofillManager : public AutofillDownloadManager::Observer, return &form_structures_; } + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger() { + return form_interactions_ukm_logger_.get(); + } + // Exposed for testing. AutofillExternalDelegate* external_delegate() { return external_delegate_; @@ -414,6 +416,9 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Parses the forms using heuristic matching and querying the Autofill server. void ParseForms(const std::vector<FormData>& forms); + // Parses the form and adds it to |form_structures_|. + bool ParseForm(const FormData& form, FormStructure** parsed_form_structure); + // Imports the form data, submitted by the user, into |personal_data_|. void ImportFormData(const FormStructure& submitted_form); @@ -507,6 +512,10 @@ class AutofillManager : public AutofillDownloadManager::Observer, // Handles single-field autocomplete form data. std::unique_ptr<AutocompleteHistoryManager> autocomplete_history_manager_; + // Utility for logging URL keyed metrics. + std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> + form_interactions_ukm_logger_; + // Utilities for logging form events. std::unique_ptr<AutofillMetrics::FormEventLogger> address_form_event_logger_; std::unique_ptr<AutofillMetrics::FormEventLogger> @@ -552,6 +561,7 @@ class AutofillManager : public AutofillDownloadManager::Observer, payments::PaymentsClient::UploadRequestDetails upload_request_; bool user_did_accept_upload_prompt_; GURL pending_upload_request_url_; + bool should_cvc_be_requested_; #ifdef ENABLE_FORM_DEBUG_DUMP // The last few autofilled forms (key/value pairs) submitted, for debugging. @@ -592,6 +602,7 @@ class AutofillManager : public AutofillDownloadManager::Observer, FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSubmittedFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressWillSubmitFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSuggestionsCount); + FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillFormSubmittedState); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtPageLoad); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSelectedFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardFilledFormEvents); diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc index 54643c65a6a..e9d94a48d40 100644 --- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc @@ -8,6 +8,7 @@ #include <algorithm> #include <memory> +#include <utility> #include <vector> #include "base/command_line.h" @@ -27,6 +28,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" @@ -57,6 +59,7 @@ #include "components/ukm/ukm_entry.h" #include "components/ukm/ukm_source.h" #include "components/variations/variations_associated_data.h" +#include "net/base/url_util.h" #include "net/url_request/url_request_test_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -170,9 +173,11 @@ class TestPersonalDataManager : public PersonalDataManager { web_profiles_.push_back(std::move(profile)); } - void AddCreditCard(std::unique_ptr<CreditCard> credit_card) { - credit_card->set_modification_date(base::Time::Now()); - local_credit_cards_.push_back(std::move(credit_card)); + void AddCreditCard(const CreditCard& credit_card) override { + std::unique_ptr<CreditCard> local_credit_card = + base::MakeUnique<CreditCard>(credit_card); + local_credit_card->set_modification_date(base::Time::Now()); + local_credit_cards_.push_back(std::move(local_credit_card)); } void RecordUseOf(const AutofillDataModel& data_model) override { @@ -492,18 +497,18 @@ class MockAutocompleteHistoryManager : public AutocompleteHistoryManager { class MockAutofillDriver : public TestAutofillDriver { public: MockAutofillDriver() - : is_off_the_record_(false), did_interact_with_credit_card_form_(false) {} + : is_incognito_(false), did_interact_with_credit_card_form_(false) {} // Mock methods to enable testability. MOCK_METHOD3(SendFormDataToRenderer, void(int query_id, RendererFormDataAction action, const FormData& data)); - void SetIsOffTheRecord(bool is_off_the_record) { - is_off_the_record_ = is_off_the_record; + void SetIsIncognito(bool is_incognito) { + is_incognito_ = is_incognito; } - bool IsOffTheRecord() const override { return is_off_the_record_; } + bool IsIncognito() const override { return is_incognito_; } void DidInteractWithCreditCardForm() override { did_interact_with_credit_card_form_ = true; @@ -518,7 +523,7 @@ class MockAutofillDriver : public TestAutofillDriver { } private: - bool is_off_the_record_; + bool is_incognito_; bool did_interact_with_credit_card_form_; DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver); }; @@ -628,8 +633,8 @@ class TestAutofillManager : public AutofillManager { personal_data_->AddProfile(std::move(profile)); } - void AddCreditCard(std::unique_ptr<CreditCard> credit_card) { - personal_data_->AddCreditCard(std::move(credit_card)); + void AddCreditCard(const CreditCard& credit_card) { + personal_data_->AddCreditCard(credit_card); } int GetPackedCreditCardID(int credit_card_id) { @@ -790,6 +795,15 @@ const ukm::Entry_Metric* FindMetric( return nullptr; } +// Get Ukm sources from the Ukm service. +std::vector<const ukm::UkmSource*> GetUkmSources(ukm::TestUkmService* service) { + std::vector<const ukm::UkmSource*> sources; + for (const auto& kv : service->GetSources()) + sources.push_back(kv.second.get()); + + return sources; +} + } // namespace class AutofillManagerTest : public testing::Test { @@ -1018,13 +1032,17 @@ class AutofillManagerTest : public testing::Test { scoped_feature_list_.InitAndEnableFeature(kAutofillUkmLogging); } - void ExpectUniqueCardUploadDecisionUkm( - AutofillMetrics::CardUploadDecisionMetric upload_decision) { + void EnableAutofillUpstreamRequestCvcIfMissingExperimentAndUkmLogging() { + scoped_feature_list_.InitWithFeatures( + {kAutofillUpstreamRequestCvcIfMissing, kAutofillUkmLogging}, {}); + } + + void ExpectUniqueFillableFormParsedUkm() { ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); // Check that one source is logged. ASSERT_EQ(1U, ukm_service->sources_count()); - const ukm::UkmSource* source = ukm_service->GetSource(0); + const ukm::UkmSource* source = GetUkmSources(ukm_service)[0]; // Check that one entry is logged. EXPECT_EQ(1U, ukm_service->entries_count()); @@ -1035,20 +1053,67 @@ class AutofillManagerTest : public testing::Test { entry->PopulateProto(&entry_proto); EXPECT_EQ(source->id(), entry_proto.source_id()); - // Check if there is an entry for card upload decisions. - EXPECT_EQ(base::HashMetricName(internal::kUKMCardUploadDecisionEntryName), + // Check if there is an entry for developer engagement decision. + EXPECT_EQ(base::HashMetricName(internal::kUKMDeveloperEngagementEntryName), entry_proto.event_hash()); EXPECT_EQ(1, entry_proto.metrics_size()); - // Check that the expected upload decision is logged. + // Check that the expected developer engagement metric is logged. const ukm::Entry_Metric* metric = FindMetric( - internal::kUKMCardUploadDecisionMetricName, entry_proto.metrics()); + internal::kUKMDeveloperEngagementMetricName, entry_proto.metrics()); ASSERT_NE(nullptr, metric); - EXPECT_EQ(static_cast<int>(upload_decision), metric->value()); + EXPECT_EQ(static_cast<int>( + AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS), + metric->value()); + } + + void ExpectCardUploadDecisionUkm( + AutofillMetrics::CardUploadDecisionMetric upload_decision) { + ExpectMetric(internal::kUKMCardUploadDecisionMetricName, + internal::kUKMCardUploadDecisionEntryName, + static_cast<int>(upload_decision), + 1 /* expected_num_matching_entries */); + } + + void ExpectFillableFormParsedUkm(int num_fillable_forms_parsed) { + ExpectMetric(internal::kUKMDeveloperEngagementMetricName, + internal::kUKMDeveloperEngagementEntryName, + static_cast<int>( + AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS), + num_fillable_forms_parsed); + } + + void ExpectMetric(const char* metric_name, + const char* entry_name, + int metric_value, + int expected_num_matching_entries) { + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + + int num_matching_entries = 0; + for (size_t i = 0; i < ukm_service->entries_count(); ++i) { + const ukm::UkmEntry* entry = ukm_service->GetEntry(i); + + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + EXPECT_EQ(entry->source_id(), entry_proto.source_id()); + + // Check if there is an entry for |entry_name|. + if (entry_proto.event_hash() == base::HashMetricName(entry_name)) { + EXPECT_EQ(1, entry_proto.metrics_size()); + + // Check that the expected |metric_value| is logged. + const ukm::Entry_Metric* metric = + FindMetric(metric_name, entry_proto.metrics()); + ASSERT_NE(nullptr, metric); + EXPECT_EQ(metric_value, metric->value()); + ++num_matching_entries; + } + } + EXPECT_EQ(expected_num_matching_entries, num_matching_entries); } protected: - base::MessageLoop message_loop_; + base::test::ScopedTaskEnvironment scoped_task_environment_; MockAutofillClient autofill_client_; std::unique_ptr<MockAutofillDriver> autofill_driver_; std::unique_ptr<TestAutofillManager> autofill_manager_; @@ -1541,12 +1606,12 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestions_StopCharsOnly) { // field has stop characters in it and some input. TEST_F(AutofillManagerTest, GetCreditCardSuggestions_StopCharsWithInput) { // Add a credit card with particular numbers that we will attempt to recall. - std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>(); - test::SetCreditCardInfo(credit_card.get(), "John Smith", + CreditCard credit_card; + test::SetCreditCardInfo(&credit_card, "John Smith", "5255667890123123", // Mastercard "08", "2017"); - credit_card->set_guid("00000000-0000-0000-0000-000000000007"); - autofill_manager_->AddCreditCard(std::move(credit_card)); + credit_card.set_guid("00000000-0000-0000-0000-000000000007"); + autofill_manager_->AddCreditCard(credit_card); // Set up our form data. FormData form; @@ -1823,13 +1888,13 @@ TEST_F(AutofillManagerTest, TEST_F(AutofillManagerTest, GetCreditCardSuggestions_RepeatedObfuscatedNumber) { // Add a credit card with the same obfuscated number as Elvis's. // |credit_card| will be owned by the mock PersonalDataManager. - std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>(); - test::SetCreditCardInfo(credit_card.get(), "Elvis Presley", + CreditCard credit_card; + test::SetCreditCardInfo(&credit_card, "Elvis Presley", "5231567890123456", // Mastercard "05", "2999"); - credit_card->set_guid("00000000-0000-0000-0000-000000000007"); - credit_card->set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); - autofill_manager_->AddCreditCard(std::move(credit_card)); + credit_card.set_guid("00000000-0000-0000-0000-000000000007"); + credit_card.set_use_date(base::Time::Now() - base::TimeDelta::FromDays(15)); + autofill_manager_->AddCreditCard(credit_card); // Set up our form data. FormData form; @@ -3445,7 +3510,7 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions) { // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); autofill_manager_->AddSeenForm(base::WrapUnique(form_structure)); // Similarly, a second form. @@ -3465,7 +3530,7 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions) { form2.fields.push_back(field); TestFormStructure* form_structure2 = new TestFormStructure(form2); - form_structure2->DetermineHeuristicTypes(); + form_structure2->DetermineHeuristicTypes(nullptr /* ukm_service */); autofill_manager_->AddSeenForm(base::WrapUnique(form_structure2)); AutofillQueryResponseContents response; @@ -3518,7 +3583,7 @@ TEST_F(AutofillManagerTest, OnLoadedServerPredictions_ResetManager) { // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); autofill_manager_->AddSeenForm(base::WrapUnique(form_structure)); AutofillQueryResponseContents response; @@ -3556,7 +3621,7 @@ TEST_F(AutofillManagerTest, FormSubmittedServerTypes) { // Simulate having seen this form on page load. // |form_structure| will be owned by |autofill_manager_|. TestFormStructure* form_structure = new TestFormStructure(form); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); // Clear the heuristic types, and instead set the appropriate server types. std::vector<ServerFieldType> heuristic_types, server_types; @@ -4151,10 +4216,10 @@ TEST_F(AutofillManagerTest, RemoveProfile) { TEST_F(AutofillManagerTest, RemoveCreditCard) { // Add and remove an Autofill credit card. - std::unique_ptr<CreditCard> credit_card = base::MakeUnique<CreditCard>(); + CreditCard credit_card; const char guid[] = "00000000-0000-0000-0000-000000100007"; - credit_card->set_guid(guid); - autofill_manager_->AddCreditCard(std::move(credit_card)); + credit_card.set_guid(guid); + autofill_manager_->AddCreditCard(credit_card); int id = MakeFrontendID(guid, std::string()); @@ -4526,6 +4591,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); FormSubmitted(address_form); @@ -4533,6 +4600,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) { FormData credit_card_form; CreateTestCreditCardFormData(&credit_card_form, true, false); FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); @@ -4550,7 +4618,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard) { histogram_tester.ExpectUniqueSample("Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -4610,6 +4678,8 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { FormData address_form; test::CreateTestAddressFormData(&address_form); FormsSeen(std::vector<FormData>(1, address_form)); + ExpectUniqueFillableFormParsedUkm(); + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); FormSubmitted(address_form); @@ -4617,6 +4687,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { FormData credit_card_form; CreateTestCreditCardFormData(&credit_card_form, true, false); FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); // Edit the data, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); @@ -4637,7 +4708,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcUnavailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4693,7 +4764,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_CvcInvalidLength) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4771,7 +4842,146 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_MultipleCvcFields) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); +} + +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoCvcFieldOnForm \ + DISABLED_UploadCreditCard_NoCvcFieldOnForm +#else +#define MAYBE_UploadCreditCard_NoCvcFieldOnForm \ + UploadCreditCard_NoCvcFieldOnForm +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoCvcFieldOnForm) { + EnableAutofillUpstreamRequestCvcIfMissingExperimentAndUkmLogging(); + autofill_manager_->set_credit_card_upload_enabled(true); + + // Remove the profiles that were created in the TestPersonalDataManager + // constructor because they would result in conflicting names that would + // prevent the upload. + personal_data_.ClearAutofillProfiles(); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. Note that CVC field is missing. + FormData credit_card_form; + credit_card_form.name = ASCIIToUTF16("MyForm"); + credit_card_form.origin = GURL("https://myform.com/form.html"); + credit_card_form.action = GURL("https://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Card Name", "cardname", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field); + credit_card_form.fields.push_back(field); + + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16("11"); + credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + + base::HistogramTester histogram_tester; + + // Upload should still happen as long as the user provides CVC in the bubble. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); + FormSubmitted(credit_card_form); + EXPECT_TRUE(autofill_manager_->credit_card_was_uploaded()); + + // Verify that the correct histogram entry (and only that) was logged. + histogram_tester.ExpectUniqueSample("Autofill.CardUploadDecisionExpanded", + AutofillMetrics::UPLOAD_OFFERED_NO_CVC, + 1); + // Verify that the correct UKM was logged. + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED_NO_CVC); +} + +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoCvcFieldOnFormExperimentOff \ + DISABLED_UploadCreditCard_NoCvcFieldOnFormExperimentOff +#else +#define MAYBE_UploadCreditCard_NoCvcFieldOnFormExperimentOff \ + UploadCreditCard_NoCvcFieldOnFormExperimentOff +#endif +TEST_F(AutofillManagerTest, + MAYBE_UploadCreditCard_NoCvcFieldOnFormExperimentOff) { + EnableUkmLogging(); + autofill_manager_->set_credit_card_upload_enabled(true); + + // Remove the profiles that were created in the TestPersonalDataManager + // constructor because they would result in conflicting names that would + // prevent the upload. + personal_data_.ClearAutofillProfiles(); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. Note that CVC field is missing. + FormData credit_card_form; + credit_card_form.name = ASCIIToUTF16("MyForm"); + credit_card_form.origin = GURL("https://myform.com/form.html"); + credit_card_form.action = GURL("https://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Card Name", "cardname", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field); + credit_card_form.fields.push_back(field); + + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16("11"); + credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + + base::HistogramTester histogram_tester; + + // Neither a local save nor an upload should happen in this case. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); + + // Verify that the correct histogram entry (and only that) was logged. + histogram_tester.ExpectUniqueSample( + "Autofill.CardUploadDecisionExpanded", + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); + // Verify that the correct UKM was logged. + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + + rappor::TestRapporServiceImpl* rappor_service = + autofill_client_.test_rappor_service(); + EXPECT_EQ(1, rappor_service->GetReportsCount()); + std::string sample; + rappor::RapporType type; + EXPECT_TRUE(rappor_service->GetRecordedSampleForMetric( + "Autofill.CardUploadNotOfferedNoCvc", &sample, &type)); + EXPECT_EQ("myform.com", sample); + EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -4811,8 +5021,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoProfileAvailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ADDRESS); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4865,7 +5074,7 @@ TEST_F(AutofillManagerTest, "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4921,8 +5130,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoNameAvailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_NAME); rappor::TestRapporServiceImpl* rappor_service = autofill_client_.test_rappor_service(); @@ -4955,6 +5163,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { address_forms.push_back(address_form1); address_forms.push_back(address_form2); FormsSeen(address_forms); + ExpectFillableFormParsedUkm(2 /* num_fillable_forms_parsed */); ManuallyFillAddressForm("Flo", "Master", "77401-8294", "US", &address_form1); FormSubmitted(address_form1); @@ -4966,6 +5175,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { FormData credit_card_form; CreateTestCreditCardFormData(&credit_card_form, true, false); FormsSeen(std::vector<FormData>(1, credit_card_form)); + ExpectFillableFormParsedUkm(3 /* num_fillable_forms_parsed */); // Edit the data, but don't include a name, and submit. credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); @@ -4986,7 +5196,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesConflict) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm( + ExpectCardUploadDecisionUkm( AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_ZIPS); } @@ -5041,7 +5251,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_ZipCodesHavePrefixMatch) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -5094,8 +5304,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoZipCodeAvailable) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_ZIP_CODE); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -5152,7 +5361,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesMatchLoosely) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_OFFERED, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); } // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. @@ -5206,7 +5415,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NamesHaveToMatch) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm( + ExpectCardUploadDecisionUkm( AutofillMetrics::UPLOAD_NOT_OFFERED_CONFLICTING_NAMES); rappor::TestRapporServiceImpl* rappor_service = @@ -5267,7 +5476,7 @@ TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_UploadDetailsFails) { "Autofill.CardUploadDecisionExpanded", AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED, 1); // Verify that the correct UKM was logged. - ExpectUniqueCardUploadDecisionUkm( + ExpectCardUploadDecisionUkm( AutofillMetrics::UPLOAD_NOT_OFFERED_GET_UPLOAD_DETAILS_FAILED); } @@ -5617,11 +5826,11 @@ TEST_F(AutofillManagerTest, ShouldUploadForm) { EXPECT_TRUE(autofill_manager_->ShouldUploadForm(form_structure_4)); // Is off the record. - autofill_driver_->SetIsOffTheRecord(true); + autofill_driver_->SetIsIncognito(true); EXPECT_FALSE(autofill_manager_->ShouldUploadForm(form_structure_4)); // Make sure it's reset for the next test case. - autofill_driver_->SetIsOffTheRecord(false); + autofill_driver_->SetIsIncognito(false); EXPECT_TRUE(autofill_manager_->ShouldUploadForm(form_structure_4)); // Has one field which is a password field. @@ -5742,4 +5951,140 @@ TEST_F(AutofillManagerTest, NotifyDriverOfCreditCardInteraction) { } } +// Tests that a form with server only types is still autofillable if the form +// gets updated in cache. +TEST_F(AutofillManagerTest, DisplaySuggestionsForUpdatedServerTypedForm) { + // Create a form with unknown heuristic fields. + FormData form; + form.name = ASCIIToUTF16("MyForm"); + form.origin = GURL("http://myform.com/form.html"); + form.action = GURL("http://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Field 1", "field1", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Field 2", "field2", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Field 3", "field3", "", "text", &field); + form.fields.push_back(field); + + auto form_structure = base::MakeUnique<TestFormStructure>(form); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); + // Make sure the form can not be autofilled now. + ASSERT_EQ(0u, form_structure->autofill_count()); + for (size_t idx = 0; idx < form_structure->field_count(); ++idx) { + ASSERT_EQ(UNKNOWN_TYPE, form_structure->field(idx)->heuristic_type()); + } + + // Prepare and set known server fields. + const std::vector<ServerFieldType> heuristic_types(form.fields.size(), + UNKNOWN_TYPE); + const std::vector<ServerFieldType> server_types{NAME_FIRST, NAME_MIDDLE, + NAME_LAST}; + form_structure->SetFieldTypes(heuristic_types, server_types); + autofill_manager_->AddSeenForm(std::move(form_structure)); + + // Make sure the form can be autofilled. + for (const FormFieldData& field : form.fields) { + GetAutofillSuggestions(form, field); + ASSERT_TRUE(external_delegate_->on_suggestions_returned_seen()); + } + + // Modify one of the fields in the original form. + form.fields[0].css_classes += ASCIIToUTF16("a"); + + // Expect the form still can be autofilled. + for (const FormFieldData& field : form.fields) { + GetAutofillSuggestions(form, field); + EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); + } + + // Modify form action URL. This can happen on in-page navitaion if the form + // doesn't have an actual action (attribute is empty). + form.action = net::AppendQueryParameter(form.action, "arg", "value"); + + // Expect the form still can be autofilled. + for (const FormFieldData& field : form.fields) { + GetAutofillSuggestions(form, field); + EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); + } +} + +// Tests that a form with <select> field is accepted if <option> value (not +// content) is quite long. Some websites use value to propagate long JSON to +// JS-backed logic. +TEST_F(AutofillManagerTest, FormWithLongOptionValuesIsAcceptable) { + FormData form; + form.name = ASCIIToUTF16("MyForm"); + form.origin = GURL("http://myform.com/form.html"); + form.action = GURL("http://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("First name", "firstname", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Last name", "lastname", "", "text", &field); + form.fields.push_back(field); + + // Prepare <select> field with long <option> values. + const size_t kOptionValueLength = 10240; + const std::string long_string(kOptionValueLength, 'a'); + const std::vector<const char*> values(3, long_string.c_str()); + const std::vector<const char*> contents{"A", "B", "C"}; + test::CreateTestSelectField("Country", "country", "", values, contents, + values.size(), &field); + form.fields.push_back(field); + + FormsSeen({form}); + + // Suggestions should be displayed. + for (const FormFieldData& field : form.fields) { + GetAutofillSuggestions(form, field); + EXPECT_TRUE(external_delegate_->on_suggestions_returned_seen()); + } +} + +// Test that a sign-in form submission sends an upload with types matching the +// fields. +TEST_F(AutofillManagerTest, SignInFormSubmission_Upload) { + // Set up our form data (it's already filled out with user data). + FormData form; + form.origin = GURL("http://myform.com/form.html"); + form.action = GURL("http://myform.com/submit.html"); + + std::vector<ServerFieldTypeSet> expected_types; + ServerFieldTypeSet types; + + FormFieldData field; + test::CreateTestFormField("Email", "email", "theking@gmail.com", "text", + &field); + form.fields.push_back(field); + types.insert(EMAIL_ADDRESS); + expected_types.push_back(types); + + test::CreateTestFormField("Password", "pw", "secret", "password", &field); + form.fields.push_back(field); + types.clear(); + types.insert(PASSWORD); + expected_types.push_back(types); + + // We will expect these types in the upload and no observed submission. (the + // callback initiated by WaitForAsyncUploadProcess checks these expectations.) + autofill_manager_->set_expected_submitted_field_types(expected_types); + autofill_manager_->set_expected_observed_submission(true); + autofill_manager_->ResetRunLoop(); + + std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); + form_structure->set_is_signin_upload(true); + form_structure->field(1)->set_possible_types({autofill::PASSWORD}); + + std::string signature = form_structure->FormSignatureAsStr(); + autofill_manager_->StartUploadProcess(std::move(form_structure), + base::TimeTicks::Now(), true); + + // Wait for upload to complete (will check expected types as well). + autofill_manager_->WaitForAsyncUploadProcess(); + + EXPECT_EQ(signature, autofill_manager_->GetSubmittedFormSignature()); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc index 239334c5e66..77442395f20 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics.cc @@ -5,6 +5,8 @@ #include "components/autofill/core/browser/autofill_metrics.h" #include <algorithm> +#include <utility> +#include <vector> #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -12,6 +14,7 @@ #include "base/metrics/user_metrics.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_experiments.h" +#include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/form_data.h" @@ -20,6 +23,30 @@ namespace internal { const char kUKMCardUploadDecisionEntryName[] = "Autofill.CardUploadDecision"; const char kUKMCardUploadDecisionMetricName[] = "UploadDecision"; +const char kUKMDeveloperEngagementEntryName[] = "Autofill.DeveloperEngagement"; +const char kUKMDeveloperEngagementMetricName[] = "DeveloperEngagement"; +const char kUKMMillisecondsSinceFormLoadedMetricName[] = + "MillisecondsSinceFormLoaded"; +const char kUKMInteractedWithFormEntryName[] = "Autofill.InteractedWithForm"; +const char kUKMIsForCreditCardMetricName[] = "IsForCreditCard"; +const char kUKMLocalRecordTypeCountMetricName[] = "LocalRecordTypeCount"; +const char kUKMServerRecordTypeCountMetricName[] = "ServerRecordTypeCount"; +const char kUKMSuggestionsShownEntryName[] = "Autofill.SuggestionsShown"; +const char kUKMSelectedMaskedServerCardEntryName[] = + "Autofill.SelectedMaskedServerCard"; +const char kUKMSuggestionFilledEntryName[] = "Autofill.SuggestionFilled"; +const char kUKMRecordTypeMetricName[] = "RecordType"; +const char kUKMTextFieldDidChangeEntryName[] = "Autofill.TextFieldDidChange"; +const char kUKMFieldTypeGroupMetricName[] = "FieldTypeGroup"; +const char kUKMHeuristicTypeMetricName[] = "HeuristicType"; +const char kUKMServerTypeMetricName[] = "ServerType"; +const char kUKMHtmlFieldTypeMetricName[] = "HtmlFieldType"; +const char kUKMHtmlFieldModeMetricName[] = "HtmlFieldMode"; +const char kUKMIsAutofilledMetricName[] = "IsAutofilled"; +const char kUKMIsEmptyMetricName[] = "IsEmpty"; +const char kUKMFormSubmittedEntryName[] = "Autofill.AutofillFormSubmitted"; +const char kUKMAutofillFormSubmittedStateMetricName[] = + "AutofillFormSubmittedState"; } // namespace internal namespace autofill { @@ -614,7 +641,8 @@ void AutofillMetrics::LogProfileActionOnFormSubmitted( // static void AutofillMetrics::LogAutofillFormSubmittedState( - AutofillFormSubmittedState state) { + AutofillFormSubmittedState state, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { UMA_HISTOGRAM_ENUMERATION("Autofill.FormSubmittedState", state, AUTOFILL_FORM_SUBMITTED_STATE_ENUM_SIZE); @@ -648,6 +676,7 @@ void AutofillMetrics::LogAutofillFormSubmittedState( NOTREACHED(); break; } + form_interactions_ukm_logger->LogFormSubmitted(state); } // static @@ -704,18 +733,32 @@ void AutofillMetrics::LogCardUploadDecisionUkm( if (upload_decision >= AutofillMetrics::NUM_CARD_UPLOAD_DECISION_METRICS) return; - // Set up as a map because the follow-up CL will add more metrics. - std::map<std::string, int> metrics = { + const std::vector<std::pair<const char*, int>> metrics = { {internal::kUKMCardUploadDecisionMetricName, static_cast<int>(upload_decision)}}; LogUkm(ukm_service, url, internal::kUKMCardUploadDecisionEntryName, metrics); } // static -bool AutofillMetrics::LogUkm(ukm::UkmService* ukm_service, - const GURL& url, - const std::string& ukm_entry_name, - const std::map<std::string, int>& metrics) { +void AutofillMetrics::LogDeveloperEngagementUkm( + ukm::UkmService* ukm_service, + const GURL& url, + std::vector<AutofillMetrics::DeveloperEngagementMetric> metrics) { + std::vector<std::pair<const char*, int>> form_structure_metrics; + for (const auto it : metrics) + form_structure_metrics.push_back( + {internal::kUKMDeveloperEngagementMetricName, static_cast<int>(it)}); + + LogUkm(ukm_service, url, internal::kUKMDeveloperEngagementEntryName, + form_structure_metrics); +} + +// static +bool AutofillMetrics::LogUkm( + ukm::UkmService* ukm_service, + const GURL& url, + const std::string& ukm_entry_name, + const std::vector<std::pair<const char*, int>>& metrics) { if (!IsUkmLoggingEnabled() || !ukm_service || !url.is_valid() || metrics.empty()) { return false; @@ -727,16 +770,18 @@ bool AutofillMetrics::LogUkm(ukm::UkmService* ukm_service, ukm_service->GetEntryBuilder(source_id, ukm_entry_name.c_str()); for (auto it = metrics.begin(); it != metrics.end(); ++it) { - builder->AddMetric(it->first.c_str(), it->second); + builder->AddMetric(it->first, it->second); } return true; } -AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card) +AutofillMetrics::FormEventLogger::FormEventLogger( + bool is_for_credit_card, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) : is_for_credit_card_(is_for_credit_card), - is_server_data_available_(false), - is_local_data_available_(false), + server_record_type_count_(0), + local_record_type_count_(0), is_context_secure_(false), has_logged_interacted_(false), has_logged_suggestions_shown_(false), @@ -745,11 +790,15 @@ AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card) has_logged_will_submit_(false), has_logged_submitted_(false), logged_suggestion_filled_was_server_data_(false), - logged_suggestion_filled_was_masked_server_card_(false) {} + logged_suggestion_filled_was_masked_server_card_(false), + form_interactions_ukm_logger_(form_interactions_ukm_logger) {} void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() { if (!has_logged_interacted_) { has_logged_interacted_ = true; + form_interactions_ukm_logger_->LogInteractedWithForm( + is_for_credit_card_, local_record_type_count_, + server_record_type_count_); Log(AutofillMetrics::FORM_EVENT_INTERACTED_ONCE); } } @@ -774,6 +823,8 @@ void AutofillMetrics::FormEventLogger::OnDidPollSuggestions( } void AutofillMetrics::FormEventLogger::OnDidShowSuggestions() { + form_interactions_ukm_logger_->LogSuggestionsShown(); + Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN); if (!has_logged_suggestions_shown_) { has_logged_suggestions_shown_ = true; @@ -791,17 +842,22 @@ void AutofillMetrics::FormEventLogger::OnDidShowSuggestions() { void AutofillMetrics::FormEventLogger::OnDidSelectMaskedServerCardSuggestion() { DCHECK(is_for_credit_card_); + form_interactions_ukm_logger_->LogSelectedMaskedServerCard(); + Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED); if (!has_logged_masked_server_card_suggestion_selected_) { has_logged_masked_server_card_suggestion_selected_ = true; - Log(AutofillMetrics - ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE); + Log(AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE); } } void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( const CreditCard& credit_card) { DCHECK(is_for_credit_card_); + form_interactions_ukm_logger_->LogDidFillSuggestion( + static_cast<int>(credit_card.record_type())); + if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED); else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) @@ -817,8 +873,8 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( logged_suggestion_filled_was_masked_server_card_ = credit_card.record_type() == CreditCard::MASKED_SERVER_CARD; if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) { - Log(AutofillMetrics - ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE); + Log(AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE); } else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) { Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE); } else { @@ -833,6 +889,9 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( const AutofillProfile& profile) { DCHECK(!is_for_credit_card_); + form_interactions_ukm_logger_->LogDidFillSuggestion( + static_cast<int>(profile.record_type())); + if (profile.record_type() == AutofillProfile::SERVER_PROFILE) Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED); else @@ -843,8 +902,8 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( logged_suggestion_filled_was_server_data_ = profile.record_type() == AutofillProfile::SERVER_PROFILE; Log(profile.record_type() == AutofillProfile::SERVER_PROFILE - ? AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE - : AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE); + ? AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE + : AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE); } base::RecordAction( @@ -892,8 +951,8 @@ void AutofillMetrics::FormEventLogger::OnFormSubmitted() { if (!has_logged_suggestion_filled_) { Log(AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE); } else if (logged_suggestion_filled_was_masked_server_card_) { - Log(AutofillMetrics - ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE); + Log(AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE); } else if (logged_suggestion_filled_was_server_data_) { Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE); } else { @@ -925,15 +984,154 @@ void AutofillMetrics::FormEventLogger::Log(FormEvent event) const { // Logging again in a different histogram for segmentation purposes. // TODO(waltercacau): Re-evaluate if we still need such fine grained // segmentation. http://crbug.com/454018 - if (!is_server_data_available_ && !is_local_data_available_) + if (server_record_type_count_ == 0 && local_record_type_count_ == 0) name += ".WithNoData"; - else if (is_server_data_available_ && !is_local_data_available_) + else if (server_record_type_count_ > 0 && local_record_type_count_ == 0) name += ".WithOnlyServerData"; - else if (!is_server_data_available_ && is_local_data_available_) + else if (server_record_type_count_ == 0 && local_record_type_count_ > 0) name += ".WithOnlyLocalData"; else name += ".WithBothServerAndLocalData"; LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS); } +AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger( + ukm::UkmService* ukm_service) + : ukm_service_(ukm_service) {} + +void AutofillMetrics::FormInteractionsUkmLogger::OnFormsLoaded( + const GURL& url) { + if (!IsUkmLoggingEnabled() || ukm_service_ == nullptr) + return; + + url_ = url; + form_loaded_timestamp_ = base::TimeTicks::Now(); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogInteractedWithForm( + bool is_for_credit_card, + size_t local_record_type_count, + size_t server_record_type_count) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMInteractedWithFormEntryName); + builder->AddMetric(internal::kUKMIsForCreditCardMetricName, + is_for_credit_card); + builder->AddMetric(internal::kUKMLocalRecordTypeCountMetricName, + local_record_type_count); + builder->AddMetric(internal::kUKMServerRecordTypeCountMetricName, + server_record_type_count); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogSuggestionsShown() { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMSuggestionsShownEntryName); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogSelectedMaskedServerCard() { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMSelectedMaskedServerCardEntryName); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogDidFillSuggestion( + int record_type) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMSuggestionFilledEntryName); + builder->AddMetric(internal::kUKMRecordTypeMetricName, record_type); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange( + const AutofillField& field) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMTextFieldDidChangeEntryName); + builder->AddMetric(internal::kUKMFieldTypeGroupMetricName, + static_cast<int>(field.Type().group())); + builder->AddMetric(internal::kUKMHeuristicTypeMetricName, + static_cast<int>(field.heuristic_type())); + builder->AddMetric(internal::kUKMServerTypeMetricName, + static_cast<int>(field.server_type())); + builder->AddMetric(internal::kUKMHtmlFieldTypeMetricName, + static_cast<int>(field.html_type())); + builder->AddMetric(internal::kUKMHtmlFieldModeMetricName, + static_cast<int>(field.html_mode())); + builder->AddMetric(internal::kUKMIsAutofilledMetricName, field.is_autofilled); + builder->AddMetric(internal::kUKMIsEmptyMetricName, field.IsEmpty()); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted( + AutofillFormSubmittedState state) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMFormSubmittedEntryName); + builder->AddMetric(internal::kUKMAutofillFormSubmittedStateMetricName, + static_cast<int>(state)); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::UpdateSourceURL( + const GURL& url) { + url_ = url; + if (CanLog()) + ukm_service_->UpdateSourceURL(source_id_, url_); +} + +bool AutofillMetrics::FormInteractionsUkmLogger::CanLog() const { + return IsUkmLoggingEnabled() && ukm_service_ && url_.is_valid(); +} + +int64_t +AutofillMetrics::FormInteractionsUkmLogger::MillisecondsSinceFormLoaded() + const { + DCHECK(!form_loaded_timestamp_.is_null()); + return (base::TimeTicks::Now() - form_loaded_timestamp_).InMilliseconds(); +} + +void AutofillMetrics::FormInteractionsUkmLogger::GetNewSourceID() { + source_id_ = ukm_service_->GetNewSourceID(); + ukm_service_->UpdateSourceURL(source_id_, url_); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h index d0aba8221f6..58ab8d26aa0 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics.h +++ b/chromium/components/autofill/core/browser/autofill_metrics.h @@ -7,18 +7,17 @@ #include <stddef.h> #include <string> +#include <utility> +#include <vector> #include "base/macros.h" +#include "base/time/time.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/common/form_field_data.h" -namespace base { -class TimeDelta; -} // namespace base - namespace ukm { class UkmService; } // namespace ukm @@ -27,10 +26,55 @@ namespace internal { // Name constants are exposed here so they can be referenced from tests. extern const char kUKMCardUploadDecisionEntryName[]; extern const char kUKMCardUploadDecisionMetricName[]; +extern const char kUKMDeveloperEngagementEntryName[]; +extern const char kUKMDeveloperEngagementMetricName[]; + +// Each form interaction event has a separate |UkmEntry|. + +// The first form event |UkmEntry| contains metrics for metadata that apply +// to all subsequent events. +extern const char kUKMInteractedWithFormEntryName[]; +extern const char kUKMIsForCreditCardMetricName[]; +extern const char kUKMLocalRecordTypeCountMetricName[]; +extern const char kUKMServerRecordTypeCountMetricName[]; + +// |UkmEntry| when we show suggestions. +extern const char kUKMSuggestionsShownEntryName[]; + +// |UkmEntry| when user selects a masked server credit card. +extern const char kUKMSelectedMaskedServerCardEntryName[]; + +// Each |UkmEntry|, except the first interaction with the form, has a metric for +// time elapsed, in milliseconds, since we loaded the form. +extern const char kUKMMillisecondsSinceFormLoadedMetricName[]; + +// |FormEvent| for FORM_EVENT_*_SUGGESTION_FILLED in credit card forms include a +// |CreditCard| |record_type()| to indicate if the suggestion was for a local +// card, masked server card or full server card. Similarly, address/profile +// forms include a |AutofillProfile| |record_type()| to indicate if the +// profile was a local profile or server profile. +extern const char kUKMSuggestionFilledEntryName[]; +extern const char kUKMRecordTypeMetricName[]; + +// |UkmEntry| for user editing text field. Metrics contain field's attributes. +extern const char kUKMTextFieldDidChangeEntryName[]; +extern const char kUKMFieldTypeGroupMetricName[]; +extern const char kUKMHeuristicTypeMetricName[]; +extern const char kUKMServerTypeMetricName[]; +extern const char kUKMHtmlFieldTypeMetricName[]; +extern const char kUKMHtmlFieldModeMetricName[]; +extern const char kUKMIsAutofilledMetricName[]; +extern const char kUKMIsEmptyMetricName[]; + +// |UkmEntry| for |AutofillFormSubmittedState|. +extern const char kUKMFormSubmittedEntryName[]; +extern const char kUKMAutofillFormSubmittedStateMetricName[]; } // namespace internal namespace autofill { +class AutofillField; + class AutofillMetrics { public: enum AutofillProfileAction { @@ -80,16 +124,23 @@ class AutofillMetrics { // were otherwise valid nor whether we would have been able to get upload // details. UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, + // No CVC was detected, but valid addresses and names were. Upload is still + // possible if the user manually enters CVC, so upload was offered. + UPLOAD_OFFERED_NO_CVC, NUM_CARD_UPLOAD_DECISION_METRICS, }; enum DeveloperEngagementMetric { - // Parsed a form that is potentially autofillable. - FILLABLE_FORM_PARSED = 0, + // Parsed a form that is potentially autofillable and does not contain any + // web developer-specified field type hint. + FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS = 0, // Parsed a form that is potentially autofillable and contains at least one // web developer-specified field type hint, a la // http://is.gd/whatwg_autocomplete - FILLABLE_FORM_CONTAINS_TYPE_HINTS, + FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, + // Parsed a form that is potentially autofillable and contains at least one + // UPI Virtual Payment Address hint (upi-vpa) + FORM_CONTAINS_UPI_VPA_HINT, NUM_DEVELOPER_ENGAGEMENT_METRICS, }; @@ -256,6 +307,7 @@ class AutofillMetrics { SAVE_CARD_PROMPT_DISMISS_CLICK_LEARN_MORE, // The prompt was dismissed because the user clicked a legal message link. SAVE_CARD_PROMPT_DISMISS_CLICK_LEGAL_MESSAGE, + NUM_SAVE_CARD_PROMPT_METRICS, }; @@ -264,9 +316,27 @@ class AutofillMetrics { // the heuristic prediction, for the crowd-sourced prediction, and for the // overall prediction. enum FieldTypeQualityMetric { - TYPE_UNKNOWN = 0, // Offered no prediction. - TYPE_MATCH, // Predicted correctly. - TYPE_MISMATCH, // Predicted incorrectly. + // The field was found to be of type T, but autofill made no prediction. + TYPE_UNKNOWN = 0, + // The field was found to be of type T, which matches the predicted type. + TYPE_MATCH, + // The field was found to be of type T, autofill predicted some other type. + TYPE_MISMATCH, + // The field was left empty and autofil predicted that the field type would + // be UNKNOWN. + TYPE_MATCH_EMPTY, + // The field was populated with data that did not match any part of the + // user's profile (it's type could not be determined). Autofill predicted + // the field's type would be UNKNOWN. + TYPE_MATCH_UNKNOWN, + // The field was left empty, autofill predicted the user would populate it + // with autofillable data. + TYPE_MISMATCH_EMPTY, + // The field was populated with data that did not match any part of the + // user's profile (it's type could not be determined). Autofill predicted + // the user would populate it with autofillable data. + TYPE_MISMATCH_UNKNOWN, + // This must be the last value. NUM_FIELD_TYPE_QUALITY_METRICS, }; @@ -345,6 +415,10 @@ class AutofillMetrics { USER_DID_EDIT_AUTOFILLED_FIELD, // Same as above, but only logged once per page load. USER_DID_EDIT_AUTOFILLED_FIELD_ONCE, + + // User entered form data that appears to be a UPI Virtual Payment Address. + USER_DID_ENTER_UPI_VPA, + NUM_USER_HAPPINESS_METRICS, }; @@ -537,6 +611,39 @@ class AutofillMetrics { NUM_CONVERTED_ADDRESS_CONVERSION_TYPES }; + // Utility to log URL keyed form interaction events. + class FormInteractionsUkmLogger { + public: + explicit FormInteractionsUkmLogger(ukm::UkmService* ukm_service); + + const GURL& url() const { return url_; } + + void OnFormsLoaded(const GURL& url); + void LogInteractedWithForm(bool is_for_credit_card, + size_t local_record_type_count, + size_t server_record_type_count); + void LogSuggestionsShown(); + void LogSelectedMaskedServerCard(); + void LogDidFillSuggestion(int record_type); + void LogTextFieldDidChange(const AutofillField& field); + void LogFormSubmitted(AutofillFormSubmittedState state); + + // We initialize |url_| with the form's URL when we log the first form + // interaction. Later, we may update |url_| with the |source_url()| for the + // submitted form. + void UpdateSourceURL(const GURL& url); + + private: + bool CanLog() const; + int64_t MillisecondsSinceFormLoaded() const; + void GetNewSourceID(); + + ukm::UkmService* ukm_service_; // Weak reference. + int32_t source_id_ = -1; + GURL url_; + base::TimeTicks form_loaded_timestamp_; + }; + static void LogCardUploadDecisionMetric(CardUploadDecisionMetric metric); static void LogCreditCardInfoBarMetric(InfoBarMetric metric, bool is_uploading); @@ -663,7 +770,9 @@ class AutofillMetrics { // This should be called at each form submission to indicate the autofilled // state of the form. - static void LogAutofillFormSubmittedState(AutofillFormSubmittedState state); + static void LogAutofillFormSubmittedState( + AutofillFormSubmittedState state, + FormInteractionsUkmLogger* form_interactions_ukm_logger); // This should be called when determining the heuristic types for a form's // fields. @@ -697,25 +806,33 @@ class AutofillMetrics { const GURL& url, AutofillMetrics::CardUploadDecisionMetric upload_decision); + // Logs the developer engagement ukm for the specified |url| and autofill + // fields in the form structure. + static void LogDeveloperEngagementUkm( + ukm::UkmService* ukm_service, + const GURL& url, + std::vector<AutofillMetrics::DeveloperEngagementMetric> metrics); + // Logs the the |ukm_entry_name| with the specified |url| and the specified // |metrics|. Returns whether the ukm was sucessfully logged. static bool LogUkm(ukm::UkmService* ukm_service, const GURL& url, const std::string& ukm_entry_name, - const std::map<std::string, int>& metrics); + const std::vector<std::pair<const char*, int>>& metrics); - // Utility to autofill form events in the relevant histograms depending on + // Utility to log autofill form events in the relevant histograms depending on // the presence of server and/or local data. class FormEventLogger { public: - FormEventLogger(bool is_for_credit_card); + FormEventLogger(bool is_for_credit_card, + FormInteractionsUkmLogger* form_interactions_ukm_logger); - inline void set_is_server_data_available(bool is_server_data_available) { - is_server_data_available_ = is_server_data_available; + inline void set_server_record_type_count(size_t server_record_type_count) { + server_record_type_count_ = server_record_type_count; } - inline void set_is_local_data_available(bool is_local_data_available) { - is_local_data_available_ = is_local_data_available; + inline void set_local_record_type_count(size_t local_record_type_count) { + local_record_type_count_ = local_record_type_count; } inline void set_is_context_secure(bool is_context_secure) { @@ -744,8 +861,8 @@ class AutofillMetrics { void Log(FormEvent event) const; bool is_for_credit_card_; - bool is_server_data_available_; - bool is_local_data_available_; + size_t server_record_type_count_; + size_t local_record_type_count_; bool is_context_secure_; bool has_logged_interacted_; bool has_logged_suggestions_shown_; @@ -758,6 +875,9 @@ class AutofillMetrics { // The last field that was polled for suggestions. FormFieldData last_polled_field_; + + FormInteractionsUkmLogger* + form_interactions_ukm_logger_; // Weak reference. }; private: diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc index 36039f5f382..3372ee37884 100644 --- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc @@ -6,8 +6,8 @@ #include <stddef.h> -#include <map> #include <memory> +#include <utility> #include <vector> #include "base/feature_list.h" @@ -19,6 +19,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "base/test/scoped_task_environment.h" #include "base/test/user_action_tester.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_experiments.h" @@ -54,6 +55,8 @@ using base::Bucket; using base::TimeTicks; using rappor::TestRapporServiceImpl; using ::testing::ElementsAre; +using ::testing::Matcher; +using ::testing::UnorderedPointwise; namespace autofill { namespace { @@ -263,6 +266,8 @@ class TestAutofillManager : public AutofillManager { base::MakeUnique<TestFormStructure>(empty_form); form_structure->SetFieldTypes(heuristic_types, server_types); form_structures()->push_back(std::move(form_structure)); + + form_interactions_ukm_logger()->OnFormsLoaded(form.origin); } // Calls AutofillManager::OnWillSubmitForm and waits for it to complete. @@ -287,9 +292,9 @@ class TestAutofillManager : public AutofillManager { void RunRunLoop() { run_loop_->Run(); } void UploadFormDataAsyncCallback(const FormStructure* submitted_form, - const base::TimeTicks& load_time, - const base::TimeTicks& interaction_time, - const base::TimeTicks& submission_time, + const TimeTicks& load_time, + const TimeTicks& interaction_time, + const TimeTicks& submission_time, bool observed_submission) override { run_loop_->Quit(); @@ -316,6 +321,83 @@ const ukm::Entry_Metric* FindMetric( return nullptr; } +MATCHER(CompareMetrics, "") { + const ukm::Entry_Metric& lhs = ::testing::get<0>(arg); + const std::pair<const char*, int64_t>& rhs = ::testing::get<1>(arg); + return lhs.metric_hash() == base::HashMetricName(rhs.first) && + lhs.value() == rhs.second; +} + +void VerifyDeveloperEngagementUkm( + const FormData& form, + const ukm::TestUkmService* ukm_service, + const std::vector<int64_t>& expected_metric_values) { + const ukm::UkmEntry* entry = ukm_service->GetEntryForEntryName( + internal::kUKMDeveloperEngagementEntryName); + ASSERT_NE(nullptr, entry); + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + + const ukm::UkmSource* source = + ukm_service->GetSourceForSourceId(entry_proto.source_id()); + ASSERT_NE(nullptr, source); + EXPECT_EQ(form.origin, source->url()); + + std::vector<std::pair<const char*, int64_t>> expected_metrics; + for (const auto it : expected_metric_values) + expected_metrics.push_back( + {internal::kUKMDeveloperEngagementMetricName, it}); + + EXPECT_THAT(entry_proto.metrics(), + UnorderedPointwise(CompareMetrics(), expected_metrics)); +} + +MATCHER(CompareMetricsIgnoringMillisecondsSinceFormLoaded, "") { + const ukm::Entry_Metric& lhs = ::testing::get<0>(arg); + const std::pair<const char*, int64_t>& rhs = ::testing::get<1>(arg); + return lhs.metric_hash() == base::HashMetricName(rhs.first) && + (lhs.value() == rhs.second || + (lhs.value() > 0 && + rhs.first == internal::kUKMMillisecondsSinceFormLoadedMetricName)); +} + +void VerifyFormInteractionUkm( + const FormData& form, + const ukm::TestUkmService* ukm_service, + const char* event_name, + const std::vector<std::vector<std::pair<const char*, int64_t>>>& + expected_metrics) { + size_t expected_metrics_index = 0; + for (size_t i = 0; i < ukm_service->entries_count(); ++i) { + const ukm::UkmEntry* entry = ukm_service->GetEntry(i); + if (entry->event_hash() != base::HashMetricName(event_name)) + continue; + + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + + const ukm::UkmSource* source = + ukm_service->GetSourceForSourceId(entry_proto.source_id()); + ASSERT_NE(nullptr, source); + EXPECT_EQ(form.origin, source->url()); + + ASSERT_LT(expected_metrics_index, expected_metrics.size()); + EXPECT_THAT( + entry_proto.metrics(), + UnorderedPointwise(CompareMetricsIgnoringMillisecondsSinceFormLoaded(), + expected_metrics[expected_metrics_index++])); + } +} + +void VerifySubmitFormUkm(const FormData& form, + const ukm::TestUkmService* ukm_service, + AutofillMetrics::AutofillFormSubmittedState state) { + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMFormSubmittedEntryName, + {{{internal::kUKMAutofillFormSubmittedStateMetricName, state}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); +} + } // namespace // This is defined in the autofill_metrics.cc implementation file. @@ -333,7 +415,7 @@ class AutofillMetricsTest : public testing::Test { void EnableWalletSync(); void EnableUkmLogging(); - base::MessageLoop message_loop_; + base::test::ScopedTaskEnvironment scoped_task_environment_; TestAutofillClient autofill_client_; std::unique_ptr<AccountTrackerService> account_tracker_; std::unique_ptr<FakeSigninManagerBase> signin_manager_; @@ -393,6 +475,7 @@ void AutofillMetricsTest::TearDown() { account_tracker_.reset(); signin_client_.reset(); test::ReenableSystemServices(); + autofill_client_.GetTestUkmService()->Purge(); } void AutofillMetricsTest::EnableWalletSync() { @@ -542,6 +625,129 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { GetFieldTypeGroupMetric(NAME_FULL, AutofillMetrics::TYPE_MISMATCH), 1); } +// Tests the true negatives (empty + no prediction and unknown + no prediction) +// and false positives (empty + bad prediction and unknown + bad prediction) +// are counted correctly. + +struct UnrecognizedOrEmptyFieldsCase { + const ServerFieldType actual_field_type; + const bool make_prediction; + const AutofillMetrics::FieldTypeQualityMetric metric_to_test; +}; + +class UnrecognizedOrEmptyFieldsTest + : public AutofillMetricsTest, + public testing::WithParamInterface<UnrecognizedOrEmptyFieldsCase> {}; + +TEST_P(UnrecognizedOrEmptyFieldsTest, QualityMetrics) { + // Setup the test parameters. + const ServerFieldType actual_field_type = GetParam().actual_field_type; + const ServerFieldType heuristic_type = + GetParam().make_prediction ? EMAIL_ADDRESS : UNKNOWN_TYPE; + const ServerFieldType server_type = + GetParam().make_prediction ? EMAIL_ADDRESS : NO_SERVER_DATA; + const AutofillMetrics::FieldTypeQualityMetric metric_to_test = + GetParam().metric_to_test; + + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + std::vector<ServerFieldType> heuristic_types, server_types; + AutofillField field; + + // Add a first name field, that is predicted correctly. + test::CreateTestFormField("first", "first", "Elvis", "text", &field); + field.set_possible_types({NAME_FIRST}); + form.fields.push_back(field); + heuristic_types.push_back(NAME_FIRST); + server_types.push_back(NAME_FIRST); + + // Add a last name field, that is predicted correctly. + test::CreateTestFormField("last", "last", "Presley", "test", &field); + field.set_possible_types({NAME_LAST}); + form.fields.push_back(field); + heuristic_types.push_back(NAME_LAST); + server_types.push_back(NAME_LAST); + + // Add an empty or unknown field, that is predicted as per the test params. + test::CreateTestFormField("Unknown", "Unknown", + (actual_field_type == EMPTY_TYPE ? "" : "unknown"), + "text", &field); + field.set_possible_types({actual_field_type}); + form.fields.push_back(field); + heuristic_types.push_back(heuristic_type); + server_types.push_back(server_type); + + // Simulate having seen this form on page load. + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + + // Run the form submission code while tracking the histograms. + base::HistogramTester histogram_tester; + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + + // Validate the histogram counter values. + for (int i = 0; i < AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS; ++i) { + // The metric enum value we're currently examining. + auto metric = static_cast<AutofillMetrics::FieldTypeQualityMetric>(i); + + // For the overall metric counts... + // If the current metric is the metric we're testing, then we expect its + // count to be 1. Otherwise, the metric's count should be zero (0) except + // for the TYPE_MATCH metric which should be 2 (because of the matching + // first and last name fields) + int overall_expected_count = + (metric == metric_to_test) + ? 1 + : ((metric == AutofillMetrics::TYPE_MATCH) ? 2 : 0); + + histogram_tester.ExpectBucketCount("Autofill.Quality.HeuristicType", metric, + overall_expected_count); + histogram_tester.ExpectBucketCount("Autofill.Quality.ServerType", metric, + overall_expected_count); + histogram_tester.ExpectBucketCount("Autofill.Quality.PredictedType", metric, + overall_expected_count); + + // For the ByFieldType metric counts... + // We only examine the counter for the field_type being tested. If the + // current metric is the metric we're testing, then we expect its + // count to be 1 otherwise it should be 0. + int field_type_expected_count = (metric == metric_to_test) ? 1 : 0; + + histogram_tester.ExpectBucketCount( + "Autofill.Quality.HeuristicType.ByFieldType", + GetFieldTypeGroupMetric(actual_field_type, metric), + field_type_expected_count); + histogram_tester.ExpectBucketCount( + "Autofill.Quality.ServerType.ByFieldType", + GetFieldTypeGroupMetric(actual_field_type, metric), + field_type_expected_count); + histogram_tester.ExpectBucketCount( + "Autofill.Quality.PredictedType.ByFieldType", + GetFieldTypeGroupMetric(actual_field_type, metric), + field_type_expected_count); + } +} + +INSTANTIATE_TEST_CASE_P( + AutofillMetricsTest, + UnrecognizedOrEmptyFieldsTest, + testing::Values( + UnrecognizedOrEmptyFieldsCase{EMPTY_TYPE, + /* make_prediction = */ false, + AutofillMetrics::TYPE_MATCH_EMPTY}, + UnrecognizedOrEmptyFieldsCase{UNKNOWN_TYPE, + /* make_prediction = */ false, + AutofillMetrics::TYPE_MATCH_UNKNOWN}, + UnrecognizedOrEmptyFieldsCase{EMPTY_TYPE, + /* make_prediction = */ true, + AutofillMetrics::TYPE_MISMATCH_EMPTY}, + UnrecognizedOrEmptyFieldsCase{UNKNOWN_TYPE, + /* make_prediction = */ true, + AutofillMetrics::TYPE_MISMATCH_UNKNOWN})); + // Ensures that metrics that measure timing some important Autofill functions // actually are recorded and retrieved. TEST_F(AutofillMetricsTest, TimingMetrics) { @@ -570,7 +776,7 @@ TEST_F(AutofillMetricsTest, TimingMetrics) { // Simulate a OnFormsSeen() call that should trigger the recording. std::vector<FormData> forms; forms.push_back(form); - autofill_manager_->OnFormsSeen(forms, base::TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); // Because these metrics are related to timing, it is not possible to know in // advance which bucket the sample will fall into, so we just need to make @@ -766,7 +972,7 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { std::unique_ptr<TestFormStructure> form_structure = base::MakeUnique<TestFormStructure>(form); TestFormStructure* form_structure_ptr = form_structure.get(); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); autofill_manager_->form_structures()->push_back(std::move(form_structure)); AutofillQueryResponseContents response; @@ -851,6 +1057,47 @@ TEST_F(AutofillMetricsTest, QualityMetrics_BasedOnAutocomplete) { GetFieldTypeGroupMetric(NAME_MIDDLE, AutofillMetrics::TYPE_MISMATCH), 1); } +// Test that we log UPI Virtual Payment Address. +TEST_F(AutofillMetricsTest, UpiVirtualPaymentAddress) { + // Set up our form data. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + std::vector<ServerFieldType> heuristic_types, server_types; + FormFieldData field; + + // Heuristic value will match with Autocomplete attribute. + test::CreateTestFormField("Last Name", "lastname", "", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(NAME_LAST); + server_types.push_back(NAME_LAST); + + // Heuristic value will NOT match with Autocomplete attribute. + test::CreateTestFormField("First Name", "firstname", "", "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(NAME_FIRST); + server_types.push_back(NAME_FIRST); + + // Heuristic value will NOT match with Autocomplete attribute. + test::CreateTestFormField("Payment Address", "payment_address", "user@upi", + "text", &field); + form.fields.push_back(field); + heuristic_types.push_back(UNKNOWN_TYPE); + server_types.push_back(NO_SERVER_DATA); + + // Simulate having seen this form on page load. + autofill_manager_->AddSeenForm(form, heuristic_types, server_types); + + // Simulate form submission. + base::HistogramTester histogram_tester; + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + + histogram_tester.ExpectBucketCount( + "Autofill.UserHappiness", AutofillMetrics::USER_DID_ENTER_UPI_VPA, 1); +} + // Test that we do not log RAPPOR metrics when the number of mismatches is not // high enough. TEST_F(AutofillMetricsTest, Rappor_LowMismatchRate_NoMetricsReported) { @@ -1380,6 +1627,11 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields) { // fields is logged. histogram_tester.ExpectUniqueSample( "Autofill.NumberOfEditedAutofilledFieldsAtSubmission", 2, 1); + + // UKM must not be logged unless enabled. + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); } // Verify that when resetting the autofill manager (such as during a @@ -1434,6 +1686,8 @@ TEST_F(AutofillMetricsTest, NumberOfEditedAutofilledFields_NoSubmission) { // Verify that we correctly log metrics regarding developer engagement. TEST_F(AutofillMetricsTest, DeveloperEngagement) { + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Start with a non-fillable form. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -1454,21 +1708,28 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { autofill_manager_->OnFormsSeen(forms, TimeTicks()); autofill_manager_->Reset(); histogram_tester.ExpectTotalCount("Autofill.DeveloperEngagement", 0); + + // UKM must not be logged unless enabled. + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); } // Add another field to the form, so that it becomes fillable. test::CreateTestFormField("Phone", "phone", "", "text", &field); forms.back().fields.push_back(field); - // Expect only the "form parsed" metric to be logged; no metrics about - // author-specified field type hints. + // Expect the "form parsed without hints" metric to be logged. { base::HistogramTester histogram_tester; autofill_manager_->OnFormsSeen(forms, TimeTicks()); autofill_manager_->Reset(); - histogram_tester.ExpectUniqueSample("Autofill.DeveloperEngagement", - AutofillMetrics::FILLABLE_FORM_PARSED, - 1); + histogram_tester.ExpectUniqueSample( + "Autofill.DeveloperEngagement", + AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS, 1); + + // UKM must not be logged unless enabled. + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); } // Add some fields with an author-specified field type to the form. @@ -1486,18 +1747,193 @@ TEST_F(AutofillMetricsTest, DeveloperEngagement) { field.autocomplete_attribute = "address-line1"; forms.back().fields.push_back(field); - // Expect both the "form parsed" metric and the author-specified field type - // hints metric to be logged. + // Expect the "form parsed with field type hints" metric to be logged. { base::HistogramTester histogram_tester; autofill_manager_->OnFormsSeen(forms, TimeTicks()); autofill_manager_->Reset(); - histogram_tester.ExpectBucketCount("Autofill.DeveloperEngagement", - AutofillMetrics::FILLABLE_FORM_PARSED, - 1); histogram_tester.ExpectBucketCount( "Autofill.DeveloperEngagement", - AutofillMetrics::FILLABLE_FORM_CONTAINS_TYPE_HINTS, 1); + AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, 1); + + // UKM must not be logged unless enabled. + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); + + histogram_tester.ExpectBucketCount( + "Autofill.DeveloperEngagement", + AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT, 0); + } + + // Add a field with an author-specified UPI-VPA field type in the form. + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = "upi-vpa"; + forms.back().fields.push_back(field); + + // Expect the "form parsed with type hints" metric, and the + // "author-specified upi-vpa type" metric to be logged. + { + base::HistogramTester histogram_tester; + autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->Reset(); + histogram_tester.ExpectBucketCount( + "Autofill.DeveloperEngagement", + AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, 1); + histogram_tester.ExpectBucketCount( + "Autofill.DeveloperEngagement", + AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT, 1); + } +} + +// Verify that we correctly log UKM for form parsed without type hints regarding +// developer engagement. +TEST_F(AutofillMetricsTest, + UkmDeveloperEngagement_LogFillableFormParsedWithoutTypeHints) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + + // Start with a non-fillable form. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Name", "name", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Email", "email", "", "text", &field); + form.fields.push_back(field); + + std::vector<FormData> forms(1, form); + + // Ensure no metrics are logged when loading a non-fillable form. + { + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->Reset(); + + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); + } + + // Add another field to the form, so that it becomes fillable. + test::CreateTestFormField("Phone", "phone", "", "text", &field); + forms.back().fields.push_back(field); + + // Expect the "form parsed without field type hints" metric and the + // "form loaded" form interaction event to be logged. + { + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->Reset(); + + ASSERT_EQ(1U, ukm_service->entries_count()); + ASSERT_EQ(1U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); + } +} + +// Verify that we correctly log UKM for form parsed with type hints regarding +// developer engagement. +TEST_F(AutofillMetricsTest, + UkmDeveloperEngagement_LogFillableFormParsedWithTypeHints) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Name", "name", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Email", "email", "", "text", &field); + form.fields.push_back(field); + + std::vector<FormData> forms(1, form); + + // Add another field to the form, so that it becomes fillable. + test::CreateTestFormField("Phone", "phone", "", "text", &field); + forms.back().fields.push_back(field); + + // Add some fields with an author-specified field type to the form. + // We need to add at least three fields, because a form must have at least + // three fillable fields to be considered to be autofillable; and if at least + // one field specifies an explicit type hint, we don't apply any of our usual + // local heuristics to detect field types in the rest of the form. + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = "given-name"; + forms.back().fields.push_back(field); + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = "email"; + forms.back().fields.push_back(field); + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = "address-line1"; + forms.back().fields.push_back(field); + + // Expect the "form parsed without field type hints" metric and the + // "form loaded" form interaction event to be logged. + { + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->Reset(); + + ASSERT_EQ(1U, ukm_service->entries_count()); + ASSERT_EQ(1U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS}); + } +} + +// Verify that we correctly log UKM for form parsed with type hints regarding +// developer engagement. +TEST_F(AutofillMetricsTest, UkmDeveloperEngagement_LogUpiVpaTypeHint) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Name", "name", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Email", "email", "", "text", &field); + form.fields.push_back(field); + test::CreateTestFormField("Payment", "payment", "", "text", &field); + field.autocomplete_attribute = "upi-vpa"; + form.fields.push_back(field); + + std::vector<FormData> forms(1, form); + + // Expect the "upi-vpa hint" metric to be logged and the "form loaded" form + // interaction event to be logged. + { + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->Reset(); + + ASSERT_EQ(1U, ukm_service->entries_count()); + ASSERT_EQ(1U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm(form, ukm_service, + {AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT}); + ukm_service->Purge(); + } + + // Add another field with an author-specified field type to the form. + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = "address-line1"; + forms.back().fields.push_back(field); + + { + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->Reset(); + + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, + AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT}); } } @@ -1680,6 +2116,9 @@ TEST_F(AutofillMetricsTest, AddressSuggestionsCount) { // Test that the credit card checkout flow user actions are correctly logged. TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + personal_data_->RecreateCreditCards( true /* include_local_credit_card */, false /* include_masked_server_credit_card */, @@ -1755,10 +2194,30 @@ TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); } + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect 2 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from + // call to |external_delegate_->DidAcceptSuggestion|. Second, from call to + // |autofill_manager_->FillOrPreviewForm|. + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState| + // because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|. + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } // Test that the profile checkout flow user actions are correctly logged. TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Create a profile. personal_data_->RecreateProfile(); @@ -1806,7 +2265,7 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { std::string guid("00000000-0000-0000-0000-000000000001"); // local profile. external_delegate_->DidAcceptSuggestion( ASCIIToUTF16("Test"), - autofill_manager_->MakeFrontendID(guid, std::string()), 0); + autofill_manager_->MakeFrontendID(std::string(), guid), 0); EXPECT_EQ(1, user_action_tester.GetActionCount("Autofill_SelectedSuggestion")); } @@ -1832,6 +2291,23 @@ TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); } + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect 2 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from + // call to |external_delegate_->DidAcceptSuggestion|. Second, from call to + // |autofill_manager_->FillOrPreviewForm|. + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState| + // because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|. + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } // Tests that the Autofill_PolledCreditCardSuggestions user action is only @@ -2121,6 +2597,11 @@ TEST_F(AutofillMetricsTest, CreditCardShownFormEvents) { "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0); } + + // UKM must not be logged unless enabled. + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); } // Test that we log selected form event for credit cards. @@ -2253,6 +2734,7 @@ TEST_F(AutofillMetricsTest, CreditCardFilledFormEvents) { autofill_manager_->MakeFrontendID(guid, std::string())); autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS, "6011000990139424"); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); @@ -2386,6 +2868,9 @@ TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration) { // Test that we log submitted form events for credit cards. TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EnableWalletSync(); // Creating all kinds of cards. personal_data_->RecreateCreditCards( @@ -2425,10 +2910,15 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2443,10 +2933,18 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2464,10 +2962,19 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2486,10 +2993,19 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::FULL_SERVER_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2502,6 +3018,7 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { autofill_manager_->MakeFrontendID(guid, std::string())); autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS, "6011000990139424"); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); @@ -2509,8 +3026,22 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::MASKED_SERVER_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSelectedMaskedServerCardEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } + // Reset the autofill manager state and purge UKM logs. + autofill_manager_->Reset(); + ukm_service->Purge(); + // Recreating cards as the previous test should have upgraded the masked // card to a full card. personal_data_->RecreateCreditCards( @@ -2527,7 +3058,24 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); autofill_manager_->SubmitForm(form, TimeTicks::Now()); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMFormSubmittedEntryName, + {{{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMFormSubmittedEntryName, + {{{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -2564,8 +3112,10 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { 0); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2608,6 +3158,12 @@ TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { AutofillMetrics:: FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } } @@ -3029,6 +3585,9 @@ TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { // Test that we log submitted form events for address. TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EnableWalletSync(); // Create a profile. personal_data_->RecreateProfile(); @@ -3065,10 +3624,15 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -3116,6 +3680,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); autofill_manager_->SubmitForm(form, TimeTicks::Now()); autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -3152,6 +3717,7 @@ TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { base::HistogramTester histogram_tester; autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); @@ -3522,7 +4088,6 @@ TEST_F(AutofillMetricsTest, AddressFormEventsAreSegmented) { } } - // Test that we log that Autofill is enabled when filling a form. TEST_F(AutofillMetricsTest, AutofillIsEnabledAtPageLoad) { base::HistogramTester histogram_tester; @@ -3561,6 +4126,9 @@ TEST_F(AutofillMetricsTest, DaysSinceLastUse_Profile) { // Verify that we correctly log the submitted form's state. TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Start with a form with insufficiently many fields. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -3581,10 +4149,16 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { // Expect no notifications when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0); } + std::vector<std::vector<std::pair<const char*, int64_t>>> + expected_form_submission_ukm_metrics = { + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}; + // No data entered in the form. { base::HistogramTester histogram_tester; @@ -3595,6 +4169,17 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); + + // Expect an entry for |DeveloperEngagement| and an entry for form + // interactions. Both entries are for the same URL. + ASSERT_EQ(2U, ukm_service->entries_count()); + ASSERT_EQ(2U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Non fillable form. @@ -3611,6 +4196,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Fill in the third field. @@ -3628,6 +4221,15 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledNone_SuggestionsNotShown")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics:: + FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Autofilled none with suggestions shown. @@ -3641,6 +4243,17 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledNone_SuggestionsShown")); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Mark one of the fields as autofilled. @@ -3657,6 +4270,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledSome")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Mark all of the fillable fields as autofilled. @@ -3674,6 +4295,14 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledAll")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Clear out the third field's value. @@ -3690,12 +4319,23 @@ TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } } // Verify that we correctly log user happiness metrics dealing with form // interaction. TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Load a fillable form. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -3795,6 +4435,50 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { "Autofill.UserHappiness", AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1); } + + autofill_manager_->Reset(); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMInteractedWithFormEntryName, + {{{internal::kUKMIsForCreditCardMetricName, false}, + {internal::kUKMLocalRecordTypeCountMetricName, 0}, + {internal::kUKMServerRecordTypeCountMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMTextFieldDidChangeEntryName, + {{{internal::kUKMFieldTypeGroupMetricName, NAME}, + {internal::kUKMHeuristicTypeMetricName, NAME_FULL}, + {internal::kUKMServerTypeMetricName, NO_SERVER_DATA}, + {internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED}, + {internal::kUKMHtmlFieldModeMetricName, HTML_MODE_NONE}, + {internal::kUKMIsAutofilledMetricName, false}, + {internal::kUKMIsEmptyMetricName, true}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMFieldTypeGroupMetricName, NAME}, + {internal::kUKMHeuristicTypeMetricName, NAME_FULL}, + {internal::kUKMServerTypeMetricName, NO_SERVER_DATA}, + {internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED}, + {internal::kUKMHtmlFieldModeMetricName, HTML_MODE_NONE}, + {internal::kUKMIsAutofilledMetricName, true}, + {internal::kUKMIsEmptyMetricName, true}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMFieldTypeGroupMetricName, EMAIL}, + {internal::kUKMHeuristicTypeMetricName, EMAIL_ADDRESS}, + {internal::kUKMServerTypeMetricName, NO_SERVER_DATA}, + {internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED}, + {internal::kUKMHtmlFieldModeMetricName, HTML_MODE_NONE}, + {internal::kUKMIsAutofilledMetricName, true}, + {internal::kUKMIsEmptyMetricName, true}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); } // Verify that we correctly log metrics tracking the duration of form fill. @@ -4366,15 +5050,14 @@ TEST_F(AutofillMetricsTest, } } -// Tests that logging a UKM works as expected. +// Tests that logging CardUploadDecision UKM works as expected. TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) { EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); int upload_decision = 1; - std::map<std::string, int> metrics; - metrics.insert(std::make_pair(internal::kUKMCardUploadDecisionMetricName, - upload_decision)); + std::vector<std::pair<const char*, int>> metrics = { + {internal::kUKMCardUploadDecisionMetricName, upload_decision}}; EXPECT_TRUE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, @@ -4385,14 +5068,15 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) { ukm_service_test_harness.test_ukm_service(); ASSERT_EQ(1U, ukm_service->sources_count()); - const ukm::UkmSource* source = ukm_service->GetSource(0); + const ukm::UkmSource* source = + ukm_service->GetSourceForUrl(url.spec().c_str()); EXPECT_EQ(url.spec(), source->url().spec()); - EXPECT_EQ(1U, ukm_service->entries_count()); + ASSERT_EQ(1U, ukm_service->entries_count()); const ukm::UkmEntry* entry = ukm_service->GetEntry(0); EXPECT_EQ(source->id(), entry->source_id()); - // Make sure that an card upload decision entry was logged. + // Make sure that a card upload decision entry was logged. ukm::Entry entry_proto; entry->PopulateProto(&entry_proto); EXPECT_EQ(source->id(), entry_proto.source_id()); @@ -4407,13 +5091,53 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) { EXPECT_EQ(upload_decision, metric->value()); } +// Tests that logging DeveloperEngagement UKM works as expected. +TEST_F(AutofillMetricsTest, RecordDeveloperEngagementMetric) { + EnableUkmLogging(); + ukm::UkmServiceTestingHarness ukm_service_test_harness; + GURL url("https://www.google.com"); + int form_structure_metric = 1; + std::vector<std::pair<const char*, int>> metrics = { + {internal::kUKMDeveloperEngagementMetricName, form_structure_metric}}; + + EXPECT_TRUE(AutofillMetrics::LogUkm( + ukm_service_test_harness.test_ukm_service(), url, + internal::kUKMDeveloperEngagementEntryName, metrics)); + + // Make sure that the UKM was logged correctly. + ukm::TestUkmService* ukm_service = + ukm_service_test_harness.test_ukm_service(); + + ASSERT_EQ(1U, ukm_service->sources_count()); + const ukm::UkmSource* source = + ukm_service->GetSourceForUrl(url.spec().c_str()); + EXPECT_EQ(url.spec(), source->url().spec()); + + ASSERT_EQ(1U, ukm_service->entries_count()); + const ukm::UkmEntry* entry = ukm_service->GetEntry(0); + EXPECT_EQ(source->id(), entry->source_id()); + + // Make sure that a developer engagement entry was logged. + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + EXPECT_EQ(source->id(), entry_proto.source_id()); + EXPECT_EQ(base::HashMetricName(internal::kUKMDeveloperEngagementEntryName), + entry_proto.event_hash()); + EXPECT_EQ(1, entry_proto.metrics_size()); + + // Make sure that the correct developer engagement metric was logged. + const ukm::Entry_Metric* metric = FindMetric( + internal::kUKMDeveloperEngagementMetricName, entry_proto.metrics()); + ASSERT_NE(nullptr, metric); + EXPECT_EQ(form_structure_metric, metric->value()); +} + // Tests that no UKM is logged when the URL is not valid. TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_InvalidUrl) { EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url(""); - std::map<std::string, int> metrics; - metrics.insert(std::make_pair("metric", 1)); + std::vector<std::pair<const char*, int>> metrics = {{"metric", 1}}; EXPECT_FALSE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); @@ -4425,7 +5149,7 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoMetrics) { EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); - std::map<std::string, int> metrics; + std::vector<std::pair<const char*, int>> metrics; EXPECT_FALSE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); @@ -4437,8 +5161,7 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoUkmService) { EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); - std::map<std::string, int> metrics; - metrics.insert(std::make_pair("metric", 1)); + std::vector<std::pair<const char*, int>> metrics = {{"metric", 1}}; EXPECT_FALSE(AutofillMetrics::LogUkm(nullptr, url, "test_ukm", metrics)); ASSERT_EQ(0U, ukm_service_test_harness.test_ukm_service()->sources_count()); @@ -4448,8 +5171,7 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoUkmService) { TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_FeatureDisabled) { ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); - std::map<std::string, int> metrics; - metrics.insert(std::make_pair("metric", 1)); + std::vector<std::pair<const char*, int>> metrics = {{"metric", 1}}; EXPECT_FALSE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc index 1828497e688..4ef1222b02a 100644 --- a/chromium/components/autofill/core/browser/autofill_profile.cc +++ b/chromium/components/autofill/core/browser/autofill_profile.cc @@ -48,8 +48,8 @@ using base::ASCIIToUTF16; using base::UTF16ToUTF8; -using i18n::addressinput::AddressData; -using i18n::addressinput::AddressField; +using ::i18n::addressinput::AddressData; +using ::i18n::addressinput::AddressField; namespace autofill { namespace { diff --git a/chromium/components/autofill/core/browser/autofill_profile_comparator.cc b/chromium/components/autofill/core/browser/autofill_profile_comparator.cc index c3e8709549b..8bfcd193dd5 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_comparator.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_comparator.cc @@ -9,7 +9,7 @@ #include "base/i18n/case_conversion.h" #include "base/i18n/char_iterator.h" -#include "base/strings/string_piece.h" +#include "base/i18n/unicodestring.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversion_utils.h" @@ -128,7 +128,7 @@ base::string16 AutofillProfileComparator::NormalizeForComparison( icu::UnicodeString value = icu::UnicodeString(result.data(), result.length()); transliterator_->transliterate(value); - return base::string16(value.getBuffer(), value.length()); + return base::i18n::UnicodeStringToString16(value); } bool AutofillProfileComparator::AreMergeable(const AutofillProfile& p1, @@ -667,7 +667,7 @@ std::set<base::string16> AutofillProfileComparator::GetNamePartVariants( const base::string16& name_part) { const size_t kMaxSupportedSubNames = 8; - std::vector<base::string16> sub_names = base::SplitString( + std::vector<base::StringPiece16> sub_names = base::SplitStringPiece( name_part, kSpace, base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); // Limit the number of sub-names we support (to constrain memory usage); @@ -680,7 +680,7 @@ std::set<base::string16> AutofillProfileComparator::GetNamePartVariants( // For each sub-name, add a variant of all the already existing variants that // appends this sub-name and one that appends the initial of this sub-name. // Duplicates will be discarded when they're added to the variants set. - for (const base::string16& sub_name : sub_names) { + for (const auto& sub_name : sub_names) { if (sub_name.empty()) continue; std::vector<base::string16> new_variants; @@ -696,7 +696,7 @@ std::set<base::string16> AutofillProfileComparator::GetNamePartVariants( // As a common case, also add the variant that just concatenates all of the // initials. base::string16 initials; - for (const base::string16& sub_name : sub_names) { + for (const auto& sub_name : sub_names) { if (sub_name.empty()) continue; initials.push_back(sub_name[0]); @@ -720,12 +720,12 @@ bool AutofillProfileComparator::IsNameVariantOf( GetNamePartVariants(name_1_parts.given); const std::set<base::string16> middle_name_variants = GetNamePartVariants(name_1_parts.middle); - const base::string16& family_name = name_1_parts.family; + base::StringPiece16 family_name = name_1_parts.family; // Iterate over all full name variants of profile 2 and see if any of them // match the full name from profile 1. - for (const base::string16& given_name : given_name_variants) { - for (const base::string16& middle_name : middle_name_variants) { + for (const auto& given_name : given_name_variants) { + for (const auto& middle_name : middle_name_variants) { base::string16 candidate = base::CollapseWhitespace( base::JoinString({given_name, middle_name, family_name}, kSpace), true); diff --git a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc index c54f2ae61c4..ee55bc0857b 100644 --- a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc +++ b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc @@ -39,39 +39,6 @@ base::string16 GetLabel(AutofillProfile* profile) { return labels[0]; } -// Holds the autofill profile |first|, |middle| and |last| names. -struct NameParts { - NameParts(const std::string& first, - const std::string& middle, - const std::string& last) - : first(first), middle(middle), last(last) {} - - std::string first; - std::string middle; - std::string last; -}; - -// Test case to be executed to validate OverwriteOrAppendNames. -struct TestCase { - TestCase(const NameParts& starting_name, - const NameParts& additional_name, - const NameParts& expected_result) - : starting_names(std::vector<NameParts>(1, starting_name)), - additional_names(std::vector<NameParts>(1, additional_name)), - expected_result(std::vector<NameParts>(1, expected_result)) {} - - TestCase(const std::vector<NameParts>& starting_names, - const std::vector<NameParts>& additional_names, - const std::vector<NameParts>& expected_result) - : starting_names(starting_names), - additional_names(additional_names), - expected_result(expected_result) {} - - std::vector<NameParts> starting_names; - std::vector<NameParts> additional_names; - std::vector<NameParts> expected_result; -}; - void SetupTestProfile(AutofillProfile& profile) { profile.set_guid(base::GenerateGUID()); profile.set_origin(kSettingsOrigin); diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.cc b/chromium/components/autofill/core/browser/autofill_regex_constants.cc index e5587bea511..57b961df461 100644 --- a/chromium/components/autofill/core/browser/autofill_regex_constants.cc +++ b/chromium/components/autofill/core/browser/autofill_regex_constants.cc @@ -309,5 +309,34 @@ const char kPhoneSuffixRe[] = const char kPhoneExtensionRe[] = "\\bext|ext\\b|extension" "|ramal"; // pt-BR, pt-PT +const char kUPIVirtualPaymentAddressRe[] = + "^\\w+@(" + "upi|" // BHIM Bharat Interface for Money + "allbank|" // Allahabad Bank UPI + "andb|" // Andhra Bank ONE + "axisbank|" // Axis Pay + "barodampay|" // Baroda MPay + "mahb|" // MAHAUPI + "cnrb|" // Canara Bank UPI - Empower + "csbpay|" // CSB UPI + "dcb|" // DCB Bank + "federal|" // Lotza + "hdfcbank|" // HDFC Bank MobileBanking + "pockets|" // Pockets- ICICI Bank + "icici|" // Pockets- ICICI Bank + "idfcbank|" // IDFC Bank UPI App + "indus|" // Indus Pay + "kbl|" // KBL Smartz + "kaypay|" // KayPay + "pnb|" // PNB UPI + "sib|" // SIB M-Pay (UPI Pay) + "sbi|" // SBI Pay + "tjsp|" // TranZapp + "uco|" // UCO UPI + "unionbank|" // Union Bank UPI + "united|" // United UPI + "vijb|" // Vijaya UPI App + "ybl" // Yes Pay + ")$"; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.h b/chromium/components/autofill/core/browser/autofill_regex_constants.h index db85f540f69..8be7b2c39a6 100644 --- a/chromium/components/autofill/core/browser/autofill_regex_constants.h +++ b/chromium/components/autofill/core/browser/autofill_regex_constants.h @@ -56,6 +56,12 @@ extern const char kPhonePrefixRe[]; extern const char kPhoneSuffixRe[]; extern const char kPhoneExtensionRe[]; +// Used to match field data that might be a UPI Virtual Payment Address. +// See: +// - http://crbug.com/702220 +// - https://upipayments.co.in/virtual-payment-address-vpa/ +extern const char kUPIVirtualPaymentAddressRe[]; + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_ diff --git a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc index fe2e1d3f566..28f7aea7b07 100644 --- a/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc +++ b/chromium/components/autofill/core/browser/autofill_save_card_infobar_delegate_mobile.cc @@ -17,7 +17,6 @@ #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/window_open_disposition.h" -#include "ui/gfx/vector_icons_public.h" #include "url/gurl.h" namespace autofill { diff --git a/chromium/components/autofill/core/browser/autofill_test_utils.cc b/chromium/components/autofill/core/browser/autofill_test_utils.cc index 78b1f0e09ed..10a23dcb2c6 100644 --- a/chromium/components/autofill/core/browser/autofill_test_utils.cc +++ b/chromium/components/autofill/core/browser/autofill_test_utils.cc @@ -226,15 +226,15 @@ AutofillProfile GetVerifiedProfile2() { CreditCard GetCreditCard() { CreditCard credit_card(base::GenerateGUID(), "http://www.example.com"); - SetCreditCardInfo( - &credit_card, "Test User", "4111111111111111" /* Visa */, "11", "2017"); + SetCreditCardInfo(&credit_card, "Test User", "4111111111111111" /* Visa */, + "11", "2022"); return credit_card; } CreditCard GetCreditCard2() { CreditCard credit_card(base::GenerateGUID(), "https://www.example.com"); - SetCreditCardInfo( - &credit_card, "Someone Else", "378282246310005" /* AmEx */, "07", "2019"); + SetCreditCardInfo(&credit_card, "Someone Else", "378282246310005" /* AmEx */, + "07", "2022"); return credit_card; } @@ -253,7 +253,7 @@ CreditCard GetVerifiedCreditCard2() { CreditCard GetMaskedServerCard() { CreditCard credit_card(CreditCard::MASKED_SERVER_CARD, "a123"); test::SetCreditCardInfo(&credit_card, "Bonnie Parker", - "2109" /* Mastercard */, "12", "2012"); + "2109" /* Mastercard */, "12", "2020"); credit_card.SetTypeForMaskedCard(kMasterCard); return credit_card; } diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc index 8dc9dbc85ad..2b97a2f6eda 100644 --- a/chromium/components/autofill/core/browser/autofill_type.cc +++ b/chromium/components/autofill/core/browser/autofill_type.cc @@ -208,6 +208,10 @@ FieldTypeGroup AutofillType::group() const { case HTML_TYPE_EMAIL: return EMAIL; + case HTML_TYPE_UPI_VPA: + // TODO(crbug/702223): Add support for UPI-VPA. + break; + case HTML_TYPE_UNSPECIFIED: case HTML_TYPE_UNRECOGNIZED: break; @@ -418,6 +422,10 @@ ServerFieldType AutofillType::GetStorableType() const { case HTML_TYPE_TRANSACTION_CURRENCY: return UNKNOWN_TYPE; + // TODO(crbug/702223): Add autofill support for UPI-VPA. + case HTML_TYPE_UPI_VPA: + return UNKNOWN_TYPE; + case HTML_TYPE_UNRECOGNIZED: return UNKNOWN_TYPE; } @@ -591,6 +599,8 @@ std::string AutofillType::ToString() const { return "HTML_TRANSACTION_AMOUNT"; case HTML_TYPE_TRANSACTION_CURRENCY: return "HTML_TRANSACTION_CURRENCY"; + case HTML_TYPE_UPI_VPA: + return "HTML_TYPE_UPI_VPA"; case HTML_TYPE_UNRECOGNIZED: return "HTML_TYPE_UNRECOGNIZED"; } diff --git a/chromium/components/autofill/core/browser/contact_info_unittest.cc b/chromium/components/autofill/core/browser/contact_info_unittest.cc index d501b5d782d..d07dfa041f1 100644 --- a/chromium/components/autofill/core/browser/contact_info_unittest.cc +++ b/chromium/components/autofill/core/browser/contact_info_unittest.cc @@ -25,47 +25,53 @@ struct FullNameTestCase { std::string given_name_output; std::string middle_name_output; std::string family_name_output; -} full_name_test_cases[] = { - { "", "", "", "" }, - { "John Smith", "John", "", "Smith" }, - { "Julien van der Poel", "Julien", "", "van der Poel" }, - { "John J Johnson", "John", "J", "Johnson" }, - { "John Smith, Jr.", "John", "", "Smith" }, - { "Mr John Smith", "John", "", "Smith" }, - { "Mr. John Smith", "John", "", "Smith" }, - { "Mr. John Smith, M.D.", "John", "", "Smith" }, - { "Mr. John Smith, MD", "John", "", "Smith" }, - { "Mr. John Smith MD", "John", "", "Smith" }, - { "William Hubert J.R.", "William", "Hubert", "J.R." }, - { "John Ma", "John", "", "Ma" }, - { "John Smith, MA", "John", "", "Smith" }, - { "John Jacob Jingleheimer Smith", "John Jacob", "Jingleheimer", "Smith" }, - { "Virgil", "Virgil", "", "" }, - { "Murray Gell-Mann", "Murray", "", "Gell-Mann" }, - { "Mikhail Yevgrafovich Saltykov-Shchedrin", "Mikhail", "Yevgrafovich", - "Saltykov-Shchedrin" }, - { "Arthur Ignatius Conan Doyle", "Arthur Ignatius", "Conan", "Doyle" }, }; -TEST(NameInfoTest, SetFullName) { - for (const FullNameTestCase& test_case : full_name_test_cases) { - SCOPED_TRACE(test_case.full_name_input); +class SetFullNameTest : public testing::TestWithParam<FullNameTestCase> {}; - NameInfo name; - name.SetInfo(AutofillType(NAME_FULL), - ASCIIToUTF16(test_case.full_name_input), - "en-US"); - EXPECT_EQ(ASCIIToUTF16(test_case.given_name_output), - name.GetInfo(AutofillType(NAME_FIRST), "en-US")); - EXPECT_EQ(ASCIIToUTF16(test_case.middle_name_output), - name.GetInfo(AutofillType(NAME_MIDDLE), "en-US")); - EXPECT_EQ(ASCIIToUTF16(test_case.family_name_output), - name.GetInfo(AutofillType(NAME_LAST), "en-US")); - EXPECT_EQ(ASCIIToUTF16(test_case.full_name_input), - name.GetInfo(AutofillType(NAME_FULL), "en-US")); - } +TEST_P(SetFullNameTest, SetFullName) { + auto test_case = GetParam(); + SCOPED_TRACE(test_case.full_name_input); + + NameInfo name; + name.SetInfo(AutofillType(NAME_FULL), ASCIIToUTF16(test_case.full_name_input), + "en-US"); + EXPECT_EQ(ASCIIToUTF16(test_case.given_name_output), + name.GetInfo(AutofillType(NAME_FIRST), "en-US")); + EXPECT_EQ(ASCIIToUTF16(test_case.middle_name_output), + name.GetInfo(AutofillType(NAME_MIDDLE), "en-US")); + EXPECT_EQ(ASCIIToUTF16(test_case.family_name_output), + name.GetInfo(AutofillType(NAME_LAST), "en-US")); + EXPECT_EQ(ASCIIToUTF16(test_case.full_name_input), + name.GetInfo(AutofillType(NAME_FULL), "en-US")); } +INSTANTIATE_TEST_CASE_P( + ContactInfoTest, + SetFullNameTest, + testing::Values( + FullNameTestCase{"", "", "", ""}, + FullNameTestCase{"John Smith", "John", "", "Smith"}, + FullNameTestCase{"Julien van der Poel", "Julien", "", "van der Poel"}, + FullNameTestCase{"John J Johnson", "John", "J", "Johnson"}, + FullNameTestCase{"John Smith, Jr.", "John", "", "Smith"}, + FullNameTestCase{"Mr John Smith", "John", "", "Smith"}, + FullNameTestCase{"Mr. John Smith", "John", "", "Smith"}, + FullNameTestCase{"Mr. John Smith, M.D.", "John", "", "Smith"}, + FullNameTestCase{"Mr. John Smith, MD", "John", "", "Smith"}, + FullNameTestCase{"Mr. John Smith MD", "John", "", "Smith"}, + FullNameTestCase{"William Hubert J.R.", "William", "Hubert", "J.R."}, + FullNameTestCase{"John Ma", "John", "", "Ma"}, + FullNameTestCase{"John Smith, MA", "John", "", "Smith"}, + FullNameTestCase{"John Jacob Jingleheimer Smith", "John Jacob", + "Jingleheimer", "Smith"}, + FullNameTestCase{"Virgil", "Virgil", "", ""}, + FullNameTestCase{"Murray Gell-Mann", "Murray", "", "Gell-Mann"}, + FullNameTestCase{"Mikhail Yevgrafovich Saltykov-Shchedrin", "Mikhail", + "Yevgrafovich", "Saltykov-Shchedrin"}, + FullNameTestCase{"Arthur Ignatius Conan Doyle", "Arthur Ignatius", + "Conan", "Doyle"})); + TEST(NameInfoTest, GetFullName) { NameInfo name; name.SetRawInfo(NAME_FIRST, ASCIIToUTF16("First")); @@ -174,209 +180,221 @@ TEST(NameInfoTest, GetFullName) { name.GetInfo(AutofillType(NAME_FULL), "en-US")); } -TEST(NameInfoTest, ParsedNamesAreEqual) { - struct TestCase { - std::string starting_names[3]; - std::string additional_names[3]; - bool expected_result; +struct ParsedNamesAreEqualTestCase { + std::string starting_names[3]; + std::string additional_names[3]; + bool expected_result; }; - struct TestCase test_cases[] = { - // Identical name comparison. - {{"Marion", "Mitchell", "Morrison"}, - {"Marion", "Mitchell", "Morrison"}, - true}, - - // Case-sensitive comparisons. - {{"Marion", "Mitchell", "Morrison"}, - {"Marion", "Mitchell", "MORRISON"}, - false}, - {{"Marion", "Mitchell", "Morrison"}, - {"MARION", "Mitchell", "MORRISON"}, - false}, - {{"Marion", "Mitchell", "Morrison"}, - {"MARION", "MITCHELL", "MORRISON"}, - false}, - {{"Marion", "", "Mitchell Morrison"}, - {"MARION", "", "MITCHELL MORRISON"}, - false}, - {{"Marion Mitchell", "", "Morrison"}, - {"MARION MITCHELL", "", "MORRISON"}, - false}, - - // Identical full names but different canonical forms. - {{"Marion", "Mitchell", "Morrison"}, - {"Marion", "", "Mitchell Morrison"}, - false}, - {{"Marion", "Mitchell", "Morrison"}, - {"Marion Mitchell", "", "MORRISON"}, - false}, - - // Different names. - {{"Marion", "Mitchell", "Morrison"}, {"Marion", "M.", "Morrison"}, false}, - {{"Marion", "Mitchell", "Morrison"}, {"MARION", "M.", "MORRISON"}, false}, - {{"Marion", "Mitchell", "Morrison"}, - {"David", "Mitchell", "Morrison"}, - false}, - - // Non-ASCII characters. - {{"M\xc3\xa1rion Mitchell", "", "Morrison"}, - {"M\xc3\xa1rion Mitchell", "", "Morrison"}, - true}, - }; + class ParsedNamesAreEqualTest + : public testing::TestWithParam<ParsedNamesAreEqualTestCase> {}; - for (size_t i = 0; i < arraysize(test_cases); ++i) { - SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i)); + TEST_P(ParsedNamesAreEqualTest, ParsedNamesAreEqual) { + auto test_case = GetParam(); // Construct the starting_profile. NameInfo starting_profile; starting_profile.SetRawInfo(NAME_FIRST, - UTF8ToUTF16(test_cases[i].starting_names[0])); + UTF8ToUTF16(test_case.starting_names[0])); starting_profile.SetRawInfo(NAME_MIDDLE, - UTF8ToUTF16(test_cases[i].starting_names[1])); + UTF8ToUTF16(test_case.starting_names[1])); starting_profile.SetRawInfo(NAME_LAST, - UTF8ToUTF16(test_cases[i].starting_names[2])); + UTF8ToUTF16(test_case.starting_names[2])); // Construct the additional_profile. NameInfo additional_profile; - additional_profile.SetRawInfo( - NAME_FIRST, UTF8ToUTF16(test_cases[i].additional_names[0])); - additional_profile.SetRawInfo( - NAME_MIDDLE, UTF8ToUTF16(test_cases[i].additional_names[1])); - additional_profile.SetRawInfo( - NAME_LAST, UTF8ToUTF16(test_cases[i].additional_names[2])); + additional_profile.SetRawInfo(NAME_FIRST, + UTF8ToUTF16(test_case.additional_names[0])); + additional_profile.SetRawInfo(NAME_MIDDLE, + UTF8ToUTF16(test_case.additional_names[1])); + additional_profile.SetRawInfo(NAME_LAST, + UTF8ToUTF16(test_case.additional_names[2])); // Verify the test expectations. - EXPECT_EQ(test_cases[i].expected_result, + EXPECT_EQ(test_case.expected_result, starting_profile.ParsedNamesAreEqual(additional_profile)); } -} -TEST(NameInfoTest, OverwriteName) { - struct TestCase { + INSTANTIATE_TEST_CASE_P( + ContactInfoTest, + ParsedNamesAreEqualTest, + testing::Values( + // Identical name comparison. + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"Marion", "Mitchell", "Morrison"}, + true}, + + // Case-sensitive comparisons. + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"Marion", "Mitchell", "MORRISON"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"MARION", "Mitchell", "MORRISON"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"MARION", "MITCHELL", "MORRISON"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion", "", "Mitchell Morrison"}, + {"MARION", "", "MITCHELL MORRISON"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion Mitchell", "", "Morrison"}, + {"MARION MITCHELL", "", "MORRISON"}, + false}, + + // Identical full names but different canonical forms. + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"Marion", "", "Mitchell Morrison"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"Marion Mitchell", "", "MORRISON"}, + false}, + + // Different names. + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"Marion", "M.", "Morrison"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"MARION", "M.", "MORRISON"}, + false}, + ParsedNamesAreEqualTestCase{{"Marion", "Mitchell", "Morrison"}, + {"David", "Mitchell", "Morrison"}, + false}, + + // Non-ASCII characters. + ParsedNamesAreEqualTestCase{ + {"M\xc3\xa1rion Mitchell", "", "Morrison"}, + {"M\xc3\xa1rion Mitchell", "", "Morrison"}, + true})); + + struct OverwriteNameTestCase { std::string existing_name[4]; std::string new_name[4]; std::string expected_name[4]; }; - struct TestCase test_cases[] = { - // Missing information in the original name gets filled with the new - // name's information. - { - {"", "", "", ""}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // The new name's values overwrite the exsiting name values if they are - // different - { - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Mario", "Mitchell", "Thompson", "Mario Mitchell Morrison"}, - {"Mario", "Mitchell", "Thompson", "Mario Mitchell Morrison"}, - }, - // An existing name values do not get replaced with empty values. - { - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"", "", "", ""}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // An existing full middle not does not get replaced by a middle name - // initial. - { - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "M", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // An existing middle name initial is overwritten by the new profile's - // middle name value. - { - {"Marion", "M", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // A NameInfo with only the full name set overwritten with a NameInfo - // with only the name parts set result in a NameInfo with all the name - // parts and name full set. - { - {"", "", "", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", ""}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - // A NameInfo with only the name parts set overwritten with a NameInfo - // with only the full name set result in a NameInfo with all the name - // parts and name full set. - { - {"Marion", "Mitchell", "Morrison", ""}, - {"", "", "", "Marion Mitchell Morrison"}, - {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, - }, - }; - - for (size_t i = 0; i < arraysize(test_cases); ++i) { - SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i)); + class OverwriteNameTest + : public testing::TestWithParam<OverwriteNameTestCase> {}; + TEST_P(OverwriteNameTest, OverwriteName) { + auto test_case = GetParam(); // Construct the starting_profile. NameInfo existing_name; existing_name.SetRawInfo(NAME_FIRST, - UTF8ToUTF16(test_cases[i].existing_name[0])); + UTF8ToUTF16(test_case.existing_name[0])); existing_name.SetRawInfo(NAME_MIDDLE, - UTF8ToUTF16(test_cases[i].existing_name[1])); + UTF8ToUTF16(test_case.existing_name[1])); existing_name.SetRawInfo(NAME_LAST, - UTF8ToUTF16(test_cases[i].existing_name[2])); + UTF8ToUTF16(test_case.existing_name[2])); existing_name.SetRawInfo(NAME_FULL, - UTF8ToUTF16(test_cases[i].existing_name[3])); + UTF8ToUTF16(test_case.existing_name[3])); // Construct the additional_profile. NameInfo new_name; - new_name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_cases[i].new_name[0])); - new_name.SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(test_cases[i].new_name[1])); - new_name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_cases[i].new_name[2])); - new_name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_cases[i].new_name[3])); + new_name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_case.new_name[0])); + new_name.SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(test_case.new_name[1])); + new_name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_case.new_name[2])); + new_name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_case.new_name[3])); existing_name.OverwriteName(new_name); // Verify the test expectations. - EXPECT_EQ(UTF8ToUTF16(test_cases[i].expected_name[0]), + EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[0]), existing_name.GetRawInfo(NAME_FIRST)); - EXPECT_EQ(UTF8ToUTF16(test_cases[i].expected_name[1]), + EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[1]), existing_name.GetRawInfo(NAME_MIDDLE)); - EXPECT_EQ(UTF8ToUTF16(test_cases[i].expected_name[2]), + EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[2]), existing_name.GetRawInfo(NAME_LAST)); - EXPECT_EQ(UTF8ToUTF16(test_cases[i].expected_name[3]), + EXPECT_EQ(UTF8ToUTF16(test_case.expected_name[3]), existing_name.GetRawInfo(NAME_FULL)); - } } -TEST(NameInfoTest, NamePartsAreEmpty) { - struct TestCase { - std::string first; - std::string middle; - std::string last; - std::string full; - bool expected_result; +INSTANTIATE_TEST_CASE_P( + ContactInfoTest, + OverwriteNameTest, + testing::Values( + // Missing information in the original name gets filled with the new + // name's information. + OverwriteNameTestCase{ + {"", "", "", ""}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + }, + // The new name's values overwrite the exsiting name values if they are + // different + OverwriteNameTestCase{ + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + {"Mario", "Mitchell", "Thompson", "Mario Mitchell Morrison"}, + {"Mario", "Mitchell", "Thompson", "Mario Mitchell Morrison"}, + }, + // An existing name values do not get replaced with empty values. + OverwriteNameTestCase{ + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + {"", "", "", ""}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + }, + // An existing full middle not does not get replaced by a middle name + // initial. + OverwriteNameTestCase{ + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + {"Marion", "M", "Morrison", "Marion Mitchell Morrison"}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + }, + // An existing middle name initial is overwritten by the new profile's + // middle name value. + OverwriteNameTestCase{ + {"Marion", "M", "Morrison", "Marion Mitchell Morrison"}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + }, + // A NameInfo with only the full name set overwritten with a NameInfo + // with only the name parts set result in a NameInfo with all the name + // parts and name full set. + OverwriteNameTestCase{ + {"", "", "", "Marion Mitchell Morrison"}, + {"Marion", "Mitchell", "Morrison", ""}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + }, + // A NameInfo with only the name parts set overwritten with a NameInfo + // with only the full name set result in a NameInfo with all the name + // parts and name full set. + OverwriteNameTestCase{ + {"Marion", "Mitchell", "Morrison", ""}, + {"", "", "", "Marion Mitchell Morrison"}, + {"Marion", "Mitchell", "Morrison", "Marion Mitchell Morrison"}, + })); + +struct NamePartsAreEmptyTestCase { + std::string first; + std::string middle; + std::string last; + std::string full; + bool expected_result; }; - struct TestCase test_cases[] = { - {"", "", "", "", true}, - {"", "", "", "Marion Mitchell Morrison", true}, - {"Marion", "", "", "", false}, - {"", "Mitchell", "", "", false}, - {"", "", "Morrison", "", false}, - }; - - for (size_t i = 0; i < arraysize(test_cases); ++i) { - SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i)); + class NamePartsAreEmptyTest + : public testing::TestWithParam<NamePartsAreEmptyTestCase> {}; + TEST_P(NamePartsAreEmptyTest, NamePartsAreEmpty) { + auto test_case = GetParam(); // Construct the NameInfo. NameInfo name; - name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_cases[i].first)); - name.SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(test_cases[i].middle)); - name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_cases[i].last)); - name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_cases[i].full)); + name.SetRawInfo(NAME_FIRST, UTF8ToUTF16(test_case.first)); + name.SetRawInfo(NAME_MIDDLE, UTF8ToUTF16(test_case.middle)); + name.SetRawInfo(NAME_LAST, UTF8ToUTF16(test_case.last)); + name.SetRawInfo(NAME_FULL, UTF8ToUTF16(test_case.full)); // Verify the test expectations. - EXPECT_EQ(test_cases[i].expected_result, name.NamePartsAreEmpty()); - } + EXPECT_EQ(test_case.expected_result, name.NamePartsAreEmpty()); } +INSTANTIATE_TEST_CASE_P( + ContactInfoTest, + NamePartsAreEmptyTest, + testing::Values(NamePartsAreEmptyTestCase{"", "", "", "", true}, + NamePartsAreEmptyTestCase{"", "", "", + "Marion Mitchell Morrison", true}, + NamePartsAreEmptyTestCase{"Marion", "", "", "", false}, + NamePartsAreEmptyTestCase{"", "Mitchell", "", "", false}, + NamePartsAreEmptyTestCase{"", "", "Morrison", "", false})); + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/country_combobox_model.h b/chromium/components/autofill/core/browser/country_combobox_model.h index b3d90c3abf6..4e36d91ba45 100644 --- a/chromium/components/autofill/core/browser/country_combobox_model.h +++ b/chromium/components/autofill/core/browser/country_combobox_model.h @@ -38,6 +38,8 @@ class CountryComboboxModel : public ui::ComboboxModel { base::string16 GetItemAt(int index) override; bool IsItemSeparatorAt(int index) override; + // The list of countries always has the default country at the top as well as + // within the sorted vector. const CountryVector& countries() const { return countries_; } // Returns the default country code for this model. diff --git a/chromium/components/autofill/core/browser/country_names.cc b/chromium/components/autofill/core/browser/country_names.cc index bbf1fb6c0ae..2f9a022e363 100644 --- a/chromium/components/autofill/core/browser/country_names.cc +++ b/chromium/components/autofill/core/browser/country_names.cc @@ -24,7 +24,7 @@ namespace { // A copy of the application locale string, which should be ready for // CountryName's construction. -static base::LazyInstance<std::string> g_application_locale = +static base::LazyInstance<std::string>::DestructorAtExit g_application_locale = LAZY_INSTANCE_INITIALIZER; // Returns the ICU sort key corresponding to |str| for the given |collator|. diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc index bd5110eced3..0e54c44176a 100644 --- a/chromium/components/autofill/core/browser/credit_card.cc +++ b/chromium/components/autofill/core/browser/credit_card.cc @@ -13,6 +13,7 @@ #include "base/guid.h" #include "base/i18n/time_formatting.h" +#include "base/i18n/unicodestring.h" #include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" @@ -870,7 +871,8 @@ bool CreditCard::ConvertMonth(const base::string16& month, int32_t num_months; const icu::UnicodeString* months = date_format_symbols.getMonths(num_months); for (int32_t i = 0; i < num_months; ++i) { - const base::string16 icu_month(months[i].getBuffer(), months[i].length()); + const base::string16 icu_month( + base::i18n::UnicodeStringToString16(months[i])); if (compare.StringsEqual(icu_month, month)) { *num = i + 1; // Adjust from 0-indexed to 1-indexed. return true; @@ -883,7 +885,7 @@ bool CreditCard::ConvertMonth(const base::string16& month, base::string16 trimmed_month; base::TrimString(month, ASCIIToUTF16("."), &trimmed_month); for (int32_t i = 0; i < num_months; ++i) { - base::string16 icu_month(months[i].getBuffer(), months[i].length()); + base::string16 icu_month(base::i18n::UnicodeStringToString16(months[i])); base::TrimString(icu_month, ASCIIToUTF16("."), &icu_month); if (compare.StringsEqual(icu_month, trimmed_month)) { *num = i + 1; // Adjust from 0-indexed to 1-indexed. diff --git a/chromium/components/autofill/core/browser/credit_card_field.h b/chromium/components/autofill/core/browser/credit_card_field.h index a737add0440..3d462507fb7 100644 --- a/chromium/components/autofill/core/browser/credit_card_field.h +++ b/chromium/components/autofill/core/browser/credit_card_field.h @@ -27,7 +27,7 @@ class CreditCardField : public FormField { void AddClassifications(FieldCandidatesMap* field_candidates) const override; private: - friend class CreditCardFieldTest; + friend class CreditCardFieldTestBase; // Returns true if |scanner| points to a field that looks like a month // <select>. diff --git a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc index 5b44a651d9c..bb7b6b252d1 100644 --- a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc +++ b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc @@ -19,10 +19,10 @@ using base::ASCIIToUTF16; namespace autofill { -class CreditCardFieldTest : public testing::Test { +class CreditCardFieldTestBase { public: - CreditCardFieldTest() {} - ~CreditCardFieldTest() override {} + CreditCardFieldTestBase() {} + ~CreditCardFieldTestBase() {} protected: std::vector<std::unique_ptr<AutofillField>> list_; @@ -59,6 +59,15 @@ class CreditCardFieldTest : public testing::Test { } private: + DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTestBase); +}; + +class CreditCardFieldTest : public CreditCardFieldTestBase, + public testing::Test { + public: + CreditCardFieldTest() {} + + private: DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTest); }; @@ -292,124 +301,144 @@ TEST_F(CreditCardFieldTest, ParseExpMonthYear2) { field_candidates_map_[ASCIIToUTF16("year4")].BestHeuristicType()); } -TEST_F(CreditCardFieldTest, ParseExpField) { - typedef struct { - const std::string label; - const int max_length; - const ServerFieldType expected_prediction; - } TestCase; - - TestCase test_cases[] = { - // General label, no maxlength. - {"Expiration Date", 0, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // General label, maxlength 4. - {"Expiration Date", 4, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // General label, maxlength 5. - {"Expiration Date", 5, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // General label, maxlength 6. - {"Expiration Date", 6, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // General label, maxlength 7. - {"Expiration Date", 7, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // General label, large maxlength. - {"Expiration Date", 12, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - - // Unsupported maxlength, general label. - {"Expiration Date", 3, UNKNOWN_TYPE}, - // Unsupported maxlength, two digit year label. - {"Expiration Date (MM/YY)", 3, UNKNOWN_TYPE}, - // Unsupported maxlength, four digit year label. - {"Expiration Date (MM/YYYY)", 3, UNKNOWN_TYPE}, - - // Two digit year, simple label. - {"MM / YY", 0, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // Two digit year, with slash (MM/YY). - {"Expiration Date (MM/YY)", 0, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // Two digit year, no slash (MMYY). - {"Expiration Date (MMYY)", 4, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // Two digit year, with slash and maxlength (MM/YY). - {"Expiration Date (MM/YY)", 5, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // Two digit year, with slash and large maxlength (MM/YY). - {"Expiration Date (MM/YY)", 12, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - - // Four digit year, simple label. - {"MM / YYYY", 0, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // Four digit year, with slash (MM/YYYY). - {"Expiration Date (MM/YYYY)", 0, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // Four digit year, no slash (MMYYYY). - {"Expiration Date (MMYYYY)", 6, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // Four digit year, with slash and maxlength (MM/YYYY). - {"Expiration Date (MM/YYYY)", 7, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - // Four digit year, with slash and large maxlength (MM/YYYY). - {"Expiration Date (MM/YYYY)", 12, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, - - // Four digit year label with restrictive maxlength (4). - {"Expiration Date (MM/YYYY)", 4, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - // Four digit year label with restrictive maxlength (5). - {"Expiration Date (MM/YYYY)", 5, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, - }; - - for (const TestCase &test_case : test_cases) { - // Clean up after previous test cases. - list_.clear(); - field_.reset(); - field_candidates_map_.clear(); - - FormFieldData field; - field.form_control_type = "text"; - - field.label = ASCIIToUTF16("Name on Card"); - field.name = ASCIIToUTF16("name_on_card"); - list_.push_back( - base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); - - field.label = ASCIIToUTF16("Card Number"); - field.name = ASCIIToUTF16("card_number"); - list_.push_back( - base::MakeUnique<AutofillField>(field, ASCIIToUTF16("num2"))); - - field.label = ASCIIToUTF16(test_case.label); - if (test_case.max_length != 0) { - field.max_length = test_case.max_length; - } - field.name = ASCIIToUTF16("cc_exp"); - list_.push_back( - base::MakeUnique<AutofillField>(field, ASCIIToUTF16("exp3"))); - - Parse(); - - // Assists in identifing which case has failed. - SCOPED_TRACE(test_case.expected_prediction); - SCOPED_TRACE(test_case.max_length); - SCOPED_TRACE(test_case.label); - - if (test_case.expected_prediction == UNKNOWN_TYPE) { - // Expect failure and continue to next test case. - // The expiry date is a required field for credit card forms, and thus the - // parse sets |field_| to nullptr. - EXPECT_EQ(nullptr, field_.get()); - continue; - } +typedef struct { + const std::string label; + const int max_length; + const ServerFieldType expected_prediction; +} ParseExpFieldTestCase; + +class ParseExpFieldTest : public CreditCardFieldTestBase, + public testing::TestWithParam<ParseExpFieldTestCase> { +}; + +TEST_P(ParseExpFieldTest, ParseExpField) { + auto test_case = GetParam(); + // Clean up after previous test cases. + list_.clear(); + field_.reset(); + field_candidates_map_.clear(); + + FormFieldData field; + field.form_control_type = "text"; + + field.label = ASCIIToUTF16("Name on Card"); + field.name = ASCIIToUTF16("name_on_card"); + list_.push_back( + base::MakeUnique<AutofillField>(field, ASCIIToUTF16("name1"))); - // Ensure that the form was determined as valid. - ASSERT_NE(nullptr, field_.get()); - AddClassifications(); - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NAME_FULL, - field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("num2")) != - field_candidates_map_.end()); - EXPECT_EQ(CREDIT_CARD_NUMBER, - field_candidates_map_[ASCIIToUTF16("num2")].BestHeuristicType()); - - ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("exp3")) != - field_candidates_map_.end()); - EXPECT_EQ(test_case.expected_prediction, - field_candidates_map_[ASCIIToUTF16("exp3")].BestHeuristicType()); + field.label = ASCIIToUTF16("Card Number"); + field.name = ASCIIToUTF16("card_number"); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("num2"))); + + field.label = ASCIIToUTF16(test_case.label); + if (test_case.max_length != 0) { + field.max_length = test_case.max_length; + } + field.name = ASCIIToUTF16("cc_exp"); + list_.push_back(base::MakeUnique<AutofillField>(field, ASCIIToUTF16("exp3"))); + + Parse(); + + // Assists in identifing which case has failed. + SCOPED_TRACE(test_case.expected_prediction); + SCOPED_TRACE(test_case.max_length); + SCOPED_TRACE(test_case.label); + + if (test_case.expected_prediction == UNKNOWN_TYPE) { + // Expect failure and continue to next test case. + // The expiry date is a required field for credit card forms, and thus the + // parse sets |field_| to nullptr. + EXPECT_EQ(nullptr, field_.get()); + return; } + + // Ensure that the form was determined as valid. + ASSERT_NE(nullptr, field_.get()); + AddClassifications(); + ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("name1")) != + field_candidates_map_.end()); + EXPECT_EQ(CREDIT_CARD_NAME_FULL, + field_candidates_map_[ASCIIToUTF16("name1")].BestHeuristicType()); + + ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("num2")) != + field_candidates_map_.end()); + EXPECT_EQ(CREDIT_CARD_NUMBER, + field_candidates_map_[ASCIIToUTF16("num2")].BestHeuristicType()); + + ASSERT_TRUE(field_candidates_map_.find(ASCIIToUTF16("exp3")) != + field_candidates_map_.end()); + EXPECT_EQ(test_case.expected_prediction, + field_candidates_map_[ASCIIToUTF16("exp3")].BestHeuristicType()); } +INSTANTIATE_TEST_CASE_P( + CreditCardFieldTest, + ParseExpFieldTest, + testing::Values( + // General label, no maxlength. + ParseExpFieldTestCase{"Expiration Date", 0, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // General label, maxlength 4. + ParseExpFieldTestCase{"Expiration Date", 4, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // General label, maxlength 5. + ParseExpFieldTestCase{"Expiration Date", 5, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // General label, maxlength 6. + ParseExpFieldTestCase{"Expiration Date", 6, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // General label, maxlength 7. + ParseExpFieldTestCase{"Expiration Date", 7, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // General label, large maxlength. + ParseExpFieldTestCase{"Expiration Date", 12, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + + // Unsupported maxlength, general label. + ParseExpFieldTestCase{"Expiration Date", 3, UNKNOWN_TYPE}, + // Unsupported maxlength, two digit year label. + ParseExpFieldTestCase{"Expiration Date (MM/YY)", 3, UNKNOWN_TYPE}, + // Unsupported maxlength, four digit year label. + ParseExpFieldTestCase{"Expiration Date (MM/YYYY)", 3, UNKNOWN_TYPE}, + + // Two digit year, simple label. + ParseExpFieldTestCase{"MM / YY", 0, CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // Two digit year, with slash (MM/YY). + ParseExpFieldTestCase{"Expiration Date (MM/YY)", 0, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // Two digit year, no slash (MMYY). + ParseExpFieldTestCase{"Expiration Date (MMYY)", 4, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // Two digit year, with slash and maxlength (MM/YY). + ParseExpFieldTestCase{"Expiration Date (MM/YY)", 5, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // Two digit year, with slash and large maxlength (MM/YY). + ParseExpFieldTestCase{"Expiration Date (MM/YY)", 12, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + + // Four digit year, simple label. + ParseExpFieldTestCase{"MM / YYYY", 0, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // Four digit year, with slash (MM/YYYY). + ParseExpFieldTestCase{"Expiration Date (MM/YYYY)", 0, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // Four digit year, no slash (MMYYYY). + ParseExpFieldTestCase{"Expiration Date (MMYYYY)", 6, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // Four digit year, with slash and maxlength (MM/YYYY). + ParseExpFieldTestCase{"Expiration Date (MM/YYYY)", 7, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + // Four digit year, with slash and large maxlength (MM/YYYY). + ParseExpFieldTestCase{"Expiration Date (MM/YYYY)", 12, + CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, + + // Four digit year label with restrictive maxlength (4). + ParseExpFieldTestCase{"Expiration Date (MM/YYYY)", 4, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}, + // Four digit year label with restrictive maxlength (5). + ParseExpFieldTestCase{"Expiration Date (MM/YYYY)", 5, + CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR})); + TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) { FormFieldData field; field.form_control_type = "text"; diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc index 993862f6d6d..4e371ec4656 100644 --- a/chromium/components/autofill/core/browser/credit_card_unittest.cc +++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc @@ -158,77 +158,90 @@ TEST(CreditCardTest, AssignmentOperator) { EXPECT_TRUE(a == b); } -TEST(CreditCardTest, SetExpirationYearFromString) { - static const struct { - std::string expiration_year; - int expected_year; - } kTestCases[] = { - // Valid values. - {"2040", 2040}, - {"45", 2045}, - {"045", 2045}, - {"9", 2009}, - - // Unrecognized year values. - {"052045", 0}, - {"123", 0}, - {"y2045", 0}, - }; +struct SetExpirationYearFromStringTestCase { + std::string expiration_year; + int expected_year; +}; - for (const auto& test_case : kTestCases) { - CreditCard card(base::GenerateGUID(), "some origin"); - card.SetExpirationYearFromString(ASCIIToUTF16(test_case.expiration_year)); +class SetExpirationYearFromStringTest + : public testing::TestWithParam<SetExpirationYearFromStringTestCase> {}; - EXPECT_EQ(test_case.expected_year, card.expiration_year()) - << test_case.expiration_year << " " << test_case.expected_year; - } +TEST_P(SetExpirationYearFromStringTest, SetExpirationYearFromString) { + auto test_case = GetParam(); + CreditCard card(base::GenerateGUID(), "some origin"); + card.SetExpirationYearFromString(ASCIIToUTF16(test_case.expiration_year)); + + EXPECT_EQ(test_case.expected_year, card.expiration_year()) + << test_case.expiration_year << " " << test_case.expected_year; } -TEST(CreditCardTest, SetExpirationDateFromString) { - static const struct { - std::string expiration_date; - int expected_month; - int expected_year; - } kTestCases[] = {{"10", 0, 0}, // Too small. - {"1020451", 0, 0}, // Too long. - - // No separators. - {"105", 0, 0}, // Too ambiguous. - {"0545", 5, 2045}, - {"52045", 0, 0}, // Too ambiguous. - {"052045", 5, 2045}, - - // "/" separator. - {"05/45", 5, 2045}, - {"5/2045", 5, 2045}, - {"05/2045", 5, 2045}, - - // "-" separator. - {"05-45", 5, 2045}, - {"5-2045", 5, 2045}, - {"05-2045", 5, 2045}, - - // "|" separator. - {"05|45", 5, 2045}, - {"5|2045", 5, 2045}, - {"05|2045", 5, 2045}, - - // Invalid values. - {"13/2016", 0, 2016}, - {"16/13", 0, 2013}, - {"May-2015", 0, 0}, - {"05-/2045", 0, 0}, - {"05_2045", 0, 0}}; +INSTANTIATE_TEST_CASE_P(CreditCardTest, + SetExpirationYearFromStringTest, + testing::Values( + // Valid values. + SetExpirationYearFromStringTestCase{"2040", 2040}, + SetExpirationYearFromStringTestCase{"45", 2045}, + SetExpirationYearFromStringTestCase{"045", 2045}, + SetExpirationYearFromStringTestCase{"9", 2009}, + + // Unrecognized year values. + SetExpirationYearFromStringTestCase{"052045", 0}, + SetExpirationYearFromStringTestCase{"123", 0}, + SetExpirationYearFromStringTestCase{"y2045", 0})); + +struct SetExpirationDateFromStringTestCase { + std::string expiration_date; + int expected_month; + int expected_year; +}; - for (const auto& test_case : kTestCases) { - CreditCard card(base::GenerateGUID(), "some origin"); - card.SetExpirationDateFromString(ASCIIToUTF16(test_case.expiration_date)); +class SetExpirationDateFromStringTest + : public testing::TestWithParam<SetExpirationDateFromStringTestCase> {}; - EXPECT_EQ(test_case.expected_month, card.expiration_month()); - EXPECT_EQ(test_case.expected_year, card.expiration_year()); - } +TEST_P(SetExpirationDateFromStringTest, SetExpirationDateFromString) { + auto test_case = GetParam(); + CreditCard card(base::GenerateGUID(), "some origin"); + card.SetExpirationDateFromString(ASCIIToUTF16(test_case.expiration_date)); + + EXPECT_EQ(test_case.expected_month, card.expiration_month()); + EXPECT_EQ(test_case.expected_year, card.expiration_year()); } +INSTANTIATE_TEST_CASE_P( + CreditCardTest, + SetExpirationDateFromStringTest, + testing::Values( + SetExpirationDateFromStringTestCase{"10", 0, 0}, // Too small. + SetExpirationDateFromStringTestCase{"1020451", 0, 0}, // Too long. + + // No separators. + SetExpirationDateFromStringTestCase{"105", 0, 0}, // Too ambiguous. + SetExpirationDateFromStringTestCase{"0545", 5, 2045}, + SetExpirationDateFromStringTestCase{"52045", 0, 0}, // Too ambiguous. + SetExpirationDateFromStringTestCase{"052045", 5, 2045}, + + // "/" separator. + SetExpirationDateFromStringTestCase{"05/45", 5, 2045}, + SetExpirationDateFromStringTestCase{"5/2045", 5, 2045}, + SetExpirationDateFromStringTestCase{"05/2045", 5, 2045}, + + // "-" separator. + SetExpirationDateFromStringTestCase{"05-45", 5, 2045}, + SetExpirationDateFromStringTestCase{"5-2045", 5, 2045}, + SetExpirationDateFromStringTestCase{"05-2045", 5, 2045}, + + // "|" separator. + SetExpirationDateFromStringTestCase{"05|45", 5, 2045}, + SetExpirationDateFromStringTestCase{"5|2045", 5, 2045}, + SetExpirationDateFromStringTestCase{"05|2045", 5, 2045}, + + // Invalid values. + SetExpirationDateFromStringTestCase{"13/2016", 0, 2016}, + SetExpirationDateFromStringTestCase{"16/13", 0, 2013}, + SetExpirationDateFromStringTestCase{"May-2015", 0, 0}, + SetExpirationDateFromStringTestCase{"05-/2045", 0, 0}, + SetExpirationDateFromStringTestCase{"05_2045", 0, 0})); + TEST(CreditCardTest, Copy) { CreditCard a(base::GenerateGUID(), "https://www.example.com"); test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010"); @@ -238,73 +251,85 @@ TEST(CreditCardTest, Copy) { EXPECT_TRUE(a == b); } -TEST(CreditCardTest, IsLocalDuplicateOfServerCard) { - struct { - CreditCard::RecordType first_card_record_type; - const char* first_card_name; - const char* first_card_number; - const char* first_card_exp_mo; - const char* first_card_exp_yr; - - CreditCard::RecordType second_card_record_type; - const char* second_card_name; - const char* second_card_number; - const char* second_card_exp_mo; - const char* second_card_exp_yr; - const char* second_card_type; - - bool is_local_duplicate; - } test_cases[] = { - { LOCAL_CARD, "", "", "", "", - LOCAL_CARD, "", "", "", "", nullptr, false }, - { LOCAL_CARD, "", "", "", "", - FULL_SERVER_CARD, "", "", "", "", nullptr, true}, - { FULL_SERVER_CARD, "", "", "", "", - FULL_SERVER_CARD, "", "", "", "", nullptr, false}, - { LOCAL_CARD, "John Dillinger", "423456789012", "01", "2010", - FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", nullptr, - true }, - { LOCAL_CARD, "J Dillinger", "423456789012", "01", "2010", - FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", nullptr, - false }, - { LOCAL_CARD, "", "423456789012", "01", "2010", - FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", nullptr, - true }, - { LOCAL_CARD, "", "423456789012", "", "", - FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", nullptr, - true }, - { LOCAL_CARD, "", "423456789012", "", "", - MASKED_SERVER_CARD, "John Dillinger", "9012", "01", "2010", kVisaCard, - true }, - { LOCAL_CARD, "", "423456789012", "", "", - MASKED_SERVER_CARD, "John Dillinger", "9012", "01", "2010", kMasterCard, - false }, - { LOCAL_CARD, "John Dillinger", "4234-5678-9012", "01", "2010", - FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", nullptr, - true }, - }; +struct IsLocalDuplicateOfServerCardTestCase { + CreditCard::RecordType first_card_record_type; + const char* first_card_name; + const char* first_card_number; + const char* first_card_exp_mo; + const char* first_card_exp_yr; + + CreditCard::RecordType second_card_record_type; + const char* second_card_name; + const char* second_card_number; + const char* second_card_exp_mo; + const char* second_card_exp_yr; + const char* second_card_type; + + bool is_local_duplicate; +}; - for (const auto& test_case : test_cases) { - CreditCard a(base::GenerateGUID(), std::string()); - a.set_record_type(test_case.first_card_record_type); - test::SetCreditCardInfo( - &a, test_case.first_card_name, test_case.first_card_number, - test_case.first_card_exp_mo, test_case.first_card_exp_yr); +class IsLocalDuplicateOfServerCardTest + : public testing::TestWithParam<IsLocalDuplicateOfServerCardTestCase> {}; - CreditCard b(base::GenerateGUID(), std::string()); - b.set_record_type(test_case.second_card_record_type); - test::SetCreditCardInfo( - &b, test_case.second_card_name, test_case.second_card_number, - test_case.second_card_exp_mo, test_case.second_card_exp_yr); +TEST_P(IsLocalDuplicateOfServerCardTest, IsLocalDuplicateOfServerCard) { + auto test_case = GetParam(); + CreditCard a(base::GenerateGUID(), std::string()); + a.set_record_type(test_case.first_card_record_type); + test::SetCreditCardInfo( + &a, test_case.first_card_name, test_case.first_card_number, + test_case.first_card_exp_mo, test_case.first_card_exp_yr); - if (test_case.second_card_record_type == CreditCard::MASKED_SERVER_CARD) - b.SetTypeForMaskedCard(test_case.second_card_type); + CreditCard b(base::GenerateGUID(), std::string()); + b.set_record_type(test_case.second_card_record_type); + test::SetCreditCardInfo( + &b, test_case.second_card_name, test_case.second_card_number, + test_case.second_card_exp_mo, test_case.second_card_exp_yr); - EXPECT_EQ(test_case.is_local_duplicate, a.IsLocalDuplicateOfServerCard(b)) - << " when comparing cards " << a.Label() << " and " << b.Label(); - } + if (test_case.second_card_record_type == CreditCard::MASKED_SERVER_CARD) + b.SetTypeForMaskedCard(test_case.second_card_type); + + EXPECT_EQ(test_case.is_local_duplicate, a.IsLocalDuplicateOfServerCard(b)) + << " when comparing cards " << a.Label() << " and " << b.Label(); } +INSTANTIATE_TEST_CASE_P( + CreditCardTest, + IsLocalDuplicateOfServerCardTest, + testing::Values( + IsLocalDuplicateOfServerCardTestCase{LOCAL_CARD, "", "", "", "", + LOCAL_CARD, "", "", "", "", + nullptr, false}, + IsLocalDuplicateOfServerCardTestCase{LOCAL_CARD, "", "", "", "", + FULL_SERVER_CARD, "", "", "", "", + nullptr, true}, + IsLocalDuplicateOfServerCardTestCase{FULL_SERVER_CARD, "", "", "", "", + FULL_SERVER_CARD, "", "", "", "", + nullptr, false}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "John Dillinger", "423456789012", "01", "2010", + FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", + nullptr, true}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "J Dillinger", "423456789012", "01", "2010", + FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", + nullptr, false}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "", "423456789012", "01", "2010", FULL_SERVER_CARD, + "John Dillinger", "423456789012", "01", "2010", nullptr, true}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "", "423456789012", "", "", FULL_SERVER_CARD, + "John Dillinger", "423456789012", "01", "2010", nullptr, true}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "", "423456789012", "", "", MASKED_SERVER_CARD, + "John Dillinger", "9012", "01", "2010", kVisaCard, true}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "", "423456789012", "", "", MASKED_SERVER_CARD, + "John Dillinger", "9012", "01", "2010", kMasterCard, false}, + IsLocalDuplicateOfServerCardTestCase{ + LOCAL_CARD, "John Dillinger", "4234-5678-9012", "01", "2010", + FULL_SERVER_CARD, "John Dillinger", "423456789012", "01", "2010", + nullptr, true})); + TEST(CreditCardTest, HasSameNumberAs) { CreditCard a(base::GenerateGUID(), std::string()); CreditCard b(base::GenerateGUID(), std::string()); @@ -603,178 +628,223 @@ TEST(CreditCardTest, CreditCardVerificationCode) { EXPECT_EQ(base::string16(), card.GetRawInfo(CREDIT_CARD_VERIFICATION_CODE)); } +struct GetCreditCardTypeTestCase { + std::string card_number; + std::string type; + bool is_valid; +}; -TEST(CreditCardTest, GetCreditCardType) { - struct { - std::string card_number; - std::string type; - bool is_valid; - } test_cases[] = { - // The relevant sample numbers from - // http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm - { "378282246310005", kAmericanExpressCard, true }, - { "371449635398431", kAmericanExpressCard, true }, - { "378734493671000", kAmericanExpressCard, true }, - { "30569309025904", kDinersCard, true }, - { "38520000023237", kDinersCard, true }, - { "6011111111111117", kDiscoverCard, true }, - { "6011000990139424", kDiscoverCard, true }, - { "3530111333300000", kJCBCard, true }, - { "3566002020360505", kJCBCard, true }, - { "5555555555554444", kMasterCard, true }, - { "5105105105105100", kMasterCard, true }, - { "4111111111111111", kVisaCard, true }, - { "4012888888881881", kVisaCard, true }, - { "4222222222222", kVisaCard, true }, - - // The relevant sample numbers from - // https://www.auricsystems.com/sample-credit-card-numbers/ - { "343434343434343", kAmericanExpressCard, true }, - { "371144371144376", kAmericanExpressCard, true }, - { "341134113411347", kAmericanExpressCard, true }, - { "36438936438936", kDinersCard, true }, - { "36110361103612", kDinersCard, true }, - { "36111111111111", kDinersCard, true }, - { "6011016011016011", kDiscoverCard, true }, - { "6011000990139424", kDiscoverCard, true }, - { "6011000000000004", kDiscoverCard, true }, - { "6011000995500000", kDiscoverCard, true }, - { "6500000000000002", kDiscoverCard, true }, - { "3566002020360505", kJCBCard, true }, - { "3528000000000007", kJCBCard, true }, - { "5500005555555559", kMasterCard, true }, - { "5555555555555557", kMasterCard, true }, - { "5454545454545454", kMasterCard, true }, - { "5555515555555551", kMasterCard, true }, - { "5405222222222226", kMasterCard, true }, - { "5478050000000007", kMasterCard, true }, - { "5111005111051128", kMasterCard, true }, - { "5112345112345114", kMasterCard, true }, - { "5115915115915118", kMasterCard, true }, - { "6247130048162403", kUnionPay, true }, - { "6247130048162403", kUnionPay, true }, - { "622384452162063648", kUnionPay, true }, - { "2204883716636153", kMirCard, true }, - { "2200111234567898", kMirCard, true }, - { "2200481349288130", kMirCard, true }, - - // Empty string - { std::string(), kGenericCard, false }, - - // Non-numeric - { "garbage", kGenericCard, false }, - { "4garbage", kVisaCard, false }, - - // Fails Luhn check. - { "4111111111111112", kVisaCard, false }, - { "6247130048162413", kUnionPay, false }, - { "2204883716636154", kMirCard, false }, - - // Invalid length. - { "3434343434343434", kAmericanExpressCard, false }, - { "411111111111116", kVisaCard, false }, - { "220011123456783", kMirCard, false }, - - // Issuer Identification Numbers (IINs) that Chrome recognizes. - { "4", kVisaCard, false }, - { "22", kMirCard, false }, - { "34", kAmericanExpressCard, false }, - { "37", kAmericanExpressCard, false }, - { "300", kDinersCard, false }, - { "301", kDinersCard, false }, - { "302", kDinersCard, false }, - { "303", kDinersCard, false }, - { "304", kDinersCard, false }, - { "305", kDinersCard, false }, - { "3095", kDinersCard, false }, - { "36", kDinersCard, false }, - { "38", kDinersCard, false }, - { "39", kDinersCard, false }, - { "6011", kDiscoverCard, false }, - { "644", kDiscoverCard, false }, - { "645", kDiscoverCard, false }, - { "646", kDiscoverCard, false }, - { "647", kDiscoverCard, false }, - { "648", kDiscoverCard, false }, - { "649", kDiscoverCard, false }, - { "65", kDiscoverCard, false }, - { "3528", kJCBCard, false }, - { "3531", kJCBCard, false }, - { "3589", kJCBCard, false }, - { "51", kMasterCard, false }, - { "52", kMasterCard, false }, - { "53", kMasterCard, false }, - { "54", kMasterCard, false }, - { "55", kMasterCard, false }, - { "62", kUnionPay, false }, - - // Not enough data to determine an IIN uniquely. - { "2", kGenericCard, false }, - { "3", kGenericCard, false }, - { "30", kGenericCard, false }, - { "309", kGenericCard, false }, - { "35", kGenericCard, false }, - { "5", kGenericCard, false }, - { "6", kGenericCard, false }, - { "60", kGenericCard, false }, - { "601", kGenericCard, false }, - { "64", kGenericCard, false }, - - // Unknown IINs. - { "0", kGenericCard, false }, - { "1", kGenericCard, false }, - { "306", kGenericCard, false }, - { "307", kGenericCard, false }, - { "308", kGenericCard, false }, - { "3091", kGenericCard, false }, - { "3094", kGenericCard, false }, - { "3096", kGenericCard, false }, - { "31", kGenericCard, false }, - { "32", kGenericCard, false }, - { "33", kGenericCard, false }, - { "351", kGenericCard, false }, - { "3527", kGenericCard, false }, - { "359", kGenericCard, false }, - { "50", kGenericCard, false }, - { "56", kGenericCard, false }, - { "57", kGenericCard, false }, - { "58", kGenericCard, false }, - { "59", kGenericCard, false }, - { "600", kGenericCard, false }, - { "602", kGenericCard, false }, - { "603", kGenericCard, false }, - { "604", kGenericCard, false }, - { "605", kGenericCard, false }, - { "606", kGenericCard, false }, - { "607", kGenericCard, false }, - { "608", kGenericCard, false }, - { "609", kGenericCard, false }, - { "61", kGenericCard, false }, - { "63", kGenericCard, false }, - { "640", kGenericCard, false }, - { "641", kGenericCard, false }, - { "642", kGenericCard, false }, - { "643", kGenericCard, false }, - { "66", kGenericCard, false }, - { "67", kGenericCard, false }, - { "68", kGenericCard, false }, - { "69", kGenericCard, false }, - { "7", kGenericCard, false }, - { "8", kGenericCard, false }, - { "9", kGenericCard, false }, - - // Oddball case: Unknown issuer, but valid Luhn check and plausible length. - { "7000700070007000", kGenericCard, true }, - }; +// We are doing batches here because INSTANTIATE_TEST_CASE_P has a +// 50 upper limit. +class GetCreditCardTypeTestBatch1 + : public testing::TestWithParam<GetCreditCardTypeTestCase> {}; + +TEST_P(GetCreditCardTypeTestBatch1, GetCreditCardType) { + auto test_case = GetParam(); + base::string16 card_number = ASCIIToUTF16(test_case.card_number); + SCOPED_TRACE(card_number); + EXPECT_EQ(test_case.type, CreditCard::GetCreditCardType(card_number)); + EXPECT_EQ(test_case.is_valid, IsValidCreditCardNumber(card_number)); +} - for (const auto& test_case : test_cases) { - base::string16 card_number = ASCIIToUTF16(test_case.card_number); - SCOPED_TRACE(card_number); - EXPECT_EQ(test_case.type, CreditCard::GetCreditCardType(card_number)); - EXPECT_EQ(test_case.is_valid, IsValidCreditCardNumber(card_number)); - } +INSTANTIATE_TEST_CASE_P( + CreditCardTest, + GetCreditCardTypeTestBatch1, + testing::Values( + // The relevant sample numbers from + // http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm + GetCreditCardTypeTestCase{"378282246310005", kAmericanExpressCard, + true}, + GetCreditCardTypeTestCase{"371449635398431", kAmericanExpressCard, + true}, + GetCreditCardTypeTestCase{"378734493671000", kAmericanExpressCard, + true}, + GetCreditCardTypeTestCase{"30569309025904", kDinersCard, true}, + GetCreditCardTypeTestCase{"38520000023237", kDinersCard, true}, + GetCreditCardTypeTestCase{"6011111111111117", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"6011000990139424", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"3530111333300000", kJCBCard, true}, + GetCreditCardTypeTestCase{"3566002020360505", kJCBCard, true}, + GetCreditCardTypeTestCase{"5555555555554444", kMasterCard, true}, + GetCreditCardTypeTestCase{"5105105105105100", kMasterCard, true}, + GetCreditCardTypeTestCase{"4111111111111111", kVisaCard, true}, + GetCreditCardTypeTestCase{"4012888888881881", kVisaCard, true}, + GetCreditCardTypeTestCase{"4222222222222", kVisaCard, true}, + + // The relevant sample numbers from + // https://www.auricsystems.com/sample-credit-card-numbers/ + GetCreditCardTypeTestCase{"343434343434343", kAmericanExpressCard, + true}, + GetCreditCardTypeTestCase{"371144371144376", kAmericanExpressCard, + true}, + GetCreditCardTypeTestCase{"341134113411347", kAmericanExpressCard, + true}, + GetCreditCardTypeTestCase{"36438936438936", kDinersCard, true}, + GetCreditCardTypeTestCase{"36110361103612", kDinersCard, true}, + GetCreditCardTypeTestCase{"36111111111111", kDinersCard, true}, + GetCreditCardTypeTestCase{"6011016011016011", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"6011000990139424", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"6011000000000004", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"6011000995500000", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"6500000000000002", kDiscoverCard, true}, + GetCreditCardTypeTestCase{"3566002020360505", kJCBCard, true}, + GetCreditCardTypeTestCase{"3528000000000007", kJCBCard, true}, + GetCreditCardTypeTestCase{"5500005555555559", kMasterCard, true}, + GetCreditCardTypeTestCase{"5555555555555557", kMasterCard, true}, + GetCreditCardTypeTestCase{"5454545454545454", kMasterCard, true}, + GetCreditCardTypeTestCase{"5555515555555551", kMasterCard, true}, + GetCreditCardTypeTestCase{"5405222222222226", kMasterCard, true}, + GetCreditCardTypeTestCase{"5478050000000007", kMasterCard, true}, + GetCreditCardTypeTestCase{"5111005111051128", kMasterCard, true}, + GetCreditCardTypeTestCase{"5112345112345114", kMasterCard, true}, + GetCreditCardTypeTestCase{"5115915115915118", kMasterCard, true}, + GetCreditCardTypeTestCase{"6247130048162403", kUnionPay, true}, + GetCreditCardTypeTestCase{"6247130048162403", kUnionPay, true}, + GetCreditCardTypeTestCase{"622384452162063648", kUnionPay, true}, + GetCreditCardTypeTestCase{"2204883716636153", kMirCard, true}, + GetCreditCardTypeTestCase{"2200111234567898", kMirCard, true}, + GetCreditCardTypeTestCase{"2200481349288130", kMirCard, true}, + + // Empty string + GetCreditCardTypeTestCase{std::string(), kGenericCard, false}, + + // Non-numeric + GetCreditCardTypeTestCase{"garbage", kGenericCard, false}, + GetCreditCardTypeTestCase{"4garbage", kVisaCard, false}, + + // Fails Luhn check. + GetCreditCardTypeTestCase{"4111111111111112", kVisaCard, false}, + GetCreditCardTypeTestCase{"6247130048162413", kUnionPay, false}, + GetCreditCardTypeTestCase{"2204883716636154", kMirCard, false})); + +class GetCreditCardTypeTestBatch2 + : public testing::TestWithParam<GetCreditCardTypeTestCase> {}; + +TEST_P(GetCreditCardTypeTestBatch2, GetCreditCardType) { + auto test_case = GetParam(); + base::string16 card_number = ASCIIToUTF16(test_case.card_number); + SCOPED_TRACE(card_number); + EXPECT_EQ(test_case.type, CreditCard::GetCreditCardType(card_number)); + EXPECT_EQ(test_case.is_valid, IsValidCreditCardNumber(card_number)); +} + +INSTANTIATE_TEST_CASE_P( + CreditCardTest, + GetCreditCardTypeTestBatch2, + testing::Values( + // Invalid length. + GetCreditCardTypeTestCase{"3434343434343434", kAmericanExpressCard, + false}, + GetCreditCardTypeTestCase{"411111111111116", kVisaCard, false}, + GetCreditCardTypeTestCase{"220011123456783", kMirCard, false}, + + // Issuer Identification Numbers (IINs) that Chrome recognizes. + GetCreditCardTypeTestCase{"4", kVisaCard, false}, + GetCreditCardTypeTestCase{"22", kMirCard, false}, + GetCreditCardTypeTestCase{"34", kAmericanExpressCard, false}, + GetCreditCardTypeTestCase{"37", kAmericanExpressCard, false}, + GetCreditCardTypeTestCase{"300", kDinersCard, false}, + GetCreditCardTypeTestCase{"301", kDinersCard, false}, + GetCreditCardTypeTestCase{"302", kDinersCard, false}, + GetCreditCardTypeTestCase{"303", kDinersCard, false}, + GetCreditCardTypeTestCase{"304", kDinersCard, false}, + GetCreditCardTypeTestCase{"305", kDinersCard, false}, + GetCreditCardTypeTestCase{"3095", kDinersCard, false}, + GetCreditCardTypeTestCase{"36", kDinersCard, false}, + GetCreditCardTypeTestCase{"38", kDinersCard, false}, + GetCreditCardTypeTestCase{"39", kDinersCard, false}, + GetCreditCardTypeTestCase{"6011", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"644", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"645", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"646", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"647", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"648", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"649", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"65", kDiscoverCard, false}, + GetCreditCardTypeTestCase{"3528", kJCBCard, false}, + GetCreditCardTypeTestCase{"3531", kJCBCard, false}, + GetCreditCardTypeTestCase{"3589", kJCBCard, false}, + GetCreditCardTypeTestCase{"51", kMasterCard, false}, + GetCreditCardTypeTestCase{"52", kMasterCard, false}, + GetCreditCardTypeTestCase{"53", kMasterCard, false}, + GetCreditCardTypeTestCase{"54", kMasterCard, false}, + GetCreditCardTypeTestCase{"55", kMasterCard, false}, + GetCreditCardTypeTestCase{"62", kUnionPay, false}, + + // Not enough data to determine an IIN uniquely. + GetCreditCardTypeTestCase{"2", kGenericCard, false}, + GetCreditCardTypeTestCase{"3", kGenericCard, false}, + GetCreditCardTypeTestCase{"30", kGenericCard, false}, + GetCreditCardTypeTestCase{"309", kGenericCard, false}, + GetCreditCardTypeTestCase{"35", kGenericCard, false}, + GetCreditCardTypeTestCase{"5", kGenericCard, false}, + GetCreditCardTypeTestCase{"6", kGenericCard, false}, + GetCreditCardTypeTestCase{"60", kGenericCard, false}, + GetCreditCardTypeTestCase{"601", kGenericCard, false}, + GetCreditCardTypeTestCase{"64", kGenericCard, false})); + +class GetCreditCardTypeTestBatch3 + : public testing::TestWithParam<GetCreditCardTypeTestCase> {}; + +TEST_P(GetCreditCardTypeTestBatch3, GetCreditCardType) { + auto test_case = GetParam(); + base::string16 card_number = ASCIIToUTF16(test_case.card_number); + SCOPED_TRACE(card_number); + EXPECT_EQ(test_case.type, CreditCard::GetCreditCardType(card_number)); + EXPECT_EQ(test_case.is_valid, IsValidCreditCardNumber(card_number)); } +INSTANTIATE_TEST_CASE_P( + CreditCardTest, + GetCreditCardTypeTestBatch3, + testing::Values( + // Unknown IINs. + GetCreditCardTypeTestCase{"0", kGenericCard, false}, + GetCreditCardTypeTestCase{"1", kGenericCard, false}, + GetCreditCardTypeTestCase{"306", kGenericCard, false}, + GetCreditCardTypeTestCase{"307", kGenericCard, false}, + GetCreditCardTypeTestCase{"308", kGenericCard, false}, + GetCreditCardTypeTestCase{"3091", kGenericCard, false}, + GetCreditCardTypeTestCase{"3094", kGenericCard, false}, + GetCreditCardTypeTestCase{"3096", kGenericCard, false}, + GetCreditCardTypeTestCase{"31", kGenericCard, false}, + GetCreditCardTypeTestCase{"32", kGenericCard, false}, + GetCreditCardTypeTestCase{"33", kGenericCard, false}, + GetCreditCardTypeTestCase{"351", kGenericCard, false}, + GetCreditCardTypeTestCase{"3527", kGenericCard, false}, + GetCreditCardTypeTestCase{"359", kGenericCard, false}, + GetCreditCardTypeTestCase{"50", kGenericCard, false}, + GetCreditCardTypeTestCase{"56", kGenericCard, false}, + GetCreditCardTypeTestCase{"57", kGenericCard, false}, + GetCreditCardTypeTestCase{"58", kGenericCard, false}, + GetCreditCardTypeTestCase{"59", kGenericCard, false}, + GetCreditCardTypeTestCase{"600", kGenericCard, false}, + GetCreditCardTypeTestCase{"602", kGenericCard, false}, + GetCreditCardTypeTestCase{"603", kGenericCard, false}, + GetCreditCardTypeTestCase{"604", kGenericCard, false}, + GetCreditCardTypeTestCase{"605", kGenericCard, false}, + GetCreditCardTypeTestCase{"606", kGenericCard, false}, + GetCreditCardTypeTestCase{"607", kGenericCard, false}, + GetCreditCardTypeTestCase{"608", kGenericCard, false}, + GetCreditCardTypeTestCase{"609", kGenericCard, false}, + GetCreditCardTypeTestCase{"61", kGenericCard, false}, + GetCreditCardTypeTestCase{"63", kGenericCard, false}, + GetCreditCardTypeTestCase{"640", kGenericCard, false}, + GetCreditCardTypeTestCase{"641", kGenericCard, false}, + GetCreditCardTypeTestCase{"642", kGenericCard, false}, + GetCreditCardTypeTestCase{"643", kGenericCard, false}, + GetCreditCardTypeTestCase{"66", kGenericCard, false}, + GetCreditCardTypeTestCase{"67", kGenericCard, false}, + GetCreditCardTypeTestCase{"68", kGenericCard, false}, + GetCreditCardTypeTestCase{"69", kGenericCard, false}, + GetCreditCardTypeTestCase{"7", kGenericCard, false}, + GetCreditCardTypeTestCase{"8", kGenericCard, false}, + GetCreditCardTypeTestCase{"9", kGenericCard, false}, + + // Oddball case: Unknown issuer, but valid Luhn check and plausible + // length. + GetCreditCardTypeTestCase{"7000700070007000", kGenericCard, true})); + TEST(CreditCardTest, LastFourDigits) { CreditCard card(base::GenerateGUID(), "https://www.example.com/"); ASSERT_EQ(base::string16(), card.LastFourDigits()); @@ -791,104 +861,149 @@ TEST(CreditCardTest, LastFourDigits) { } // Verifies that a credit card should be updated. -TEST(CreditCardTest, ShouldUpdateExpiration) { - base::Time now = base::Time::Now(); - - base::Time::Exploded last_year; - (now - base::TimeDelta::FromDays(365)).LocalExplode(&last_year); - - base::Time::Exploded last_month; - (now - base::TimeDelta::FromDays(31)).LocalExplode(&last_month); - - base::Time::Exploded current; - now.LocalExplode(¤t); - - base::Time::Exploded next_month; - (now + base::TimeDelta::FromDays(31)).LocalExplode(&next_month); +struct ShouldUpdateExpirationTestCase { + bool should_update_expiration; + int month; + int year; + CreditCard::RecordType record_type; + CreditCard::ServerStatus server_status; +}; - base::Time::Exploded next_year; - (now + base::TimeDelta::FromDays(365)).LocalExplode(&next_year); +class ShouldUpdateExpirationTest + : public testing::TestWithParam<ShouldUpdateExpirationTestCase> {}; + +class TestingTimes { + public: + TestingTimes() { + now_ = base::Time::Now(); + (now_ - base::TimeDelta::FromDays(365)).LocalExplode(&last_year_); + (now_ - base::TimeDelta::FromDays(31)).LocalExplode(&last_month_); + now_.LocalExplode(¤t_); + (now_ + base::TimeDelta::FromDays(31)).LocalExplode(&next_month_); + (now_ + base::TimeDelta::FromDays(365)).LocalExplode(&next_year_); + } - static const struct { - bool should_update_expiration; - int month; - int year; - CreditCard::RecordType record_type; - CreditCard::ServerStatus server_status; - } kTestCases[] = { + base::Time now_; + base::Time::Exploded last_year_; + base::Time::Exploded last_month_; + base::Time::Exploded current_; + base::Time::Exploded next_month_; + base::Time::Exploded next_year_; +}; - // Cards that expired last year should always be updated. - {true, last_year.month, last_year.year, CreditCard::LOCAL_CARD}, - {true, last_year.month, last_year.year, CreditCard::FULL_SERVER_CARD, - CreditCard::OK}, - {true, last_year.month, last_year.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::OK}, - {true, last_year.month, last_year.year, CreditCard::FULL_SERVER_CARD, - CreditCard::EXPIRED}, - {true, last_year.month, last_year.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::EXPIRED}, - - // Cards that expired last month should always be updated. - {true, last_month.month, last_month.year, CreditCard::LOCAL_CARD}, - {true, last_month.month, last_month.year, CreditCard::FULL_SERVER_CARD, - CreditCard::OK}, - {true, last_month.month, last_month.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::OK}, - {true, last_month.month, last_month.year, CreditCard::FULL_SERVER_CARD, - CreditCard::EXPIRED}, - {true, last_month.month, last_month.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::EXPIRED}, - - // Cards that expire this month should be updated only if the server - // status is EXPIRED. - {false, current.month, current.year, CreditCard::LOCAL_CARD}, - {false, current.month, current.year, CreditCard::FULL_SERVER_CARD, - CreditCard::OK}, - {false, current.month, current.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::OK}, - {true, current.month, current.year, CreditCard::FULL_SERVER_CARD, - CreditCard::EXPIRED}, - {true, current.month, current.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::EXPIRED}, - - // Cards that expire next month should be updated only if the server - // status is EXPIRED. - {false, next_month.month, next_month.year, CreditCard::LOCAL_CARD}, - {false, next_month.month, next_month.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::OK}, - {false, next_month.month, next_month.year, CreditCard::FULL_SERVER_CARD, - CreditCard::OK}, - {true, next_month.month, next_month.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::EXPIRED}, - {true, next_month.month, next_month.year, CreditCard::FULL_SERVER_CARD, - CreditCard::EXPIRED}, - - // Cards that expire next year should be updated only if the server status - // is EXPIRED. - {false, next_year.month, next_year.year, CreditCard::LOCAL_CARD}, - {false, next_year.month, next_year.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::OK}, - {false, next_year.month, next_year.year, CreditCard::FULL_SERVER_CARD, - CreditCard::OK}, - {true, next_year.month, next_year.year, CreditCard::MASKED_SERVER_CARD, - CreditCard::EXPIRED}, - {true, next_year.month, next_year.year, CreditCard::FULL_SERVER_CARD, - CreditCard::EXPIRED}, - }; +TestingTimes testingTimes; - for (const auto& test_case : kTestCases) { - CreditCard card; - card.SetExpirationMonth(test_case.month); - card.SetExpirationYear(test_case.year); - card.set_record_type(test_case.record_type); - if (card.record_type() != CreditCard::LOCAL_CARD) - card.SetServerStatus(test_case.server_status); - - EXPECT_EQ(test_case.should_update_expiration, - card.ShouldUpdateExpiration(now)); - } +TEST_P(ShouldUpdateExpirationTest, ShouldUpdateExpiration) { + auto test_case = GetParam(); + CreditCard card; + card.SetExpirationMonth(test_case.month); + card.SetExpirationYear(test_case.year); + card.set_record_type(test_case.record_type); + if (card.record_type() != CreditCard::LOCAL_CARD) + card.SetServerStatus(test_case.server_status); + + EXPECT_EQ(test_case.should_update_expiration, + card.ShouldUpdateExpiration(testingTimes.now_)); } +INSTANTIATE_TEST_CASE_P( + CreditCardTest, + ShouldUpdateExpirationTest, + testing::Values( + // Cards that expired last year should always be updated. + ShouldUpdateExpirationTestCase{true, testingTimes.last_year_.month, + testingTimes.last_year_.year, + CreditCard::LOCAL_CARD}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_year_.month, testingTimes.last_year_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_year_.month, testingTimes.last_year_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_year_.month, testingTimes.last_year_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::EXPIRED}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_year_.month, testingTimes.last_year_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::EXPIRED}, + + // Cards that expired last month should always be updated. + ShouldUpdateExpirationTestCase{true, testingTimes.last_month_.month, + testingTimes.last_month_.year, + CreditCard::LOCAL_CARD}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_month_.month, testingTimes.last_month_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_month_.month, testingTimes.last_month_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_month_.month, testingTimes.last_month_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::EXPIRED}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.last_month_.month, testingTimes.last_month_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::EXPIRED}, + + // Cards that expire this month should be updated only if the server + // status is EXPIRED. + ShouldUpdateExpirationTestCase{false, testingTimes.current_.month, + testingTimes.current_.year, + CreditCard::LOCAL_CARD}, + ShouldUpdateExpirationTestCase{ + false, testingTimes.current_.month, testingTimes.current_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + false, testingTimes.current_.month, testingTimes.current_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.current_.month, testingTimes.current_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::EXPIRED}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.current_.month, testingTimes.current_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::EXPIRED}, + + // Cards that expire next month should be updated only if the server + // status is EXPIRED. + ShouldUpdateExpirationTestCase{false, testingTimes.next_month_.month, + testingTimes.next_month_.year, + CreditCard::LOCAL_CARD}, + ShouldUpdateExpirationTestCase{false, testingTimes.next_month_.month, + testingTimes.next_month_.year, + CreditCard::MASKED_SERVER_CARD, + CreditCard::OK}, + ShouldUpdateExpirationTestCase{false, testingTimes.next_month_.month, + testingTimes.next_month_.year, + CreditCard::FULL_SERVER_CARD, + CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.next_month_.month, testingTimes.next_month_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::EXPIRED}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.next_month_.month, testingTimes.next_month_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::EXPIRED}, + + // Cards that expire next year should be updated only if the server + // status is EXPIRED. + ShouldUpdateExpirationTestCase{false, testingTimes.next_year_.month, + testingTimes.next_year_.year, + CreditCard::LOCAL_CARD}, + ShouldUpdateExpirationTestCase{ + false, testingTimes.next_year_.month, testingTimes.next_year_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + false, testingTimes.next_year_.month, testingTimes.next_year_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::OK}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.next_year_.month, testingTimes.next_year_.year, + CreditCard::MASKED_SERVER_CARD, CreditCard::EXPIRED}, + ShouldUpdateExpirationTestCase{ + true, testingTimes.next_year_.month, testingTimes.next_year_.year, + CreditCard::FULL_SERVER_CARD, CreditCard::EXPIRED})); + +// TODO(wuandy): rewriting below test with INSTANTIATE_TEST_CASE_P seems to +// trigger a complaint on windows compilers. Removing it and revert to +// original test for now. + // Test that credit card last used date suggestion can be generated correctly // in different variations. TEST(CreditCardTest, GetLastUsedDateForDisplay) { diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h index 6c2aa807752..cee305fcda5 100644 --- a/chromium/components/autofill/core/browser/field_types.h +++ b/chromium/components/autofill/core/browser/field_types.h @@ -234,7 +234,10 @@ enum HtmlFieldType { HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, - // Non standard autcomplete types. + // Universal Payment Interface - Virtual Payment Address. + HTML_TYPE_UPI_VPA, + + // Non-standard autocomplete types. HTML_TYPE_UNRECOGNIZED, }; diff --git a/chromium/components/autofill/core/browser/form_group.cc b/chromium/components/autofill/core/browser/form_group.cc index 417f08a9043..1f248ccf94e 100644 --- a/chromium/components/autofill/core/browser/form_group.cc +++ b/chromium/components/autofill/core/browser/form_group.cc @@ -57,4 +57,14 @@ bool FormGroup::SetInfo(const AutofillType& type, return true; } +bool FormGroup::HasInfo(ServerFieldType type) const { + return HasInfo(AutofillType(type)); +} + +bool FormGroup::HasInfo(const AutofillType& type) const { + // Use "en-US" as a placeholder locale. We are only interested in emptiness, + // not in the presentation of the string. + return !GetInfo(type, "en-US").empty(); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/form_group.h b/chromium/components/autofill/core/browser/form_group.h index 86bd4531bb1..c3d9cd14e2d 100644 --- a/chromium/components/autofill/core/browser/form_group.h +++ b/chromium/components/autofill/core/browser/form_group.h @@ -53,6 +53,10 @@ class FormGroup { const base::string16& value, const std::string& app_locale); + // Returns true iff the string associated with |type| is nonempty. + bool HasInfo(ServerFieldType type) const; + bool HasInfo(const AutofillType& type) const; + protected: // AutofillProfile needs to call into GetSupportedTypes() for objects of // non-AutofillProfile type, for which mere inheritance is insufficient. diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc index addf89f686b..9bff7e2f0bc 100644 --- a/chromium/components/autofill/core/browser/form_structure.cc +++ b/chromium/components/autofill/core/browser/form_structure.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <map> #include <utility> +#include <vector> #include "base/command_line.h" #include "base/i18n/case_conversion.h" @@ -27,6 +28,7 @@ #include "components/autofill/core/browser/field_candidates.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/form_field.h" +#include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_constants.h" #include "components/autofill/core/common/autofill_util.h" #include "components/autofill/core/common/form_data.h" @@ -36,6 +38,7 @@ #include "components/autofill/core/common/signatures_util.h" #include "components/rappor/public/rappor_utils.h" #include "components/rappor/rappor_service_impl.h" +#include "components/ukm/ukm_service.h" namespace autofill { namespace { @@ -268,6 +271,9 @@ HtmlFieldType FieldTypeFromAutocompleteAttributeValue( if (autocomplete_attribute_value == "email") return HTML_TYPE_EMAIL; + if (autocomplete_attribute_value == "upi-vpa") + return HTML_TYPE_UPI_VPA; + return HTML_TYPE_UNRECOGNIZED; } @@ -300,11 +306,13 @@ FormStructure::FormStructure(const FormData& form) upload_required_(USE_UPLOAD_RATES), has_author_specified_types_(false), has_author_specified_sections_(false), + has_author_specified_upi_vpa_hint_(false), was_parsed_for_autocomplete_attributes_(false), has_password_field_(false), is_form_tag_(form.is_form_tag), is_formless_checkout_(form.is_formless_checkout), - all_fields_are_passwords_(true) { + all_fields_are_passwords_(true), + is_signin_upload_(false) { // Copy the form fields. std::map<base::string16, size_t> unique_names; for (const FormFieldData& field : form.fields) { @@ -332,7 +340,7 @@ FormStructure::FormStructure(const FormData& form) FormStructure::~FormStructure() {} -void FormStructure::DetermineHeuristicTypes() { +void FormStructure::DetermineHeuristicTypes(ukm::UkmService* ukm_service) { const auto determine_heuristic_types_start_time = base::TimeTicks::Now(); // First, try to detect field types based on each field's |autocomplete| @@ -357,15 +365,25 @@ void FormStructure::DetermineHeuristicTypes() { UpdateAutofillCount(); IdentifySections(has_author_specified_sections_); + std::vector<AutofillMetrics::DeveloperEngagementMetric> metrics; if (IsAutofillable()) { + AutofillMetrics::DeveloperEngagementMetric metric = + has_author_specified_types_ + ? AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS + : AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS; + metrics.push_back(metric); + AutofillMetrics::LogDeveloperEngagementMetric(metric); + } + + if (has_author_specified_upi_vpa_hint_) { AutofillMetrics::LogDeveloperEngagementMetric( - AutofillMetrics::FILLABLE_FORM_PARSED); - if (has_author_specified_types_) { - AutofillMetrics::LogDeveloperEngagementMetric( - AutofillMetrics::FILLABLE_FORM_CONTAINS_TYPE_HINTS); - } + AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT); + metrics.push_back(AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT); } + AutofillMetrics::LogDeveloperEngagementUkm(ukm_service, source_url(), + metrics); + AutofillMetrics::LogDetermineHeuristicTypesTiming( base::TimeTicks::Now() - determine_heuristic_types_start_time); } @@ -600,7 +618,7 @@ bool FormStructure::ShouldBeParsed() const { if (active_field_count() < kRequiredFieldsForPredictionRoutines && (!all_fields_are_passwords() || active_field_count() < kRequiredFieldsForFormsWithOnlyPasswordFields) && - !has_author_specified_types_) { + !is_signin_upload_ && !has_author_specified_types_) { return false; } @@ -624,17 +642,16 @@ bool FormStructure::ShouldBeCrowdsourced() const { ShouldBeParsed(); } -void FormStructure::UpdateFromCache(const FormStructure& cached_form) { +void FormStructure::UpdateFromCache(const FormStructure& cached_form, + const bool apply_is_autofilled) { // Map from field signatures to cached fields. - std::map<std::string, const AutofillField*> cached_fields; + std::map<base::string16, const AutofillField*> cached_fields; for (size_t i = 0; i < cached_form.field_count(); ++i) { auto* const field = cached_form.field(i); - cached_fields[field->FieldSignatureAsStr()] = field; + cached_fields[field->unique_name()] = field; } - for (auto& field : *this) { - std::map<std::string, const AutofillField*>::const_iterator cached_field = - cached_fields.find(field->FieldSignatureAsStr()); + const auto& cached_field = cached_fields.find(field->unique_name()); if (cached_field != cached_fields.end()) { if (field->form_control_type != "select-one" && field->value == cached_field->second->value) { @@ -649,6 +666,9 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) { field->set_server_type(cached_field->second->server_type()); field->SetHtmlType(cached_field->second->html_type(), cached_field->second->html_mode()); + if (apply_is_autofilled) { + field->is_autofilled = cached_field->second->is_autofilled; + } field->set_previously_autofilled( cached_field->second->previously_autofilled()); field->set_section(cached_field->second->section()); @@ -662,24 +682,30 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) { // rearranged via JavaScript between page load and form submission, so we // copy over the |form_signature_field_names_| corresponding to the query // request. - DCHECK_EQ(cached_form.form_name_, form_name_); - DCHECK_EQ(cached_form.source_url_, source_url_); - DCHECK_EQ(cached_form.target_url_, target_url_); form_signature_ = cached_form.form_signature_; } -void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, - const base::TimeTicks& interaction_time, - const base::TimeTicks& submission_time, - rappor::RapporServiceImpl* rappor_service, - bool did_show_suggestions, - bool observed_submission) const { +void FormStructure::LogQualityMetrics( + const base::TimeTicks& load_time, + const base::TimeTicks& interaction_time, + const base::TimeTicks& submission_time, + rappor::RapporServiceImpl* rappor_service, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, + bool did_show_suggestions, + bool observed_submission) const { size_t num_detected_field_types = 0; size_t num_server_mismatches = 0; size_t num_heuristic_mismatches = 0; size_t num_edited_autofilled_fields = 0; bool did_autofill_all_possible_fields = true; bool did_autofill_some_possible_fields = false; + + // Determine the correct suffix for the metric, depending on whether or + // not a submission was observed. + const AutofillMetrics::QualityMetricType metric_type = + observed_submission ? AutofillMetrics::TYPE_SUBMISSION + : AutofillMetrics::TYPE_NO_SUBMISSION; + for (size_t i = 0; i < field_count(); ++i) { auto* const field = this->field(i); @@ -689,17 +715,51 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, if (field->form_control_type == "password") continue; + if (IsUPIVirtualPaymentAddress(field->value)) { + AutofillMetrics::LogUserHappinessMetric( + AutofillMetrics::USER_DID_ENTER_UPI_VPA); + } // We count fields that were autofilled but later modified, regardless of // whether the data now in the field is recognized. if (field->previously_autofilled()) num_edited_autofilled_fields++; - // No further logging for empty fields nor for fields where the entered data - // does not appear to already exist in the user's stored Autofill data. + // Aliases for the field types predicted by heuristics, server and overall. + ServerFieldType heuristic_type = + AutofillType(field->heuristic_type()).GetStorableType(); + ServerFieldType server_type = + AutofillType(field->server_type()).GetStorableType(); + ServerFieldType predicted_type = field->Type().GetStorableType(); + const ServerFieldTypeSet& field_types = field->possible_types(); DCHECK(!field_types.empty()); - if (field_types.count(EMPTY_TYPE) || field_types.count(UNKNOWN_TYPE)) + + // If the field data is empty, or unrecognized, log whether or not autofill + // predicted that it would be populated with an autofillable data type. + bool has_empty_data = field_types.count(EMPTY_TYPE) != 0; + bool has_unrecognized_data = field_types.count(UNKNOWN_TYPE) != 0; + if (has_empty_data || has_unrecognized_data) { + AutofillMetrics::FieldTypeQualityMetric match_empty_or_unknown = + has_empty_data ? AutofillMetrics::TYPE_MATCH_EMPTY + : AutofillMetrics::TYPE_MATCH_UNKNOWN; + AutofillMetrics::FieldTypeQualityMetric mismatch_empty_or_unknown = + has_empty_data ? AutofillMetrics::TYPE_MISMATCH_EMPTY + : AutofillMetrics::TYPE_MISMATCH_UNKNOWN; + ServerFieldType field_type = has_empty_data ? EMPTY_TYPE : UNKNOWN_TYPE; + AutofillMetrics::LogHeuristicTypePrediction( + (heuristic_type == UNKNOWN_TYPE ? match_empty_or_unknown + : mismatch_empty_or_unknown), + field_type, metric_type); + AutofillMetrics::LogServerTypePrediction( + (server_type == NO_SERVER_DATA ? match_empty_or_unknown + : mismatch_empty_or_unknown), + field_type, metric_type); + AutofillMetrics::LogOverallTypePrediction( + (predicted_type == UNKNOWN_TYPE ? match_empty_or_unknown + : mismatch_empty_or_unknown), + field_type, metric_type); continue; + } ++num_detected_field_types; if (field->is_autofilled) @@ -726,17 +786,7 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, if (collapsed_field_types.size() == 1) field_type = *collapsed_field_types.begin(); - ServerFieldType heuristic_type = - AutofillType(field->heuristic_type()).GetStorableType(); - ServerFieldType server_type = - AutofillType(field->server_type()).GetStorableType(); - ServerFieldType predicted_type = field->Type().GetStorableType(); - - // Log heuristic, server, and overall type quality metrics, independently of - // whether the field was autofilled. - const AutofillMetrics::QualityMetricType metric_type = - observed_submission ? AutofillMetrics::TYPE_SUBMISSION - : AutofillMetrics::TYPE_NO_SUBMISSION; + // Log heuristic, server, and overall type quality metrics. if (heuristic_type == UNKNOWN_TYPE) { AutofillMetrics::LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN, field_type, metric_type); @@ -779,24 +829,20 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, // We log "submission" and duration metrics if we are here after observing a // submission event. if (observed_submission) { + AutofillMetrics::AutofillFormSubmittedState state; if (num_detected_field_types < kRequiredFieldsForPredictionRoutines) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); + state = AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA; } else { if (did_autofill_all_possible_fields) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL); + state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL; } else if (did_autofill_some_possible_fields) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME); + state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME; } else if (!did_show_suggestions) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics:: - FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS); + state = AutofillMetrics:: + FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS; } else { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics:: - FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS); + state = + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS; } // Log some RAPPOR metrics for problematic cases. @@ -843,6 +889,10 @@ void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, } } } + if (form_interactions_ukm_logger->url() != source_url()) + form_interactions_ukm_logger->UpdateSourceURL(source_url()); + AutofillMetrics::LogAutofillFormSubmittedState( + state, form_interactions_ukm_logger); } } @@ -888,6 +938,7 @@ void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { has_author_specified_types_ = false; has_author_specified_sections_ = false; + has_author_specified_upi_vpa_hint_ = false; for (const auto& field : fields_) { // To prevent potential section name collisions, add a default suffix for // other fields. Without this, 'autocomplete' attribute values @@ -925,6 +976,11 @@ void FormStructure::ParseFieldTypesFromAutocompleteAttributes() { tokens.pop_back(); HtmlFieldType field_type = FieldTypeFromAutocompleteAttributeValue(field_type_token, *field); + if (field_type == HTML_TYPE_UPI_VPA) { + has_author_specified_upi_vpa_hint_ = true; + // TODO(crbug/702223): Flesh out support for UPI-VPA. + field_type = HTML_TYPE_UNRECOGNIZED; + } if (field_type == HTML_TYPE_UNSPECIFIED) continue; diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h index 09c4ee13b89..5365ec17fff 100644 --- a/chromium/components/autofill/core/browser/form_structure.h +++ b/chromium/components/autofill/core/browser/form_structure.h @@ -18,6 +18,7 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/proto/server.pb.h" @@ -37,6 +38,10 @@ namespace rappor { class RapporServiceImpl; } +namespace ukm { +class UkmService; +} + namespace autofill { struct FormData; @@ -50,8 +55,9 @@ class FormStructure { virtual ~FormStructure(); // Runs several heuristics against the form fields to determine their possible - // types. - void DetermineHeuristicTypes(); + // types. If |ukm_service| is specified, logs UKM for the form structure + // corresponding to |source_url_|. + void DetermineHeuristicTypes(ukm::UkmService* ukm_service); // Encodes the proto |upload| request from this FormStructure. // In some cases, a |login_form_signature| is included as part of the upload. @@ -116,7 +122,8 @@ class FormStructure { bool ShouldBeCrowdsourced() const; // Sets the field types to be those set for |cached_form|. - void UpdateFromCache(const FormStructure& cached_form); + void UpdateFromCache(const FormStructure& cached_form, + const bool apply_is_autofilled); // Logs quality metrics for |this|, which should be a user-submitted form. // This method should only be called after the possible field types have been @@ -126,12 +133,16 @@ class FormStructure { // indicates whether this method is called as a result of observing a // submission event (otherwise, it may be that an upload was triggered after // a form was unfocused or a navigation occurred). - void LogQualityMetrics(const base::TimeTicks& load_time, - const base::TimeTicks& interaction_time, - const base::TimeTicks& submission_time, - rappor::RapporServiceImpl* rappor_service, - bool did_show_suggestions, - bool observed_submission) const; + // TODO(sebsg): We log more than quality metrics. Maybe rename or split + // function? + void LogQualityMetrics( + const base::TimeTicks& load_time, + const base::TimeTicks& interaction_time, + const base::TimeTicks& submission_time, + rappor::RapporServiceImpl* rappor_service, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, + bool did_show_suggestions, + bool observed_submission) const; // Log the quality of the heuristics and server predictions for this form // structure, if autocomplete attributes are present on the fields (they are @@ -201,12 +212,18 @@ class FormStructure { const GURL& target_url() const { return target_url_; } - bool has_author_specified_types() { return has_author_specified_types_; } + bool has_author_specified_types() const { + return has_author_specified_types_; + } - bool has_author_specified_sections() { + bool has_author_specified_sections() const { return has_author_specified_sections_; } + bool has_author_specified_upi_vpa_hint() const { + return has_author_specified_upi_vpa_hint_; + } + void set_upload_required(UploadRequired required) { upload_required_ = required; } @@ -214,6 +231,11 @@ class FormStructure { bool all_fields_are_passwords() const { return all_fields_are_passwords_; } + bool is_signin_upload() const { return is_signin_upload_; } + void set_is_signin_upload(bool is_signin_upload) { + is_signin_upload_ = is_signin_upload; + } + FormSignature form_signature() const { return form_signature_; } // Returns a FormData containing the data this form structure knows about. @@ -292,6 +314,10 @@ class FormStructure { // author, via the autocomplete attribute. bool has_author_specified_sections_; + // Whether the form includes a field that explicitly sets it autocomplete + // type to "upi-vpa". + bool has_author_specified_upi_vpa_hint_; + // Whether the form was parsed for autocomplete attribute, thus assigning // the real values of |has_author_specified_types_| and // |has_author_specified_sections_|. @@ -309,6 +335,10 @@ class FormStructure { // True if all form fields are password fields. bool all_fields_are_passwords_; + // True if the form is submitted and has 2 fields: one text and one password + // field. + bool is_signin_upload_; + // The unique signature for this form, composed of the target url domain, // the form name, and the form field names in a 64-bit hash. FormSignature form_signature_; diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc index c1ecb59405c..ac809717668 100644 --- a/chromium/components/autofill/core/browser/form_structure_unittest.cc +++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc @@ -149,7 +149,7 @@ TEST_F(FormStructureTest, AutofillCount) { // Only text and select fields that are heuristically matched are counted. form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_EQ(3U, form_structure->autofill_count()); // Add a field with should_autocomplete=false. This should not be considered a @@ -161,7 +161,7 @@ TEST_F(FormStructureTest, AutofillCount) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_EQ(4U, form_structure->autofill_count()); } @@ -196,7 +196,7 @@ TEST_F(FormStructureTest, IsAutofillable) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsAutofillable()); // We now have three text fields, but only two auto-fillable fields. @@ -211,7 +211,7 @@ TEST_F(FormStructureTest, IsAutofillable) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsAutofillable()); // We now have three auto-fillable fields. @@ -221,19 +221,19 @@ TEST_F(FormStructureTest, IsAutofillable) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // The target cannot include http(s)://*/search... form.action = GURL("http://google.com/search?q=hello"); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsAutofillable()); // But search can be in the URL. form.action = GURL("http://search.com/?q=hello"); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); } @@ -334,6 +334,19 @@ TEST_F(FormStructureTest, ShouldBeParsed) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); EXPECT_TRUE(form_structure->ShouldBeParsed()); + + // There are 2 fields, one of which is password, and this is an upload of + // a sign-in form submission, should be parsed. + form.fields.clear(); + field.name = ASCIIToUTF16("username"); + field.form_control_type = "text"; + form.fields.push_back(field); + field.name = ASCIIToUTF16("pw"); + field.form_control_type = "password"; + form.fields.push_back(field); + form_structure.reset(new FormStructure(form)); + form_structure->set_is_signin_upload(true); + EXPECT_TRUE(form_structure->ShouldBeParsed()); } // Tests that ShouldBeParsed returns true for a form containing less than three @@ -405,7 +418,7 @@ TEST_F(FormStructureTest, HeuristicsContactInfo) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -456,20 +469,29 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttribute) { field.autocomplete_attribute = "email"; form.fields.push_back(field); + field.label = base::string16(); + field.name = ASCIIToUTF16("field4"); + field.autocomplete_attribute = "upi-vpa"; + form.fields.push_back(field); + form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); + EXPECT_TRUE(form_structure->has_author_specified_types()); + EXPECT_TRUE(form_structure->has_author_specified_upi_vpa_hint()); // Expect the correct number of fields. - ASSERT_EQ(3U, form_structure->field_count()); + ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); EXPECT_EQ(HTML_TYPE_GIVEN_NAME, form_structure->field(0)->html_type()); EXPECT_EQ(HTML_TYPE_FAMILY_NAME, form_structure->field(1)->html_type()); EXPECT_EQ(HTML_TYPE_EMAIL, form_structure->field(2)->html_type()); + EXPECT_EQ(HTML_TYPE_UNRECOGNIZED, form_structure->field(3)->html_type()); EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(0)->heuristic_type()); EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(1)->heuristic_type()); EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type()); + EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(3)->heuristic_type()); } // Verify that the heuristics are not run for non checkout formless forms. @@ -496,7 +518,7 @@ TEST_F(FormStructureTest, Heuristics_FormlessNonCheckoutForm) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -512,7 +534,7 @@ TEST_F(FormStructureTest, Heuristics_FormlessNonCheckoutForm) { form.is_form_tag = false; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -553,7 +575,7 @@ TEST_F(FormStructureTest, StripCommonNamePrefix) { form.fields.push_back(field); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -593,7 +615,7 @@ TEST_F(FormStructureTest, StripCommonNamePrefix_SmallPrefix) { form.fields.push_back(field); std::unique_ptr<FormStructure> form_structure(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -629,7 +651,7 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_Minimal) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsCompleteCreditCardForm()); } @@ -667,7 +689,7 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_Full) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsCompleteCreditCardForm()); } @@ -685,7 +707,7 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_OnlyCCNumber) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsCompleteCreditCardForm()); } @@ -726,7 +748,7 @@ TEST_F(FormStructureTest, IsCompleteCreditCardForm_AddressForm) { field.name = base::string16(); form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsCompleteCreditCardForm()); } @@ -756,7 +778,7 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttributePhoneTypes) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -796,7 +818,7 @@ TEST_F(FormStructureTest, form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); EXPECT_TRUE(form_structure->ShouldBeCrowdsourced()); @@ -834,7 +856,7 @@ TEST_F(FormStructureTest, form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); EXPECT_TRUE(form_structure->ShouldBeCrowdsourced()); @@ -877,7 +899,7 @@ TEST_F(FormStructureTest, form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); EXPECT_TRUE(form_structure->ShouldBeCrowdsourced()); @@ -909,7 +931,7 @@ TEST_F(FormStructureTest, form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsAutofillable()); EXPECT_FALSE(form_structure->ShouldBeCrowdsourced()); @@ -944,7 +966,7 @@ TEST_F(FormStructureTest, form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(form_structure->IsAutofillable()); EXPECT_FALSE(form_structure->ShouldBeCrowdsourced()); @@ -987,7 +1009,7 @@ TEST_F(FormStructureTest, PasswordFormShouldBeCrowdsourced) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure.ShouldBeCrowdsourced()); } @@ -1038,7 +1060,7 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSections) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure.IsAutofillable()); // Expect the correct number of fields. @@ -1083,7 +1105,7 @@ TEST_F(FormStructureTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); // Expect the correct number of fields. ASSERT_EQ(6U, form_structure.field_count()); @@ -1112,7 +1134,7 @@ TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsRepeated) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); // Expect the correct number of fields. ASSERT_EQ(2U, form_structure.field_count()); @@ -1149,7 +1171,7 @@ TEST_F(FormStructureTest, HeuristicsDontOverrideAutocompleteAttributeSections) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); // Expect the correct number of fields. ASSERT_EQ(4U, form_structure.field_count()); @@ -1213,7 +1235,7 @@ TEST_F(FormStructureTest, HeuristicsSample8) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(10U, form_structure->field_count()); ASSERT_EQ(9U, form_structure->autofill_count()); @@ -1279,7 +1301,7 @@ TEST_F(FormStructureTest, HeuristicsSample6) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(7U, form_structure->field_count()); ASSERT_EQ(6U, form_structure->autofill_count()); @@ -1344,7 +1366,7 @@ TEST_F(FormStructureTest, HeuristicsLabelsOnly) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(8U, form_structure->field_count()); ASSERT_EQ(7U, form_structure->autofill_count()); @@ -1401,7 +1423,7 @@ TEST_F(FormStructureTest, HeuristicsCreditCardInfo) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(6U, form_structure->field_count()); ASSERT_EQ(5U, form_structure->autofill_count()); @@ -1461,7 +1483,7 @@ TEST_F(FormStructureTest, HeuristicsCreditCardInfoWithUnknownCardField) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(7U, form_structure->field_count()); ASSERT_EQ(5U, form_structure->autofill_count()); @@ -1508,7 +1530,7 @@ TEST_F(FormStructureTest, ThreeAddressLines) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(4U, form_structure->autofill_count()); @@ -1548,7 +1570,7 @@ TEST_F(FormStructureTest, SurplusAddressLinesIgnored) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); @@ -1591,7 +1613,7 @@ TEST_F(FormStructureTest, ThreeAddressLinesExpedia) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(4U, form_structure->field_count()); EXPECT_EQ(4U, form_structure->autofill_count()); @@ -1629,7 +1651,7 @@ TEST_F(FormStructureTest, TwoAddressLinesEbay) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(3U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); @@ -1662,7 +1684,7 @@ TEST_F(FormStructureTest, HeuristicsStateWithProvince) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(3U, form_structure->field_count()); ASSERT_EQ(3U, form_structure->autofill_count()); @@ -1728,7 +1750,7 @@ TEST_F(FormStructureTest, HeuristicsWithBilling) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(11U, form_structure->field_count()); ASSERT_EQ(11U, form_structure->autofill_count()); @@ -1777,7 +1799,7 @@ TEST_F(FormStructureTest, ThreePartPhoneNumber) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); ASSERT_EQ(4U, form_structure->field_count()); ASSERT_EQ(4U, form_structure->autofill_count()); @@ -1822,7 +1844,7 @@ TEST_F(FormStructureTest, HeuristicsInfernoCC) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -1876,7 +1898,7 @@ TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesNotFirst) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -1934,7 +1956,7 @@ TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesFirst) { form.fields.push_back(field); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(form_structure->IsAutofillable()); // Expect the correct number of fields. @@ -2117,7 +2139,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -2293,7 +2315,7 @@ TEST_F(FormStructureTest, std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.label = ASCIIToUTF16("First Name"); @@ -2432,7 +2454,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithAutocomplete) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -2503,7 +2525,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_ObservedSubmissionFalse) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -2572,7 +2594,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithLabels) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -2709,7 +2731,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_WithFormName) { // Setting the form name which we expect to see in the upload. form.name = ASCIIToUTF16("myform"); form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -2771,7 +2793,7 @@ TEST_F(FormStructureTest, EncodeUploadRequestPartialMetadata) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -2844,7 +2866,7 @@ TEST_F(FormStructureTest, EncodeUploadRequest_DisabledMetadataTrial) { std::vector<ServerFieldTypeSet> possible_field_types; FormData form; form_structure.reset(new FormStructure(form)); - form_structure->DetermineHeuristicTypes(); + form_structure->DetermineHeuristicTypes(nullptr /* ukm_service */); FormFieldData field; field.form_control_type = "text"; @@ -3772,7 +3794,7 @@ TEST_F(FormStructureTest, ParseQueryResponseAuthorDefinedTypes) { FormStructure form_structure(form); std::vector<FormStructure*> forms; forms.push_back(&form_structure); - forms.front()->DetermineHeuristicTypes(); + forms.front()->DetermineHeuristicTypes(nullptr /* ukm_service */); AutofillQueryResponseContents response; response.add_field()->set_autofill_type(EMAIL_ADDRESS); diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.cc b/chromium/components/autofill/core/browser/payments/full_card_request.cc index ee21ebaf829..68177a70329 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.cc +++ b/chromium/components/autofill/core/browser/payments/full_card_request.cc @@ -16,17 +16,17 @@ namespace autofill { namespace payments { -FullCardRequest::FullCardRequest(AutofillClient* autofill_client, +FullCardRequest::FullCardRequest(RiskDataLoader* risk_data_loader, payments::PaymentsClient* payments_client, PersonalDataManager* personal_data_manager) - : autofill_client_(autofill_client), + : risk_data_loader_(risk_data_loader), payments_client_(payments_client), personal_data_manager_(personal_data_manager), result_delegate_(nullptr), ui_delegate_(nullptr), should_unmask_card_(false), weak_ptr_factory_(this) { - DCHECK(autofill_client_); + DCHECK(risk_data_loader_); DCHECK(payments_client_); DCHECK(personal_data_manager_); } @@ -62,7 +62,7 @@ void FullCardRequest::GetFullCard(const CreditCard& card, weak_ptr_factory_.GetWeakPtr()); if (should_unmask_card_) { - autofill_client_->LoadRiskData( + risk_data_loader_->LoadRiskData( base::Bind(&FullCardRequest::OnDidGetUnmaskRiskData, weak_ptr_factory_.GetWeakPtr())); } diff --git a/chromium/components/autofill/core/browser/payments/full_card_request.h b/chromium/components/autofill/core/browser/payments/full_card_request.h index 263b3c88fe7..9db61fd5ad3 100644 --- a/chromium/components/autofill/core/browser/payments/full_card_request.h +++ b/chromium/components/autofill/core/browser/payments/full_card_request.h @@ -48,7 +48,7 @@ class FullCardRequest : public CardUnmaskDelegate { }; // The parameters should outlive the FullCardRequest. - FullCardRequest(AutofillClient* autofill_client, + FullCardRequest(RiskDataLoader* risk_data_loader, payments::PaymentsClient* payments_client, PersonalDataManager* personal_data_manager); ~FullCardRequest(); @@ -83,9 +83,8 @@ class FullCardRequest : public CardUnmaskDelegate { // Resets the state of the request. void Reset(); - // Responsible for showing the UI that prompts the user for the CVC and/or the - // updated expiration date. - AutofillClient* const autofill_client_; + // Used to fetch risk data for this request. + RiskDataLoader* const risk_data_loader_; // Responsible for unmasking a masked server card. payments::PaymentsClient* const payments_client_; diff --git a/chromium/components/autofill/core/browser/payments/payments_client.cc b/chromium/components/autofill/core/browser/payments/payments_client.cc index 5fbeca35005..44e85c2e8d9 100644 --- a/chromium/components/autofill/core/browser/payments/payments_client.cc +++ b/chromium/components/autofill/core/browser/payments/payments_client.cc @@ -28,6 +28,7 @@ #include "net/base/escape.h" #include "net/base/load_flags.h" #include "net/http/http_status_code.h" +#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_request_context_getter.h" @@ -415,9 +416,44 @@ void PaymentsClient::IssueRequest(std::unique_ptr<PaymentsRequest> request, } void PaymentsClient::InitializeUrlFetcher() { + net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("payments_sync_cards", R"( + semantics { + sender: "Payments" + description: + "This service communicates with Google Payments servers to upload " + "(save) or receive the user's credit card info." + trigger: + "Requests are triggered by a user action, such as selecting a " + "masked server card from Chromium's credit card autofill dropdown, " + "submitting a form which has credit card information, or accepting " + "the prompt to save a credit card to Payments servers." + data: + "In case of save, a protocol buffer containing relevant address " + "and credit card information which should be saved in Google " + "Payments servers, along with user credentials. In case of load, a " + "protocol buffer containing the id of the credit card to unmask, " + "an encrypted cvc value, an optional updated card expiration date, " + "and user credentials." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: false + setting: + "Users can enable or disable this feature in Chromium settings by " + "toggling 'Credit cards and addresses using Google Payments', " + "under 'Advanced sync settings...'. This feature is enabled by " + "default." + chrome_policy { + AutoFillEnabled { + policy_options {mode: MANDATORY} + AutoFillEnabled: false + } + } + })"); url_fetcher_ = net::URLFetcher::Create(0, GetRequestUrl(request_->GetRequestUrlPath()), - net::URLFetcher::POST, this); + net::URLFetcher::POST, this, traffic_annotation); data_use_measurement::DataUseUserData::AttachToFetcher( url_fetcher_.get(), data_use_measurement::DataUseUserData::AUTOFILL); diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc index 1ea5a2f555f..e634110a718 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager.cc @@ -883,7 +883,11 @@ std::vector<Suggestion> PersonalDataManager::GetProfileSuggestions( // trial group or SIZE_MAX if no limit is defined. std::string limit_str = variations::GetVariationParamValue( kFrecencyFieldTrialName, kFrecencyFieldTrialLimitParam); - size_t limit = base::StringToSizeT(limit_str, &limit) ? limit : SIZE_MAX; + size_t limit = SIZE_MAX; + // Reassign SIZE_MAX to |limit| is needed after calling base::StringToSizeT, + // as this method can modify |limit| even if it returns false. + if (!base::StringToSizeT(limit_str, &limit)) + limit = SIZE_MAX; unique_suggestions.resize(std::min(unique_suggestions.size(), limit)); diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h index a7f9808e53a..d8247d526c0 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager.h +++ b/chromium/components/autofill/core/browser/personal_data_manager.h @@ -27,9 +27,7 @@ #include "components/keyed_service/core/keyed_service.h" #include "components/prefs/pref_member.h" #include "components/webdata/common/web_data_service_consumer.h" -#if defined(OS_ANDROID) #include "net/url_request/url_request_context_getter.h" -#endif class AccountTrackerService; class Browser; @@ -148,7 +146,7 @@ class PersonalDataManager : public KeyedService, const std::vector<AutofillProfile*>& profiles); // Adds |credit_card| to the web database. - void AddCreditCard(const CreditCard& credit_card); + virtual void AddCreditCard(const CreditCard& credit_card); // Updates |credit_card| which already exists in the web database. This // can only be used on local credit cards. @@ -274,7 +272,6 @@ class PersonalDataManager : public KeyedService, NotifyPersonalDataChanged(); } -#if defined(OS_ANDROID) // Sets the URL request context getter to be used when normalizing addresses // with libaddressinput's address validator. void SetURLRequestContextGetter( @@ -286,7 +283,6 @@ class PersonalDataManager : public KeyedService, net::URLRequestContextGetter* GetURLRequestContextGetter() const { return context_getter_.get(); } -#endif protected: // Only PersonalDataManagerFactory and certain tests can create instances of @@ -330,17 +326,19 @@ class PersonalDataManager : public KeyedService, ConvertWalletAddressesAndUpdateWalletCards_MergedProfile); FRIEND_TEST_ALL_PREFIXES( PersonalDataManagerTest, - ConvertWalletAddressesAndUpdateWalletCards_NewCard_AddressAlreadyConverted); + ConvertWalletAddressesAndUpdateWalletCards_NewCard_AddressAlreadyConverted); // NOLINT FRIEND_TEST_ALL_PREFIXES( PersonalDataManagerTest, ConvertWalletAddressesAndUpdateWalletCards_AlreadyConverted); FRIEND_TEST_ALL_PREFIXES( PersonalDataManagerTest, - ConvertWalletAddressesAndUpdateWalletCards_MultipleSimilarWalletAddresses); + ConvertWalletAddressesAndUpdateWalletCards_MultipleSimilarWalletAddresses); // NOLINT friend class autofill::AutofillInteractiveTest; friend class autofill::AutofillTest; friend class autofill::PersonalDataManagerFactory; friend class PersonalDataManagerTest; + friend class PersonalDataManagerTestBase; + friend class SaveImportedProfileTest; friend class ProfileSyncServiceAutofillTest; friend class ::RemoveAutofillTester; friend std::default_delete<PersonalDataManager>; @@ -600,11 +598,9 @@ class PersonalDataManager : public KeyedService, // Whether new information was received from the sync server. bool has_synced_new_data_ = false; -#if defined(OS_ANDROID) // The context for the request to be used to fetch libaddressinput's address // validation rules. scoped_refptr<net::URLRequestContextGetter> context_getter_; -#endif DISALLOW_COPY_AND_ASSIGN(PersonalDataManager); }; diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc index 6bb7a52b7e0..f5b2aa6d4ce 100644 --- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -119,60 +119,9 @@ void ExpectSameElements(const std::vector<T*>& expectations, } // anonymous namespace -class PersonalDataManagerTest : public testing::Test { +class PersonalDataManagerTestBase { protected: - PersonalDataManagerTest() : autofill_table_(nullptr) {} - - void SetUp() override { - OSCryptMocker::SetUpWithSingleton(); - prefs_ = test::PrefServiceForTesting(); - ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); - base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB"); - web_database_ = - new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get()); - - // Setup account tracker. - signin_client_.reset(new TestSigninClient(prefs_.get())); - account_tracker_.reset(new AccountTrackerService()); - account_tracker_->Initialize(signin_client_.get()); - signin_manager_.reset(new FakeSigninManagerBase(signin_client_.get(), - account_tracker_.get())); - signin_manager_->Initialize(prefs_.get()); - - // Hacky: hold onto a pointer but pass ownership. - autofill_table_ = new AutofillTable; - web_database_->AddTable(std::unique_ptr<WebDatabaseTable>(autofill_table_)); - web_database_->LoadDatabase(); - autofill_database_service_ = new AutofillWebDataService( - web_database_, base::ThreadTaskRunnerHandle::Get(), - base::ThreadTaskRunnerHandle::Get(), - WebDataServiceBase::ProfileErrorCallback()); - autofill_database_service_->Init(); - - test::DisableSystemServices(prefs_.get()); - ResetPersonalDataManager(USER_MODE_NORMAL); - - // Reset the deduping pref to its default value. - personal_data_->pref_service_->SetInteger( - prefs::kAutofillLastVersionDeduped, 0); - personal_data_->pref_service_->SetBoolean( - prefs::kAutofillProfileUseDatesFixed, false); - } - - void TearDown() override { - // Order of destruction is important as AutofillManager relies on - // PersonalDataManager to be around when it gets destroyed. - signin_manager_->Shutdown(); - signin_manager_.reset(); - - account_tracker_->Shutdown(); - account_tracker_.reset(); - signin_client_.reset(); - - test::DisableSystemServices(prefs_.get()); - OSCryptMocker::TearDown(); - } + PersonalDataManagerTestBase() : autofill_table_(nullptr) {} void ResetPersonalDataManager(UserMode user_mode) { bool is_incognito = (user_mode == USER_MODE_INCOGNITO); @@ -315,7 +264,7 @@ class PersonalDataManagerTest : public testing::Test { const char* exp_cc_month, const char* exp_cc_year) { FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -351,6 +300,60 @@ class PersonalDataManagerTest : public testing::Test { variations::testing::VariationParamsManager variation_params_; }; +class PersonalDataManagerTest : public PersonalDataManagerTestBase, + public testing::Test { + void SetUp() override { + OSCryptMocker::SetUpWithSingleton(); + prefs_ = test::PrefServiceForTesting(); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB"); + web_database_ = + new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + + // Setup account tracker. + signin_client_.reset(new TestSigninClient(prefs_.get())); + account_tracker_.reset(new AccountTrackerService()); + account_tracker_->Initialize(signin_client_.get()); + signin_manager_.reset(new FakeSigninManagerBase(signin_client_.get(), + account_tracker_.get())); + signin_manager_->Initialize(prefs_.get()); + + // Hacky: hold onto a pointer but pass ownership. + autofill_table_ = new AutofillTable; + web_database_->AddTable(std::unique_ptr<WebDatabaseTable>(autofill_table_)); + web_database_->LoadDatabase(); + autofill_database_service_ = new AutofillWebDataService( + web_database_, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get(), + WebDataServiceBase::ProfileErrorCallback()); + autofill_database_service_->Init(); + + test::DisableSystemServices(prefs_.get()); + ResetPersonalDataManager(USER_MODE_NORMAL); + + // Reset the deduping pref to its default value. + personal_data_->pref_service_->SetInteger( + prefs::kAutofillLastVersionDeduped, 0); + personal_data_->pref_service_->SetBoolean( + prefs::kAutofillProfileUseDatesFixed, false); + } + + void TearDown() override { + // Order of destruction is important as AutofillManager relies on + // PersonalDataManager to be around when it gets destroyed. + signin_manager_->Shutdown(); + signin_manager_.reset(); + + account_tracker_->Shutdown(); + account_tracker_.reset(); + signin_client_.reset(); + + test::DisableSystemServices(prefs_.get()); + OSCryptMocker::TearDown(); + } +}; + TEST_F(PersonalDataManagerTest, AddProfile) { // Add profile0 to the database. AutofillProfile profile0(test::GetFullProfile()); @@ -1030,7 +1033,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -1068,7 +1071,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_BadEmail) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles(); @@ -1098,7 +1101,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_TwoEmails) { "Confirm email:", "confirm_email", "example@example.com", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles(); ASSERT_EQ(1U, results.size()); @@ -1127,7 +1130,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_TwoDifferentEmails) { "Email:", "email2", "example2@example.com", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles(); ASSERT_EQ(0U, results.size()); @@ -1147,7 +1150,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_NotEnoughFilledFields) { "Card number:", "card_number", "4111 1111 1111 1111", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); @@ -1175,7 +1178,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MinimumAddressUSA) { test::CreateTestFormField("Country:", "country", "USA", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); ASSERT_EQ(1U, profiles.size()); @@ -1200,7 +1203,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MinimumAddressGB) { "Country:", "country", "United Kingdom", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); ASSERT_EQ(1U, profiles.size()); @@ -1220,7 +1223,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MinimumAddressGI) { test::CreateTestFormField("Country:", "country", "Gibraltar", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles(); ASSERT_EQ(1U, profiles.size()); @@ -1258,7 +1261,7 @@ TEST_F(PersonalDataManagerTest, test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -1302,7 +1305,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MultilineAddress) { test::CreateTestFormField("Zip:", "zip", "94102", "text", &field); form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -1343,7 +1346,7 @@ TEST_F(PersonalDataManagerTest, form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure1)); // Verify that the web database has been updated and the notification sent. @@ -1381,7 +1384,7 @@ TEST_F(PersonalDataManagerTest, form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure2)); // Verify that the web database has been updated and the notification sent. @@ -1442,7 +1445,7 @@ TEST_F(PersonalDataManagerTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -1520,7 +1523,7 @@ TEST_F(PersonalDataManagerTest, // Still able to do the import. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -1604,7 +1607,7 @@ TEST_F(PersonalDataManagerTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -1659,7 +1662,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_SameProfileWithConflict) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure1)); // Verify that the web database has been updated and the notification sent. @@ -1707,7 +1710,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_SameProfileWithConflict) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure2)); // Verify that the web database has been updated and the notification sent. @@ -1745,7 +1748,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MissingInfoInOld) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure1)); // Verify that the web database has been updated and the notification sent. @@ -1783,7 +1786,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MissingInfoInOld) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure2)); // Verify that the web database has been updated and the notification sent. @@ -1828,7 +1831,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MissingInfoInNew) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure1)); // Verify that the web database has been updated and the notification sent. @@ -1867,7 +1870,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_MissingInfoInNew) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure2)); // Verify that the web database has been updated and the notification sent. @@ -1905,7 +1908,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_InsufficientAddress) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(ImportAddressProfiles(form_structure1)); // Since no refresh is expected, reload the data from the database to make @@ -1962,7 +1965,7 @@ TEST_F(PersonalDataManagerTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Wait for the refresh, which in this case is a no-op. @@ -1983,7 +1986,7 @@ TEST_F(PersonalDataManagerTest, form.fields[0] = field; FormStructure form_structure2(form); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure2)); // Wait for the refresh, which in this case is a no-op. @@ -2024,7 +2027,7 @@ TEST_F(PersonalDataManagerTest, ImportAddressProfiles_UnrecognizedCountry) { form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(ImportAddressProfiles(form_structure)); // Since no refresh is expected, reload the data from the database to make @@ -2065,7 +2068,7 @@ TEST_F(PersonalDataManagerTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_TRUE(ImportAddressProfiles(form_structure)); // Verify that the web database has been updated and the notification sent. @@ -2114,7 +2117,7 @@ TEST_F(PersonalDataManagerTest, form.fields.push_back(field); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); EXPECT_FALSE(ImportAddressProfiles(form_structure)); // Since no refresh is expected, reload the data from the database to make @@ -2137,7 +2140,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_Valid) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2163,7 +2166,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_Invalid) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_FALSE(imported_credit_card); @@ -2197,7 +2200,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_MonthSelectInvalidText) { form.fields[2].option_contents = contents; FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2224,7 +2227,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_TwoValidCards) { "2999"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2247,7 +2250,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_TwoValidCards) { AddFullCreditCardForm(&form2, "", "5500 0000 0000 0004", "02", "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2)); ASSERT_TRUE(imported_credit_card2); @@ -2362,7 +2365,7 @@ TEST_F(PersonalDataManagerTest, // The card should be offered to be saved locally because it only matches the // masked server card. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2392,7 +2395,7 @@ TEST_F(PersonalDataManagerTest, // The card should not be offered to be saved locally because it only matches // the full server card. FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_FALSE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_FALSE(imported_credit_card); @@ -2405,7 +2408,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_SameCreditCardWithConflict) { "2998"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2430,7 +2433,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_SameCreditCardWithConflict) { /* different year */ "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2)); EXPECT_FALSE(imported_credit_card2); @@ -2457,7 +2460,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_ShouldReturnLocalCard) { "2998"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2482,7 +2485,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_ShouldReturnLocalCard) { /* different year */ "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, /* should_return_local_card= */ true, @@ -2512,7 +2515,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_EmptyCardWithConflict) { "2998"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2536,7 +2539,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_EmptyCardWithConflict) { "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_FALSE( ImportCreditCard(form_structure2, false, &imported_credit_card2)); @@ -2562,7 +2565,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_MissingInfoInNew) { "2999"); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure1, false, &imported_credit_card)); ASSERT_TRUE(imported_credit_card); @@ -2587,7 +2590,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_MissingInfoInNew) { "4111-1111-1111-1111", "01", "2999"); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_TRUE(ImportCreditCard(form_structure2, false, &imported_credit_card2)); EXPECT_FALSE(imported_credit_card2); @@ -2611,7 +2614,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_MissingInfoInNew) { /* no year */ nullptr); FormStructure form_structure3(form3); - form_structure3.DetermineHeuristicTypes(); + form_structure3.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card3; EXPECT_FALSE( ImportCreditCard(form_structure3, false, &imported_credit_card3)); @@ -2654,7 +2657,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_MissingInfoInOld) { /* different year */ "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); EXPECT_FALSE(imported_credit_card); @@ -2699,7 +2702,7 @@ TEST_F(PersonalDataManagerTest, ImportCreditCard_SameCardWithSeparators) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); EXPECT_FALSE(imported_credit_card); @@ -2738,7 +2741,7 @@ TEST_F(PersonalDataManagerTest, /* different year */ "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(ImportCreditCard(form_structure, false, &imported_credit_card)); ASSERT_FALSE(imported_credit_card); @@ -2785,7 +2788,7 @@ TEST_F(PersonalDataManagerTest, ImportFormData_OneAddressOneCreditCard) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_TRUE(personal_data_->ImportFormData(form_structure, false, &imported_credit_card)); @@ -2863,7 +2866,7 @@ TEST_F(PersonalDataManagerTest, ImportFormData_TwoAddressesOneCreditCard) { "2999"); FormStructure form_structure(form); - form_structure.DetermineHeuristicTypes(); + form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; // Still returns true because the credit card import was successful. EXPECT_TRUE(personal_data_->ImportFormData(form_structure, false, @@ -4139,7 +4142,7 @@ TEST_F(PersonalDataManagerTest, DontDuplicateServerCard) { form1.fields.push_back(field); FormStructure form_structure1(form1); - form_structure1.DetermineHeuristicTypes(); + form_structure1.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card; EXPECT_FALSE(personal_data_->ImportFormData(form_structure1, false, &imported_credit_card)); @@ -4161,7 +4164,7 @@ TEST_F(PersonalDataManagerTest, DontDuplicateServerCard) { form2.fields.push_back(field); FormStructure form_structure2(form2); - form_structure2.DetermineHeuristicTypes(); + form_structure2.DetermineHeuristicTypes(nullptr /* ukm_service */); std::unique_ptr<CreditCard> imported_credit_card2; EXPECT_FALSE(personal_data_->ImportFormData(form_structure2, false, &imported_credit_card2)); @@ -4170,10 +4173,9 @@ TEST_F(PersonalDataManagerTest, DontDuplicateServerCard) { // Tests the SaveImportedProfile method with different profiles to make sure the // merge logic works correctly. -TEST_F(PersonalDataManagerTest, SaveImportedProfile) { - typedef struct { - autofill::ServerFieldType field_type; - std::string field_value; +typedef struct { + autofill::ServerFieldType field_type; + std::string field_value; } ProfileField; typedef std::vector<ProfileField> ProfileFields; @@ -4188,254 +4190,72 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfile) { // For tests with profile merging, makes sure that these fields' values are // the ones we expect (depending on the test). ProfileFields changed_field_values; - } TestCase; - - TestCase test_cases[] = { - // Test that saving an identical profile except for the name results in - // two profiles being saved. - {ProfileFields(), {{NAME_FIRST, "Marionette"}}}, - - // Test that saving an identical profile except with the middle name - // initial instead of the full middle name results in the profiles - // getting merged and the full middle name being kept. - {ProfileFields(), - {{NAME_MIDDLE, "M"}}, - {{NAME_MIDDLE, "Mitchell"}, {NAME_FULL, "Marion Mitchell Morrison"}}}, - - // Test that saving an identical profile except with the full middle name - // instead of the middle name initial results in the profiles getting - // merged and the full middle name replacing the initial. - {{{NAME_MIDDLE, "M"}}, - {{NAME_MIDDLE, "Mitchell"}}, - {{NAME_MIDDLE, "Mitchell"}}}, - - // Test that saving an identical profile except with no middle name - // results in the profiles getting merged and the full middle name being - // kept. - {ProfileFields(), {{NAME_MIDDLE, ""}}, {{NAME_MIDDLE, "Mitchell"}}}, - - // Test that saving an identical profile except with a middle name initial - // results in the profiles getting merged and the middle name initial - // being saved. - {{{NAME_MIDDLE, ""}}, {{NAME_MIDDLE, "M"}}, {{NAME_MIDDLE, "M"}}}, - - // Test that saving an identical profile except with a middle name - // results in the profiles getting merged and the full middle name being - // saved. - {{{NAME_MIDDLE, ""}}, - {{NAME_MIDDLE, "Mitchell"}}, - {{NAME_MIDDLE, "Mitchell"}}}, - - // Test that saving a identical profile except with the full name set - // instead of the name parts results in the two profiles being merged and - // all the name parts kept and the full name being added. - { - { - {NAME_FIRST, "Marion"}, - {NAME_MIDDLE, "Mitchell"}, - {NAME_LAST, "Morrison"}, - {NAME_FULL, ""}, - }, - { - {NAME_FIRST, ""}, - {NAME_MIDDLE, ""}, - {NAME_LAST, ""}, - {NAME_FULL, "Marion Mitchell Morrison"}, - }, - { - {NAME_FIRST, "Marion"}, - {NAME_MIDDLE, "Mitchell"}, - {NAME_LAST, "Morrison"}, - {NAME_FULL, "Marion Mitchell Morrison"}, - }, - }, - - // Test that saving a identical profile except with the name parts set - // instead of the full name results in the two profiles being merged and - // the full name being kept and all the name parts being added. - { - { - {NAME_FIRST, ""}, - {NAME_MIDDLE, ""}, - {NAME_LAST, ""}, - {NAME_FULL, "Marion Mitchell Morrison"}, - }, - { - {NAME_FIRST, "Marion"}, - {NAME_MIDDLE, "Mitchell"}, - {NAME_LAST, "Morrison"}, - {NAME_FULL, ""}, - }, - { - {NAME_FIRST, "Marion"}, - {NAME_MIDDLE, "Mitchell"}, - {NAME_LAST, "Morrison"}, - {NAME_FULL, "Marion Mitchell Morrison"}, - }, - }, - - // Test that saving a profile that has only a full name set does not get - // merged with a profile with only the name parts set if the names are - // different. - { - { - {NAME_FIRST, "Marion"}, - {NAME_MIDDLE, "Mitchell"}, - {NAME_LAST, "Morrison"}, - {NAME_FULL, ""}, - }, - { - {NAME_FIRST, ""}, - {NAME_MIDDLE, ""}, - {NAME_LAST, ""}, - {NAME_FULL, "John Thompson Smith"}, - }, - }, - - // Test that saving a profile that has only the name parts set does not - // get merged with a profile with only the full name set if the names are - // different. - { - { - {NAME_FIRST, ""}, - {NAME_MIDDLE, ""}, - {NAME_LAST, ""}, - {NAME_FULL, "John Thompson Smith"}, - }, - { - {NAME_FIRST, "Marion"}, - {NAME_MIDDLE, "Mitchell"}, - {NAME_LAST, "Morrison"}, - {NAME_FULL, ""}, - }, - }, - - // Test that saving an identical profile except for the first address line - // results in two profiles being saved. - {ProfileFields(), {{ADDRESS_HOME_LINE1, "123 Aquarium St."}}}, - - // Test that saving an identical profile except for the second address - // line results in two profiles being saved. - {ProfileFields(), {{ADDRESS_HOME_LINE2, "unit 7"}}}, - - // Tests that saving an identical profile that has a new piece of - // information (company name) results in a merge and that the original - // empty value gets overwritten by the new information. - {{{COMPANY_NAME, ""}}, ProfileFields(), {{COMPANY_NAME, "Fox"}}}, - - // Tests that saving an identical profile except a loss of information - // results in a merge but the original value is not overwritten (no - // information loss). - {ProfileFields(), {{COMPANY_NAME, ""}}, {{COMPANY_NAME, "Fox"}}}, - - // Tests that saving an identical profile except a slightly different - // postal code results in a merge with the new value kept. - {{{ADDRESS_HOME_ZIP, "R2C 0A1"}}, - {{ADDRESS_HOME_ZIP, "R2C0A1"}}, - {{ADDRESS_HOME_ZIP, "R2C0A1"}}}, - {{{ADDRESS_HOME_ZIP, "R2C0A1"}}, - {{ADDRESS_HOME_ZIP, "R2C 0A1"}}, - {{ADDRESS_HOME_ZIP, "R2C 0A1"}}}, - {{{ADDRESS_HOME_ZIP, "r2c 0a1"}}, - {{ADDRESS_HOME_ZIP, "R2C0A1"}}, - {{ADDRESS_HOME_ZIP, "R2C0A1"}}}, - - // Tests that saving an identical profile plus a new piece of information - // on the address line 2 results in a merge and that the original empty - // value gets overwritten by the new information. - {{{ADDRESS_HOME_LINE2, ""}}, - ProfileFields(), - {{ADDRESS_HOME_LINE2, "unit 5"}}}, - - // Tests that saving an identical profile except a loss of information on - // the address line 2 results in a merge but that the original value gets - // not overwritten (no information loss). - {ProfileFields(), - {{ADDRESS_HOME_LINE2, ""}}, - {{ADDRESS_HOME_LINE2, "unit 5"}}}, - - // Tests that saving an identical except with more punctuation in the fist - // address line, while the second is empty, results in a merge and that - // the original address gets overwritten. - {{{ADDRESS_HOME_LINE2, ""}}, - {{ADDRESS_HOME_LINE2, ""}, {ADDRESS_HOME_LINE1, "123, Zoo St."}}, - {{ADDRESS_HOME_LINE1, "123, Zoo St."}}}, - - // Tests that saving an identical profile except with less punctuation in - // the fist address line, while the second is empty, results in a merge - // and that the longer address is retained. - {{{ADDRESS_HOME_LINE2, ""}, {ADDRESS_HOME_LINE1, "123, Zoo St."}}, - {{ADDRESS_HOME_LINE2, ""}}, - {{ADDRESS_HOME_LINE1, "123 Zoo St"}}}, - - // Tests that saving an identical profile except additional punctuation in - // the two address lines results in a merge and that the newer address - // is retained. - {ProfileFields(), - {{ADDRESS_HOME_LINE1, "123, Zoo St."}, {ADDRESS_HOME_LINE2, "unit. 5"}}, - {{ADDRESS_HOME_LINE1, "123, Zoo St."}, {ADDRESS_HOME_LINE2, "unit. 5"}}}, - - // Tests that saving an identical profile except less punctuation in the - // two address lines results in a merge and that the newer address is - // retained. - {{{ADDRESS_HOME_LINE1, "123, Zoo St."}, {ADDRESS_HOME_LINE2, "unit. 5"}}, - ProfileFields(), - {{ADDRESS_HOME_LINE1, "123 Zoo St"}, {ADDRESS_HOME_LINE2, "unit 5"}}}, - - // Tests that saving an identical profile with accented characters in - // the two address lines results in a merge and that the newer address - // is retained. - {ProfileFields(), - {{ADDRESS_HOME_LINE1, "123 Zôö St"}, {ADDRESS_HOME_LINE2, "üñìt 5"}}, - {{ADDRESS_HOME_LINE1, "123 Zôö St"}, {ADDRESS_HOME_LINE2, "üñìt 5"}}}, - - // Tests that saving an identical profile without accented characters in - // the two address lines results in a merge and that the newer address - // is retained. - {{{ADDRESS_HOME_LINE1, "123 Zôö St"}, {ADDRESS_HOME_LINE2, "üñìt 5"}}, - ProfileFields(), - {{ADDRESS_HOME_LINE1, "123 Zoo St"}, {ADDRESS_HOME_LINE2, "unit 5"}}}, - - // Tests that saving an identical profile except that the address line 1 - // is in the address line 2 results in a merge and that the multi-lne - // address is retained. - {ProfileFields(), - {{ADDRESS_HOME_LINE1, "123 Zoo St, unit 5"}, {ADDRESS_HOME_LINE2, ""}}, - {{ADDRESS_HOME_LINE1, "123 Zoo St"}, {ADDRESS_HOME_LINE2, "unit 5"}}}, - - // Tests that saving an identical profile except that the address line 2 - // contains part of the old address line 1 results in a merge and that the - // original address lines of the reference profile get overwritten. - {{{ADDRESS_HOME_LINE1, "123 Zoo St, unit 5"}, {ADDRESS_HOME_LINE2, ""}}, - ProfileFields(), - {{ADDRESS_HOME_LINE1, "123 Zoo St"}, {ADDRESS_HOME_LINE2, "unit 5"}}}, - - // Tests that saving an identical profile except that the state is the - // abbreviation instead of the full form results in a merge and that the - // original state gets overwritten. - {{{ADDRESS_HOME_STATE, "California"}}, - ProfileFields(), - {{ADDRESS_HOME_STATE, "CA"}}}, - - // Tests that saving an identical profile except that the state is the - // full form instead of the abbreviation results in a merge and that the - // abbreviated state is retained. - {ProfileFields(), - {{ADDRESS_HOME_STATE, "California"}}, - {{ADDRESS_HOME_STATE, "CA"}}}, - - // Tests that saving and identical profile except that the company name - // has different punctuation and case results in a merge and that the - // syntax of the new profile replaces the old one. - {{{COMPANY_NAME, "Stark inc"}}, - {{COMPANY_NAME, "Stark Inc."}}, - {{COMPANY_NAME, "Stark Inc."}}}, - }; + } SaveImportedProfileTestCase; + + class SaveImportedProfileTest + : public PersonalDataManagerTestBase, + public testing::TestWithParam<SaveImportedProfileTestCase> { + public: + SaveImportedProfileTest() {} + ~SaveImportedProfileTest() override {} + + void SetUp() override { + OSCryptMocker::SetUpWithSingleton(); + prefs_ = test::PrefServiceForTesting(); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + base::FilePath path = temp_dir_.GetPath().AppendASCII("TestWebDB"); + web_database_ = + new WebDatabaseService(path, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get()); + + // Setup account tracker. + signin_client_.reset(new TestSigninClient(prefs_.get())); + account_tracker_.reset(new AccountTrackerService()); + account_tracker_->Initialize(signin_client_.get()); + signin_manager_.reset(new FakeSigninManagerBase(signin_client_.get(), + account_tracker_.get())); + signin_manager_->Initialize(prefs_.get()); + + // Hacky: hold onto a pointer but pass ownership. + autofill_table_ = new AutofillTable; + web_database_->AddTable( + std::unique_ptr<WebDatabaseTable>(autofill_table_)); + web_database_->LoadDatabase(); + autofill_database_service_ = new AutofillWebDataService( + web_database_, base::ThreadTaskRunnerHandle::Get(), + base::ThreadTaskRunnerHandle::Get(), + WebDataServiceBase::ProfileErrorCallback()); + autofill_database_service_->Init(); + + test::DisableSystemServices(prefs_.get()); + ResetPersonalDataManager(USER_MODE_NORMAL); + + // Reset the deduping pref to its default value. + personal_data_->pref_service_->SetInteger( + prefs::kAutofillLastVersionDeduped, 0); + personal_data_->pref_service_->SetBoolean( + prefs::kAutofillProfileUseDatesFixed, false); + } - // Create the test clock. - TestAutofillClock test_clock; + void TearDown() override { + // Order of destruction is important as AutofillManager relies on + // PersonalDataManager to be around when it gets destroyed. + signin_manager_->Shutdown(); + signin_manager_.reset(); + + account_tracker_->Shutdown(); + account_tracker_.reset(); + signin_client_.reset(); + + test::DisableSystemServices(prefs_.get()); + OSCryptMocker::TearDown(); + } + }; - for (TestCase test_case : test_cases) { + TEST_P(SaveImportedProfileTest, SaveImportedProfile) { + // Create the test clock. + TestAutofillClock test_clock; + auto test_case = GetParam(); // Set the time to a specific value. test_clock.SetNow(kArbitraryTime); @@ -4490,45 +4310,322 @@ TEST_F(PersonalDataManagerTest, SaveImportedProfile) { // Erase the profiles for the next test. ResetProfiles(); } -} - -// Tests that MergeProfile tries to merge the imported profile into the -// existing profile in decreasing order of frecency. -TEST_F(PersonalDataManagerTest, MergeProfile_Frecency) { - // Create two very similar profiles except with different company names. - std::unique_ptr<AutofillProfile> profile1 = base::MakeUnique<AutofillProfile>( - base::GenerateGUID(), "https://www.example.com"); - test::SetProfileInfo(profile1.get(), "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "SNP", "742 Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", "12345678910"); - AutofillProfile* profile2 = - new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); - test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "Fox", "742 Evergreen Terrace", - "", "Springfield", "IL", "91601", "US", "12345678910"); - // Give the "Fox" profile a bigger frecency score. - profile2->set_use_count(15); + INSTANTIATE_TEST_CASE_P( + PersonalDataManagerTest, + SaveImportedProfileTest, + testing::Values( + // Test that saving an identical profile except for the name results + // in two profiles being saved. + SaveImportedProfileTestCase{ProfileFields(), + {{NAME_FIRST, "Marionette"}}}, + + // Test that saving an identical profile except with the middle name + // initial instead of the full middle name results in the profiles + // getting merged and the full middle name being kept. + SaveImportedProfileTestCase{ + ProfileFields(), + {{NAME_MIDDLE, "M"}}, + {{NAME_MIDDLE, "Mitchell"}, + {NAME_FULL, "Marion Mitchell Morrison"}}}, + + // Test that saving an identical profile except with the full middle + // name instead of the middle name initial results in the profiles + // getting merged and the full middle name replacing the initial. + SaveImportedProfileTestCase{{{NAME_MIDDLE, "M"}}, + {{NAME_MIDDLE, "Mitchell"}}, + {{NAME_MIDDLE, "Mitchell"}}}, + + // Test that saving an identical profile except with no middle name + // results in the profiles getting merged and the full middle name + // being kept. + SaveImportedProfileTestCase{ProfileFields(), + {{NAME_MIDDLE, ""}}, + {{NAME_MIDDLE, "Mitchell"}}}, + + // Test that saving an identical profile except with a middle name + // initial results in the profiles getting merged and the middle name + // initial being saved. + SaveImportedProfileTestCase{{{NAME_MIDDLE, ""}}, + {{NAME_MIDDLE, "M"}}, + {{NAME_MIDDLE, "M"}}}, + + // Test that saving an identical profile except with a middle name + // results in the profiles getting merged and the full middle name + // being saved. + SaveImportedProfileTestCase{{{NAME_MIDDLE, ""}}, + {{NAME_MIDDLE, "Mitchell"}}, + {{NAME_MIDDLE, "Mitchell"}}}, + + // Test that saving a identical profile except with the full name set + // instead of the name parts results in the two profiles being merged + // and all the name parts kept and the full name being added. + SaveImportedProfileTestCase{ + { + {NAME_FIRST, "Marion"}, + {NAME_MIDDLE, "Mitchell"}, + {NAME_LAST, "Morrison"}, + {NAME_FULL, ""}, + }, + { + {NAME_FIRST, ""}, + {NAME_MIDDLE, ""}, + {NAME_LAST, ""}, + {NAME_FULL, "Marion Mitchell Morrison"}, + }, + { + {NAME_FIRST, "Marion"}, + {NAME_MIDDLE, "Mitchell"}, + {NAME_LAST, "Morrison"}, + {NAME_FULL, "Marion Mitchell Morrison"}, + }, + }, - // Create the |existing_profiles| vector. - std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; - existing_profiles.push_back(std::move(profile1)); - existing_profiles.push_back(base::WrapUnique(profile2)); + // Test that saving a identical profile except with the name parts set + // instead of the full name results in the two profiles being merged + // and the full name being kept and all the name parts being added. + SaveImportedProfileTestCase{ + { + {NAME_FIRST, ""}, + {NAME_MIDDLE, ""}, + {NAME_LAST, ""}, + {NAME_FULL, "Marion Mitchell Morrison"}, + }, + { + {NAME_FIRST, "Marion"}, + {NAME_MIDDLE, "Mitchell"}, + {NAME_LAST, "Morrison"}, + {NAME_FULL, ""}, + }, + { + {NAME_FIRST, "Marion"}, + {NAME_MIDDLE, "Mitchell"}, + {NAME_LAST, "Morrison"}, + {NAME_FULL, "Marion Mitchell Morrison"}, + }, + }, - // Create a new imported profile with no company name. - AutofillProfile imported_profile(base::GenerateGUID(), - "https://www.example.com"); - test::SetProfileInfo(&imported_profile, "Homer", "Jay", "Simpson", - "homer.simpson@abc.com", "", "742 Evergreen Terrace", "", - "Springfield", "IL", "91601", "US", "12345678910"); + // Test that saving a profile that has only a full name set does not + // get merged with a profile with only the name parts set if the names + // are different. + SaveImportedProfileTestCase{ + { + {NAME_FIRST, "Marion"}, + {NAME_MIDDLE, "Mitchell"}, + {NAME_LAST, "Morrison"}, + {NAME_FULL, ""}, + }, + { + {NAME_FIRST, ""}, + {NAME_MIDDLE, ""}, + {NAME_LAST, ""}, + {NAME_FULL, "John Thompson Smith"}, + }, + }, - // Merge the imported profile into the existing profiles. - std::vector<AutofillProfile> profiles; - std::string guid = personal_data_->MergeProfile( - imported_profile, &existing_profiles, "US-EN", &profiles); + // Test that saving a profile that has only the name parts set does + // not get merged with a profile with only the full name set if the + // names are different. + SaveImportedProfileTestCase{ + { + {NAME_FIRST, ""}, + {NAME_MIDDLE, ""}, + {NAME_LAST, ""}, + {NAME_FULL, "John Thompson Smith"}, + }, + { + {NAME_FIRST, "Marion"}, + {NAME_MIDDLE, "Mitchell"}, + {NAME_LAST, "Morrison"}, + {NAME_FULL, ""}, + }, + }, - // The new profile should be merged into the "fox" profile. - EXPECT_EQ(profile2->guid(), guid); + // Test that saving an identical profile except for the first address + // line results in two profiles being saved. + SaveImportedProfileTestCase{ + ProfileFields(), + {{ADDRESS_HOME_LINE1, "123 Aquarium St."}}}, + + // Test that saving an identical profile except for the second address + // line results in two profiles being saved. + SaveImportedProfileTestCase{ProfileFields(), + {{ADDRESS_HOME_LINE2, "unit 7"}}}, + + // Tests that saving an identical profile that has a new piece of + // information (company name) results in a merge and that the original + // empty value gets overwritten by the new information. + SaveImportedProfileTestCase{{{COMPANY_NAME, ""}}, + ProfileFields(), + {{COMPANY_NAME, "Fox"}}}, + + // Tests that saving an identical profile except a loss of information + // results in a merge but the original value is not overwritten (no + // information loss). + SaveImportedProfileTestCase{ProfileFields(), + {{COMPANY_NAME, ""}}, + {{COMPANY_NAME, "Fox"}}}, + + // Tests that saving an identical profile except a slightly different + // postal code results in a merge with the new value kept. + SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, "R2C 0A1"}}, + {{ADDRESS_HOME_ZIP, "R2C0A1"}}, + {{ADDRESS_HOME_ZIP, "R2C0A1"}}}, + SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, "R2C0A1"}}, + {{ADDRESS_HOME_ZIP, "R2C 0A1"}}, + {{ADDRESS_HOME_ZIP, "R2C 0A1"}}}, + SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, "r2c 0a1"}}, + {{ADDRESS_HOME_ZIP, "R2C0A1"}}, + {{ADDRESS_HOME_ZIP, "R2C0A1"}}}, + + // Tests that saving an identical profile plus a new piece of + // information on the address line 2 results in a merge and that the + // original empty value gets overwritten by the new information. + SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE2, ""}}, + ProfileFields(), + {{ADDRESS_HOME_LINE2, "unit 5"}}}, + + // Tests that saving an identical profile except a loss of information + // on the address line 2 results in a merge but that the original + // value gets not overwritten (no information loss). + SaveImportedProfileTestCase{ProfileFields(), + {{ADDRESS_HOME_LINE2, ""}}, + {{ADDRESS_HOME_LINE2, "unit 5"}}}, + + // Tests that saving an identical except with more punctuation in the + // fist address line, while the second is empty, results in a merge + // and that the original address gets overwritten. + SaveImportedProfileTestCase{ + {{ADDRESS_HOME_LINE2, ""}}, + {{ADDRESS_HOME_LINE2, ""}, {ADDRESS_HOME_LINE1, "123, Zoo St."}}, + {{ADDRESS_HOME_LINE1, "123, Zoo St."}}}, + + // Tests that saving an identical profile except with less punctuation + // in the fist address line, while the second is empty, results in a + // merge and that the longer address is retained. + SaveImportedProfileTestCase{ + {{ADDRESS_HOME_LINE2, ""}, {ADDRESS_HOME_LINE1, "123, Zoo St."}}, + {{ADDRESS_HOME_LINE2, ""}}, + {{ADDRESS_HOME_LINE1, "123 Zoo St"}}}, + + // Tests that saving an identical profile except additional + // punctuation in the two address lines results in a merge and that + // the newer address is retained. + SaveImportedProfileTestCase{ProfileFields(), + {{ADDRESS_HOME_LINE1, "123, Zoo St."}, + {ADDRESS_HOME_LINE2, "unit. 5"}}, + {{ADDRESS_HOME_LINE1, "123, Zoo St."}, + {ADDRESS_HOME_LINE2, "unit. 5"}}}, + + // Tests that saving an identical profile except less punctuation in + // the two address lines results in a merge and that the newer address + // is retained. + SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE1, "123, Zoo St."}, + {ADDRESS_HOME_LINE2, "unit. 5"}}, + ProfileFields(), + {{ADDRESS_HOME_LINE1, "123 Zoo St"}, + {ADDRESS_HOME_LINE2, "unit 5"}}}, + + // Tests that saving an identical profile with accented characters in + // the two address lines results in a merge and that the newer address + // is retained. + SaveImportedProfileTestCase{ProfileFields(), + {{ADDRESS_HOME_LINE1, "123 Zôö St"}, + {ADDRESS_HOME_LINE2, "üñìt 5"}}, + {{ADDRESS_HOME_LINE1, "123 Zôö St"}, + {ADDRESS_HOME_LINE2, "üñìt 5"}}}, + + // Tests that saving an identical profile without accented characters + // in the two address lines results in a merge and that the newer + // address is retained. + SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE1, "123 Zôö St"}, + {ADDRESS_HOME_LINE2, "üñìt 5"}}, + ProfileFields(), + {{ADDRESS_HOME_LINE1, "123 Zoo St"}, + {ADDRESS_HOME_LINE2, "unit 5"}}}, + + // Tests that saving an identical profile except that the address line + // 1 is in the address line 2 results in a merge and that the + // multi-lne address is retained. + SaveImportedProfileTestCase{ + ProfileFields(), + {{ADDRESS_HOME_LINE1, "123 Zoo St, unit 5"}, + {ADDRESS_HOME_LINE2, ""}}, + {{ADDRESS_HOME_LINE1, "123 Zoo St"}, + {ADDRESS_HOME_LINE2, "unit 5"}}}, + + // Tests that saving an identical profile except that the address line + // 2 contains part of the old address line 1 results in a merge and + // that the original address lines of the reference profile get + // overwritten. + SaveImportedProfileTestCase{ + {{ADDRESS_HOME_LINE1, "123 Zoo St, unit 5"}, + {ADDRESS_HOME_LINE2, ""}}, + ProfileFields(), + {{ADDRESS_HOME_LINE1, "123 Zoo St"}, + {ADDRESS_HOME_LINE2, "unit 5"}}}, + + // Tests that saving an identical profile except that the state is the + // abbreviation instead of the full form results in a merge and that + // the original state gets overwritten. + SaveImportedProfileTestCase{{{ADDRESS_HOME_STATE, "California"}}, + ProfileFields(), + {{ADDRESS_HOME_STATE, "CA"}}}, + + // Tests that saving an identical profile except that the state is the + // full form instead of the abbreviation results in a merge and that + // the abbreviated state is retained. + SaveImportedProfileTestCase{ProfileFields(), + {{ADDRESS_HOME_STATE, "California"}}, + {{ADDRESS_HOME_STATE, "CA"}}}, + + // Tests that saving and identical profile except that the company + // name has different punctuation and case results in a merge and that + // the syntax of the new profile replaces the old one. + SaveImportedProfileTestCase{{{COMPANY_NAME, "Stark inc"}}, + {{COMPANY_NAME, "Stark Inc."}}, + {{COMPANY_NAME, "Stark Inc."}}})); + + // Tests that MergeProfile tries to merge the imported profile into the + // existing profile in decreasing order of frecency. + TEST_F(PersonalDataManagerTest, MergeProfile_Frecency) { + // Create two very similar profiles except with different company names. + std::unique_ptr<AutofillProfile> profile1 = + base::MakeUnique<AutofillProfile>(base::GenerateGUID(), + "https://www.example.com"); + test::SetProfileInfo(profile1.get(), "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "SNP", + "742 Evergreen Terrace", "", "Springfield", "IL", + "91601", "US", "12345678910"); + AutofillProfile* profile2 = + new AutofillProfile(base::GenerateGUID(), "https://www.example.com"); + test::SetProfileInfo(profile2, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "Fox", + "742 Evergreen Terrace", "", "Springfield", "IL", + "91601", "US", "12345678910"); + + // Give the "Fox" profile a bigger frecency score. + profile2->set_use_count(15); + + // Create the |existing_profiles| vector. + std::vector<std::unique_ptr<AutofillProfile>> existing_profiles; + existing_profiles.push_back(std::move(profile1)); + existing_profiles.push_back(base::WrapUnique(profile2)); + + // Create a new imported profile with no company name. + AutofillProfile imported_profile(base::GenerateGUID(), + "https://www.example.com"); + test::SetProfileInfo(&imported_profile, "Homer", "Jay", "Simpson", + "homer.simpson@abc.com", "", "742 Evergreen Terrace", + "", "Springfield", "IL", "91601", "US", "12345678910"); + + // Merge the imported profile into the existing profiles. + std::vector<AutofillProfile> profiles; + std::string guid = personal_data_->MergeProfile( + imported_profile, &existing_profiles, "US-EN", &profiles); + + // The new profile should be merged into the "fox" profile. + EXPECT_EQ(profile2->guid(), guid); } // Tests that MergeProfile produces a merged profile with the expected usage diff --git a/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc b/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc index 240319e1663..17a24311876 100644 --- a/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc +++ b/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc @@ -46,101 +46,117 @@ TEST(PhoneNumberI18NTest, NormalizePhoneNumber) { EXPECT_EQ(NormalizePhoneNumber(phone5, "US"), ASCIIToUTF16("6502346789")); } -TEST(PhoneNumberI18NTest, ParsePhoneNumber) { - const struct test_case { - // Expected parsing result. - bool valid; - // Inputs. - std::string input; - std::string assumed_region; - // Further expectations. - std::string number; - std::string city_code; - std::string country_code; - std::string deduced_region; - } test_cases[] = { +struct ParseNumberTestCase { + // Expected parsing result. + bool valid; + // Inputs. + std::string input; + std::string assumed_region; + // Further expectations. + std::string number; + std::string city_code; + std::string country_code; + std::string deduced_region; +}; + +class ParseNumberTest : public testing::TestWithParam<ParseNumberTestCase> {}; + +TEST_P(ParseNumberTest, ParsePhoneNumber) { + auto test_case = GetParam(); + SCOPED_TRACE("Testing phone number " + test_case.input); + + base::string16 country_code, city_code, number; + std::string deduced_region; + ::i18n::phonenumbers::PhoneNumber unused_i18n_number; + EXPECT_EQ( + test_case.valid, + ParsePhoneNumber(ASCIIToUTF16(test_case.input), test_case.assumed_region, + &country_code, &city_code, &number, &deduced_region, + &unused_i18n_number)); + EXPECT_EQ(ASCIIToUTF16(test_case.number), number); + EXPECT_EQ(ASCIIToUTF16(test_case.city_code), city_code); + EXPECT_EQ(ASCIIToUTF16(test_case.country_code), country_code); + EXPECT_EQ(test_case.deduced_region, deduced_region); +} + +INSTANTIATE_TEST_CASE_P( + PhoneNumberI18NTest, + ParseNumberTest, + testing::Values( // Test for empty string. Should give back empty strings. - {false, "", "US"}, + ParseNumberTestCase{false, "", "US"}, // Test for string with less than 7 digits. Should give back empty // strings. - {false, "1234", "US"}, + ParseNumberTestCase{false, "1234", "US"}, // Test for string with exactly 7 digits. // Not a valid number - starts with 1 - {false, "17134567", "US"}, + ParseNumberTestCase{false, "17134567", "US"}, // Not a valid number - does not have area code. - {false, "7134567", "US"}, + ParseNumberTestCase{false, "7134567", "US"}, // Valid Canadian toll-free number. - {true, "3101234", "US", "3101234", "", "", "CA"}, + ParseNumberTestCase{true, "3101234", "US", "3101234", "", "", "CA"}, // Test for string with greater than 7 digits but less than 10 digits. // Should fail parsing in US. - {false, "123456789", "US"}, + ParseNumberTestCase{false, "123456789", "US"}, // Test for string with greater than 7 digits but less than 10 digits // and // separators. // Should fail parsing in US. - {false, "12.345-6789", "US"}, + ParseNumberTestCase{false, "12.345-6789", "US"}, // Test for string with exactly 10 digits. // Should give back phone number and city code. // This one going to fail because of the incorrect area code. - {false, "1234567890", "US"}, + ParseNumberTestCase{false, "1234567890", "US"}, // This one going to fail because of the incorrect number (starts with // 1). - {false, "6501567890", "US"}, - {true, "6504567890", "US", "4567890", "650", "", "US"}, + ParseNumberTestCase{false, "6501567890", "US"}, + ParseNumberTestCase{true, "6504567890", "US", "4567890", "650", "", + "US"}, // Test for string with exactly 10 digits and separators. // Should give back phone number and city code. - {true, "(650) 456-7890", "US", "4567890", "650", "", "US"}, + ParseNumberTestCase{true, "(650) 456-7890", "US", "4567890", "650", "", + "US"}, // Tests for string with over 10 digits. // 01 is incorrect prefix in the USA, and if we interpret 011 as prefix, // the // rest is too short for international number - the parsing should fail. - {false, "0116504567890", "US"}, + ParseNumberTestCase{false, "0116504567890", "US"}, // 011 is a correct "dial out" prefix in the USA - the parsing should // succeed. - {true, "01116504567890", "US", "4567890", "650", "1", "US"}, + ParseNumberTestCase{true, "01116504567890", "US", "4567890", "650", "1", + "US"}, // 011 is a correct "dial out" prefix in the USA but the rest of the // number // can't parse as a US number. - {true, "01178124567890", "US", "4567890", "812", "7", "RU"}, + ParseNumberTestCase{true, "01178124567890", "US", "4567890", "812", "7", + "RU"}, // Test for string with over 10 digits with separator characters. // Should give back phone number, city code, and country code. "011" is // US "dial out" code, which is discarded. - {true, "(0111) 650-456.7890", "US", "4567890", "650", "1", "US"}, + ParseNumberTestCase{true, "(0111) 650-456.7890", "US", "4567890", "650", + "1", "US"}, // Now try phone from Czech republic - it has 00 dial out code, 420 // country // code and variable length area codes. - {true, "+420 27-89.10.112", "US", "910112", "278", "420", "CZ"}, - {false, "27-89.10.112", "US"}, - {true, "27-89.10.112", "CZ", "910112", "278", "", "CZ"}, - {false, "420 57-89.10.112", "US"}, - {true, "420 57-89.10.112", "CZ", "910112", "578", "420", "CZ"}, + ParseNumberTestCase{true, "+420 27-89.10.112", "US", "910112", "278", + "420", "CZ"}, + ParseNumberTestCase{false, "27-89.10.112", "US"}, + ParseNumberTestCase{true, "27-89.10.112", "CZ", "910112", "278", "", + "CZ"}, + ParseNumberTestCase{false, "420 57-89.10.112", "US"}, + ParseNumberTestCase{true, "420 57-89.10.112", "CZ", "910112", "578", + "420", "CZ"}, // Parses vanity numbers. - {true, "1-650-FLOWERS", "US", "3569377", "650", "1", "US"}, + ParseNumberTestCase{true, "1-650-FLOWERS", "US", "3569377", "650", "1", + "US"}, // 800 is not an area code, but the destination code. In our library // these // codes should be treated the same as area codes. - {true, "1-800-FLOWERS", "US", "3569377", "800", "1", "US"}, + ParseNumberTestCase{true, "1-800-FLOWERS", "US", "3569377", "800", "1", + "US"}, // Don't add a country code where there was none. - {true, "(08) 450 777 7777", "DE", "7777777", "8450", "", "DE"}, - }; - - for (const auto& test_case : test_cases) { - SCOPED_TRACE("Testing phone number " + test_case.input); - - base::string16 country_code, city_code, number; - std::string deduced_region; - ::i18n::phonenumbers::PhoneNumber unused_i18n_number; - EXPECT_EQ( - test_case.valid, - ParsePhoneNumber(ASCIIToUTF16(test_case.input), - test_case.assumed_region, &country_code, &city_code, - &number, &deduced_region, &unused_i18n_number)); - EXPECT_EQ(ASCIIToUTF16(test_case.number), number); - EXPECT_EQ(ASCIIToUTF16(test_case.city_code), city_code); - EXPECT_EQ(ASCIIToUTF16(test_case.country_code), country_code); - EXPECT_EQ(test_case.deduced_region, deduced_region); - } -} + ParseNumberTestCase{true, "(08) 450 777 7777", "DE", "7777777", "8450", + "", "DE"})); TEST(PhoneNumberI18NTest, ConstructPhoneNumber) { base::string16 number; diff --git a/chromium/components/autofill/core/browser/region_combobox_model.cc b/chromium/components/autofill/core/browser/region_combobox_model.cc new file mode 100644 index 00000000000..509bd596e31 --- /dev/null +++ b/chromium/components/autofill/core/browser/region_combobox_model.cc @@ -0,0 +1,117 @@ +// Copyright 2017 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 "components/autofill/core/browser/region_combobox_model.h" + +#include <utility> + +#include "base/callback.h" +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "components/strings/grit/components_strings.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/region_data.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/region_data_builder.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/combobox_model_observer.h" + +namespace autofill { + +RegionComboboxModel::RegionComboboxModel( + std::unique_ptr<const ::i18n::addressinput::Source> source, + std::unique_ptr<::i18n::addressinput::Storage> storage, + const std::string& app_locale, + const std::string& country_code) + : failed_to_load_data_(false), + pending_region_data_load_(false), + app_locale_(app_locale), + region_data_supplier_(source.release(), storage.release()) { + region_data_supplier_callback_.reset(::i18n::addressinput::BuildCallback( + this, &RegionComboboxModel::RegionDataLoaded)); + LoadRegionData(country_code); +} + +RegionComboboxModel::~RegionComboboxModel() {} + +int RegionComboboxModel::GetItemCount() const { + // The combobox view needs to always have at least one item. If the regions + // have not been completely loaded yet, we display a single "loading" item. + if (regions_.size() == 0) + return 1; + return regions_.size(); +} + +base::string16 RegionComboboxModel::GetItemAt(int index) { + DCHECK_GE(index, 0); + // This might happen because of the asynchonous nature of the data. + if (static_cast<size_t>(index) >= regions_.size()) + return l10n_util::GetStringUTF16(IDS_AUTOFILL_LOADING_REGIONS); + + if (!regions_[index].first.empty()) + return base::UTF8ToUTF16(regions_[index].second); + + // The separator item. Implemented for platforms that don't yet support + // IsItemSeparatorAt(). + return base::ASCIIToUTF16("---"); +} + +bool RegionComboboxModel::IsItemSeparatorAt(int index) { + // This might happen because of the asynchonous nature of the data. + DCHECK_GE(index, 0); + if (static_cast<size_t>(index) >= regions_.size()) + return false; + return regions_[index].first.empty(); +} + +void RegionComboboxModel::AddObserver(ui::ComboboxModelObserver* observer) { + observers_.AddObserver(observer); +} + +void RegionComboboxModel::RemoveObserver(ui::ComboboxModelObserver* observer) { + observers_.RemoveObserver(observer); +} + +void RegionComboboxModel::SetFailureModeForTests(bool failed_to_load_data) { + failed_to_load_data_ = failed_to_load_data; + for (auto& observer : observers_) { + observer.OnComboboxModelChanged(this); + } +} + +void RegionComboboxModel::LoadRegionData(const std::string& country_code) { + pending_region_data_load_ = true; + region_data_supplier_.LoadRules(country_code, + *region_data_supplier_callback_.get()); +} + +void RegionComboboxModel::RegionDataLoaded(bool success, + const std::string& country_code, + int rule_count) { + pending_region_data_load_ = false; + if (success) { + std::string best_region_tree_language_tag; + ::i18n::addressinput::RegionDataBuilder builder(®ion_data_supplier_); + const std::vector<const ::i18n::addressinput::RegionData*>& regions = + builder.Build(country_code, app_locale_, &best_region_tree_language_tag) + .sub_regions(); + // Some countries expose a state field but have not region names available. + if (regions.size() > 0) { + failed_to_load_data_ = false; + for (auto* const region : regions) { + regions_.push_back(std::make_pair(region->key(), region->name())); + } + } else { + failed_to_load_data_ = true; + } + } else { + // TODO(mad): Maybe use a static list as is done for countries in + // components\autofill\core\browser\country_data.cc + failed_to_load_data_ = true; + } + + for (auto& observer : observers_) { + observer.OnComboboxModelChanged(this); + } +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/region_combobox_model.h b/chromium/components/autofill/core/browser/region_combobox_model.h new file mode 100644 index 00000000000..aa16bc88e5d --- /dev/null +++ b/chromium/components/autofill/core/browser/region_combobox_model.h @@ -0,0 +1,88 @@ +// Copyright 2017 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 COMPONENTS_AUTOFILL_CORE_BROWSER_REGION_COMBOBOX_MODEL_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_REGION_COMBOBOX_MODEL_H_ + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/preload_supplier.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h" +#include "ui/base/models/combobox_model.h" + +namespace autofill { + +// A model for country regions (aka state/provinces) to be used to enter +// addresses. Note that loading these regions can happen asynchronously so a +// ui::ComboboxModelObserver should be attached to this model to be updated when +// the regions load is completed. +class RegionComboboxModel : public ui::ComboboxModel { + public: + // |source| and |storage| are needed to initialize the + // ::i18n::addressinput::PreloadSupplier, |app_locale| is needed for + // ::i18n::addressinput::RegionDataBuilder and |country_code| identifies which + // country's region to load into the model. + RegionComboboxModel( + std::unique_ptr<const ::i18n::addressinput::Source> source, + std::unique_ptr<::i18n::addressinput::Storage> storage, + const std::string& app_locale, + const std::string& country_code); + ~RegionComboboxModel() override; + + bool pending_region_data_load() const { return pending_region_data_load_; } + bool failed_to_load_data() const { return failed_to_load_data_; } + + // ui::ComboboxModel implementation: + int GetItemCount() const override; + base::string16 GetItemAt(int index) override; + bool IsItemSeparatorAt(int index) override; + void AddObserver(ui::ComboboxModelObserver* observer) override; + void RemoveObserver(ui::ComboboxModelObserver* observer) override; + + // To allow testing failure states. + void SetFailureModeForTests(bool failed_to_load_data); + + private: + // Start the potentially asynchronous process of loading region data. + void LoadRegionData(const std::string& country_code); + + // Callback for ::i18n::addressinput::PreloadSupplier::LoadRules + void RegionDataLoaded(bool success, const std::string&, int rule_count); + + // Whether the region data load failed or not. + bool failed_to_load_data_; + + // Set to true during region data load, and false otherwise. Whether the load + // succeeded or not doesn't affect this value. + bool pending_region_data_load_; + + // The application locale. + const std::string app_locale_; + + // The callback to give to |region_data_supplier_| for async operations. + ::i18n::addressinput::scoped_ptr< + ::i18n::addressinput::PreloadSupplier::Callback> + region_data_supplier_callback_; + + // A supplier of region data. + ::i18n::addressinput::PreloadSupplier region_data_supplier_; + + // List of <code, name> pairs for ADDRESS_HOME_STATE combobox values; + std::vector<std::pair<std::string, std::string>> regions_; + + // To be called when the data for the given country code was loaded. + base::ObserverList<ui::ComboboxModelObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(RegionComboboxModel); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_REGION_COMBOBOX_MODEL_H_ diff --git a/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc b/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc new file mode 100644 index 00000000000..52878f0e921 --- /dev/null +++ b/chromium/components/autofill/core/browser/region_combobox_model_unittest.cc @@ -0,0 +1,107 @@ +// Copyright 2017 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 "components/autofill/core/browser/region_combobox_model.h" + +#include <memory> + +#include "base/json/json_writer.h" +#include "base/memory/ptr_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/test_personal_data_manager.h" +#include "components/prefs/pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/null_storage.h" +#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h" + +namespace autofill { + +// Strings used in more than one place and must be the same everywhere. +const char kTestCountryCode[] = "CA"; +const char kQuebec[] = "Quebec"; +const char kOntario[] = "Ontario"; + +class RegionComboboxModelTest : public testing::Test { + public: + RegionComboboxModelTest() + : pref_service_(autofill::test::PrefServiceForTesting()) { + manager_.SetTestingPrefService(pref_service_.get()); + manager_.set_timezone_country_code(kTestCountryCode); + } + + void SetupCombobox(bool source_failure) { + model_.reset(new RegionComboboxModel( + base::MakeUnique<TestSource>(source_failure), + base::MakeUnique<::i18n::addressinput::NullStorage>(), + manager_.app_locale(), kTestCountryCode)); + } + + void TearDown() override { manager_.SetTestingPrefService(nullptr); } + + RegionComboboxModel* model() { return model_.get(); } + + private: + // The source that returns the region data. Using + // third_party/libaddressinput/src/cpp/test/testdata_source.h wouldn't help + // much since it only implements the Get method overriden below anyway. + class TestSource : public ::i18n::addressinput::Source { + public: + explicit TestSource(bool source_failure) + : source_failure_(source_failure) {} + ~TestSource() override {} + + void Get(const std::string& key, + const ::i18n::addressinput::Source::Callback& data_ready) + const override { + if (source_failure_) { + data_ready(false, key, nullptr); + return; + } + // Only set the fields needed to fill the combobox, since only the + // combobox code needs to be tested here. + std::string* json = new std::string("{\"data/CA\":{"); + *json += "\"id\":\"data/CA\","; + *json += "\"key\":\"CA\","; + *json += "\"sub_keys\":\"QC~ON\"},"; + + *json += "\"data/CA/ON\":{"; + *json += "\"id\":\"data/CA/ON\","; + *json += "\"key\":\"ON\","; + *json += "\"name\":\"Ontario\"},"; + + *json += "\"data/CA/QC\":{"; + *json += "\"id\":\"data/CA/QC\","; + *json += "\"key\":\"QC\","; + *json += "\"name\":\"Quebec\"}}"; + data_ready(true, key, json); + } + + private: + bool source_failure_{false}; + }; + TestPersonalDataManager manager_; + std::unique_ptr<PrefService> pref_service_; + std::unique_ptr<RegionComboboxModel> model_; +}; + +// Make sure the two regions returned by the source are properly set in the +// model. +TEST_F(RegionComboboxModelTest, QuebecOntarioRegions) { + SetupCombobox(false); + EXPECT_EQ(2, model()->GetItemCount()); + EXPECT_EQ(base::ASCIIToUTF16(kQuebec), model()->GetItemAt(0)); + EXPECT_EQ(base::ASCIIToUTF16(kOntario), model()->GetItemAt(1)); + EXPECT_FALSE(model()->failed_to_load_data()); +} + +// Make sure the combo box properly support source failures. +TEST_F(RegionComboboxModelTest, FailingSource) { + SetupCombobox(true); + EXPECT_EQ(1, model()->GetItemCount()); + EXPECT_TRUE(model()->failed_to_load_data()); +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/risk_data_loader.h b/chromium/components/autofill/core/browser/risk_data_loader.h new file mode 100644 index 00000000000..be3def3bdda --- /dev/null +++ b/chromium/components/autofill/core/browser/risk_data_loader.h @@ -0,0 +1,26 @@ +// Copyright 2017 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 COMPONENTS_AUTOFILL_CORE_BROWSER_RISK_DATA_LOADER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_RISK_DATA_LOADER_H_ + +#include <string> + +#include "base/callback.h" + +namespace autofill { + +class RiskDataLoader { + public: + // Gathers risk data and provides it to |callback|. + virtual void LoadRiskData( + const base::Callback<void(const std::string&)>& callback) = 0; + + protected: + virtual ~RiskDataLoader() {} +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_RISK_DATA_LOADER_H_ diff --git a/chromium/components/autofill/core/browser/test_autofill_client.cc b/chromium/components/autofill/core/browser/test_autofill_client.cc index 72b2da47cc1..1c67ac60f38 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.cc +++ b/chromium/components/autofill/core/browser/test_autofill_client.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "components/autofill/core/browser/test_autofill_client.h" +#if !defined(OS_ANDROID) +#include "components/autofill/core/browser/ui/mock_save_card_bubble_controller.h" +#endif #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" @@ -12,6 +15,9 @@ TestAutofillClient::TestAutofillClient() : token_service_(new FakeOAuth2TokenService()), identity_provider_(new FakeIdentityProvider(token_service_.get())), rappor_service_(new rappor::TestRapporServiceImpl()), +#if !defined(OS_ANDROID) + save_card_bubble_controller_(new MockSaveCardBubbleController()), +#endif form_origin_(GURL("https://example.test")) {} TestAutofillClient::~TestAutofillClient() { @@ -45,6 +51,14 @@ ukm::UkmService* TestAutofillClient::GetUkmService() { return ukm_service_test_harness_.test_ukm_service(); } +SaveCardBubbleController* TestAutofillClient::GetSaveCardBubbleController() { +#if defined(OS_ANDROID) + return nullptr; +#else + return save_card_bubble_controller_.get(); +#endif +} + void TestAutofillClient::ShowAutofillSettings() { } @@ -65,6 +79,7 @@ void TestAutofillClient::ConfirmSaveCreditCardLocally( void TestAutofillClient::ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) { callback.Run(); } @@ -117,9 +132,6 @@ void TestAutofillClient::DidFillOrPreviewField( const base::string16& profile_full_name) { } -void TestAutofillClient::OnFirstUserGestureObserved() { -} - bool TestAutofillClient::IsContextSecure() { // Simplified secure context check for tests. return form_origin_.SchemeIs("https"); diff --git a/chromium/components/autofill/core/browser/test_autofill_client.h b/chromium/components/autofill/core/browser/test_autofill_client.h index 5b25ecfabab..a140c772665 100644 --- a/chromium/components/autofill/core/browser/test_autofill_client.h +++ b/chromium/components/autofill/core/browser/test_autofill_client.h @@ -36,6 +36,7 @@ class TestAutofillClient : public AutofillClient { IdentityProvider* GetIdentityProvider() override; rappor::RapporServiceImpl* GetRapporServiceImpl() override; ukm::UkmService* GetUkmService() override; + SaveCardBubbleController* GetSaveCardBubbleController() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, UnmaskCardReason reason, @@ -46,6 +47,7 @@ class TestAutofillClient : public AutofillClient { void ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) override; void ConfirmCreditCardFillAssist(const CreditCard& card, const base::Closure& callback) override; @@ -68,7 +70,6 @@ class TestAutofillClient : public AutofillClient { const std::vector<autofill::FormStructure*>& forms) override; void DidFillOrPreviewField(const base::string16& autofilled_value, const base::string16& profile_full_name) override; - void OnFirstUserGestureObserved() override; // By default, TestAutofillClient will report that the context is // secure. This can be adjusted by calling set_form_origin() with an // http:// URL. @@ -98,6 +99,9 @@ class TestAutofillClient : public AutofillClient { std::unique_ptr<FakeIdentityProvider> identity_provider_; std::unique_ptr<rappor::TestRapporServiceImpl> rappor_service_; ukm::UkmServiceTestingHarness ukm_service_test_harness_; +#if !defined(OS_ANDROID) + std::unique_ptr<SaveCardBubbleController> save_card_bubble_controller_; +#endif GURL form_origin_; DISALLOW_COPY_AND_ASSIGN(TestAutofillClient); diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.cc b/chromium/components/autofill/core/browser/test_autofill_driver.cc index be3bc7b3fec..51a61ce03d5 100644 --- a/chromium/components/autofill/core/browser/test_autofill_driver.cc +++ b/chromium/components/autofill/core/browser/test_autofill_driver.cc @@ -17,7 +17,7 @@ TestAutofillDriver::TestAutofillDriver() TestAutofillDriver::~TestAutofillDriver() {} -bool TestAutofillDriver::IsOffTheRecord() const { +bool TestAutofillDriver::IsIncognito() const { return false; } diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.h b/chromium/components/autofill/core/browser/test_autofill_driver.h index 6ac0c9ccd02..c1839293b7f 100644 --- a/chromium/components/autofill/core/browser/test_autofill_driver.h +++ b/chromium/components/autofill/core/browser/test_autofill_driver.h @@ -25,7 +25,7 @@ class TestAutofillDriver : public AutofillDriver { ~TestAutofillDriver() override; // AutofillDriver implementation. - bool IsOffTheRecord() const override; + bool IsIncognito() const override; // Returns the value passed in to the last call to |SetURLRequestContext()| // or NULL if that method has never been called. net::URLRequestContextGetter* GetURLRequestContext() override; diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h index 98a4fa59d93..6e63936060c 100644 --- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h +++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller.h @@ -37,6 +37,7 @@ class CardUnmaskPromptController { virtual bool InputCvcIsValid(const base::string16& input_text) const = 0; virtual bool InputExpirationIsValid(const base::string16& month, const base::string16& year) const = 0; + virtual int GetExpectedCvcLength() const = 0; }; } // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc index 38778caf3bd..00c26f4c732 100644 --- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc +++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.cc @@ -309,6 +309,10 @@ bool CardUnmaskPromptControllerImpl::InputExpirationIsValid( AutofillClock::Now()); } +int CardUnmaskPromptControllerImpl::GetExpectedCvcLength() const { + return GetCvcLengthForCardType(card_.type()); +} + base::TimeDelta CardUnmaskPromptControllerImpl::GetSuccessMessageDuration() const { return base::TimeDelta::FromMilliseconds( diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h index e6b6af6dc44..f2717397cfd 100644 --- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h +++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl.h @@ -52,11 +52,13 @@ class CardUnmaskPromptControllerImpl : public CardUnmaskPromptController { bool InputCvcIsValid(const base::string16& input_text) const override; bool InputExpirationIsValid(const base::string16& month, const base::string16& year) const override; + int GetExpectedCvcLength() const override; base::TimeDelta GetSuccessMessageDuration() const override; protected: // Exposed for testing. CardUnmaskPromptView* view() { return card_unmask_view_; } + void SetCreditCardForTesting(CreditCard test_card) { card_ = test_card; } private: bool AllowsRetry(AutofillClient::PaymentsRpcResult result); diff --git a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc index 7a6ff9f04fc..703b6404fa4 100644 --- a/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc +++ b/chromium/components/autofill/core/browser/ui/card_unmask_prompt_controller_impl_unittest.cc @@ -15,6 +15,7 @@ #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_test_utils.h" +#include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/ui/card_unmask_prompt_view.h" #include "components/autofill/core/common/autofill_pref_names.h" #include "components/prefs/pref_registry_simple.h" @@ -71,6 +72,10 @@ class TestCardUnmaskPromptController : public CardUnmaskPromptControllerImpl { void set_can_store_locally(bool can) { can_store_locally_ = can; } + void SetCreditCardForTesting(CreditCard card) { + CardUnmaskPromptControllerImpl::SetCreditCardForTesting(card); + } + base::WeakPtr<TestCardUnmaskPromptController> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -82,19 +87,9 @@ class TestCardUnmaskPromptController : public CardUnmaskPromptControllerImpl { DISALLOW_COPY_AND_ASSIGN(TestCardUnmaskPromptController); }; -class CardUnmaskPromptControllerImplTest : public testing::Test { +class CardUnmaskPromptControllerImplGenericTest { public: - CardUnmaskPromptControllerImplTest() {} - ~CardUnmaskPromptControllerImplTest() override {} - - void SetUp() override { - test_unmask_prompt_view_.reset(new TestCardUnmaskPromptView()); - pref_service_.reset(new TestingPrefServiceSimple()); - controller_.reset(new TestCardUnmaskPromptController(pref_service_.get())); - delegate_.reset(new TestCardUnmaskDelegate()); - pref_service_->registry()->RegisterBooleanPref( - prefs::kAutofillWalletImportStorageCheckboxState, false); - } + CardUnmaskPromptControllerImplGenericTest() {} void ShowPrompt() { controller_->ShowPrompt(test_unmask_prompt_view_.get(), @@ -134,6 +129,26 @@ class CardUnmaskPromptControllerImplTest : public testing::Test { std::unique_ptr<TestCardUnmaskDelegate> delegate_; private: + DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptControllerImplGenericTest); +}; + +class CardUnmaskPromptControllerImplTest + : public CardUnmaskPromptControllerImplGenericTest, + public testing::Test { + public: + CardUnmaskPromptControllerImplTest() {} + ~CardUnmaskPromptControllerImplTest() override {} + + void SetUp() override { + test_unmask_prompt_view_.reset(new TestCardUnmaskPromptView()); + pref_service_.reset(new TestingPrefServiceSimple()); + controller_.reset(new TestCardUnmaskPromptController(pref_service_.get())); + delegate_.reset(new TestCardUnmaskDelegate()); + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillWalletImportStorageCheckboxState, false); + } + + private: DISALLOW_COPY_AND_ASSIGN(CardUnmaskPromptControllerImplTest); }; @@ -433,80 +448,138 @@ TEST_F(CardUnmaskPromptControllerImplTest, "Autofill.UnmaskPrompt.UnmaskingDuration.Failure", 1); } -TEST_F(CardUnmaskPromptControllerImplTest, CvcInputValidation) { - struct CvcCase { - const char* input; - bool valid; - // null when |valid| is false. - const char* canonicalized_input; - }; - CvcCase cvc_cases[] = { - { "123", true, "123" }, - { "123 ", true, "123" }, - { " 1234 ", false }, - { "IOU", false }, - }; +struct CvcCase { + const char* input; + bool valid; + // null when |valid| is false. + const char* canonicalized_input; +}; - ShowPrompt(); +class CvcInputValidationTest : public CardUnmaskPromptControllerImplGenericTest, + public testing::TestWithParam<CvcCase> { + public: + CvcInputValidationTest() {} + ~CvcInputValidationTest() override {} - for (const CvcCase& cvc_case : cvc_cases) { - EXPECT_EQ(cvc_case.valid, - controller_->InputCvcIsValid(ASCIIToUTF16(cvc_case.input))); - if (!cvc_case.valid) - continue; - - controller_->OnUnmaskResponse(ASCIIToUTF16(cvc_case.input), - ASCIIToUTF16("1"), ASCIIToUTF16("2050"), - false); - EXPECT_EQ(ASCIIToUTF16(cvc_case.canonicalized_input), - delegate_->response().cvc); + void SetUp() override { + test_unmask_prompt_view_.reset(new TestCardUnmaskPromptView()); + pref_service_.reset(new TestingPrefServiceSimple()); + controller_.reset(new TestCardUnmaskPromptController(pref_service_.get())); + delegate_.reset(new TestCardUnmaskDelegate()); + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillWalletImportStorageCheckboxState, false); } - CvcCase cvc_cases_amex[] = { - { "123", false }, - { "123 ", false }, - { "1234", true, "1234" }, - { "\t1234 ", true, "1234" }, - { " 1234", true, "1234" }, - { "IOU$", false }, - }; + private: + DISALLOW_COPY_AND_ASSIGN(CvcInputValidationTest); +}; - ShowPromptAmex(); +TEST_P(CvcInputValidationTest, CvcInputValidation) { + auto cvc_case = GetParam(); + ShowPrompt(); + EXPECT_EQ(cvc_case.valid, + controller_->InputCvcIsValid(ASCIIToUTF16(cvc_case.input))); + if (!cvc_case.valid) + return; + + controller_->OnUnmaskResponse(ASCIIToUTF16(cvc_case.input), ASCIIToUTF16("1"), + ASCIIToUTF16("2050"), false); + EXPECT_EQ(ASCIIToUTF16(cvc_case.canonicalized_input), + delegate_->response().cvc); +} - for (const CvcCase& cvc_case_amex : cvc_cases_amex) { - EXPECT_EQ(cvc_case_amex.valid, - controller_->InputCvcIsValid(ASCIIToUTF16(cvc_case_amex.input))); - if (!cvc_case_amex.valid) - continue; +INSTANTIATE_TEST_CASE_P(CardUnmaskPromptControllerImplTest, + CvcInputValidationTest, + testing::Values(CvcCase{"123", true, "123"}, + CvcCase{"123 ", true, "123"}, + CvcCase{" 1234 ", false}, + CvcCase{"IOU", false})); - controller_->OnUnmaskResponse(ASCIIToUTF16(cvc_case_amex.input), - base::string16(), base::string16(), false); - EXPECT_EQ(ASCIIToUTF16(cvc_case_amex.canonicalized_input), - delegate_->response().cvc); +class CvcInputAmexValidationTest + : public CardUnmaskPromptControllerImplGenericTest, + public testing::TestWithParam<CvcCase> { + public: + CvcInputAmexValidationTest() {} + ~CvcInputAmexValidationTest() override {} + + void SetUp() override { + test_unmask_prompt_view_.reset(new TestCardUnmaskPromptView()); + pref_service_.reset(new TestingPrefServiceSimple()); + controller_.reset(new TestCardUnmaskPromptController(pref_service_.get())); + delegate_.reset(new TestCardUnmaskDelegate()); + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillWalletImportStorageCheckboxState, false); } + + private: + DISALLOW_COPY_AND_ASSIGN(CvcInputAmexValidationTest); +}; + +TEST_P(CvcInputAmexValidationTest, CvcInputValidation) { + auto cvc_case_amex = GetParam(); + ShowPromptAmex(); + EXPECT_EQ(cvc_case_amex.valid, + controller_->InputCvcIsValid(ASCIIToUTF16(cvc_case_amex.input))); + if (!cvc_case_amex.valid) + return; + + controller_->OnUnmaskResponse(ASCIIToUTF16(cvc_case_amex.input), + base::string16(), base::string16(), false); + EXPECT_EQ(ASCIIToUTF16(cvc_case_amex.canonicalized_input), + delegate_->response().cvc); } -TEST_F(CardUnmaskPromptControllerImplTest, ExpirationDateValidation) { - struct { - const char* input_month; - const char* input_year; - bool valid; - } exp_cases[] = { - {"01", "2040", true}, - {"1", "2040", true}, - {"1", "40", true}, - {"10", "40", true}, - {"01", "1940", false}, - {"13", "2040", false}, - }; +INSTANTIATE_TEST_CASE_P(CardUnmaskPromptControllerImplTest, + CvcInputAmexValidationTest, + testing::Values(CvcCase{"123", false}, + CvcCase{"123 ", false}, + CvcCase{"1234", true, "1234"}, + CvcCase{"\t1234 ", true, "1234"}, + CvcCase{" 1234", true, "1234"}, + CvcCase{"IOU$", false})); + +struct ExpirationDateTestCase { + const char* input_month; + const char* input_year; + bool valid; +}; - ShowPrompt(); +class ExpirationDateValidationTest + : public CardUnmaskPromptControllerImplGenericTest, + public testing::TestWithParam<ExpirationDateTestCase> { + public: + ExpirationDateValidationTest() {} + ~ExpirationDateValidationTest() override {} - for (const auto& exp_case : exp_cases) { - EXPECT_EQ(exp_case.valid, controller_->InputExpirationIsValid( - ASCIIToUTF16(exp_case.input_month), - ASCIIToUTF16(exp_case.input_year))); + void SetUp() override { + test_unmask_prompt_view_.reset(new TestCardUnmaskPromptView()); + pref_service_.reset(new TestingPrefServiceSimple()); + controller_.reset(new TestCardUnmaskPromptController(pref_service_.get())); + delegate_.reset(new TestCardUnmaskDelegate()); + pref_service_->registry()->RegisterBooleanPref( + prefs::kAutofillWalletImportStorageCheckboxState, false); } + + private: + DISALLOW_COPY_AND_ASSIGN(ExpirationDateValidationTest); +}; + +TEST_P(ExpirationDateValidationTest, ExpirationDateValidation) { + auto exp_case = GetParam(); + ShowPrompt(); + EXPECT_EQ(exp_case.valid, controller_->InputExpirationIsValid( + ASCIIToUTF16(exp_case.input_month), + ASCIIToUTF16(exp_case.input_year))); } +INSTANTIATE_TEST_CASE_P( + CardUnmaskPromptControllerImplTest, + ExpirationDateValidationTest, + testing::Values(ExpirationDateTestCase{"01", "2040", true}, + ExpirationDateTestCase{"1", "2040", true}, + ExpirationDateTestCase{"1", "40", true}, + ExpirationDateTestCase{"10", "40", true}, + ExpirationDateTestCase{"01", "1940", false}, + ExpirationDateTestCase{"13", "2040", false})); + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/mock_save_card_bubble_controller.cc b/chromium/components/autofill/core/browser/ui/mock_save_card_bubble_controller.cc new file mode 100644 index 00000000000..b0c8d787035 --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/mock_save_card_bubble_controller.cc @@ -0,0 +1,22 @@ +// Copyright 2017 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 "components/autofill/core/browser/ui/mock_save_card_bubble_controller.h" + +namespace autofill { + +MockSaveCardBubbleController::MockSaveCardBubbleController() {} + +MockSaveCardBubbleController::~MockSaveCardBubbleController() {} + +base::string16 MockSaveCardBubbleController::GetCvcEnteredByUser() const { + return cvc_entered_by_user_; +} + +void MockSaveCardBubbleController::OnSaveButton(const base::string16& cvc) { + if (!cvc.empty()) + cvc_entered_by_user_ = cvc; +} + +} // namespace autofill diff --git a/chromium/components/autofill/core/browser/ui/mock_save_card_bubble_controller.h b/chromium/components/autofill/core/browser/ui/mock_save_card_bubble_controller.h new file mode 100644 index 00000000000..ce040ae2008 --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/mock_save_card_bubble_controller.h @@ -0,0 +1,42 @@ +// Copyright 2017 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 COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOCK_SAVE_CARD_BUBBLE_CONTROLLER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOCK_SAVE_CARD_BUBBLE_CONTROLLER_H_ + +#include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace autofill { + +class MockSaveCardBubbleController : public SaveCardBubbleController { + public: + MockSaveCardBubbleController(); + ~MockSaveCardBubbleController() override; + + MOCK_CONST_METHOD0(GetWindowTitle, base::string16()); + MOCK_CONST_METHOD0(GetExplanatoryMessage, base::string16()); + MOCK_CONST_METHOD0(GetCard, const CreditCard()); + MOCK_CONST_METHOD0(GetCvcImageResourceId, int()); + MOCK_CONST_METHOD0(ShouldRequestCvcFromUser, bool()); + MOCK_METHOD0(OnCancelButton, void()); + MOCK_METHOD0(OnLearnMoreClicked, void()); + MOCK_METHOD1(OnLegalMessageLinkClicked, void(const GURL& url)); + MOCK_METHOD0(OnBubbleClosed, void()); + MOCK_CONST_METHOD0(GetLegalMessageLines, const LegalMessageLines&()); + MOCK_CONST_METHOD1(InputCvcIsValid, bool(const base::string16& input_text)); + + base::string16 GetCvcEnteredByUser() const override; + void OnSaveButton(const base::string16& cvc = base::string16()) override; + + private: + base::string16 cvc_entered_by_user_; + + DISALLOW_COPY_AND_ASSIGN(MockSaveCardBubbleController); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOCK_SAVE_CARD_BUBBLE_CONTROLLER_H_ diff --git a/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h b/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h new file mode 100644 index 00000000000..f01cac3f220 --- /dev/null +++ b/chromium/components/autofill/core/browser/ui/save_card_bubble_controller.h @@ -0,0 +1,70 @@ +// 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 COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SAVE_CARD_BUBBLE_CONTROLLER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SAVE_CARD_BUBBLE_CONTROLLER_H_ + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "components/autofill/core/browser/legal_message_line.h" +#include "url/gurl.h" + +namespace autofill { + +class CreditCard; +class SaveCardBubbleView; + +// Interface that exposes controller functionality to SaveCardBubbleView. +class SaveCardBubbleController { + public: + SaveCardBubbleController() {} + virtual ~SaveCardBubbleController() {} + + // Returns the title that should be displayed in the bubble. + virtual base::string16 GetWindowTitle() const = 0; + + // Returns the explanatory text that should be displayed in the bubble. + // Returns an empty string if no message should be displayed. + virtual base::string16 GetExplanatoryMessage() const = 0; + + // Returns the card that will be uploaded if the user accepts. + virtual const CreditCard GetCard() const = 0; + + // Returns the CVC image icon resource ID. + virtual int GetCvcImageResourceId() const = 0; + + // Returns whether the dialog should include a field requesting the card's CVC + // from the user. + virtual bool ShouldRequestCvcFromUser() const = 0; + + // Returns the CVC provided by the user in the save card bubble. + virtual base::string16 GetCvcEnteredByUser() const = 0; + + // Interaction. + // OnSaveButton takes in a string value representing the CVC entered by the + // user if it was requested, or an empty string otherwise. + virtual void OnSaveButton(const base::string16& cvc) = 0; + virtual void OnCancelButton() = 0; + virtual void OnLearnMoreClicked() = 0; + virtual void OnLegalMessageLinkClicked(const GURL& url) = 0; + virtual void OnBubbleClosed() = 0; + + // State. + + // Returns empty vector if no legal message should be shown. + virtual const LegalMessageLines& GetLegalMessageLines() const = 0; + + // Utilities. + virtual bool InputCvcIsValid(const base::string16& input_text) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SaveCardBubbleController); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SAVE_CARD_BUBBLE_CONTROLLER_H_ diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc index d18ccbab5cf..e7237f7b3b7 100644 --- a/chromium/components/autofill/core/browser/validation.cc +++ b/chromium/components/autofill/core/browser/validation.cc @@ -13,7 +13,9 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_data_util.h" +#include "components/autofill/core/browser/autofill_regex_constants.h" #include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/state_names.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_regexes.h" @@ -94,8 +96,7 @@ bool IsValidCreditCardNumber(const base::string16& text) { bool IsValidCreditCardSecurityCode(const base::string16& code, const base::StringPiece card_type) { - size_t required_length = card_type == kAmericanExpressCard ? 4 : 3; - return code.length() == required_length && + return code.length() == GetCvcLengthForCardType(card_type) && base::ContainsOnlyChars(code, base::ASCIIToUTF16("0123456789")); } @@ -137,6 +138,12 @@ bool IsValidState(const base::string16& text) { !state_names::GetNameForAbbreviation(text).empty(); } +bool IsValidPhoneNumber(const base::string16& text, + const std::string& country_code) { + i18n::PhoneObject phone_number(text, country_code); + return phone_number.IsValidNumber(); +} + bool IsValidZip(const base::string16& text) { const base::string16 kZipPattern = base::ASCIIToUTF16("^\\d{5}(-\\d{4})?$"); return MatchesPattern(text, kZipPattern); @@ -308,4 +315,15 @@ bool IsValidForType(const base::string16& value, return false; } +size_t GetCvcLengthForCardType(const base::StringPiece card_type) { + if (card_type == kAmericanExpressCard) + return AMEX_CVC_LENGTH; + + return GENERAL_CVC_LENGTH; +} + +bool IsUPIVirtualPaymentAddress(const base::string16& value) { + return MatchesPattern(value, base::ASCIIToUTF16(kUPIVirtualPaymentAddressRe)); +} + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/validation.h b/chromium/components/autofill/core/browser/validation.h index 0b496e2b950..93a52e59b4f 100644 --- a/chromium/components/autofill/core/browser/validation.h +++ b/chromium/components/autofill/core/browser/validation.h @@ -15,6 +15,10 @@ class Time; namespace autofill { +// Constants for the length of a CVC. +static const size_t GENERAL_CVC_LENGTH = 3; +static const size_t AMEX_CVC_LENGTH = 4; + // Returns true if |year| and |month| describe a date later than |now|. // |year| must have 4 digits. bool IsValidCreditCardExpirationDate(int year, @@ -45,6 +49,11 @@ bool IsValidEmailAddress(const base::string16& text); // insensitive. Valid for US states only. bool IsValidState(const base::string16& text); +// Returns whether the number contained in |text| is valid for the specified +// |country_code|. Callers should cache the result as the parsing is expensive. +bool IsValidPhoneNumber(const base::string16& text, + const std::string& country_code); + // Returns true if |text| looks like a valid zip code. // Valid for US zip codes only. bool IsValidZip(const base::string16& text); @@ -58,6 +67,13 @@ bool IsValidForType(const base::string16& value, ServerFieldType type, base::string16* error_message); +// Returns the expected CVC length based on the |card_type|. +size_t GetCvcLengthForCardType(const base::StringPiece card_type); + +// Returns true if |value| appears to be a UPI Virtual Payment Address. +// https://upipayments.co.in/virtual-payment-address-vpa/ +bool IsUPIVirtualPaymentAddress(const base::string16& value); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_ diff --git a/chromium/components/autofill/core/browser/validation_unittest.cc b/chromium/components/autofill/core/browser/validation_unittest.cc index ec6ff0a346f..ee36fbc4543 100644 --- a/chromium/components/autofill/core/browser/validation_unittest.cc +++ b/chromium/components/autofill/core/browser/validation_unittest.cc @@ -412,4 +412,81 @@ INSTANTIATE_TEST_CASE_P( false, IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE))); +struct GetCvcLengthForCardTypeCase { + GetCvcLengthForCardTypeCase(const char* card_type, size_t expected_length) + : card_type(card_type), expected_length(expected_length) {} + ~GetCvcLengthForCardTypeCase() {} + + const char* const card_type; + const size_t expected_length; +}; + +class AutofillGetCvcLengthForCardType + : public testing::TestWithParam<GetCvcLengthForCardTypeCase> {}; + +TEST_P(AutofillGetCvcLengthForCardType, GetCvcLengthForCardType) { + EXPECT_EQ(GetParam().expected_length, + GetCvcLengthForCardType(GetParam().card_type)); +} + +INSTANTIATE_TEST_CASE_P( + CreditCardCvcLength, + AutofillGetCvcLengthForCardType, + testing::Values( + GetCvcLengthForCardTypeCase{kAmericanExpressCard, AMEX_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kDinersCard, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kDiscoverCard, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kGenericCard, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kJCBCard, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kMasterCard, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kMirCard, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kUnionPay, GENERAL_CVC_LENGTH}, + GetCvcLengthForCardTypeCase{kVisaCard, GENERAL_CVC_LENGTH})); + +class AutofillIsUPIVirtualPaymentAddress + : public testing::TestWithParam<std::string> {}; + +TEST_P(AutofillIsUPIVirtualPaymentAddress, IsUPIVirtualPaymentAddress) { + // Expected format is user@bank + EXPECT_TRUE(IsUPIVirtualPaymentAddress(ASCIIToUTF16("user@" + GetParam()))); + + // Deviations should not match: bank, @bank, user@prefixbank, user@banksuffix. + EXPECT_FALSE(IsUPIVirtualPaymentAddress(ASCIIToUTF16(GetParam()))); + EXPECT_FALSE(IsUPIVirtualPaymentAddress(ASCIIToUTF16(GetParam() + "@"))); + EXPECT_FALSE(IsUPIVirtualPaymentAddress(ASCIIToUTF16("@" + GetParam()))); + EXPECT_FALSE( + IsUPIVirtualPaymentAddress(ASCIIToUTF16("user@invalid" + GetParam()))); + EXPECT_FALSE( + IsUPIVirtualPaymentAddress(ASCIIToUTF16("user@" + GetParam() + ".com"))); +} + +INSTANTIATE_TEST_CASE_P(UPIVirtualPaymentAddress, + AutofillIsUPIVirtualPaymentAddress, + testing::Values("upi", + "allbank", + "andb", + "axisbank", + "barodampay", + "mahb", + "cnrb", + "csbpay", + "dcb", + "federal", + "hdfcbank", + "pockets", + "icici", + "idfcbank", + "indus", + "kbl", + "kaypay", + "pnb", + "sib", + "sbi", + "tjsp", + "uco", + "unionbank", + "united", + "vijb", + "ybl")); + } // namespace autofill diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc index 589df1628af..9ed91408190 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.cc @@ -458,6 +458,9 @@ void AutocompleteSyncBridge::LoadMetadata() { std::string AutocompleteSyncBridge::GetClientTag( const EntityData& entity_data) { DCHECK(entity_data.specifics.has_autofill()); + // Must have the format "autofill_entry|$name|$value" where $name and $value + // are URL escaped. This is to maintain compatibility with the previous sync + // integration (Directory and SyncableService). return std::string(kAutocompleteEntryNamespaceTag) + EscapeIdentifiers(entity_data.specifics.autofill()); } @@ -465,6 +468,9 @@ std::string AutocompleteSyncBridge::GetClientTag( std::string AutocompleteSyncBridge::GetStorageKey( const EntityData& entity_data) { DCHECK(entity_data.specifics.has_autofill()); + // Marginally more space efficient than GetClientTag() by omitting the + // kAutocompleteEntryNamespaceTag prefix and using protobuf serialization + // instead of URL escaping for Unicode characters. const AutofillSpecifics specifics = entity_data.specifics.autofill(); return BuildSerializedStorageKey(specifics.name(), specifics.value()); } diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h index 2ec41c5ae51..3d3053c6110 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge.h @@ -54,14 +54,7 @@ class AutocompleteSyncBridge : public base::SupportsUserData::Data, syncer::EntityChangeList entity_changes) override; void GetData(StorageKeyList storage_keys, DataCallback callback) override; void GetAllData(DataCallback callback) override; - // Generate a tag that uniquely identifies |entity_data| across all data - // types. This is used to identify the entity on the server. The format, which - // must remain the same for server compatibility, is: - // "autofill_entry|$name|$value" where $name and $value are escaped. std::string GetClientTag(const syncer::EntityData& entity_data) override; - // Generate a string key uniquely identifying |entity_data| in the context of - // local storage. The format, which should stay the same, is $name|$value" - // where $name and $value are escaped. std::string GetStorageKey(const syncer::EntityData& entity_data) override; // AutofillWebDataServiceObserverOnDBThread implementation. diff --git a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc index a4334eaa230..bdb786daa6a 100644 --- a/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc @@ -16,6 +16,7 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/autofill/core/browser/webdata/autofill_entry.h" @@ -276,7 +277,7 @@ class AutocompleteSyncBridgeTest : public testing::Test { } ScopedTempDir temp_dir_; - base::MessageLoop message_loop_; + base::test::ScopedTaskEnvironment scoped_task_environment_; FakeAutofillBackend backend_; AutofillTable table_; WebDatabase db_; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc index e5bc5b989bf..7c840464cf4 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_profile_syncable_service_unittest.cc @@ -1191,77 +1191,127 @@ TEST_F(AutofillProfileSyncableServiceTest, NoUsageStatsNoSync) { autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } -// Usage stats should be updated by sync. -TEST_F(AutofillProfileSyncableServiceTest, SyncUpdatesUsageStats) { - typedef struct { - size_t local_use_count; - base::Time local_use_date; - size_t remote_use_count; - int remote_use_date; - size_t synced_use_count; - base::Time synced_use_date; - } TestCase; - - TestCase test_cases[] = { - // Local profile with default stats. - {0U, base::Time(), 9U, 4321, 9U, base::Time::FromTimeT(4321)}, - // Local profile has older stats than the server. - {3U, base::Time::FromTimeT(1234), 9U, 4321, 9U, - base::Time::FromTimeT(4321)}, - // Local profile has newer stats than the server - {10U, base::Time::FromTimeT(9999), 9U, 4321, 9U, - base::Time::FromTimeT(4321)}}; - - for (const TestCase& test_case : test_cases) { - SetUp(); - std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db; - - AutofillProfile profile(kGuid1, kHttpsOrigin); - profile.set_language_code("en"); - profile.set_use_count(test_case.local_use_count); - profile.set_use_date(test_case.local_use_date); - EXPECT_EQ(test_case.local_use_count, profile.use_count()); - EXPECT_EQ(test_case.local_use_date, profile.use_date()); - profiles_from_web_db.push_back(base::MakeUnique<AutofillProfile>(profile)); - - // Remote data has usage stats. - sync_pb::EntitySpecifics specifics; - sync_pb::AutofillProfileSpecifics* autofill_specifics = - specifics.mutable_autofill_profile(); - autofill_specifics->set_guid(profile.guid()); - autofill_specifics->set_origin(profile.origin()); - autofill_specifics->add_name_first(std::string()); - autofill_specifics->add_name_middle(std::string()); - autofill_specifics->add_name_last(std::string()); - autofill_specifics->add_name_full(std::string()); - autofill_specifics->add_email_address(std::string()); - autofill_specifics->add_phone_home_whole_number(std::string()); - autofill_specifics->set_address_home_language_code("en"); - autofill_specifics->set_use_count(test_case.remote_use_count); - autofill_specifics->set_use_date(test_case.remote_use_date); - EXPECT_TRUE(autofill_specifics->has_use_count()); - EXPECT_TRUE(autofill_specifics->has_use_date()); - - syncer::SyncDataList data_list; - data_list.push_back(syncer::SyncData::CreateLocalData( - profile.guid(), profile.guid(), specifics)); - - // Expect the local autofill profile to have usage stats after sync. - MockAutofillProfileSyncableService::DataBundle expected_bundle; - AutofillProfile expected_profile = profile; - expected_profile.set_use_count(test_case.synced_use_count); - expected_profile.set_use_date(test_case.synced_use_date); - expected_bundle.profiles_to_update.push_back(&expected_profile); - - // Expect no changes to remote data. - syncer::SyncChangeList expected_empty_change_list; - - MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list, - expected_bundle, expected_empty_change_list); - autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); +struct SyncUpdatesUsageStatsTestCase { + size_t local_use_count; + base::Time local_use_date; + size_t remote_use_count; + int remote_use_date; + size_t synced_use_count; + base::Time synced_use_date; +}; + +class SyncUpdatesUsageStatsTest + : public testing::TestWithParam<SyncUpdatesUsageStatsTestCase> { + public: + SyncUpdatesUsageStatsTest() { CountryNames::SetLocaleString("en-US"); } + + void SetUp() override { sync_processor_.reset(new MockSyncChangeProcessor); } + + // Wrapper around AutofillProfileSyncableService::MergeDataAndStartSyncing() + // that also verifies expectations. + void MergeDataAndStartSyncing( + std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db, + const syncer::SyncDataList& data_list, + const MockAutofillProfileSyncableService::DataBundle& expected_bundle, + const syncer::SyncChangeList& expected_change_list) { + auto profile_returner = [&profiles_from_web_db]() { + return std::move(profiles_from_web_db); + }; + EXPECT_CALL(autofill_syncable_service_, LoadAutofillData(_)) + .Times(1) + .WillOnce(DoAll(LoadAutofillProfiles(profile_returner), Return(true))); + EXPECT_CALL(autofill_syncable_service_, + SaveChangesToWebData(DataBundleCheck(expected_bundle))) + .Times(1) + .WillOnce(Return(true)); + if (expected_change_list.empty()) { + EXPECT_CALL(*sync_processor_, ProcessSyncChanges(_, _)).Times(0); + } else { + ON_CALL(*sync_processor_, ProcessSyncChanges(_, _)) + .WillByDefault(Return(syncer::SyncError())); + EXPECT_CALL(*sync_processor_, + ProcessSyncChanges(_, CheckSyncChanges(expected_change_list))) + .Times(1) + .WillOnce(Return(syncer::SyncError())); + } + + // Takes ownership of sync_processor_. + autofill_syncable_service_.MergeDataAndStartSyncing( + syncer::AUTOFILL_PROFILE, data_list, std::move(sync_processor_), + std::unique_ptr<syncer::SyncErrorFactory>( + new syncer::SyncErrorFactoryMock())); } + + protected: + base::MessageLoop message_loop_; + MockAutofillProfileSyncableService autofill_syncable_service_; + std::unique_ptr<MockSyncChangeProcessor> sync_processor_; +}; + +TEST_P(SyncUpdatesUsageStatsTest, SyncUpdatesUsageStats) { + auto test_case = GetParam(); + SetUp(); + std::vector<std::unique_ptr<AutofillProfile>> profiles_from_web_db; + + AutofillProfile profile(kGuid1, kHttpsOrigin); + profile.set_language_code("en"); + profile.set_use_count(test_case.local_use_count); + profile.set_use_date(test_case.local_use_date); + EXPECT_EQ(test_case.local_use_count, profile.use_count()); + EXPECT_EQ(test_case.local_use_date, profile.use_date()); + profiles_from_web_db.push_back(base::MakeUnique<AutofillProfile>(profile)); + + // Remote data has usage stats. + sync_pb::EntitySpecifics specifics; + sync_pb::AutofillProfileSpecifics* autofill_specifics = + specifics.mutable_autofill_profile(); + autofill_specifics->set_guid(profile.guid()); + autofill_specifics->set_origin(profile.origin()); + autofill_specifics->add_name_first(std::string()); + autofill_specifics->add_name_middle(std::string()); + autofill_specifics->add_name_last(std::string()); + autofill_specifics->add_name_full(std::string()); + autofill_specifics->add_email_address(std::string()); + autofill_specifics->add_phone_home_whole_number(std::string()); + autofill_specifics->set_address_home_language_code("en"); + autofill_specifics->set_use_count(test_case.remote_use_count); + autofill_specifics->set_use_date(test_case.remote_use_date); + EXPECT_TRUE(autofill_specifics->has_use_count()); + EXPECT_TRUE(autofill_specifics->has_use_date()); + + syncer::SyncDataList data_list; + data_list.push_back(syncer::SyncData::CreateLocalData( + profile.guid(), profile.guid(), specifics)); + + // Expect the local autofill profile to have usage stats after sync. + MockAutofillProfileSyncableService::DataBundle expected_bundle; + AutofillProfile expected_profile = profile; + expected_profile.set_use_count(test_case.synced_use_count); + expected_profile.set_use_date(test_case.synced_use_date); + expected_bundle.profiles_to_update.push_back(&expected_profile); + + // Expect no changes to remote data. + syncer::SyncChangeList expected_empty_change_list; + + MergeDataAndStartSyncing(std::move(profiles_from_web_db), data_list, + expected_bundle, expected_empty_change_list); + autofill_syncable_service_.StopSyncing(syncer::AUTOFILL_PROFILE); } +INSTANTIATE_TEST_CASE_P( + AutofillProfileSyncableServiceTest, + SyncUpdatesUsageStatsTest, + testing::Values( + // Local profile with default stats. + SyncUpdatesUsageStatsTestCase{0U, base::Time(), 9U, 4321, 9U, + base::Time::FromTimeT(4321)}, + // Local profile has older stats than the server. + SyncUpdatesUsageStatsTestCase{3U, base::Time::FromTimeT(1234), 9U, 4321, + 9U, base::Time::FromTimeT(4321)}, + // Local profile has newer stats than the server + SyncUpdatesUsageStatsTestCase{10U, base::Time::FromTimeT(9999), 9U, + 4321, 9U, base::Time::FromTimeT(4321)})); + // Usage stats should be updated by the client. TEST_F(AutofillProfileSyncableServiceTest, ClientOverwritesUsageStats) { TestSyncChangeProcessor* sync_change_processor = new TestSyncChangeProcessor; diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc index 72cf35b3c7b..27d1c00d470 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc @@ -1700,13 +1700,8 @@ bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type, syncer::MetadataBatch* metadata_batch) { DCHECK_EQ(model_type, syncer::AUTOFILL) << "Only the AUTOFILL model type is supported"; - syncer::EntityMetadataMap metadata_records; - if (GetAllSyncEntityMetadata(model_type, &metadata_records)) { - for (const auto& pair : metadata_records) { - // TODO(pnoland): Add batch transfer of metadata map. - metadata_batch->AddMetadata(pair.first, pair.second); - } - } else { + DCHECK(metadata_batch); + if (!GetAllSyncEntityMetadata(model_type, metadata_batch)) { return false; } @@ -1722,9 +1717,10 @@ bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type, bool AutofillTable::GetAllSyncEntityMetadata( syncer::ModelType model_type, - syncer::EntityMetadataMap* metadata_records) { + syncer::MetadataBatch* metadata_batch) { DCHECK_EQ(model_type, syncer::AUTOFILL) << "Only the AUTOFILL model type is supported"; + DCHECK(metadata_batch); sql::Statement s(db_->GetUniqueStatement( "SELECT storage_key, value FROM autofill_sync_metadata")); @@ -1732,10 +1728,12 @@ bool AutofillTable::GetAllSyncEntityMetadata( while (s.Step()) { std::string storage_key = s.ColumnString(0); std::string serialized_metadata = s.ColumnString(1); - sync_pb::EntityMetadata metadata_record; - if (metadata_record.ParseFromString(serialized_metadata)) { - metadata_records->insert(std::make_pair(storage_key, metadata_record)); + sync_pb::EntityMetadata entity_metadata; + if (entity_metadata.ParseFromString(serialized_metadata)) { + metadata_batch->AddMetadata(storage_key, entity_metadata); } else { + DLOG(WARNING) << "Failed to deserialize AUTOFILL model type " + "sync_pb::EntityMetadata."; return false; } } diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h index f3f8a14dcc4..872d127b393 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table.h +++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h @@ -517,7 +517,7 @@ class AutofillTable : public WebDatabaseTable { base::Time time); bool GetAllSyncEntityMetadata(syncer::ModelType model_type, - syncer::EntityMetadataMap* metadata_records); + syncer::MetadataBatch* metadata_batch); bool GetModelTypeState(syncer::ModelType model_type, sync_pb::ModelTypeState* state); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc index 235dd06230b..58439067907 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -1955,61 +1955,112 @@ TEST_F(AutofillTableTest, DeleteUnmaskedCard) { outputs.clear(); } -TEST_F(AutofillTableTest, GetFormValuesForElementName_SubstringMatchEnabled) { +const size_t kMaxCount = 2; +struct GetFormValuesTestCase { + const char* const field_suggestion[kMaxCount]; + const char* const field_contents; + size_t expected_suggestion_count; + const char* const expected_suggestion[kMaxCount]; +}; + +class GetFormValuesTest : public testing::TestWithParam<GetFormValuesTestCase> { + public: + GetFormValuesTest() {} + ~GetFormValuesTest() override {} + + protected: + void SetUp() override { + OSCryptMocker::SetUpWithSingleton(); + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + file_ = temp_dir_.GetPath().AppendASCII("TestWebDatabase"); + + table_.reset(new AutofillTable); + db_.reset(new WebDatabase); + db_->AddTable(table_.get()); + ASSERT_EQ(sql::INIT_OK, db_->Init(file_)); + } + + void TearDown() override { OSCryptMocker::TearDown(); } + + base::FilePath file_; + base::ScopedTempDir temp_dir_; + std::unique_ptr<AutofillTable> table_; + std::unique_ptr<WebDatabase> db_; + + private: + DISALLOW_COPY_AND_ASSIGN(GetFormValuesTest); +}; + +TEST_P(GetFormValuesTest, GetFormValuesForElementName_SubstringMatchEnabled) { // Token matching is currently behind a flag. base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableSuggestionsWithSubstringMatch); - const size_t kMaxCount = 2; - const struct { - const char* const field_suggestion[kMaxCount]; - const char* const field_contents; - size_t expected_suggestion_count; - const char* const expected_suggestion[kMaxCount]; - } kTestCases[] = { - {{"user.test", "test_user"}, "TEST", 2, {"test_user", "user.test"}}, - {{"user test", "test-user"}, "user", 2, {"user test", "test-user"}}, - {{"user test", "test-rest"}, "user", 1, {"user test", nullptr}}, - {{"user@test", "test_user"}, "user@t", 1, {"user@test", nullptr}}, - {{"user.test", "test_user"}, "er.tes", 0, {nullptr, nullptr}}, - {{"user test", "test_user"}, "_ser", 0, {nullptr, nullptr}}, - {{"user.test", "test_user"}, "%ser", 0, {nullptr, nullptr}}, - {{"user.test", "test_user"}, - "; DROP TABLE autofill;", - 0, - {nullptr, nullptr}}, - }; - - for (const auto& test_case : kTestCases) { - SCOPED_TRACE(testing::Message() - << "suggestion = " << test_case.field_suggestion[0] - << ", contents = " << test_case.field_contents); - - Time t1 = Time::Now(); + auto test_case = GetParam(); + SCOPED_TRACE(testing::Message() + << "suggestion = " << test_case.field_suggestion[0] + << ", contents = " << test_case.field_contents); - // Simulate the submission of a handful of entries in a field called "Name". - AutofillChangeList changes; - FormFieldData field; - for (size_t k = 0; k < kMaxCount; ++k) { - field.name = ASCIIToUTF16("Name"); - field.value = ASCIIToUTF16(test_case.field_suggestion[k]); - table_->AddFormFieldValue(field, &changes); - } + Time t1 = Time::Now(); - std::vector<base::string16> v; - table_->GetFormValuesForElementName( - ASCIIToUTF16("Name"), ASCIIToUTF16(test_case.field_contents), &v, 6); + // Simulate the submission of a handful of entries in a field called "Name". + AutofillChangeList changes; + FormFieldData field; + for (size_t k = 0; k < kMaxCount; ++k) { + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16(test_case.field_suggestion[k]); + table_->AddFormFieldValue(field, &changes); + } - EXPECT_EQ(test_case.expected_suggestion_count, v.size()); - for (size_t j = 0; j < test_case.expected_suggestion_count; ++j) { - EXPECT_EQ(ASCIIToUTF16(test_case.expected_suggestion[j]), v[j]); - } + std::vector<base::string16> v; + table_->GetFormValuesForElementName( + ASCIIToUTF16("Name"), ASCIIToUTF16(test_case.field_contents), &v, 6); - changes.clear(); - table_->RemoveFormElementsAddedBetween(t1, Time(), &changes); + EXPECT_EQ(test_case.expected_suggestion_count, v.size()); + for (size_t j = 0; j < test_case.expected_suggestion_count; ++j) { + EXPECT_EQ(ASCIIToUTF16(test_case.expected_suggestion[j]), v[j]); } + + changes.clear(); + table_->RemoveFormElementsAddedBetween(t1, Time(), &changes); } +INSTANTIATE_TEST_CASE_P( + AutofillTableTest, + GetFormValuesTest, + testing::Values(GetFormValuesTestCase{{"user.test", "test_user"}, + "TEST", + 2, + {"test_user", "user.test"}}, + GetFormValuesTestCase{{"user test", "test-user"}, + "user", + 2, + {"user test", "test-user"}}, + GetFormValuesTestCase{{"user test", "test-rest"}, + "user", + 1, + {"user test", nullptr}}, + GetFormValuesTestCase{{"user@test", "test_user"}, + "user@t", + 1, + {"user@test", nullptr}}, + GetFormValuesTestCase{{"user.test", "test_user"}, + "er.tes", + 0, + {nullptr, nullptr}}, + GetFormValuesTestCase{{"user test", "test_user"}, + "_ser", + 0, + {nullptr, nullptr}}, + GetFormValuesTestCase{{"user.test", "test_user"}, + "%ser", + 0, + {nullptr, nullptr}}, + GetFormValuesTestCase{{"user.test", "test_user"}, + "; DROP TABLE autofill;", + 0, + {nullptr, nullptr}})); + TEST_F(AutofillTableTest, AutofillNoMetadata) { MetadataBatch metadata_batch; EXPECT_TRUE(table_->GetAllSyncMetadata(syncer::AUTOFILL, &metadata_batch)); diff --git a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc index b43f4fc3ed7..fc6948f1aef 100644 --- a/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc +++ b/chromium/components/autofill/core/browser/webdata/autofill_wallet_syncable_service.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/logging.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "components/autofill/core/browser/webdata/autofill_table.h" @@ -86,8 +87,8 @@ AutofillProfile ProfileFromSpecifics( AutofillProfile profile(AutofillProfile::SERVER_PROFILE, std::string()); // AutofillProfile stores multi-line addresses with newline separators. - std::vector<std::string> street_address(address.street_address().begin(), - address.street_address().end()); + std::vector<base::StringPiece> street_address( + address.street_address().begin(), address.street_address().end()); profile.SetRawInfo(ADDRESS_HOME_STREET_ADDRESS, base::UTF8ToUTF16(base::JoinString(street_address, "\n"))); diff --git a/chromium/components/autofill/core/common/BUILD.gn b/chromium/components/autofill/core/common/BUILD.gn index f7a34bb28b6..f3b320f0827 100644 --- a/chromium/components/autofill/core/common/BUILD.gn +++ b/chromium/components/autofill/core/common/BUILD.gn @@ -33,6 +33,7 @@ static_library("common") { "password_form_field_prediction_map.h", "password_form_fill_data.cc", "password_form_fill_data.h", + "password_form_generation_data.cc", "password_form_generation_data.h", "password_generation_util.cc", "password_generation_util.h", diff --git a/chromium/components/autofill/core/common/autofill_clock.cc b/chromium/components/autofill/core/common/autofill_clock.cc index 8702403d9e5..20d70c95413 100644 --- a/chromium/components/autofill/core/common/autofill_clock.cc +++ b/chromium/components/autofill/core/common/autofill_clock.cc @@ -13,7 +13,7 @@ namespace autofill { namespace { -static base::LazyInstance<AutofillClock> g_autofill_clock = +static base::LazyInstance<AutofillClock>::DestructorAtExit g_autofill_clock = LAZY_INSTANCE_INITIALIZER; } // namespace diff --git a/chromium/components/autofill/core/common/autofill_clock.h b/chromium/components/autofill/core/common/autofill_clock.h index 45a991211ba..b94ff272ab4 100644 --- a/chromium/components/autofill/core/common/autofill_clock.h +++ b/chromium/components/autofill/core/common/autofill_clock.h @@ -26,7 +26,7 @@ class AutofillClock { private: friend class TestAutofillClock; - friend struct base::DefaultLazyInstanceTraits<AutofillClock>; + friend struct base::LazyInstanceTraitsBase<AutofillClock>; // Resets a normal clock. static void SetClock(); diff --git a/chromium/components/autofill/core/common/autofill_data_validation.cc b/chromium/components/autofill/core/common/autofill_data_validation.cc index 07c2b9dc9db..f1075dc384d 100644 --- a/chromium/components/autofill/core/common/autofill_data_validation.cc +++ b/chromium/components/autofill/core/common/autofill_data_validation.cc @@ -35,7 +35,6 @@ bool IsValidFormFieldData(const FormFieldData& field) { IsValidString16(field.value) && IsValidString(field.form_control_type) && IsValidString(field.autocomplete_attribute) && - IsValidString16Vector(field.option_values) && IsValidString16Vector(field.option_contents); } diff --git a/chromium/components/autofill/core/common/autofill_regexes_unittest.cc b/chromium/components/autofill/core/common/autofill_regexes_unittest.cc index 107830d52d1..a267e414991 100644 --- a/chromium/components/autofill/core/common/autofill_regexes_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_regexes_unittest.cc @@ -16,194 +16,217 @@ using base::ASCIIToUTF16; namespace autofill { -TEST(AutofillRegexesTest, SampleRegexes) { - struct TestCase { - const char* const input; - const char* const pattern; +struct InputPatternTestCase { + const char* const input; + const char* const pattern; }; - const TestCase kPositiveCases[] = { - // Empty pattern - {"", ""}, - {"Look, ma' -- a non-empty string!", ""}, - // Substring - {"string", "tri"}, - // Substring at beginning - {"string", "str"}, - {"string", "^str"}, - // Substring at end - {"string", "ring"}, - {"string", "ring$"}, - // Case-insensitive - {"StRiNg", "string"}, - }; - for (const auto& test_case : kPositiveCases) { + class PositiveSampleTest + : public testing::TestWithParam<InputPatternTestCase> {}; + + TEST_P(PositiveSampleTest, SampleRegexes) { + auto test_case = GetParam(); SCOPED_TRACE(test_case.input); SCOPED_TRACE(test_case.pattern); EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), ASCIIToUTF16(test_case.pattern))); } - const TestCase kNegativeCases[] = { - // Empty string - {"", "Look, ma' -- a non-empty pattern!"}, - // Substring - {"string", "trn"}, - // Substring at beginning - {"string", " str"}, - {"string", "^tri"}, - // Substring at end - {"string", "ring "}, - {"string", "rin$"}, - }; - for (const auto& test_case : kNegativeCases) { + INSTANTIATE_TEST_CASE_P(AutofillRegexes, + PositiveSampleTest, + testing::Values( + // Empty pattern + InputPatternTestCase{"", ""}, + InputPatternTestCase{ + "Look, ma' -- a non-empty string!", ""}, + // Substring + InputPatternTestCase{"string", "tri"}, + // Substring at beginning + InputPatternTestCase{"string", "str"}, + InputPatternTestCase{"string", "^str"}, + // Substring at end + InputPatternTestCase{"string", "ring"}, + InputPatternTestCase{"string", "ring$"}, + // Case-insensitive + InputPatternTestCase{"StRiNg", "string"})); + + class NegativeSampleTest + : public testing::TestWithParam<InputPatternTestCase> {}; + + TEST_P(NegativeSampleTest, SampleRegexes) { + auto test_case = GetParam(); SCOPED_TRACE(test_case.input); SCOPED_TRACE(test_case.pattern); EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), ASCIIToUTF16(test_case.pattern))); - } } -TEST(AutofillRegexesTest, ExpirationDate2DigitYearRegexes) { - struct TestCase { - const char* const input; +INSTANTIATE_TEST_CASE_P(AutofillRegexes, + NegativeSampleTest, + testing::Values( + // Empty string + InputPatternTestCase{ + "", "Look, ma' -- a non-empty pattern!"}, + // Substring + InputPatternTestCase{"string", "trn"}, + // Substring at beginning + InputPatternTestCase{"string", " str"}, + InputPatternTestCase{"string", "^tri"}, + // Substring at end + InputPatternTestCase{"string", "ring "}, + InputPatternTestCase{"string", "rin$"})); + +struct InputTestCase { + const char* const input; }; - const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); - - const TestCase kPositiveCases[] = { - // Simple two year cases - {"mm / yy"}, - {"mm/ yy"}, - {"mm /yy"}, - {"mm/yy"}, - {"mm - yy"}, - {"mm- yy"}, - {"mm -yy"}, - {"mm-yy"}, - {"mmyy"}, - // Complex two year cases - {"Expiration Date (MM / YY)"}, - {"Expiration Date (MM/YY)"}, - {"Expiration Date (MM - YY)"}, - {"Expiration Date (MM-YY)"}, - {"Expiration Date MM / YY"}, - {"Expiration Date MM/YY"}, - {"Expiration Date MM - YY"}, - {"Expiration Date MM-YY"}, - {"expiration date yy"}, - {"Exp Date (MM / YY)"}, - }; + class ExpirationDate2DigitYearPositive + : public testing::TestWithParam<InputTestCase> {}; - for (const auto& test_case : kPositiveCases) { + TEST_P(ExpirationDate2DigitYearPositive, ExpirationDate2DigitYearRegexes) { + auto test_case = GetParam(); SCOPED_TRACE(test_case.input); - EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input),pattern)); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); + EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); } - const TestCase kNegativeCases[] = { - {""}, - {"Look, ma' -- an invalid string!"}, - {"mmfavouritewordyy"}, - {"mm a yy"}, - {"mm a yyyy"}, - // Simple four year cases - {"mm / yyyy"}, - {"mm/ yyyy"}, - {"mm /yyyy"}, - {"mm/yyyy"}, - {"mm - yyyy"}, - {"mm- yyyy"}, - {"mm -yyyy"}, - {"mm-yyyy"}, - {"mmyyyy"}, - // Complex four year cases - {"Expiration Date (MM / YYYY)"}, - {"Expiration Date (MM/YYYY)"}, - {"Expiration Date (MM - YYYY)"}, - {"Expiration Date (MM-YYYY)"}, - {"Expiration Date MM / YYYY"}, - {"Expiration Date MM/YYYY"}, - {"Expiration Date MM - YYYY"}, - {"Expiration Date MM-YYYY"}, - {"expiration date yyyy"}, - {"Exp Date (MM / YYYY)"}, - }; - - for (const auto& test_case : kNegativeCases) { + INSTANTIATE_TEST_CASE_P( + AutofillRegexes, + ExpirationDate2DigitYearPositive, + testing::Values(InputTestCase{"mm / yy"}, + InputTestCase{"mm/ yy"}, + InputTestCase{"mm /yy"}, + InputTestCase{"mm/yy"}, + InputTestCase{"mm - yy"}, + InputTestCase{"mm- yy"}, + InputTestCase{"mm -yy"}, + InputTestCase{"mm-yy"}, + InputTestCase{"mmyy"}, + // Complex two year cases + InputTestCase{"Expiration Date (MM / YY)"}, + InputTestCase{"Expiration Date (MM/YY)"}, + InputTestCase{"Expiration Date (MM - YY)"}, + InputTestCase{"Expiration Date (MM-YY)"}, + InputTestCase{"Expiration Date MM / YY"}, + InputTestCase{"Expiration Date MM/YY"}, + InputTestCase{"Expiration Date MM - YY"}, + InputTestCase{"Expiration Date MM-YY"}, + InputTestCase{"expiration date yy"}, + InputTestCase{"Exp Date (MM / YY)"})); + + class ExpirationDate2DigitYearNegative + : public testing::TestWithParam<InputTestCase> {}; + + TEST_P(ExpirationDate2DigitYearNegative, ExpirationDate2DigitYearRegexes) { + auto test_case = GetParam(); SCOPED_TRACE(test_case.input); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate2DigitYearRe); EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); } -} -TEST(AutofillRegexesTest, ExpirationDate4DigitYearRegexes) { - struct TestCase { - const char* const input; - }; - - const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); - - const TestCase kPositiveCases[] = { - // Simple four year cases - {"mm / yyyy"}, - {"mm/ yyyy"}, - {"mm /yyyy"}, - {"mm/yyyy"}, - {"mm - yyyy"}, - {"mm- yyyy"}, - {"mm -yyyy"}, - {"mm-yyyy"}, - {"mmyyyy"}, - // Complex four year cases - {"Expiration Date (MM / YYYY)"}, - {"Expiration Date (MM/YYYY)"}, - {"Expiration Date (MM - YYYY)"}, - {"Expiration Date (MM-YYYY)"}, - {"Expiration Date MM / YYYY"}, - {"Expiration Date MM/YYYY"}, - {"Expiration Date MM - YYYY"}, - {"Expiration Date MM-YYYY"}, - {"expiration date yyyy"}, - {"Exp Date (MM / YYYY)"}, - }; - - for (const auto& test_case : kPositiveCases) { + INSTANTIATE_TEST_CASE_P( + AutofillRegexes, + ExpirationDate2DigitYearNegative, + testing::Values(InputTestCase{""}, + InputTestCase{"Look, ma' -- an invalid string!"}, + InputTestCase{"mmfavouritewordyy"}, + InputTestCase{"mm a yy"}, + InputTestCase{"mm a yyyy"}, + // Simple four year cases + InputTestCase{"mm / yyyy"}, + InputTestCase{"mm/ yyyy"}, + InputTestCase{"mm /yyyy"}, + InputTestCase{"mm/yyyy"}, + InputTestCase{"mm - yyyy"}, + InputTestCase{"mm- yyyy"}, + InputTestCase{"mm -yyyy"}, + InputTestCase{"mm-yyyy"}, + InputTestCase{"mmyyyy"}, + // Complex four year cases + InputTestCase{"Expiration Date (MM / YYYY)"}, + InputTestCase{"Expiration Date (MM/YYYY)"}, + InputTestCase{"Expiration Date (MM - YYYY)"}, + InputTestCase{"Expiration Date (MM-YYYY)"}, + InputTestCase{"Expiration Date MM / YYYY"}, + InputTestCase{"Expiration Date MM/YYYY"}, + InputTestCase{"Expiration Date MM - YYYY"}, + InputTestCase{"Expiration Date MM-YYYY"}, + InputTestCase{"expiration date yyyy"}, + InputTestCase{"Exp Date (MM / YYYY)"})); + + class ExpirationDate4DigitYearPositive + : public testing::TestWithParam<InputTestCase> {}; + + TEST_P(ExpirationDate4DigitYearPositive, ExpirationDate4DigitYearRegexes) { + auto test_case = GetParam(); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); SCOPED_TRACE(test_case.input); - EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input),pattern)); + EXPECT_TRUE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); } - const TestCase kNegativeCases[] = { - {""}, - {"Look, ma' -- an invalid string!"}, - {"mmfavouritewordyy"}, - {"mm a yy"}, - {"mm a yyyy"}, - // Simple two year cases - {"mm / yy"}, - {"mm/ yy"}, - {"mm /yy"}, - {"mm/yy"}, - {"mm - yy"}, - {"mm- yy"}, - {"mm -yy"}, - {"mm-yy"}, - {"mmyy"}, - // Complex two year cases - {"Expiration Date (MM / YY)"}, - {"Expiration Date (MM/YY)"}, - {"Expiration Date (MM - YY)"}, - {"Expiration Date (MM-YY)"}, - {"Expiration Date MM / YY"}, - {"Expiration Date MM/YY"}, - {"Expiration Date MM - YY"}, - {"Expiration Date MM-YY"}, - {"expiration date yy"}, - {"Exp Date (MM / YY)"}, - }; - - for (const auto& test_case : kNegativeCases) { + INSTANTIATE_TEST_CASE_P(AutofillRegexes, + ExpirationDate4DigitYearPositive, + testing::Values( + // Simple four year cases + InputTestCase{"mm / yyyy"}, + InputTestCase{"mm/ yyyy"}, + InputTestCase{"mm /yyyy"}, + InputTestCase{"mm/yyyy"}, + InputTestCase{"mm - yyyy"}, + InputTestCase{"mm- yyyy"}, + InputTestCase{"mm -yyyy"}, + InputTestCase{"mm-yyyy"}, + InputTestCase{"mmyyyy"}, + // Complex four year cases + InputTestCase{"Expiration Date (MM / YYYY)"}, + InputTestCase{"Expiration Date (MM/YYYY)"}, + InputTestCase{"Expiration Date (MM - YYYY)"}, + InputTestCase{"Expiration Date (MM-YYYY)"}, + InputTestCase{"Expiration Date MM / YYYY"}, + InputTestCase{"Expiration Date MM/YYYY"}, + InputTestCase{"Expiration Date MM - YYYY"}, + InputTestCase{"Expiration Date MM-YYYY"}, + InputTestCase{"expiration date yyyy"}, + InputTestCase{"Exp Date (MM / YYYY)"})); + + class ExpirationDate4DigitYearNegative + : public testing::TestWithParam<InputTestCase> {}; + + TEST_P(ExpirationDate4DigitYearNegative, ExpirationDate4DigitYearRegexes) { + auto test_case = GetParam(); + const base::string16 pattern = ASCIIToUTF16(kExpirationDate4DigitYearRe); SCOPED_TRACE(test_case.input); EXPECT_FALSE(MatchesPattern(ASCIIToUTF16(test_case.input), pattern)); - } } +INSTANTIATE_TEST_CASE_P( + AutofillRegexes, + ExpirationDate4DigitYearNegative, + testing::Values(InputTestCase{""}, + InputTestCase{"Look, ma' -- an invalid string!"}, + InputTestCase{"mmfavouritewordyy"}, + InputTestCase{"mm a yy"}, + InputTestCase{"mm a yyyy"}, + // Simple two year cases + InputTestCase{"mm / yy"}, + InputTestCase{"mm/ yy"}, + InputTestCase{"mm /yy"}, + InputTestCase{"mm/yy"}, + InputTestCase{"mm - yy"}, + InputTestCase{"mm- yy"}, + InputTestCase{"mm -yy"}, + InputTestCase{"mm-yy"}, + InputTestCase{"mmyy"}, + // Complex two year cases + InputTestCase{"Expiration Date (MM / YY)"}, + InputTestCase{"Expiration Date (MM/YY)"}, + InputTestCase{"Expiration Date (MM - YY)"}, + InputTestCase{"Expiration Date (MM-YY)"}, + InputTestCase{"Expiration Date MM / YY"}, + InputTestCase{"Expiration Date MM/YY"}, + InputTestCase{"Expiration Date MM - YY"}, + InputTestCase{"Expiration Date MM-YY"}, + InputTestCase{"expiration date yy"}, + InputTestCase{"Exp Date (MM / YY)"})); + } // namespace autofill diff --git a/chromium/components/autofill/core/common/autofill_util_unittest.cc b/chromium/components/autofill/core/common/autofill_util_unittest.cc index 2115b3ed3ab..6e04dc76031 100644 --- a/chromium/components/autofill/core/common/autofill_util_unittest.cc +++ b/chromium/components/autofill/core/common/autofill_util_unittest.cc @@ -15,7 +15,18 @@ namespace autofill { // Tests for FieldIsSuggestionSubstringStartingOnTokenBoundary(). -TEST(AutofillUtilTest, FieldIsSuggestionSubstringStartingOnTokenBoundary) { +struct FieldIsTokenBoundarySubstringCase { + const char* const field_suggestion; + const char* const field_contents; + const bool case_sensitive; + const bool expected_result; +}; + +class FieldIsTokenBoundarySubstringCaseTest + : public testing::TestWithParam<FieldIsTokenBoundarySubstringCase> {}; + +TEST_P(FieldIsTokenBoundarySubstringCaseTest, + FieldIsSuggestionSubstringStartingOnTokenBoundary) { // FieldIsSuggestionSubstringStartingOnTokenBoundary should not work yet // without a flag. EXPECT_FALSE(FieldIsSuggestionSubstringStartingOnTokenBoundary( @@ -25,106 +36,124 @@ TEST(AutofillUtilTest, FieldIsSuggestionSubstringStartingOnTokenBoundary) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableSuggestionsWithSubstringMatch); - const struct { - const char* const field_suggestion; - const char* const field_contents; - bool case_sensitive; - bool expected_result; - } kTestCases[] = { - {"ab@cd.b", "a", false, true}, - {"ab@cd.b", "b", false, true}, - {"ab@cd.b", "Ab", false, true}, - {"ab@cd.b", "Ab", true, false}, - {"ab@cd.b", "cd", true, true}, - {"ab@cd.b", "d", false, false}, - {"ab@cd.b", "b@", true, false}, - {"ab@cd.b", "ab", false, true}, - {"ab@cd.b", "cd.b", true, true}, - {"ab@cd.b", "b@cd", false, false}, - {"ab@cd.b", "ab@c", false, true}, - {"ba.a.ab", "a.a", false, true}, - {"", "ab", false, false}, - {"", "ab", true, false}, - {"ab", "", false, true}, - {"ab", "", true, true}, - }; - - for (const auto& test_case : kTestCases) { - SCOPED_TRACE(testing::Message() - << "suggestion = " << test_case.field_suggestion - << ", contents = " << test_case.field_contents - << ", case_sensitive = " << test_case.case_sensitive); - - EXPECT_EQ(test_case.expected_result, - FieldIsSuggestionSubstringStartingOnTokenBoundary( - base::ASCIIToUTF16(test_case.field_suggestion), - base::ASCIIToUTF16(test_case.field_contents), - test_case.case_sensitive)); - } + auto test_case = GetParam(); + SCOPED_TRACE(testing::Message() + << "suggestion = " << test_case.field_suggestion + << ", contents = " << test_case.field_contents + << ", case_sensitive = " << test_case.case_sensitive); + + EXPECT_EQ(test_case.expected_result, + FieldIsSuggestionSubstringStartingOnTokenBoundary( + base::ASCIIToUTF16(test_case.field_suggestion), + base::ASCIIToUTF16(test_case.field_contents), + test_case.case_sensitive)); } +INSTANTIATE_TEST_CASE_P( + AutofillUtilTest, + FieldIsTokenBoundarySubstringCaseTest, + testing::Values( + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "a", false, true}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "b", false, true}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "Ab", false, true}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "Ab", true, false}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "cd", true, true}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "d", false, false}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "b@", true, false}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "ab", false, true}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "cd.b", true, true}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "b@cd", false, false}, + FieldIsTokenBoundarySubstringCase{"ab@cd.b", "ab@c", false, true}, + FieldIsTokenBoundarySubstringCase{"ba.a.ab", "a.a", false, true}, + FieldIsTokenBoundarySubstringCase{"", "ab", false, false}, + FieldIsTokenBoundarySubstringCase{"", "ab", true, false}, + FieldIsTokenBoundarySubstringCase{"ab", "", false, true}, + FieldIsTokenBoundarySubstringCase{"ab", "", true, true})); + // Tests for GetTextSelectionStart(). -TEST(AutofillUtilTest, GetTextSelectionStart) { - const size_t kInvalid = base::string16::npos; - const struct { - const char* const field_suggestion; - const char* const field_contents; - bool case_sensitive; - size_t expected_start; - } kTestCases[] = { - {"ab@cd.b", "a", false, 1}, - {"ab@cd.b", "A", true, kInvalid}, - {"ab@cd.b", "Ab", false, 2}, - {"ab@cd.b", "Ab", true, kInvalid}, - {"ab@cd.b", "cd", false, 5}, - {"ab@cd.b", "ab@c", false, 4}, - {"ab@cd.b", "cd.b", false, 7}, - {"ab@cd.b", "b@cd", false, kInvalid}, - {"ab@cd.b", "b", false, 7}, - {"ba.a.ab", "a.a", false, 6}, - {"texample@example.com", "example", false, 16}, - }; - - for (const auto& test_case : kTestCases) { - SCOPED_TRACE(testing::Message() - << "suggestion = " << test_case.field_suggestion - << ", contents = " << test_case.field_contents - << ", case_sensitive = " << test_case.case_sensitive); - - EXPECT_EQ( - test_case.expected_start, - GetTextSelectionStart(base::ASCIIToUTF16(test_case.field_suggestion), - base::ASCIIToUTF16(test_case.field_contents), - test_case.case_sensitive)); - } +struct GetTextSelectionStartCase { + const char* const field_suggestion; + const char* const field_contents; + const bool case_sensitive; + const size_t expected_start; +}; + +class GetTextSelectionStartTest + : public testing::TestWithParam<GetTextSelectionStartCase> {}; + +TEST_P(GetTextSelectionStartTest, GetTextSelectionStart) { + auto test_case = GetParam(); + SCOPED_TRACE(testing::Message() + << "suggestion = " << test_case.field_suggestion + << ", contents = " << test_case.field_contents + << ", case_sensitive = " << test_case.case_sensitive); + EXPECT_EQ( + test_case.expected_start, + GetTextSelectionStart(base::ASCIIToUTF16(test_case.field_suggestion), + base::ASCIIToUTF16(test_case.field_contents), + test_case.case_sensitive)); } +INSTANTIATE_TEST_CASE_P( + AutofillUtilTest, + GetTextSelectionStartTest, + testing::Values( + GetTextSelectionStartCase{"ab@cd.b", "a", false, 1}, + GetTextSelectionStartCase{"ab@cd.b", "A", true, base::string16::npos}, + GetTextSelectionStartCase{"ab@cd.b", "Ab", false, 2}, + GetTextSelectionStartCase{"ab@cd.b", "Ab", true, base::string16::npos}, + GetTextSelectionStartCase{"ab@cd.b", "cd", false, 5}, + GetTextSelectionStartCase{"ab@cd.b", "ab@c", false, 4}, + GetTextSelectionStartCase{"ab@cd.b", "cd.b", false, 7}, + GetTextSelectionStartCase{"ab@cd.b", "b@cd", false, + base::string16::npos}, + GetTextSelectionStartCase{"ab@cd.b", "b", false, 7}, + GetTextSelectionStartCase{"ba.a.ab", "a.a", false, 6}, + GetTextSelectionStartCase{"texample@example.com", "example", false, + 16})); + // Tests for LowercaseAndTokenizeAttributeString -TEST(AutofillUtilTest, LowercaseAndTokenizeAttributeString) { - const struct { - const char* const attribute; - std::vector<std::string> tokens; - } kTestCases[] = { - // Test leading and trailing whitespace, test tabs and newlines - {"foo bar baz", {"foo", "bar", "baz"}}, - {" foo bar baz ", {"foo", "bar", "baz"}}, - {"foo\tbar baz ", {"foo", "bar", "baz"}}, - {"foo\nbar baz ", {"foo", "bar", "baz"}}, - - // Test different forms of capitalization - {"FOO BAR BAZ", {"foo", "bar", "baz"}}, - {"foO baR bAz", {"foo", "bar", "baz"}}, - - // Test collapsing of multiple whitespace characters in a row - {" \t\t\n\n ", std::vector<std::string>()}, - {"foO baR bAz", {"foo", "bar", "baz"}}, - }; - - for (const auto& test_case : kTestCases) { - SCOPED_TRACE(testing::Message() << "attribute = " << test_case.attribute); - - EXPECT_EQ(test_case.tokens, - LowercaseAndTokenizeAttributeString(test_case.attribute)); - } +struct LowercaseAndTokenizeAttributeStringCase { + const char* const attribute; + std::vector<std::string> tokens; +}; + +class LowercaseAndTokenizeAttributeStringTest + : public testing::TestWithParam<LowercaseAndTokenizeAttributeStringCase> {}; + +TEST_P(LowercaseAndTokenizeAttributeStringTest, + LowercaseAndTokenizeAttributeStringTest) { + auto test_case = GetParam(); + SCOPED_TRACE(testing::Message() << "attribute = " << test_case.attribute); + + EXPECT_EQ(test_case.tokens, + LowercaseAndTokenizeAttributeString(test_case.attribute)); } + +INSTANTIATE_TEST_CASE_P( + AutofillUtilTest, + LowercaseAndTokenizeAttributeStringTest, + testing::Values( + // Test leading and trailing whitespace, test tabs and newlines + LowercaseAndTokenizeAttributeStringCase{"foo bar baz", + {"foo", "bar", "baz"}}, + LowercaseAndTokenizeAttributeStringCase{" foo bar baz ", + {"foo", "bar", "baz"}}, + LowercaseAndTokenizeAttributeStringCase{"foo\tbar baz ", + {"foo", "bar", "baz"}}, + LowercaseAndTokenizeAttributeStringCase{"foo\nbar baz ", + {"foo", "bar", "baz"}}, + + // Test different forms of capitalization + LowercaseAndTokenizeAttributeStringCase{"FOO BAR BAZ", + {"foo", "bar", "baz"}}, + LowercaseAndTokenizeAttributeStringCase{"foO baR bAz", + {"foo", "bar", "baz"}}, + + // Test collapsing of multiple whitespace characters in a row + LowercaseAndTokenizeAttributeStringCase{" \t\t\n\n ", + std::vector<std::string>()}, + LowercaseAndTokenizeAttributeStringCase{"foO baR bAz", + {"foo", "bar", "baz"}})); + } // namespace autofill diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc index 3969e1ff4e4..17892915c65 100644 --- a/chromium/components/autofill/core/common/password_form.cc +++ b/chromium/components/autofill/core/common/password_form.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <algorithm> #include <ostream> #include <sstream> @@ -40,9 +41,9 @@ void PasswordFormToJSON(const PasswordForm& form, form.new_password_value_is_default); target->SetBoolean("new_password_marked_by_site", form.new_password_marked_by_site); - target->SetString("other_possible_usernames", - base::JoinString(form.other_possible_usernames, - base::ASCIIToUTF16("|"))); + target->SetString( + "other_possible_usernames", + OtherPossibleUsernamesToString(form.other_possible_usernames)); target->SetBoolean("blacklisted", form.blacklisted_by_user); target->SetBoolean("preferred", form.preferred); target->SetDouble("date_created", form.date_created.ToDoubleT()); @@ -167,6 +168,16 @@ bool LessThanUniqueKey::operator()( return left->origin < right->origin; } +base::string16 OtherPossibleUsernamesToString( + const PossibleUsernamesVector& possible_usernames) { + std::vector<base::string16> pairs(possible_usernames.size()); + std::transform(possible_usernames.begin(), possible_usernames.end(), + pairs.begin(), [](const PossibleUsernamePair& p) { + return p.first + base::ASCIIToUTF16("+") + p.second; + }); + return base::JoinString(pairs, base::ASCIIToUTF16(", ")); +} + std::ostream& operator<<(std::ostream& os, PasswordForm::Layout layout) { switch (layout) { case PasswordForm::Layout::LAYOUT_OTHER: diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h index 74716230236..8bcf44850a8 100644 --- a/chromium/components/autofill/core/common/password_form.h +++ b/chromium/components/autofill/core/common/password_form.h @@ -17,6 +17,12 @@ namespace autofill { +// Pair of possible username value and field name that contained this value. +using PossibleUsernamePair = std::pair<base::string16, base::string16>; + +// Vector of possible username values and corresponding field names. +using PossibleUsernamesVector = std::vector<PossibleUsernamePair>; + // The PasswordForm struct encapsulates information about a login form, // which can be an HTML form or a dialog with username/password text fields. // @@ -141,7 +147,7 @@ struct PasswordForm { // determining the username are incorrect. Optional. // // When parsing an HTML form, this is typically empty. - std::vector<base::string16> other_possible_usernames; + PossibleUsernamesVector other_possible_usernames; // The name of the input element corresponding to the current password. // Optional (improves scoring). @@ -301,6 +307,10 @@ struct LessThanUniqueKey { const std::unique_ptr<PasswordForm>& right) const; }; +// Converts a vector of possible usernames to string. +base::string16 OtherPossibleUsernamesToString( + const PossibleUsernamesVector& possible_usernames); + // For testing. std::ostream& operator<<(std::ostream& os, PasswordForm::Layout layout); std::ostream& operator<<(std::ostream& os, const PasswordForm& form); diff --git a/chromium/components/autofill/core/common/password_form_fill_data.cc b/chromium/components/autofill/core/common/password_form_fill_data.cc index 9b1383046e0..e53c160b3be 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data.cc +++ b/chromium/components/autofill/core/common/password_form_fill_data.cc @@ -84,10 +84,17 @@ void InitPasswordFormFillData( if (it.second->is_public_suffix_match || it.second->is_affiliation_based_match) key.realm = it.second->signon_realm; - result->other_possible_usernames[key] = - it.second->other_possible_usernames; } } } +PasswordFormFillData ClearPasswordValues(const PasswordFormFillData& data) { + PasswordFormFillData result(data); + if (result.wait_for_username) + result.password_field.value.clear(); + for (auto& credentials : result.additional_logins) + credentials.second.password.clear(); + return result; +} + } // namespace autofill diff --git a/chromium/components/autofill/core/common/password_form_fill_data.h b/chromium/components/autofill/core/common/password_form_fill_data.h index fd0f64fe84a..b7e8e27c0cd 100644 --- a/chromium/components/autofill/core/common/password_form_fill_data.h +++ b/chromium/components/autofill/core/common/password_form_fill_data.h @@ -63,6 +63,7 @@ struct PasswordFormFillData { // that the original saved username is correct. This data is keyed by the // saved username/password to ensure uniqueness, though the username is not // used. + // TODO(crbug/188908). Remove |other_possible_usernames| or launch. UsernamesCollection other_possible_usernames; // Tells us whether we need to wait for the user to enter a valid username @@ -96,6 +97,10 @@ void InitPasswordFormFillData( bool enable_other_possible_usernames, PasswordFormFillData* result); +// Renderer needs to have only a password that should be autofilled, all other +// passwords might be safety erased. +PasswordFormFillData ClearPasswordValues(const PasswordFormFillData& data); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_FILL_DATA_H__ diff --git a/chromium/components/autofill/core/common/password_form_generation_data.cc b/chromium/components/autofill/core/common/password_form_generation_data.cc new file mode 100644 index 00000000000..521eabc1818 --- /dev/null +++ b/chromium/components/autofill/core/common/password_form_generation_data.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2017 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 "components/autofill/core/common/password_form_generation_data.h" + +namespace autofill { + +PasswordFormGenerationData::PasswordFormGenerationData() = default; + +PasswordFormGenerationData::PasswordFormGenerationData( + FormSignature form_signature, + FieldSignature field_signature) + : form_signature(form_signature), field_signature(field_signature) {} + +PasswordFormGenerationData::PasswordFormGenerationData( + const PasswordFormGenerationData& other) = default; + +PasswordFormGenerationData::~PasswordFormGenerationData() = default; + +} // namespace autofill diff --git a/chromium/components/autofill/core/common/password_form_generation_data.h b/chromium/components/autofill/core/common/password_form_generation_data.h index d010d9a8ddb..f14d2953821 100644 --- a/chromium/components/autofill/core/common/password_form_generation_data.h +++ b/chromium/components/autofill/core/common/password_form_generation_data.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include "base/optional.h" #include "base/strings/string16.h" #include "components/autofill/core/common/form_field_data.h" #include "components/autofill/core/common/signatures_util.h" @@ -17,6 +18,12 @@ namespace autofill { // Structure used for sending information from browser to renderer about on // which fields password should be generated. struct PasswordFormGenerationData { + PasswordFormGenerationData(); + PasswordFormGenerationData(FormSignature form_signature, + FieldSignature field_signature); + PasswordFormGenerationData(const PasswordFormGenerationData& other); + ~PasswordFormGenerationData(); + // The unique signature of form where password should be generated // (see components/autofill/core/browser/form_structure.h). FormSignature form_signature; @@ -24,6 +31,10 @@ struct PasswordFormGenerationData { // The unique signature of field where password should be generated // (see components/autofill/core/browser/autofill_field.h). FieldSignature field_signature; + + // The unique signature of the confirmation field where the generated password + // should be copied to. + base::Optional<FieldSignature> confirmation_field_signature; }; } // namespace autofill diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.cc b/chromium/components/autofill/core/common/save_password_progress_logger.cc index 5fc676c20e9..8a55fdd2b36 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger.cc @@ -18,7 +18,6 @@ using base::checked_cast; using base::Value; using base::DictionaryValue; -using base::StringValue; namespace autofill { @@ -94,7 +93,7 @@ void SavePasswordProgressLogger::LogHTMLForm( void SavePasswordProgressLogger::LogURL( SavePasswordProgressLogger::StringID label, const GURL& url) { - LogValue(label, StringValue(ScrubURL(url))); + LogValue(label, Value(ScrubURL(url))); } void SavePasswordProgressLogger::LogBoolean( @@ -117,7 +116,7 @@ void SavePasswordProgressLogger::LogNumber( void SavePasswordProgressLogger::LogMessage( SavePasswordProgressLogger::StringID message) { - LogValue(STRING_MESSAGE, StringValue(GetStringFromID(message))); + LogValue(STRING_MESSAGE, Value(GetStringFromID(message))); } // static @@ -144,7 +143,7 @@ std::string SavePasswordProgressLogger::ScrubElementID( // static std::string SavePasswordProgressLogger::ScrubElementID(std::string element_id) { std::replace_if(element_id.begin(), element_id.end(), IsUnwantedInElementID, - ' '); + '_'); return element_id; } @@ -391,6 +390,39 @@ std::string SavePasswordProgressLogger::GetStringFromID( return "Form votes"; case SavePasswordProgressLogger::STRING_REUSE_FOUND: return "Password reused from "; + case SavePasswordProgressLogger::STRING_GENERATION_DISABLED_SAVING_DISABLED: + return "Generation disabled: saving disabled"; + case SavePasswordProgressLogger::STRING_GENERATION_DISABLED_NO_SYNC: + return "Generation disabled: no sync"; + case SavePasswordProgressLogger:: + STRING_GENERATION_DISABLED_CUSTOM_PASSPHRASE: + return "Generation disabled: custom passphrase"; + case STRING_GENERATION_RENDERER_ENABLED: + return "Generation renderer enabled"; + case STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM: + return "Generation invalid PasswordForm"; + case STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS: + return "Generation possible account creation forms"; + case STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS: + return "Generation: no PasswordManager access"; + case STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND: + return "Generation: account creation form already found"; + case STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS: + return "Generation: no possible account creation forms"; + case STRING_GENERATION_RENDERER_NOT_BLACKLISTED: + return "Generation: no non-blacklisted confirmation"; + case STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE: + return "Generation: autocomplete attributes found"; + case STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL: + return "Generation: no server signal"; + case STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND: + return "Generation: eligible form found"; + case STRING_GENERATION_RENDERER_NO_FIELD_FOUND: + return "Generation: fields for generation are not found"; + case STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP: + return "Show generation popup"; + case STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED: + return "Generated password accepted"; case SavePasswordProgressLogger::STRING_INVALID: return "INVALID"; // Intentionally no default: clause here -- all IDs need to get covered. diff --git a/chromium/components/autofill/core/common/save_password_progress_logger.h b/chromium/components/autofill/core/common/save_password_progress_logger.h index de8a08232b4..19c9e04c0dd 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger.h +++ b/chromium/components/autofill/core/common/save_password_progress_logger.h @@ -150,6 +150,22 @@ class SavePasswordProgressLogger { STRING_SERVER_PREDICTIONS, STRING_FORM_VOTES, STRING_REUSE_FOUND, + STRING_GENERATION_DISABLED_SAVING_DISABLED, + STRING_GENERATION_DISABLED_NO_SYNC, + STRING_GENERATION_DISABLED_CUSTOM_PASSPHRASE, + STRING_GENERATION_RENDERER_ENABLED, + STRING_GENERATION_RENDERER_INVALID_PASSWORD_FORM, + STRING_GENERATION_RENDERER_POSSIBLE_ACCOUNT_CREATION_FORMS, + STRING_GENERATION_RENDERER_NO_PASSWORD_MANAGER_ACCESS, + STRING_GENERATION_RENDERER_FORM_ALREADY_FOUND, + STRING_GENERATION_RENDERER_NO_POSSIBLE_CREATION_FORMS, + STRING_GENERATION_RENDERER_NOT_BLACKLISTED, + STRING_GENERATION_RENDERER_AUTOCOMPLETE_ATTRIBUTE, + STRING_GENERATION_RENDERER_NO_SERVER_SIGNAL, + STRING_GENERATION_RENDERER_ELIGIBLE_FORM_FOUND, + STRING_GENERATION_RENDERER_NO_FIELD_FOUND, + STRING_GENERATION_RENDERER_SHOW_GENERATION_POPUP, + STRING_GENERATION_RENDERER_GENERATED_PASSWORD_ACCEPTED, STRING_INVALID, // Represents a string returned in a case of an error. STRING_MAX = STRING_INVALID }; diff --git a/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc b/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc index f047183d414..a20d11946af 100644 --- a/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc +++ b/chromium/components/autofill/core/common/save_password_progress_logger_unittest.cc @@ -63,11 +63,11 @@ TEST(SavePasswordProgressLoggerTest, LogPasswordFormElementID) { TestLogger logger; PasswordForm form; const std::string kHTMLInside("Username <script> element"); - const std::string kHTMLInsideExpected("Username script element"); + const std::string kHTMLInsideExpected("Username__script__element"); const std::string kIPAddressInside("y128.0.0.1Y"); - const std::string kIPAddressInsideExpected("y128 0 0 1Y"); + const std::string kIPAddressInsideExpected("y128_0_0_1Y"); const std::string kSpecialCharsInside("X@#a$%B&*c()D;:e+!x"); - const std::string kSpecialCharsInsideExpected("X a B c D e x"); + const std::string kSpecialCharsInsideExpected("X__a__B__c__D__e__x"); form.username_element = UTF8ToUTF16(kHTMLInside); form.password_element = UTF8ToUTF16(kIPAddressInside); form.new_password_element = UTF8ToUTF16(kSpecialCharsInside); |