summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-09 22:56:56 +0000
committerjoi@chromium.org <joi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-09 22:56:56 +0000
commite4b2fa31a1f25cfc80a422bcf1c76317c9e00cf2 (patch)
treef2b18dd29be63dddf88dfd728b8ebcf0ab87834b /components
parent3a8a6d92816b0e5b4cd67a74516fbda36fdc692d (diff)
downloadchromium_src-e4b2fa31a1f25cfc80a422bcf1c76317c9e00cf2.zip
chromium_src-e4b2fa31a1f25cfc80a422bcf1c76317c9e00cf2.tar.gz
chromium_src-e4b2fa31a1f25cfc80a422bcf1c76317c9e00cf2.tar.bz2
Move remaining Autofill code to //components/autofill.
This leaves behind all browser tests, the AutofillCCInfobarDelegate and PersonalDataManagerFactory, which remain Chrome-specific. All source files were moved using //tools/git/move_source_file.py, which updates includes of moved files, sorts include order, and updates header guards. The only manual bits of this change were: - Move .proto file - Move .cc.utf8 file - Update .gypi files - Update DEPS files - Remove an unnecessary include from autofill_manager.cc TBR=ben@chromium.org BUG=140037 Review URL: https://chromiumcodereview.appspot.com/12434004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187173 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r--components/autofill.gypi32
-rw-r--r--components/autofill/DEPS7
-rw-r--r--components/autofill/browser/DEPS60
-rw-r--r--components/autofill/browser/address.cc114
-rw-r--r--components/autofill/browser/address.h54
-rw-r--r--components/autofill/browser/address_field.cc335
-rw-r--r--components/autofill/browser/address_field.h84
-rw-r--r--components/autofill/browser/address_field_unittest.cc294
-rw-r--r--components/autofill/browser/address_unittest.cc134
-rw-r--r--components/autofill/browser/autocheckout/whitelist_manager.cc158
-rw-r--r--components/autofill/browser/autocheckout/whitelist_manager.h93
-rw-r--r--components/autofill/browser/autocheckout/whitelist_manager_unittest.cc242
-rw-r--r--components/autofill/browser/autocheckout_manager.cc285
-rw-r--r--components/autofill/browser/autocheckout_manager.h117
-rw-r--r--components/autofill/browser/autocheckout_manager_unittest.cc529
-rw-r--r--components/autofill/browser/autocheckout_page_meta_data.cc27
-rw-r--r--components/autofill/browser/autocheckout_page_meta_data.h49
-rw-r--r--components/autofill/browser/autocheckout_page_meta_data_unittest.cc54
-rw-r--r--components/autofill/browser/autocomplete_history_manager.cc289
-rw-r--r--components/autofill/browser/autocomplete_history_manager.h91
-rw-r--r--components/autofill/browser/autocomplete_history_manager_unittest.cc239
-rw-r--r--components/autofill/browser/autofill-inl.h35
-rw-r--r--components/autofill/browser/autofill_common_test.cc110
-rw-r--r--components/autofill/browser/autofill_common_test.h58
-rw-r--r--components/autofill/browser/autofill_country.cc896
-rw-r--r--components/autofill/browser/autofill_country.h69
-rw-r--r--components/autofill/browser/autofill_country_unittest.cc86
-rw-r--r--components/autofill/browser/autofill_download.cc340
-rw-r--r--components/autofill/browser/autofill_download.h166
-rw-r--r--components/autofill/browser/autofill_download_unittest.cc505
-rw-r--r--components/autofill/browser/autofill_download_url.cc45
-rw-r--r--components/autofill/browser/autofill_download_url.h18
-rw-r--r--components/autofill/browser/autofill_download_url_unittest.cc23
-rw-r--r--components/autofill/browser/autofill_external_delegate.cc382
-rw-r--r--components/autofill/browser/autofill_external_delegate.h189
-rw-r--r--components/autofill/browser/autofill_external_delegate_unittest.cc308
-rw-r--r--components/autofill/browser/autofill_field.cc84
-rw-r--r--components/autofill/browser/autofill_field.h88
-rw-r--r--components/autofill/browser/autofill_field_unittest.cc93
-rw-r--r--components/autofill/browser/autofill_ie_toolbar_import_win.cc295
-rw-r--r--components/autofill/browser/autofill_ie_toolbar_import_win.h20
-rw-r--r--components/autofill/browser/autofill_ie_toolbar_import_win_unittest.cc204
-rw-r--r--components/autofill/browser/autofill_manager.cc1321
-rw-r--r--components/autofill/browser/autofill_manager.h442
-rw-r--r--components/autofill/browser/autofill_manager_delegate.h145
-rw-r--r--components/autofill/browser/autofill_manager_unittest.cc3228
-rw-r--r--components/autofill/browser/autofill_merge_unittest.cc217
-rw-r--r--components/autofill/browser/autofill_metrics.cc453
-rw-r--r--components/autofill/browser/autofill_metrics.h248
-rw-r--r--components/autofill/browser/autofill_metrics_unittest.cc1578
-rw-r--r--components/autofill/browser/autofill_popup_delegate.h40
-rw-r--r--components/autofill/browser/autofill_profile.cc848
-rw-r--r--components/autofill/browser/autofill_profile.h211
-rw-r--r--components/autofill/browser/autofill_profile_unittest.cc848
-rw-r--r--components/autofill/browser/autofill_regex_constants.cc.utf8293
-rw-r--r--components/autofill/browser/autofill_regex_constants.h59
-rw-r--r--components/autofill/browser/autofill_regexes.cc83
-rw-r--r--components/autofill/browser/autofill_regexes.h19
-rw-r--r--components/autofill/browser/autofill_regexes_unittest.cc60
-rw-r--r--components/autofill/browser/autofill_scanner.cc54
-rw-r--r--components/autofill/browser/autofill_scanner.h57
-rw-r--r--components/autofill/browser/autofill_server_field_info.h20
-rw-r--r--components/autofill/browser/autofill_type.cc294
-rw-r--r--components/autofill/browser/autofill_type.h54
-rw-r--r--components/autofill/browser/autofill_type_unittest.cc52
-rw-r--r--components/autofill/browser/autofill_xml_parser.cc197
-rw-r--r--components/autofill/browser/autofill_xml_parser.h167
-rw-r--r--components/autofill/browser/autofill_xml_parser_unittest.cc367
-rw-r--r--components/autofill/browser/contact_info.cc196
-rw-r--r--components/autofill/browser/contact_info.h94
-rw-r--r--components/autofill/browser/contact_info_unittest.cc101
-rw-r--r--components/autofill/browser/credit_card.cc678
-rw-r--r--components/autofill/browser/credit_card.h153
-rw-r--r--components/autofill/browser/credit_card_field.cc229
-rw-r--r--components/autofill/browser/credit_card_field.h71
-rw-r--r--components/autofill/browser/credit_card_field_unittest.cc318
-rw-r--r--components/autofill/browser/credit_card_unittest.cc359
-rw-r--r--components/autofill/browser/crypto/rc4_decryptor.h106
-rw-r--r--components/autofill/browser/data_driven_test.cc90
-rw-r--r--components/autofill/browser/data_driven_test.h50
-rw-r--r--components/autofill/browser/email_field.cc28
-rw-r--r--components/autofill/browser/email_field.h28
-rw-r--r--components/autofill/browser/field_types.h91
-rw-r--r--components/autofill/browser/form_field.cc197
-rw-r--r--components/autofill/browser/form_field.h120
-rw-r--r--components/autofill/browser/form_field_unittest.cc146
-rw-r--r--components/autofill/browser/form_group.cc322
-rw-r--r--components/autofill/browser/form_group.h92
-rw-r--r--components/autofill/browser/form_structure.cc1156
-rw-r--r--components/autofill/browser/form_structure.h241
-rw-r--r--components/autofill/browser/form_structure_unittest.cc2202
-rw-r--r--components/autofill/browser/name_field.cc214
-rw-r--r--components/autofill/browser/name_field.h42
-rw-r--r--components/autofill/browser/name_field_unittest.cc307
-rw-r--r--components/autofill/browser/password_autofill_manager.cc83
-rw-r--r--components/autofill/browser/password_autofill_manager.h71
-rw-r--r--components/autofill/browser/password_autofill_manager_unittest.cc74
-rw-r--r--components/autofill/browser/password_generator.cc125
-rw-r--r--components/autofill/browser/password_generator.h48
-rw-r--r--components/autofill/browser/password_generator_unittest.cc58
-rw-r--r--components/autofill/browser/personal_data_manager.cc972
-rw-r--r--components/autofill/browser/personal_data_manager.h282
-rw-r--r--components/autofill/browser/personal_data_manager_mac.mm262
-rw-r--r--components/autofill/browser/personal_data_manager_observer.h21
-rw-r--r--components/autofill/browser/personal_data_manager_unittest.cc1975
-rw-r--r--components/autofill/browser/phone_field.cc273
-rw-r--r--components/autofill/browser/phone_field.h89
-rw-r--r--components/autofill/browser/phone_field_unittest.cc226
-rw-r--r--components/autofill/browser/phone_number.cc238
-rw-r--r--components/autofill/browser/phone_number.h94
-rw-r--r--components/autofill/browser/phone_number_i18n.cc296
-rw-r--r--components/autofill/browser/phone_number_i18n.h106
-rw-r--r--components/autofill/browser/phone_number_i18n_unittest.cc372
-rw-r--r--components/autofill/browser/phone_number_unittest.cc203
-rw-r--r--components/autofill/browser/risk/fingerprint.cc422
-rw-r--r--components/autofill/browser/risk/fingerprint.h75
-rw-r--r--components/autofill/browser/risk/proto/fingerprint.proto218
-rw-r--r--components/autofill/browser/test_autofill_external_delegate.cc29
-rw-r--r--components/autofill/browser/test_autofill_external_delegate.h21
-rw-r--r--components/autofill/browser/test_autofill_manager_delegate.cc74
-rw-r--r--components/autofill/browser/test_autofill_manager_delegate.h65
-rw-r--r--components/autofill/browser/validation.cc76
-rw-r--r--components/autofill/browser/validation.h28
-rw-r--r--components/autofill/browser/validation_unittest.cc101
-rw-r--r--components/autofill/browser/wallet/cart.cc25
-rw-r--r--components/autofill/browser/wallet/cart.h50
-rw-r--r--components/autofill/browser/wallet/cart_unittest.cc21
-rw-r--r--components/autofill/browser/wallet/encryption_escrow_client.cc178
-rw-r--r--components/autofill/browser/wallet/encryption_escrow_client.h91
-rw-r--r--components/autofill/browser/wallet/encryption_escrow_client_observer.h50
-rw-r--r--components/autofill/browser/wallet/encryption_escrow_client_unittest.cc209
-rw-r--r--components/autofill/browser/wallet/full_wallet.cc224
-rw-r--r--components/autofill/browser/wallet/full_wallet.h121
-rw-r--r--components/autofill/browser/wallet/full_wallet_unittest.cc465
-rw-r--r--components/autofill/browser/wallet/instrument.cc110
-rw-r--r--components/autofill/browser/wallet/instrument.h93
-rw-r--r--components/autofill/browser/wallet/instrument_unittest.cc189
-rw-r--r--components/autofill/browser/wallet/required_action.cc65
-rw-r--r--components/autofill/browser/wallet/required_action.h44
-rw-r--r--components/autofill/browser/wallet/wallet_address.cc263
-rw-r--r--components/autofill/browser/wallet/wallet_address.h167
-rw-r--r--components/autofill/browser/wallet/wallet_address_unittest.cc313
-rw-r--r--components/autofill/browser/wallet/wallet_client.cc664
-rw-r--r--components/autofill/browser/wallet/wallet_client.h229
-rw-r--r--components/autofill/browser/wallet/wallet_client_observer.h88
-rw-r--r--components/autofill/browser/wallet/wallet_client_unittest.cc1441
-rw-r--r--components/autofill/browser/wallet/wallet_items.cc411
-rw-r--r--components/autofill/browser/wallet/wallet_items.h262
-rw-r--r--components/autofill/browser/wallet/wallet_items_unittest.cc543
-rw-r--r--components/autofill/browser/wallet/wallet_service_url.cc107
-rw-r--r--components/autofill/browser/wallet/wallet_service_url.h37
-rw-r--r--components/autofill/browser/wallet/wallet_service_url_unittest.cc40
-rw-r--r--components/autofill/browser/wallet/wallet_test_util.cc49
-rw-r--r--components/autofill/browser/wallet/wallet_test_util.h23
-rw-r--r--components/autofill/renderer/DEPS7
-rw-r--r--components/autofill/renderer/autofill_agent.cc858
-rw-r--r--components/autofill/renderer/autofill_agent.h270
-rw-r--r--components/autofill/renderer/form_autofill_util.cc1032
-rw-r--r--components/autofill/renderer/form_autofill_util.h139
-rw-r--r--components/autofill/renderer/form_cache.cc295
-rw-r--r--components/autofill/renderer/form_cache.h74
-rw-r--r--components/autofill/renderer/page_click_listener.h36
-rw-r--r--components/autofill/renderer/page_click_tracker.cc160
-rw-r--r--components/autofill/renderer/page_click_tracker.h74
-rw-r--r--components/autofill/renderer/password_autofill_manager.cc649
-rw-r--r--components/autofill/renderer/password_autofill_manager.h131
-rw-r--r--components/autofill/renderer/password_generation_manager.cc256
-rw-r--r--components/autofill/renderer/password_generation_manager.h90
168 files changed, 43906 insertions, 1 deletions
diff --git a/components/autofill.gypi b/components/autofill.gypi
index c5d2611..2afd3e9 100644
--- a/components/autofill.gypi
+++ b/components/autofill.gypi
@@ -3,6 +3,38 @@
# found in the LICENSE file.
{
+ 'targets': [
+ {
+ 'target_name': 'autofill_regexes',
+ 'type': 'none',
+ 'actions': [{
+ 'action_name': 'autofill_regexes',
+ 'inputs': [
+ '<(DEPTH)/build/escape_unicode.py',
+ 'autofill/browser/autofill_regex_constants.cc.utf8',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/autofill_regex_constants.cc',
+ ],
+ 'action': ['python', '<(DEPTH)/build/escape_unicode.py',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)',
+ 'autofill/browser/autofill_regex_constants.cc.utf8'],
+ }],
+ },
+ {
+ # Protobuf compiler / generate rule for Autofill's risk integration.
+ 'target_name': 'autofill_risk_proto',
+ 'type': 'static_library',
+ 'sources': [
+ 'autofill/browser/risk/proto/fingerprint.proto',
+ ],
+ 'variables': {
+ 'proto_in_dir': 'autofill/browser/risk/proto',
+ 'proto_out_dir': 'components/autofill/browser/risk/proto',
+ },
+ 'includes': [ '../build/protoc.gypi' ]
+ },
+ ],
'conditions': [
['OS != "ios"', {
'targets': [
diff --git a/components/autofill/DEPS b/components/autofill/DEPS
index 9d52b11..cd724eb 100644
--- a/components/autofill/DEPS
+++ b/components/autofill/DEPS
@@ -1,4 +1,9 @@
include_rules = [
- "+third_party/WebKit/Source/WebKit/chromium/public",
+ "+google_apis/gaia/gaia_urls.h",
+ "+grit", # For generated headers
+ "+net",
+ # Allow inclusion of WebKit API files.
+ "+third_party/WebKit/Source/Platform/chromium",
+ "+third_party/WebKit/Source/WebKit/chromium",
"+ui",
]
diff --git a/components/autofill/browser/DEPS b/components/autofill/browser/DEPS
new file mode 100644
index 0000000..f1032f5
--- /dev/null
+++ b/components/autofill/browser/DEPS
@@ -0,0 +1,60 @@
+include_rules = [
+ "+content/public/browser",
+ "+crypto/random.h",
+ "+google_apis/google_api_keys.h",
+ "+net",
+ "+third_party/libjingle",
+ "+third_party/libphonenumber", # For phone number i18n.
+ "+webkit/plugins/webplugininfo.h",
+
+ # TODO(joi): This is "ok" temporarily (it's just a header file with
+ # a large enum, and doesn't bring in any other dependencies on
+ # //chrome). The plan of record to get rid of this is to change
+ # Autofill and WebData to not use NotificationService, and instead
+ # use typed callback interfaces or allow registering base::Callback
+ # callbacks for each event.
+ "!chrome/common/chrome_notification_types.h",
+
+ # TODO(joi, kaiwang): Bring this list to zero.
+ "!chrome/browser/api/webdata",
+
+ # TODO(akalin): Remove this dependency.
+ "!sync/util/data_encryption_win.h",
+]
+
+specific_include_rules = {
+ '.*_[a-z]*test\.cc': [
+ "+content/public/test",
+
+ # TODO(joi, kaiwang): Bring this list to zero.
+ #
+ # Do not add to the list of temporarily-allowed dependencies below,
+ # and please do not introduce more #includes of these files.
+ "!chrome/browser/autofill/autofill_cc_infobar_delegate.h",
+ "!chrome/browser/autofill/personal_data_manager_factory.h",
+ "!chrome/browser/api/infobars/confirm_infobar_delegate.h",
+ "!chrome/browser/api/infobars/infobar_service.h",
+ "!chrome/browser/password_manager/encryptor.h",
+ "!chrome/browser/password_manager/password_manager.h",
+ "!chrome/browser/password_manager/password_manager_delegate_impl.h",
+ "!chrome/browser/profiles/profile.h",
+ "!chrome/browser/sync/profile_sync_service.h",
+ "!chrome/browser/sync/profile_sync_service_factory.h",
+ "!chrome/browser/translate/translate_infobar_delegate.h",
+ "!chrome/browser/translate/translate_manager.h",
+ "!chrome/browser/webdata/autofill_web_data_service_impl.h",
+ "!chrome/browser/webdata/web_data_service.h",
+ "!chrome/browser/webdata/web_data_service_factory.h",
+ "!chrome/browser/ui/autofill/tab_autofill_manager_delegate.h",
+ "!chrome/browser/ui/browser.h",
+ "!chrome/browser/ui/browser_tabstrip.h",
+ "!chrome/browser/ui/browser_window.h",
+ "!chrome/browser/ui/tabs/tab_strip_model.h",
+ "!chrome/common/chrome_constants.h",
+ "!chrome/common/chrome_paths.h",
+ "!chrome/common/pref_names.h",
+ "!chrome/common/render_messages.h",
+ "!chrome/common/url_constants.h",
+ "!chrome/test/base",
+ ],
+}
diff --git a/components/autofill/browser/address.cc b/components/autofill/browser/address.cc
new file mode 100644
index 0000000..7e430f8
--- /dev/null
+++ b/components/autofill/browser/address.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2011 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/browser/address.h"
+
+#include <stddef.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+
+namespace {
+
+const char16 kAddressSplitChars[] = {'-', ',', '#', '.', ' ', 0};
+
+} // namespace
+
+Address::Address() {}
+
+Address::Address(const Address& address) : FormGroup() {
+ *this = address;
+}
+
+Address::~Address() {}
+
+Address& Address::operator=(const Address& address) {
+ if (this == &address)
+ return *this;
+
+ line1_ = address.line1_;
+ line2_ = address.line2_;
+ city_ = address.city_;
+ state_ = address.state_;
+ country_code_ = address.country_code_;
+ zip_code_ = address.zip_code_;
+ return *this;
+}
+
+void Address::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ supported_types->insert(ADDRESS_HOME_LINE1);
+ supported_types->insert(ADDRESS_HOME_LINE2);
+ supported_types->insert(ADDRESS_HOME_CITY);
+ supported_types->insert(ADDRESS_HOME_STATE);
+ supported_types->insert(ADDRESS_HOME_ZIP);
+ supported_types->insert(ADDRESS_HOME_COUNTRY);
+}
+
+string16 Address::GetRawInfo(AutofillFieldType type) const {
+ if (type == ADDRESS_HOME_LINE1)
+ return line1_;
+
+ if (type == ADDRESS_HOME_LINE2)
+ return line2_;
+
+ if (type == ADDRESS_HOME_CITY)
+ return city_;
+
+ if (type == ADDRESS_HOME_STATE)
+ return state_;
+
+ if (type == ADDRESS_HOME_ZIP)
+ return zip_code_;
+
+ if (type == ADDRESS_HOME_COUNTRY)
+ return Country();
+
+ return string16();
+}
+
+void Address::SetRawInfo(AutofillFieldType type, const string16& value) {
+ type = AutofillType::GetEquivalentFieldType(type);
+ if (type == ADDRESS_HOME_LINE1)
+ line1_ = value;
+ else if (type == ADDRESS_HOME_LINE2)
+ line2_ = value;
+ else if (type == ADDRESS_HOME_CITY)
+ city_ = value;
+ else if (type == ADDRESS_HOME_STATE)
+ state_ = value;
+ else if (type == ADDRESS_HOME_COUNTRY)
+ // TODO(isherman): When setting the country, it should only be possible to
+ // call this with a country code, which means we should be able to drop the
+ // call to GetCountryCode() below.
+ country_code_ =
+ AutofillCountry::GetCountryCode(value,
+ AutofillCountry::ApplicationLocale());
+ else if (type == ADDRESS_HOME_ZIP)
+ zip_code_ = value;
+ else
+ NOTREACHED();
+}
+
+void Address::GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const {
+ FormGroup::GetMatchingTypes(text, app_locale, matching_types);
+
+ // Check to see if the |text| canonicalized as a country name is a match.
+ std::string country_code = AutofillCountry::GetCountryCode(text, app_locale);
+ if (!country_code.empty() && country_code_ == country_code)
+ matching_types->insert(ADDRESS_HOME_COUNTRY);
+}
+
+string16 Address::Country() const {
+ if (country_code().empty())
+ return string16();
+
+ std::string app_locale = AutofillCountry::ApplicationLocale();
+ return AutofillCountry(country_code(), app_locale).name();
+}
diff --git a/components/autofill/browser/address.h b/components/autofill/browser/address.h
new file mode 100644
index 0000000..5a3127d
--- /dev/null
+++ b/components/autofill/browser/address.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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_BROWSER_ADDRESS_H_
+#define COMPONENTS_AUTOFILL_BROWSER_ADDRESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_group.h"
+
+// A form group that stores address information.
+class Address : public FormGroup {
+ public:
+ Address();
+ Address(const Address& address);
+ virtual ~Address();
+
+ Address& operator=(const Address& address);
+
+ // FormGroup:
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+ virtual void GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const OVERRIDE;
+
+ const std::string& country_code() const { return country_code_; }
+ void set_country_code(const std::string& country_code) {
+ country_code_ = country_code;
+ }
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ // Returns the localized country name corresponding to |country_code_|.
+ string16 Country() const;
+
+ // The address.
+ string16 line1_;
+ string16 line2_;
+ string16 city_;
+ string16 state_;
+ std::string country_code_;
+ string16 zip_code_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_ADDRESS_H_
diff --git a/components/autofill/browser/address_field.cc b/components/autofill/browser/address_field.cc
new file mode 100644
index 0000000..a8f614d
--- /dev/null
+++ b/components/autofill/browser/address_field.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2011 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/browser/address_field.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_regex_constants.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/field_types.h"
+#include "ui/base/l10n/l10n_util.h"
+
+FormField* AddressField::Parse(AutofillScanner* scanner) {
+ if (scanner->IsEnd())
+ return NULL;
+
+ scoped_ptr<AddressField> address_field(new AddressField);
+ const AutofillField* const initial_field = scanner->Cursor();
+ size_t saved_cursor = scanner->SaveCursor();
+
+ string16 attention_ignored = UTF8ToUTF16(autofill::kAttentionIgnoredRe);
+ string16 region_ignored = UTF8ToUTF16(autofill::kRegionIgnoredRe);
+
+ // Allow address fields to appear in any order.
+ size_t begin_trailing_non_labeled_fields = 0;
+ bool has_trailing_non_labeled_fields = false;
+ while (!scanner->IsEnd()) {
+ const size_t cursor = scanner->SaveCursor();
+ if (ParseAddressLines(scanner, address_field.get()) ||
+ ParseCity(scanner, address_field.get()) ||
+ ParseState(scanner, address_field.get()) ||
+ ParseZipCode(scanner, address_field.get()) ||
+ ParseCountry(scanner, address_field.get()) ||
+ ParseCompany(scanner, address_field.get())) {
+ has_trailing_non_labeled_fields = false;
+ continue;
+ } else if (ParseField(scanner, attention_ignored, NULL) ||
+ ParseField(scanner, region_ignored, NULL)) {
+ // We ignore the following:
+ // * Attention.
+ // * Province/Region/Other.
+ continue;
+ } else if (scanner->Cursor() != initial_field &&
+ ParseEmptyLabel(scanner, NULL)) {
+ // Ignore non-labeled fields within an address; the page
+ // MapQuest Driving Directions North America.html contains such a field.
+ // We only ignore such fields after we've parsed at least one other field;
+ // otherwise we'd effectively parse address fields before other field
+ // types after any non-labeled fields, and we want email address fields to
+ // have precedence since some pages contain fields labeled
+ // "Email address".
+ if (!has_trailing_non_labeled_fields) {
+ has_trailing_non_labeled_fields = true;
+ begin_trailing_non_labeled_fields = cursor;
+ }
+
+ continue;
+ } else {
+ // No field found.
+ break;
+ }
+ }
+
+ // If we have identified any address fields in this field then it should be
+ // added to the list of fields.
+ if (address_field->company_ != NULL ||
+ address_field->address1_ != NULL || address_field->address2_ != NULL ||
+ address_field->city_ != NULL || address_field->state_ != NULL ||
+ address_field->zip_ != NULL || address_field->zip4_ ||
+ address_field->country_ != NULL) {
+ // Don't slurp non-labeled fields at the end into the address.
+ if (has_trailing_non_labeled_fields)
+ scanner->RewindTo(begin_trailing_non_labeled_fields);
+
+ address_field->type_ = address_field->FindType();
+ return address_field.release();
+ }
+
+ scanner->RewindTo(saved_cursor);
+ return NULL;
+}
+
+AddressField::AddressType AddressField::FindType() const {
+ // First look at the field name, which itself will sometimes contain
+ // "bill" or "ship".
+ if (company_) {
+ string16 name = StringToLowerASCII(company_->name);
+ return AddressTypeFromText(name);
+ }
+ if (address1_) {
+ string16 name = StringToLowerASCII(address1_->name);
+ return AddressTypeFromText(name);
+ }
+ if (address2_) {
+ string16 name = StringToLowerASCII(address2_->name);
+ return AddressTypeFromText(name);
+ }
+ if (city_) {
+ string16 name = StringToLowerASCII(city_->name);
+ return AddressTypeFromText(name);
+ }
+ if (zip_) {
+ string16 name = StringToLowerASCII(zip_->name);
+ return AddressTypeFromText(name);
+ }
+ if (state_) {
+ string16 name = StringToLowerASCII(state_->name);
+ return AddressTypeFromText(name);
+ }
+ if (country_) {
+ string16 name = StringToLowerASCII(country_->name);
+ return AddressTypeFromText(name);
+ }
+
+ return kGenericAddress;
+}
+
+AddressField::AddressField()
+ : company_(NULL),
+ address1_(NULL),
+ address2_(NULL),
+ city_(NULL),
+ state_(NULL),
+ zip_(NULL),
+ zip4_(NULL),
+ country_(NULL),
+ type_(kGenericAddress) {
+}
+
+bool AddressField::ClassifyField(FieldTypeMap* map) const {
+ AutofillFieldType address_company;
+ AutofillFieldType address_line1;
+ AutofillFieldType address_line2;
+ AutofillFieldType address_city;
+ AutofillFieldType address_state;
+ AutofillFieldType address_zip;
+ AutofillFieldType address_country;
+
+ switch (type_) {
+ case kShippingAddress:
+ // Fall through. Autofill does not support shipping addresses.
+ case kGenericAddress:
+ address_company = COMPANY_NAME;
+ address_line1 = ADDRESS_HOME_LINE1;
+ address_line2 = ADDRESS_HOME_LINE2;
+ address_city = ADDRESS_HOME_CITY;
+ address_state = ADDRESS_HOME_STATE;
+ address_zip = ADDRESS_HOME_ZIP;
+ address_country = ADDRESS_HOME_COUNTRY;
+ break;
+
+ case kBillingAddress:
+ address_company = COMPANY_NAME;
+ address_line1 = ADDRESS_BILLING_LINE1;
+ address_line2 = ADDRESS_BILLING_LINE2;
+ address_city = ADDRESS_BILLING_CITY;
+ address_state = ADDRESS_BILLING_STATE;
+ address_zip = ADDRESS_BILLING_ZIP;
+ address_country = ADDRESS_BILLING_COUNTRY;
+ break;
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ bool ok = AddClassification(company_, address_company, map);
+ ok = ok && AddClassification(address1_, address_line1, map);
+ ok = ok && AddClassification(address2_, address_line2, map);
+ ok = ok && AddClassification(city_, address_city, map);
+ ok = ok && AddClassification(state_, address_state, map);
+ ok = ok && AddClassification(zip_, address_zip, map);
+ ok = ok && AddClassification(country_, address_country, map);
+ return ok;
+}
+
+// static
+bool AddressField::ParseCompany(AutofillScanner* scanner,
+ AddressField* address_field) {
+ if (address_field->company_ && !address_field->company_->IsEmpty())
+ return false;
+
+ return ParseField(scanner, UTF8ToUTF16(autofill::kCompanyRe),
+ &address_field->company_);
+}
+
+// static
+bool AddressField::ParseAddressLines(AutofillScanner* scanner,
+ AddressField* address_field) {
+ // We only match the string "address" in page text, not in element names,
+ // because sometimes every element in a group of address fields will have
+ // a name containing the string "address"; for example, on the page
+ // Kohl's - Register Billing Address.html the text element labeled "city"
+ // has the name "BILL_TO_ADDRESS<>city". We do match address labels
+ // such as "address1", which appear as element names on various pages (eg
+ // AmericanGirl-Registration.html, BloomingdalesBilling.html,
+ // EBay Registration Enter Information.html).
+ if (address_field->address1_)
+ return false;
+
+ string16 pattern = UTF8ToUTF16(autofill::kAddressLine1Re);
+ string16 label_pattern = UTF8ToUTF16(autofill::kAddressLine1LabelRe);
+
+ if (!ParseField(scanner, pattern, &address_field->address1_) &&
+ !ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT,
+ &address_field->address1_)) {
+ return false;
+ }
+
+ // Optionally parse more address lines, which may have empty labels.
+ // Some pages have 3 address lines (eg SharperImageModifyAccount.html)
+ // Some pages even have 4 address lines (e.g. uk/ShoesDirect2.html)!
+ pattern = UTF8ToUTF16(autofill::kAddressLine2Re);
+ label_pattern = UTF8ToUTF16(autofill::kAddressLine2LabelRe);
+ if (!ParseEmptyLabel(scanner, &address_field->address2_) &&
+ !ParseField(scanner, pattern, &address_field->address2_)) {
+ ParseFieldSpecifics(scanner, label_pattern, MATCH_LABEL | MATCH_TEXT,
+ &address_field->address2_);
+ }
+
+ // Try for a third line, which we will promptly discard.
+ if (address_field->address2_ != NULL) {
+ pattern = UTF8ToUTF16(autofill::kAddressLine3Re);
+ ParseField(scanner, pattern, NULL);
+ }
+
+ return true;
+}
+
+// static
+bool AddressField::ParseCountry(AutofillScanner* scanner,
+ AddressField* address_field) {
+ // Parse a country. The occasional page (e.g.
+ // Travelocity_New Member Information1.html) calls this a "location".
+ if (address_field->country_ && !address_field->country_->IsEmpty())
+ return false;
+
+ return ParseFieldSpecifics(scanner,
+ UTF8ToUTF16(autofill::kCountryRe),
+ MATCH_DEFAULT | MATCH_SELECT,
+ &address_field->country_);
+}
+
+// static
+bool AddressField::ParseZipCode(AutofillScanner* scanner,
+ AddressField* address_field) {
+ // Parse a zip code. On some UK pages (e.g. The China Shop2.html) this
+ // is called a "post code".
+ //
+ // HACK: Just for the MapQuest driving directions page we match the
+ // exact name "1z", which MapQuest uses to label its zip code field.
+ // Hopefully before long we'll be smart enough to find the zip code
+ // on that page automatically.
+ if (address_field->zip_)
+ return false;
+
+ string16 pattern = UTF8ToUTF16(autofill::kZipCodeRe);
+ if (!ParseField(scanner, pattern, &address_field->zip_))
+ return false;
+
+ address_field->type_ = kGenericAddress;
+ // Look for a zip+4, whose field name will also often contain
+ // the substring "zip".
+ ParseField(scanner,
+ UTF8ToUTF16(autofill::kZip4Re),
+ &address_field->zip4_);
+
+ return true;
+}
+
+// static
+bool AddressField::ParseCity(AutofillScanner* scanner,
+ AddressField* address_field) {
+ // Parse a city name. Some UK pages (e.g. The China Shop2.html) use
+ // the term "town".
+ if (address_field->city_)
+ return false;
+
+ // Select fields are allowed here. This occurs on top-100 site rediff.com.
+ return ParseFieldSpecifics(scanner,
+ UTF8ToUTF16(autofill::kCityRe),
+ MATCH_DEFAULT | MATCH_SELECT,
+ &address_field->city_);
+}
+
+// static
+bool AddressField::ParseState(AutofillScanner* scanner,
+ AddressField* address_field) {
+ if (address_field->state_)
+ return false;
+
+ return ParseFieldSpecifics(scanner,
+ UTF8ToUTF16(autofill::kStateRe),
+ MATCH_DEFAULT | MATCH_SELECT,
+ &address_field->state_);
+}
+
+AddressField::AddressType AddressField::AddressTypeFromText(
+ const string16 &text) {
+ size_t same_as = text.find(UTF8ToUTF16(autofill::kAddressTypeSameAsRe));
+ size_t use_shipping = text.find(UTF8ToUTF16(autofill::kAddressTypeUseMyRe));
+ if (same_as != string16::npos || use_shipping != string16::npos)
+ // This text could be a checkbox label such as "same as my billing
+ // address" or "use my shipping address".
+ // ++ It would help if we generally skipped all text that appears
+ // after a check box.
+ return kGenericAddress;
+
+ // Not all pages say "billing address" and "shipping address" explicitly;
+ // for example, Craft Catalog1.html has "Bill-to Address" and
+ // "Ship-to Address".
+ size_t bill = text.rfind(UTF8ToUTF16(autofill::kBillingDesignatorRe));
+ size_t ship = text.rfind(UTF8ToUTF16(autofill::kShippingDesignatorRe));
+
+ if (bill == string16::npos && ship == string16::npos)
+ return kGenericAddress;
+
+ if (bill != string16::npos && ship == string16::npos)
+ return kBillingAddress;
+
+ if (bill == string16::npos && ship != string16::npos)
+ return kShippingAddress;
+
+ if (bill > ship)
+ return kBillingAddress;
+
+ return kShippingAddress;
+}
diff --git a/components/autofill/browser/address_field.h b/components/autofill/browser/address_field.h
new file mode 100644
index 0000000..debdf53
--- /dev/null
+++ b/components/autofill/browser/address_field.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 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_BROWSER_ADDRESS_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_ADDRESS_FIELD_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/form_field.h"
+
+class AutofillField;
+class AutofillScanner;
+
+class AddressField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ enum AddressType {
+ kGenericAddress = 0,
+ kBillingAddress,
+ kShippingAddress
+ };
+
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseOneLineAddress);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseOneLineAddressBilling);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseOneLineAddressShipping);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseTwoLineAddress);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseThreeLineAddress);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseCity);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseState);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseZip);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseStateAndZipOneLabel);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseCountry);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseTwoLineAddressMissingLabel);
+ FRIEND_TEST_ALL_PREFIXES(AddressFieldTest, ParseCompany);
+
+ AddressField();
+
+ static bool ParseCompany(AutofillScanner* scanner,
+ AddressField* address_field);
+ static bool ParseAddressLines(AutofillScanner* scanner,
+ AddressField* address_field);
+ static bool ParseCountry(AutofillScanner* scanner,
+ AddressField* address_field);
+ static bool ParseZipCode(AutofillScanner* scanner,
+ AddressField* address_field);
+ static bool ParseCity(AutofillScanner* scanner,
+ AddressField* address_field);
+ static bool ParseState(AutofillScanner* scanner,
+ AddressField* address_field);
+
+ // Looks for an address type in the given text, which the caller must
+ // convert to lowercase.
+ static AddressType AddressTypeFromText(const string16& text);
+
+ // Tries to determine the billing/shipping type of this address.
+ AddressType FindType() const;
+
+ const AutofillField* company_; // optional
+ const AutofillField* address1_;
+ const AutofillField* address2_; // optional
+ const AutofillField* city_;
+ const AutofillField* state_; // optional
+ const AutofillField* zip_;
+ const AutofillField* zip4_; // optional ZIP+4; we don't fill this yet
+ const AutofillField* country_; // optional
+
+ AddressType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(AddressField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_ADDRESS_FIELD_H_
diff --git a/components/autofill/browser/address_field_unittest.cc b/components/autofill/browser/address_field_unittest.cc
new file mode 100644
index 0000000..2798f28
--- /dev/null
+++ b/components/autofill/browser/address_field_unittest.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/address_field.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class AddressFieldTest : public testing::Test {
+ public:
+ AddressFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<AddressField> field_;
+ FieldTypeMap field_type_map_;
+
+ // Downcast for tests.
+ static AddressField* Parse(AutofillScanner* scanner) {
+ return static_cast<AddressField*>(AddressField::Parse(scanner));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AddressFieldTest);
+};
+
+TEST_F(AddressFieldTest, Empty) {
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<AddressField*>(NULL), field_.get());
+}
+
+TEST_F(AddressFieldTest, NonParse) {
+ list_.push_back(new AutofillField);
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<AddressField*>(NULL), field_.get());
+}
+
+TEST_F(AddressFieldTest, ParseOneLineAddress) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, field_type_map_[ASCIIToUTF16("addr1")]);
+}
+
+TEST_F(AddressFieldTest, ParseOneLineAddressBilling) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("billingAddress");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kBillingAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_BILLING_LINE1, field_type_map_[ASCIIToUTF16("addr1")]);
+}
+
+TEST_F(AddressFieldTest, ParseOneLineAddressShipping) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("shippingAddress");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kShippingAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, field_type_map_[ASCIIToUTF16("addr1")]);
+}
+
+TEST_F(AddressFieldTest, ParseTwoLineAddress) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+
+ field.label = string16();
+ field.name = string16();
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, field_type_map_[ASCIIToUTF16("addr1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr2")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE2, field_type_map_[ASCIIToUTF16("addr2")]);
+}
+
+TEST_F(AddressFieldTest, ParseThreeLineAddress) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address Line1");
+ field.name = ASCIIToUTF16("Address1");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+
+ field.label = ASCIIToUTF16("Address Line2");
+ field.name = ASCIIToUTF16("Address2");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2")));
+
+ field.label = ASCIIToUTF16("Address Line3");
+ field.name = ASCIIToUTF16("Address3");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, field_type_map_[ASCIIToUTF16("addr1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr2")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE2, field_type_map_[ASCIIToUTF16("addr2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr3")) == field_type_map_.end());
+}
+
+TEST_F(AddressFieldTest, ParseCity) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("city1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("city1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_CITY, field_type_map_[ASCIIToUTF16("city1")]);
+}
+
+TEST_F(AddressFieldTest, ParseState) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("state1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("state1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_STATE, field_type_map_[ASCIIToUTF16("state1")]);
+}
+
+TEST_F(AddressFieldTest, ParseZip) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Zip");
+ field.name = ASCIIToUTF16("zip");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("zip1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("zip1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_ZIP, field_type_map_[ASCIIToUTF16("zip1")]);
+}
+
+TEST_F(AddressFieldTest, ParseStateAndZipOneLabel) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("State/Province, Zip/Postal Code");
+ field.name = ASCIIToUTF16("state");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("state")));
+
+ field.label = ASCIIToUTF16("State/Province, Zip/Postal Code");
+ field.name = ASCIIToUTF16("zip");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("zip")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("state")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_STATE, field_type_map_[ASCIIToUTF16("state")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("zip")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_ZIP, field_type_map_[ASCIIToUTF16("zip")]);
+}
+
+TEST_F(AddressFieldTest, ParseCountry) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("country1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("country1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, field_type_map_[ASCIIToUTF16("country1")]);
+}
+
+TEST_F(AddressFieldTest, ParseTwoLineAddressMissingLabel) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("bogus");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("addr2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr1")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE1, field_type_map_[ASCIIToUTF16("addr1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("addr2")) != field_type_map_.end());
+ EXPECT_EQ(ADDRESS_HOME_LINE2, field_type_map_[ASCIIToUTF16("addr2")]);
+}
+
+TEST_F(AddressFieldTest, ParseCompany) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Company");
+ field.name = ASCIIToUTF16("company");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("company1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<AddressField*>(NULL), field_.get());
+ EXPECT_EQ(AddressField::kGenericAddress, field_->FindType());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("company1")) != field_type_map_.end());
+ EXPECT_EQ(COMPANY_NAME, field_type_map_[ASCIIToUTF16("company1")]);
+}
diff --git a/components/autofill/browser/address_unittest.cc b/components/autofill/browser/address_unittest.cc
new file mode 100644
index 0000000..0925146
--- /dev/null
+++ b/components/autofill/browser/address_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2011 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 <string>
+
+#include "base/message_loop.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/address.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+class AddressTest : public testing::Test {
+ public:
+ // In order to access the application locale -- which the tested functions do
+ // internally -- this test must run on the UI thread.
+ AddressTest() : ui_thread_(BrowserThread::UI, &message_loop_) {}
+
+ private:
+ MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(AddressTest);
+};
+
+// Test that the getters and setters for country code are working.
+TEST_F(AddressTest, CountryCode) {
+ Address address;
+ EXPECT_EQ(std::string(), address.country_code());
+
+ address.set_country_code("US");
+ EXPECT_EQ("US", address.country_code());
+
+ address.set_country_code("CA");
+ EXPECT_EQ("CA", address.country_code());
+}
+
+// Test that country codes are properly decoded as country names.
+TEST_F(AddressTest, GetCountry) {
+ Address address;
+ EXPECT_EQ(std::string(), address.country_code());
+
+ // Make sure that nothing breaks when the country code is missing.
+ string16 country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ(string16(), country);
+
+ address.set_country_code("US");
+ country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+
+ address.set_country_code("CA");
+ country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ(ASCIIToUTF16("Canada"), country);
+}
+
+// Test that we properly detect country codes appropriate for each country.
+TEST_F(AddressTest, SetCountry) {
+ Address address;
+ EXPECT_EQ(std::string(), address.country_code());
+
+ // Test basic conversion.
+ address.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("United States"), "en-US");
+ string16 country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ("US", address.country_code());
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+
+ // Test basic synonym detection.
+ address.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("USA"), "en-US");
+ country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ("US", address.country_code());
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+
+ // Test case-insensitivity.
+ address.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("canADA"), "en-US");
+ country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ("CA", address.country_code());
+ EXPECT_EQ(ASCIIToUTF16("Canada"), country);
+
+ // Test country code detection.
+ address.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("JP"), "en-US");
+ country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ("JP", address.country_code());
+ EXPECT_EQ(ASCIIToUTF16("Japan"), country);
+
+ // Test that we ignore unknown countries.
+ address.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("Unknown"), "en-US");
+ country = address.GetInfo(ADDRESS_HOME_COUNTRY, "en-US");
+ EXPECT_EQ(std::string(), address.country_code());
+ EXPECT_EQ(string16(), country);
+}
+
+// Test that we properly match typed values to stored country data.
+TEST_F(AddressTest, IsCountry) {
+ Address address;
+ address.set_country_code("US");
+
+ const char* const kValidMatches[] = {
+ "United States",
+ "USA",
+ "US",
+ "United states",
+ "us"
+ };
+ for (size_t i = 0; i < arraysize(kValidMatches); ++i) {
+ SCOPED_TRACE(kValidMatches[i]);
+ FieldTypeSet matching_types;
+ address.GetMatchingTypes(ASCIIToUTF16(kValidMatches[i]), "US",
+ &matching_types);
+ ASSERT_EQ(1U, matching_types.size());
+ EXPECT_EQ(ADDRESS_HOME_COUNTRY, *matching_types.begin());
+ }
+
+ const char* const kInvalidMatches[] = {
+ "United",
+ "Garbage"
+ };
+ for (size_t i = 0; i < arraysize(kInvalidMatches); ++i) {
+ FieldTypeSet matching_types;
+ address.GetMatchingTypes(ASCIIToUTF16(kInvalidMatches[i]), "US",
+ &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ }
+
+ // Make sure that garbage values don't match when the country code is empty.
+ address.set_country_code("");
+ EXPECT_EQ(std::string(), address.country_code());
+ FieldTypeSet matching_types;
+ address.GetMatchingTypes(ASCIIToUTF16("Garbage"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+}
diff --git a/components/autofill/browser/autocheckout/whitelist_manager.cc b/components/autofill/browser/autocheckout/whitelist_manager.cc
new file mode 100644
index 0000000..410ef7d
--- /dev/null
+++ b/components/autofill/browser/autocheckout/whitelist_manager.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2013 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/browser/autocheckout/whitelist_manager.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/strings/string_split.h"
+#include "components/autofill/common/autofill_switches.h"
+#include "content/public/browser/browser_context.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace {
+
+// Back off in seconds after each whitelist download is attempted.
+const int kDownloadIntervalSeconds = 86400; // 1 day
+
+// The delay in seconds after startup before download whitelist. This helps
+// to reduce contention at startup time.
+const int kInitialDownloadDelaySeconds = 3;
+
+const char kWhitelistUrl[] =
+ "http://www.gstatic.com/commerce/autocheckout/whitelist.csv";
+
+const char kWhiteListKeyName[] = "autocheckout_whitelist_manager";
+
+} // namespace
+
+
+namespace autofill {
+namespace autocheckout {
+
+// static
+WhitelistManager* WhitelistManager::GetForBrowserContext(
+ content::BrowserContext* context) {
+ WhitelistManager* whitelist_manager = static_cast<WhitelistManager*>(
+ context->GetUserData(kWhiteListKeyName));
+ if (!whitelist_manager) {
+ whitelist_manager =
+ new WhitelistManager(context->GetRequestContext());
+ whitelist_manager->ScheduleDownload(kInitialDownloadDelaySeconds);
+ context->SetUserData(kWhiteListKeyName, whitelist_manager);
+ }
+ return whitelist_manager;
+}
+
+WhitelistManager::WhitelistManager(
+ net::URLRequestContextGetter* context_getter)
+ : callback_is_pending_(false),
+ context_getter_(context_getter),
+ experimental_form_filling_enabled_(
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalFormFilling)){
+ DCHECK(context_getter);
+}
+
+WhitelistManager::~WhitelistManager() {}
+
+void WhitelistManager::ScheduleDownload(size_t interval_seconds) {
+ if (!experimental_form_filling_enabled_) {
+ // The feature is not enabled: do not do the request.
+ return;
+ }
+ if (download_timer_.IsRunning() || callback_is_pending_) {
+ // A download activity is already scheduled or happening.
+ return;
+ }
+ StartDownloadTimer(interval_seconds);
+}
+
+void WhitelistManager::StartDownloadTimer(size_t interval_seconds) {
+ download_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(interval_seconds),
+ this,
+ &WhitelistManager::TriggerDownload);
+}
+
+void WhitelistManager::TriggerDownload() {
+ callback_is_pending_ = true;
+
+ request_.reset(net::URLFetcher::Create(
+ 0, GURL(kWhitelistUrl), net::URLFetcher::GET, this));
+ request_->SetRequestContext(context_getter_);
+ request_->SetAutomaticallyRetryOn5xx(false);
+ request_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES);
+ request_->Start();
+}
+
+void WhitelistManager::StopDownloadTimer() {
+ download_timer_.Stop();
+ callback_is_pending_ = false;
+}
+
+void WhitelistManager::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ DCHECK(callback_is_pending_);
+ callback_is_pending_ = false;
+ scoped_ptr<net::URLFetcher> old_request = request_.Pass();
+ DCHECK_EQ(source, old_request.get());
+
+ if (source->GetResponseCode() == net::HTTP_OK) {
+ std::string data;
+ source->GetResponseAsString(&data);
+ BuildWhitelist(data);
+ }
+
+ ScheduleDownload(kDownloadIntervalSeconds);
+}
+
+std::string WhitelistManager::GetMatchedURLPrefix(const GURL& url) const {
+ if (!experimental_form_filling_enabled_ || url.is_empty())
+ return std::string();
+
+ for (std::vector<std::string>::const_iterator it = url_prefixes_.begin();
+ it != url_prefixes_.end(); ++it) {
+ // This is only for ~20 sites initially, liner search is sufficient.
+ // TODO(benquan): Look for optimization options when we support
+ // more sites.
+ if (StartsWithASCII(url.spec(), *it, true)) {
+ DVLOG(1) << "WhitelistManager matched URLPrefix: " << *it;
+ return *it;
+ }
+ }
+ return std::string();
+}
+
+void WhitelistManager::BuildWhitelist(const std::string& data) {
+ std::vector<std::string> new_url_prefixes;
+
+ std::vector<std::string> lines;
+ base::SplitString(data, '\n', &lines);
+
+ for (std::vector<std::string>::const_iterator line = lines.begin();
+ line != lines.end(); ++line) {
+ if (!line->empty()) {
+ std::vector<std::string> fields;
+ base::SplitString(*line, ',', &fields);
+ // Currently we have only one column in the whitelist file, if we decide
+ // to add more metadata as additional columns, previous versions of
+ // Chrome can ignore them and continue to work.
+ if (!fields[0].empty())
+ new_url_prefixes.push_back(fields[0]);
+ }
+ }
+ url_prefixes_ = new_url_prefixes;
+}
+
+} // namespace autocheckout
+} // namespace autofill
+
diff --git a/components/autofill/browser/autocheckout/whitelist_manager.h b/components/autofill/browser/autocheckout/whitelist_manager.h
new file mode 100644
index 0000000..6e28941
--- /dev/null
+++ b/components/autofill/browser/autocheckout/whitelist_manager.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2013 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_BROWSER_AUTOCHECKOUT_WHITELIST_MANAGER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_WHITELIST_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/supports_user_data.h"
+#include "base/timer.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+class GURL;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace autofill {
+namespace autocheckout {
+
+// Downloads and caches the list of URL prefixes whitelisted for use with
+// Autocheckout.
+class WhitelistManager : public net::URLFetcherDelegate,
+ public base::SupportsUserData::Data {
+ public:
+ static WhitelistManager* GetForBrowserContext(
+ content::BrowserContext* context);
+
+ // Matches the url with whitelist and return the matched url prefix.
+ // Returns empty string when it is not matched.
+ std::string GetMatchedURLPrefix(const GURL& url) const;
+
+ protected:
+ explicit WhitelistManager(net::URLRequestContextGetter* context_getter);
+ virtual ~WhitelistManager();
+
+ // Schedules a future call to TriggerDownload if one isn't already pending.
+ virtual void ScheduleDownload(size_t interval_seconds);
+
+ // Start the download timer. It is called by ScheduleDownload(), and exposed
+ // as a separate method for mocking out in tests.
+ virtual void StartDownloadTimer(size_t interval_seconds);
+
+ // Timer callback indicating it's time to download whitelist from server.
+ void TriggerDownload();
+
+ // Used by tests only.
+ void StopDownloadTimer();
+
+ const std::vector<std::string>& url_prefixes() const {
+ return url_prefixes_;
+ }
+
+ private:
+ // Implements net::URLFetcherDelegate.
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // Parse whitelist data and build whitelist.
+ void BuildWhitelist(const std::string& data);
+
+ // A list of whitelisted url prefixes.
+ std::vector<std::string> url_prefixes_;
+
+ base::OneShotTimer<WhitelistManager> download_timer_;
+
+ // Indicates that the last triggered download hasn't resolved yet.
+ bool callback_is_pending_;
+
+ // The context for the request.
+ net::URLRequestContextGetter* const context_getter_; // WEAK
+
+ // State of the kEnableExperimentalFormFilling flag.
+ const bool experimental_form_filling_enabled_;
+
+ // The request object.
+ scoped_ptr<net::URLFetcher> request_;
+
+ DISALLOW_COPY_AND_ASSIGN(WhitelistManager);
+};
+
+} // namespace autocheckout
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_WHITELIST_MANAGER_H_
+
diff --git a/components/autofill/browser/autocheckout/whitelist_manager_unittest.cc b/components/autofill/browser/autocheckout/whitelist_manager_unittest.cc
new file mode 100644
index 0000000..9d23c13
--- /dev/null
+++ b/components/autofill/browser/autocheckout/whitelist_manager_unittest.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autocheckout/whitelist_manager.h"
+#include "components/autofill/common/autofill_switches.h"
+#include "content/public/test/test_browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const size_t kTestDownloadInterval = 3; // 3 seconds
+
+const char kDownloadWhitelistResponse[] =
+ "https://www.merchant1.com/checkout/\n"
+ "https://cart.merchant2.com/";
+
+} // namespace
+
+namespace autofill {
+namespace autocheckout {
+
+class WhitelistManagerTest;
+
+class TestWhitelistManager : public WhitelistManager {
+ public:
+ explicit TestWhitelistManager(net::URLRequestContextGetter* context_getter)
+ : WhitelistManager(context_getter),
+ did_start_download_timer_(false) {}
+
+ virtual void ScheduleDownload(size_t interval_seconds) OVERRIDE {
+ did_start_download_timer_ = false;
+ return WhitelistManager::ScheduleDownload(interval_seconds);
+ }
+
+ virtual void StartDownloadTimer(size_t interval_seconds) OVERRIDE {
+ WhitelistManager::StartDownloadTimer(interval_seconds);
+ did_start_download_timer_ = true;
+ }
+
+ bool did_start_download_timer() const {
+ return did_start_download_timer_;
+ }
+
+ void TriggerDownload() {
+ WhitelistManager::TriggerDownload();
+ }
+
+ void StopDownloadTimer() {
+ WhitelistManager::StopDownloadTimer();
+ }
+
+ const std::vector<std::string>& url_prefixes() const {
+ return WhitelistManager::url_prefixes();
+ }
+
+ private:
+ bool did_start_download_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWhitelistManager);
+};
+
+class WhitelistManagerTest : public testing::Test {
+ public:
+ WhitelistManagerTest() : io_thread_(content::BrowserThread::IO) {}
+
+ virtual void SetUp() {
+ io_thread_.StartIOThread();
+ profile_.CreateRequestContext();
+ }
+
+ virtual void TearDown() {
+ profile_.ResetRequestContext();
+ io_thread_.Stop();
+ }
+
+ protected:
+ void CreateWhitelistManager() {
+ if (!whitelist_manager_.get()) {
+ whitelist_manager_.reset(new TestWhitelistManager(
+ profile_.GetRequestContext()));
+ }
+ }
+
+ void DownloadWhitelist(int response_code, const std::string& response) {
+ // Create and register factory.
+ net::TestURLFetcherFactory factory;
+
+ CreateWhitelistManager();
+
+ whitelist_manager_->TriggerDownload();
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+
+ void ResetBackOff() {
+ whitelist_manager_->StopDownloadTimer();
+ }
+
+ const std::vector<std::string>& get_url_prefixes() const {
+ return whitelist_manager_->url_prefixes();
+ }
+
+ protected:
+ TestingProfile profile_;
+ scoped_ptr<TestWhitelistManager> whitelist_manager_;
+
+ private:
+ MessageLoopForIO message_loop_;
+ // The profile's request context must be released on the IO thread.
+ content::TestBrowserThread io_thread_;
+};
+
+TEST_F(WhitelistManagerTest, DownloadWhitelist) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ ASSERT_EQ(2U, get_url_prefixes().size());
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ get_url_prefixes()[0]);
+ EXPECT_EQ("https://cart.merchant2.com/",
+ get_url_prefixes()[1]);
+}
+
+TEST_F(WhitelistManagerTest, DoNotDownloadWhitelistWhenSwitchIsOff) {
+ CreateWhitelistManager();
+ whitelist_manager_->ScheduleDownload(kTestDownloadInterval);
+ EXPECT_FALSE(whitelist_manager_->did_start_download_timer());
+}
+
+TEST_F(WhitelistManagerTest, DoNotDownloadWhitelistWhenBackOff) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ CreateWhitelistManager();
+ // First attempt should schedule a download.
+ whitelist_manager_->ScheduleDownload(kTestDownloadInterval);
+ EXPECT_TRUE(whitelist_manager_->did_start_download_timer());
+ // Second attempt should NOT schedule a download while there is already one.
+ whitelist_manager_->ScheduleDownload(kTestDownloadInterval);
+ EXPECT_FALSE(whitelist_manager_->did_start_download_timer());
+ // It should schedule a new download when not in backoff mode.
+ ResetBackOff();
+ whitelist_manager_->ScheduleDownload(kTestDownloadInterval);
+ EXPECT_TRUE(whitelist_manager_->did_start_download_timer());
+}
+
+TEST_F(WhitelistManagerTest, DownloadWhitelistFailed) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ DownloadWhitelist(net::HTTP_INTERNAL_SERVER_ERROR,
+ kDownloadWhitelistResponse);
+ EXPECT_EQ(0U, get_url_prefixes().size());
+
+ ResetBackOff();
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, get_url_prefixes().size());
+
+ ResetBackOff();
+ DownloadWhitelist(net::HTTP_INTERNAL_SERVER_ERROR,
+ kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, get_url_prefixes().size());
+}
+
+TEST_F(WhitelistManagerTest, GetMatchedURLPrefix) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, get_url_prefixes().size());
+
+ // Empty url.
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(GURL(std::string())));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(GURL()));
+
+ // Positive tests.
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/checkout/")));
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/checkout/Shipping")));
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/checkout/?a=b&c=d")));
+ EXPECT_EQ("https://cart.merchant2.com/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://cart.merchant2.com/")));
+ EXPECT_EQ("https://cart.merchant2.com/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://cart.merchant2.com/ShippingInfo")));
+ EXPECT_EQ("https://cart.merchant2.com/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://cart.merchant2.com/ShippingInfo?a=b&c=d")));
+
+ // Negative tests.
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/checkout")));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/")));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/Building")));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant2.com/cart")));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("a random string")));
+
+ // Test different cases in schema, host and path.
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("http://www.merchant1.com/checkout/")));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("www.merchant1.com/checkout/")));
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.Merchant1.com/checkout/")));
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/CheckOut/")));
+}
+
+} // namespace autocheckout
+} // namespace autofill
+
diff --git a/components/autofill/browser/autocheckout_manager.cc b/components/autofill/browser/autocheckout_manager.cc
new file mode 100644
index 0000000..d9724cf
--- /dev/null
+++ b/components/autofill/browser/autocheckout_manager.cc
@@ -0,0 +1,285 @@
+// Copyright (c) 2013 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/browser/autocheckout_manager.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/common/autocheckout_status.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/web_element_descriptor.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/ssl_status.h"
+#include "googleurl/src/gurl.h"
+#include "ui/gfx/rect.h"
+
+using content::RenderViewHost;
+using content::SSLStatus;
+using content::WebContents;
+
+namespace {
+
+// Build FormFieldData based on the supplied |autocomplete_attribute|. Will
+// fill rest of properties with default values.
+FormFieldData BuildField(const std::string& autocomplete_attribute) {
+ FormFieldData field;
+ field.name = string16();
+ field.value = string16();
+ field.autocomplete_attribute = autocomplete_attribute;
+ field.form_control_type = "text";
+ return field;
+}
+
+// Build Autocheckout specific form data to be consumed by
+// AutofillDialogController to show the Autocheckout specific UI.
+FormData BuildAutocheckoutFormData() {
+ FormData formdata;
+ formdata.fields.push_back(BuildField("name"));
+ formdata.fields.push_back(BuildField("tel"));
+ formdata.fields.push_back(BuildField("email"));
+ formdata.fields.push_back(BuildField("cc-name"));
+ formdata.fields.push_back(BuildField("cc-number"));
+ formdata.fields.push_back(BuildField("cc-exp-month"));
+ formdata.fields.push_back(BuildField("cc-exp-year"));
+ formdata.fields.push_back(BuildField("cc-csc"));
+ formdata.fields.push_back(BuildField("billing street-address"));
+ formdata.fields.push_back(BuildField("billing locality"));
+ formdata.fields.push_back(BuildField("billing region"));
+ formdata.fields.push_back(BuildField("billing country"));
+ formdata.fields.push_back(BuildField("billing postal-code"));
+ formdata.fields.push_back(BuildField("shipping street-address"));
+ formdata.fields.push_back(BuildField("shipping locality"));
+ formdata.fields.push_back(BuildField("shipping region"));
+ formdata.fields.push_back(BuildField("shipping country"));
+ formdata.fields.push_back(BuildField("shipping postal-code"));
+ return formdata;
+}
+
+} // namespace
+
+namespace autofill {
+
+AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager)
+ : autofill_manager_(autofill_manager),
+ autocheckout_bubble_shown_(false),
+ in_autocheckout_flow_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+}
+
+AutocheckoutManager::~AutocheckoutManager() {
+}
+
+void AutocheckoutManager::FillForms() {
+ // |page_meta_data_| should have been set by OnLoadedPageMetaData.
+ DCHECK(page_meta_data_);
+
+ // Fill the forms on the page with data given by user.
+ std::vector<FormData> filled_forms;
+ const std::vector<FormStructure*>& form_structures =
+ autofill_manager_->GetFormStructures();
+ for (std::vector<FormStructure*>::const_iterator iter =
+ form_structures.begin(); iter != form_structures.end(); ++iter) {
+ const FormStructure& form_structure = **iter;
+ FormData form = form_structure.ToFormData();
+ DCHECK_EQ(form_structure.field_count(), form.fields.size());
+
+ for (size_t i = 0; i < form_structure.field_count(); ++i) {
+ const AutofillField* field = form_structure.field(i);
+ SetValue(*field, &form.fields[i]);
+ }
+
+ filled_forms.push_back(form);
+ }
+
+ // Send filled forms along with proceed descriptor to renderer.
+ RenderViewHost* host =
+ autofill_manager_->GetWebContents()->GetRenderViewHost();
+ if (!host)
+ return;
+
+ host->Send(new AutofillMsg_FillFormsAndClick(
+ host->GetRoutingID(),
+ filled_forms,
+ page_meta_data_->IsEndOfAutofillableFlow() ?
+ WebElementDescriptor() :
+ *page_meta_data_->proceed_element_descriptor));
+}
+
+void AutocheckoutManager::OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData> page_meta_data) {
+ scoped_ptr<AutocheckoutPageMetaData> old_meta_data =
+ page_meta_data_.Pass();
+ page_meta_data_ = page_meta_data.Pass();
+
+ // On the first page of an Autocheckout flow, when this function is called the
+ // user won't have opted into the flow yet.
+ if (!in_autocheckout_flow_)
+ return;
+
+ // Missing Autofill server results.
+ if (!page_meta_data_) {
+ in_autocheckout_flow_ = false;
+ } else if (page_meta_data_->IsStartOfAutofillableFlow()) {
+ // Not possible unless Autocheckout failed to proceed.
+ in_autocheckout_flow_ = false;
+ } else if (!page_meta_data_->IsInAutofillableFlow()) {
+ // Missing Autocheckout meta data in the Autofill server results.
+ in_autocheckout_flow_ = false;
+ } else if (!page_meta_data_->proceed_element_descriptor &&
+ !page_meta_data_->IsEndOfAutofillableFlow()) {
+ // Missing Autocheckout proceed data in meta data in the Autofill server
+ // results.
+ in_autocheckout_flow_ = false;
+ } else if (page_meta_data_->current_page_number <=
+ old_meta_data->current_page_number) {
+ // Not possible unless Autocheckout failed to proceed.
+ in_autocheckout_flow_ = false;
+ }
+
+ // Encountered an error during the Autocheckout flow.
+ if (!in_autocheckout_flow_) {
+ // TODO(ahutter): SendAutocheckoutStatus of the error.
+ autofill_manager_->delegate()->OnAutocheckoutError();
+ return;
+ }
+
+ // Add 1.0 since page numbers are 0-indexed.
+ autofill_manager_->delegate()->UpdateProgressBar(
+ (1.0 + page_meta_data_->current_page_number) /
+ page_meta_data_->total_pages);
+ FillForms();
+ // If the current page is the last page in the flow, close the dialog.
+ if (page_meta_data_->IsEndOfAutofillableFlow()) {
+ // TODO(ahutter): SendAutocheckoutStatus of SUCCESS.
+ autofill_manager_->delegate()->HideRequestAutocompleteDialog();
+ in_autocheckout_flow_ = false;
+ }
+}
+
+void AutocheckoutManager::OnFormsSeen() {
+ autocheckout_bubble_shown_ = false;
+}
+
+bool AutocheckoutManager::MaybeShowAutocheckoutBubble(
+ const GURL& frame_url,
+ const content::SSLStatus& ssl_status,
+ const gfx::NativeView& native_view,
+ const gfx::RectF& bounding_box) {
+ if (autocheckout_bubble_shown_)
+ return false;
+
+ base::Closure callback = base::Bind(
+ &AutocheckoutManager::ShowAutocheckoutDialog,
+ weak_ptr_factory_.GetWeakPtr(),
+ frame_url,
+ ssl_status);
+ autofill_manager_->delegate()->ShowAutocheckoutBubble(
+ bounding_box,
+ native_view,
+ callback);
+ autocheckout_bubble_shown_ = true;
+ return true;
+}
+
+void AutocheckoutManager::ShowAutocheckoutDialog(
+ const GURL& frame_url,
+ const SSLStatus& ssl_status) {
+ base::Callback<void(const FormStructure*)> callback =
+ base::Bind(&AutocheckoutManager::ReturnAutocheckoutData,
+ weak_ptr_factory_.GetWeakPtr());
+ autofill_manager_->ShowRequestAutocompleteDialog(
+ BuildAutocheckoutFormData(), frame_url, ssl_status,
+ DIALOG_TYPE_AUTOCHECKOUT, callback);
+}
+
+bool AutocheckoutManager::IsStartOfAutofillableFlow() const {
+ return page_meta_data_ && page_meta_data_->IsStartOfAutofillableFlow();
+}
+
+bool AutocheckoutManager::IsInAutofillableFlow() const {
+ return page_meta_data_ && page_meta_data_->IsInAutofillableFlow();
+}
+
+void AutocheckoutManager::ReturnAutocheckoutData(const FormStructure* result) {
+ if (!result)
+ return;
+
+ in_autocheckout_flow_ = true;
+
+ profile_.reset(new AutofillProfile());
+ credit_card_.reset(new CreditCard());
+
+ for (size_t i = 0; i < result->field_count(); ++i) {
+ AutofillFieldType type = result->field(i)->type();
+ if (type == CREDIT_CARD_VERIFICATION_CODE) {
+ // TODO(ramankk): CVV is not handled by CreditCard, not sure how to
+ // handle it yet.
+ cvv_ = result->field(i)->value;
+ continue;
+ }
+ if (AutofillType(type).group() == AutofillType::CREDIT_CARD) {
+ credit_card_->SetRawInfo(result->field(i)->type(),
+ result->field(i)->value);
+ } else {
+ profile_->SetRawInfo(result->field(i)->type(), result->field(i)->value);
+ }
+ }
+
+ // Add 1.0 since page numbers are 0-indexed.
+ autofill_manager_->delegate()->UpdateProgressBar(
+ (1.0 + page_meta_data_->current_page_number) /
+ page_meta_data_->total_pages);
+ FillForms();
+}
+
+void AutocheckoutManager::SetValue(const AutofillField& field,
+ FormFieldData* field_to_fill) {
+ AutofillFieldType type = field.type();
+
+ if (type == FIELD_WITH_DEFAULT_VALUE) {
+ DCHECK(field.is_checkable);
+ // For a form with radio buttons, like:
+ // <form>
+ // <input type="radio" name="sex" value="male">Male<br>
+ // <input type="radio" name="sex" value="female">Female
+ // </form>
+ // If the default value specified at the server is "female", then
+ // Autofill server responds back with following field mappings
+ // (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
+ // (fieldtype: FIELD_WITH_DEFAULT_VALUE, value: "female")
+ // Note that, the field mapping is repeated twice to respond to both the
+ // input elements with the same name/signature in the form.
+ string16 default_value = UTF8ToUTF16(field.default_value());
+ // Mark the field checked if server says the default value of the field
+ // to be this field's value.
+ field_to_fill->is_checked = (field.value == default_value);
+ return;
+ }
+
+ // Handle verification code directly.
+ if (type == CREDIT_CARD_VERIFICATION_CODE) {
+ field_to_fill->value = cvv_;
+ return;
+ }
+
+ // TODO(ramankk): Handle variants in a better fashion, need to distinguish
+ // between shipping and billing address.
+ if (AutofillType(type).group() == AutofillType::CREDIT_CARD)
+ credit_card_->FillFormField(field, 0, field_to_fill);
+ else
+ profile_->FillFormField(field, 0, field_to_fill);
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/autocheckout_manager.h b/components/autofill/browser/autocheckout_manager.h
new file mode 100644
index 0000000..8383a31
--- /dev/null
+++ b/components/autofill/browser/autocheckout_manager.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2013 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_BROWSER_AUTOCHECKOUT_MANAGER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_MANAGER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/string16.h"
+#include "components/autofill/browser/autocheckout_page_meta_data.h"
+#include "ui/gfx/native_widget_types.h"
+
+class AutofillField;
+class AutofillManager;
+class AutofillProfile;
+class CreditCard;
+class FormStructure;
+class GURL;
+
+struct FormData;
+struct FormFieldData;
+
+namespace content {
+struct SSLStatus;
+}
+
+namespace gfx {
+class RectF;
+}
+
+namespace autofill {
+
+class AutocheckoutManager {
+ public:
+ explicit AutocheckoutManager(AutofillManager* autofill_manager);
+ virtual ~AutocheckoutManager();
+
+ // Fill all the forms seen by the Autofill manager with the information
+ // gathered from the requestAutocomplete dialog.
+ void FillForms();
+
+ // Sets |page_meta_data_| with the meta data for the current page.
+ void OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData> page_meta_data);
+
+ // Called when a page containing forms is loaded.
+ void OnFormsSeen();
+
+ // Causes the Autocheckout bubble to be displayed if the user hasn't seen it
+ // yet for the current page. |frame_url| is the page where Autocheckout is
+ // being initiated. |ssl_status| is the SSL status of the page. |native_view|
+ // is the parent view of the bubble. |bounding_box| is the bounding box of the
+ // input field in focus. Returns true if the bubble was shown and false
+ // otherwise.
+ virtual bool MaybeShowAutocheckoutBubble(const GURL& frame_url,
+ const content::SSLStatus& ssl_status,
+ const gfx::NativeView& native_view,
+ const gfx::RectF& bounding_box);
+
+ // Show the requestAutocomplete dialog.
+ virtual void ShowAutocheckoutDialog(const GURL& frame_url,
+ const content::SSLStatus& ssl_status);
+
+ // Whether or not the current page is the start of a multipage Autofill flow.
+ bool IsStartOfAutofillableFlow() const;
+
+ // Whether or not the current page is part of a multipage Autofill flow.
+ bool IsInAutofillableFlow() const;
+
+ protected:
+ // Exposed for testing.
+ bool in_autocheckout_flow() const { return in_autocheckout_flow_; }
+
+ // Exposed for testing.
+ bool autocheckout_bubble_shown() const { return autocheckout_bubble_shown_; }
+
+ private:
+ // Callback called from AutofillDialogController on filling up the UI form.
+ void ReturnAutocheckoutData(const FormStructure* result);
+
+ // Sets value of form field data |field_to_fill| based on the Autofill
+ // field type specified by |field|.
+ void SetValue(const AutofillField& field, FormFieldData* field_to_fill);
+
+ AutofillManager* autofill_manager_; // WEAK; owns us
+
+ // Credit card verification code.
+ string16 cvv_;
+
+ // Profile built using the data supplied by requestAutocomplete dialog.
+ scoped_ptr<AutofillProfile> profile_;
+
+ // Credit card built using the data supplied by requestAutocomplete dialog.
+ scoped_ptr<CreditCard> credit_card_;
+
+ // Autocheckout specific page meta data.
+ scoped_ptr<AutocheckoutPageMetaData> page_meta_data_;
+
+ // Whether or not the Autocheckout bubble has been displayed to the user for
+ // the current forms. Ensures the Autocheckout bubble is only shown to a
+ // user once per pageview.
+ bool autocheckout_bubble_shown_;
+
+ // Whether or not the user is in an Autocheckout flow.
+ bool in_autocheckout_flow_;
+
+ base::WeakPtrFactory<AutocheckoutManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_MANAGER_H_
diff --git a/components/autofill/browser/autocheckout_manager_unittest.cc b/components/autofill/browser/autocheckout_manager_unittest.cc
new file mode 100644
index 0000000..28a6c57
--- /dev/null
+++ b/components/autofill/browser/autocheckout_manager_unittest.cc
@@ -0,0 +1,529 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/tuple.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/autofill/browser/autocheckout_manager.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/form_data.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_browser_thread.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+using content::BrowserThread;
+
+namespace autofill {
+
+namespace {
+
+typedef Tuple2<std::vector<FormData>, WebElementDescriptor> AutofillParam;
+
+FormFieldData BuildFieldWithValue(
+ const std::string& autocomplete_attribute,
+ const std::string& value) {
+ FormFieldData field;
+ field.name = ASCIIToUTF16(autocomplete_attribute);
+ field.value = ASCIIToUTF16(value);
+ field.autocomplete_attribute = autocomplete_attribute;
+ field.form_control_type = "text";
+ return field;
+}
+
+FormFieldData BuildField(const std::string& autocomplete_attribute) {
+ return BuildFieldWithValue(autocomplete_attribute, autocomplete_attribute);
+}
+
+scoped_ptr<FormStructure> CreateTestFormStructure(
+ const std::vector<AutofillFieldType>& autofill_types) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ form.user_submitted = true;
+
+ // Add some fields, autocomplete_attribute is not important and we
+ // fake that server sends authoritative field mappings.
+ for (size_t i = 0; i < autofill_types.size(); ++i)
+ form.fields.push_back(BuildField("SomeField"));
+
+ scoped_ptr<FormStructure> form_structure(
+ new FormStructure(form, std::string()));
+
+ // Set mocked Autofill server field types.
+ for (size_t i = 0; i < autofill_types.size(); ++i)
+ form_structure->field(i)->set_server_type(autofill_types[i]);
+
+ return form_structure.Pass();
+}
+
+scoped_ptr<FormStructure> CreateTestAddressFormStructure() {
+ std::vector<AutofillFieldType> autofill_types;
+ autofill_types.push_back(NAME_FULL);
+ autofill_types.push_back(PHONE_HOME_WHOLE_NUMBER);
+ autofill_types.push_back(EMAIL_ADDRESS);
+
+ return CreateTestFormStructure(autofill_types);
+}
+
+scoped_ptr<FormStructure> CreateTestCreditCardFormStructure() {
+ std::vector<AutofillFieldType> autofill_types;
+ autofill_types.push_back(CREDIT_CARD_NAME);
+ autofill_types.push_back(CREDIT_CARD_NUMBER);
+ autofill_types.push_back(CREDIT_CARD_EXP_MONTH);
+ autofill_types.push_back(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ autofill_types.push_back(CREDIT_CARD_VERIFICATION_CODE);
+ autofill_types.push_back(ADDRESS_HOME_LINE1);
+ autofill_types.push_back(ADDRESS_HOME_CITY);
+ autofill_types.push_back(ADDRESS_HOME_STATE);
+ autofill_types.push_back(ADDRESS_HOME_COUNTRY);
+ autofill_types.push_back(ADDRESS_HOME_ZIP);
+ return CreateTestFormStructure(autofill_types);
+}
+
+scoped_ptr<FormStructure> CreateTestFormStructureWithDefaultValues() {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ form.user_submitted = true;
+
+ // Add two radio button fields.
+ FormFieldData male = BuildFieldWithValue("sex", "male");
+ male.is_checkable = true;
+ form.fields.push_back(male);
+ FormFieldData female = BuildFieldWithValue("sex", "female");
+ female.is_checkable = true;
+ form.fields.push_back(female);
+
+ scoped_ptr<FormStructure> form_structure(
+ new FormStructure(form, std::string()));
+
+ // Fake server response. Set all fields as fields with default value.
+ form_structure->field(0)->set_server_type(FIELD_WITH_DEFAULT_VALUE);
+ form_structure->field(0)->set_default_value("female");
+ form_structure->field(1)->set_server_type(FIELD_WITH_DEFAULT_VALUE);
+ form_structure->field(1)->set_default_value("female");
+
+ return form_structure.Pass();
+}
+
+scoped_ptr<WebElementDescriptor> CreateProceedElement() {
+ scoped_ptr<WebElementDescriptor> proceed_element(new WebElementDescriptor());
+ proceed_element->descriptor = "#foo";
+ proceed_element->retrieval_method = WebElementDescriptor::ID;
+ return proceed_element.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateStartOfFlowMetaData() {
+ scoped_ptr<AutocheckoutPageMetaData> start_of_flow(
+ new AutocheckoutPageMetaData());
+ start_of_flow->current_page_number = 0;
+ start_of_flow->total_pages = 3;
+ start_of_flow->proceed_element_descriptor = CreateProceedElement().Pass();
+ return start_of_flow.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateInFlowMetaData() {
+ scoped_ptr<AutocheckoutPageMetaData> in_flow(new AutocheckoutPageMetaData());
+ in_flow->current_page_number = 1;
+ in_flow->total_pages = 3;
+ in_flow->proceed_element_descriptor = CreateProceedElement().Pass();
+ return in_flow.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateEndOfFlowMetaData() {
+ scoped_ptr<AutocheckoutPageMetaData> end_of_flow(
+ new AutocheckoutPageMetaData());
+ end_of_flow->current_page_number = 2;
+ end_of_flow->total_pages = 3;
+ return end_of_flow.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateMissingProceedMetaData() {
+ scoped_ptr<AutocheckoutPageMetaData> missing_proceed(
+ new AutocheckoutPageMetaData());
+ missing_proceed->current_page_number = 1;
+ missing_proceed->total_pages = 3;
+ return missing_proceed.Pass();
+}
+
+struct TestField {
+ const char* const field_type;
+ const char* const field_value;
+ AutofillFieldType autofill_type;
+};
+
+const TestField kTestFields[] = {
+ {"name", "Test User", NAME_FULL},
+ {"tel", "650-123-9909", PHONE_HOME_WHOLE_NUMBER},
+ {"email", "blah@blah.com", EMAIL_ADDRESS},
+ {"cc-name", "Test User", CREDIT_CARD_NAME},
+ {"cc-number", "4444444444444448", CREDIT_CARD_NUMBER},
+ {"cc-exp-month", "10", CREDIT_CARD_EXP_MONTH},
+ {"cc-exp-year", "2020", CREDIT_CARD_EXP_4_DIGIT_YEAR},
+ {"cc-csc", "123", CREDIT_CARD_VERIFICATION_CODE},
+ {"street-address", "Fake Street", ADDRESS_HOME_LINE1},
+ {"locality", "Mocked City", ADDRESS_HOME_CITY},
+ {"region", "California", ADDRESS_HOME_STATE},
+ {"country", "USA", ADDRESS_HOME_COUNTRY},
+ {"postal-code", "49012", ADDRESS_HOME_ZIP}
+};
+
+// Build Autocheckout specific form data to be consumed by
+// AutofillDialogController to show the Autocheckout specific UI.
+scoped_ptr<FormStructure> FakeUserSubmittedFormStructure() {
+ FormData formdata;
+ for (size_t i = 0; i < arraysize(kTestFields); i++) {
+ formdata.fields.push_back(
+ BuildFieldWithValue(kTestFields[i].field_type,
+ kTestFields[i].field_value));
+ }
+ scoped_ptr<FormStructure> form_structure;
+ form_structure.reset(new FormStructure(formdata, std::string()));
+ for (size_t i = 0; i < arraysize(kTestFields); ++i)
+ form_structure->field(i)->set_server_type(kTestFields[i].autofill_type);
+
+ return form_structure.Pass();
+}
+
+class MockAutofillManagerDelegate : public TestAutofillManagerDelegate {
+ public:
+ MockAutofillManagerDelegate()
+ : request_autocomplete_dialog_open_(false),
+ autocheckout_bubble_shown_(false) {}
+
+ virtual ~MockAutofillManagerDelegate() {}
+
+ virtual void HideRequestAutocompleteDialog() OVERRIDE {
+ request_autocomplete_dialog_open_ = false;
+ }
+
+ MOCK_METHOD0(OnAutocheckoutError, void());
+
+ virtual void ShowAutocheckoutBubble(
+ const gfx::RectF& bounds,
+ const gfx::NativeView& native_view,
+ const base::Closure& callback) OVERRIDE {
+ autocheckout_bubble_shown_ = true;
+ callback.Run();
+ }
+
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ const AutofillMetrics& metric_logger,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*)>& callback) OVERRIDE {
+ request_autocomplete_dialog_open_ = true;
+ callback.Run(user_supplied_data_.get());
+ }
+
+ MOCK_METHOD1(UpdateProgressBar, void(double value));
+
+ void SetUserSuppliedData(scoped_ptr<FormStructure> user_supplied_data) {
+ user_supplied_data_.reset(user_supplied_data.release());
+ }
+
+ bool autocheckout_bubble_shown() const {
+ return autocheckout_bubble_shown_;
+ }
+
+ void set_autocheckout_bubble_shown(bool autocheckout_bubble_shown) {
+ autocheckout_bubble_shown_ = autocheckout_bubble_shown;
+ }
+
+ bool request_autocomplete_dialog_open() const {
+ return request_autocomplete_dialog_open_;
+ }
+
+ private:
+ bool request_autocomplete_dialog_open_;
+ bool autocheckout_bubble_shown_;
+ scoped_ptr<FormStructure> user_supplied_data_;
+};
+
+class TestAutofillManager : public AutofillManager {
+ public:
+ explicit TestAutofillManager(content::WebContents* contents,
+ AutofillManagerDelegate* delegate)
+ : AutofillManager(contents, delegate, NULL) {
+ }
+ virtual ~TestAutofillManager() {}
+
+ void SetFormStructure(scoped_ptr<FormStructure> form_structure) {
+ form_structures()->clear();
+ form_structures()->push_back(form_structure.release());
+ }
+};
+
+
+class TestAutocheckoutManager: public AutocheckoutManager {
+ public:
+ explicit TestAutocheckoutManager(AutofillManager* autofill_manager)
+ : AutocheckoutManager(autofill_manager) {}
+
+ using AutocheckoutManager::in_autocheckout_flow;
+ using AutocheckoutManager::autocheckout_bubble_shown;
+};
+
+} // namespace
+
+class AutocheckoutManagerTest : public ChromeRenderViewHostTestHarness {
+ public:
+ AutocheckoutManagerTest()
+ : ChromeRenderViewHostTestHarness(),
+ ui_thread_(BrowserThread::UI, &message_loop_) {
+ }
+
+ std::vector<FormData> ReadFilledForms() {
+ uint32 kMsgID = AutofillMsg_FillFormsAndClick::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ AutofillParam autofill_param;
+ AutofillMsg_FillFormsAndClick::Read(message, &autofill_param);
+ return autofill_param.a;
+ }
+
+ void CheckIpcMessageSent() {
+ EXPECT_EQ(1U, process()->sink().message_count());
+ uint32 kMsgID = AutofillMsg_FillFormsAndClick::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ ClearIpcSink();
+ }
+
+ void ClearIpcSink() {
+ process()->sink().ClearMessages();
+ }
+
+ void OpenRequestAutocompleteDialog() {
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_FALSE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ // Simulate the user submitting some data via the requestAutocomplete UI.
+ autofill_manager_delegate_->SetUserSuppliedData(
+ FakeUserSubmittedFormStructure());
+ GURL frame_url;
+ content::SSLStatus ssl_status;
+ EXPECT_CALL(*autofill_manager_delegate_,
+ UpdateProgressBar(testing::DoubleEq(1.0/3.0))).Times(1);
+ autocheckout_manager_->ShowAutocheckoutDialog(frame_url, ssl_status);
+ CheckIpcMessageSent();
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+ }
+
+ void HideRequestAutocompleteDialog() {
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ autofill_manager_delegate_->HideRequestAutocompleteDialog();
+ EXPECT_FALSE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ }
+
+ protected:
+ content::TestBrowserThread ui_thread_;
+ scoped_ptr<TestAutofillManager> autofill_manager_;
+ scoped_ptr<TestAutocheckoutManager> autocheckout_manager_;
+ scoped_ptr<MockAutofillManagerDelegate> autofill_manager_delegate_;
+
+ private:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+ autofill_manager_delegate_.reset(new MockAutofillManagerDelegate());
+ autofill_manager_.reset(new TestAutofillManager(
+ web_contents(),
+ autofill_manager_delegate_.get()));
+ autocheckout_manager_.reset(
+ new TestAutocheckoutManager(autofill_manager_.get()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ autocheckout_manager_.reset();
+ autofill_manager_delegate_.reset();
+ autofill_manager_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutManagerTest);
+};
+
+TEST_F(AutocheckoutManagerTest, TestFillForms) {
+ OpenRequestAutocompleteDialog();
+
+ // Test if autocheckout manager can fill the first page.
+ autofill_manager_->SetFormStructure(CreateTestAddressFormStructure());
+
+ autocheckout_manager_->FillForms();
+
+ std::vector<FormData> filled_forms = ReadFilledForms();
+ ASSERT_EQ(1U, filled_forms.size());
+ ASSERT_EQ(3U, filled_forms[0].fields.size());
+ EXPECT_EQ(ASCIIToUTF16("Test User"), filled_forms[0].fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("650-123-9909"), filled_forms[0].fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("blah@blah.com"), filled_forms[0].fields[2].value);
+
+ filled_forms.clear();
+ ClearIpcSink();
+
+ // Test if autocheckout manager can fill form on second page.
+ autofill_manager_->SetFormStructure(CreateTestCreditCardFormStructure());
+
+ autocheckout_manager_->FillForms();
+
+ filled_forms = ReadFilledForms();
+ ASSERT_EQ(1U, filled_forms.size());
+ ASSERT_EQ(10U, filled_forms[0].fields.size());
+ EXPECT_EQ(ASCIIToUTF16("Test User"), filled_forms[0].fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("4444444444444448"), filled_forms[0].fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("10"), filled_forms[0].fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("2020"), filled_forms[0].fields[3].value);
+ EXPECT_EQ(ASCIIToUTF16("123"), filled_forms[0].fields[4].value);
+ EXPECT_EQ(ASCIIToUTF16("Fake Street"), filled_forms[0].fields[5].value);
+ EXPECT_EQ(ASCIIToUTF16("Mocked City"), filled_forms[0].fields[6].value);
+ EXPECT_EQ(ASCIIToUTF16("California"), filled_forms[0].fields[7].value);
+ EXPECT_EQ(ASCIIToUTF16("United States"), filled_forms[0].fields[8].value);
+ EXPECT_EQ(ASCIIToUTF16("49012"), filled_forms[0].fields[9].value);
+
+ filled_forms.clear();
+ ClearIpcSink();
+
+ // Test form with default values.
+ autofill_manager_->SetFormStructure(
+ CreateTestFormStructureWithDefaultValues());
+
+ autocheckout_manager_->FillForms();
+
+ filled_forms = ReadFilledForms();
+ ASSERT_EQ(1U, filled_forms.size());
+ ASSERT_EQ(2U, filled_forms[0].fields.size());
+ EXPECT_FALSE(filled_forms[0].fields[0].is_checked);
+ EXPECT_EQ(ASCIIToUTF16("male"), filled_forms[0].fields[0].value);
+ EXPECT_TRUE(filled_forms[0].fields[1].is_checked);
+ EXPECT_EQ(ASCIIToUTF16("female"), filled_forms[0].fields[1].value);
+}
+
+TEST_F(AutocheckoutManagerTest, OnFormsSeenTest) {
+ GURL frame_url;
+ content::SSLStatus ssl_status;
+ gfx::NativeView native_view;
+ gfx::RectF bounding_box;
+ EXPECT_TRUE(autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url,
+ ssl_status,
+ native_view,
+ bounding_box));
+ EXPECT_TRUE(autocheckout_manager_->autocheckout_bubble_shown());
+ // OnFormsSeen resets whether or not the bubble was shown.
+ autocheckout_manager_->OnFormsSeen();
+ EXPECT_FALSE(autocheckout_manager_->autocheckout_bubble_shown());
+}
+
+TEST_F(AutocheckoutManagerTest, MaybeShowAutocheckoutBubbleTest) {
+ GURL frame_url;
+ content::SSLStatus ssl_status;
+ gfx::NativeView native_view;
+ gfx::RectF bounding_box;
+ // MaybeShowAutocheckoutBubble shows bubble if it has not been shown.
+ EXPECT_TRUE(autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url,
+ ssl_status,
+ native_view,
+ bounding_box));
+ EXPECT_TRUE(autocheckout_manager_->autocheckout_bubble_shown());
+ EXPECT_TRUE(autofill_manager_delegate_->autocheckout_bubble_shown());
+
+ // Reset |autofill_manager_delegate_|.
+ HideRequestAutocompleteDialog();
+ autofill_manager_delegate_->set_autocheckout_bubble_shown(false);
+
+ // MaybeShowAutocheckoutBubble does nothing if the bubble was already shown
+ // for the current page.
+ EXPECT_FALSE(autocheckout_manager_->MaybeShowAutocheckoutBubble(
+ frame_url,
+ ssl_status,
+ native_view,
+ bounding_box));
+ EXPECT_TRUE(autocheckout_manager_->autocheckout_bubble_shown());
+ EXPECT_FALSE(autofill_manager_delegate_->autocheckout_bubble_shown());
+ EXPECT_FALSE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+}
+
+TEST_F(AutocheckoutManagerTest, OnLoadedPageMetaDataTest) {
+ // Gettting no meta data after any autocheckout page is an error.
+ OpenRequestAutocompleteDialog();
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData>());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ HideRequestAutocompleteDialog();
+
+ // Getting start page twice in a row is an error.
+ OpenRequestAutocompleteDialog();
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ HideRequestAutocompleteDialog();
+
+ // A missing proceed element when not at the end of a flow is an error.
+ OpenRequestAutocompleteDialog();
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateMissingProceedMetaData());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ HideRequestAutocompleteDialog();
+
+ // Repeating a page is an error.
+ OpenRequestAutocompleteDialog();
+ // Go to second page.
+ EXPECT_CALL(*autofill_manager_delegate_,
+ UpdateProgressBar(testing::DoubleEq(2.0/3.0))).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ CheckIpcMessageSent();
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ HideRequestAutocompleteDialog();
+
+ // If not in flow, OnLoadedPageMetaData does not fill forms.
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ // Go to second page.
+ EXPECT_CALL(*autofill_manager_delegate_,
+ UpdateProgressBar(testing::_)).Times(0);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_EQ(0U, process()->sink().message_count());
+
+ // Test for progression through last page.
+ OpenRequestAutocompleteDialog();
+ // Go to second page.
+ EXPECT_CALL(*autofill_manager_delegate_,
+ UpdateProgressBar(testing::DoubleEq(2.0/3.0))).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ CheckIpcMessageSent();
+ // Go to third page.
+ EXPECT_CALL(*autofill_manager_delegate_, UpdateProgressBar(1)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateEndOfFlowMetaData());
+ CheckIpcMessageSent();
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_FALSE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/autocheckout_page_meta_data.cc b/components/autofill/browser/autocheckout_page_meta_data.cc
new file mode 100644
index 0000000..aa48ca7
--- /dev/null
+++ b/components/autofill/browser/autocheckout_page_meta_data.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 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/browser/autocheckout_page_meta_data.h"
+
+namespace autofill {
+
+AutocheckoutPageMetaData::AutocheckoutPageMetaData()
+ : current_page_number(-1),
+ total_pages(-1) {}
+
+AutocheckoutPageMetaData::~AutocheckoutPageMetaData() {}
+
+bool AutocheckoutPageMetaData::IsStartOfAutofillableFlow() const {
+ return current_page_number == 0 && total_pages > 0;
+}
+
+bool AutocheckoutPageMetaData::IsInAutofillableFlow() const {
+ return current_page_number >= 0 && current_page_number < total_pages;
+}
+
+bool AutocheckoutPageMetaData::IsEndOfAutofillableFlow() const {
+ return total_pages > 0 && current_page_number == total_pages - 1;
+}
+
+} // namesapce autofill
diff --git a/components/autofill/browser/autocheckout_page_meta_data.h b/components/autofill/browser/autocheckout_page_meta_data.h
new file mode 100644
index 0000000..20b0883
--- /dev/null
+++ b/components/autofill/browser/autocheckout_page_meta_data.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 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_BROWSER_AUTOCHECKOUT_PAGE_META_DATA_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_PAGE_META_DATA_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/common/web_element_descriptor.h"
+
+namespace autofill {
+
+// Container for multipage Autocheckout data.
+struct AutocheckoutPageMetaData {
+ AutocheckoutPageMetaData();
+ ~AutocheckoutPageMetaData();
+
+ // Returns true if the Autofill server says that the current page is start of
+ // a multipage Autofill flow.
+ bool IsStartOfAutofillableFlow() const;
+
+ // Returns true if the Autofill server says that the current page is in a
+ // multipage Autofill flow.
+ bool IsInAutofillableFlow() const;
+
+ // Returns true if the Autofill server says that the current page is the end
+ // of a multipage Autofill flow.
+ bool IsEndOfAutofillableFlow() const;
+
+ // Page number of the multipage Autofill flow this form belongs to
+ // (zero-indexed). If this form doesn't belong to any autofill flow, it is set
+ // to -1.
+ int current_page_number;
+
+ // Total number of pages in the multipage Autofill flow. If this form doesn't
+ // belong to any autofill flow, it is set to -1.
+ int total_pages;
+
+ // The proceed element of the multipage Autofill flow. Can be null if the
+ // current page is the last page of a flow or isn't a member of a flow.
+ scoped_ptr<WebElementDescriptor> proceed_element_descriptor;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutPageMetaData);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_PAGE_META_DATA_H_
diff --git a/components/autofill/browser/autocheckout_page_meta_data_unittest.cc b/components/autofill/browser/autocheckout_page_meta_data_unittest.cc
new file mode 100644
index 0000000..bb3f2da
--- /dev/null
+++ b/components/autofill/browser/autocheckout_page_meta_data_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2013 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/browser/autocheckout_page_meta_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void SetPageDetails(autofill::AutocheckoutPageMetaData* meta_data,
+ int current_page,
+ int total) {
+ meta_data->current_page_number = current_page;
+ meta_data->total_pages = total;
+}
+
+} // namespace
+
+namespace autofill {
+
+TEST(AutocheckoutPageMetaDataTest, AutofillableFlow) {
+
+ AutocheckoutPageMetaData page_meta_data;
+ EXPECT_FALSE(page_meta_data.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsInAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsEndOfAutofillableFlow());
+
+ SetPageDetails(&page_meta_data, -1, 0);
+ EXPECT_FALSE(page_meta_data.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsInAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsEndOfAutofillableFlow());
+
+ SetPageDetails(&page_meta_data, 0, 0);
+ EXPECT_FALSE(page_meta_data.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsInAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsEndOfAutofillableFlow());
+
+ SetPageDetails(&page_meta_data, 0, 1);
+ EXPECT_TRUE(page_meta_data.IsStartOfAutofillableFlow());
+ EXPECT_TRUE(page_meta_data.IsInAutofillableFlow());
+ EXPECT_TRUE(page_meta_data.IsEndOfAutofillableFlow());
+
+ SetPageDetails(&page_meta_data, 1, 2);
+ EXPECT_FALSE(page_meta_data.IsStartOfAutofillableFlow());
+ EXPECT_TRUE(page_meta_data.IsInAutofillableFlow());
+ EXPECT_TRUE(page_meta_data.IsEndOfAutofillableFlow());
+
+ SetPageDetails(&page_meta_data, 2, 2);
+ EXPECT_FALSE(page_meta_data.IsStartOfAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsInAutofillableFlow());
+ EXPECT_FALSE(page_meta_data.IsEndOfAutofillableFlow());
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/autocomplete_history_manager.cc b/components/autofill/browser/autocomplete_history_manager.cc
new file mode 100644
index 0000000..a38b0d6
--- /dev/null
+++ b/components/autofill/browser/autocomplete_history_manager.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autocomplete_history_manager.h"
+
+#include <vector>
+
+#include "base/prefs/pref_service.h"
+#include "base/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_external_delegate.h"
+#include "components/autofill/browser/validation.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/autofill_pref_names.h"
+#include "components/autofill/common/form_data.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+
+using base::StringPiece16;
+using content::BrowserContext;
+using content::WebContents;
+
+namespace {
+
+// Limit on the number of suggestions to appear in the pop-up menu under an
+// text input element in a form.
+const int kMaxAutocompleteMenuItems = 6;
+
+// The separator characters for SSNs.
+const char16 kSSNSeparators[] = {' ', '-', 0};
+
+bool IsSSN(const string16& text) {
+ string16 number_string;
+ RemoveChars(text, kSSNSeparators, &number_string);
+
+ // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S =
+ // serial number). The validation we do here is simply checking if the area,
+ // group, and serial numbers are valid.
+ //
+ // Historically, the area number was assigned per state, with the group number
+ // ascending in an alternating even/odd sequence. With that scheme it was
+ // possible to check for validity by referencing a table that had the highest
+ // group number assigned for a given area number. (This was something that
+ // Chromium never did though, because the "high group" values were constantly
+ // changing.)
+ //
+ // However, starting on 25 June 2011 the SSA began issuing SSNs randomly from
+ // all areas and groups. Group numbers and serial numbers of zero remain
+ // invalid, and areas 000, 666, and 900-999 remain invalid.
+ //
+ // References for current practices:
+ // http://www.socialsecurity.gov/employer/randomization.html
+ // http://www.socialsecurity.gov/employer/randomizationfaqs.html
+ //
+ // References for historic practices:
+ // http://www.socialsecurity.gov/history/ssn/geocard.html
+ // http://www.socialsecurity.gov/employer/stateweb.htm
+ // http://www.socialsecurity.gov/employer/ssnvhighgroup.htm
+
+ if (number_string.length() != 9 || !IsStringASCII(number_string))
+ return false;
+
+ int area;
+ if (!base::StringToInt(StringPiece16(number_string.begin(),
+ number_string.begin() + 3),
+ &area)) {
+ return false;
+ }
+ if (area < 1 ||
+ area == 666 ||
+ area >= 900) {
+ return false;
+ }
+
+ int group;
+ if (!base::StringToInt(StringPiece16(number_string.begin() + 3,
+ number_string.begin() + 5),
+ &group)
+ || group == 0) {
+ return false;
+ }
+
+ int serial;
+ if (!base::StringToInt(StringPiece16(number_string.begin() + 5,
+ number_string.begin() + 9),
+ &serial)
+ || serial == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool IsTextField(const FormFieldData& field) {
+ return
+ field.form_control_type == "text" ||
+ field.form_control_type == "search" ||
+ field.form_control_type == "tel" ||
+ field.form_control_type == "url" ||
+ field.form_control_type == "email" ||
+ field.form_control_type == "text";
+}
+
+} // namespace
+
+AutocompleteHistoryManager::AutocompleteHistoryManager(
+ WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ browser_context_(web_contents->GetBrowserContext()),
+ autofill_data_(
+ AutofillWebDataService::FromBrowserContext(browser_context_)),
+ pending_query_handle_(0),
+ query_id_(0),
+ external_delegate_(NULL) {
+ autofill_enabled_.Init(
+ prefs::kAutofillEnabled,
+ components::UserPrefs::Get(browser_context_));
+}
+
+AutocompleteHistoryManager::~AutocompleteHistoryManager() {
+ CancelPendingQuery();
+}
+
+bool AutocompleteHistoryManager::OnMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AutocompleteHistoryManager, message)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_RemoveAutocompleteEntry,
+ OnRemoveAutocompleteEntry)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AutocompleteHistoryManager::OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle h,
+ const WDTypedResult* result) {
+ DCHECK(pending_query_handle_);
+ pending_query_handle_ = 0;
+
+ if (!*autofill_enabled_) {
+ SendSuggestions(NULL);
+ return;
+ }
+
+ DCHECK(result);
+ // Returning early here if |result| is NULL. We've seen this happen on
+ // Linux due to NFS dismounting and causing sql failures.
+ // See http://crbug.com/68783.
+ if (!result) {
+ SendSuggestions(NULL);
+ return;
+ }
+
+ DCHECK_EQ(AUTOFILL_VALUE_RESULT, result->GetType());
+ const WDResult<std::vector<string16> >* autofill_result =
+ static_cast<const WDResult<std::vector<string16> >*>(result);
+ std::vector<string16> suggestions = autofill_result->GetValue();
+ SendSuggestions(&suggestions);
+}
+
+void AutocompleteHistoryManager::OnGetAutocompleteSuggestions(
+ int query_id,
+ const string16& name,
+ const string16& prefix,
+ const std::vector<string16>& autofill_values,
+ const std::vector<string16>& autofill_labels,
+ const std::vector<string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids) {
+ CancelPendingQuery();
+
+ query_id_ = query_id;
+ autofill_values_ = autofill_values;
+ autofill_labels_ = autofill_labels;
+ autofill_icons_ = autofill_icons;
+ autofill_unique_ids_ = autofill_unique_ids;
+ if (!*autofill_enabled_) {
+ SendSuggestions(NULL);
+ return;
+ }
+
+ if (autofill_data_.get()) {
+ pending_query_handle_ = autofill_data_->GetFormValuesForElementName(
+ name, prefix, kMaxAutocompleteMenuItems, this);
+ }
+}
+
+void AutocompleteHistoryManager::OnFormSubmitted(const FormData& form) {
+ if (!*autofill_enabled_)
+ return;
+
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ // Don't save data that was submitted through JavaScript.
+ if (!form.user_submitted)
+ return;
+
+ // We put the following restriction on stored FormFields:
+ // - non-empty name
+ // - non-empty value
+ // - text field
+ // - value is not a credit card number
+ // - value is not a SSN
+ std::vector<FormFieldData> values;
+ for (std::vector<FormFieldData>::const_iterator iter =
+ form.fields.begin();
+ iter != form.fields.end(); ++iter) {
+ if (!iter->value.empty() &&
+ !iter->name.empty() &&
+ IsTextField(*iter) &&
+ !autofill::IsValidCreditCardNumber(iter->value) &&
+ !IsSSN(iter->value)) {
+ values.push_back(*iter);
+ }
+ }
+
+ if (!values.empty() && autofill_data_.get())
+ autofill_data_->AddFormFields(values);
+}
+
+void AutocompleteHistoryManager::OnRemoveAutocompleteEntry(
+ const string16& name, const string16& value) {
+ if (autofill_data_.get())
+ autofill_data_->RemoveFormValueForElementName(name, value);
+}
+
+void AutocompleteHistoryManager::SetExternalDelegate(
+ AutofillExternalDelegate* delegate) {
+ external_delegate_ = delegate;
+}
+
+void AutocompleteHistoryManager::CancelPendingQuery() {
+ if (pending_query_handle_) {
+ if (autofill_data_)
+ autofill_data_->CancelRequest(pending_query_handle_);
+ pending_query_handle_ = 0;
+ }
+}
+
+void AutocompleteHistoryManager::SendSuggestions(
+ const std::vector<string16>* suggestions) {
+ if (suggestions) {
+ // Combine Autofill and Autocomplete values into values and labels.
+ for (size_t i = 0; i < suggestions->size(); ++i) {
+ bool unique = true;
+ for (size_t j = 0; j < autofill_values_.size(); ++j) {
+ // Don't add duplicate values.
+ if (autofill_values_[j] == (*suggestions)[i]) {
+ unique = false;
+ break;
+ }
+ }
+
+ if (unique) {
+ autofill_values_.push_back((*suggestions)[i]);
+ autofill_labels_.push_back(string16());
+ autofill_icons_.push_back(string16());
+ autofill_unique_ids_.push_back(0); // 0 means no profile.
+ }
+ }
+ }
+
+ if (external_delegate_) {
+ external_delegate_->OnSuggestionsReturned(
+ query_id_,
+ autofill_values_,
+ autofill_labels_,
+ autofill_icons_,
+ autofill_unique_ids_);
+ } else {
+ Send(new AutofillMsg_SuggestionsReturned(routing_id(),
+ query_id_,
+ autofill_values_,
+ autofill_labels_,
+ autofill_icons_,
+ autofill_unique_ids_));
+ }
+
+ query_id_ = 0;
+ autofill_values_.clear();
+ autofill_labels_.clear();
+ autofill_icons_.clear();
+ autofill_unique_ids_.clear();
+}
diff --git a/components/autofill/browser/autocomplete_history_manager.h b/components/autofill/browser/autocomplete_history_manager.h
new file mode 100644
index 0000000..e503087
--- /dev/null
+++ b/components/autofill/browser/autocomplete_history_manager.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/prefs/public/pref_member.h"
+#include "chrome/browser/api/webdata/autofill_web_data_service.h"
+#include "chrome/browser/api/webdata/web_data_service_consumer.h"
+#include "content/public/browser/web_contents_observer.h"
+
+struct FormData;
+
+namespace content {
+class BrowserContext;
+}
+
+class AutofillExternalDelegate;
+
+// Per-tab Autocomplete history manager. Handles receiving form data
+// from the renderer and the storing and retrieving of form data
+// through WebDataServiceBase.
+class AutocompleteHistoryManager : public content::WebContentsObserver,
+ public WebDataServiceConsumer {
+ public:
+ explicit AutocompleteHistoryManager(content::WebContents* web_contents);
+ virtual ~AutocompleteHistoryManager();
+
+ // content::WebContentsObserver implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // WebDataServiceConsumer implementation.
+ virtual void OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle h,
+ const WDTypedResult* result) OVERRIDE;
+
+ // Pass-through functions that are called by AutofillManager, after it has
+ // dispatched a message.
+ void OnGetAutocompleteSuggestions(
+ int query_id,
+ const string16& name,
+ const string16& prefix,
+ const std::vector<string16>& autofill_values,
+ const std::vector<string16>& autofill_labels,
+ const std::vector<string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids);
+ void OnFormSubmitted(const FormData& form);
+
+ // Must be public for the external delegate to use.
+ void OnRemoveAutocompleteEntry(const string16& name, const string16& value);
+
+ // Sets our external delegate.
+ void SetExternalDelegate(AutofillExternalDelegate* delegate);
+
+ protected:
+ friend class AutofillManagerTest;
+
+ // Sends the given |suggestions| for display in the Autofill popup.
+ void SendSuggestions(const std::vector<string16>* suggestions);
+
+ private:
+ // Cancels the currently pending WebDataService query, if there is one.
+ void CancelPendingQuery();
+
+ content::BrowserContext* browser_context_;
+ scoped_ptr<AutofillWebDataService> autofill_data_;
+
+ BooleanPrefMember autofill_enabled_;
+
+ // When the manager makes a request from WebDataServiceBase, the database is
+ // queried on another thread, we record the query handle until we get called
+ // back. We also store the autofill results so we can send them together.
+ WebDataServiceBase::Handle pending_query_handle_;
+ int query_id_;
+ std::vector<string16> autofill_values_;
+ std::vector<string16> autofill_labels_;
+ std::vector<string16> autofill_icons_;
+ std::vector<int> autofill_unique_ids_;
+
+ // Delegate to perform external processing (display, selection) on
+ // our behalf. Weak.
+ AutofillExternalDelegate* external_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocompleteHistoryManager);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
diff --git a/components/autofill/browser/autocomplete_history_manager_unittest.cc b/components/autofill/browser/autocomplete_history_manager_unittest.cc
new file mode 100644
index 0000000..e2becff
--- /dev/null
+++ b/components/autofill/browser/autocomplete_history_manager_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/prefs/testing_pref_service.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/webdata/autofill_web_data_service_impl.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/browser/webdata/web_data_service_factory.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autocomplete_history_manager.h"
+#include "components/autofill/browser/autofill_external_delegate.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/common/form_data.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+
+using content::BrowserThread;
+using content::WebContents;
+using testing::_;
+
+namespace {
+
+class MockWebDataService : public WebDataService {
+ public:
+ MockWebDataService()
+ : WebDataService(NULL) {
+ current_mock_web_data_service_ = this;
+ }
+
+ static scoped_refptr<RefcountedProfileKeyedService> Build(Profile* profile) {
+ return current_mock_web_data_service_;
+ }
+
+ virtual void ShutdownOnUIThread() OVERRIDE {}
+
+ MOCK_METHOD1(AddFormFields, void(const std::vector<FormFieldData>&));
+
+ protected:
+ virtual ~MockWebDataService() {}
+
+ private:
+ // Keep track of the most recently created instance, so that it can be
+ // associated with the current profile when Build() is called.
+ static MockWebDataService* current_mock_web_data_service_;
+};
+
+MockWebDataService* MockWebDataService::current_mock_web_data_service_ = NULL;
+
+class MockAutofillManagerDelegate
+ : public autofill::TestAutofillManagerDelegate {
+ public:
+ MockAutofillManagerDelegate() {}
+ virtual ~MockAutofillManagerDelegate() {}
+ virtual PrefService* GetPrefs() { return &prefs_; }
+
+ private:
+ TestingPrefServiceSimple prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
+};
+
+} // namespace
+
+class AutocompleteHistoryManagerTest : public ChromeRenderViewHostTestHarness {
+ protected:
+ AutocompleteHistoryManagerTest()
+ : db_thread_(BrowserThread::DB) {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+ web_data_service_ = new MockWebDataService();
+ WebDataServiceFactory::GetInstance()->SetTestingFactory(
+ profile(), MockWebDataService::Build);
+ autocomplete_manager_.reset(new AutocompleteHistoryManager(web_contents()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ autocomplete_manager_.reset();
+ web_data_service_ = NULL;
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ content::TestBrowserThread db_thread_;
+ scoped_refptr<MockWebDataService> web_data_service_;
+ scoped_ptr<AutocompleteHistoryManager> autocomplete_manager_;
+ MockAutofillManagerDelegate manager_delegate;
+};
+
+// Tests that credit card numbers are not sent to the WebDatabase to be saved.
+TEST_F(AutocompleteHistoryManagerTest, CreditCardNumberValue) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+ form.user_submitted = true;
+
+ // Valid Visa credit card number pulled from the paypal help site.
+ FormFieldData valid_cc;
+ valid_cc.label = ASCIIToUTF16("Credit Card");
+ valid_cc.name = ASCIIToUTF16("ccnum");
+ valid_cc.value = ASCIIToUTF16("4012888888881881");
+ valid_cc.form_control_type = "text";
+ form.fields.push_back(valid_cc);
+
+ EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
+ autocomplete_manager_->OnFormSubmitted(form);
+}
+
+// Contrary test to AutocompleteHistoryManagerTest.CreditCardNumberValue. The
+// value being submitted is not a valid credit card number, so it will be sent
+// to the WebDatabase to be saved.
+TEST_F(AutocompleteHistoryManagerTest, NonCreditCardNumberValue) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+ form.user_submitted = true;
+
+ // Invalid credit card number.
+ FormFieldData invalid_cc;
+ invalid_cc.label = ASCIIToUTF16("Credit Card");
+ invalid_cc.name = ASCIIToUTF16("ccnum");
+ invalid_cc.value = ASCIIToUTF16("4580123456789012");
+ invalid_cc.form_control_type = "text";
+ form.fields.push_back(invalid_cc);
+
+ EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_)).Times(1);
+ autocomplete_manager_->OnFormSubmitted(form);
+}
+
+// Tests that SSNs are not sent to the WebDatabase to be saved.
+TEST_F(AutocompleteHistoryManagerTest, SSNValue) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData ssn;
+ ssn.label = ASCIIToUTF16("Social Security Number");
+ ssn.name = ASCIIToUTF16("ssn");
+ ssn.value = ASCIIToUTF16("078-05-1120");
+ ssn.form_control_type = "text";
+ form.fields.push_back(ssn);
+
+ EXPECT_CALL(*web_data_service_, AddFormFields(_)).Times(0);
+ autocomplete_manager_->OnFormSubmitted(form);
+}
+
+// Verify that autocomplete text is saved for search fields.
+TEST_F(AutocompleteHistoryManagerTest, SearchField) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+ form.user_submitted = true;
+
+ // Search field.
+ FormFieldData search_field;
+ search_field.label = ASCIIToUTF16("Search");
+ search_field.name = ASCIIToUTF16("search");
+ search_field.value = ASCIIToUTF16("my favorite query");
+ search_field.form_control_type = "search";
+ form.fields.push_back(search_field);
+
+ EXPECT_CALL(*(web_data_service_.get()), AddFormFields(_)).Times(1);
+ autocomplete_manager_->OnFormSubmitted(form);
+}
+
+namespace {
+
+class MockAutofillExternalDelegate : public AutofillExternalDelegate {
+ public:
+ explicit MockAutofillExternalDelegate(content::WebContents* web_contents)
+ : AutofillExternalDelegate(
+ web_contents, AutofillManager::FromWebContents(web_contents)) {}
+ virtual ~MockAutofillExternalDelegate() {}
+
+ MOCK_METHOD5(OnSuggestionsReturned,
+ void(int query_id,
+ const std::vector<string16>& autofill_values,
+ const std::vector<string16>& autofill_labels,
+ const std::vector<string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillExternalDelegate);
+};
+
+class AutocompleteHistoryManagerStubSend : public AutocompleteHistoryManager {
+ public:
+ explicit AutocompleteHistoryManagerStubSend(WebContents* web_contents)
+ : AutocompleteHistoryManager(web_contents) {}
+
+ // Increase visibility for testing.
+ void SendSuggestions(const std::vector<string16>* suggestions) {
+ AutocompleteHistoryManager::SendSuggestions(suggestions);
+ }
+
+ // Intentionally swallow the message.
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ delete message;
+ return true;
+ }
+};
+
+} // namespace
+
+// Make sure our external delegate is called at the right time.
+TEST_F(AutocompleteHistoryManagerTest, ExternalDelegate) {
+ // Local version with a stubbed out Send()
+ AutocompleteHistoryManagerStubSend autocomplete_history_manager(
+ web_contents());
+
+ AutofillManager::CreateForWebContentsAndDelegate(
+ web_contents(), &manager_delegate);
+
+ MockAutofillExternalDelegate external_delegate(web_contents());
+ autocomplete_history_manager.SetExternalDelegate(&external_delegate);
+
+ // Should trigger a call to OnSuggestionsReturned, verified by the mock.
+ EXPECT_CALL(external_delegate, OnSuggestionsReturned(_, _, _, _, _));
+ autocomplete_history_manager.SendSuggestions(NULL);
+}
diff --git a/components/autofill/browser/autofill-inl.h b/components/autofill/browser/autofill-inl.h
new file mode 100644
index 0000000..726d443
--- /dev/null
+++ b/components/autofill/browser/autofill-inl.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_INL_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_INL_H_
+
+template<typename T>
+class FormGroupMatchesByCompareFunctor {
+ public:
+ explicit FormGroupMatchesByCompareFunctor(const T& form_group)
+ : form_group_(form_group) {
+ }
+
+ bool operator()(const T* form_group) {
+ return form_group->Compare(form_group_) == 0;
+ }
+
+ bool operator()(const T& form_group) {
+ return form_group.Compare(form_group_) == 0;
+ }
+
+ private:
+ const T& form_group_;
+};
+
+template<typename C, typename T>
+bool FindByContents(const C& container, const T& form_group) {
+ return std::find_if(
+ container.begin(),
+ container.end(),
+ FormGroupMatchesByCompareFunctor<T>(form_group)) != container.end();
+}
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_INL_H_
diff --git a/components/autofill/browser/autofill_common_test.cc b/components/autofill/browser/autofill_common_test.cc
new file mode 100644
index 0000000..447c12d
--- /dev/null
+++ b/components/autofill/browser/autofill_common_test.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2011 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/browser/autofill_common_test.h"
+
+#include "base/guid.h"
+#include "base/prefs/pref_service.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/password_manager/encryptor.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/common/autofill_pref_names.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/user_prefs/user_prefs.h"
+
+namespace autofill_test {
+
+void CreateTestFormField(const char* label,
+ const char* name,
+ const char* value,
+ const char* type,
+ FormFieldData* field) {
+ field->label = ASCIIToUTF16(label);
+ field->name = ASCIIToUTF16(name);
+ field->value = ASCIIToUTF16(value);
+ field->form_control_type = type;
+}
+
+inline void check_and_set(
+ FormGroup* profile, AutofillFieldType type, const char* value) {
+ if (value)
+ profile->SetRawInfo(type, UTF8ToUTF16(value));
+}
+
+AutofillProfile GetFullProfile() {
+ AutofillProfile profile(base::GenerateGUID());
+ SetProfileInfo(&profile,
+ "John",
+ "H.",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "Apt 8",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ return profile;
+}
+
+void SetProfileInfo(AutofillProfile* profile,
+ const char* first_name, const char* middle_name,
+ const char* last_name, const char* email, const char* company,
+ const char* address1, const char* address2, const char* city,
+ const char* state, const char* zipcode, const char* country,
+ const char* phone) {
+ check_and_set(profile, NAME_FIRST, first_name);
+ check_and_set(profile, NAME_MIDDLE, middle_name);
+ check_and_set(profile, NAME_LAST, last_name);
+ check_and_set(profile, EMAIL_ADDRESS, email);
+ check_and_set(profile, COMPANY_NAME, company);
+ check_and_set(profile, ADDRESS_HOME_LINE1, address1);
+ check_and_set(profile, ADDRESS_HOME_LINE2, address2);
+ check_and_set(profile, ADDRESS_HOME_CITY, city);
+ check_and_set(profile, ADDRESS_HOME_STATE, state);
+ check_and_set(profile, ADDRESS_HOME_ZIP, zipcode);
+ check_and_set(profile, ADDRESS_HOME_COUNTRY, country);
+ check_and_set(profile, PHONE_HOME_WHOLE_NUMBER, phone);
+}
+
+void SetProfileInfoWithGuid(AutofillProfile* profile,
+ const char* guid, const char* first_name, const char* middle_name,
+ const char* last_name, const char* email, const char* company,
+ const char* address1, const char* address2, const char* city,
+ const char* state, const char* zipcode, const char* country,
+ const char* phone) {
+ if (guid)
+ profile->set_guid(guid);
+ SetProfileInfo(profile, first_name, middle_name, last_name, email,
+ company, address1, address2, city, state, zipcode, country,
+ phone);
+}
+
+void SetCreditCardInfo(CreditCard* credit_card,
+ const char* name_on_card, const char* card_number,
+ const char* expiration_month, const char* expiration_year) {
+ check_and_set(credit_card, CREDIT_CARD_NAME, name_on_card);
+ check_and_set(credit_card, CREDIT_CARD_NUMBER, card_number);
+ check_and_set(credit_card, CREDIT_CARD_EXP_MONTH, expiration_month);
+ check_and_set(credit_card, CREDIT_CARD_EXP_4_DIGIT_YEAR, expiration_year);
+}
+
+void DisableSystemServices(Profile* profile) {
+ // Use a mock Keychain rather than the OS one to store credit card data.
+#if defined(OS_MACOSX)
+ Encryptor::UseMockKeychain(true);
+#endif
+
+ // Disable auxiliary profiles for unit testing. These reach out to system
+ // services on the Mac.
+ if (profile) {
+ components::UserPrefs::Get(profile)->SetBoolean(
+ prefs::kAutofillAuxiliaryProfilesEnabled, false);
+ }
+}
+
+} // namespace autofill_test
diff --git a/components/autofill/browser/autofill_common_test.h b/components/autofill/browser/autofill_common_test.h
new file mode 100644
index 0000000..858d8da
--- /dev/null
+++ b/components/autofill/browser/autofill_common_test.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_COMMON_TEST_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_COMMON_TEST_H_
+
+class AutofillProfile;
+class CreditCard;
+class Profile;
+
+struct FormFieldData;
+
+// Common utilities shared amongst Autofill tests.
+namespace autofill_test {
+
+// Provides a quick way to populate a FormField with c-strings.
+void CreateTestFormField(const char* label,
+ const char* name,
+ const char* value,
+ const char* type,
+ FormFieldData* field);
+
+// Returns a profile full of dummy info.
+AutofillProfile GetFullProfile();
+
+// A unit testing utility that is common to a number of the Autofill unit
+// tests. |SetProfileInfo| provides a quick way to populate a profile with
+// c-strings.
+void SetProfileInfo(AutofillProfile* profile,
+ const char* first_name, const char* middle_name,
+ const char* last_name, const char* email, const char* company,
+ const char* address1, const char* address2, const char* city,
+ const char* state, const char* zipcode, const char* country,
+ const char* phone);
+
+void SetProfileInfoWithGuid(AutofillProfile* profile,
+ const char* guid, const char* first_name, const char* middle_name,
+ const char* last_name, const char* email, const char* company,
+ const char* address1, const char* address2, const char* city,
+ const char* state, const char* zipcode, const char* country,
+ const char* phone);
+
+// A unit testing utility that is common to a number of the Autofill unit
+// tests. |SetCreditCardInfo| provides a quick way to populate a credit card
+// with c-strings.
+void SetCreditCardInfo(CreditCard* credit_card,
+ const char* name_on_card, const char* card_number,
+ const char* expiration_month, const char* expiration_year);
+
+// TODO(isherman): We should do this automatically for all tests, not manually
+// on a per-test basis: http://crbug.com/57221
+// Disables or mocks out code that would otherwise reach out to system services.
+void DisableSystemServices(Profile* profile);
+
+} // namespace autofill_test
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_COMMON_TEST_H_
diff --git a/components/autofill/browser/autofill_country.cc b/components/autofill/browser/autofill_country.cc
new file mode 100644
index 0000000..e8a8f9e
--- /dev/null
+++ b/components/autofill/browser/autofill_country.cc
@@ -0,0 +1,896 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_country.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <map>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/threading/thread_checker.h"
+#include "base/utf_string_conversions.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "grit/generated_resources.h"
+#include "third_party/icu/public/common/unicode/locid.h"
+#include "third_party/icu/public/common/unicode/uloc.h"
+#include "third_party/icu/public/common/unicode/unistr.h"
+#include "third_party/icu/public/common/unicode/urename.h"
+#include "third_party/icu/public/common/unicode/utypes.h"
+#include "third_party/icu/public/i18n/unicode/coll.h"
+#include "third_party/icu/public/i18n/unicode/ucol.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using content::BrowserThread;
+
+namespace {
+
+// The maximum capacity needed to store a locale up to the country code.
+const size_t kLocaleCapacity =
+ ULOC_LANG_CAPACITY + ULOC_SCRIPT_CAPACITY + ULOC_COUNTRY_CAPACITY + 1;
+
+struct CountryData {
+ int postal_code_label_id;
+ int state_label_id;
+};
+
+struct StaticCountryData {
+ char country_code[3];
+ CountryData country_data;
+};
+
+// Maps country codes to localized label string identifiers.
+const StaticCountryData kCountryData[] = {
+ { "AD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PARISH } },
+ { "AE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_EMIRATE } },
+ { "AF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "AS", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "AT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "AW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "AZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PARISH } },
+ { "BD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "BS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "BT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "BZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "CM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "CX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "CZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "DE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "DJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "DK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "DM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "DO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "DZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "EC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "EE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "EG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "EH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ER", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ES", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ET", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "FI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "FJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "FK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "FM", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "FO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "FR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_COUNTY } },
+ { "GD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GU", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "GW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "GY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "HK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_AREA } },
+ { "HM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "HN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "HR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "HT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "HU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ID", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "IE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_COUNTY } },
+ { "IL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "IM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "IN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "IO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "IQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "IS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "IT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "JE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "JM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PARISH } },
+ { "JO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "JP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PREFECTURE } },
+ { "KE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "KM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "KP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "KY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "KZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "LY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ME", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MH", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "MK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ML", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MP", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "MQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "MX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "MY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "MZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "NI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_DEPARTMENT } },
+ { "NL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_DISTRICT } },
+ { "NU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "NZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "OM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "PG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PR", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "PW", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "PY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "QA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "RE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "RO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "RS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "RU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "RW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "SE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ST", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "SZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "TV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND } },
+ { "TW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_COUNTY } },
+ { "TZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "UA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "UG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "UM", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "US", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "UY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "UZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "VA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "VC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "VE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "VG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "VI", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE } },
+ { "VN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "VU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "WF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "WS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "YE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "YT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ZA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ZM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+ { "ZW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE } },
+};
+
+// A singleton class that encapsulates a map from country codes to country data.
+class CountryDataMap {
+ public:
+ // A const iterator over the wrapped map data.
+ typedef std::map<std::string, CountryData>::const_iterator Iterator;
+
+ static CountryDataMap* GetInstance();
+ static const Iterator Begin();
+ static const Iterator End();
+ static const Iterator Find(const std::string& country_code);
+
+ private:
+ CountryDataMap();
+ friend struct DefaultSingletonTraits<CountryDataMap>;
+
+ std::map<std::string, CountryData> country_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(CountryDataMap);
+};
+
+// static
+CountryDataMap* CountryDataMap::GetInstance() {
+ return Singleton<CountryDataMap>::get();
+}
+
+CountryDataMap::CountryDataMap() {
+ // Add all the countries we have explicit data for.
+ for (size_t i = 0; i < arraysize(kCountryData); ++i) {
+ const StaticCountryData& static_data = kCountryData[i];
+ country_data_.insert(std::make_pair(static_data.country_code,
+ static_data.country_data));
+ }
+
+ // Add any other countries that ICU knows about, falling back to default data
+ // values.
+ for (const char* const* country_pointer = icu::Locale::getISOCountries();
+ *country_pointer;
+ ++country_pointer) {
+ std::string country_code = *country_pointer;
+ if (!country_data_.count(country_code)) {
+ CountryData data = {
+ IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE
+ };
+ country_data_.insert(std::make_pair(country_code, data));
+ }
+ }
+}
+
+const CountryDataMap::Iterator CountryDataMap::Begin() {
+ return GetInstance()->country_data_.begin();
+}
+
+const CountryDataMap::Iterator CountryDataMap::End() {
+ return GetInstance()->country_data_.end();
+}
+
+const CountryDataMap::Iterator CountryDataMap::Find(
+ const std::string& country_code) {
+ return GetInstance()->country_data_.find(country_code);
+}
+
+// A singleton class that encapsulates mappings from country names to their
+// corresponding country codes.
+class CountryNames {
+ public:
+ static CountryNames* GetInstance();
+
+ // Returns the application locale.
+ const std::string ApplicationLocale();
+
+ // Returns the country code corresponding to |country|, which should be a
+ // country code or country name localized to |locale|.
+ const std::string GetCountryCode(const string16& country,
+ const std::string& locale);
+
+ private:
+ CountryNames();
+ ~CountryNames();
+ friend struct DefaultSingletonTraits<CountryNames>;
+
+ // Populates |locales_to_localized_names_| with the mapping of country names
+ // localized to |locale| to their corresponding country codes.
+ void AddLocalizedNamesForLocale(const std::string& locale);
+
+ // Interprets |country_name| as a full country name localized to the given
+ // |locale| and returns the corresponding country code stored in
+ // |locales_to_localized_names_|, or an empty string if there is none.
+ const std::string GetCountryCodeForLocalizedName(const string16& country_name,
+ const std::string& locale);
+
+ // Returns an ICU collator -- i.e. string comparator -- appropriate for the
+ // given |locale|.
+ icu::Collator* GetCollatorForLocale(const std::string& locale);
+
+ // Returns the ICU sort key corresponding to |str| for the given |collator|.
+ // Uses |buffer| as temporary storage, and might resize |buffer| as a side-
+ // effect. |buffer_size| should specify the |buffer|'s size, and is updated if
+ // the |buffer| is resized.
+ const std::string GetSortKey(const icu::Collator& collator,
+ const string16& str,
+ scoped_array<uint8_t>* buffer,
+ int32_t* buffer_size) const;
+
+ // Maps from common country names, including 2- and 3-letter country codes,
+ // to the corresponding 2-letter country codes. The keys are uppercase ASCII
+ // strings.
+ std::map<std::string, std::string> common_names_;
+
+ // The outer map keys are ICU locale identifiers.
+ // The inner maps map from localized country names to their corresponding
+ // country codes. The inner map keys are ICU collation sort keys corresponding
+ // to the target localized country name.
+ std::map<std::string, std::map<std::string, std::string> >
+ locales_to_localized_names_;
+
+ // Maps ICU locale names to their corresponding collators.
+ std::map<std::string, icu::Collator*> collators_;
+
+ // Verifies thread-safety of accesses to the application locale.
+ base::ThreadChecker thread_checker_;
+
+ // Caches the application locale, for thread-safe access.
+ std::string application_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(CountryNames);
+};
+
+// static
+CountryNames* CountryNames::GetInstance() {
+ return Singleton<CountryNames>::get();
+}
+
+const std::string CountryNames::ApplicationLocale() {
+ if (application_locale_.empty()) {
+ // In production code, this class is always constructed on the UI thread, so
+ // the two conditions in the below DCHECK are identical. In test code,
+ // sometimes there is a UI thread, and sometimes there is just the unnamed
+ // main thread. Since this class is a singleton, it needs to support both
+ // cases. Hence, the somewhat strange looking DCHECK below.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
+ thread_checker_.CalledOnValidThread());
+ application_locale_ =
+ content::GetContentClient()->browser()->GetApplicationLocale();
+ }
+
+ return application_locale_;
+}
+
+CountryNames::CountryNames() {
+ // Add 2- and 3-letter ISO country codes.
+ for (CountryDataMap::Iterator it = CountryDataMap::Begin();
+ it != CountryDataMap::End();
+ ++it) {
+ const std::string& country_code = it->first;
+ std::string iso3_country_code =
+ icu::Locale(NULL, country_code.c_str()).getISO3Country();
+
+ common_names_.insert(std::make_pair(country_code, country_code));
+ common_names_.insert(std::make_pair(iso3_country_code, country_code));
+ }
+
+ // Add a few other common synonyms.
+ common_names_.insert(std::make_pair("UNITED STATES OF AMERICA", "US"));
+ common_names_.insert(std::make_pair("U.S.A.", "US"));
+ common_names_.insert(std::make_pair("GREAT BRITAIN", "GB"));
+ common_names_.insert(std::make_pair("UK", "GB"));
+ common_names_.insert(std::make_pair("BRASIL", "BR"));
+ common_names_.insert(std::make_pair("DEUTSCHLAND", "DE"));
+}
+
+CountryNames::~CountryNames() {
+ STLDeleteContainerPairSecondPointers(collators_.begin(),
+ collators_.end());
+}
+
+const std::string CountryNames::GetCountryCode(const string16& country,
+ const std::string& locale) {
+ // First, check common country names, including 2- and 3-letter country codes.
+ std::string country_utf8 = UTF16ToUTF8(StringToUpperASCII(country));
+ std::map<std::string, std::string>::const_iterator result =
+ common_names_.find(country_utf8);
+ if (result != common_names_.end())
+ return result->second;
+
+ // Next, check country names localized to |locale|.
+ std::string country_code = GetCountryCodeForLocalizedName(country, locale);
+ if (!country_code.empty())
+ return country_code;
+
+ // Finally, check country names localized to US English.
+ return GetCountryCodeForLocalizedName(country, "en_US");
+}
+
+void CountryNames::AddLocalizedNamesForLocale(const std::string& locale) {
+ // Nothing to do if we've previously added the localized names for the given
+ // |locale|.
+ if (locales_to_localized_names_.count(locale))
+ return;
+
+ std::map<std::string, std::string> localized_names;
+ const icu::Collator* collator = GetCollatorForLocale(locale);
+ int32_t buffer_size = 1000;
+ scoped_array<uint8_t> buffer(new uint8_t[buffer_size]);
+
+ for (CountryDataMap::Iterator it = CountryDataMap::Begin();
+ it != CountryDataMap::End();
+ ++it) {
+ const std::string& country_code = it->first;
+ string16 country_name = l10n_util::GetDisplayNameForCountry(country_code,
+ locale);
+ std::string sort_key = GetSortKey(*collator,
+ country_name,
+ &buffer,
+ &buffer_size);
+
+ localized_names.insert(std::make_pair(sort_key, country_code));
+ }
+
+ locales_to_localized_names_.insert(std::make_pair(locale, localized_names));
+}
+
+const std::string CountryNames::GetCountryCodeForLocalizedName(
+ const string16& country_name,
+ const std::string& locale) {
+ AddLocalizedNamesForLocale(locale);
+
+ icu::Collator* collator = GetCollatorForLocale(locale);
+
+ // As recommended[1] by ICU, initialize the buffer size to four times the
+ // source string length.
+ // [1] http://userguide.icu-project.org/collation/api#TOC-Examples
+ int32_t buffer_size = country_name.size() * 4;
+ scoped_array<uint8_t> buffer(new uint8_t[buffer_size]);
+ std::string sort_key = GetSortKey(*collator,
+ country_name,
+ &buffer,
+ &buffer_size);
+
+ const std::map<std::string, std::string>& localized_names =
+ locales_to_localized_names_[locale];
+ std::map<std::string, std::string>::const_iterator result =
+ localized_names.find(sort_key);
+
+ if (result != localized_names.end())
+ return result->second;
+
+ return std::string();
+}
+
+icu::Collator* CountryNames::GetCollatorForLocale(const std::string& locale) {
+ if (!collators_.count(locale)) {
+ icu::Locale icu_locale(locale.c_str());
+ UErrorCode ignored = U_ZERO_ERROR;
+ icu::Collator* collator(icu::Collator::createInstance(icu_locale, ignored));
+
+ // Compare case-insensitively and ignoring punctuation.
+ ignored = U_ZERO_ERROR;
+ collator->setAttribute(UCOL_STRENGTH, UCOL_SECONDARY, ignored);
+ ignored = U_ZERO_ERROR;
+ collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, ignored);
+
+ collators_.insert(std::make_pair(locale, collator));
+ }
+
+ return collators_[locale];
+}
+
+const std::string CountryNames::GetSortKey(const icu::Collator& collator,
+ const string16& str,
+ scoped_array<uint8_t>* buffer,
+ int32_t* buffer_size) const {
+ DCHECK(buffer);
+ DCHECK(buffer_size);
+
+ icu::UnicodeString icu_str(str.c_str(), str.length());
+ int32_t expected_size = collator.getSortKey(icu_str, buffer->get(),
+ *buffer_size);
+ if (expected_size > *buffer_size) {
+ // If there wasn't enough space, grow the buffer and try again.
+ *buffer_size = expected_size;
+ buffer->reset(new uint8_t[*buffer_size]);
+ DCHECK(buffer->get());
+
+ expected_size = collator.getSortKey(icu_str, buffer->get(), *buffer_size);
+ DCHECK_EQ(*buffer_size, expected_size);
+ }
+
+ return std::string(reinterpret_cast<const char*>(buffer->get()));
+}
+
+} // namespace
+
+AutofillCountry::AutofillCountry(const std::string& country_code,
+ const std::string& locale) {
+ const CountryDataMap::Iterator result = CountryDataMap::Find(country_code);
+ DCHECK(result != CountryDataMap::End());
+ const CountryData& data = result->second;
+
+ country_code_ = country_code;
+ name_ = l10n_util::GetDisplayNameForCountry(country_code, locale);
+ postal_code_label_ = l10n_util::GetStringUTF16(data.postal_code_label_id);
+ state_label_ = l10n_util::GetStringUTF16(data.state_label_id);
+}
+
+AutofillCountry::~AutofillCountry() {
+}
+
+// static
+void AutofillCountry::GetAvailableCountries(
+ std::vector<std::string>* country_codes) {
+ DCHECK(country_codes);
+
+ for (CountryDataMap::Iterator it = CountryDataMap::Begin();
+ it != CountryDataMap::End();
+ ++it) {
+ country_codes->push_back(it->first);
+ }
+}
+
+// static
+const std::string AutofillCountry::CountryCodeForLocale(
+ const std::string& locale) {
+ // Add likely subtags to the locale. In particular, add any likely country
+ // subtags -- e.g. for locales like "ru" that only include the language.
+ std::string likely_locale;
+ UErrorCode error_ignored = U_ZERO_ERROR;
+ uloc_addLikelySubtags(locale.c_str(),
+ WriteInto(&likely_locale, kLocaleCapacity),
+ kLocaleCapacity,
+ &error_ignored);
+
+ // Extract the country code.
+ std::string country_code = icu::Locale(likely_locale.c_str()).getCountry();
+
+ // Default to the United States if we have no better guess.
+ if (CountryDataMap::Find(country_code) == CountryDataMap::End())
+ return "US";
+
+ return country_code;
+}
+
+// static
+const std::string AutofillCountry::GetCountryCode(const string16& country,
+ const std::string& locale) {
+ return CountryNames::GetInstance()->GetCountryCode(country, locale);
+}
+
+// static
+const std::string AutofillCountry::ApplicationLocale() {
+ return CountryNames::GetInstance()->ApplicationLocale();
+}
+
+AutofillCountry::AutofillCountry(const std::string& country_code,
+ const string16& name,
+ const string16& postal_code_label,
+ const string16& state_label)
+ : country_code_(country_code),
+ name_(name),
+ postal_code_label_(postal_code_label),
+ state_label_(state_label) {
+}
diff --git a/components/autofill/browser/autofill_country.h b/components/autofill/browser/autofill_country.h
new file mode 100644
index 0000000..2dcaef6
--- /dev/null
+++ b/components/autofill/browser/autofill_country.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_COUNTRY_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_COUNTRY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+
+// Stores data associated with a country. Strings are localized to the app
+// locale.
+class AutofillCountry {
+ public:
+ // Returns country data corresponding to the two-letter ISO code
+ // |country_code|.
+ AutofillCountry(const std::string& country_code, const std::string& locale);
+ ~AutofillCountry();
+
+ // Fills |country_codes| with a list of the available countries' codes.
+ static void GetAvailableCountries(
+ std::vector<std::string>* country_codes);
+
+ // Returns the likely country code for |locale|, or "US" as a fallback if no
+ // mapping from the locale is available.
+ static const std::string CountryCodeForLocale(const std::string& locale);
+
+ // Returns the country code corresponding to |country|, which should be a
+ // country code or country name localized to |locale|. This function can
+ // be expensive so use judiciously.
+ static const std::string GetCountryCode(const string16& country,
+ const std::string& locale);
+
+ // Returns the application locale.
+ // The first time this is called, it should be called from the UI thread.
+ // Once [ http://crbug.com/100845 ] is fixed, this method should *only* be
+ // called from the UI thread.
+ static const std::string ApplicationLocale();
+
+ const std::string country_code() const { return country_code_; }
+ const string16 name() const { return name_; }
+ const string16 postal_code_label() const { return postal_code_label_; }
+ const string16 state_label() const { return state_label_; }
+
+ private:
+ AutofillCountry(const std::string& country_code,
+ const string16& name,
+ const string16& postal_code_label,
+ const string16& state_label);
+
+ // The two-letter ISO-3166 country code.
+ std::string country_code_;
+
+ // The country's name, localized to the app locale.
+ string16 name_;
+
+ // The localized label for the postal code (or zip code) field.
+ string16 postal_code_label_;
+
+ // The localized label for the state (or province, district, etc.) field.
+ string16 state_label_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillCountry);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_COUNTRY_H_
diff --git a/components/autofill/browser/autofill_country_unittest.cc b/components/autofill/browser/autofill_country_unittest.cc
new file mode 100644
index 0000000..3615129
--- /dev/null
+++ b/components/autofill/browser/autofill_country_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 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 <string>
+
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test the constructor and accessors
+TEST(AutofillCountryTest, AutofillCountry) {
+ AutofillCountry united_states_en("US", "en_US");
+ EXPECT_EQ("US", united_states_en.country_code());
+ EXPECT_EQ(ASCIIToUTF16("United States"), united_states_en.name());
+ EXPECT_EQ(ASCIIToUTF16("ZIP code"), united_states_en.postal_code_label());
+ EXPECT_EQ(ASCIIToUTF16("State"), united_states_en.state_label());
+
+ AutofillCountry united_states_es("US", "es");
+ EXPECT_EQ("US", united_states_es.country_code());
+ EXPECT_EQ(ASCIIToUTF16("Estados Unidos"), united_states_es.name());
+
+ AutofillCountry canada_en("CA", "en_US");
+ EXPECT_EQ("CA", canada_en.country_code());
+ EXPECT_EQ(ASCIIToUTF16("Canada"), canada_en.name());
+ EXPECT_EQ(ASCIIToUTF16("Postal code"), canada_en.postal_code_label());
+ EXPECT_EQ(ASCIIToUTF16("Province"), canada_en.state_label());
+
+ AutofillCountry canada_hu("CA", "hu");
+ EXPECT_EQ("CA", canada_hu.country_code());
+ EXPECT_EQ(ASCIIToUTF16("Kanada"), canada_hu.name());
+}
+
+// Test locale to country code mapping.
+TEST(AutofillCountryTest, CountryCodeForLocale) {
+ EXPECT_EQ("US", AutofillCountry::CountryCodeForLocale("en_US"));
+ EXPECT_EQ("CA", AutofillCountry::CountryCodeForLocale("fr_CA"));
+ EXPECT_EQ("FR", AutofillCountry::CountryCodeForLocale("fr"));
+ EXPECT_EQ("US", AutofillCountry::CountryCodeForLocale("Unknown"));
+ // "es-419" isn't associated with a country. See base/l10n/l10n_util.cc
+ // for details about this locale. Default to US.
+ EXPECT_EQ("US", AutofillCountry::CountryCodeForLocale("es-419"));
+}
+
+// Test mapping of localized country names to country codes.
+TEST(AutofillCountryTest, GetCountryCode) {
+ // Basic mapping
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("United States"),
+ "en_US"));
+ EXPECT_EQ("CA", AutofillCountry::GetCountryCode(ASCIIToUTF16("Canada"),
+ "en_US"));
+
+ // Case-insensitive mapping
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("united states"),
+ "en_US"));
+
+ // Country codes should map to themselves, independent of locale.
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("US"), "en_US"));
+ EXPECT_EQ("HU", AutofillCountry::GetCountryCode(ASCIIToUTF16("hu"), "en_US"));
+ EXPECT_EQ("CA", AutofillCountry::GetCountryCode(ASCIIToUTF16("CA"), "fr_CA"));
+ EXPECT_EQ("MX", AutofillCountry::GetCountryCode(ASCIIToUTF16("mx"), "fr_CA"));
+
+ // Basic synonyms
+ EXPECT_EQ("US",
+ AutofillCountry::GetCountryCode(
+ ASCIIToUTF16("United States of America"), "en_US"));
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("USA"),
+ "en_US"));
+
+ // Other locales
+ EXPECT_EQ("US",
+ AutofillCountry::GetCountryCode(ASCIIToUTF16("Estados Unidos"),
+ "es"));
+ EXPECT_EQ("IT", AutofillCountry::GetCountryCode(ASCIIToUTF16("Italia"),
+ "it"));
+ EXPECT_EQ("DE", AutofillCountry::GetCountryCode(ASCIIToUTF16("duitsland"),
+ "nl"));
+
+ // Should fall back to "en_US" locale if all else fails.
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("United States"),
+ "es"));
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("united states"),
+ "es"));
+ EXPECT_EQ("US", AutofillCountry::GetCountryCode(ASCIIToUTF16("USA"), "es"));
+}
diff --git a/components/autofill/browser/autofill_download.cc b/components/autofill/browser/autofill_download.cc
new file mode 100644
index 0000000..6377184
--- /dev/null
+++ b/components/autofill/browser/autofill_download.cc
@@ -0,0 +1,340 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_download.h"
+
+#include <algorithm>
+#include <ostream>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/prefs/pref_service.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "components/autofill/browser/autofill_download_url.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/autofill_xml_parser.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/common/autofill_pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_fetcher.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
+
+using content::BrowserContext;
+
+namespace {
+const char kAutofillQueryServerNameStartInHeader[] = "GFE/";
+
+const size_t kMaxFormCacheSize = 16;
+
+// Log the contents of the upload request
+static void LogUploadRequest(const GURL& url, const std::string& signature,
+ const std::string& form_xml) {
+ VLOG(2) << url;
+ VLOG(2) << signature;
+ VLOG(2) << form_xml;
+}
+
+};
+
+// static
+std::string AutofillDownloadManager::AutofillRequestTypeToString(
+ const AutofillRequestType type) {
+ switch (type) {
+ case AutofillDownloadManager::REQUEST_QUERY:
+ return "query";
+ case AutofillDownloadManager::REQUEST_UPLOAD:
+ return "upload";
+ }
+ return std::string();
+}
+
+struct AutofillDownloadManager::FormRequestData {
+ std::vector<std::string> form_signatures;
+ AutofillRequestType request_type;
+};
+
+AutofillDownloadManager::AutofillDownloadManager(BrowserContext* context,
+ Observer* observer)
+ : browser_context_(context),
+ observer_(observer),
+ max_form_cache_size_(kMaxFormCacheSize),
+ next_query_request_(base::Time::Now()),
+ next_upload_request_(base::Time::Now()),
+ positive_upload_rate_(0),
+ negative_upload_rate_(0),
+ fetcher_id_for_unittest_(0) {
+ DCHECK(observer_);
+ PrefService* preferences = components::UserPrefs::Get(browser_context_);
+ positive_upload_rate_ =
+ preferences->GetDouble(prefs::kAutofillPositiveUploadRate);
+ negative_upload_rate_ =
+ preferences->GetDouble(prefs::kAutofillNegativeUploadRate);
+}
+
+AutofillDownloadManager::~AutofillDownloadManager() {
+ STLDeleteContainerPairFirstPointers(url_fetchers_.begin(),
+ url_fetchers_.end());
+}
+
+bool AutofillDownloadManager::StartQueryRequest(
+ const std::vector<FormStructure*>& forms,
+ const AutofillMetrics& metric_logger) {
+ if (next_query_request_ > base::Time::Now()) {
+ // We are in back-off mode: do not do the request.
+ return false;
+ }
+ std::string form_xml;
+ FormRequestData request_data;
+ if (!FormStructure::EncodeQueryRequest(forms, &request_data.form_signatures,
+ &form_xml)) {
+ return false;
+ }
+
+ request_data.request_type = AutofillDownloadManager::REQUEST_QUERY;
+ metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_SENT);
+
+ std::string query_data;
+ if (CheckCacheForQueryRequest(request_data.form_signatures, &query_data)) {
+ DVLOG(1) << "AutofillDownloadManager: query request has been retrieved from"
+ << "the cache";
+ observer_->OnLoadedServerPredictions(query_data);
+ return true;
+ }
+
+ return StartRequest(form_xml, request_data);
+}
+
+bool AutofillDownloadManager::StartUploadRequest(
+ const FormStructure& form,
+ bool form_was_autofilled,
+ const FieldTypeSet& available_field_types) {
+ std::string form_xml;
+ if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled,
+ &form_xml))
+ return false;
+
+ LogUploadRequest(form.source_url(), form.FormSignature(), form_xml);
+
+ if (next_upload_request_ > base::Time::Now()) {
+ // We are in back-off mode: do not do the request.
+ DVLOG(1) << "AutofillDownloadManager: Upload request is throttled.";
+ return false;
+ }
+
+ // Flip a coin to see if we should upload this form.
+ double upload_rate = form_was_autofilled ? GetPositiveUploadRate() :
+ GetNegativeUploadRate();
+ if (form.upload_required() == UPLOAD_NOT_REQUIRED ||
+ (form.upload_required() == USE_UPLOAD_RATES &&
+ base::RandDouble() > upload_rate)) {
+ DVLOG(1) << "AutofillDownloadManager: Upload request is ignored.";
+ // If we ever need notification that upload was skipped, add it here.
+ return false;
+ }
+
+ FormRequestData request_data;
+ request_data.form_signatures.push_back(form.FormSignature());
+ request_data.request_type = AutofillDownloadManager::REQUEST_UPLOAD;
+
+ return StartRequest(form_xml, request_data);
+}
+
+double AutofillDownloadManager::GetPositiveUploadRate() const {
+ return positive_upload_rate_;
+}
+
+double AutofillDownloadManager::GetNegativeUploadRate() const {
+ return negative_upload_rate_;
+}
+
+void AutofillDownloadManager::SetPositiveUploadRate(double rate) {
+ if (rate == positive_upload_rate_)
+ return;
+ positive_upload_rate_ = rate;
+ DCHECK_GE(rate, 0.0);
+ DCHECK_LE(rate, 1.0);
+ PrefService* preferences = components::UserPrefs::Get(browser_context_);
+ preferences->SetDouble(prefs::kAutofillPositiveUploadRate, rate);
+}
+
+void AutofillDownloadManager::SetNegativeUploadRate(double rate) {
+ if (rate == negative_upload_rate_)
+ return;
+ negative_upload_rate_ = rate;
+ DCHECK_GE(rate, 0.0);
+ DCHECK_LE(rate, 1.0);
+ PrefService* preferences = components::UserPrefs::Get(browser_context_);
+ preferences->SetDouble(prefs::kAutofillNegativeUploadRate, rate);
+}
+
+bool AutofillDownloadManager::StartRequest(
+ const std::string& form_xml,
+ const FormRequestData& request_data) {
+ net::URLRequestContextGetter* request_context =
+ browser_context_->GetRequestContext();
+ DCHECK(request_context);
+ GURL request_url;
+ if (request_data.request_type == AutofillDownloadManager::REQUEST_QUERY)
+ request_url = autofill::GetAutofillQueryUrl();
+ else
+ request_url = autofill::GetAutofillUploadUrl();
+
+ // Id is ignored for regular chrome, in unit test id's for fake fetcher
+ // factory will be 0, 1, 2, ...
+ net::URLFetcher* fetcher = net::URLFetcher::Create(
+ fetcher_id_for_unittest_++, request_url, net::URLFetcher::POST,
+ this);
+ url_fetchers_[fetcher] = request_data;
+ fetcher->SetAutomaticallyRetryOn5xx(false);
+ fetcher->SetRequestContext(request_context);
+ fetcher->SetUploadData("text/plain", form_xml);
+ fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES);
+ fetcher->Start();
+
+ DVLOG(1) << "Sending AutofillDownloadManager "
+ << AutofillRequestTypeToString(request_data.request_type)
+ << " request: " << form_xml;
+
+ return true;
+}
+
+void AutofillDownloadManager::CacheQueryRequest(
+ const std::vector<std::string>& forms_in_query,
+ const std::string& query_data) {
+ std::string signature = GetCombinedSignature(forms_in_query);
+ for (QueryRequestCache::iterator it = cached_forms_.begin();
+ it != cached_forms_.end(); ++it) {
+ if (it->first == signature) {
+ // We hit the cache, move to the first position and return.
+ std::pair<std::string, std::string> data = *it;
+ cached_forms_.erase(it);
+ cached_forms_.push_front(data);
+ return;
+ }
+ }
+ std::pair<std::string, std::string> data;
+ data.first = signature;
+ data.second = query_data;
+ cached_forms_.push_front(data);
+ while (cached_forms_.size() > max_form_cache_size_)
+ cached_forms_.pop_back();
+}
+
+bool AutofillDownloadManager::CheckCacheForQueryRequest(
+ const std::vector<std::string>& forms_in_query,
+ std::string* query_data) const {
+ std::string signature = GetCombinedSignature(forms_in_query);
+ for (QueryRequestCache::const_iterator it = cached_forms_.begin();
+ it != cached_forms_.end(); ++it) {
+ if (it->first == signature) {
+ // We hit the cache, fill the data and return.
+ *query_data = it->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+std::string AutofillDownloadManager::GetCombinedSignature(
+ const std::vector<std::string>& forms_in_query) const {
+ size_t total_size = forms_in_query.size();
+ for (size_t i = 0; i < forms_in_query.size(); ++i)
+ total_size += forms_in_query[i].length();
+ std::string signature;
+
+ signature.reserve(total_size);
+
+ for (size_t i = 0; i < forms_in_query.size(); ++i) {
+ if (i)
+ signature.append(",");
+ signature.append(forms_in_query[i]);
+ }
+ return signature;
+}
+
+void AutofillDownloadManager::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ std::map<net::URLFetcher *, FormRequestData>::iterator it =
+ url_fetchers_.find(const_cast<net::URLFetcher*>(source));
+ if (it == url_fetchers_.end()) {
+ // Looks like crash on Mac is possibly caused with callback entering here
+ // with unknown fetcher when network is refreshed.
+ return;
+ }
+ std::string type_of_request(
+ AutofillRequestTypeToString(it->second.request_type));
+ const int kHttpResponseOk = 200;
+ const int kHttpInternalServerError = 500;
+ const int kHttpBadGateway = 502;
+ const int kHttpServiceUnavailable = 503;
+
+ CHECK(it->second.form_signatures.size());
+ if (source->GetResponseCode() != kHttpResponseOk) {
+ bool back_off = false;
+ std::string server_header;
+ switch (source->GetResponseCode()) {
+ case kHttpBadGateway:
+ if (!source->GetResponseHeaders()->EnumerateHeader(NULL, "server",
+ &server_header) ||
+ StartsWithASCII(server_header.c_str(),
+ kAutofillQueryServerNameStartInHeader,
+ false) != 0)
+ break;
+ // Bad gateway was received from Autofill servers. Fall through to back
+ // off.
+ case kHttpInternalServerError:
+ case kHttpServiceUnavailable:
+ back_off = true;
+ break;
+ }
+
+ if (back_off) {
+ base::Time back_off_time(base::Time::Now() + source->GetBackoffDelay());
+ if (it->second.request_type == AutofillDownloadManager::REQUEST_QUERY) {
+ next_query_request_ = back_off_time;
+ } else {
+ next_upload_request_ = back_off_time;
+ }
+ }
+
+ DVLOG(1) << "AutofillDownloadManager: " << type_of_request
+ << " request has failed with response "
+ << source->GetResponseCode();
+ observer_->OnServerRequestError(it->second.form_signatures[0],
+ it->second.request_type,
+ source->GetResponseCode());
+ } else {
+ std::string response_body;
+ source->GetResponseAsString(&response_body);
+ DVLOG(1) << "AutofillDownloadManager: " << type_of_request
+ << " request has succeeded with response body: "
+ << response_body;
+ if (it->second.request_type == AutofillDownloadManager::REQUEST_QUERY) {
+ CacheQueryRequest(it->second.form_signatures, response_body);
+ observer_->OnLoadedServerPredictions(response_body);
+ } else {
+ double new_positive_upload_rate = 0;
+ double new_negative_upload_rate = 0;
+ AutofillUploadXmlParser parse_handler(&new_positive_upload_rate,
+ &new_negative_upload_rate);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(response_body.data(), response_body.length(), true);
+ if (parse_handler.succeeded()) {
+ SetPositiveUploadRate(new_positive_upload_rate);
+ SetNegativeUploadRate(new_negative_upload_rate);
+ }
+
+ observer_->OnUploadedPossibleFieldTypes();
+ }
+ }
+ delete it->first;
+ url_fetchers_.erase(it);
+}
diff --git a/components/autofill/browser/autofill_download.h b/components/autofill/browser/autofill_download.h
new file mode 100644
index 0000000..18a237c
--- /dev/null
+++ b/components/autofill/browser/autofill_download.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_DOWNLOAD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_DOWNLOAD_H_
+
+#include <stddef.h>
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/time.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+class AutofillMetrics;
+class FormStructure;
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace net {
+class URLFetcher;
+} // namespace net
+
+// Handles getting and updating Autofill heuristics.
+class AutofillDownloadManager : public net::URLFetcherDelegate {
+ public:
+ enum AutofillRequestType {
+ REQUEST_QUERY,
+ REQUEST_UPLOAD,
+ };
+
+ // An interface used to notify clients of AutofillDownloadManager.
+ class Observer {
+ public:
+ // Called when field type predictions are successfully received from the
+ // server. |response_xml| contains the server response.
+ virtual void OnLoadedServerPredictions(const std::string& response_xml) = 0;
+
+ // These notifications are used to help with testing.
+ // Called when heuristic either successfully considered for upload and
+ // not send or uploaded.
+ virtual void OnUploadedPossibleFieldTypes() {}
+ // Called when there was an error during the request.
+ // |form_signature| - the signature of the requesting form.
+ // |request_type| - type of request that failed.
+ // |http_error| - HTTP error code.
+ virtual void OnServerRequestError(const std::string& form_signature,
+ AutofillRequestType request_type,
+ int http_error) {}
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ // |observer| - observer to notify on successful completion or error.
+ AutofillDownloadManager(content::BrowserContext* context,
+ Observer* observer);
+ virtual ~AutofillDownloadManager();
+
+ // Starts a query request to Autofill servers. The observer is called with the
+ // list of the fields of all requested forms.
+ // |forms| - array of forms aggregated in this request.
+ bool StartQueryRequest(const std::vector<FormStructure*>& forms,
+ const AutofillMetrics& metric_logger);
+
+ // Starts an upload request for the given |form|, unless throttled by the
+ // server. The probability of the request going over the wire is
+ // GetPositiveUploadRate() if |form_was_autofilled| is true, or
+ // GetNegativeUploadRate() otherwise. The observer will be called even if
+ // there was no actual trip over the wire.
+ // |available_field_types| should contain the types for which we have data
+ // stored on the local client.
+ bool StartUploadRequest(const FormStructure& form,
+ bool form_was_autofilled,
+ const FieldTypeSet& available_field_types);
+
+ private:
+ friend class AutofillDownloadTest;
+ FRIEND_TEST_ALL_PREFIXES(AutofillDownloadTest, QueryAndUploadTest);
+
+ static std::string AutofillRequestTypeToString(const AutofillRequestType);
+
+ struct FormRequestData;
+ typedef std::list<std::pair<std::string, std::string> > QueryRequestCache;
+
+ // Initiates request to Autofill servers to download/upload heuristics.
+ // |form_xml| - form structure XML to upload/download.
+ // |request_data| - form signature hash(es) and indicator if it was a query.
+ // |request_data.query| - if true the data is queried and observer notified
+ // with new data, if available. If false heuristic data is uploaded to our
+ // servers.
+ bool StartRequest(const std::string& form_xml,
+ const FormRequestData& request_data);
+
+ // Each request is page visited. We store last |max_form_cache_size|
+ // request, to avoid going over the wire. Set to 16 in constructor. Warning:
+ // the search is linear (newest first), so do not make the constant very big.
+ void set_max_form_cache_size(size_t max_form_cache_size) {
+ max_form_cache_size_ = max_form_cache_size;
+ }
+
+ // Caches query request. |forms_in_query| is a vector of form signatures in
+ // the query. |query_data| is the successful data returned over the wire.
+ void CacheQueryRequest(const std::vector<std::string>& forms_in_query,
+ const std::string& query_data);
+ // Returns true if query is in the cache, while filling |query_data|, false
+ // otherwise. |forms_in_query| is a vector of form signatures in the query.
+ bool CheckCacheForQueryRequest(const std::vector<std::string>& forms_in_query,
+ std::string* query_data) const;
+ // Concatenates |forms_in_query| into one signature.
+ std::string GetCombinedSignature(
+ const std::vector<std::string>& forms_in_query) const;
+
+ // net::URLFetcherDelegate implementation:
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // Probability of the form upload. Between 0 (no upload) and 1 (upload all).
+ // GetPositiveUploadRate() is for matched forms,
+ // GetNegativeUploadRate() for non-matched.
+ double GetPositiveUploadRate() const;
+ double GetNegativeUploadRate() const;
+ void SetPositiveUploadRate(double rate);
+ void SetNegativeUploadRate(double rate);
+
+ // The pointer value is const, so this can only be set in the
+ // constructor. Must not be null.
+ content::BrowserContext* const browser_context_; // WEAK
+
+ // The observer to notify when server predictions are successfully received.
+ // The pointer value is const, so this can only be set in the constructor.
+ // Must not be null.
+ AutofillDownloadManager::Observer* const observer_; // WEAK
+
+ // For each requested form for both query and upload we create a separate
+ // request and save its info. As url fetcher is identified by its address
+ // we use a map between fetchers and info.
+ std::map<net::URLFetcher*, FormRequestData> url_fetchers_;
+
+ // Cached QUERY requests.
+ QueryRequestCache cached_forms_;
+ size_t max_form_cache_size_;
+
+ // Time when next query/upload requests are allowed. If 50x HTTP received,
+ // exponential back off is initiated, so this times will be in the future
+ // for awhile.
+ base::Time next_query_request_;
+ base::Time next_upload_request_;
+
+ // |positive_upload_rate_| is for matched forms,
+ // |negative_upload_rate_| for non matched.
+ double positive_upload_rate_;
+ double negative_upload_rate_;
+
+ // Needed for unit-test.
+ int fetcher_id_for_unittest_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_DOWNLOAD_H_
diff --git a/components/autofill/browser/autofill_download_unittest.cc b/components/autofill/browser/autofill_download_unittest.cc
new file mode 100644
index 0000000..3218bcc
--- /dev/null
+++ b/components/autofill/browser/autofill_download_unittest.cc
@@ -0,0 +1,505 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <list>
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/test/test_timeouts.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autofill_download.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/common/form_data.h"
+#include "content/public/test/test_browser_thread.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+
+using content::BrowserThread;
+using WebKit::WebInputElement;
+
+namespace {
+
+class MockAutofillMetrics : public AutofillMetrics {
+ public:
+ MockAutofillMetrics() {}
+ MOCK_CONST_METHOD1(LogServerQueryMetric, void(ServerQueryMetric metric));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics);
+};
+
+// Call |fetcher->OnURLFetchComplete()| as the URLFetcher would when
+// a response is received. Params allow caller to set fake status.
+void FakeOnURLFetchComplete(net::TestURLFetcher* fetcher,
+ int response_code,
+ const std::string& response_body) {
+ fetcher->set_url(GURL());
+ fetcher->set_status(net::URLRequestStatus());
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response_body);
+
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+} // namespace
+
+// This tests AutofillDownloadManager. AutofillDownloadTest implements
+// AutofillDownloadManager::Observer and creates an instance of
+// AutofillDownloadManager. Then it records responses to different initiated
+// requests, which are verified later. To mock network requests
+// TestURLFetcherFactory is used, which creates URLFetchers that do not
+// go over the wire, but allow calling back HTTP responses directly.
+// The responses in test are out of order and verify: successful query request,
+// successful upload request, failed upload request.
+class AutofillDownloadTest : public AutofillDownloadManager::Observer,
+ public testing::Test {
+ public:
+ AutofillDownloadTest()
+ : download_manager_(&profile_, this),
+ io_thread_(BrowserThread::IO) {
+ }
+
+ virtual void SetUp() {
+ io_thread_.StartIOThread();
+ profile_.CreateRequestContext();
+ }
+
+ virtual void TearDown() {
+ profile_.ResetRequestContext();
+ io_thread_.Stop();
+ }
+
+ void LimitCache(size_t cache_size) {
+ download_manager_.set_max_form_cache_size(cache_size);
+ }
+
+ // AutofillDownloadManager::Observer implementation.
+ virtual void OnLoadedServerPredictions(
+ const std::string& response_xml) OVERRIDE {
+ ResponseData response;
+ response.response = response_xml;
+ response.type_of_response = QUERY_SUCCESSFULL;
+ responses_.push_back(response);
+ }
+
+ virtual void OnUploadedPossibleFieldTypes() OVERRIDE {
+ ResponseData response;
+ response.type_of_response = UPLOAD_SUCCESSFULL;
+ responses_.push_back(response);
+ }
+
+ virtual void OnServerRequestError(
+ const std::string& form_signature,
+ AutofillDownloadManager::AutofillRequestType request_type,
+ int http_error) OVERRIDE {
+ ResponseData response;
+ response.signature = form_signature;
+ response.error = http_error;
+ response.type_of_response =
+ request_type == AutofillDownloadManager::REQUEST_QUERY ?
+ REQUEST_QUERY_FAILED : REQUEST_UPLOAD_FAILED;
+ responses_.push_back(response);
+ }
+
+ enum ResponseType {
+ QUERY_SUCCESSFULL,
+ UPLOAD_SUCCESSFULL,
+ REQUEST_QUERY_FAILED,
+ REQUEST_UPLOAD_FAILED,
+ };
+
+ struct ResponseData {
+ ResponseType type_of_response;
+ int error;
+ std::string signature;
+ std::string response;
+
+ ResponseData() : type_of_response(REQUEST_QUERY_FAILED), error(0) {}
+ };
+ std::list<ResponseData> responses_;
+
+ TestingProfile profile_;
+ AutofillDownloadManager download_manager_;
+
+ private:
+ // The profile's request context must be released on the IO thread.
+ content::TestBrowserThread io_thread_;
+};
+
+TEST_F(AutofillDownloadTest, QueryAndUploadTest) {
+ MessageLoopForUI message_loop;
+ // Create and register factory.
+ net::TestURLFetcherFactory factory;
+
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("email");
+ field.name = ASCIIToUTF16("email");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("email2");
+ field.name = ASCIIToUTF16("email2");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ FormStructure *form_structure = new FormStructure(form, std::string());
+ ScopedVector<FormStructure> form_structures;
+ form_structures.push_back(form_structure);
+
+ form.fields.clear();
+
+ field.label = ASCIIToUTF16("address");
+ field.name = ASCIIToUTF16("address");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("address2");
+ field.name = ASCIIToUTF16("address2");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("city");
+ field.name = ASCIIToUTF16("city");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure = new FormStructure(form, std::string());
+ form_structures.push_back(form_structure);
+
+ // Request with id 0.
+ MockAutofillMetrics mock_metric_logger;
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get(),
+ mock_metric_logger));
+ // Set upload to 100% so requests happen.
+ download_manager_.SetPositiveUploadRate(1.0);
+ download_manager_.SetNegativeUploadRate(1.0);
+ // Request with id 1.
+ EXPECT_TRUE(download_manager_.StartUploadRequest(
+ *(form_structures[0]), true, FieldTypeSet()));
+ // Request with id 2.
+ EXPECT_TRUE(download_manager_.StartUploadRequest(
+ *(form_structures[1]), false, FieldTypeSet()));
+
+ const char *responses[] = {
+ "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"3\" />"
+ "<field autofilltype=\"5\" />"
+ "<field autofilltype=\"9\" />"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"30\" />"
+ "<field autofilltype=\"31\" />"
+ "<field autofilltype=\"33\" />"
+ "</autofillqueryresponse>",
+ "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"/>",
+ "<html></html>",
+ };
+
+ // Return them out of sequence.
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 200, std::string(responses[1]));
+
+ // After that upload rates would be adjusted to 0.5/0.3
+ EXPECT_DOUBLE_EQ(0.5, download_manager_.GetPositiveUploadRate());
+ EXPECT_DOUBLE_EQ(0.3, download_manager_.GetNegativeUploadRate());
+
+ fetcher = factory.GetFetcherByID(2);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 404, std::string(responses[2]));
+
+ fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 200, std::string(responses[0]));
+ EXPECT_EQ(static_cast<size_t>(3), responses_.size());
+
+ EXPECT_EQ(AutofillDownloadTest::UPLOAD_SUCCESSFULL,
+ responses_.front().type_of_response);
+ EXPECT_EQ(0, responses_.front().error);
+ EXPECT_EQ(std::string(), responses_.front().signature);
+ // Expected response on non-query request is an empty string.
+ EXPECT_EQ(std::string(), responses_.front().response);
+ responses_.pop_front();
+
+ EXPECT_EQ(AutofillDownloadTest::REQUEST_UPLOAD_FAILED,
+ responses_.front().type_of_response);
+ EXPECT_EQ(404, responses_.front().error);
+ EXPECT_EQ(form_structures[1]->FormSignature(),
+ responses_.front().signature);
+ // Expected response on non-query request is an empty string.
+ EXPECT_EQ(std::string(), responses_.front().response);
+ responses_.pop_front();
+
+ EXPECT_EQ(responses_.front().type_of_response,
+ AutofillDownloadTest::QUERY_SUCCESSFULL);
+ EXPECT_EQ(0, responses_.front().error);
+ EXPECT_EQ(std::string(), responses_.front().signature);
+ EXPECT_EQ(responses[0], responses_.front().response);
+ responses_.pop_front();
+
+ // Set upload to 0% so no new requests happen.
+ download_manager_.SetPositiveUploadRate(0.0);
+ download_manager_.SetNegativeUploadRate(0.0);
+ // No actual requests for the next two calls, as we set upload rate to 0%.
+ EXPECT_FALSE(download_manager_.StartUploadRequest(
+ *(form_structures[0]), true, FieldTypeSet()));
+ EXPECT_FALSE(download_manager_.StartUploadRequest(
+ *(form_structures[1]), false, FieldTypeSet()));
+ fetcher = factory.GetFetcherByID(3);
+ EXPECT_EQ(NULL, fetcher);
+
+ // Modify form structures to miss the cache.
+ field.label = ASCIIToUTF16("Address line 2");
+ field.name = ASCIIToUTF16("address2");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+ form_structure = new FormStructure(form, std::string());
+ form_structures.push_back(form_structure);
+
+ // Request with id 3.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures.get(),
+ mock_metric_logger));
+ fetcher = factory.GetFetcherByID(3);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_backoff_delay(TestTimeouts::action_max_timeout());
+ FakeOnURLFetchComplete(fetcher, 500, std::string(responses[0]));
+
+ EXPECT_EQ(AutofillDownloadTest::REQUEST_QUERY_FAILED,
+ responses_.front().type_of_response);
+ EXPECT_EQ(500, responses_.front().error);
+ // Expected response on non-query request is an empty string.
+ EXPECT_EQ(std::string(), responses_.front().response);
+ responses_.pop_front();
+
+ // Query requests should be ignored for the next 10 seconds.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(0);
+ EXPECT_FALSE(download_manager_.StartQueryRequest(form_structures.get(),
+ mock_metric_logger));
+ fetcher = factory.GetFetcherByID(4);
+ EXPECT_EQ(NULL, fetcher);
+
+ // Set upload required to true so requests happen.
+ form_structures[0]->upload_required_ = UPLOAD_REQUIRED;
+ // Request with id 4.
+ EXPECT_TRUE(download_manager_.StartUploadRequest(
+ *(form_structures[0]), true, FieldTypeSet()));
+ fetcher = factory.GetFetcherByID(4);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_backoff_delay(TestTimeouts::action_max_timeout());
+ FakeOnURLFetchComplete(fetcher, 503, std::string(responses[2]));
+ EXPECT_EQ(AutofillDownloadTest::REQUEST_UPLOAD_FAILED,
+ responses_.front().type_of_response);
+ EXPECT_EQ(503, responses_.front().error);
+ responses_.pop_front();
+
+ // Upload requests should be ignored for the next 10 seconds.
+ EXPECT_FALSE(download_manager_.StartUploadRequest(
+ *(form_structures[0]), true, FieldTypeSet()));
+ fetcher = factory.GetFetcherByID(5);
+ EXPECT_EQ(NULL, fetcher);
+}
+
+TEST_F(AutofillDownloadTest, CacheQueryTest) {
+ MessageLoopForUI message_loop;
+ // Create and register factory.
+ net::TestURLFetcherFactory factory;
+
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ form.fields.push_back(field);
+
+ FormStructure *form_structure = new FormStructure(form, std::string());
+ ScopedVector<FormStructure> form_structures0;
+ form_structures0.push_back(form_structure);
+
+ // Add a slightly different form, which should result in a different request.
+ field.label = ASCIIToUTF16("email");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+ form_structure = new FormStructure(form, std::string());
+ ScopedVector<FormStructure> form_structures1;
+ form_structures1.push_back(form_structure);
+
+ // Add another slightly different form, which should also result in a
+ // different request.
+ field.label = ASCIIToUTF16("email2");
+ field.name = ASCIIToUTF16("email2");
+ form.fields.push_back(field);
+ form_structure = new FormStructure(form, std::string());
+ ScopedVector<FormStructure> form_structures2;
+ form_structures2.push_back(form_structure);
+
+ // Limit cache to two forms.
+ LimitCache(2);
+
+ const char *responses[] = {
+ "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"3\" />"
+ "<field autofilltype=\"5\" />"
+ "</autofillqueryresponse>",
+ "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"3\" />"
+ "<field autofilltype=\"5\" />"
+ "<field autofilltype=\"9\" />"
+ "</autofillqueryresponse>",
+ "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"3\" />"
+ "<field autofilltype=\"5\" />"
+ "<field autofilltype=\"9\" />"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>",
+ };
+
+ // Request with id 0.
+ MockAutofillMetrics mock_metric_logger;
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get(),
+ mock_metric_logger));
+ // No responses yet
+ EXPECT_EQ(static_cast<size_t>(0), responses_.size());
+
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 200, std::string(responses[0]));
+ ASSERT_EQ(static_cast<size_t>(1), responses_.size());
+ EXPECT_EQ(responses[0], responses_.front().response);
+
+ responses_.clear();
+
+ // No actual request - should be a cache hit.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get(),
+ mock_metric_logger));
+ // Data is available immediately from cache - no over-the-wire trip.
+ ASSERT_EQ(static_cast<size_t>(1), responses_.size());
+ EXPECT_EQ(responses[0], responses_.front().response);
+ responses_.clear();
+
+ // Request with id 1.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures1.get(),
+ mock_metric_logger));
+ // No responses yet
+ EXPECT_EQ(static_cast<size_t>(0), responses_.size());
+
+ fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 200, std::string(responses[1]));
+ ASSERT_EQ(static_cast<size_t>(1), responses_.size());
+ EXPECT_EQ(responses[1], responses_.front().response);
+
+ responses_.clear();
+
+ // Request with id 2.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures2.get(),
+ mock_metric_logger));
+
+ fetcher = factory.GetFetcherByID(2);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 200, std::string(responses[2]));
+ ASSERT_EQ(static_cast<size_t>(1), responses_.size());
+ EXPECT_EQ(responses[2], responses_.front().response);
+
+ responses_.clear();
+
+ // No actual requests - should be a cache hit.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures1.get(),
+ mock_metric_logger));
+
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures2.get(),
+ mock_metric_logger));
+
+ ASSERT_EQ(static_cast<size_t>(2), responses_.size());
+ EXPECT_EQ(responses[1], responses_.front().response);
+ EXPECT_EQ(responses[2], responses_.back().response);
+ responses_.clear();
+
+ // The first structure should've expired.
+ // Request with id 3.
+ EXPECT_CALL(mock_metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_SENT)).Times(1);
+ EXPECT_TRUE(download_manager_.StartQueryRequest(form_structures0.get(),
+ mock_metric_logger));
+ // No responses yet
+ EXPECT_EQ(static_cast<size_t>(0), responses_.size());
+
+ fetcher = factory.GetFetcherByID(3);
+ ASSERT_TRUE(fetcher);
+ FakeOnURLFetchComplete(fetcher, 200, std::string(responses[0]));
+ ASSERT_EQ(static_cast<size_t>(1), responses_.size());
+ EXPECT_EQ(responses[0], responses_.front().response);
+}
diff --git a/components/autofill/browser/autofill_download_url.cc b/components/autofill/browser/autofill_download_url.cc
new file mode 100644
index 0000000..8c8251b
--- /dev/null
+++ b/components/autofill/browser/autofill_download_url.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_download_url.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "components/autofill/common/autofill_switches.h"
+#include "googleurl/src/gurl.h"
+
+namespace {
+
+const char kDefaultAutofillServiceUrl[] =
+ "https://clients1.google.com/tbproxy/af/";
+
+#if defined(GOOGLE_CHROME_BUILD)
+const char kClientName[] = "Google Chrome";
+#else
+const char kClientName[] = "Chromium";
+#endif // defined(GOOGLE_CHROME_BUILD)
+
+std::string GetBaseAutofillUrl() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ std::string baseAutofillServiceUrl = command_line.GetSwitchValueASCII(
+ switches::kAutofillServiceUrl);
+ if (baseAutofillServiceUrl.empty())
+ return kDefaultAutofillServiceUrl;
+
+ return baseAutofillServiceUrl;
+}
+
+} // anonymous namespace
+
+GURL autofill::GetAutofillQueryUrl() {
+ std::string baseAutofillServiceUrl = GetBaseAutofillUrl();
+ return GURL(baseAutofillServiceUrl + "query?client=" + kClientName);
+}
+
+GURL autofill::GetAutofillUploadUrl() {
+ std::string baseAutofillServiceUrl = GetBaseAutofillUrl();
+ return GURL(baseAutofillServiceUrl + "upload?client=" + kClientName);
+}
+
diff --git a/components/autofill/browser/autofill_download_url.h b/components/autofill/browser/autofill_download_url.h
new file mode 100644
index 0000000..427fec3
--- /dev/null
+++ b/components/autofill/browser/autofill_download_url.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_DOWNLOAD_URL_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_DOWNLOAD_URL_H_
+
+class GURL;
+
+namespace autofill {
+
+GURL GetAutofillQueryUrl();
+GURL GetAutofillUploadUrl();
+
+}
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_DOWNLOAD_URL_H_
+
diff --git a/components/autofill/browser/autofill_download_url_unittest.cc b/components/autofill/browser/autofill_download_url_unittest.cc
new file mode 100644
index 0000000..7ae3449
--- /dev/null
+++ b/components/autofill/browser/autofill_download_url_unittest.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_download_url.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::StartsWith;
+
+TEST(AutofillDownloadUrlTest, CheckDefaultUrls) {
+ std::string query_url =
+ autofill::GetAutofillQueryUrl().spec();
+ EXPECT_THAT(query_url,
+ StartsWith("https://clients1.google.com/tbproxy/af/query?client="));
+
+ std::string upload_url =
+ autofill::GetAutofillUploadUrl().spec();
+ EXPECT_THAT(upload_url,
+ StartsWith("https://clients1.google.com/tbproxy/af/upload?client="));
+}
+
diff --git a/components/autofill/browser/autofill_external_delegate.cc b/components/autofill/browser/autofill_external_delegate.cc
new file mode 100644
index 0000000..128beb4
--- /dev/null
+++ b/components/autofill/browser/autofill_external_delegate.cc
@@ -0,0 +1,382 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autocomplete_history_manager.h"
+#include "components/autofill/browser/autofill_external_delegate.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_ANDROID)
+#include "content/public/browser/android/content_view_core.h"
+#endif
+
+using content::RenderViewHost;
+using WebKit::WebAutofillClient;
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(AutofillExternalDelegate);
+
+void AutofillExternalDelegate::CreateForWebContentsAndManager(
+ content::WebContents* web_contents,
+ AutofillManager* autofill_manager) {
+ if (FromWebContents(web_contents))
+ return;
+
+ web_contents->SetUserData(
+ UserDataKey(),
+ new AutofillExternalDelegate(web_contents, autofill_manager));
+}
+
+AutofillExternalDelegate::AutofillExternalDelegate(
+ content::WebContents* web_contents,
+ AutofillManager* autofill_manager)
+ : web_contents_(web_contents),
+ autofill_manager_(autofill_manager),
+ password_autofill_manager_(web_contents),
+ autofill_query_id_(0),
+ display_warning_if_disabled_(false),
+ has_autofill_suggestion_(false),
+ has_shown_autofill_popup_for_current_edit_(false),
+ registered_keyboard_listener_with_(NULL) {
+ DCHECK(autofill_manager);
+
+ registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ content::Source<content::WebContents>(web_contents));
+ registrar_.Add(
+ this,
+ content::NOTIFICATION_NAV_ENTRY_COMMITTED,
+ content::Source<content::NavigationController>(
+ &(web_contents->GetController())));
+}
+
+AutofillExternalDelegate::~AutofillExternalDelegate() {}
+
+void AutofillExternalDelegate::OnQuery(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& element_bounds,
+ bool display_warning_if_disabled) {
+ autofill_query_form_ = form;
+ autofill_query_field_ = field;
+ display_warning_if_disabled_ = display_warning_if_disabled;
+ autofill_query_id_ = query_id;
+ element_bounds_ = element_bounds;
+}
+
+void AutofillExternalDelegate::OnSuggestionsReturned(
+ int query_id,
+ const std::vector<string16>& autofill_values,
+ const std::vector<string16>& autofill_labels,
+ const std::vector<string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids) {
+ if (query_id != autofill_query_id_)
+ return;
+
+ std::vector<string16> values(autofill_values);
+ std::vector<string16> labels(autofill_labels);
+ std::vector<string16> icons(autofill_icons);
+ std::vector<int> ids(autofill_unique_ids);
+
+ // Add a separator to go between the values and menu items.
+ values.push_back(string16());
+ labels.push_back(string16());
+ icons.push_back(string16());
+ ids.push_back(WebAutofillClient::MenuItemIDSeparator);
+
+ ApplyAutofillWarnings(&values, &labels, &icons, &ids);
+
+ // Only include "Autofill Options" special menu item if we have Autofill
+ // suggestions.
+ has_autofill_suggestion_ = false;
+ for (size_t i = 0; i < ids.size(); ++i) {
+ if (ids[i] > 0) {
+ has_autofill_suggestion_ = true;
+ break;
+ }
+ }
+
+ if (has_autofill_suggestion_)
+ ApplyAutofillOptions(&values, &labels, &icons, &ids);
+
+ // Remove the separator if it is the last element.
+ if (ids.back() == WebAutofillClient::MenuItemIDSeparator) {
+ values.pop_back();
+ labels.pop_back();
+ icons.pop_back();
+ ids.pop_back();
+ }
+
+ InsertDataListValues(&values, &labels, &icons, &ids);
+
+ if (values.empty()) {
+ // No suggestions, any popup currently showing is obsolete.
+ autofill_manager_->delegate()->HideAutofillPopup();
+ return;
+ }
+
+ // Send to display.
+ if (autofill_query_field_.is_focusable) {
+ autofill_manager_->delegate()->ShowAutofillPopup(
+ element_bounds_, values, labels, icons, ids, this);
+ }
+}
+
+void AutofillExternalDelegate::OnShowPasswordSuggestions(
+ const std::vector<string16>& suggestions,
+ const FormFieldData& field,
+ const gfx::RectF& element_bounds) {
+ autofill_query_field_ = field;
+ element_bounds_ = element_bounds;
+
+ if (suggestions.empty()) {
+ autofill_manager_->delegate()->HideAutofillPopup();
+ return;
+ }
+
+ std::vector<string16> empty(suggestions.size());
+ std::vector<int> password_ids(suggestions.size(),
+ WebAutofillClient::MenuItemIDPasswordEntry);
+ autofill_manager_->delegate()->ShowAutofillPopup(
+ element_bounds_, suggestions, empty, empty, password_ids, this);
+}
+
+void AutofillExternalDelegate::SetCurrentDataListValues(
+ const std::vector<string16>& data_list_values,
+ const std::vector<string16>& data_list_labels,
+ const std::vector<string16>& data_list_icons,
+ const std::vector<int>& data_list_unique_ids) {
+ data_list_values_ = data_list_values;
+ data_list_labels_ = data_list_labels;
+ data_list_icons_ = data_list_icons;
+ data_list_unique_ids_ = data_list_unique_ids;
+}
+
+void AutofillExternalDelegate::OnPopupShown(
+ content::KeyboardListener* listener) {
+ if (!registered_keyboard_listener_with_) {
+ registered_keyboard_listener_with_ = web_contents_->GetRenderViewHost();
+ registered_keyboard_listener_with_->AddKeyboardListener(listener);
+ }
+
+ autofill_manager_->OnDidShowAutofillSuggestions(
+ has_autofill_suggestion_ && !has_shown_autofill_popup_for_current_edit_);
+ has_shown_autofill_popup_for_current_edit_ |= has_autofill_suggestion_;
+}
+
+void AutofillExternalDelegate::OnPopupHidden(
+ content::KeyboardListener* listener) {
+ if (registered_keyboard_listener_with_ == web_contents_->GetRenderViewHost())
+ web_contents_->GetRenderViewHost()->RemoveKeyboardListener(listener);
+
+ registered_keyboard_listener_with_ = NULL;
+}
+
+void AutofillExternalDelegate::DidSelectSuggestion(int identifier) {
+ ClearPreviewedForm();
+
+ // Only preview the data if it is a profile.
+ if (identifier > 0)
+ FillAutofillFormData(identifier, true);
+}
+
+void AutofillExternalDelegate::DidAcceptSuggestion(const string16& value,
+ int identifier) {
+ RenderViewHost* host = web_contents_->GetRenderViewHost();
+
+ if (identifier == WebAutofillClient::MenuItemIDAutofillOptions) {
+ // User selected 'Autofill Options'.
+ autofill_manager_->OnShowAutofillDialog();
+ } else if (identifier == WebAutofillClient::MenuItemIDClearForm) {
+ // User selected 'Clear form'.
+ host->Send(new AutofillMsg_ClearForm(host->GetRoutingID()));
+ } else if (identifier == WebAutofillClient::MenuItemIDPasswordEntry &&
+ password_autofill_manager_.DidAcceptAutofillSuggestion(
+ autofill_query_field_, value)) {
+ // DidAcceptAutofillSuggestion has already handled the work to fill in
+ // the page as required.
+ } else if (identifier == WebAutofillClient::MenuItemIDDataListEntry) {
+ host->Send(new AutofillMsg_AcceptDataListSuggestion(host->GetRoutingID(),
+ value));
+ } else if (identifier == WebAutofillClient::MenuItemIDAutocompleteEntry) {
+ // User selected an Autocomplete, so we fill directly.
+ host->Send(new AutofillMsg_SetNodeText(host->GetRoutingID(), value));
+ } else {
+ FillAutofillFormData(identifier, false);
+ }
+
+ autofill_manager_->delegate()->HideAutofillPopup();
+}
+
+void AutofillExternalDelegate::RemoveSuggestion(const string16& value,
+ int identifier) {
+ if (identifier > 0) {
+ autofill_manager_->RemoveAutofillProfileOrCreditCard(identifier);
+ } else {
+ autofill_manager_->RemoveAutocompleteEntry(autofill_query_field_.name,
+ value);
+ }
+}
+
+void AutofillExternalDelegate::DidEndTextFieldEditing() {
+ autofill_manager_->delegate()->HideAutofillPopup();
+
+ has_shown_autofill_popup_for_current_edit_ = false;
+}
+
+void AutofillExternalDelegate::ClearPreviewedForm() {
+ RenderViewHost* host = web_contents_->GetRenderViewHost();
+ if (host)
+ host->Send(new AutofillMsg_ClearPreviewedForm(host->GetRoutingID()));
+}
+
+void AutofillExternalDelegate::Reset() {
+ autofill_manager_->delegate()->HideAutofillPopup();
+
+ password_autofill_manager_.Reset();
+}
+
+void AutofillExternalDelegate::AddPasswordFormMapping(
+ const FormFieldData& form,
+ const PasswordFormFillData& fill_data) {
+ password_autofill_manager_.AddPasswordFormMapping(form, fill_data);
+}
+
+void AutofillExternalDelegate::FillAutofillFormData(int unique_id,
+ bool is_preview) {
+ // If the selected element is a warning we don't want to do anything.
+ if (unique_id == WebAutofillClient::MenuItemIDWarningMessage)
+ return;
+
+ RenderViewHost* host = web_contents_->GetRenderViewHost();
+
+ if (is_preview) {
+ host->Send(new AutofillMsg_SetAutofillActionPreview(
+ host->GetRoutingID()));
+ } else {
+ host->Send(new AutofillMsg_SetAutofillActionFill(
+ host->GetRoutingID()));
+ }
+
+ // Fill the values for the whole form.
+ autofill_manager_->OnFillAutofillFormData(autofill_query_id_,
+ autofill_query_form_,
+ autofill_query_field_,
+ unique_id);
+}
+
+void AutofillExternalDelegate::ApplyAutofillWarnings(
+ std::vector<string16>* autofill_values,
+ std::vector<string16>* autofill_labels,
+ std::vector<string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids) {
+ if (!autofill_query_field_.should_autocomplete) {
+ // If autofill is disabled and we had suggestions, show a warning instead.
+ autofill_values->assign(
+ 1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED));
+ autofill_labels->assign(1, string16());
+ autofill_icons->assign(1, string16());
+ autofill_unique_ids->assign(1, WebAutofillClient::MenuItemIDWarningMessage);
+ } else if (autofill_unique_ids->size() > 1 &&
+ (*autofill_unique_ids)[0] ==
+ WebAutofillClient::MenuItemIDWarningMessage) {
+ // If we received a warning instead of suggestions from autofill but regular
+ // suggestions from autocomplete, don't show the autofill warning.
+ autofill_values->erase(autofill_values->begin());
+ autofill_labels->erase(autofill_labels->begin());
+ autofill_icons->erase(autofill_icons->begin());
+ autofill_unique_ids->erase(autofill_unique_ids->begin());
+ }
+
+ // If we were about to show a warning and we shouldn't, don't.
+ if (!autofill_unique_ids->empty() &&
+ (*autofill_unique_ids)[0] ==
+ WebAutofillClient::MenuItemIDWarningMessage &&
+ !display_warning_if_disabled_) {
+ autofill_values->clear();
+ autofill_labels->clear();
+ autofill_icons->clear();
+ autofill_unique_ids->clear();
+ }
+}
+
+void AutofillExternalDelegate::ApplyAutofillOptions(
+ std::vector<string16>* autofill_values,
+ std::vector<string16>* autofill_labels,
+ std::vector<string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids) {
+ // The form has been auto-filled, so give the user the chance to clear the
+ // form. Append the 'Clear form' menu item.
+ if (autofill_query_field_.is_autofilled) {
+ autofill_values->push_back(
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
+ autofill_labels->push_back(string16());
+ autofill_icons->push_back(string16());
+ autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDClearForm);
+ }
+
+ // Append the 'Chrome Autofill options' menu item;
+ autofill_values->push_back(
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP));
+ autofill_labels->push_back(string16());
+ autofill_icons->push_back(string16());
+ autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDAutofillOptions);
+}
+
+void AutofillExternalDelegate::InsertDataListValues(
+ std::vector<string16>* autofill_values,
+ std::vector<string16>* autofill_labels,
+ std::vector<string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids) {
+ if (data_list_values_.empty())
+ return;
+
+ // Insert the separator between the datalist and Autofill values (if there
+ // are any).
+ if (!autofill_values->empty()) {
+ autofill_values->insert(autofill_values->begin(), string16());
+ autofill_labels->insert(autofill_labels->begin(), string16());
+ autofill_icons->insert(autofill_icons->begin(), string16());
+ autofill_unique_ids->insert(autofill_unique_ids->begin(),
+ WebAutofillClient::MenuItemIDSeparator);
+ }
+
+ // Insert the datalist elements.
+ autofill_values->insert(autofill_values->begin(),
+ data_list_values_.begin(),
+ data_list_values_.end());
+ autofill_labels->insert(autofill_labels->begin(),
+ data_list_labels_.begin(),
+ data_list_labels_.end());
+ autofill_icons->insert(autofill_icons->begin(),
+ data_list_icons_.begin(),
+ data_list_icons_.end());
+ autofill_unique_ids->insert(autofill_unique_ids->begin(),
+ data_list_unique_ids_.begin(),
+ data_list_unique_ids_.end());
+}
+
+void AutofillExternalDelegate::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ if (type == content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED) {
+ if (!*content::Details<bool>(details).ptr())
+ autofill_manager_->delegate()->HideAutofillPopup();
+ } else if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) {
+ autofill_manager_->delegate()->HideAutofillPopup();
+ } else {
+ NOTREACHED();
+ }
+}
diff --git a/components/autofill/browser/autofill_external_delegate.h b/components/autofill/browser/autofill_external_delegate.h
new file mode 100644
index 0000000..48a5236
--- /dev/null
+++ b/components/autofill/browser/autofill_external_delegate.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_EXTERNAL_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_EXTERNAL_DELEGATE_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/string16.h"
+#include "components/autofill/browser/autofill_popup_delegate.h"
+#include "components/autofill/browser/password_autofill_manager.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/password_form_fill_data.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "ui/gfx/rect.h"
+
+class AutofillManager;
+
+namespace gfx {
+class Rect;
+}
+
+namespace content {
+class WebContents;
+}
+
+// TODO(csharp): A lot of the logic in this class is copied from autofillagent.
+// Once Autofill is moved out of WebKit this class should be the only home for
+// this logic. See http://crbug.com/51644
+
+// Delegate for in-browser Autocomplete and Autofill display and selection.
+class AutofillExternalDelegate
+ : public content::WebContentsUserData<AutofillExternalDelegate>,
+ public content::NotificationObserver,
+ public AutofillPopupDelegate {
+ public:
+ // Creates an AutofillExternalDelegate and attaches it to the specified
+ // contents; the second argument is an AutofillManager managing Autofill for
+ // that WebContents.
+ static void CreateForWebContentsAndManager(content::WebContents* web_contents,
+ AutofillManager* autofill_manager);
+
+ // AutofillPopupDelegate implementation.
+ virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE;
+ virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE;
+ virtual void DidSelectSuggestion(int identifier) OVERRIDE;
+ virtual void DidAcceptSuggestion(const string16& value,
+ int identifier) OVERRIDE;
+ virtual void RemoveSuggestion(const string16& value, int identifier) OVERRIDE;
+ virtual void ClearPreviewedForm() OVERRIDE;
+
+ // Records and associates a query_id with web form data. Called
+ // when the renderer posts an Autofill query to the browser. |bounds|
+ // is window relative. |display_warning_if_disabled| tells us if we should
+ // display warnings (such as autofill is disabled, but had suggestions).
+ // We might not want to display the warning if a website has disabled
+ // Autocomplete because they have their own popup, and showing our popup
+ // on to of theirs would be a poor user experience.
+ virtual void OnQuery(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& element_bounds,
+ bool display_warning_if_disabled);
+
+ // Records query results and correctly formats them before sending them off
+ // to be displayed. Called when an Autofill query result is available.
+ virtual void OnSuggestionsReturned(
+ int query_id,
+ const std::vector<string16>& autofill_values,
+ const std::vector<string16>& autofill_labels,
+ const std::vector<string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids);
+
+ // Show password suggestions in the popup.
+ void OnShowPasswordSuggestions(const std::vector<string16>& suggestions,
+ const FormFieldData& field,
+ const gfx::RectF& bounds);
+
+ // Set the data list value associated with the current field.
+ void SetCurrentDataListValues(const std::vector<string16>& autofill_values,
+ const std::vector<string16>& autofill_labels,
+ const std::vector<string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids);
+
+ // Inform the delegate that the text field editing has ended. This is
+ // used to help record the metrics of when a new popup is shown.
+ void DidEndTextFieldEditing();
+
+ // Returns the delegate to its starting state by removing any page specific
+ // values or settings.
+ void Reset();
+
+ // Inform the Password Manager of a filled form.
+ void AddPasswordFormMapping(
+ const FormFieldData& form,
+ const PasswordFormFillData& fill_data);
+
+ protected:
+ friend class content::WebContentsUserData<AutofillExternalDelegate>;
+ AutofillExternalDelegate(content::WebContents* web_contents,
+ AutofillManager* autofill_manager);
+ virtual ~AutofillExternalDelegate();
+
+ content::WebContents* web_contents() { return web_contents_; }
+
+ private:
+ // Fills the form with the Autofill data corresponding to |unique_id|.
+ // If |is_preview| is true then this is just a preview to show the user what
+ // would be selected and if |is_preview| is false then the user has selected
+ // this data.
+ void FillAutofillFormData(int unique_id, bool is_preview);
+
+ // Handle applying any Autofill warnings to the Autofill popup.
+ void ApplyAutofillWarnings(std::vector<string16>* autofill_values,
+ std::vector<string16>* autofill_labels,
+ std::vector<string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids);
+
+ // Handle applying any Autofill option listings to the Autofill popup.
+ // This function should only get called when there is at least one
+ // multi-field suggestion in the list of suggestions.
+ void ApplyAutofillOptions(std::vector<string16>* autofill_values,
+ std::vector<string16>* autofill_labels,
+ std::vector<string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids);
+
+ // Insert the data list values at the start of the given list, including
+ // any required separators.
+ void InsertDataListValues(std::vector<string16>* autofill_values,
+ std::vector<string16>* autofill_labels,
+ std::vector<string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids);
+
+ // content::NotificationObserver method override.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // The web_contents associated with this delegate.
+ content::WebContents* web_contents_; // weak; owns me.
+ AutofillManager* autofill_manager_; // weak.
+
+ // Password Autofill manager, handles all password-related Autofilling.
+ PasswordAutofillManager password_autofill_manager_;
+
+ // The ID of the last request sent for form field Autofill. Used to ignore
+ // out of date responses.
+ int autofill_query_id_;
+
+ // A scoped container for notification registries.
+ content::NotificationRegistrar registrar_;
+
+ // The current form and field selected by Autofill.
+ FormData autofill_query_form_;
+ FormFieldData autofill_query_field_;
+
+ // The bounds of the form field that user is interacting with.
+ gfx::RectF element_bounds_;
+
+ // Should we display a warning if Autofill is disabled?
+ bool display_warning_if_disabled_;
+
+ // Does the popup include any Autofill profile or credit card suggestions?
+ bool has_autofill_suggestion_;
+
+ // Have we already shown Autofill suggestions for the field the user is
+ // currently editing? Used to keep track of state for metrics logging.
+ bool has_shown_autofill_popup_for_current_edit_;
+
+ // The RenderViewHost that this object has been registered with as a
+ // keyboard listener.
+ content::RenderViewHost* registered_keyboard_listener_with_;
+
+ // The current data list values.
+ std::vector<string16> data_list_values_;
+ std::vector<string16> data_list_labels_;
+ std::vector<string16> data_list_icons_;
+ std::vector<int> data_list_unique_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillExternalDelegate);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_EXTERNAL_DELEGATE_H_
diff --git a/components/autofill/browser/autofill_external_delegate_unittest.cc b/components/autofill/browser/autofill_external_delegate_unittest.cc
new file mode 100644
index 0000000..e6b5cb5
--- /dev/null
+++ b/components/autofill/browser/autofill_external_delegate_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/string16.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/browser/test_autofill_external_delegate.h"
+#include "components/autofill/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
+#include "ui/gfx/rect.h"
+
+using content::BrowserThread;
+using testing::_;
+using WebKit::WebAutofillClient;
+
+namespace {
+
+// A constant value to use as the Autofill query ID.
+const int kQueryId = 5;
+
+// A constant value to use as an Autofill profile ID.
+const int kAutofillProfileId = 1;
+
+class MockAutofillExternalDelegate : public AutofillExternalDelegate {
+ public:
+ MockAutofillExternalDelegate(content::WebContents* web_contents,
+ AutofillManager* autofill_manger)
+ : AutofillExternalDelegate(web_contents, autofill_manger) {}
+
+ ~MockAutofillExternalDelegate() {}
+
+ MOCK_METHOD0(ClearPreviewedForm, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillExternalDelegate);
+};
+
+class MockAutofillManagerDelegate
+ : public autofill::TestAutofillManagerDelegate {
+ public:
+ MockAutofillManagerDelegate() {}
+
+ MOCK_METHOD6(ShowAutofillPopup,
+ void(const gfx::RectF& element_bounds,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& identifiers,
+ AutofillPopupDelegate* delegate));
+
+ MOCK_METHOD0(HideAutofillPopup, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
+};
+
+class MockAutofillManager : public AutofillManager {
+ public:
+ MockAutofillManager(content::WebContents* web_contents,
+ MockAutofillManagerDelegate* delegate)
+ // Force to use the constructor designated for unit test, but we don't
+ // really need personal_data in this test so we pass a NULL pointer.
+ : AutofillManager(web_contents, delegate, NULL) {
+ }
+ virtual ~MockAutofillManager() {}
+
+ MOCK_METHOD4(OnFillAutofillFormData,
+ void(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ int unique_id));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManager);
+};
+
+} // namespace
+
+class AutofillExternalDelegateUnitTest
+ : public ChromeRenderViewHostTestHarness {
+ public:
+ AutofillExternalDelegateUnitTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_) {}
+ virtual ~AutofillExternalDelegateUnitTest() {}
+
+ protected:
+ // Issue an OnQuery call with the given |query_id|.
+ void IssueOnQuery(int query_id) {
+ const FormData form;
+ FormFieldData field;
+ field.is_focusable = true;
+ field.should_autocomplete = true;
+ const gfx::RectF element_bounds;
+
+ external_delegate_->OnQuery(query_id, form, field, element_bounds, false);
+ }
+
+ MockAutofillManagerDelegate manager_delegate_;
+ scoped_ptr<MockAutofillManager> autofill_manager_;
+ scoped_ptr<testing::NiceMock<MockAutofillExternalDelegate> >
+ external_delegate_;
+
+ private:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+ autofill_manager_.reset(
+ new MockAutofillManager(web_contents(), &manager_delegate_));
+ external_delegate_.reset(
+ new testing::NiceMock<MockAutofillExternalDelegate>(
+ web_contents(),
+ autofill_manager_.get()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Order of destruction is important as AutofillManager relies on
+ // PersonalDataManager to be around when it gets destroyed. Also, a real
+ // AutofillManager is tied to the lifetime of the WebContents, so it must
+ // be destroyed at the destruction of the WebContents.
+ autofill_manager_.reset();
+ external_delegate_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ content::TestBrowserThread ui_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillExternalDelegateUnitTest);
+};
+
+// Test that our external delegate called the virtual methods at the right time.
+TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) {
+ IssueOnQuery(kQueryId);
+
+ // The enums must be cast to ints to prevent compile errors on linux_rel.
+ EXPECT_CALL(manager_delegate_,
+ ShowAutofillPopup(
+ _, _, _, _,
+ testing::ElementsAre(
+ kAutofillProfileId,
+ static_cast<int>(WebAutofillClient::MenuItemIDSeparator),
+ static_cast<int>(
+ WebAutofillClient::MenuItemIDAutofillOptions)),
+ external_delegate_.get()));
+
+ // This should call ShowAutofillPopup.
+ std::vector<string16> autofill_item;
+ autofill_item.push_back(string16());
+ std::vector<int> autofill_ids;
+ autofill_ids.push_back(kAutofillProfileId);
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_item,
+ autofill_item,
+ autofill_item,
+ autofill_ids);
+
+ // Called by DidAutofillSuggestions, add expectation to remove warning.
+ EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _));
+
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup());
+
+ // This should trigger a call to hide the popup since we've selected an
+ // option.
+ external_delegate_->DidAcceptSuggestion(autofill_item[0], autofill_ids[0]);
+}
+
+// Test that data list elements for a node will appear in the Autofill popup.
+TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateDataList) {
+ IssueOnQuery(kQueryId);
+
+ std::vector<string16> data_list_items;
+ data_list_items.push_back(string16());
+ std::vector<int> data_list_ids;
+ data_list_ids.push_back(WebAutofillClient::MenuItemIDDataListEntry);
+
+ external_delegate_->SetCurrentDataListValues(data_list_items,
+ data_list_items,
+ data_list_items,
+ data_list_ids);
+
+ // The enums must be cast to ints to prevent compile errors on linux_rel.
+ EXPECT_CALL(manager_delegate_,
+ ShowAutofillPopup(
+ _, _, _, _,
+ testing::ElementsAre(
+ static_cast<int>(
+ WebAutofillClient::MenuItemIDDataListEntry),
+ static_cast<int>(WebAutofillClient::MenuItemIDSeparator),
+ kAutofillProfileId,
+ static_cast<int>(WebAutofillClient::MenuItemIDSeparator),
+ static_cast<int>(
+ WebAutofillClient::MenuItemIDAutofillOptions)),
+ external_delegate_.get()));
+
+ // This should call ShowAutofillPopup.
+ std::vector<string16> autofill_item;
+ autofill_item.push_back(string16());
+ std::vector<int> autofill_ids;
+ autofill_ids.push_back(kAutofillProfileId);
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_item,
+ autofill_item,
+ autofill_item,
+ autofill_ids);
+
+ // Try calling OnSuggestionsReturned with no Autofill values and ensure
+ // the datalist items are still shown.
+ // The enum must be cast to an int to prevent compile errors on linux_rel.
+ EXPECT_CALL(manager_delegate_,
+ ShowAutofillPopup(
+ _, _, _, _,
+ testing::ElementsAre(
+ static_cast<int>(
+ WebAutofillClient::MenuItemIDDataListEntry)),
+ external_delegate_.get()));
+
+ autofill_item = std::vector<string16>();
+ autofill_ids = std::vector<int>();
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_item,
+ autofill_item,
+ autofill_item,
+ autofill_ids);
+}
+
+// Test that the Autofill delegate doesn't try and fill a form with a
+// negative unique id.
+TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateInvalidUniqueId) {
+ // Ensure it doesn't try to preview the negative id.
+ EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)).Times(0);
+ EXPECT_CALL(*external_delegate_, ClearPreviewedForm()).Times(1);
+ external_delegate_->DidSelectSuggestion(-1);
+
+ // Ensure it doesn't try to fill the form in with the negative id.
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup());
+ EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)).Times(0);
+ external_delegate_->DidAcceptSuggestion(string16(), -1);
+}
+
+// Test that the ClearPreview IPC is only sent the form was being previewed
+// (i.e. it isn't autofilling a password).
+TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearPreviewedForm) {
+ // Called by DidSelectSuggestion, add expectation to remove warning.
+ EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _));
+
+ // Ensure selecting a new password entries or Autofill entries will
+ // cause any previews to get cleared.
+ EXPECT_CALL(*external_delegate_, ClearPreviewedForm()).Times(1);
+ external_delegate_->DidSelectSuggestion(
+ WebAutofillClient::MenuItemIDPasswordEntry);
+
+ EXPECT_CALL(*external_delegate_, ClearPreviewedForm()).Times(1);
+ external_delegate_->DidSelectSuggestion(1);
+}
+
+// Test that the popup is hidden once we are done editing the autofill field.
+TEST_F(AutofillExternalDelegateUnitTest,
+ ExternalDelegateHidePopupAfterEditing) {
+ EXPECT_CALL(manager_delegate_, ShowAutofillPopup(_, _, _, _, _, _));
+ autofill::GenerateTestAutofillPopup(external_delegate_.get());
+
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup());
+ external_delegate_->DidEndTextFieldEditing();
+}
+
+// Test that the popup is marked as visible after recieving password
+// suggestions.
+TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegatePasswordSuggestions) {
+ std::vector<string16> suggestions;
+ suggestions.push_back(string16());
+
+ FormFieldData field;
+ field.is_focusable = true;
+ field.should_autocomplete = true;
+ const gfx::RectF element_bounds;
+
+ // The enums must be cast to ints to prevent compile errors on linux_rel.
+ EXPECT_CALL(manager_delegate_,
+ ShowAutofillPopup(
+ _, _, _, _,
+ testing::ElementsAre(
+ static_cast<int>(
+ WebAutofillClient::MenuItemIDPasswordEntry)),
+ external_delegate_.get()));
+
+ external_delegate_->OnShowPasswordSuggestions(suggestions,
+ field,
+ element_bounds);
+
+ // Called by DidAutofillSuggestions, add expectation to remove warning.
+ EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _));
+
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup());
+
+ // This should trigger a call to hide the popup since
+ // we've selected an option.
+ external_delegate_->DidAcceptSuggestion(
+ suggestions[0],
+ WebAutofillClient::MenuItemIDPasswordEntry);
+}
diff --git a/components/autofill/browser/autofill_field.cc b/components/autofill/browser/autofill_field.cc
new file mode 100644
index 0000000..ba3817e
--- /dev/null
+++ b/components/autofill/browser/autofill_field.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 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/browser/autofill_field.h"
+
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+
+namespace {
+
+static std::string Hash32Bit(const std::string& str) {
+ std::string hash_bin = base::SHA1HashString(str);
+ DCHECK_EQ(20U, hash_bin.length());
+
+ uint32 hash32 = ((hash_bin[0] & 0xFF) << 24) |
+ ((hash_bin[1] & 0xFF) << 16) |
+ ((hash_bin[2] & 0xFF) << 8) |
+ (hash_bin[3] & 0xFF);
+
+ return base::UintToString(hash32);
+}
+
+} // namespace
+
+AutofillField::AutofillField()
+ : server_type_(NO_SERVER_DATA),
+ heuristic_type_(UNKNOWN_TYPE),
+ phone_part_(IGNORED) {
+}
+
+AutofillField::AutofillField(const FormFieldData& field,
+ const string16& unique_name)
+ : FormFieldData(field),
+ unique_name_(unique_name),
+ server_type_(NO_SERVER_DATA),
+ heuristic_type_(UNKNOWN_TYPE),
+ phone_part_(IGNORED) {
+}
+
+AutofillField::~AutofillField() {}
+
+void AutofillField::set_heuristic_type(AutofillFieldType type) {
+ if (type >= 0 && type < MAX_VALID_FIELD_TYPE &&
+ type != FIELD_WITH_DEFAULT_VALUE) {
+ heuristic_type_ = type;
+ } else {
+ NOTREACHED();
+ // This case should not be reachable; but since this has potential
+ // implications on data uploaded to the server, better safe than sorry.
+ heuristic_type_ = UNKNOWN_TYPE;
+ }
+}
+
+void AutofillField::set_server_type(AutofillFieldType type) {
+ // Chrome no longer supports fax numbers, but the server still does.
+ if (type >= PHONE_FAX_NUMBER && type <= PHONE_FAX_WHOLE_NUMBER)
+ return;
+
+ server_type_ = type;
+}
+
+AutofillFieldType AutofillField::type() const {
+ if (server_type_ != NO_SERVER_DATA)
+ return server_type_;
+
+ return heuristic_type_;
+}
+
+bool AutofillField::IsEmpty() const {
+ return value.empty();
+}
+
+std::string AutofillField::FieldSignature() const {
+ std::string field_name = UTF16ToUTF8(name);
+ std::string field_string = field_name + "&" + form_control_type;
+ return Hash32Bit(field_string);
+}
+
+bool AutofillField::IsFieldFillable() const {
+ return type() != UNKNOWN_TYPE;
+}
diff --git a/components/autofill/browser/autofill_field.h b/components/autofill/browser/autofill_field.h
new file mode 100644
index 0000000..aa43091
--- /dev/null
+++ b/components/autofill/browser/autofill_field.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_FIELD_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/common/form_field_data.h"
+
+class AutofillField : public FormFieldData {
+ public:
+ enum PhonePart {
+ IGNORED = 0,
+ PHONE_PREFIX = 1,
+ PHONE_SUFFIX = 2,
+ };
+
+ AutofillField();
+ AutofillField(const FormFieldData& field, const string16& unique_name);
+ virtual ~AutofillField();
+
+ const string16& unique_name() const { return unique_name_; }
+
+ const std::string& section() const { return section_; }
+ AutofillFieldType heuristic_type() const { return heuristic_type_; }
+ AutofillFieldType server_type() const { return server_type_; }
+ const FieldTypeSet& possible_types() const { return possible_types_; }
+ PhonePart phone_part() const { return phone_part_; }
+
+ // Sets the heuristic type of this field, validating the input.
+ void set_section(const std::string& section) { section_ = section; }
+ void set_heuristic_type(AutofillFieldType type);
+ void set_server_type(AutofillFieldType type);
+ void set_possible_types(const FieldTypeSet& possible_types) {
+ possible_types_ = possible_types;
+ }
+ void set_phone_part(PhonePart part) { phone_part_ = part; }
+
+ // This function automatically chooses between server and heuristic autofill
+ // type, depending on the data available.
+ AutofillFieldType type() const;
+
+ // Returns true if the value of this field is empty.
+ bool IsEmpty() const;
+
+ // The unique signature of this field, composed of the field name and the html
+ // input type in a 32-bit hash.
+ std::string FieldSignature() const;
+
+ // Returns true if the field type has been determined (without the text in the
+ // field).
+ bool IsFieldFillable() const;
+
+ void set_default_value(const std::string& value) { default_value_ = value; }
+ const std::string& default_value() const { return default_value_; }
+
+ private:
+ // The unique name of this field, generated by Autofill.
+ string16 unique_name_;
+
+ // The unique identifier for the section (e.g. billing vs. shipping address)
+ // that this field belongs to.
+ std::string section_;
+
+ // The type of the field, as determined by the Autofill server.
+ AutofillFieldType server_type_;
+
+ // The type of the field, as determined by the local heuristics.
+ AutofillFieldType heuristic_type_;
+
+ // The set of possible types for this field.
+ FieldTypeSet possible_types_;
+
+ // Used to track whether this field is a phone prefix or suffix.
+ PhonePart phone_part_;
+
+ // The default value returned by the Autofill server.
+ std::string default_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_FIELD_H_
diff --git a/components/autofill/browser/autofill_field_unittest.cc b/components/autofill/browser/autofill_field_unittest.cc
new file mode 100644
index 0000000..745f6fc
--- /dev/null
+++ b/components/autofill/browser/autofill_field_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(AutofillFieldTest, Type) {
+ AutofillField field;
+ ASSERT_EQ(NO_SERVER_DATA, field.server_type());
+ ASSERT_EQ(UNKNOWN_TYPE, field.heuristic_type());
+
+ // |server_type_| is NO_SERVER_DATA, so |heuristic_type_| is returned.
+ EXPECT_EQ(UNKNOWN_TYPE, field.type());
+
+ // Set the heuristic type and check it.
+ field.set_heuristic_type(NAME_FIRST);
+ EXPECT_EQ(NAME_FIRST, field.type());
+
+ // Set the server type and check it.
+ field.set_server_type(ADDRESS_BILLING_LINE1);
+ EXPECT_EQ(ADDRESS_BILLING_LINE1, field.type());
+
+ // Remove the server type to make sure the heuristic type is preserved.
+ field.set_server_type(NO_SERVER_DATA);
+ EXPECT_EQ(NAME_FIRST, field.type());
+}
+
+TEST(AutofillFieldTest, IsEmpty) {
+ AutofillField field;
+ ASSERT_EQ(string16(), field.value);
+
+ // Field value is empty.
+ EXPECT_TRUE(field.IsEmpty());
+
+ // Field value is non-empty.
+ field.value = ASCIIToUTF16("Value");
+ EXPECT_FALSE(field.IsEmpty());
+}
+
+TEST(AutofillFieldTest, FieldSignature) {
+ AutofillField field;
+ ASSERT_EQ(string16(), field.name);
+ ASSERT_EQ(std::string(), field.form_control_type);
+
+ // Signature is empty.
+ EXPECT_EQ("2085434232", field.FieldSignature());
+
+ // Field name is set.
+ field.name = ASCIIToUTF16("Name");
+ EXPECT_EQ("1606968241", field.FieldSignature());
+
+ // Field form control type is set.
+ field.form_control_type = "text";
+ EXPECT_EQ("502192749", field.FieldSignature());
+
+ // Heuristic type does not affect FieldSignature.
+ field.set_heuristic_type(NAME_FIRST);
+ EXPECT_EQ("502192749", field.FieldSignature());
+
+ // Server type does not affect FieldSignature.
+ field.set_server_type(NAME_LAST);
+ EXPECT_EQ("502192749", field.FieldSignature());
+}
+
+TEST(AutofillFieldTest, IsFieldFillable) {
+ AutofillField field;
+ ASSERT_EQ(UNKNOWN_TYPE, field.type());
+
+ // Type is unknown.
+ EXPECT_FALSE(field.IsFieldFillable());
+
+ // Only heuristic type is set.
+ field.set_heuristic_type(NAME_FIRST);
+ EXPECT_TRUE(field.IsFieldFillable());
+
+ // Only server type is set.
+ field.set_heuristic_type(UNKNOWN_TYPE);
+ field.set_server_type(NAME_LAST);
+ EXPECT_TRUE(field.IsFieldFillable());
+
+ // Both types set.
+ field.set_heuristic_type(NAME_FIRST);
+ field.set_server_type(NAME_LAST);
+ EXPECT_TRUE(field.IsFieldFillable());
+}
+
+} // namespace
diff --git a/components/autofill/browser/autofill_ie_toolbar_import_win.cc b/components/autofill/browser/autofill_ie_toolbar_import_win.cc
new file mode 100644
index 0000000..29ba67b
--- /dev/null
+++ b/components/autofill/browser/autofill_ie_toolbar_import_win.cc
@@ -0,0 +1,295 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_ie_toolbar_import_win.h"
+
+#include <stddef.h>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/string16.h"
+#include "base/win/registry.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/crypto/rc4_decryptor.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_group.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/browser/personal_data_manager_observer.h"
+#include "components/autofill/browser/phone_number.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+#include "sync/util/data_encryption_win.h"
+
+using base::win::RegKey;
+
+// Forward declaration. This function is not in unnamed namespace as it
+// is referenced in the unittest.
+bool ImportCurrentUserProfiles(std::vector<AutofillProfile>* profiles,
+ std::vector<CreditCard>* credit_cards);
+namespace {
+
+const wchar_t* const kProfileKey =
+ L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles";
+const wchar_t* const kCreditCardKey =
+ L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards";
+const wchar_t* const kPasswordHashValue = L"password_hash";
+const wchar_t* const kSaltValue = L"salt";
+
+// This is RC4 decryption for Toolbar credit card data. This is necessary
+// because it is not standard, so Crypto API cannot be used.
+std::wstring DecryptCCNumber(const std::wstring& data) {
+ const wchar_t* kEmptyKey =
+ L"\x3605\xCEE5\xCE49\x44F7\xCF4E\xF6CC\x604B\xFCBE\xC70A\x08FD";
+ const size_t kMacLen = 10;
+
+ if (data.length() <= kMacLen)
+ return std::wstring();
+
+ RC4Decryptor rc4_algorithm(kEmptyKey);
+ return rc4_algorithm.Run(data.substr(kMacLen));
+}
+
+bool IsEmptySalt(std::wstring const& salt) {
+ // Empty salt in IE Toolbar is \x1\x2...\x14
+ if (salt.length() != 20)
+ return false;
+ for (size_t i = 0; i < salt.length(); ++i) {
+ if (salt[i] != i + 1)
+ return false;
+ }
+ return true;
+}
+
+string16 ReadAndDecryptValue(const RegKey& key, const wchar_t* value_name) {
+ DWORD data_type = REG_BINARY;
+ DWORD data_size = 0;
+ LONG result = key.ReadValue(value_name, NULL, &data_size, &data_type);
+ if ((result != ERROR_SUCCESS) || !data_size || data_type != REG_BINARY)
+ return string16();
+ std::vector<uint8> data;
+ data.resize(data_size);
+ result = key.ReadValue(value_name, &(data[0]), &data_size, &data_type);
+ if (result == ERROR_SUCCESS) {
+ std::string out_data;
+ if (syncer::DecryptData(data, &out_data)) {
+ // The actual data is in UTF16 already.
+ if (!(out_data.size() & 1) && (out_data.size() > 2) &&
+ !out_data[out_data.size() - 1] && !out_data[out_data.size() - 2]) {
+ return string16(
+ reinterpret_cast<const wchar_t *>(out_data.c_str()));
+ }
+ }
+ }
+ return string16();
+}
+
+struct {
+ AutofillFieldType field_type;
+ const wchar_t *reg_value_name;
+} profile_reg_values[] = {
+ { NAME_FIRST, L"name_first" },
+ { NAME_MIDDLE, L"name_middle" },
+ { NAME_LAST, L"name_last" },
+ { NAME_SUFFIX, L"name_suffix" },
+ { EMAIL_ADDRESS, L"email" },
+ { COMPANY_NAME, L"company_name" },
+ { PHONE_HOME_NUMBER, L"phone_home_number" },
+ { PHONE_HOME_CITY_CODE, L"phone_home_city_code" },
+ { PHONE_HOME_COUNTRY_CODE, L"phone_home_country_code" },
+ { ADDRESS_HOME_LINE1, L"address_home_line1" },
+ { ADDRESS_HOME_LINE2, L"address_home_line2" },
+ { ADDRESS_HOME_CITY, L"address_home_city" },
+ { ADDRESS_HOME_STATE, L"address_home_state" },
+ { ADDRESS_HOME_ZIP, L"address_home_zip" },
+ { ADDRESS_HOME_COUNTRY, L"address_home_country" },
+ { ADDRESS_BILLING_LINE1, L"address_billing_line1" },
+ { ADDRESS_BILLING_LINE2, L"address_billing_line2" },
+ { ADDRESS_BILLING_CITY, L"address_billing_city" },
+ { ADDRESS_BILLING_STATE, L"address_billing_state" },
+ { ADDRESS_BILLING_ZIP, L"address_billing_zip" },
+ { ADDRESS_BILLING_COUNTRY, L"address_billing_country" },
+ { CREDIT_CARD_NAME, L"credit_card_name" },
+ { CREDIT_CARD_NUMBER, L"credit_card_number" },
+ { CREDIT_CARD_EXP_MONTH, L"credit_card_exp_month" },
+ { CREDIT_CARD_EXP_4_DIGIT_YEAR, L"credit_card_exp_4_digit_year" },
+ { CREDIT_CARD_TYPE, L"credit_card_type" },
+ // We do not import verification code.
+};
+
+typedef std::map<std::wstring, AutofillFieldType> RegToFieldMap;
+
+// Imports address or credit card data from the given registry |key| into the
+// given |form_group|, with the help of |reg_to_field|. When importing address
+// data, writes the phone data into |phone|; otherwise, |phone| should be null.
+// Returns true if any fields were set, false otherwise.
+bool ImportSingleFormGroup(const RegKey& key,
+ const RegToFieldMap& reg_to_field,
+ FormGroup* form_group,
+ PhoneNumber::PhoneCombineHelper* phone) {
+ if (!key.Valid())
+ return false;
+
+ bool has_non_empty_fields = false;
+
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ for (uint32 i = 0; i < key.GetValueCount(); ++i) {
+ std::wstring value_name;
+ if (key.GetValueNameAt(i, &value_name) != ERROR_SUCCESS)
+ continue;
+
+ RegToFieldMap::const_iterator it = reg_to_field.find(value_name);
+ if (it == reg_to_field.end())
+ continue; // This field is not imported.
+
+ string16 field_value = ReadAndDecryptValue(key, value_name.c_str());
+ if (!field_value.empty()) {
+ if (it->second == CREDIT_CARD_NUMBER)
+ field_value = DecryptCCNumber(field_value);
+
+ // Phone numbers are stored piece-by-piece, and then reconstructed from
+ // the pieces. The rest of the fields are set "as is".
+ if (!phone || !phone->SetInfo(it->second, field_value)) {
+ has_non_empty_fields = true;
+ form_group->SetInfo(it->second, field_value, app_locale);
+ }
+ }
+ }
+
+ return has_non_empty_fields;
+}
+
+// Imports address data from the given registry |key| into the given |profile|,
+// with the help of |reg_to_field|. Returns true if any fields were set, false
+// otherwise.
+bool ImportSingleProfile(const RegKey& key,
+ const RegToFieldMap& reg_to_field,
+ AutofillProfile* profile) {
+ PhoneNumber::PhoneCombineHelper phone;
+ bool has_non_empty_fields =
+ ImportSingleFormGroup(key, reg_to_field, profile, &phone);
+
+ // Now re-construct the phones if needed.
+ string16 constructed_number;
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ if (phone.ParseNumber(*profile, app_locale, &constructed_number)) {
+ has_non_empty_fields = true;
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number);
+ }
+
+ return has_non_empty_fields;
+}
+
+// Imports profiles from the IE toolbar and stores them. Asynchronous
+// if PersonalDataManager has not been loaded yet. Deletes itself on completion.
+class AutofillImporter : public PersonalDataManagerObserver {
+ public:
+ explicit AutofillImporter(PersonalDataManager* personal_data_manager)
+ : personal_data_manager_(personal_data_manager) {
+ personal_data_manager_->AddObserver(this);
+ }
+
+ bool ImportProfiles() {
+ if (!ImportCurrentUserProfiles(&profiles_, &credit_cards_)) {
+ delete this;
+ return false;
+ }
+ if (personal_data_manager_->IsDataLoaded())
+ OnPersonalDataChanged();
+ return true;
+ }
+
+ // PersonalDataManagerObserver:
+ virtual void OnPersonalDataChanged() OVERRIDE {
+ for (std::vector<AutofillProfile>::const_iterator iter = profiles_.begin();
+ iter != profiles_.end(); ++iter) {
+ personal_data_manager_->AddProfile(*iter);
+ }
+ for (std::vector<CreditCard>::const_iterator iter = credit_cards_.begin();
+ iter != credit_cards_.end(); ++iter) {
+ personal_data_manager_->AddCreditCard(*iter);
+ }
+ delete this;
+ }
+
+ private:
+ ~AutofillImporter() {
+ personal_data_manager_->RemoveObserver(this);
+ }
+
+ PersonalDataManager* personal_data_manager_;
+ std::vector<AutofillProfile> profiles_;
+ std::vector<CreditCard> credit_cards_;
+};
+
+} // namespace
+
+// Imports Autofill profiles and credit cards from IE Toolbar if present and not
+// password protected. Returns true if data is successfully retrieved. False if
+// there is no data, data is password protected or error occurred.
+bool ImportCurrentUserProfiles(std::vector<AutofillProfile>* profiles,
+ std::vector<CreditCard>* credit_cards) {
+ DCHECK(profiles);
+ DCHECK(credit_cards);
+
+ // Create a map of possible fields for a quick access.
+ RegToFieldMap reg_to_field;
+ for (size_t i = 0; i < arraysize(profile_reg_values); ++i) {
+ reg_to_field[std::wstring(profile_reg_values[i].reg_value_name)] =
+ profile_reg_values[i].field_type;
+ }
+
+ base::win::RegistryKeyIterator iterator_profiles(HKEY_CURRENT_USER,
+ kProfileKey);
+ for (; iterator_profiles.Valid(); ++iterator_profiles) {
+ std::wstring key_name(kProfileKey);
+ key_name.append(L"\\");
+ key_name.append(iterator_profiles.Name());
+ RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
+ AutofillProfile profile;
+ if (ImportSingleProfile(key, reg_to_field, &profile)) {
+ // Combine phones into whole phone #.
+ profiles->push_back(profile);
+ }
+ }
+ string16 password_hash;
+ string16 salt;
+ RegKey cc_key(HKEY_CURRENT_USER, kCreditCardKey, KEY_READ);
+ if (cc_key.Valid()) {
+ password_hash = ReadAndDecryptValue(cc_key, kPasswordHashValue);
+ salt = ReadAndDecryptValue(cc_key, kSaltValue);
+ }
+
+ // We import CC profiles only if they are not password protected.
+ if (password_hash.empty() && IsEmptySalt(salt)) {
+ base::win::RegistryKeyIterator iterator_cc(HKEY_CURRENT_USER,
+ kCreditCardKey);
+ for (; iterator_cc.Valid(); ++iterator_cc) {
+ std::wstring key_name(kCreditCardKey);
+ key_name.append(L"\\");
+ key_name.append(iterator_cc.Name());
+ RegKey key(HKEY_CURRENT_USER, key_name.c_str(), KEY_READ);
+ CreditCard credit_card;
+ if (ImportSingleFormGroup(key, reg_to_field, &credit_card, NULL)) {
+ string16 cc_number = credit_card.GetRawInfo(CREDIT_CARD_NUMBER);
+ if (!cc_number.empty())
+ credit_cards->push_back(credit_card);
+ }
+ }
+ }
+ return (profiles->size() + credit_cards->size()) > 0;
+}
+
+bool ImportAutofillDataWin(PersonalDataManager* pdm) {
+ // In incognito mode we do not have PDM - and we should not import anything.
+ if (!pdm)
+ return false;
+ AutofillImporter *importer = new AutofillImporter(pdm);
+ // importer will self delete.
+ return importer->ImportProfiles();
+}
diff --git a/components/autofill/browser/autofill_ie_toolbar_import_win.h b/components/autofill/browser/autofill_ie_toolbar_import_win.h
new file mode 100644
index 0000000..d89604d
--- /dev/null
+++ b/components/autofill/browser/autofill_ie_toolbar_import_win.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_
+
+// This importer is here and not in chrome/browser/importer/toolbar_importer.cc
+// because of the following:
+// 1. The data is not saved in profile, but rather in registry, thus it is
+// accessed without going through toolbar front end.
+// 2. This applies to IE (thus Windows) toolbar only.
+// 3. The functionality relevant only to and completely encapsulated in the
+// autofill.
+// 4. This is completely automated as opposed to Importers, which are explicit.
+class PersonalDataManager;
+
+bool ImportAutofillDataWin(PersonalDataManager* pdm);
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_
diff --git a/components/autofill/browser/autofill_ie_toolbar_import_win_unittest.cc b/components/autofill/browser/autofill_ie_toolbar_import_win_unittest.cc
new file mode 100644
index 0000000..b81a833
--- /dev/null
+++ b/components/autofill/browser/autofill_ie_toolbar_import_win_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_ie_toolbar_import_win.h"
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "base/win/registry.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/field_types.h"
+#include "sync/util/data_encryption_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::win::RegKey;
+
+// Defined in autofill_ie_toolbar_import_win.cc. Not exposed in the header file.
+bool ImportCurrentUserProfiles(std::vector<AutofillProfile>* profiles,
+ std::vector<CreditCard>* credit_cards);
+
+namespace {
+
+const wchar_t kUnitTestRegistrySubKey[] = L"SOFTWARE\\Chromium Unit Tests";
+const wchar_t kUnitTestUserOverrideSubKey[] =
+ L"SOFTWARE\\Chromium Unit Tests\\HKCU Override";
+
+const wchar_t* const kProfileKey =
+ L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Profiles";
+const wchar_t* const kCreditCardKey =
+ L"Software\\Google\\Google Toolbar\\4.0\\Autofill\\Credit Cards";
+const wchar_t* const kPasswordHashValue = L"password_hash";
+const wchar_t* const kSaltValue = L"salt";
+
+struct ValueDescription {
+ wchar_t const* const value_name;
+ wchar_t const* const value;
+};
+
+ValueDescription profile1[] = {
+ { L"name_first", L"John" },
+ { L"name_middle", L"Herman" },
+ { L"name_last", L"Doe" },
+ { L"email", L"jdoe@test.com" },
+ { L"company_name", L"Testcompany" },
+ { L"phone_home_number", L"555-5555" },
+ { L"phone_home_city_code", L"650" },
+ { L"phone_home_country_code", L"1" },
+};
+
+ValueDescription profile2[] = {
+ { L"name_first", L"Jane" },
+ { L"name_last", L"Doe" },
+ { L"email", L"janedoe@test.com" },
+ { L"company_name", L"Testcompany" },
+};
+
+ValueDescription credit_card[] = {
+ { L"credit_card_name", L"Tommy Gun" },
+ // "4111111111111111" encrypted:
+ { L"credit_card_number", L"\xE53F\x19AB\xC1BF\xC9EB\xECCC\x9BDA\x8515"
+ L"\xE14D\x6852\x80A8\x50A3\x4375\xFD9F\x1E07"
+ L"\x790E\x7336\xB773\xAF33\x93EA\xB846\xEC89"
+ L"\x265C\xD0E6\x4E23\xB75F\x7983" },
+ { L"credit_card_exp_month", L"11" },
+ { L"credit_card_exp_4_digit_year", L"2011" },
+};
+
+ValueDescription empty_salt = {
+ L"salt", L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF\x10\x11\x12\x13\x14"
+};
+
+ValueDescription empty_password = {
+ L"password_hash", L""
+};
+
+ValueDescription protected_salt = {
+ L"salt", L"\x4854\xB906\x9C7C\x50A6\x4376\xFD9D\x1E02"
+};
+
+ValueDescription protected_password = {
+ L"password_hash", L"\x18B7\xE586\x459B\x7457\xA066\x3842\x71DA"
+};
+
+void EncryptAndWrite(RegKey* key, const ValueDescription* value) {
+ std::string data;
+ size_t data_size = (lstrlen(value->value) + 1) * sizeof(wchar_t);
+ data.resize(data_size);
+ memcpy(&data[0], value->value, data_size);
+
+ std::vector<uint8> encrypted_data = syncer::EncryptData(data);
+ EXPECT_EQ(ERROR_SUCCESS, key->WriteValue(value->value_name,
+ &encrypted_data[0], encrypted_data.size(), REG_BINARY));
+}
+
+void CreateSubkey(RegKey* key, wchar_t const* subkey_name,
+ const ValueDescription* values, size_t values_size) {
+ RegKey subkey;
+ subkey.Create(key->Handle(), subkey_name, KEY_ALL_ACCESS);
+ EXPECT_TRUE(subkey.Valid());
+ for (size_t i = 0; i < values_size; ++i)
+ EncryptAndWrite(&subkey, values + i);
+}
+
+} // namespace
+
+class AutofillIeToolbarImportTest : public testing::Test {
+ public:
+ AutofillIeToolbarImportTest();
+
+ // testing::Test method overrides:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ private:
+ RegKey temp_hkcu_hive_key_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillIeToolbarImportTest);
+};
+
+AutofillIeToolbarImportTest::AutofillIeToolbarImportTest() {
+}
+
+void AutofillIeToolbarImportTest::SetUp() {
+ temp_hkcu_hive_key_.Create(HKEY_CURRENT_USER,
+ kUnitTestUserOverrideSubKey,
+ KEY_ALL_ACCESS);
+ EXPECT_TRUE(temp_hkcu_hive_key_.Valid());
+ EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER,
+ temp_hkcu_hive_key_.Handle()));
+}
+
+void AutofillIeToolbarImportTest::TearDown() {
+ EXPECT_EQ(ERROR_SUCCESS, RegOverridePredefKey(HKEY_CURRENT_USER, NULL));
+ temp_hkcu_hive_key_.Close();
+ RegKey key(HKEY_CURRENT_USER, kUnitTestRegistrySubKey, KEY_ALL_ACCESS);
+ key.DeleteKey(L"");
+}
+
+TEST_F(AutofillIeToolbarImportTest, TestAutofillImport) {
+ RegKey profile_key;
+ profile_key.Create(HKEY_CURRENT_USER, kProfileKey, KEY_ALL_ACCESS);
+ EXPECT_TRUE(profile_key.Valid());
+
+ CreateSubkey(&profile_key, L"0", profile1, arraysize(profile1));
+ CreateSubkey(&profile_key, L"1", profile2, arraysize(profile2));
+
+ RegKey cc_key;
+ cc_key.Create(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS);
+ EXPECT_TRUE(cc_key.Valid());
+ CreateSubkey(&cc_key, L"0", credit_card, arraysize(credit_card));
+ EncryptAndWrite(&cc_key, &empty_password);
+ EncryptAndWrite(&cc_key, &empty_salt);
+
+ profile_key.Close();
+ cc_key.Close();
+
+ std::vector<AutofillProfile> profiles;
+ std::vector<CreditCard> credit_cards;
+ EXPECT_TRUE(ImportCurrentUserProfiles(&profiles, &credit_cards));
+ ASSERT_EQ(2U, profiles.size());
+ // The profiles are read in reverse order.
+ EXPECT_EQ(profile1[0].value, profiles[1].GetRawInfo(NAME_FIRST));
+ EXPECT_EQ(profile1[1].value, profiles[1].GetRawInfo(NAME_MIDDLE));
+ EXPECT_EQ(profile1[2].value, profiles[1].GetRawInfo(NAME_LAST));
+ EXPECT_EQ(profile1[3].value, profiles[1].GetRawInfo(EMAIL_ADDRESS));
+ EXPECT_EQ(profile1[4].value, profiles[1].GetRawInfo(COMPANY_NAME));
+ EXPECT_EQ(profile1[7].value,
+ profiles[1].GetInfo(PHONE_HOME_COUNTRY_CODE, "US"));
+ EXPECT_EQ(profile1[6].value, profiles[1].GetInfo(PHONE_HOME_CITY_CODE, "US"));
+ EXPECT_EQ(L"5555555", profiles[1].GetInfo(PHONE_HOME_NUMBER, "US"));
+ EXPECT_EQ(L"+1 650-555-5555",
+ profiles[1].GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ EXPECT_EQ(profile2[0].value, profiles[0].GetRawInfo(NAME_FIRST));
+ EXPECT_EQ(profile2[1].value, profiles[0].GetRawInfo(NAME_LAST));
+ EXPECT_EQ(profile2[2].value, profiles[0].GetRawInfo(EMAIL_ADDRESS));
+ EXPECT_EQ(profile2[3].value, profiles[0].GetRawInfo(COMPANY_NAME));
+
+ ASSERT_EQ(1U, credit_cards.size());
+ EXPECT_EQ(credit_card[0].value, credit_cards[0].GetRawInfo(CREDIT_CARD_NAME));
+ EXPECT_EQ(L"4111111111111111",
+ credit_cards[0].GetRawInfo(CREDIT_CARD_NUMBER));
+ EXPECT_EQ(credit_card[2].value,
+ credit_cards[0].GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(credit_card[3].value,
+ credit_cards[0].GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ // Mock password encrypted cc.
+ cc_key.Open(HKEY_CURRENT_USER, kCreditCardKey, KEY_ALL_ACCESS);
+ EXPECT_TRUE(cc_key.Valid());
+ EncryptAndWrite(&cc_key, &protected_password);
+ EncryptAndWrite(&cc_key, &protected_salt);
+ cc_key.Close();
+
+ profiles.clear();
+ credit_cards.clear();
+ EXPECT_TRUE(ImportCurrentUserProfiles(&profiles, &credit_cards));
+ // Profiles are not protected.
+ EXPECT_EQ(2U, profiles.size());
+ // Credit cards are.
+ EXPECT_EQ(0U, credit_cards.size());
+}
+
diff --git a/components/autofill/browser/autofill_manager.cc b/components/autofill/browser/autofill_manager.cc
new file mode 100644
index 0000000..5d9f5ac
--- /dev/null
+++ b/components/autofill/browser/autofill_manager.cc
@@ -0,0 +1,1321 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_manager.h"
+
+#include <stddef.h>
+
+#include <limits>
+#include <map>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/prefs/pref_service.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "components/autofill/browser/autocheckout/whitelist_manager.h"
+#include "components/autofill/browser/autocheckout_manager.h"
+#include "components/autofill/browser/autocomplete_history_manager.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_external_delegate.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/password_generator.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/browser/phone_number.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/autofill_pref_names.h"
+#include "components/autofill/common/autofill_switches.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_data_predictions.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/password_form_fill_data.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "content/public/common/url_constants.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "ipc/ipc_message_macros.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/rect.h"
+
+typedef PersonalDataManager::GUIDPair GUIDPair;
+
+using base::TimeTicks;
+using content::BrowserThread;
+using content::RenderViewHost;
+using WebKit::WebFormElement;
+
+namespace {
+
+const char* kAutofillManagerWebContentsUserDataKey = "web_contents_autofill";
+
+// We only send a fraction of the forms to upload server.
+// The rate for positive/negative matches potentially could be different.
+const double kAutofillPositiveUploadRateDefaultValue = 0.20;
+const double kAutofillNegativeUploadRateDefaultValue = 0.20;
+
+const size_t kMaxRecentFormSignaturesToRemember = 3;
+
+// Set a conservative upper bound on the number of forms we are willing to
+// cache, simply to prevent unbounded memory consumption.
+const size_t kMaxFormCacheSize = 100;
+
+// Removes duplicate suggestions whilst preserving their original order.
+void RemoveDuplicateSuggestions(std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) {
+ DCHECK_EQ(values->size(), labels->size());
+ DCHECK_EQ(values->size(), icons->size());
+ DCHECK_EQ(values->size(), unique_ids->size());
+
+ std::set<std::pair<string16, string16> > seen_suggestions;
+ std::vector<string16> values_copy;
+ std::vector<string16> labels_copy;
+ std::vector<string16> icons_copy;
+ std::vector<int> unique_ids_copy;
+
+ for (size_t i = 0; i < values->size(); ++i) {
+ const std::pair<string16, string16> suggestion((*values)[i], (*labels)[i]);
+ if (seen_suggestions.insert(suggestion).second) {
+ values_copy.push_back((*values)[i]);
+ labels_copy.push_back((*labels)[i]);
+ icons_copy.push_back((*icons)[i]);
+ unique_ids_copy.push_back((*unique_ids)[i]);
+ }
+ }
+
+ values->swap(values_copy);
+ labels->swap(labels_copy);
+ icons->swap(icons_copy);
+ unique_ids->swap(unique_ids_copy);
+}
+
+// Precondition: |form_structure| and |form| should correspond to the same
+// logical form. Returns true if any field in the given |section| within |form|
+// is auto-filled.
+bool SectionIsAutofilled(const FormStructure& form_structure,
+ const FormData& form,
+ const std::string& section) {
+ DCHECK_EQ(form_structure.field_count(), form.fields.size());
+ for (size_t i = 0; i < form_structure.field_count(); ++i) {
+ if (form_structure.field(i)->section() == section &&
+ form.fields[i].is_autofilled) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool FormIsHTTPS(const FormStructure& form) {
+ return form.source_url().SchemeIs(chrome::kHttpsScheme);
+}
+
+// Uses the existing personal data in |profiles| and |credit_cards| to determine
+// possible field types for the |submitted_form|. This is potentially
+// expensive -- on the order of 50ms even for a small set of |stored_data|.
+// Hence, it should not run on the UI thread -- to avoid locking up the UI --
+// nor on the IO thread -- to avoid blocking IPC calls.
+void DeterminePossibleFieldTypesForUpload(
+ const std::vector<AutofillProfile>& profiles,
+ const std::vector<CreditCard>& credit_cards,
+ const std::string& app_locale,
+ FormStructure* submitted_form) {
+ DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+ // For each field in the |submitted_form|, extract the value. Then for each
+ // 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);
+ string16 value = CollapseWhitespace(field->value, false);
+
+ FieldTypeSet matching_types;
+ for (std::vector<AutofillProfile>::const_iterator it = profiles.begin();
+ it != profiles.end(); ++it) {
+ it->GetMatchingTypes(value, app_locale, &matching_types);
+ }
+ for (std::vector<CreditCard>::const_iterator it = credit_cards.begin();
+ it != credit_cards.end(); ++it) {
+ it->GetMatchingTypes(value, app_locale, &matching_types);
+ }
+
+ if (matching_types.empty())
+ matching_types.insert(UNKNOWN_TYPE);
+
+ field->set_possible_types(matching_types);
+ }
+}
+
+} // namespace
+
+// static
+void AutofillManager::CreateForWebContentsAndDelegate(
+ content::WebContents* contents,
+ autofill::AutofillManagerDelegate* delegate) {
+ if (FromWebContents(contents))
+ return;
+
+ contents->SetUserData(kAutofillManagerWebContentsUserDataKey,
+ new AutofillManager(contents, delegate));
+}
+
+// static
+AutofillManager* AutofillManager::FromWebContents(
+ content::WebContents* contents) {
+ return static_cast<AutofillManager*>(
+ contents->GetUserData(kAutofillManagerWebContentsUserDataKey));
+}
+
+AutofillManager::AutofillManager(content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate)
+ : content::WebContentsObserver(web_contents),
+ manager_delegate_(delegate),
+ personal_data_(delegate->GetPersonalDataManager()),
+ download_manager_(web_contents->GetBrowserContext(), this),
+ disable_download_manager_requests_(false),
+ autocomplete_history_manager_(web_contents),
+ autocheckout_manager_(this),
+ metric_logger_(new AutofillMetrics),
+ has_logged_autofill_enabled_(false),
+ has_logged_address_suggestions_count_(false),
+ did_show_suggestions_(false),
+ user_did_type_(false),
+ user_did_autofill_(false),
+ user_did_edit_autofilled_field_(false),
+ password_generation_enabled_(false),
+ external_delegate_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ RegisterWithSyncService();
+ registrar_.Init(manager_delegate_->GetPrefs());
+ registrar_.Add(
+ prefs::kPasswordGenerationEnabled,
+ base::Bind(&AutofillManager::OnPasswordGenerationEnabledChanged,
+ base::Unretained(this)));
+}
+
+AutofillManager::~AutofillManager() {
+}
+
+// static
+void AutofillManager::RegisterUserPrefs(PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(prefs::kAutofillEnabled,
+ true,
+ PrefRegistrySyncable::SYNCABLE_PREF);
+ registry->RegisterBooleanPref(prefs::kPasswordGenerationEnabled,
+ true,
+ PrefRegistrySyncable::SYNCABLE_PREF);
+#if defined(OS_MACOSX)
+ registry->RegisterBooleanPref(prefs::kAutofillAuxiliaryProfilesEnabled,
+ true,
+ PrefRegistrySyncable::SYNCABLE_PREF);
+#else
+ registry->RegisterBooleanPref(prefs::kAutofillAuxiliaryProfilesEnabled,
+ false,
+ PrefRegistrySyncable::UNSYNCABLE_PREF);
+#endif
+ registry->RegisterDoublePref(prefs::kAutofillPositiveUploadRate,
+ kAutofillPositiveUploadRateDefaultValue,
+ PrefRegistrySyncable::UNSYNCABLE_PREF);
+ registry->RegisterDoublePref(prefs::kAutofillNegativeUploadRate,
+ kAutofillNegativeUploadRateDefaultValue,
+ PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+void AutofillManager::RegisterWithSyncService() {
+ // TODO(joi): If/when SupportsWebData supports structured
+ // destruction ordering, we could use a base::Unretained here and
+ // just unsubscribe in our destructor. As is, we can't guarantee
+ // that the delegate doesn't get destroyed (by WebContent's
+ // SupportsUserData) right before the AutofillManager.
+ manager_delegate_->SetSyncStateChangedCallback(base::Bind(
+ &AutofillManager::OnSyncStateChanged, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AutofillManager::SendPasswordGenerationStateToRenderer(
+ content::RenderViewHost* host, bool enabled) {
+ host->Send(new AutofillMsg_PasswordGenerationEnabled(host->GetRoutingID(),
+ enabled));
+}
+
+// In order for password generation to be enabled, we need to make sure:
+// (1) Password sync is enabled,
+// (2) Password manager is enabled, and
+// (3) Password generation preference check box is checked.
+void AutofillManager::UpdatePasswordGenerationState(
+ content::RenderViewHost* host,
+ bool new_renderer) {
+ bool saving_passwords_enabled = manager_delegate_->IsSavingPasswordsEnabled();
+ bool preference_checked = manager_delegate_->GetPrefs()->GetBoolean(
+ prefs::kPasswordGenerationEnabled);
+
+ bool new_password_generation_enabled =
+ manager_delegate_->IsPasswordSyncEnabled() &&
+ saving_passwords_enabled &&
+ preference_checked;
+
+ if (new_password_generation_enabled != password_generation_enabled_ ||
+ new_renderer) {
+ password_generation_enabled_ = new_password_generation_enabled;
+ SendPasswordGenerationStateToRenderer(host, password_generation_enabled_);
+ }
+}
+
+void AutofillManager::RenderViewCreated(content::RenderViewHost* host) {
+ UpdatePasswordGenerationState(host, true);
+}
+
+void AutofillManager::OnPasswordGenerationEnabledChanged() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ UpdatePasswordGenerationState(web_contents()->GetRenderViewHost(), false);
+}
+
+void AutofillManager::OnSyncStateChanged() {
+ // It is possible for sync state to change during tab contents destruction.
+ // In this case, we don't need to update the renderer since it's going away.
+ if (web_contents() && web_contents()->GetRenderViewHost()) {
+ UpdatePasswordGenerationState(web_contents()->GetRenderViewHost(),
+ false);
+ }
+}
+
+void AutofillManager::DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) {
+ Reset();
+}
+
+void AutofillManager::SetExternalDelegate(AutofillExternalDelegate* delegate) {
+ // TODO(jrg): consider passing delegate into the ctor. That won't
+ // work if the delegate has a pointer to the AutofillManager, but
+ // future directions may not need such a pointer.
+ external_delegate_ = delegate;
+ autocomplete_history_manager_.SetExternalDelegate(delegate);
+}
+
+bool AutofillManager::IsNativeUiEnabled() {
+ return external_delegate_ != NULL;
+}
+
+bool AutofillManager::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AutofillManager, message)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_FormsSeen, OnFormsSeen)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_FormSubmitted, OnFormSubmitted)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_TextFieldDidChange,
+ OnTextFieldDidChange)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_QueryFormFieldAutofill,
+ OnQueryFormFieldAutofill)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowAutofillDialog,
+ OnShowAutofillDialog)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_FillAutofillFormData,
+ OnFillAutofillFormData)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_DidPreviewAutofillFormData,
+ OnDidPreviewAutofillFormData)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_DidFillAutofillFormData,
+ OnDidFillAutofillFormData)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_DidShowAutofillSuggestions,
+ OnDidShowAutofillSuggestions)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_DidEndTextFieldEditing,
+ OnDidEndTextFieldEditing)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_HideAutofillPopup,
+ OnHideAutofillPopup)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordGenerationPopup,
+ OnShowPasswordGenerationPopup)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_AddPasswordFormMapping,
+ OnAddPasswordFormMapping)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordSuggestions,
+ OnShowPasswordSuggestions)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_SetDataList,
+ OnSetDataList)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_RequestAutocomplete,
+ OnRequestAutocomplete)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_ClickFailed,
+ OnClickFailed)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+}
+
+void AutofillManager::WebContentsDestroyed(content::WebContents* web_contents) {
+ // Unsubscribe.
+ manager_delegate_->SetSyncStateChangedCallback(base::Closure());
+}
+
+bool AutofillManager::OnFormSubmitted(const FormData& form,
+ const TimeTicks& timestamp) {
+ // Let AutoComplete know as well.
+ autocomplete_history_manager_.OnFormSubmitted(form);
+
+ if (!IsAutofillEnabled())
+ return false;
+
+ if (web_contents()->GetBrowserContext()->IsOffTheRecord())
+ return false;
+
+ // Don't save data that was submitted through JavaScript.
+ if (!form.user_submitted)
+ return false;
+
+ // Grab a copy of the form data.
+ scoped_ptr<FormStructure> submitted_form(
+ new FormStructure(form, GetAutocheckoutURLPrefix()));
+
+ // Disregard forms that we wouldn't ever autofill in the first place.
+ if (!submitted_form->ShouldBeParsed(true))
+ return false;
+
+ // Ignore forms not present in our cache. These are typically forms with
+ // wonky JavaScript that also makes them not auto-fillable.
+ FormStructure* cached_submitted_form;
+ if (!FindCachedForm(form, &cached_submitted_form))
+ return false;
+
+ submitted_form->UpdateFromCache(*cached_submitted_form);
+ if (submitted_form->IsAutofillable(true))
+ ImportFormData(*submitted_form);
+
+ // Only upload server statistics and UMA metrics if at least some local data
+ // is available to use as a baseline.
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards();
+ if (!profiles.empty() || !credit_cards.empty()) {
+ // Copy the profile and credit card data, so that it can be accessed on a
+ // separate thread.
+ std::vector<AutofillProfile> copied_profiles;
+ copied_profiles.reserve(profiles.size());
+ for (std::vector<AutofillProfile*>::const_iterator it = profiles.begin();
+ it != profiles.end(); ++it) {
+ copied_profiles.push_back(**it);
+ }
+
+ std::vector<CreditCard> copied_credit_cards;
+ copied_credit_cards.reserve(credit_cards.size());
+ for (std::vector<CreditCard*>::const_iterator it = credit_cards.begin();
+ it != credit_cards.end(); ++it) {
+ copied_credit_cards.push_back(**it);
+ }
+
+ // Note that ownership of |submitted_form| is passed to the second task,
+ // using |base::Owned|.
+ FormStructure* raw_submitted_form = submitted_form.get();
+ BrowserThread::GetBlockingPool()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DeterminePossibleFieldTypesForUpload,
+ copied_profiles,
+ copied_credit_cards,
+ AutofillCountry::ApplicationLocale(),
+ raw_submitted_form),
+ base::Bind(&AutofillManager::UploadFormDataAsyncCallback,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Owned(submitted_form.release()),
+ forms_loaded_timestamp_,
+ initial_interaction_timestamp_,
+ timestamp));
+ }
+
+ return true;
+}
+
+void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms,
+ const TimeTicks& timestamp) {
+ autocheckout_manager_.OnFormsSeen();
+ bool enabled = IsAutofillEnabled();
+ if (!has_logged_autofill_enabled_) {
+ metric_logger_->LogIsAutofillEnabledAtPageLoad(enabled);
+ has_logged_autofill_enabled_ = true;
+ }
+
+ if (!enabled)
+ return;
+
+ forms_loaded_timestamp_ = timestamp;
+ ParseForms(forms);
+}
+
+void AutofillManager::OnTextFieldDidChange(const FormData& form,
+ const FormFieldData& field,
+ const TimeTicks& timestamp) {
+ FormStructure* form_structure = NULL;
+ AutofillField* autofill_field = NULL;
+ if (!GetCachedFormAndField(form, field, &form_structure, &autofill_field))
+ return;
+
+ if (!user_did_type_) {
+ user_did_type_ = true;
+ metric_logger_->LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE);
+ }
+
+ if (autofill_field->is_autofilled) {
+ autofill_field->is_autofilled = false;
+ metric_logger_->LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD);
+
+ if (!user_did_edit_autofilled_field_) {
+ user_did_edit_autofilled_field_ = true;
+ metric_logger_->LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD_ONCE);
+ }
+ }
+
+ UpdateInitialInteractionTimestamp(timestamp);
+}
+
+void AutofillManager::OnQueryFormFieldAutofill(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool display_warning) {
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+
+ if (external_delegate_) {
+ external_delegate_->OnQuery(query_id,
+ form,
+ field,
+ bounding_box,
+ display_warning);
+ }
+
+ RenderViewHost* host = NULL;
+ FormStructure* form_structure = NULL;
+ AutofillField* autofill_field = NULL;
+ if (GetHost(&host) &&
+ GetCachedFormAndField(form, field, &form_structure, &autofill_field) &&
+ // Don't send suggestions for forms that aren't auto-fillable.
+ form_structure->IsAutofillable(false)) {
+ AutofillFieldType type = autofill_field->type();
+ bool is_filling_credit_card =
+ (AutofillType(type).group() == AutofillType::CREDIT_CARD);
+ if (is_filling_credit_card) {
+ GetCreditCardSuggestions(
+ field, type, &values, &labels, &icons, &unique_ids);
+ } else {
+ GetProfileSuggestions(
+ form_structure, field, type, &values, &labels, &icons, &unique_ids);
+ }
+
+ DCHECK_EQ(values.size(), labels.size());
+ DCHECK_EQ(values.size(), icons.size());
+ DCHECK_EQ(values.size(), unique_ids.size());
+
+ if (!values.empty()) {
+ // Don't provide Autofill suggestions when Autofill is disabled, and don't
+ // provide credit card suggestions for non-HTTPS pages. However, provide a
+ // warning to the user in these cases.
+ int warning = 0;
+ if (!form_structure->IsAutofillable(true))
+ warning = IDS_AUTOFILL_WARNING_FORM_DISABLED;
+ else if (is_filling_credit_card && !FormIsHTTPS(*form_structure))
+ warning = IDS_AUTOFILL_WARNING_INSECURE_CONNECTION;
+ if (warning) {
+ values.assign(1, l10n_util::GetStringUTF16(warning));
+ labels.assign(1, string16());
+ icons.assign(1, string16());
+ unique_ids.assign(1,
+ WebKit::WebAutofillClient::MenuItemIDWarningMessage);
+ } else {
+ bool section_is_autofilled =
+ SectionIsAutofilled(*form_structure, form,
+ autofill_field->section());
+ if (section_is_autofilled) {
+ // If the relevant section is auto-filled and the renderer is querying
+ // for suggestions, then the user is editing the value of a field.
+ // In this case, mimic autocomplete: don't display labels or icons,
+ // as that information is redundant.
+ labels.assign(labels.size(), string16());
+ icons.assign(icons.size(), string16());
+ }
+
+ // When filling credit card suggestions, the values and labels are
+ // typically obfuscated, which makes detecting duplicates hard. Since
+ // duplicates only tend to be a problem when filling address forms
+ // anyway, only don't de-dup credit card suggestions.
+ if (!is_filling_credit_card)
+ RemoveDuplicateSuggestions(&values, &labels, &icons, &unique_ids);
+
+ // The first time we show suggestions on this page, log the number of
+ // suggestions shown.
+ if (!has_logged_address_suggestions_count_ && !section_is_autofilled) {
+ metric_logger_->LogAddressSuggestionsCount(values.size());
+ has_logged_address_suggestions_count_ = true;
+ }
+ }
+ }
+
+ // If form is known to be at the start of the autofillable flow (i.e, when
+ // Autofill server said so), then trigger payments UI while also returning
+ // standard autofill suggestions to renderer process.
+ if (autocheckout_manager_.IsStartOfAutofillableFlow()) {
+ bool bubble_shown =
+ autocheckout_manager_.MaybeShowAutocheckoutBubble(
+ form.origin,
+ form.ssl_status,
+ web_contents()->GetView()->GetContentNativeView(),
+ bounding_box);
+ if (bubble_shown)
+ return;
+ }
+ }
+
+ // Add the results from AutoComplete. They come back asynchronously, so we
+ // hand off what we generated and they will send the results back to the
+ // renderer.
+ autocomplete_history_manager_.OnGetAutocompleteSuggestions(
+ query_id, field.name, field.value, values, labels, icons, unique_ids);
+}
+
+void AutofillManager::OnFillAutofillFormData(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ int unique_id) {
+ RenderViewHost* host = NULL;
+ const FormGroup* form_group = NULL;
+ size_t variant = 0;
+ FormStructure* form_structure = NULL;
+ AutofillField* autofill_field = NULL;
+ // NOTE: GetHost may invalidate |form_group| because it causes the
+ // PersonalDataManager to reload Mac address book entries. Thus it must
+ // come before GetProfileOrCreditCard.
+ if (!GetHost(&host) ||
+ !GetProfileOrCreditCard(unique_id, &form_group, &variant) ||
+ !GetCachedFormAndField(form, field, &form_structure, &autofill_field))
+ return;
+
+ DCHECK(host);
+ DCHECK(form_structure);
+ DCHECK(autofill_field);
+
+ FormData result = form;
+
+ // If the relevant section is auto-filled, we should fill |field| but not the
+ // rest of the form.
+ if (SectionIsAutofilled(*form_structure, form, autofill_field->section())) {
+ for (std::vector<FormFieldData>::iterator iter = result.fields.begin();
+ iter != result.fields.end(); ++iter) {
+ if ((*iter) == field) {
+ form_group->FillFormField(*autofill_field, variant, &(*iter));
+ // Mark the cached field as autofilled, so that we can detect when a
+ // user edits an autofilled field (for metrics).
+ autofill_field->is_autofilled = true;
+ break;
+ }
+ }
+
+ host->Send(new AutofillMsg_FormDataFilled(host->GetRoutingID(), query_id,
+ result));
+ return;
+ }
+
+ // Cache the field type for the field from which the user initiated autofill.
+ FieldTypeGroup initiating_group_type =
+ AutofillType(autofill_field->type()).group();
+ DCHECK_EQ(form_structure->field_count(), form.fields.size());
+ for (size_t i = 0; i < form_structure->field_count(); ++i) {
+ if (form_structure->field(i)->section() != autofill_field->section())
+ continue;
+
+ DCHECK_EQ(*form_structure->field(i), result.fields[i]);
+
+ const AutofillField* cached_field = form_structure->field(i);
+ FieldTypeGroup field_group_type =
+ AutofillType(cached_field->type()).group();
+ if (field_group_type != AutofillType::NO_GROUP) {
+ // If the field being filled is either
+ // (a) the field that the user initiated the fill from, or
+ // (b) part of the same logical unit, e.g. name or phone number,
+ // then take the multi-profile "variant" into account.
+ // Otherwise fill with the default (zeroth) variant.
+ size_t use_variant = 0;
+ if (result.fields[i] == field ||
+ field_group_type == initiating_group_type) {
+ use_variant = variant;
+ }
+ form_group->FillFormField(*cached_field,
+ use_variant,
+ &result.fields[i]);
+ // Mark the cached field as autofilled, so that we can detect when a user
+ // edits an autofilled field (for metrics).
+ form_structure->field(i)->is_autofilled = true;
+ }
+ }
+
+ autofilled_form_signatures_.push_front(form_structure->FormSignature());
+ // Only remember the last few forms that we've seen, both to avoid false
+ // positives and to avoid wasting memory.
+ if (autofilled_form_signatures_.size() > kMaxRecentFormSignaturesToRemember)
+ autofilled_form_signatures_.pop_back();
+
+ host->Send(new AutofillMsg_FormDataFilled(
+ host->GetRoutingID(), query_id, result));
+}
+
+void AutofillManager::OnShowAutofillDialog() {
+ manager_delegate_->ShowAutofillSettings();
+}
+
+void AutofillManager::OnDidPreviewAutofillFormData() {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_AUTOFILL_DID_FILL_FORM_DATA,
+ content::Source<RenderViewHost>(web_contents()->GetRenderViewHost()),
+ content::NotificationService::NoDetails());
+}
+
+void AutofillManager::OnDidFillAutofillFormData(const TimeTicks& timestamp) {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_AUTOFILL_DID_FILL_FORM_DATA,
+ content::Source<RenderViewHost>(web_contents()->GetRenderViewHost()),
+ content::NotificationService::NoDetails());
+
+ metric_logger_->LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL);
+ if (!user_did_autofill_) {
+ user_did_autofill_ = true;
+ metric_logger_->LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL_ONCE);
+ }
+
+ UpdateInitialInteractionTimestamp(timestamp);
+}
+
+void AutofillManager::OnDidShowAutofillSuggestions(bool is_new_popup) {
+ content::NotificationService::current()->Notify(
+ chrome::NOTIFICATION_AUTOFILL_DID_SHOW_SUGGESTIONS,
+ content::Source<RenderViewHost>(web_contents()->GetRenderViewHost()),
+ content::NotificationService::NoDetails());
+
+ if (is_new_popup) {
+ metric_logger_->LogUserHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN);
+
+ if (!did_show_suggestions_) {
+ did_show_suggestions_ = true;
+ metric_logger_->LogUserHappinessMetric(
+ AutofillMetrics::SUGGESTIONS_SHOWN_ONCE);
+ }
+ }
+}
+
+void AutofillManager::OnHideAutofillPopup() {
+ if (IsNativeUiEnabled())
+ manager_delegate_->HideAutofillPopup();
+}
+
+void AutofillManager::OnShowPasswordGenerationPopup(
+ const gfx::Rect& bounds,
+ int max_length,
+ const content::PasswordForm& form) {
+ password_generator_.reset(new autofill::PasswordGenerator(max_length));
+ manager_delegate_->ShowPasswordGenerationBubble(
+ bounds, form, password_generator_.get());
+}
+
+void AutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
+ const FormGroup* form_group = NULL;
+ size_t variant = 0;
+ if (!GetProfileOrCreditCard(unique_id, &form_group, &variant)) {
+ NOTREACHED();
+ return;
+ }
+
+ // TODO(csharp): If we are dealing with a variant only the variant should
+ // be deleted, instead of doing nothing.
+ // http://crbug.com/124211
+ if (variant != 0)
+ return;
+
+ personal_data_->RemoveByGUID(form_group->GetGUID());
+}
+
+void AutofillManager::RemoveAutocompleteEntry(const string16& name,
+ const string16& value) {
+ autocomplete_history_manager_.OnRemoveAutocompleteEntry(name, value);
+}
+
+content::WebContents* AutofillManager::GetWebContents() const {
+ return web_contents();
+}
+
+const std::vector<FormStructure*>& AutofillManager::GetFormStructures() {
+ return form_structures_.get();
+}
+
+void AutofillManager::ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ autofill::DialogType dialog_type,
+ const base::Callback<void(const FormStructure*)>& callback) {
+ manager_delegate_->ShowRequestAutocompleteDialog(
+ form, source_url, ssl_status, *metric_logger_, dialog_type, callback);
+}
+
+void AutofillManager::RequestAutocompleteDialogClosed() {
+ manager_delegate_->RequestAutocompleteDialogClosed();
+}
+
+void AutofillManager::OnAddPasswordFormMapping(
+ const FormFieldData& form,
+ const PasswordFormFillData& fill_data) {
+ if (external_delegate_)
+ external_delegate_->AddPasswordFormMapping(form, fill_data);
+}
+
+void AutofillManager::OnShowPasswordSuggestions(
+ const FormFieldData& field,
+ const gfx::RectF& bounds,
+ const std::vector<string16>& suggestions) {
+ if (external_delegate_)
+ external_delegate_->OnShowPasswordSuggestions(suggestions, field, bounds);
+}
+
+void AutofillManager::OnSetDataList(const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& unique_ids) {
+ if (labels.size() != values.size() ||
+ icons.size() != values.size() ||
+ unique_ids.size() != values.size()) {
+ return;
+ }
+ if (external_delegate_) {
+ external_delegate_->SetCurrentDataListValues(values,
+ labels,
+ icons,
+ unique_ids);
+ }
+}
+
+void AutofillManager::OnRequestAutocomplete(
+ const FormData& form,
+ const GURL& frame_url,
+ const content::SSLStatus& ssl_status) {
+ if (!IsAutofillEnabled()) {
+ ReturnAutocompleteResult(WebFormElement::AutocompleteResultErrorDisabled,
+ FormData());
+ return;
+ }
+
+ base::Callback<void(const FormStructure*)> callback =
+ base::Bind(&AutofillManager::ReturnAutocompleteData,
+ weak_ptr_factory_.GetWeakPtr());
+ ShowRequestAutocompleteDialog(
+ form, frame_url, ssl_status,
+ autofill::DIALOG_TYPE_REQUEST_AUTOCOMPLETE, callback);
+}
+
+void AutofillManager::ReturnAutocompleteResult(
+ WebFormElement::AutocompleteResult result, const FormData& form_data) {
+ // web_contents() will be NULL when the interactive autocomplete is closed due
+ // to a tab or browser window closing.
+ if (!web_contents())
+ return;
+
+ RenderViewHost* host = web_contents()->GetRenderViewHost();
+ if (!host)
+ return;
+
+ host->Send(new AutofillMsg_RequestAutocompleteResult(host->GetRoutingID(),
+ result,
+ form_data));
+}
+
+void AutofillManager::ReturnAutocompleteData(const FormStructure* result) {
+ RequestAutocompleteDialogClosed();
+ if (!result) {
+ ReturnAutocompleteResult(WebFormElement::AutocompleteResultErrorCancel,
+ FormData());
+ } else {
+ ReturnAutocompleteResult(WebFormElement::AutocompleteResultSuccess,
+ result->ToFormData());
+ }
+}
+
+void AutofillManager::OnLoadedServerPredictions(
+ const std::string& response_xml) {
+ scoped_ptr<autofill::AutocheckoutPageMetaData> page_meta_data(
+ new autofill::AutocheckoutPageMetaData());
+
+ // Parse and store the server predictions.
+ FormStructure::ParseQueryResponse(response_xml,
+ form_structures_.get(),
+ page_meta_data.get(),
+ *metric_logger_);
+
+ autocheckout_manager_.OnLoadedPageMetaData(page_meta_data.Pass());
+
+ // If the corresponding flag is set, annotate forms with the predicted types.
+ SendAutofillTypePredictions(form_structures_.get());
+}
+
+void AutofillManager::OnDidEndTextFieldEditing() {
+ if (external_delegate_)
+ external_delegate_->DidEndTextFieldEditing();
+}
+
+void AutofillManager::OnClickFailed(autofill::AutocheckoutStatus status) {
+ // TODO(ahutter): Plug into WalletClient.
+}
+
+std::string AutofillManager::GetAutocheckoutURLPrefix() const {
+ if (!web_contents())
+ return std::string();
+
+ autofill::autocheckout::WhitelistManager* whitelist_manager =
+ autofill::autocheckout::WhitelistManager::GetForBrowserContext(
+ web_contents()->GetBrowserContext());
+ return whitelist_manager->GetMatchedURLPrefix(web_contents()->GetURL());
+}
+
+bool AutofillManager::IsAutofillEnabled() const {
+ return manager_delegate_->GetPrefs()->GetBoolean(prefs::kAutofillEnabled);
+}
+
+void AutofillManager::SendAutofillTypePredictions(
+ const std::vector<FormStructure*>& forms) const {
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kShowAutofillTypePredictions))
+ return;
+
+ RenderViewHost* host = web_contents()->GetRenderViewHost();
+ if (!host)
+ return;
+
+ std::vector<FormDataPredictions> type_predictions;
+ FormStructure::GetFieldTypePredictions(forms, &type_predictions);
+ host->Send(
+ new AutofillMsg_FieldTypePredictionsAvailable(host->GetRoutingID(),
+ type_predictions));
+}
+
+void AutofillManager::ImportFormData(const FormStructure& submitted_form) {
+ const CreditCard* imported_credit_card;
+ if (!personal_data_->ImportFormData(submitted_form, &imported_credit_card))
+ return;
+
+ // If credit card information was submitted, we need to confirm whether to
+ // save it.
+ if (imported_credit_card) {
+ manager_delegate_->ConfirmSaveCreditCard(
+ *metric_logger_,
+ *imported_credit_card,
+ base::Bind(&PersonalDataManager::SaveImportedCreditCard,
+ base::Unretained(personal_data_), *imported_credit_card));
+ }
+}
+
+// Note that |submitted_form| is passed as a pointer rather than as a reference
+// so that we can get memory management right across threads. Note also that we
+// explicitly pass in all the time stamps of interest, as the cached ones might
+// get reset before this method executes.
+void AutofillManager::UploadFormDataAsyncCallback(
+ const FormStructure* submitted_form,
+ const TimeTicks& load_time,
+ const TimeTicks& interaction_time,
+ const TimeTicks& submission_time) {
+ submitted_form->LogQualityMetrics(*metric_logger_,
+ load_time,
+ interaction_time,
+ submission_time);
+
+ if (submitted_form->ShouldBeCrowdsourced())
+ UploadFormData(*submitted_form);
+}
+
+void AutofillManager::UploadFormData(const FormStructure& submitted_form) {
+ if (disable_download_manager_requests_)
+ return;
+
+ // Check if the form is among the forms that were recently auto-filled.
+ bool was_autofilled = false;
+ std::string form_signature = submitted_form.FormSignature();
+ for (std::list<std::string>::const_iterator it =
+ autofilled_form_signatures_.begin();
+ it != autofilled_form_signatures_.end() && !was_autofilled;
+ ++it) {
+ if (*it == form_signature)
+ was_autofilled = true;
+ }
+
+ FieldTypeSet non_empty_types;
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+
+ download_manager_.StartUploadRequest(submitted_form, was_autofilled,
+ non_empty_types);
+}
+
+void AutofillManager::Reset() {
+ form_structures_.clear();
+ has_logged_autofill_enabled_ = false;
+ has_logged_address_suggestions_count_ = false;
+ did_show_suggestions_ = false;
+ user_did_type_ = false;
+ user_did_autofill_ = false;
+ user_did_edit_autofilled_field_ = false;
+ forms_loaded_timestamp_ = TimeTicks();
+ initial_interaction_timestamp_ = TimeTicks();
+
+ if (external_delegate_)
+ external_delegate_->Reset();
+}
+
+AutofillManager::AutofillManager(content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate,
+ PersonalDataManager* personal_data)
+ : content::WebContentsObserver(web_contents),
+ manager_delegate_(delegate),
+ personal_data_(personal_data),
+ download_manager_(web_contents->GetBrowserContext(), this),
+ disable_download_manager_requests_(true),
+ autocomplete_history_manager_(web_contents),
+ autocheckout_manager_(this),
+ metric_logger_(new AutofillMetrics),
+ has_logged_autofill_enabled_(false),
+ has_logged_address_suggestions_count_(false),
+ did_show_suggestions_(false),
+ user_did_type_(false),
+ user_did_autofill_(false),
+ user_did_edit_autofilled_field_(false),
+ password_generation_enabled_(false),
+ external_delegate_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ DCHECK(web_contents);
+ DCHECK(manager_delegate_);
+ RegisterWithSyncService();
+ // Test code doesn't need registrar_.
+}
+
+void AutofillManager::set_metric_logger(const AutofillMetrics* metric_logger) {
+ metric_logger_.reset(metric_logger);
+}
+
+bool AutofillManager::GetHost(RenderViewHost** host) const {
+ if (!IsAutofillEnabled())
+ return false;
+
+ // No autofill data to return if the profiles are empty.
+ if (personal_data_->GetProfiles().empty() &&
+ personal_data_->credit_cards().empty()) {
+ return false;
+ }
+
+ *host = web_contents()->GetRenderViewHost();
+ if (!*host)
+ return false;
+
+ return true;
+}
+
+bool AutofillManager::GetProfileOrCreditCard(
+ int unique_id,
+ const FormGroup** form_group,
+ size_t* variant) const {
+ // Unpack the |unique_id| into component parts.
+ GUIDPair credit_card_guid;
+ GUIDPair profile_guid;
+ UnpackGUIDs(unique_id, &credit_card_guid, &profile_guid);
+ DCHECK(!base::IsValidGUID(credit_card_guid.first) ||
+ !base::IsValidGUID(profile_guid.first));
+
+ // Find the profile that matches the |profile_guid|, if one is specified.
+ // Otherwise find the credit card that matches the |credit_card_guid|,
+ // if specified.
+ if (base::IsValidGUID(profile_guid.first)) {
+ *form_group = personal_data_->GetProfileByGUID(profile_guid.first);
+ *variant = profile_guid.second;
+ } else if (base::IsValidGUID(credit_card_guid.first)) {
+ *form_group =
+ personal_data_->GetCreditCardByGUID(credit_card_guid.first);
+ *variant = credit_card_guid.second;
+ }
+
+ return !!*form_group;
+}
+
+bool AutofillManager::FindCachedForm(const FormData& form,
+ FormStructure** form_structure) const {
+ // Find the FormStructure that corresponds to |form|.
+ // Scan backward through the cached |form_structures_|, as updated versions of
+ // forms are added to the back of the list, whereas original versions of these
+ // forms might appear toward the beginning of the list. The communication
+ // protocol with the crowdsourcing server does not permit us to discard the
+ // original versions of the forms.
+ *form_structure = NULL;
+ for (std::vector<FormStructure*>::const_reverse_iterator iter =
+ form_structures_.rbegin();
+ iter != form_structures_.rend(); ++iter) {
+ if (**iter == form) {
+ *form_structure = *iter;
+
+ // The same form might be cached with multiple field counts: in some
+ // cases, non-autofillable fields are filtered out, whereas in other cases
+ // they are not. To avoid thrashing the cache, keep scanning until we
+ // find a cached version with the same number of fields, if there is one.
+ if ((*iter)->field_count() == form.fields.size())
+ break;
+ }
+ }
+
+ if (!(*form_structure))
+ return false;
+
+ return true;
+}
+
+bool AutofillManager::GetCachedFormAndField(const FormData& form,
+ const FormFieldData& field,
+ FormStructure** form_structure,
+ AutofillField** autofill_field) {
+ // Find the FormStructure that corresponds to |form|.
+ // If we do not have this form in our cache but it is parseable, we'll add it
+ // in the call to |UpdateCachedForm()|.
+ if (!FindCachedForm(form, form_structure) &&
+ !FormStructure(form, GetAutocheckoutURLPrefix()).ShouldBeParsed(false)) {
+ return false;
+ }
+
+ // Update the cached form to reflect any dynamic changes to the form data, if
+ // necessary.
+ if (!UpdateCachedForm(form, *form_structure, form_structure))
+ return false;
+
+ // No data to return if there are no auto-fillable fields.
+ if (!(*form_structure)->autofill_count())
+ return false;
+
+ // Find the AutofillField that corresponds to |field|.
+ *autofill_field = NULL;
+ for (std::vector<AutofillField*>::const_iterator iter =
+ (*form_structure)->begin();
+ iter != (*form_structure)->end(); ++iter) {
+ if ((**iter) == field) {
+ *autofill_field = *iter;
+ break;
+ }
+ }
+
+ // Even though we always update the cache, the field might not exist if the
+ // website disables autocomplete while the user is interacting with the form.
+ // See http://crbug.com/160476
+ return *autofill_field != NULL;
+}
+
+bool AutofillManager::UpdateCachedForm(const FormData& live_form,
+ const FormStructure* cached_form,
+ FormStructure** updated_form) {
+ bool needs_update =
+ (!cached_form ||
+ live_form.fields.size() != cached_form->field_count());
+ for (size_t i = 0; !needs_update && i < cached_form->field_count(); ++i) {
+ needs_update = *cached_form->field(i) != live_form.fields[i];
+ }
+
+ if (!needs_update)
+ return true;
+
+ if (form_structures_.size() >= kMaxFormCacheSize)
+ return false;
+
+ // Add the new or updated form to our cache.
+ form_structures_.push_back(
+ new FormStructure(live_form, GetAutocheckoutURLPrefix()));
+ *updated_form = *form_structures_.rbegin();
+ (*updated_form)->DetermineHeuristicTypes(*metric_logger_);
+
+ // If we have cached data, propagate it to the updated form.
+ if (cached_form) {
+ std::map<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);
+ std::map<string16, const AutofillField*>::iterator 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;
+ }
+ }
+
+ // 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.
+ }
+
+ // Annotate the updated form with its predicted types.
+ std::vector<FormStructure*> forms(1, *updated_form);
+ SendAutofillTypePredictions(forms);
+
+ return true;
+}
+
+void AutofillManager::GetProfileSuggestions(
+ FormStructure* form,
+ const FormFieldData& field,
+ AutofillFieldType type,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) const {
+ std::vector<AutofillFieldType> field_types(form->field_count());
+ for (size_t i = 0; i < form->field_count(); ++i) {
+ field_types[i] = form->field(i)->type();
+ }
+ std::vector<GUIDPair> guid_pairs;
+
+ personal_data_->GetProfileSuggestions(
+ type, field.value, field.is_autofilled, field_types,
+ values, labels, icons, &guid_pairs);
+
+ for (size_t i = 0; i < guid_pairs.size(); ++i) {
+ unique_ids->push_back(PackGUIDs(GUIDPair(std::string(), 0),
+ guid_pairs[i]));
+ }
+}
+
+void AutofillManager::GetCreditCardSuggestions(
+ const FormFieldData& field,
+ AutofillFieldType type,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) const {
+ std::vector<GUIDPair> guid_pairs;
+ personal_data_->GetCreditCardSuggestions(
+ type, field.value, values, labels, icons, &guid_pairs);
+
+ for (size_t i = 0; i < guid_pairs.size(); ++i) {
+ unique_ids->push_back(PackGUIDs(guid_pairs[i], GUIDPair(std::string(), 0)));
+ }
+}
+
+void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
+ std::vector<FormStructure*> non_queryable_forms;
+ std::string autocheckout_url_prefix = GetAutocheckoutURLPrefix();
+ for (std::vector<FormData>::const_iterator iter = forms.begin();
+ iter != forms.end(); ++iter) {
+ scoped_ptr<FormStructure> form_structure(
+ new FormStructure(*iter, autocheckout_url_prefix));
+ if (!form_structure->ShouldBeParsed(false))
+ continue;
+
+ form_structure->DetermineHeuristicTypes(*metric_logger_);
+
+ // 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())
+ form_structures_.push_back(form_structure.release());
+ else
+ non_queryable_forms.push_back(form_structure.release());
+ }
+
+ // If none of the forms were parsed, no use querying the server.
+ if (!form_structures_.empty() && !disable_download_manager_requests_) {
+ download_manager_.StartQueryRequest(form_structures_.get(),
+ *metric_logger_);
+ }
+
+ for (std::vector<FormStructure*>::const_iterator iter =
+ non_queryable_forms.begin();
+ iter != non_queryable_forms.end(); ++iter) {
+ form_structures_.push_back(*iter);
+ }
+
+ if (!form_structures_.empty())
+ metric_logger_->LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED);
+
+ // For the |non_queryable_forms|, we have all the field type info we're ever
+ // going to get about them. For the other forms, we'll wait until we get a
+ // response from the server.
+ SendAutofillTypePredictions(non_queryable_forms);
+}
+
+int AutofillManager::GUIDToID(const GUIDPair& guid) const {
+ if (!base::IsValidGUID(guid.first))
+ return 0;
+
+ std::map<GUIDPair, int>::const_iterator iter = guid_id_map_.find(guid);
+ if (iter == guid_id_map_.end()) {
+ int id = guid_id_map_.size() + 1;
+ guid_id_map_[guid] = id;
+ id_guid_map_[id] = guid;
+ return id;
+ } else {
+ return iter->second;
+ }
+}
+
+const GUIDPair AutofillManager::IDToGUID(int id) const {
+ if (id == 0)
+ return GUIDPair(std::string(), 0);
+
+ std::map<int, GUIDPair>::const_iterator iter = id_guid_map_.find(id);
+ if (iter == id_guid_map_.end()) {
+ NOTREACHED();
+ return GUIDPair(std::string(), 0);
+ }
+
+ return iter->second;
+}
+
+// When sending IDs (across processes) to the renderer we pack credit card and
+// profile IDs into a single integer. Credit card IDs are sent in the high
+// word and profile IDs are sent in the low word.
+int AutofillManager::PackGUIDs(const GUIDPair& cc_guid,
+ const GUIDPair& profile_guid) const {
+ int cc_id = GUIDToID(cc_guid);
+ int profile_id = GUIDToID(profile_guid);
+
+ DCHECK(cc_id <= std::numeric_limits<unsigned short>::max());
+ DCHECK(profile_id <= std::numeric_limits<unsigned short>::max());
+
+ return cc_id << std::numeric_limits<unsigned short>::digits | profile_id;
+}
+
+// When receiving IDs (across processes) from the renderer we unpack credit card
+// and profile IDs from a single integer. Credit card IDs are stored in the
+// high word and profile IDs are stored in the low word.
+void AutofillManager::UnpackGUIDs(int id,
+ GUIDPair* cc_guid,
+ GUIDPair* profile_guid) const {
+ int cc_id = id >> std::numeric_limits<unsigned short>::digits &
+ std::numeric_limits<unsigned short>::max();
+ int profile_id = id & std::numeric_limits<unsigned short>::max();
+
+ *cc_guid = IDToGUID(cc_id);
+ *profile_guid = IDToGUID(profile_id);
+}
+
+void AutofillManager::UpdateInitialInteractionTimestamp(
+ const TimeTicks& interaction_timestamp) {
+ if (initial_interaction_timestamp_.is_null() ||
+ interaction_timestamp < initial_interaction_timestamp_) {
+ initial_interaction_timestamp_ = interaction_timestamp;
+ }
+}
diff --git a/components/autofill/browser/autofill_manager.h b/components/autofill/browser/autofill_manager.h
new file mode 100644
index 0000000..094043a
--- /dev/null
+++ b/components/autofill/browser/autofill_manager.h
@@ -0,0 +1,442 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_MANAGER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_MANAGER_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/prefs/public/pref_change_registrar.h"
+#include "base/string16.h"
+#include "base/supports_user_data.h"
+#include "base/time.h"
+#include "components/autofill/browser/autocheckout_manager.h"
+#include "components/autofill/browser/autocomplete_history_manager.h"
+#include "components/autofill/browser/autofill_download.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/common/autocheckout_status.h"
+#include "components/autofill/common/form_data.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/ssl_status.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+
+class AutofillExternalDelegate;
+class AutofillField;
+class AutofillProfile;
+class AutofillMetrics;
+class CreditCard;
+class FormGroup;
+class GURL;
+class PrefRegistrySyncable;
+class ProfileSyncService;
+
+struct FormData;
+struct FormFieldData;
+struct PasswordFormFillData;
+struct ViewHostMsg_FrameNavigate_Params;
+
+namespace autofill {
+class AutofillManagerDelegate;
+class PasswordGenerator;
+}
+
+namespace content {
+class RenderViewHost;
+class WebContents;
+
+struct PasswordForm;
+}
+
+namespace gfx {
+class Rect;
+class RectF;
+}
+
+namespace IPC {
+class Message;
+}
+
+// Manages saving and restoring the user's personal information entered into web
+// forms.
+class AutofillManager : public content::WebContentsObserver,
+ public AutofillDownloadManager::Observer,
+ public base::SupportsUserData::Data {
+ public:
+ static void CreateForWebContentsAndDelegate(
+ content::WebContents* contents,
+ autofill::AutofillManagerDelegate* delegate);
+ static AutofillManager* FromWebContents(content::WebContents* contents);
+
+ // Registers our Enable/Disable Autofill pref.
+ static void RegisterUserPrefs(PrefRegistrySyncable* registry);
+
+ // Set an external delegate.
+ void SetExternalDelegate(AutofillExternalDelegate* delegate);
+
+ // Whether browser process will create and own the Autofill popup UI.
+ bool IsNativeUiEnabled();
+
+ // Called from our external delegate so they cannot be private.
+ virtual void OnFillAutofillFormData(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ int unique_id);
+ void OnDidShowAutofillSuggestions(bool is_new_popup);
+ void OnDidFillAutofillFormData(const base::TimeTicks& timestamp);
+ void OnShowAutofillDialog();
+ void OnDidPreviewAutofillFormData();
+ void OnShowPasswordGenerationPopup(const gfx::Rect& bounds,
+ int max_length,
+ const content::PasswordForm& form);
+
+ // Remove the credit card or Autofill profile that matches |unique_id|
+ // from the database.
+ void RemoveAutofillProfileOrCreditCard(int unique_id);
+
+ // Remove the specified Autocomplete entry.
+ void RemoveAutocompleteEntry(const string16& name, const string16& value);
+
+ // Returns the present web_contents state.
+ content::WebContents* GetWebContents() const;
+
+ // Returns the present form structures seen by Autofill manager.
+ const std::vector<FormStructure*>& GetFormStructures();
+
+ // Causes the dialog for request autocomplete feature to be shown.
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ autofill::DialogType dialog_type,
+ const base::Callback<void(const FormStructure*)>& callback);
+
+ // Happens when the autocomplete dialog runs its callback when being closed.
+ void RequestAutocompleteDialogClosed();
+
+ autofill::AutofillManagerDelegate* delegate() const {
+ return manager_delegate_;
+ }
+
+ protected:
+ // Only test code should subclass AutofillManager.
+ AutofillManager(content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate);
+ virtual ~AutofillManager();
+
+ // Test code should prefer to use this constructor.
+ AutofillManager(content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate,
+ PersonalDataManager* personal_data);
+
+ // Returns the value of the AutofillEnabled pref.
+ virtual bool IsAutofillEnabled() const;
+
+ // Uploads the form data to the Autofill server.
+ virtual void UploadFormData(const FormStructure& submitted_form);
+
+ // Reset cache.
+ void Reset();
+
+ // Informs the renderer of the current password generation state. This is a
+ // separate function to aid with testing.
+ virtual void SendPasswordGenerationStateToRenderer(
+ content::RenderViewHost* host,
+ bool enabled);
+
+ // Logs quality metrics for the |submitted_form| and uploads the form data
+ // to the crowdsourcing server, if appropriate.
+ virtual void UploadFormDataAsyncCallback(
+ const FormStructure* submitted_form,
+ const base::TimeTicks& load_time,
+ const base::TimeTicks& interaction_time,
+ const base::TimeTicks& submission_time);
+
+ // Maps GUIDs to and from IDs that are used to identify profiles and credit
+ // cards sent to and from the renderer process.
+ virtual int GUIDToID(const PersonalDataManager::GUIDPair& guid) const;
+ virtual const PersonalDataManager::GUIDPair IDToGUID(int id) const;
+
+ // Methods for packing and unpacking credit card and profile IDs for sending
+ // and receiving to and from the renderer process.
+ int PackGUIDs(const PersonalDataManager::GUIDPair& cc_guid,
+ const PersonalDataManager::GUIDPair& profile_guid) const;
+ void UnpackGUIDs(int id,
+ PersonalDataManager::GUIDPair* cc_guid,
+ PersonalDataManager::GUIDPair* profile_guid) const;
+
+ const AutofillMetrics* metric_logger() const { return metric_logger_.get(); }
+ void set_metric_logger(const AutofillMetrics* metric_logger);
+
+ ScopedVector<FormStructure>* form_structures() { return &form_structures_; }
+
+ // Exposed for testing.
+ AutofillExternalDelegate* external_delegate() {
+ return external_delegate_;
+ }
+
+ // Exposed for testing.
+ autofill::AutocheckoutManager* autocheckout_manager() {
+ return &autocheckout_manager_;
+ }
+
+ // Processes the submitted |form|, saving any new Autofill data and uploading
+ // the possible field types for the submitted fields to the crowdsouring
+ // server. Returns false if this form is not relevant for Autofill.
+ bool OnFormSubmitted(const FormData& form,
+ const base::TimeTicks& timestamp);
+
+ // Tell the renderer the current interactive autocomplete finished.
+ virtual void ReturnAutocompleteResult(
+ WebKit::WebFormElement::AutocompleteResult result,
+ const FormData& form_data);
+
+ private:
+ // content::WebContentsObserver:
+ virtual void RenderViewCreated(content::RenderViewHost* host) OVERRIDE;
+ virtual void DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void WebContentsDestroyed(
+ content::WebContents* web_contents) OVERRIDE;
+
+ // AutofillDownloadManager::Observer:
+ virtual void OnLoadedServerPredictions(
+ const std::string& response_xml) OVERRIDE;
+
+ void OnSyncStateChanged();
+
+ // Register as an observer with the sync service.
+ void RegisterWithSyncService();
+
+ // Called when password generation preference state changes.
+ void OnPasswordGenerationEnabledChanged();
+
+ // Determines what the current state of password generation is, and if it has
+ // changed from |password_generation_enabled_|. If it has changed or if
+ // |new_renderer| is true, it notifies the renderer of this change via
+ // SendPasswordGenerationStateToRenderer.
+ void UpdatePasswordGenerationState(content::RenderViewHost* host,
+ bool new_renderer);
+
+ void OnFormsSeen(const std::vector<FormData>& forms,
+ const base::TimeTicks& timestamp);
+ void OnTextFieldDidChange(const FormData& form,
+ const FormFieldData& field,
+ const base::TimeTicks& timestamp);
+
+ // The |bounding_box| is a window relative value.
+ void OnQueryFormFieldAutofill(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounding_box,
+ bool display_warning);
+ void OnDidEndTextFieldEditing();
+ void OnHideAutofillPopup();
+ void OnAddPasswordFormMapping(
+ const FormFieldData& form,
+ const PasswordFormFillData& fill_data);
+ void OnShowPasswordSuggestions(const FormFieldData& field,
+ const gfx::RectF& bounds,
+ const std::vector<string16>& suggestions);
+ void OnSetDataList(const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& unique_ids);
+
+ // Requests an interactive autocomplete UI be shown.
+ void OnRequestAutocomplete(const FormData& form,
+ const GURL& frame_url,
+ const content::SSLStatus& ssl_status);
+
+ // Passes return data for an OnRequestAutocomplete call back to the page.
+ void ReturnAutocompleteData(const FormStructure* result);
+
+ // Called to signal clicking an element failed in some way during an
+ // Autocheckout flow.
+ void OnClickFailed(autofill::AutocheckoutStatus status);
+
+ // Returns the matched whitelist URL prefix for the current tab's url.
+ std::string GetAutocheckoutURLPrefix() const;
+
+ // Fills |host| with the RenderViewHost for this tab.
+ // Returns false if Autofill is disabled or if the host is unavailable.
+ bool GetHost(content::RenderViewHost** host) const WARN_UNUSED_RESULT;
+
+ // Unpacks |unique_id| and fills |form_group| and |variant| with the
+ // appropriate data source and variant index. Returns false if the unpacked
+ // id cannot be found.
+ bool GetProfileOrCreditCard(int unique_id,
+ const FormGroup** form_group,
+ size_t* variant) const WARN_UNUSED_RESULT;
+
+ // Fills |form_structure| cached element corresponding to |form|.
+ // Returns false if the cached element was not found.
+ bool FindCachedForm(const FormData& form,
+ FormStructure** form_structure) const WARN_UNUSED_RESULT;
+
+ // Fills |form_structure| and |autofill_field| with the cached elements
+ // corresponding to |form| and |field|. This might have the side-effect of
+ // updating the cache. Returns false if the |form| is not autofillable, or if
+ // it is not already present in the cache and the cache is full.
+ bool GetCachedFormAndField(const FormData& form,
+ const FormFieldData& field,
+ FormStructure** form_structure,
+ AutofillField** autofill_field) WARN_UNUSED_RESULT;
+
+ // Re-parses |live_form| and adds the result to |form_structures_|.
+ // |cached_form| should be a pointer to the existing version of the form, or
+ // NULL if no cached version exists. The updated form is then written into
+ // |updated_form|. Returns false if the cache could not be updated.
+ bool UpdateCachedForm(const FormData& live_form,
+ const FormStructure* cached_form,
+ FormStructure** updated_form) WARN_UNUSED_RESULT;
+
+ // Returns a list of values from the stored profiles that match |type| and the
+ // value of |field| and returns the labels of the matching profiles. |labels|
+ // is filled with the Profile label.
+ void GetProfileSuggestions(FormStructure* form,
+ const FormFieldData& field,
+ AutofillFieldType type,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) const;
+
+ // Returns a list of values from the stored credit cards that match |type| and
+ // the value of |field| and returns the labels of the matching credit cards.
+ void GetCreditCardSuggestions(const FormFieldData& field,
+ AutofillFieldType type,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) const;
+
+ // Parses the forms using heuristic matching and querying the Autofill server.
+ void ParseForms(const std::vector<FormData>& forms);
+
+ // Imports the form data, submitted by the user, into |personal_data_|.
+ void ImportFormData(const FormStructure& submitted_form);
+
+ // If |initial_interaction_timestamp_| is unset or is set to a later time than
+ // |interaction_timestamp|, updates the cached timestamp. The latter check is
+ // needed because IPC messages can arrive out of order.
+ void UpdateInitialInteractionTimestamp(
+ const base::TimeTicks& interaction_timestamp);
+
+ // Send our current field type predictions to the renderer. This is a no-op if
+ // the appropriate command-line flag is not set.
+ void SendAutofillTypePredictions(
+ const std::vector<FormStructure*>& forms) const;
+
+ autofill::AutofillManagerDelegate* const manager_delegate_;
+
+ // The personal data manager, used to save and load personal data to/from the
+ // web database. This is overridden by the AutofillManagerTest.
+ // Weak reference.
+ // May be NULL. NULL indicates OTR.
+ PersonalDataManager* personal_data_;
+
+ std::list<std::string> autofilled_form_signatures_;
+
+ // Handles queries and uploads to Autofill servers.
+ AutofillDownloadManager download_manager_;
+
+ // Should be set to true in AutofillManagerTest and other tests, false in
+ // AutofillDownloadManagerTest and in non-test environment. Is false by
+ // default for the public constructor, and true by default for the test-only
+ // constructors.
+ bool disable_download_manager_requests_;
+
+ // Handles single-field autocomplete form data.
+ AutocompleteHistoryManager autocomplete_history_manager_;
+
+ // Handles autocheckout flows.
+ autofill::AutocheckoutManager autocheckout_manager_;
+
+ // For logging UMA metrics. Overridden by metrics tests.
+ scoped_ptr<const AutofillMetrics> metric_logger_;
+ // Have we logged whether Autofill is enabled for this page load?
+ bool has_logged_autofill_enabled_;
+ // Have we logged an address suggestions count metric for this page?
+ bool has_logged_address_suggestions_count_;
+ // Have we shown Autofill suggestions at least once?
+ bool did_show_suggestions_;
+ // Has the user manually edited at least one form field among the autofillable
+ // ones?
+ bool user_did_type_;
+ // Has the user autofilled a form on this page?
+ bool user_did_autofill_;
+ // Has the user edited a field that was previously autofilled?
+ bool user_did_edit_autofilled_field_;
+ // When the page finished loading.
+ base::TimeTicks forms_loaded_timestamp_;
+ // When the user first interacted with a potentially fillable form on this
+ // page.
+ base::TimeTicks initial_interaction_timestamp_;
+ // If password generation is enabled. We cache this value so that we don't
+ // spam the renderer with messages during startup when the sync state
+ // is changing rapidly.
+ bool password_generation_enabled_;
+ // Listens for changes to the 'enabled' state for password generation.
+ PrefChangeRegistrar registrar_;
+
+ // To be passed to the password generation UI to generate the password.
+ scoped_ptr<autofill::PasswordGenerator> password_generator_;
+
+ // Our copy of the form data.
+ ScopedVector<FormStructure> form_structures_;
+
+ // GUID to ID mapping. We keep two maps to convert back and forth.
+ mutable std::map<PersonalDataManager::GUIDPair, int> guid_id_map_;
+ mutable std::map<int, PersonalDataManager::GUIDPair> id_guid_map_;
+
+ // Delegate to perform external processing (display, selection) on
+ // our behalf. Weak.
+ AutofillExternalDelegate* external_delegate_;
+
+ base::WeakPtrFactory<AutofillManager> weak_ptr_factory_;
+
+ friend class AutofillManagerTest;
+ friend class FormStructureBrowserTest;
+ FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
+ DeterminePossibleFieldTypesForUpload);
+ FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
+ DeterminePossibleFieldTypesForUploadStressTest);
+ FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
+ DisabledAutofillDispatchesError);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSuggestionsCount);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtPageLoad);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, DeveloperEngagement);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, FormFillDuration);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
+ NoQualityMetricsForNonAutofillableForms);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, QualityMetrics);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, QualityMetricsForFailure);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, QualityMetricsWithExperimentId);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, SaneMetricsWithCacheMismatch);
+ FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest, TestExternalDelegate);
+ FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
+ TestTabContentsWithExternalDelegate);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest,
+ UserHappinessFormLoadAndSubmission);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, UserHappinessFormInteraction);
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillManager);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_MANAGER_H_
diff --git a/components/autofill/browser/autofill_manager_delegate.h b/components/autofill/browser/autofill_manager_delegate.h
new file mode 100644
index 0000000..1e228e1
--- /dev/null
+++ b/components/autofill/browser/autofill_manager_delegate.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_MANAGER_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_MANAGER_DELEGATE_H_
+
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/string16.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace autofill {
+class PasswordGenerator;
+}
+
+namespace content {
+struct PasswordForm;
+struct SSLStatus;
+}
+
+namespace gfx {
+class Rect;
+class RectF;
+}
+
+class AutofillMetrics;
+class AutofillPopupDelegate;
+class CreditCard;
+class FormStructure;
+class GURL;
+class InfoBarService;
+class PersonalDataManager;
+class PrefService;
+class ProfileSyncServiceBase;
+
+struct FormData;
+
+namespace autofill {
+
+enum DialogType {
+ // Autofill dialog for the Autocheckout feature.
+ DIALOG_TYPE_AUTOCHECKOUT,
+ // Autofill dialog for the requestAutocomplete feature.
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE,
+};
+
+// A delegate interface that needs to be supplied to AutofillManager
+// by the embedder.
+//
+// Each delegate instance is associated with a given context within
+// which an AutofillManager is used (e.g. a single tab), so when we
+// say "for the delegate" below, we mean "in the execution context the
+// delegate is associated with" (e.g. for the tab the AutofillManager is
+// attached to).
+class AutofillManagerDelegate {
+ public:
+ virtual ~AutofillManagerDelegate() {}
+
+ // Gets the PersonalDataManager instance associated with the delegate.
+ virtual PersonalDataManager* GetPersonalDataManager() = 0;
+
+ // Gets the preferences associated with the delegate.
+ virtual PrefService* GetPrefs() = 0;
+
+ // Hides the associated request autocomplete dialog (if it exists).
+ virtual void HideRequestAutocompleteDialog() = 0;
+
+ // Returns true if saving passwords is currently enabled for the
+ // delegate.
+ virtual bool IsSavingPasswordsEnabled() const = 0;
+
+ // Returns true if Sync is enabled for the passwords datatype.
+ virtual bool IsPasswordSyncEnabled() const = 0;
+
+ // Sets a callback that will be called when sync state changes.
+ //
+ // Set the callback to an object for which |is_null()| evaluates to
+ // true to stop receiving notifications
+ // (e.g. SetSyncStateChangedCallback(base::Closure())).
+ virtual void SetSyncStateChangedCallback(const base::Closure& callback) = 0;
+
+ // Causes an error explaining that Autocheckout has failed to be displayed to
+ // the user.
+ virtual void OnAutocheckoutError() = 0;
+
+ // Causes the Autofill settings UI to be shown.
+ virtual void ShowAutofillSettings() = 0;
+
+ // Run |save_card_callback| if the credit card should be imported as personal
+ // data. |metric_logger| can be used to log user actions.
+ virtual void ConfirmSaveCreditCard(
+ const AutofillMetrics& metric_logger,
+ const CreditCard& credit_card,
+ const base::Closure& save_card_callback) = 0;
+
+ // Causes the password generation bubble UI to be shown using the
+ // specified form with the given bounds.
+ virtual void ShowPasswordGenerationBubble(
+ const gfx::Rect& bounds,
+ const content::PasswordForm& form,
+ autofill::PasswordGenerator* generator) = 0;
+
+ // Causes the Autocheckout bubble UI to be displayed. |bounding_box| is the
+ // anchor for the bubble. |native_view| is the parent view of the bubble.
+ // |callback| is run if the bubble is accepted.
+ virtual void ShowAutocheckoutBubble(
+ const gfx::RectF& bounding_box,
+ const gfx::NativeView& native_view,
+ const base::Closure& callback) = 0;
+
+ // Causes the dialog for request autocomplete feature to be shown.
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ const AutofillMetrics& metric_logger,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*)>& callback) = 0;
+
+ // Called when the dialog for request autocomplete closes. (So UI code will
+ // free memory, etc.)
+ virtual void RequestAutocompleteDialogClosed() = 0;
+
+ // Shows an Autofill popup with the given |values|, |labels|, |icons|, and
+ // |identifiers| for the element at |element_bounds|. |delegate| will be
+ // notified of popup events.
+ virtual void ShowAutofillPopup(const gfx::RectF& element_bounds,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& identifiers,
+ AutofillPopupDelegate* delegate) = 0;
+
+ // Hide the Autofill popup if one is currently showing.
+ virtual void HideAutofillPopup() = 0;
+
+ // Updates the Autocheckout progress bar. |value| must be in [0.0, 1.0].
+ virtual void UpdateProgressBar(double value) = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_MANAGER_DELEGATE_H_
diff --git a/components/autofill/browser/autofill_manager_unittest.cc b/components/autofill/browser/autofill_manager_unittest.cc
new file mode 100644
index 0000000..6584def
--- /dev/null
+++ b/components/autofill/browser/autofill_manager_unittest.cc
@@ -0,0 +1,3228 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/prefs/pref_service.h"
+#include "base/string16.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time.h"
+#include "base/tuple.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/password_manager/password_manager_delegate_impl.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autocomplete_history_manager.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/browser/test_autofill_external_delegate.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/ssl_status.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/rect.h"
+
+typedef PersonalDataManager::GUIDPair GUIDPair;
+using content::BrowserThread;
+using content::WebContents;
+using testing::_;
+using WebKit::WebFormElement;
+
+namespace {
+
+// The page ID sent to the AutofillManager from the RenderView, used to send
+// an IPC message back to the renderer.
+const int kDefaultPageID = 137;
+
+typedef Tuple5<int,
+ std::vector<string16>,
+ std::vector<string16>,
+ std::vector<string16>,
+ std::vector<int> > AutofillParam;
+
+class TestPersonalDataManager : public PersonalDataManager {
+ public:
+ TestPersonalDataManager() {
+ CreateTestAutofillProfiles(&web_profiles_);
+ CreateTestCreditCards(&credit_cards_);
+ }
+
+ void SetBrowserContext(content::BrowserContext* context) {
+ set_browser_context(context);
+ }
+
+ // Factory method for keyed service. PersonalDataManager is NULL for testing.
+ static ProfileKeyedService* Build(Profile* profile) {
+ return NULL;
+ }
+
+ MOCK_METHOD1(SaveImportedProfile, void(const AutofillProfile&));
+
+ AutofillProfile* GetProfileWithGUID(const char* guid) {
+ for (std::vector<AutofillProfile *>::iterator it = web_profiles_.begin();
+ it != web_profiles_.end(); ++it) {
+ if (!(*it)->guid().compare(guid))
+ return *it;
+ }
+ return NULL;
+ }
+
+ CreditCard* GetCreditCardWithGUID(const char* guid) {
+ for (std::vector<CreditCard *>::iterator it = credit_cards_.begin();
+ it != credit_cards_.end(); ++it){
+ if (!(*it)->guid().compare(guid))
+ return *it;
+ }
+ return NULL;
+ }
+
+ void AddProfile(AutofillProfile* profile) {
+ web_profiles_.push_back(profile);
+ }
+
+ void AddCreditCard(CreditCard* credit_card) {
+ credit_cards_.push_back(credit_card);
+ }
+
+ virtual void RemoveByGUID(const std::string& guid) OVERRIDE {
+ CreditCard* credit_card = GetCreditCardWithGUID(guid.c_str());
+ if (credit_card) {
+ credit_cards_.erase(
+ std::remove(credit_cards_.begin(), credit_cards_.end(), credit_card),
+ credit_cards_.end());
+ }
+
+ AutofillProfile* profile = GetProfileWithGUID(guid.c_str());
+ if (profile) {
+ web_profiles_.erase(
+ std::remove(web_profiles_.begin(), web_profiles_.end(), profile),
+ web_profiles_.end());
+ }
+ }
+
+ // Do nothing (auxiliary profiles will be created in
+ // CreateTestAuxiliaryProfile).
+ virtual void LoadAuxiliaryProfiles() OVERRIDE {}
+
+ void ClearAutofillProfiles() {
+ web_profiles_.clear();
+ }
+
+ void ClearCreditCards() {
+ credit_cards_.clear();
+ }
+
+ void CreateTestAuxiliaryProfiles() {
+ CreateTestAutofillProfiles(&auxiliary_profiles_);
+ }
+
+ void CreateTestCreditCardsYearAndMonth(const char* year, const char* month) {
+ ClearCreditCards();
+ CreditCard* credit_card = new CreditCard;
+ autofill_test::SetCreditCardInfo(credit_card, "Miku Hatsune",
+ "4234567890654321", // Visa
+ month, year);
+ credit_card->set_guid("00000000-0000-0000-0000-000000000007");
+ credit_cards_.push_back(credit_card);
+ }
+
+ private:
+ void CreateTestAutofillProfiles(ScopedVector<AutofillProfile>* profiles) {
+ AutofillProfile* profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "Elvis", "Aaron",
+ "Presley", "theking@gmail.com", "RCA",
+ "3734 Elvis Presley Blvd.", "Apt. 10",
+ "Memphis", "Tennessee", "38116", "US",
+ "12345678901");
+ profile->set_guid("00000000-0000-0000-0000-000000000001");
+ profiles->push_back(profile);
+ profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "Charles", "Hardin",
+ "Holley", "buddy@gmail.com", "Decca",
+ "123 Apple St.", "unit 6", "Lubbock",
+ "Texas", "79401", "US", "23456789012");
+ profile->set_guid("00000000-0000-0000-0000-000000000002");
+ profiles->push_back(profile);
+ profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "", "", "", "", "", "", "", "", "",
+ "", "", "");
+ profile->set_guid("00000000-0000-0000-0000-000000000003");
+ profiles->push_back(profile);
+ }
+
+ void CreateTestCreditCards(ScopedVector<CreditCard>* credit_cards) {
+ CreditCard* credit_card = new CreditCard;
+ autofill_test::SetCreditCardInfo(credit_card, "Elvis Presley",
+ "4234 5678 9012 3456", // Visa
+ "04", "2012");
+ credit_card->set_guid("00000000-0000-0000-0000-000000000004");
+ credit_cards->push_back(credit_card);
+
+ credit_card = new CreditCard;
+ autofill_test::SetCreditCardInfo(credit_card, "Buddy Holly",
+ "5187654321098765", // Mastercard
+ "10", "2014");
+ credit_card->set_guid("00000000-0000-0000-0000-000000000005");
+ credit_cards->push_back(credit_card);
+
+ credit_card = new CreditCard;
+ autofill_test::SetCreditCardInfo(credit_card, "", "", "", "");
+ credit_card->set_guid("00000000-0000-0000-0000-000000000006");
+ credit_cards->push_back(credit_card);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TestPersonalDataManager);
+};
+
+// Populates |form| with data corresponding to a simple address form.
+// Note that this actually appends fields to the form data, which can be useful
+// for building up more complex test forms.
+void CreateTestAddressFormData(FormData* form) {
+ form->name = ASCIIToUTF16("MyForm");
+ form->method = ASCIIToUTF16("POST");
+ form->origin = GURL("http://myform.com/form.html");
+ form->action = GURL("http://myform.com/submit.html");
+ form->user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First Name", "firstname", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Middle Name", "middlename", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last Name", "lastname", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 1", "addr1", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 2", "addr2", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City", "city", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State", "state", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Postal Code", "zipcode", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Country", "country", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone Number", "phonenumber", "", "tel", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email", "email", "", "email", &field);
+ form->fields.push_back(field);
+}
+
+// Populates |form| with data corresponding to a simple credit card form.
+// Note that this actually appends fields to the form data, which can be useful
+// for building up more complex test forms.
+void CreateTestCreditCardFormData(FormData* form,
+ bool is_https,
+ bool use_month_type) {
+ form->name = ASCIIToUTF16("MyForm");
+ form->method = ASCIIToUTF16("POST");
+ if (is_https) {
+ form->origin = GURL("https://myform.com/form.html");
+ form->action = GURL("https://myform.com/submit.html");
+ } else {
+ form->origin = GURL("http://myform.com/form.html");
+ form->action = GURL("http://myform.com/submit.html");
+ }
+ form->user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on Card", "nameoncard", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number", "cardnumber", "", "text", &field);
+ form->fields.push_back(field);
+ if (use_month_type) {
+ autofill_test::CreateTestFormField(
+ "Expiration Date", "ccmonth", "", "month", &field);
+ form->fields.push_back(field);
+ } else {
+ autofill_test::CreateTestFormField(
+ "Expiration Date", "ccmonth", "", "text", &field);
+ form->fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "", "ccyear", "", "text", &field);
+ form->fields.push_back(field);
+ }
+}
+
+void ExpectSuggestions(int page_id,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& unique_ids,
+ int expected_page_id,
+ size_t expected_num_suggestions,
+ const string16 expected_values[],
+ const string16 expected_labels[],
+ const string16 expected_icons[],
+ const int expected_unique_ids[]) {
+ EXPECT_EQ(expected_page_id, page_id);
+ ASSERT_EQ(expected_num_suggestions, values.size());
+ ASSERT_EQ(expected_num_suggestions, labels.size());
+ ASSERT_EQ(expected_num_suggestions, icons.size());
+ ASSERT_EQ(expected_num_suggestions, unique_ids.size());
+ for (size_t i = 0; i < expected_num_suggestions; ++i) {
+ SCOPED_TRACE(StringPrintf("i: %" PRIuS, i));
+ EXPECT_EQ(expected_values[i], values[i]);
+ EXPECT_EQ(expected_labels[i], labels[i]);
+ EXPECT_EQ(expected_icons[i], icons[i]);
+ EXPECT_EQ(expected_unique_ids[i], unique_ids[i]);
+ }
+}
+
+void ExpectFilledField(const char* expected_label,
+ const char* expected_name,
+ const char* expected_value,
+ const char* expected_form_control_type,
+ const FormFieldData& field) {
+ SCOPED_TRACE(expected_label);
+ EXPECT_EQ(UTF8ToUTF16(expected_label), field.label);
+ EXPECT_EQ(UTF8ToUTF16(expected_name), field.name);
+ EXPECT_EQ(UTF8ToUTF16(expected_value), field.value);
+ EXPECT_EQ(expected_form_control_type, field.form_control_type);
+}
+
+// Verifies that the |filled_form| has been filled with the given data.
+// Verifies address fields if |has_address_fields| is true, and verifies
+// credit card fields if |has_credit_card_fields| is true. Verifies both if both
+// are true. |use_month_type| is used for credit card input month type.
+void ExpectFilledForm(int page_id,
+ const FormData& filled_form,
+ int expected_page_id,
+ const char* first,
+ const char* middle,
+ const char* last,
+ const char* address1,
+ const char* address2,
+ const char* city,
+ const char* state,
+ const char* postal_code,
+ const char* country,
+ const char* phone,
+ const char* email,
+ const char* name_on_card,
+ const char* card_number,
+ const char* expiration_month,
+ const char* expiration_year,
+ bool has_address_fields,
+ bool has_credit_card_fields,
+ bool use_month_type) {
+ // The number of fields in the address and credit card forms created above.
+ const size_t kAddressFormSize = 11;
+ const size_t kCreditCardFormSize = use_month_type ? 3 : 4;
+
+ EXPECT_EQ(expected_page_id, page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), filled_form.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), filled_form.method);
+ if (has_credit_card_fields) {
+ EXPECT_EQ(GURL("https://myform.com/form.html"), filled_form.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), filled_form.action);
+ } else {
+ EXPECT_EQ(GURL("http://myform.com/form.html"), filled_form.origin);
+ EXPECT_EQ(GURL("http://myform.com/submit.html"), filled_form.action);
+ }
+ EXPECT_TRUE(filled_form.user_submitted);
+
+ size_t form_size = 0;
+ if (has_address_fields)
+ form_size += kAddressFormSize;
+ if (has_credit_card_fields)
+ form_size += kCreditCardFormSize;
+ ASSERT_EQ(form_size, filled_form.fields.size());
+
+ if (has_address_fields) {
+ ExpectFilledField("First Name", "firstname", first, "text",
+ filled_form.fields[0]);
+ ExpectFilledField("Middle Name", "middlename", middle, "text",
+ filled_form.fields[1]);
+ ExpectFilledField("Last Name", "lastname", last, "text",
+ filled_form.fields[2]);
+ ExpectFilledField("Address Line 1", "addr1", address1, "text",
+ filled_form.fields[3]);
+ ExpectFilledField("Address Line 2", "addr2", address2, "text",
+ filled_form.fields[4]);
+ ExpectFilledField("City", "city", city, "text",
+ filled_form.fields[5]);
+ ExpectFilledField("State", "state", state, "text",
+ filled_form.fields[6]);
+ ExpectFilledField("Postal Code", "zipcode", postal_code, "text",
+ filled_form.fields[7]);
+ ExpectFilledField("Country", "country", country, "text",
+ filled_form.fields[8]);
+ ExpectFilledField("Phone Number", "phonenumber", phone, "tel",
+ filled_form.fields[9]);
+ ExpectFilledField("Email", "email", email, "email",
+ filled_form.fields[10]);
+ }
+
+ if (has_credit_card_fields) {
+ size_t offset = has_address_fields? kAddressFormSize : 0;
+ ExpectFilledField("Name on Card", "nameoncard", name_on_card, "text",
+ filled_form.fields[offset + 0]);
+ ExpectFilledField("Card Number", "cardnumber", card_number, "text",
+ filled_form.fields[offset + 1]);
+ if (use_month_type) {
+ std::string exp_year = expiration_year;
+ std::string exp_month = expiration_month;
+ std::string date;
+ if (!exp_year.empty() && !exp_month.empty())
+ date = exp_year + "-" + exp_month;
+
+ ExpectFilledField("Expiration Date", "ccmonth", date.c_str(), "month",
+ filled_form.fields[offset + 2]);
+ } else {
+ ExpectFilledField("Expiration Date", "ccmonth", expiration_month, "text",
+ filled_form.fields[offset + 2]);
+ ExpectFilledField("", "ccyear", expiration_year, "text",
+ filled_form.fields[offset + 3]);
+ }
+ }
+}
+
+void ExpectFilledAddressFormElvis(int page_id,
+ const FormData& filled_form,
+ int expected_page_id,
+ bool has_credit_card_fields) {
+ ExpectFilledForm(page_id, filled_form, expected_page_id, "Elvis", "Aaron",
+ "Presley", "3734 Elvis Presley Blvd.", "Apt. 10", "Memphis",
+ "Tennessee", "38116", "United States", "12345678901",
+ "theking@gmail.com", "", "", "", "", true,
+ has_credit_card_fields, false);
+}
+
+void ExpectFilledCreditCardFormElvis(int page_id,
+ const FormData& filled_form,
+ int expected_page_id,
+ bool has_address_fields) {
+ ExpectFilledForm(page_id, filled_form, expected_page_id,
+ "", "", "", "", "", "", "", "", "", "", "",
+ "Elvis Presley", "4234567890123456", "04", "2012",
+ has_address_fields, true, false);
+}
+
+void ExpectFilledCreditCardYearMonthWithYearMonth(int page_id,
+ const FormData& filled_form,
+ int expected_page_id,
+ bool has_address_fields,
+ const char* year,
+ const char* month) {
+ ExpectFilledForm(page_id, filled_form, expected_page_id,
+ "", "", "", "", "", "", "", "", "", "", "",
+ "Miku Hatsune", "4234567890654321", month, year,
+ has_address_fields, true, true);
+}
+
+class TestAutofillManager : public AutofillManager {
+ public:
+ TestAutofillManager(content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate,
+ TestPersonalDataManager* personal_data)
+ : AutofillManager(web_contents, delegate, personal_data),
+ personal_data_(personal_data),
+ autofill_enabled_(true),
+ did_finish_async_form_submit_(false),
+ message_loop_is_running_(false) {
+ }
+ virtual ~TestAutofillManager() {}
+
+ virtual bool IsAutofillEnabled() const OVERRIDE { return autofill_enabled_; }
+
+ void set_autofill_enabled(bool autofill_enabled) {
+ autofill_enabled_ = autofill_enabled;
+ }
+
+ const std::vector<std::pair<WebFormElement::AutocompleteResult, FormData> >&
+ request_autocomplete_results() const {
+ return request_autocomplete_results_;
+ }
+
+
+ void set_expected_submitted_field_types(
+ const std::vector<FieldTypeSet>& expected_types) {
+ expected_submitted_field_types_ = expected_types;
+ }
+
+ virtual void UploadFormDataAsyncCallback(
+ const FormStructure* submitted_form,
+ const base::TimeTicks& load_time,
+ const base::TimeTicks& interaction_time,
+ const base::TimeTicks& submission_time) OVERRIDE {
+ if (message_loop_is_running_) {
+ MessageLoop::current()->Quit();
+ message_loop_is_running_ = false;
+ } else {
+ did_finish_async_form_submit_ = true;
+ }
+
+ // If we have expected field types set, make sure they match.
+ if (!expected_submitted_field_types_.empty()) {
+ ASSERT_EQ(expected_submitted_field_types_.size(),
+ submitted_form->field_count());
+ for (size_t i = 0; i < expected_submitted_field_types_.size(); ++i) {
+ SCOPED_TRACE(
+ StringPrintf("Field %d with value %s", static_cast<int>(i),
+ UTF16ToUTF8(submitted_form->field(i)->value).c_str()));
+ const FieldTypeSet& possible_types =
+ submitted_form->field(i)->possible_types();
+ EXPECT_EQ(expected_submitted_field_types_[i].size(),
+ possible_types.size());
+ for (FieldTypeSet::const_iterator it =
+ expected_submitted_field_types_[i].begin();
+ it != expected_submitted_field_types_[i].end(); ++it) {
+ EXPECT_TRUE(possible_types.count(*it))
+ << "Expected type: " << AutofillType::FieldTypeToString(*it);
+ }
+ }
+ }
+
+ AutofillManager::UploadFormDataAsyncCallback(submitted_form,
+ load_time,
+ interaction_time,
+ submission_time);
+ }
+
+ // Wait for the asynchronous OnFormSubmitted() call to complete.
+ void WaitForAsyncFormSubmit() {
+ if (!did_finish_async_form_submit_) {
+ // TODO(isherman): It seems silly to need this variable. Is there some
+ // way I can just query the message loop's state?
+ message_loop_is_running_ = true;
+ MessageLoop::current()->Run();
+ } else {
+ did_finish_async_form_submit_ = false;
+ }
+ }
+
+ virtual void UploadFormData(const FormStructure& submitted_form) OVERRIDE {
+ submitted_form_signature_ = submitted_form.FormSignature();
+ }
+
+ virtual void SendPasswordGenerationStateToRenderer(
+ content::RenderViewHost* host, bool enabled) OVERRIDE {
+ sent_states_.push_back(enabled);
+ }
+
+ const std::vector<bool>& GetSentStates() {
+ return sent_states_;
+ }
+
+ void ClearSentStates() {
+ sent_states_.clear();
+ }
+
+ const std::string GetSubmittedFormSignature() {
+ return submitted_form_signature_;
+ }
+
+ AutofillProfile* GetProfileWithGUID(const char* guid) {
+ return personal_data_->GetProfileWithGUID(guid);
+ }
+
+ CreditCard* GetCreditCardWithGUID(const char* guid) {
+ return personal_data_->GetCreditCardWithGUID(guid);
+ }
+
+ void AddProfile(AutofillProfile* profile) {
+ personal_data_->AddProfile(profile);
+ }
+
+ void AddCreditCard(CreditCard* credit_card) {
+ personal_data_->AddCreditCard(credit_card);
+ }
+
+ int GetPackedCreditCardID(int credit_card_id) {
+ std::string credit_card_guid =
+ base::StringPrintf("00000000-0000-0000-0000-%012d", credit_card_id);
+
+ return PackGUIDs(GUIDPair(credit_card_guid, 0), GUIDPair(std::string(), 0));
+ }
+
+ void AddSeenForm(FormStructure* form) {
+ form_structures()->push_back(form);
+ }
+
+ virtual void ReturnAutocompleteResult(
+ WebFormElement::AutocompleteResult result,
+ const FormData& form_data) OVERRIDE {
+ request_autocomplete_results_.push_back(std::make_pair(result, form_data));
+ }
+
+ private:
+ // Weak reference.
+ TestPersonalDataManager* personal_data_;
+
+ bool autofill_enabled_;
+ std::vector<std::pair<WebFormElement::AutocompleteResult, FormData> >
+ request_autocomplete_results_;
+
+ bool did_finish_async_form_submit_;
+ bool message_loop_is_running_;
+
+ std::string submitted_form_signature_;
+ std::vector<FieldTypeSet> expected_submitted_field_types_;
+ std::vector<bool> sent_states_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillManager);
+};
+
+} // namespace
+
+class AutofillManagerTest : public ChromeRenderViewHostTestHarness {
+ public:
+ AutofillManagerTest()
+ : ChromeRenderViewHostTestHarness(),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE),
+ io_thread_(BrowserThread::IO) {
+ }
+
+ virtual ~AutofillManagerTest() {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ TestingProfile* profile = CreateProfile();
+ profile->CreateRequestContext();
+ browser_context_.reset(profile);
+ PersonalDataManagerFactory::GetInstance()->SetTestingFactory(
+ profile, TestPersonalDataManager::Build);
+
+ ChromeRenderViewHostTestHarness::SetUp();
+ io_thread_.StartIOThread();
+ autofill::TabAutofillManagerDelegate::CreateForWebContents(web_contents());
+ personal_data_.SetBrowserContext(profile);
+ autofill_manager_.reset(new TestAutofillManager(
+ web_contents(),
+ autofill::TabAutofillManagerDelegate::FromWebContents(web_contents()),
+ &personal_data_));
+
+ file_thread_.Start();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Order of destruction is important as AutofillManager relies on
+ // PersonalDataManager to be around when it gets destroyed. Also, a real
+ // AutofillManager is tied to the lifetime of the WebContents, so it must
+ // be destroyed at the destruction of the WebContents.
+ autofill_manager_.reset();
+ file_thread_.Stop();
+ ChromeRenderViewHostTestHarness::TearDown();
+ io_thread_.Stop();
+ }
+
+ virtual TestingProfile* CreateProfile() {
+ return new TestingProfile();
+ }
+
+ void UpdatePasswordGenerationState(bool new_renderer) {
+ autofill_manager_->UpdatePasswordGenerationState(NULL, new_renderer);
+ }
+
+ void GetAutofillSuggestions(int query_id,
+ const FormData& form,
+ const FormFieldData& field) {
+ autofill_manager_->OnQueryFormFieldAutofill(query_id,
+ form,
+ field,
+ gfx::Rect(),
+ false);
+ }
+
+ void GetAutofillSuggestions(const FormData& form,
+ const FormFieldData& field) {
+ GetAutofillSuggestions(kDefaultPageID, form, field);
+ }
+
+ void AutocompleteSuggestionsReturned(const std::vector<string16>& result) {
+ autofill_manager_->autocomplete_history_manager_.SendSuggestions(&result);
+ }
+
+ void FormsSeen(const std::vector<FormData>& forms) {
+ autofill_manager_->OnFormsSeen(forms, base::TimeTicks());
+ }
+
+ void FormSubmitted(const FormData& form) {
+ if (autofill_manager_->OnFormSubmitted(form, base::TimeTicks::Now()))
+ autofill_manager_->WaitForAsyncFormSubmit();
+ }
+
+ void FillAutofillFormData(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ int unique_id) {
+ autofill_manager_->OnFillAutofillFormData(query_id, form, field, unique_id);
+ }
+
+ int PackGUIDs(const GUIDPair& cc_guid, const GUIDPair& profile_guid) const {
+ return autofill_manager_->PackGUIDs(cc_guid, profile_guid);
+ }
+
+ bool GetAutofillSuggestionsMessage(int* page_id,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) {
+ const uint32 kMsgID = AutofillMsg_SuggestionsReturned::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ if (!message)
+ return false;
+
+ AutofillParam autofill_param;
+ AutofillMsg_SuggestionsReturned::Read(message, &autofill_param);
+ if (page_id)
+ *page_id = autofill_param.a;
+ if (values)
+ *values = autofill_param.b;
+ if (labels)
+ *labels = autofill_param.c;
+ if (icons)
+ *icons = autofill_param.d;
+ if (unique_ids)
+ *unique_ids = autofill_param.e;
+
+ autofill_manager_->autocomplete_history_manager_.CancelPendingQuery();
+ process()->sink().ClearMessages();
+ return true;
+ }
+
+ bool GetAutofillFormDataFilledMessage(int* page_id, FormData* results) {
+ const uint32 kMsgID = AutofillMsg_FormDataFilled::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ if (!message)
+ return false;
+ Tuple2<int, FormData> autofill_param;
+ AutofillMsg_FormDataFilled::Read(message, &autofill_param);
+ if (page_id)
+ *page_id = autofill_param.a;
+ if (results)
+ *results = autofill_param.b;
+
+ process()->sink().ClearMessages();
+ return true;
+ }
+
+ protected:
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+ content::TestBrowserThread io_thread_;
+
+ scoped_ptr<TestAutofillManager> autofill_manager_;
+ TestPersonalDataManager personal_data_;
+
+ // Used when we want an off the record profile. This will store the original
+ // profile from which the off the record profile is derived.
+ scoped_ptr<Profile> other_browser_context_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillManagerTest);
+};
+
+class IncognitoAutofillManagerTest : public AutofillManagerTest {
+ public:
+ IncognitoAutofillManagerTest() {}
+ virtual ~IncognitoAutofillManagerTest() {}
+
+ virtual TestingProfile* CreateProfile() OVERRIDE {
+ // Create an incognito profile.
+ TestingProfile::Builder builder;
+ scoped_ptr<TestingProfile> profile = builder.Build();
+ profile->set_incognito(true);
+ return profile.release();
+ }
+};
+
+class TestFormStructure : public FormStructure {
+ public:
+ explicit TestFormStructure(const FormData& form)
+ : FormStructure(form, std::string()) {}
+ virtual ~TestFormStructure() {}
+
+ void SetFieldTypes(const std::vector<AutofillFieldType>& heuristic_types,
+ const std::vector<AutofillFieldType>& server_types) {
+ ASSERT_EQ(field_count(), heuristic_types.size());
+ ASSERT_EQ(field_count(), server_types.size());
+
+ for (size_t i = 0; i < field_count(); ++i) {
+ AutofillField* form_field = field(i);
+ ASSERT_TRUE(form_field);
+ form_field->set_heuristic_type(heuristic_types[i]);
+ form_field->set_server_type(server_types[i]);
+ }
+
+ UpdateAutofillCount();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestFormStructure);
+};
+
+// Test that we return all address profile suggestions when all form fields are
+// empty.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsEmptyValue) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ GetAutofillSuggestionsMessage(
+ &page_id, &values, &labels, &icons, &unique_ids);
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ // Inferred labels include full first relevant field, which in this case is
+ // the address line 1.
+ string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ string16 expected_icons[] = {string16(), string16()};
+ int expected_unique_ids[] = {1, 2};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return only matching address profile suggestions when the
+// selected form field has been partially filled out.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsMatchCharacter) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField("First Name", "firstname", "E", "text",
+ &field);
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {ASCIIToUTF16("Elvis")};
+ string16 expected_labels[] = {ASCIIToUTF16("3734 Elvis Presley Blvd.")};
+ string16 expected_icons[] = {string16()};
+ int expected_unique_ids[] = {1};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return no suggestions when the form has no relevant fields.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsUnknownFields) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField("Username", "username", "", "text",
+ &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Password", "password", "", "password",
+ &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Quest", "quest", "", "quest", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Color", "color", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GetAutofillSuggestions(form, field);
+ EXPECT_FALSE(GetAutofillSuggestionsMessage(NULL, NULL, NULL, NULL, NULL));
+}
+
+// Test that we cull duplicate profile suggestions.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsWithDuplicates) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Add a duplicate profile.
+ AutofillProfile* duplicate_profile =
+ new AutofillProfile(
+ *(autofill_manager_->GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000001")));
+ autofill_manager_->AddProfile(duplicate_profile);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ string16 expected_icons[] = {string16(), string16()};
+ int expected_unique_ids[] = {1, 2};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return no suggestions when autofill is disabled.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsAutofillDisabledByUser) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Disable Autofill.
+ autofill_manager_->set_autofill_enabled(false);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+ EXPECT_FALSE(GetAutofillSuggestionsMessage(NULL, NULL, NULL, NULL, NULL));
+}
+
+// Test that we return a warning explaining that autofill suggestions are
+// unavailable when the form method is GET rather than POST.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsMethodGet) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ form.method = ASCIIToUTF16("GET");
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)
+ };
+ string16 expected_labels[] = {string16()};
+ string16 expected_icons[] = {string16()};
+ int expected_unique_ids[] =
+ {WebKit::WebAutofillClient::MenuItemIDWarningMessage};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+
+ // Now add some Autocomplete suggestions. We should return the autocomplete
+ // suggestions and the warning; these will be culled by the renderer.
+ const int kPageID2 = 2;
+ GetAutofillSuggestions(kPageID2, form, field);
+
+ std::vector<string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("Jay"));
+ suggestions.push_back(ASCIIToUTF16("Jason"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values2[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED),
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ string16 expected_labels2[] = {string16(), string16(), string16()};
+ string16 expected_icons2[] = {string16(), string16(), string16()};
+ int expected_unique_ids2[] = {-1, 0, 0};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kPageID2, arraysize(expected_values2), expected_values2,
+ expected_labels2, expected_icons2, expected_unique_ids2);
+
+ // Now clear the test profiles and try again -- we shouldn't return a warning.
+ personal_data_.ClearAutofillProfiles();
+ GetAutofillSuggestions(form, field);
+ EXPECT_FALSE(GetAutofillSuggestionsMessage(NULL, NULL, NULL, NULL, NULL));
+}
+
+// Test that we return all credit card profile suggestions when all form fields
+// are empty.
+TEST_F(AutofillManagerTest, GetCreditCardSuggestionsEmptyValue) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field = form.fields[1];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("************3456"),
+ ASCIIToUTF16("************8765")
+ };
+ string16 expected_labels[] = {ASCIIToUTF16("*3456"), ASCIIToUTF16("*8765")};
+ string16 expected_icons[] = {
+ ASCIIToUTF16("visaCC"),
+ ASCIIToUTF16("genericCC")
+ };
+ int expected_unique_ids[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5)
+ };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return only matching credit card profile suggestions when the
+// selected form field has been partially filled out.
+TEST_F(AutofillManagerTest, GetCreditCardSuggestionsMatchCharacter) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Card Number", "cardnumber", "4", "text", &field);
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {ASCIIToUTF16("************3456")};
+ string16 expected_labels[] = {ASCIIToUTF16("*3456")};
+ string16 expected_icons[] = {ASCIIToUTF16("visaCC")};
+ int expected_unique_ids[] = {autofill_manager_->GetPackedCreditCardID(4)};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return credit card profile suggestions when the selected form
+// field is not the credit card number field.
+TEST_F(AutofillManagerTest, GetCreditCardSuggestionsNonCCNumber) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis Presley"),
+ ASCIIToUTF16("Buddy Holly")
+ };
+ string16 expected_labels[] = {ASCIIToUTF16("*3456"), ASCIIToUTF16("*8765")};
+ string16 expected_icons[] = {
+ ASCIIToUTF16("visaCC"),
+ ASCIIToUTF16("genericCC")
+ };
+ int expected_unique_ids[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5)
+ };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return a warning explaining that credit card profile suggestions
+// are unavailable when the form is not https.
+TEST_F(AutofillManagerTest, GetCreditCardSuggestionsNonHTTPS) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, false, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION)
+ };
+ string16 expected_labels[] = {string16()};
+ string16 expected_icons[] = {string16()};
+ int expected_unique_ids[] = {-1};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+
+ // Now add some Autocomplete suggestions. We should show the autocomplete
+ // suggestions and the warning.
+ const int kPageID2 = 2;
+ GetAutofillSuggestions(kPageID2, form, field);
+
+ std::vector<string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("Jay"));
+ suggestions.push_back(ASCIIToUTF16("Jason"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+ string16 expected_values2[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION),
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ string16 expected_labels2[] = {string16(), string16(), string16()};
+ string16 expected_icons2[] = {string16(), string16(), string16()};
+ int expected_unique_ids2[] = {-1, 0, 0};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kPageID2, arraysize(expected_values2), expected_values2,
+ expected_labels2, expected_icons2, expected_unique_ids2);
+
+ // Clear the test credit cards and try again -- we shouldn't return a warning.
+ personal_data_.ClearCreditCards();
+ GetAutofillSuggestions(form, field);
+ EXPECT_FALSE(GetAutofillSuggestionsMessage(NULL, NULL, NULL, NULL, NULL));
+}
+
+// Test that we return all credit card suggestions in the case that two cards
+// have the same obfuscated number.
+TEST_F(AutofillManagerTest, GetCreditCardSuggestionsRepeatedObfuscatedNumber) {
+ // Add a credit card with the same obfuscated number as Elvis's.
+ // |credit_card| will be owned by the mock PersonalDataManager.
+ CreditCard* credit_card = new CreditCard;
+ autofill_test::SetCreditCardInfo(credit_card, "Elvis Presley",
+ "5231567890123456", // Mastercard
+ "04", "2012");
+ credit_card->set_guid("00000000-0000-0000-0000-000000000007");
+ autofill_manager_->AddCreditCard(credit_card);
+
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field = form.fields[1];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("************3456"),
+ ASCIIToUTF16("************8765"),
+ ASCIIToUTF16("************3456")
+ };
+ string16 expected_labels[] = {
+ ASCIIToUTF16("*3456"),
+ ASCIIToUTF16("*8765"),
+ ASCIIToUTF16("*3456"),
+ };
+ string16 expected_icons[] = {
+ ASCIIToUTF16("visaCC"),
+ ASCIIToUTF16("genericCC"),
+ ASCIIToUTF16("masterCardCC")
+ };
+ int expected_unique_ids[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5),
+ autofill_manager_->GetPackedCreditCardID(7)
+ };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return profile and credit card suggestions for combined forms.
+TEST_F(AutofillManagerTest, GetAddressAndCreditCardSuggestions) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right address suggestions to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ string16 expected_icons[] = {string16(), string16()};
+ int expected_unique_ids[] = {1, 2};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+
+ const int kPageID2 = 2;
+ autofill_test::CreateTestFormField(
+ "Card Number", "cardnumber", "", "text", &field);
+ GetAutofillSuggestions(kPageID2, form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the credit card suggestions to the renderer.
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values2[] = {
+ ASCIIToUTF16("************3456"),
+ ASCIIToUTF16("************8765")
+ };
+ string16 expected_labels2[] = {ASCIIToUTF16("*3456"), ASCIIToUTF16("*8765")};
+ string16 expected_icons2[] = {
+ ASCIIToUTF16("visaCC"),
+ ASCIIToUTF16("genericCC")
+ };
+ int expected_unique_ids2[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5)
+ };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kPageID2, arraysize(expected_values2), expected_values2,
+ expected_labels2, expected_icons2, expected_unique_ids2);
+}
+
+// Test that for non-https forms with both address and credit card fields, we
+// only return address suggestions. Instead of credit card suggestions, we
+// should return a warning explaining that credit card profile suggestions are
+// unavailable when the form is not https.
+TEST_F(AutofillManagerTest, GetAddressAndCreditCardSuggestionsNonHttps) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ CreateTestCreditCardFormData(&form, false, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right address suggestions to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ string16 expected_icons[] = {string16(), string16()};
+ int expected_unique_ids[] = {1, 2};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+
+ autofill_test::CreateTestFormField(
+ "Card Number", "cardnumber", "", "text", &field);
+ const int kPageID2 = 2;
+ GetAutofillSuggestions(kPageID2, form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values2[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION)
+ };
+ string16 expected_labels2[] = {string16()};
+ string16 expected_icons2[] = {string16()};
+ int expected_unique_ids2[] = {-1};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kPageID2, arraysize(expected_values2), expected_values2,
+ expected_labels2, expected_icons2, expected_unique_ids2);
+
+ // Clear the test credit cards and try again -- we shouldn't return a warning.
+ personal_data_.ClearCreditCards();
+ GetAutofillSuggestions(form, field);
+ EXPECT_FALSE(GetAutofillSuggestionsMessage(NULL, NULL, NULL, NULL, NULL));
+}
+
+// Test that we correctly combine autofill and autocomplete suggestions.
+TEST_F(AutofillManagerTest, GetCombinedAutofillAndAutocompleteSuggestions) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // Add some Autocomplete suggestions.
+ // This triggers the combined message send.
+ std::vector<string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("Jay"));
+ // This suggestion is a duplicate, and should be trimmed.
+ suggestions.push_back(ASCIIToUTF16("Elvis"));
+ suggestions.push_back(ASCIIToUTF16("Jason"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles"),
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St."),
+ string16(),
+ string16()
+ };
+ string16 expected_icons[] = {string16(), string16(), string16(), string16()};
+ int expected_unique_ids[] = {1, 2, 0, 0};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we return autocomplete-like suggestions when trying to autofill
+// already filled forms.
+TEST_F(AutofillManagerTest, GetFieldSuggestionsWhenFormIsAutofilled) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Mark one of the fields as filled.
+ form.fields[2].is_autofilled = true;
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ string16 expected_labels[] = {string16(), string16()};
+ string16 expected_icons[] = {string16(), string16()};
+ int expected_unique_ids[] = {1, 2};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that nothing breaks when there are autocomplete suggestions but no
+// autofill suggestions.
+TEST_F(AutofillManagerTest, GetFieldSuggestionsForAutocompleteOnly) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Some Field", "somefield", "", "text", &field);
+ form.fields.push_back(field);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GetAutofillSuggestions(form, field);
+
+ // Add some Autocomplete suggestions.
+ // This triggers the combined message send.
+ std::vector<string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("one"));
+ suggestions.push_back(ASCIIToUTF16("two"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("one"),
+ ASCIIToUTF16("two")
+ };
+ string16 expected_labels[] = {string16(), string16()};
+ string16 expected_icons[] = {string16(), string16()};
+ int expected_unique_ids[] = {0, 0};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we do not return duplicate values drawn from multiple profiles when
+// filling an already filled field.
+TEST_F(AutofillManagerTest, GetFieldSuggestionsWithDuplicateValues) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // |profile| will be owned by the mock PersonalDataManager.
+ AutofillProfile* profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "Elvis", "", "", "", "", "", "", "",
+ "", "", "", "");
+ profile->set_guid("00000000-0000-0000-0000-000000000101");
+ autofill_manager_->AddProfile(profile);
+
+ FormFieldData& field = form.fields[0];
+ field.is_autofilled = true;
+ field.value = ASCIIToUTF16("Elvis");
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = { ASCIIToUTF16("Elvis") };
+ string16 expected_labels[] = { string16() };
+ string16 expected_icons[] = { string16() };
+ int expected_unique_ids[] = { 1 };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that a non-default value is suggested for multi-valued profile, on an
+// unfilled form.
+TEST_F(AutofillManagerTest, GetFieldSuggestionsForMultiValuedProfileUnfilled) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // |profile| will be owned by the mock PersonalDataManager.
+ AutofillProfile* profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "Elvis", "", "Presley", "me@x.com", "",
+ "", "", "", "", "", "", "");
+ profile->set_guid("00000000-0000-0000-0000-000000000101");
+ std::vector<string16> multi_values(2);
+ multi_values[0] = ASCIIToUTF16("Elvis Presley");
+ multi_values[1] = ASCIIToUTF16("Elena Love");
+ profile->SetRawMultiInfo(NAME_FULL, multi_values);
+ personal_data_.ClearAutofillProfiles();
+ autofill_manager_->AddProfile(profile);
+
+ {
+ // Get the first name field.
+ // Start out with "E", hoping for either "Elvis" or "Elena.
+ FormFieldData& field = form.fields[0];
+ field.value = ASCIIToUTF16("E");
+ field.is_autofilled = false;
+ GetAutofillSuggestions(form, field);
+
+ // Trigger the |Send|.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels,
+ &icons, &unique_ids));
+ string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Elena")
+ };
+ string16 expected_labels[] = {
+ ASCIIToUTF16("me@x.com"),
+ ASCIIToUTF16("me@x.com")
+ };
+ string16 expected_icons[] = { string16(), string16() };
+ int expected_unique_ids[] = { 1, 2 };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values),
+ expected_values, expected_labels, expected_icons,
+ expected_unique_ids);
+ }
+
+ {
+ // Get the first name field.
+ // This time, start out with "Ele", hoping for "Elena".
+ FormFieldData& field = form.fields[0];
+ field.value = ASCIIToUTF16("Ele");
+ field.is_autofilled = false;
+ GetAutofillSuggestions(form, field);
+
+ // Trigger the |Send|.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels,
+ &icons, &unique_ids));
+
+ string16 expected_values[] = { ASCIIToUTF16("Elena") };
+ string16 expected_labels[] = { ASCIIToUTF16("me@x.com") };
+ string16 expected_icons[] = { string16() };
+ int expected_unique_ids[] = { 2 };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values),
+ expected_values, expected_labels, expected_icons,
+ expected_unique_ids);
+ }
+}
+
+// Test that all values are suggested for multi-valued profile, on a filled
+// form. This is the per-field "override" case.
+TEST_F(AutofillManagerTest, GetFieldSuggestionsForMultiValuedProfileFilled) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // |profile| will be owned by the mock PersonalDataManager.
+ AutofillProfile* profile = new AutofillProfile;
+ profile->set_guid("00000000-0000-0000-0000-000000000102");
+ std::vector<string16> multi_values(3);
+ multi_values[0] = ASCIIToUTF16("Travis Smith");
+ multi_values[1] = ASCIIToUTF16("Cynthia Love");
+ multi_values[2] = ASCIIToUTF16("Zac Mango");
+ profile->SetRawMultiInfo(NAME_FULL, multi_values);
+ autofill_manager_->AddProfile(profile);
+
+ // Get the first name field. And start out with "Travis", hoping for all the
+ // multi-valued variants as suggestions.
+ FormFieldData& field = form.fields[0];
+ field.value = ASCIIToUTF16("Travis");
+ field.is_autofilled = true;
+ GetAutofillSuggestions(form, field);
+
+ // Trigger the |Send|.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ EXPECT_TRUE(GetAutofillSuggestionsMessage(&page_id, &values, &labels, &icons,
+ &unique_ids));
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("Travis"),
+ ASCIIToUTF16("Cynthia"),
+ ASCIIToUTF16("Zac")
+ };
+ string16 expected_labels[] = { string16(), string16(), string16() };
+ string16 expected_icons[] = { string16(), string16(), string16() };
+ int expected_unique_ids[] = { 1, 2, 3 };
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+TEST_F(AutofillManagerTest, GetProfileSuggestionsFancyPhone) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ AutofillProfile* profile = new AutofillProfile;
+ profile->set_guid("00000000-0000-0000-0000-000000000103");
+ std::vector<string16> multi_values(1);
+ multi_values[0] = ASCIIToUTF16("Natty Bumppo");
+ profile->SetRawMultiInfo(NAME_FULL, multi_values);
+ multi_values[0] = ASCIIToUTF16("1800PRAIRIE");
+ profile->SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, multi_values);
+ autofill_manager_->AddProfile(profile);
+
+ const FormFieldData& field = form.fields[9];
+ GetAutofillSuggestions(form, field);
+
+ // No suggestions provided, so send an empty vector as the results.
+ // This triggers the combined message send.
+ AutocompleteSuggestionsReturned(std::vector<string16>());
+
+ // Test that we sent the right message to the renderer.
+ int page_id = 0;
+ std::vector<string16> values;
+ std::vector<string16> labels;
+ std::vector<string16> icons;
+ std::vector<int> unique_ids;
+ GetAutofillSuggestionsMessage(
+ &page_id, &values, &labels, &icons, &unique_ids);
+
+ string16 expected_values[] = {
+ ASCIIToUTF16("12345678901"),
+ ASCIIToUTF16("23456789012"),
+ ASCIIToUTF16("18007724743"), // 1800PRAIRIE
+ };
+ // Inferred labels include full first relevant field, which in this case is
+ // the address line 1.
+ string16 expected_labels[] = {
+ ASCIIToUTF16("Elvis Aaron Presley"),
+ ASCIIToUTF16("Charles Hardin Holley"),
+ ASCIIToUTF16("Natty Bumppo"),
+ };
+ string16 expected_icons[] = {string16(), string16(), string16()};
+ int expected_unique_ids[] = {1, 2, 3};
+ ExpectSuggestions(page_id, values, labels, icons, unique_ids,
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that we correctly fill an address form.
+TEST_F(AutofillManagerTest, FillAddressForm) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+}
+
+// Test that we correctly fill an address form from an auxiliary profile.
+TEST_F(AutofillManagerTest, FillAddressFormFromAuxiliaryProfile) {
+ personal_data_.ClearAutofillProfiles();
+ PrefService* prefs = components::UserPrefs::Get(profile());
+ prefs->SetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled, true);
+ personal_data_.CreateTestAuxiliaryProfiles();
+
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+}
+
+// Test that we correctly fill a credit card form.
+TEST_F(AutofillManagerTest, FillCreditCardForm) {
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000004", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledCreditCardFormElvis(page_id, results, kDefaultPageID, false);
+}
+
+// Test that we correctly fill a credit card form with month input type.
+// 1. year empty, month empty
+TEST_F(AutofillManagerTest, FillCreditCardFormNoYearNoMonth) {
+ // Same as the SetUp(), but generate 4 credit cards with year month
+ // combination.
+ personal_data_.CreateTestCreditCardsYearAndMonth("", "");
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, true);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000007", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results,
+ kDefaultPageID, false, "", "");
+}
+
+
+// Test that we correctly fill a credit card form with month input type.
+// 2. year empty, month non-empty
+TEST_F(AutofillManagerTest, FillCreditCardFormNoYearMonth) {
+ // Same as the SetUp(), but generate 4 credit cards with year month
+ // combination.
+ personal_data_.CreateTestCreditCardsYearAndMonth("", "04");
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, true);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000007", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results,
+ kDefaultPageID, false, "", "04");
+}
+
+// Test that we correctly fill a credit card form with month input type.
+// 3. year non-empty, month empty
+TEST_F(AutofillManagerTest, FillCreditCardFormYearNoMonth) {
+ // Same as the SetUp(), but generate 4 credit cards with year month
+ // combination.
+ personal_data_.CreateTestCreditCardsYearAndMonth("2012", "");
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, true);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000007", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results,
+ kDefaultPageID, false, "2012", "");
+}
+
+// Test that we correctly fill a credit card form with month input type.
+// 4. year non-empty, month empty
+TEST_F(AutofillManagerTest, FillCreditCardFormYearMonth) {
+ // Same as the SetUp(), but generate 4 credit cards with year month
+ // combination.
+ personal_data_.ClearCreditCards();
+ personal_data_.CreateTestCreditCardsYearAndMonth("2012", "04");
+ // Set up our form data.
+ FormData form;
+ CreateTestCreditCardFormData(&form, true, true);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000007", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledCreditCardYearMonthWithYearMonth(page_id, results,
+ kDefaultPageID, false, "2012", "04");
+}
+
+// Test that we correctly fill a combined address and credit card form.
+TEST_F(AutofillManagerTest, FillAddressAndCreditCardForm) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // First fill the address data.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Address");
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, true);
+ }
+
+ // Now fill the credit card data.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000004", 0);
+ FillAutofillFormData(kPageID2, form, form.fields.back(),
+ PackGUIDs(guid2, empty));
+
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Credit card");
+ ExpectFilledCreditCardFormElvis(page_id, results, kPageID2, true);
+ }
+}
+
+// Test that we correctly fill a form that has multiple logical sections, e.g.
+// both a billing and a shipping address.
+TEST_F(AutofillManagerTest, FillFormWithMultipleSections) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ const size_t kAddressFormSize = form.fields.size();
+ CreateTestAddressFormData(&form);
+ for (size_t i = kAddressFormSize; i < form.fields.size(); ++i) {
+ // Make sure the fields have distinct names.
+ form.fields[i].name = form.fields[i].name + ASCIIToUTF16("_");
+ }
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Fill the first section.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Address 1");
+
+ // The second address section should be empty.
+ ASSERT_EQ(results.fields.size(), 2*kAddressFormSize);
+ for (size_t i = kAddressFormSize; i < form.fields.size(); ++i) {
+ EXPECT_EQ(string16(), results.fields[i].value);
+ }
+
+ // The first address section should be filled with Elvis's data.
+ results.fields.resize(kAddressFormSize);
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+ }
+
+ // Fill the second section, with the initiating field somewhere in the middle
+ // of the section.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000001", 0);
+ ASSERT_LT(9U, kAddressFormSize);
+ FillAutofillFormData(kPageID2, form, form.fields[kAddressFormSize + 9],
+ PackGUIDs(empty, guid2));
+
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Address 2");
+ ASSERT_EQ(results.fields.size(), form.fields.size());
+
+ // The first address section should be empty.
+ ASSERT_EQ(results.fields.size(), 2*kAddressFormSize);
+ for (size_t i = 0; i < kAddressFormSize; ++i) {
+ EXPECT_EQ(string16(), results.fields[i].value);
+ }
+
+ // The second address section should be filled with Elvis's data.
+ FormData secondSection = results;
+ secondSection.fields.erase(secondSection.fields.begin(),
+ secondSection.fields.begin() + kAddressFormSize);
+ for (size_t i = 0; i < kAddressFormSize; ++i) {
+ // Restore the expected field names.
+ string16 name = secondSection.fields[i].name;
+ string16 original_name = name.substr(0, name.size() - 1);
+ secondSection.fields[i].name = original_name;
+ }
+ ExpectFilledAddressFormElvis(page_id, secondSection, kPageID2, false);
+ }
+}
+
+// Test that we correctly fill a form that has author-specified sections, which
+// might not match our expected section breakdown.
+TEST_F(AutofillManagerTest, FillFormWithAuthorSpecifiedSections) {
+ // Create a form with a billing section and an unnamed section, interleaved.
+ // The billing section includes both address and credit card fields.
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("https://myform.com/form.html");
+ form.action = GURL("https://myform.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+
+ autofill_test::CreateTestFormField("", "country", "", "text", &field);
+ field.autocomplete_attribute = "section-billing country";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "firstname", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "lastname", "", "text", &field);
+ field.autocomplete_attribute = "family-name";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "address", "", "text", &field);
+ field.autocomplete_attribute = "section-billing street-address";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "city", "", "text", &field);
+ field.autocomplete_attribute = "section-billing locality";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "state", "", "text", &field);
+ field.autocomplete_attribute = "section-billing region";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "zip", "", "text", &field);
+ field.autocomplete_attribute = "section-billing postal-code";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "ccname", "", "text", &field);
+ field.autocomplete_attribute = "section-billing cc-name";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "ccnumber", "", "text", &field);
+ field.autocomplete_attribute = "section-billing cc-number";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "ccexp", "", "text", &field);
+ field.autocomplete_attribute = "section-billing cc-exp";
+ form.fields.push_back(field);
+
+ autofill_test::CreateTestFormField("", "email", "", "text", &field);
+ field.autocomplete_attribute = "email";
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Fill the unnamed section.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[1],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Unnamed section");
+ EXPECT_EQ(kDefaultPageID, page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), results.method);
+ EXPECT_EQ(GURL("https://myform.com/form.html"), results.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action);
+ EXPECT_TRUE(results.user_submitted);
+ ASSERT_EQ(11U, results.fields.size());
+
+ ExpectFilledField("", "country", "", "text", results.fields[0]);
+ ExpectFilledField("", "firstname", "Elvis", "text", results.fields[1]);
+ ExpectFilledField("", "lastname", "Presley", "text", results.fields[2]);
+ ExpectFilledField("", "address", "", "text", results.fields[3]);
+ ExpectFilledField("", "city", "", "text", results.fields[4]);
+ ExpectFilledField("", "state", "", "text", results.fields[5]);
+ ExpectFilledField("", "zip", "", "text", results.fields[6]);
+ ExpectFilledField("", "ccname", "", "text", results.fields[7]);
+ ExpectFilledField("", "ccnumber", "", "text", results.fields[8]);
+ ExpectFilledField("", "ccexp", "", "text", results.fields[9]);
+ ExpectFilledField("", "email", "theking@gmail.com", "text",
+ results.fields[10]);
+ }
+
+ // Fill the address portion of the billing section.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000001", 0);
+ FillAutofillFormData(kPageID2, form, form.fields[0],
+ PackGUIDs(empty, guid2));
+
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Billing address");
+ EXPECT_EQ(kPageID2, page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), results.method);
+ EXPECT_EQ(GURL("https://myform.com/form.html"), results.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action);
+ EXPECT_TRUE(results.user_submitted);
+ ASSERT_EQ(11U, results.fields.size());
+
+ ExpectFilledField("", "country", "United States", "text",
+ results.fields[0]);
+ ExpectFilledField("", "firstname", "", "text", results.fields[1]);
+ ExpectFilledField("", "lastname", "", "text", results.fields[2]);
+ ExpectFilledField("", "address", "3734 Elvis Presley Blvd.", "text",
+ results.fields[3]);
+ ExpectFilledField("", "city", "Memphis", "text", results.fields[4]);
+ ExpectFilledField("", "state", "Tennessee", "text", results.fields[5]);
+ ExpectFilledField("", "zip", "38116", "text", results.fields[6]);
+ ExpectFilledField("", "ccname", "", "text", results.fields[7]);
+ ExpectFilledField("", "ccnumber", "", "text", results.fields[8]);
+ ExpectFilledField("", "ccexp", "", "text", results.fields[9]);
+ ExpectFilledField("", "email", "", "text", results.fields[10]);
+ }
+
+ // Fill the credit card portion of the billing section.
+ const int kPageID3 = 3;
+ GUIDPair guid3("00000000-0000-0000-0000-000000000004", 0);
+ FillAutofillFormData(kPageID3, form, form.fields[form.fields.size() - 2],
+ PackGUIDs(guid3, empty));
+
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Credit card");
+ EXPECT_EQ(kPageID3, page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), results.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), results.method);
+ EXPECT_EQ(GURL("https://myform.com/form.html"), results.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), results.action);
+ EXPECT_TRUE(results.user_submitted);
+ ASSERT_EQ(11U, results.fields.size());
+
+ ExpectFilledField("", "country", "", "text", results.fields[0]);
+ ExpectFilledField("", "firstname", "", "text", results.fields[1]);
+ ExpectFilledField("", "lastname", "", "text", results.fields[2]);
+ ExpectFilledField("", "address", "", "text", results.fields[3]);
+ ExpectFilledField("", "city", "", "text", results.fields[4]);
+ ExpectFilledField("", "state", "", "text", results.fields[5]);
+ ExpectFilledField("", "zip", "", "text", results.fields[6]);
+ ExpectFilledField("", "ccname", "Elvis Presley", "text", results.fields[7]);
+ ExpectFilledField("", "ccnumber", "4234567890123456", "text",
+ results.fields[8]);
+ ExpectFilledField("", "ccexp", "04/2012", "text", results.fields[9]);
+ ExpectFilledField("", "email", "", "text", results.fields[10]);
+ }
+}
+
+// Test that we correctly fill a form that has a single logical section with
+// multiple email address fields.
+TEST_F(AutofillManagerTest, FillFormWithMultipleEmails) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Confirm email", "email2", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Fill the form.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+
+ // The second email address should be filled.
+ EXPECT_EQ(ASCIIToUTF16("theking@gmail.com"), results.fields.back().value);
+
+ // The remainder of the form should be filled as usual.
+ results.fields.pop_back();
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+}
+
+// Test that we correctly fill a previously auto-filled form.
+TEST_F(AutofillManagerTest, FillAutofilledForm) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ // Mark one of the address fields as autofilled.
+ form.fields[4].is_autofilled = true;
+ CreateTestCreditCardFormData(&form, true, false);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // First fill the address data.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Address");
+ ExpectFilledForm(page_id, results, kDefaultPageID,
+ "Elvis", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", true, true, false);
+ }
+
+ // Now fill the credit card data.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000004", 0);
+ FillAutofillFormData(kPageID2, form, form.fields.back(),
+ PackGUIDs(guid2, empty));
+
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Credit card 1");
+ ExpectFilledCreditCardFormElvis(page_id, results, kPageID2, true);
+ }
+
+ // Now set the credit card fields to also be auto-filled, and try again to
+ // fill the credit card data
+ for (std::vector<FormFieldData>::iterator iter = form.fields.begin();
+ iter != form.fields.end();
+ ++iter) {
+ iter->is_autofilled = true;
+ }
+
+ const int kPageID3 = 3;
+ FillAutofillFormData(kPageID3, form, *form.fields.rbegin(),
+ PackGUIDs(guid2, empty));
+
+ page_id = 0;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ {
+ SCOPED_TRACE("Credit card 2");
+ ExpectFilledForm(page_id, results, kPageID3,
+ "", "", "", "", "", "", "", "", "", "", "", "", "", "",
+ "2012", true, true, false);
+ }
+}
+
+// Test that we correctly fill an address form with a non-default variant for a
+// multi-valued field.
+TEST_F(AutofillManagerTest, FillAddressFormWithVariantType) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Add a name variant to the Elvis profile.
+ AutofillProfile* profile = autofill_manager_->GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000001");
+ const string16 elvis_name = profile->GetRawInfo(NAME_FULL);
+
+ std::vector<string16> name_variants;
+ name_variants.push_back(ASCIIToUTF16("Some Other Guy"));
+ name_variants.push_back(elvis_name);
+ profile->SetRawMultiInfo(NAME_FULL, name_variants);
+
+ GUIDPair guid(profile->guid(), 1);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results1;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results1));
+ {
+ SCOPED_TRACE("Valid variant");
+ ExpectFilledAddressFormElvis(page_id, results1, kDefaultPageID, false);
+ }
+
+ // Try filling with a variant that doesn't exist. The fields to which this
+ // variant would normally apply should not be filled.
+ const int kPageID2 = 2;
+ GUIDPair guid2(profile->guid(), 2);
+ FillAutofillFormData(kPageID2, form, form.fields[0],
+ PackGUIDs(empty, guid2));
+
+ page_id = 0;
+ FormData results2;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results2));
+ {
+ SCOPED_TRACE("Invalid variant");
+ ExpectFilledForm(page_id, results2, kPageID2, "", "", "",
+ "3734 Elvis Presley Blvd.", "Apt. 10", "Memphis",
+ "Tennessee", "38116", "United States", "12345678901",
+ "theking@gmail.com", "", "", "", "", true, false, false);
+ }
+}
+
+// Test that we correctly fill a phone number split across multiple fields.
+TEST_F(AutofillManagerTest, FillPhoneNumber) {
+ // In one form, rely on the maxlength attribute to imply phone number parts.
+ // In the other form, rely on the autocompletetype attribute.
+ FormData form_with_maxlength;
+ form_with_maxlength.name = ASCIIToUTF16("MyMaxlengthPhoneForm");
+ form_with_maxlength.method = ASCIIToUTF16("POST");
+ form_with_maxlength.origin = GURL("http://myform.com/phone_form.html");
+ form_with_maxlength.action = GURL("http://myform.com/phone_submit.html");
+ form_with_maxlength.user_submitted = true;
+ FormData form_with_autocompletetype = form_with_maxlength;
+ form_with_autocompletetype.name = ASCIIToUTF16("MyAutocompletetypePhoneForm");
+
+ struct {
+ const char* label;
+ const char* name;
+ size_t max_length;
+ const char* autocomplete_attribute;
+ } test_fields[] = {
+ { "country code", "country_code", 1, "tel-country-code" },
+ { "area code", "area_code", 3, "tel-area-code" },
+ { "phone", "phone_prefix", 3, "tel-local-prefix" },
+ { "-", "phone_suffix", 4, "tel-local-suffix" },
+ { "Phone Extension", "ext", 3, "tel-extension" }
+ };
+
+ FormFieldData field;
+ const size_t default_max_length = field.max_length;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_fields); ++i) {
+ autofill_test::CreateTestFormField(
+ test_fields[i].label, test_fields[i].name, "", "text", &field);
+ field.max_length = test_fields[i].max_length;
+ field.autocomplete_attribute = std::string();
+ form_with_maxlength.fields.push_back(field);
+
+ field.max_length = default_max_length;
+ field.autocomplete_attribute = test_fields[i].autocomplete_attribute;
+ form_with_autocompletetype.fields.push_back(field);
+ }
+
+ std::vector<FormData> forms;
+ forms.push_back(form_with_maxlength);
+ forms.push_back(form_with_autocompletetype);
+ FormsSeen(forms);
+
+ // We should be able to fill prefix and suffix fields for US numbers.
+ AutofillProfile* work_profile = autofill_manager_->GetProfileWithGUID(
+ "00000000-0000-0000-0000-000000000002");
+ ASSERT_TRUE(work_profile != NULL);
+ work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("16505554567"));
+
+ GUIDPair guid(work_profile->guid(), 0);
+ GUIDPair empty(std::string(), 0);
+
+ int page_id = 1;
+ FillAutofillFormData(page_id, form_with_maxlength,
+ *form_with_maxlength.fields.begin(),
+ PackGUIDs(empty, guid));
+ page_id = 0;
+ FormData results1;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results1));
+ EXPECT_EQ(1, page_id);
+
+ ASSERT_EQ(5U, results1.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("1"), results1.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("650"), results1.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("555"), results1.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("4567"), results1.fields[3].value);
+ EXPECT_EQ(string16(), results1.fields[4].value);
+
+ page_id = 2;
+ FillAutofillFormData(page_id, form_with_autocompletetype,
+ *form_with_autocompletetype.fields.begin(),
+ PackGUIDs(empty, guid));
+ page_id = 0;
+ FormData results2;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results2));
+ EXPECT_EQ(2, page_id);
+
+ ASSERT_EQ(5U, results2.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("1"), results2.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("650"), results2.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("555"), results2.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("4567"), results2.fields[3].value);
+ EXPECT_EQ(string16(), results2.fields[4].value);
+
+ // We should not be able to fill prefix and suffix fields for international
+ // numbers.
+ work_profile->SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("GB"));
+ work_profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("447700954321"));
+ page_id = 3;
+ FillAutofillFormData(page_id, form_with_maxlength,
+ *form_with_maxlength.fields.begin(),
+ PackGUIDs(empty, guid));
+ page_id = 0;
+ FormData results3;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results3));
+ EXPECT_EQ(3, page_id);
+
+ ASSERT_EQ(5U, results3.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("44"), results3.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("7700"), results3.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), results3.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), results3.fields[3].value);
+ EXPECT_EQ(string16(), results3.fields[4].value);
+
+ page_id = 4;
+ FillAutofillFormData(page_id, form_with_autocompletetype,
+ *form_with_autocompletetype.fields.begin(),
+ PackGUIDs(empty, guid));
+ page_id = 0;
+ FormData results4;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results4));
+ EXPECT_EQ(4, page_id);
+
+ ASSERT_EQ(5U, results4.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("44"), results4.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("7700"), results4.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), results4.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), results4.fields[3].value);
+ EXPECT_EQ(string16(), results4.fields[4].value);
+
+ // We should fill all phone fields with the same phone number variant.
+ std::vector<string16> phone_variants;
+ phone_variants.push_back(ASCIIToUTF16("16505554567"));
+ phone_variants.push_back(ASCIIToUTF16("18887771234"));
+ work_profile->SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ work_profile->SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, phone_variants);
+
+ page_id = 5;
+ GUIDPair variant_guid(work_profile->guid(), 1);
+ FillAutofillFormData(page_id, form_with_maxlength,
+ *form_with_maxlength.fields.begin(),
+ PackGUIDs(empty, variant_guid));
+ page_id = 0;
+ FormData results5;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results5));
+ EXPECT_EQ(5, page_id);
+
+ ASSERT_EQ(5U, results5.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("1"), results5.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("888"), results5.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("777"), results5.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("1234"), results5.fields[3].value);
+ EXPECT_EQ(string16(), results5.fields[4].value);
+}
+
+// Test that we can still fill a form when a field has been removed from it.
+TEST_F(AutofillManagerTest, FormChangesRemoveField) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+
+ // Add a field -- we'll remove it again later.
+ FormFieldData field;
+ autofill_test::CreateTestFormField("Some", "field", "", "text", &field);
+ form.fields.insert(form.fields.begin() + 3, field);
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Now, after the call to |FormsSeen|, we remove the field before filling.
+ form.fields.erase(form.fields.begin() + 3);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+}
+
+// Test that we can still fill a form when a field has been added to it.
+TEST_F(AutofillManagerTest, FormChangesAddField) {
+ // The offset of the phone field in the address form.
+ const int kPhoneFieldOffset = 9;
+
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+
+ // Remove the phone field -- we'll add it back later.
+ std::vector<FormFieldData>::iterator pos =
+ form.fields.begin() + kPhoneFieldOffset;
+ FormFieldData field = *pos;
+ pos = form.fields.erase(pos);
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Now, after the call to |FormsSeen|, we restore the field before filling.
+ form.fields.insert(pos, field);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+}
+
+// Test that we are able to save form data when forms are submitted.
+TEST_F(AutofillManagerTest, FormSubmitted) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Fill the form.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+
+ // Simulate form submission. We should call into the PDM to try to save the
+ // filled data.
+ EXPECT_CALL(personal_data_, SaveImportedProfile(::testing::_)).Times(1);
+ FormSubmitted(results);
+}
+
+// Test that we are able to save form data when forms are submitted and we only
+// have server data for the field types.
+TEST_F(AutofillManagerTest, FormSubmittedServerTypes) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ TestFormStructure* form_structure = new TestFormStructure(form);
+ AutofillMetrics metrics_logger; // ignored
+ form_structure->DetermineHeuristicTypes(metrics_logger);
+
+ // Clear the heuristic types, and instead set the appropriate server types.
+ std::vector<AutofillFieldType> heuristic_types, server_types;
+ for (size_t i = 0; i < form.fields.size(); ++i) {
+ heuristic_types.push_back(UNKNOWN_TYPE);
+ server_types.push_back(form_structure->field(i)->type());
+ }
+ form_structure->SetFieldTypes(heuristic_types, server_types);
+ autofill_manager_->AddSeenForm(form_structure);
+
+ // Fill the form.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+ ExpectFilledAddressFormElvis(page_id, results, kDefaultPageID, false);
+
+ // Simulate form submission. We should call into the PDM to try to save the
+ // filled data.
+ EXPECT_CALL(personal_data_, SaveImportedProfile(::testing::_)).Times(1);
+ FormSubmitted(results);
+}
+
+// Test that the form signature for an uploaded form always matches the form
+// signature from the query.
+TEST_F(AutofillManagerTest, FormSubmittedWithDifferentFields) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Cache the expected form signature.
+ std::string signature = FormStructure(form, std::string()).FormSignature();
+
+ // Change the structure of the form prior to submission.
+ // Websites would typically invoke JavaScript either on page load or on form
+ // submit to achieve this.
+ form.fields.pop_back();
+ FormFieldData field = form.fields[3];
+ form.fields[3] = form.fields[7];
+ form.fields[7] = field;
+
+ // Simulate form submission.
+ FormSubmitted(form);
+ EXPECT_EQ(signature, autofill_manager_->GetSubmittedFormSignature());
+}
+
+// Test that we do not save form data when submitted fields contain default
+// values.
+TEST_F(AutofillManagerTest, FormSubmittedWithDefaultValues) {
+ // Set up our form data.
+ FormData form;
+ CreateTestAddressFormData(&form);
+ form.fields[3].value = ASCIIToUTF16("Enter your address");
+
+ // Convert the state field to a <select> popup, to make sure that we only
+ // reject default values for text fields.
+ ASSERT_TRUE(form.fields[6].name == ASCIIToUTF16("state"));
+ form.fields[6].form_control_type = "select-one";
+ form.fields[6].value = ASCIIToUTF16("Tennessee");
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // Fill the form.
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ FillAutofillFormData(kDefaultPageID, form, form.fields[3],
+ PackGUIDs(empty, guid));
+
+ int page_id = 0;
+ FormData results;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&page_id, &results));
+
+ // Simulate form submission. We should call into the PDM to try to save the
+ // filled data.
+ EXPECT_CALL(personal_data_, SaveImportedProfile(::testing::_)).Times(1);
+ FormSubmitted(results);
+
+ // Set the address field's value back to the default value.
+ results.fields[3].value = ASCIIToUTF16("Enter your address");
+
+ // Simulate form submission. We should not call into the PDM to try to save
+ // the filled data, since the filled form is effectively missing an address.
+ EXPECT_CALL(personal_data_, SaveImportedProfile(::testing::_)).Times(0);
+ FormSubmitted(results);
+}
+
+// Checks that resetting the auxiliary profile enabled preference does the right
+// thing on all platforms.
+TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) {
+ PrefService* prefs = components::UserPrefs::Get(profile());
+#if defined(OS_MACOSX)
+ // Auxiliary profiles is implemented on Mac only. It enables Mac Address
+ // Book integration.
+ ASSERT_TRUE(prefs->GetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled));
+ prefs->SetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled, false);
+ prefs->ClearPref(prefs::kAutofillAuxiliaryProfilesEnabled);
+ ASSERT_TRUE(prefs->GetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled));
+#else
+ ASSERT_FALSE(prefs->GetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled));
+ prefs->SetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled, true);
+ prefs->ClearPref(prefs::kAutofillAuxiliaryProfilesEnabled);
+ ASSERT_FALSE(prefs->GetBoolean(prefs::kAutofillAuxiliaryProfilesEnabled));
+#endif
+}
+
+TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUpload) {
+ FormData form;
+ form.name = ASCIIToUTF16("MyForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://myform.com/form.html");
+ form.action = GURL("http://myform.com/submit.html");
+ form.user_submitted = true;
+
+ std::vector<FieldTypeSet> expected_types;
+
+ // These fields should all match.
+ FormFieldData field;
+ FieldTypeSet types;
+ autofill_test::CreateTestFormField("", "1", "Elvis", "text", &field);
+ types.clear();
+ types.insert(NAME_FIRST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "2", "Aaron", "text", &field);
+ types.clear();
+ types.insert(NAME_MIDDLE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "3", "A", "text", &field);
+ types.clear();
+ types.insert(NAME_MIDDLE_INITIAL);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "4", "Presley", "text", &field);
+ types.clear();
+ types.insert(NAME_LAST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "5", "Elvis Presley", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_NAME);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "6", "Elvis Aaron Presley", "text",
+ &field);
+ types.clear();
+ types.insert(NAME_FULL);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "7", "theking@gmail.com", "email",
+ &field);
+ types.clear();
+ types.insert(EMAIL_ADDRESS);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "8", "RCA", "text", &field);
+ types.clear();
+ types.insert(COMPANY_NAME);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "9", "3734 Elvis Presley Blvd.",
+ "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_LINE1);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "10", "Apt. 10", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_LINE2);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "11", "Memphis", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_CITY);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "12", "Tennessee", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_STATE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "13", "38116", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_ZIP);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "14", "USA", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_COUNTRY);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "15", "United States", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_COUNTRY);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "16", "+1 (234) 567-8901", "text",
+ &field);
+ types.clear();
+ types.insert(PHONE_HOME_WHOLE_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "17", "2345678901", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_CITY_AND_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "18", "1", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_COUNTRY_CODE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "19", "234", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_CITY_CODE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "20", "5678901", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "21", "567", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "22", "8901", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "23", "4234-5678-9012-3456", "text",
+ &field);
+ types.clear();
+ types.insert(CREDIT_CARD_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "24", "04", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_MONTH);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "25", "April", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_MONTH);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "26", "2012", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "27", "12", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "28", "04/2012", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ // Make sure that we trim whitespace properly.
+ autofill_test::CreateTestFormField("", "29", "", "text", &field);
+ types.clear();
+ types.insert(EMPTY_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "30", " ", "text", &field);
+ types.clear();
+ types.insert(EMPTY_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "31", " Elvis", "text", &field);
+ types.clear();
+ types.insert(NAME_FIRST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "32", "Elvis ", "text", &field);
+ types.clear();
+ types.insert(NAME_FIRST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ // These fields should not match, as they differ by case.
+ autofill_test::CreateTestFormField("", "33", "elvis", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "34", "3734 Elvis Presley BLVD",
+ "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ // These fields should not match, as they are unsupported variants.
+ autofill_test::CreateTestFormField("", "35", "Elvis Aaron", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "36", "Mr. Presley", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "37", "3734 Elvis Presley", "text",
+ &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "38", "TN", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "39", "38116-1023", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "20", "5", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "20", "56", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_test::CreateTestFormField("", "20", "901", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ autofill_manager_->set_expected_submitted_field_types(expected_types);
+ FormSubmitted(form);
+}
+
+TEST_F(AutofillManagerTest, UpdatePasswordSyncState) {
+ PasswordManagerDelegateImpl::CreateForWebContents(web_contents());
+ PasswordManager::CreateForWebContentsAndDelegate(
+ web_contents(),
+ PasswordManagerDelegateImpl::FromWebContents(web_contents()));
+
+ PrefService* prefs = components::UserPrefs::Get(profile());
+
+ // Allow this test to control what should get synced.
+ prefs->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
+ // Always set password generation enabled check box so we can test the
+ // behavior of password sync.
+ prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true);
+
+ // Sync some things, but not passwords. Shouldn't send anything since
+ // password generation is disabled by default.
+ ProfileSyncService* sync_service = ProfileSyncServiceFactory::GetForProfile(
+ profile());
+ sync_service->SetSyncSetupCompleted();
+ syncer::ModelTypeSet preferred_set;
+ preferred_set.Put(syncer::EXTENSIONS);
+ preferred_set.Put(syncer::PREFERENCES);
+ sync_service->ChangePreferredDataTypes(preferred_set);
+ syncer::ModelTypeSet new_set = sync_service->GetPreferredDataTypes();
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(0u, autofill_manager_->GetSentStates().size());
+
+ // Now sync passwords.
+ preferred_set.Put(syncer::PASSWORDS);
+ sync_service->ChangePreferredDataTypes(preferred_set);
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(1u, autofill_manager_->GetSentStates().size());
+ EXPECT_TRUE(autofill_manager_->GetSentStates()[0]);
+ autofill_manager_->ClearSentStates();
+
+ // Add some additional synced state. Nothing should be sent.
+ preferred_set.Put(syncer::THEMES);
+ sync_service->ChangePreferredDataTypes(preferred_set);
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(0u, autofill_manager_->GetSentStates().size());
+
+ // Disable syncing. This should disable the feature.
+ sync_service->DisableForUser();
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(1u, autofill_manager_->GetSentStates().size());
+ EXPECT_FALSE(autofill_manager_->GetSentStates()[0]);
+ autofill_manager_->ClearSentStates();
+
+ // When a new render_view is created, we send the state even if it's the
+ // same.
+ UpdatePasswordGenerationState(true);
+ EXPECT_EQ(1u, autofill_manager_->GetSentStates().size());
+ EXPECT_FALSE(autofill_manager_->GetSentStates()[0]);
+ autofill_manager_->ClearSentStates();
+}
+
+TEST_F(IncognitoAutofillManagerTest, UpdatePasswordSyncStateIncognito) {
+ // Disable password manager by going incognito, and enable syncing. The
+ // feature should still be disabled, and nothing will be sent.
+ PasswordManagerDelegateImpl::CreateForWebContents(web_contents());
+ PasswordManager::CreateForWebContentsAndDelegate(
+ web_contents(),
+ PasswordManagerDelegateImpl::FromWebContents(web_contents()));
+
+ PrefService* prefs = components::UserPrefs::Get(profile());
+
+ // Allow this test to control what should get synced.
+ prefs->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
+ // Always set password generation enabled check box so we can test the
+ // behavior of password sync.
+ prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true);
+
+ browser_sync::SyncPrefs sync_prefs(profile()->GetPrefs());
+ sync_prefs.SetSyncSetupCompleted();
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(0u, autofill_manager_->GetSentStates().size());
+}
+
+TEST_F(AutofillManagerTest, UpdatePasswordGenerationState) {
+ PasswordManagerDelegateImpl::CreateForWebContents(web_contents());
+ PasswordManager::CreateForWebContentsAndDelegate(
+ web_contents(),
+ PasswordManagerDelegateImpl::FromWebContents(web_contents()));
+
+ PrefService* prefs = components::UserPrefs::Get(profile());
+
+ // Always set password sync enabled so we can test the behavior of password
+ // generation.
+ prefs->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
+ ProfileSyncService* sync_service = ProfileSyncServiceFactory::GetForProfile(
+ profile());
+ sync_service->SetSyncSetupCompleted();
+ syncer::ModelTypeSet preferred_set;
+ preferred_set.Put(syncer::PASSWORDS);
+ sync_service->ChangePreferredDataTypes(preferred_set);
+
+ // Enabled state remains false, should not sent.
+ prefs->SetBoolean(prefs::kPasswordGenerationEnabled, false);
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(0u, autofill_manager_->GetSentStates().size());
+
+ // Enabled state from false to true, should sent true.
+ prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true);
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(1u, autofill_manager_->GetSentStates().size());
+ EXPECT_TRUE(autofill_manager_->GetSentStates()[0]);
+ autofill_manager_->ClearSentStates();
+
+ // Enabled states remains true, should not sent.
+ prefs->SetBoolean(prefs::kPasswordGenerationEnabled, true);
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(0u, autofill_manager_->GetSentStates().size());
+
+ // Enabled states from true to false, should sent false.
+ prefs->SetBoolean(prefs::kPasswordGenerationEnabled, false);
+ UpdatePasswordGenerationState(false);
+ EXPECT_EQ(1u, autofill_manager_->GetSentStates().size());
+ EXPECT_FALSE(autofill_manager_->GetSentStates()[0]);
+ autofill_manager_->ClearSentStates();
+
+ // When a new render_view is created, we send the state even if it's the
+ // same.
+ UpdatePasswordGenerationState(true);
+ EXPECT_EQ(1u, autofill_manager_->GetSentStates().size());
+ EXPECT_FALSE(autofill_manager_->GetSentStates()[0]);
+ autofill_manager_->ClearSentStates();
+}
+
+TEST_F(AutofillManagerTest, RemoveProfile) {
+ // Add and remove an Autofill profile.
+ AutofillProfile* profile = new AutofillProfile;
+ std::string guid = "00000000-0000-0000-0000-000000000102";
+ profile->set_guid(guid.c_str());
+ autofill_manager_->AddProfile(profile);
+
+ GUIDPair guid_pair(guid, 0);
+ GUIDPair empty(std::string(), 0);
+ int id = PackGUIDs(empty, guid_pair);
+
+ autofill_manager_->RemoveAutofillProfileOrCreditCard(id);
+
+ EXPECT_FALSE(autofill_manager_->GetProfileWithGUID(guid.c_str()));
+}
+
+TEST_F(AutofillManagerTest, RemoveCreditCard){
+ // Add and remove an Autofill credit card.
+ CreditCard* credit_card = new CreditCard;
+ std::string guid = "00000000-0000-0000-0000-000000100007";
+ credit_card->set_guid(guid.c_str());
+ autofill_manager_->AddCreditCard(credit_card);
+
+ GUIDPair guid_pair(guid, 0);
+ GUIDPair empty(std::string(), 0);
+ int id = PackGUIDs(guid_pair, empty);
+
+ autofill_manager_->RemoveAutofillProfileOrCreditCard(id);
+
+ EXPECT_FALSE(autofill_manager_->GetCreditCardWithGUID(guid.c_str()));
+}
+
+TEST_F(AutofillManagerTest, RemoveProfileVariant) {
+ // Add and remove an Autofill profile.
+ AutofillProfile* profile = new AutofillProfile;
+ std::string guid = "00000000-0000-0000-0000-000000000102";
+ profile->set_guid(guid.c_str());
+ autofill_manager_->AddProfile(profile);
+
+ GUIDPair guid_pair(guid, 1);
+ GUIDPair empty(std::string(), 0);
+ int id = PackGUIDs(empty, guid_pair);
+
+ autofill_manager_->RemoveAutofillProfileOrCreditCard(id);
+
+ // TODO(csharp): Currently variants should not be deleted, but once they are
+ // update these expectations.
+ // http://crbug.com/124211
+ EXPECT_TRUE(autofill_manager_->GetProfileWithGUID(guid.c_str()));
+}
+
+TEST_F(AutofillManagerTest, DisabledAutofillDispatchesError) {
+ EXPECT_TRUE(autofill_manager_->request_autocomplete_results().empty());
+
+ autofill_manager_->set_autofill_enabled(false);
+ autofill_manager_->OnRequestAutocomplete(FormData(),
+ GURL(),
+ content::SSLStatus());
+
+ EXPECT_EQ(1U, autofill_manager_->request_autocomplete_results().size());
+ EXPECT_EQ(WebFormElement::AutocompleteResultErrorDisabled,
+ autofill_manager_->request_autocomplete_results()[0].first);
+}
+
+namespace {
+
+class MockAutofillExternalDelegate : public AutofillExternalDelegate {
+ public:
+ explicit MockAutofillExternalDelegate(content::WebContents* web_contents,
+ AutofillManager* autofill_manager)
+ : AutofillExternalDelegate(web_contents, autofill_manager) {}
+ virtual ~MockAutofillExternalDelegate() {}
+
+ MOCK_METHOD5(OnQuery, void(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounds,
+ bool display_warning));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillExternalDelegate);
+};
+
+} // namespace
+
+// Test our external delegate is called at the right time.
+TEST_F(AutofillManagerTest, TestExternalDelegate) {
+ MockAutofillExternalDelegate external_delegate(web_contents(),
+ autofill_manager_.get());
+ EXPECT_CALL(external_delegate, OnQuery(_, _, _, _, _));
+ autofill_manager_->SetExternalDelegate(&external_delegate);
+
+ FormData form;
+ CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field); // should call the delegate's OnQuery()
+
+ autofill_manager_->SetExternalDelegate(NULL);
+}
diff --git a/components/autofill/browser/autofill_merge_unittest.cc b/components/autofill/browser/autofill_merge_unittest.cc
new file mode 100644
index 0000000..dad4fdb
--- /dev/null
+++ b/components/autofill/browser/autofill_merge_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/data_driven_test.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/common/form_data.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+
+namespace {
+
+const base::FilePath::CharType kTestName[] = FILE_PATH_LITERAL("merge");
+const base::FilePath::CharType kFileNamePattern[] = FILE_PATH_LITERAL("*.in");
+
+const char kFieldSeparator[] = ": ";
+const char kProfileSeparator[] = "---";
+const size_t kFieldOffset = arraysize(kFieldSeparator) - 1;
+
+const AutofillFieldType kProfileFieldTypes[] = {
+ NAME_FIRST,
+ NAME_MIDDLE,
+ NAME_LAST,
+ EMAIL_ADDRESS,
+ COMPANY_NAME,
+ ADDRESS_HOME_LINE1,
+ ADDRESS_HOME_LINE2,
+ ADDRESS_HOME_CITY,
+ ADDRESS_HOME_STATE,
+ ADDRESS_HOME_ZIP,
+ ADDRESS_HOME_COUNTRY,
+ PHONE_HOME_WHOLE_NUMBER
+};
+
+// Serializes the |profiles| into a string.
+std::string SerializeProfiles(const std::vector<AutofillProfile*>& profiles) {
+ std::string result;
+ for (size_t i = 0; i < profiles.size(); ++i) {
+ result += kProfileSeparator;
+ result += "\n";
+ for (size_t j = 0; j < arraysize(kProfileFieldTypes); ++j) {
+ AutofillFieldType type = kProfileFieldTypes[j];
+ std::vector<string16> values;
+ profiles[i]->GetRawMultiInfo(type, &values);
+ for (size_t k = 0; k < values.size(); ++k) {
+ result += AutofillType::FieldTypeToString(type);
+ result += kFieldSeparator;
+ result += UTF16ToUTF8(values[k]);
+ result += "\n";
+ }
+ }
+ }
+
+ return result;
+}
+
+class PersonalDataManagerMock : public PersonalDataManager {
+ public:
+ PersonalDataManagerMock();
+ virtual ~PersonalDataManagerMock();
+
+ // Reset the saved profiles.
+ void Reset();
+
+ // PersonalDataManager:
+ virtual void SaveImportedProfile(const AutofillProfile& profile) OVERRIDE;
+ virtual const std::vector<AutofillProfile*>& web_profiles() const OVERRIDE;
+
+ private:
+ ScopedVector<AutofillProfile> profiles_;
+
+ DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerMock);
+};
+
+PersonalDataManagerMock::PersonalDataManagerMock() : PersonalDataManager() {
+}
+
+PersonalDataManagerMock::~PersonalDataManagerMock() {
+}
+
+void PersonalDataManagerMock::Reset() {
+ profiles_.clear();
+}
+
+void PersonalDataManagerMock::SaveImportedProfile(
+ const AutofillProfile& profile) {
+ std::vector<AutofillProfile> profiles;
+ if (!MergeProfile(profile, profiles_.get(), &profiles))
+ profiles_.push_back(new AutofillProfile(profile));
+}
+
+const std::vector<AutofillProfile*>& PersonalDataManagerMock::web_profiles()
+ const {
+ return profiles_.get();
+}
+
+} // namespace
+
+// A data-driven test for verifying merging of Autofill profiles. Each input is
+// a structured dump of a set of implicitly detected autofill profiles. The
+// corresponding output file is a dump of the saved profiles that result from
+// importing the input profiles. The output file format is identical to the
+// input format.
+class AutofillMergeTest : public testing::Test,
+ public DataDrivenTest {
+ protected:
+ AutofillMergeTest();
+ virtual ~AutofillMergeTest();
+
+ // testing::Test:
+ virtual void SetUp();
+
+ // DataDrivenTest:
+ virtual void GenerateResults(const std::string& input,
+ std::string* output) OVERRIDE;
+
+ // Deserializes a set of Autofill profiles from |profiles|, imports each
+ // sequentially, and fills |merged_profiles| with the serialized result.
+ void MergeProfiles(const std::string& profiles, std::string* merged_profiles);
+
+ PersonalDataManagerMock personal_data_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillMergeTest);
+};
+
+AutofillMergeTest::AutofillMergeTest() : DataDrivenTest() {
+}
+
+AutofillMergeTest::~AutofillMergeTest() {
+}
+
+void AutofillMergeTest::SetUp() {
+ autofill_test::DisableSystemServices(NULL);
+}
+
+void AutofillMergeTest::GenerateResults(const std::string& input,
+ std::string* output) {
+ MergeProfiles(input, output);
+}
+
+void AutofillMergeTest::MergeProfiles(const std::string& profiles,
+ std::string* merged_profiles) {
+ // Start with no saved profiles.
+ personal_data_.Reset();
+
+ // Create a test form.
+ FormData form;
+ form.name = ASCIIToUTF16("MyTestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("https://www.example.com/origin.html");
+ form.action = GURL("https://www.example.com/action.html");
+ form.user_submitted = true;
+
+ // Parse the input line by line.
+ std::vector<std::string> lines;
+ Tokenize(profiles, "\n", &lines);
+ for (size_t i = 0; i < lines.size(); ++i) {
+ std::string line = lines[i];
+
+ if (line != kProfileSeparator) {
+ // Add a field to the current profile.
+ size_t separator_pos = line.find(kFieldSeparator);
+ ASSERT_NE(std::string::npos, separator_pos);
+ string16 field_type = UTF8ToUTF16(line.substr(0, separator_pos));
+ string16 value = UTF8ToUTF16(line.substr(separator_pos + kFieldOffset));
+
+ FormFieldData field;
+ field.label = field_type;
+ field.name = field_type;
+ field.value = value;
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+ }
+
+ // The first line is always a profile separator, and the last profile is not
+ // followed by an explicit separator.
+ if ((i > 0 && line == kProfileSeparator) || i == lines.size() - 1) {
+ // Reached the end of a profile. Try to import it.
+ FormStructure form_structure(form, std::string());
+ for (size_t i = 0; i < form_structure.field_count(); ++i) {
+ // Set the heuristic type for each field, which is currently serialized
+ // into the field's name.
+ AutofillField* field =
+ const_cast<AutofillField*>(form_structure.field(i));
+ AutofillFieldType type =
+ AutofillType::StringToFieldType(UTF16ToUTF8(field->name));
+ field->set_heuristic_type(type);
+ }
+
+ // Import the profile.
+ const CreditCard* imported_credit_card;
+ personal_data_.ImportFormData(form_structure, &imported_credit_card);
+ EXPECT_EQ(static_cast<const CreditCard*>(NULL), imported_credit_card);
+
+ // Clear the |form| to start a new profile.
+ form.fields.clear();
+ }
+ }
+
+ *merged_profiles = SerializeProfiles(personal_data_.web_profiles());
+}
+
+TEST_F(AutofillMergeTest, DataDrivenMergeProfiles) {
+ RunDataDrivenTest(GetInputDirectory(kTestName), GetOutputDirectory(kTestName),
+ kFileNamePattern);
+}
diff --git a/components/autofill/browser/autofill_metrics.cc b/components/autofill/browser/autofill_metrics.cc
new file mode 100644
index 0000000..b59ba54
--- /dev/null
+++ b/components/autofill/browser/autofill_metrics.cc
@@ -0,0 +1,453 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_metrics.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/time.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/common/form_data.h"
+
+namespace {
+
+// Server experiments we support.
+enum ServerExperiment {
+ NO_EXPERIMENT = 0,
+ UNKNOWN_EXPERIMENT,
+ ACCEPTANCE_RATIO_06,
+ ACCEPTANCE_RATIO_1,
+ ACCEPTANCE_RATIO_2,
+ ACCEPTANCE_RATIO_4,
+ ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15,
+ ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_25,
+ ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15_MIN_FORM_SCORE_5,
+ TOOLBAR_DATA_ONLY,
+ ACCEPTANCE_RATIO_04_WINNER_LEAD_RATIO_3_MIN_FORM_SCORE_4,
+ NO_SERVER_RESPONSE,
+ PROBABILITY_PICKER_05,
+ PROBABILITY_PICKER_025,
+ PROBABILITY_PICKER_025_CC_THRESHOLD_03,
+ PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03,
+ PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03_WITH_FALLBACK,
+ PROBABILITY_PICKER_05_CC_NAME_THRESHOLD_03_EXPERIMENT_1,
+ NUM_SERVER_EXPERIMENTS
+};
+
+enum FieldTypeGroupForMetrics {
+ AMBIGUOUS = 0,
+ NAME,
+ COMPANY,
+ ADDRESS_LINE_1,
+ ADDRESS_LINE_2,
+ ADDRESS_CITY,
+ ADDRESS_STATE,
+ ADDRESS_ZIP,
+ ADDRESS_COUNTRY,
+ PHONE,
+ FAX, // Deprecated.
+ EMAIL,
+ CREDIT_CARD_NAME,
+ CREDIT_CARD_NUMBER,
+ CREDIT_CARD_DATE,
+ CREDIT_CARD_TYPE,
+ NUM_FIELD_TYPE_GROUPS_FOR_METRICS
+};
+
+// First, translates |field_type| to the corresponding logical |group| from
+// |FieldTypeGroupForMetrics|. Then, interpolates this with the given |metric|,
+// which should be in the range [0, |num_possible_metrics|).
+// Returns the interpolated index.
+//
+// The interpolation maps the pair (|group|, |metric|) to a single index, so
+// that all the indicies for a given group are adjacent. In particular, with
+// the groups {AMBIGUOUS, NAME, ...} combining with the metrics {UNKNOWN, MATCH,
+// MISMATCH}, we create this set of mapped indices:
+// {
+// AMBIGUOUS+UNKNOWN,
+// AMBIGUOUS+MATCH,
+// AMBIGUOUS+MISMATCH,
+// NAME+UNKNOWN,
+// NAME+MATCH,
+// NAME+MISMATCH,
+// ...
+// }.
+//
+// Clients must ensure that |field_type| is one of the types Chrome supports
+// natively, e.g. |field_type| must not be a billng address.
+int GetFieldTypeGroupMetric(const AutofillFieldType field_type,
+ const int metric,
+ const int num_possible_metrics) {
+ DCHECK(metric < num_possible_metrics);
+
+ FieldTypeGroupForMetrics group;
+ switch (AutofillType(field_type).group()) {
+ case AutofillType::NO_GROUP:
+ group = AMBIGUOUS;
+ break;
+
+ case AutofillType::NAME:
+ group = NAME;
+ break;
+
+ case AutofillType::COMPANY:
+ group = COMPANY;
+ break;
+
+ case AutofillType::ADDRESS_HOME:
+ switch (field_type) {
+ case ADDRESS_HOME_LINE1:
+ group = ADDRESS_LINE_1;
+ break;
+ case ADDRESS_HOME_LINE2:
+ group = ADDRESS_LINE_2;
+ break;
+ case ADDRESS_HOME_CITY:
+ group = ADDRESS_CITY;
+ break;
+ case ADDRESS_HOME_STATE:
+ group = ADDRESS_STATE;
+ break;
+ case ADDRESS_HOME_ZIP:
+ group = ADDRESS_ZIP;
+ break;
+ case ADDRESS_HOME_COUNTRY:
+ group = ADDRESS_COUNTRY;
+ break;
+ default:
+ NOTREACHED();
+ group = AMBIGUOUS;
+ }
+ break;
+
+ case AutofillType::EMAIL:
+ group = EMAIL;
+ break;
+
+ case AutofillType::PHONE:
+ group = PHONE;
+ break;
+
+ case AutofillType::CREDIT_CARD:
+ switch (field_type) {
+ case ::CREDIT_CARD_NAME:
+ group = CREDIT_CARD_NAME;
+ break;
+ case ::CREDIT_CARD_NUMBER:
+ group = CREDIT_CARD_NUMBER;
+ break;
+ case ::CREDIT_CARD_TYPE:
+ group = CREDIT_CARD_TYPE;
+ default:
+ group = CREDIT_CARD_DATE;
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ group = AMBIGUOUS;
+ }
+
+ // Interpolate the |metric| with the |group|, so that all metrics for a given
+ // |group| are adjacent.
+ return (group * num_possible_metrics) + metric;
+}
+
+// A version of the UMA_HISTOGRAM_ENUMERATION macro that allows the |name|
+// to vary over the program's runtime.
+void LogUMAHistogramEnumeration(const std::string& name,
+ int sample,
+ int boundary_value) {
+ // Note: This leaks memory, which is expected behavior.
+ base::HistogramBase* histogram =
+ base::LinearHistogram::FactoryGet(
+ name,
+ 1,
+ boundary_value,
+ boundary_value + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->Add(sample);
+}
+
+// A version of the UMA_HISTOGRAM_LONG_TIMES macro that allows the |name|
+// to vary over the program's runtime.
+void LogUMAHistogramLongTimes(const std::string& name,
+ const base::TimeDelta& duration) {
+ // Note: This leaks memory, which is expected behavior.
+ base::HistogramBase* histogram =
+ base::Histogram::FactoryTimeGet(
+ name,
+ base::TimeDelta::FromMilliseconds(1),
+ base::TimeDelta::FromHours(1),
+ 50,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->AddTime(duration);
+}
+
+// Logs a type quality metric. The primary histogram name is constructed based
+// on |base_name| and |experiment_id|. The field-specific histogram name also
+// factors in the |field_type|. Logs a sample of |metric|, which should be in
+// the range [0, |num_possible_metrics|).
+void LogTypeQualityMetric(const std::string& base_name,
+ const int metric,
+ const int num_possible_metrics,
+ const AutofillFieldType field_type,
+ const std::string& experiment_id) {
+ DCHECK(metric < num_possible_metrics);
+
+ std::string histogram_name = base_name;
+ if (!experiment_id.empty())
+ histogram_name += "_" + experiment_id;
+ LogUMAHistogramEnumeration(histogram_name, metric, num_possible_metrics);
+
+ std::string sub_histogram_name = base_name + ".ByFieldType";
+ if (!experiment_id.empty())
+ sub_histogram_name += "_" + experiment_id;
+ const int field_type_group_metric =
+ GetFieldTypeGroupMetric(field_type, metric, num_possible_metrics);
+ const int num_field_type_group_metrics =
+ num_possible_metrics * NUM_FIELD_TYPE_GROUPS_FOR_METRICS;
+ LogUMAHistogramEnumeration(sub_histogram_name,
+ field_type_group_metric,
+ num_field_type_group_metrics);
+}
+
+void LogServerExperimentId(const std::string& histogram_name,
+ const std::string& experiment_id) {
+ ServerExperiment metric = UNKNOWN_EXPERIMENT;
+
+ const std::string default_experiment_name =
+ FormStructure(FormData(), std::string()).server_experiment_id();
+ if (experiment_id.empty())
+ metric = NO_EXPERIMENT;
+ else if (experiment_id == "ar06")
+ metric = ACCEPTANCE_RATIO_06;
+ else if (experiment_id == "ar1")
+ metric = ACCEPTANCE_RATIO_1;
+ else if (experiment_id == "ar2")
+ metric = ACCEPTANCE_RATIO_2;
+ else if (experiment_id == "ar4")
+ metric = ACCEPTANCE_RATIO_4;
+ else if (experiment_id == "ar05wlr15")
+ metric = ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15;
+ else if (experiment_id == "ar05wlr25")
+ metric = ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_25;
+ else if (experiment_id == "ar05wr15fs5")
+ metric = ACCEPTANCE_RATIO_05_WINNER_LEAD_RATIO_15_MIN_FORM_SCORE_5;
+ else if (experiment_id == "tbar1")
+ metric = TOOLBAR_DATA_ONLY;
+ else if (experiment_id == "ar04wr3fs4")
+ metric = ACCEPTANCE_RATIO_04_WINNER_LEAD_RATIO_3_MIN_FORM_SCORE_4;
+ else if (experiment_id == default_experiment_name)
+ metric = NO_SERVER_RESPONSE;
+ else if (experiment_id == "fp05")
+ metric = PROBABILITY_PICKER_05;
+ else if (experiment_id == "fp025")
+ metric = PROBABILITY_PICKER_025;
+ else if (experiment_id == "fp05cc03")
+ metric = PROBABILITY_PICKER_025_CC_THRESHOLD_03;
+ else if (experiment_id == "fp05cco03")
+ metric = PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03;
+ else if (experiment_id == "fp05cco03cstd")
+ metric = PROBABILITY_PICKER_025_CONTEXTUAL_CC_THRESHOLD_03_WITH_FALLBACK;
+ else if (experiment_id == "fp05cc03e1")
+ metric = PROBABILITY_PICKER_05_CC_NAME_THRESHOLD_03_EXPERIMENT_1;
+
+ DCHECK(metric < NUM_SERVER_EXPERIMENTS);
+ LogUMAHistogramEnumeration(histogram_name, metric, NUM_SERVER_EXPERIMENTS);
+}
+
+} // namespace
+
+AutofillMetrics::AutofillMetrics() {
+}
+
+AutofillMetrics::~AutofillMetrics() {
+}
+
+void AutofillMetrics::LogAutocheckoutBubbleMetric(BubbleMetric metric) const {
+ DCHECK(metric < NUM_BUBBLE_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autocheckout.Bubble", metric, NUM_BUBBLE_METRICS);
+}
+
+void AutofillMetrics::LogCreditCardInfoBarMetric(InfoBarMetric metric) const {
+ DCHECK(metric < NUM_INFO_BAR_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardInfoBar", metric,
+ NUM_INFO_BAR_METRICS);
+}
+
+void AutofillMetrics::LogRequestAutocompleteUiDuration(
+ const base::TimeDelta& duration,
+ autofill::DialogType dialog_type,
+ DialogDismissalAction dismissal_action) const {
+ std::string prefix;
+ switch (dialog_type) {
+ case autofill::DIALOG_TYPE_AUTOCHECKOUT:
+ prefix = "Autocheckout";
+ break;
+
+ case autofill::DIALOG_TYPE_REQUEST_AUTOCOMPLETE:
+ prefix = "RequestAutocomplete";
+ break;
+ }
+
+ std::string suffix;
+ switch (dismissal_action) {
+ case DIALOG_ACCEPTED:
+ suffix = "Submit";
+ break;
+
+ case DIALOG_CANCELED:
+ suffix = "Cancel";
+ break;
+ }
+
+ LogUMAHistogramLongTimes(prefix + ".UiDuration", duration);
+ LogUMAHistogramLongTimes(prefix + ".UiDuration." + suffix, duration);
+}
+
+void AutofillMetrics::LogAutocheckoutDuration(
+ const base::TimeDelta& duration,
+ AutocheckoutCompletionStatus status) const {
+ std::string suffix;
+ switch (status) {
+ case AUTOCHECKOUT_FAILED:
+ suffix = "Failed";
+ break;
+
+ case AUTOCHECKOUT_SUCCEEDED:
+ suffix = "Succeeded";
+ break;
+ }
+
+ LogUMAHistogramLongTimes("Autocheckout.FlowDuration", duration);
+ LogUMAHistogramLongTimes("Autocheckout.FlowDuration." + suffix, duration);
+}
+
+void AutofillMetrics::LogDeveloperEngagementMetric(
+ DeveloperEngagementMetric metric) const {
+ DCHECK(metric < NUM_DEVELOPER_ENGAGEMENT_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.DeveloperEngagement", metric,
+ NUM_DEVELOPER_ENGAGEMENT_METRICS);
+}
+
+void AutofillMetrics::LogHeuristicTypePrediction(
+ FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id) const {
+ LogTypeQualityMetric("Autofill.Quality.HeuristicType",
+ metric, NUM_FIELD_TYPE_QUALITY_METRICS,
+ field_type, experiment_id);
+}
+
+void AutofillMetrics::LogOverallTypePrediction(
+ FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id) const {
+ LogTypeQualityMetric("Autofill.Quality.PredictedType",
+ metric, NUM_FIELD_TYPE_QUALITY_METRICS,
+ field_type, experiment_id);
+}
+
+void AutofillMetrics::LogServerTypePrediction(
+ FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id) const {
+ LogTypeQualityMetric("Autofill.Quality.ServerType",
+ metric, NUM_FIELD_TYPE_QUALITY_METRICS,
+ field_type, experiment_id);
+}
+
+void AutofillMetrics::LogQualityMetric(QualityMetric metric,
+ const std::string& experiment_id) const {
+ DCHECK(metric < NUM_QUALITY_METRICS);
+
+ std::string histogram_name = "Autofill.Quality";
+ if (!experiment_id.empty())
+ histogram_name += "_" + experiment_id;
+
+ LogUMAHistogramEnumeration(histogram_name, metric, NUM_QUALITY_METRICS);
+}
+
+void AutofillMetrics::LogServerQueryMetric(ServerQueryMetric metric) const {
+ DCHECK(metric < NUM_SERVER_QUERY_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.ServerQueryResponse", metric,
+ NUM_SERVER_QUERY_METRICS);
+}
+
+void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric) const {
+ DCHECK(metric < NUM_USER_HAPPINESS_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness", metric,
+ NUM_USER_HAPPINESS_METRICS);
+}
+
+void AutofillMetrics::LogFormFillDurationFromLoadWithAutofill(
+ const base::TimeDelta& duration) const {
+ UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithAutofill",
+ duration,
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMinutes(10),
+ 50);
+}
+
+void AutofillMetrics::LogFormFillDurationFromLoadWithoutAutofill(
+ const base::TimeDelta& duration) const {
+ UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithoutAutofill",
+ duration,
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMinutes(10),
+ 50);
+}
+
+void AutofillMetrics::LogFormFillDurationFromInteractionWithAutofill(
+ const base::TimeDelta& duration) const {
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "Autofill.FillDuration.FromInteraction.WithAutofill",
+ duration,
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMinutes(10),
+ 50);
+}
+
+void AutofillMetrics::LogFormFillDurationFromInteractionWithoutAutofill(
+ const base::TimeDelta& duration) const {
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "Autofill.FillDuration.FromInteraction.WithoutAutofill",
+ duration,
+ base::TimeDelta::FromMilliseconds(100),
+ base::TimeDelta::FromMinutes(10),
+ 50);
+}
+
+void AutofillMetrics::LogIsAutofillEnabledAtStartup(bool enabled) const {
+ UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.Startup", enabled);
+}
+
+void AutofillMetrics::LogIsAutofillEnabledAtPageLoad(bool enabled) const {
+ UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.PageLoad", enabled);
+}
+
+void AutofillMetrics::LogStoredProfileCount(size_t num_profiles) const {
+ UMA_HISTOGRAM_COUNTS("Autofill.StoredProfileCount", num_profiles);
+}
+
+void AutofillMetrics::LogAddressSuggestionsCount(size_t num_suggestions) const {
+ UMA_HISTOGRAM_COUNTS("Autofill.AddressSuggestionsCount", num_suggestions);
+}
+
+void AutofillMetrics::LogServerExperimentIdForQuery(
+ const std::string& experiment_id) const {
+ LogServerExperimentId("Autofill.ServerExperimentId.Query", experiment_id);
+}
+
+void AutofillMetrics::LogServerExperimentIdForUpload(
+ const std::string& experiment_id) const {
+ LogServerExperimentId("Autofill.ServerExperimentId.Upload", experiment_id);
+}
diff --git a/components/autofill/browser/autofill_metrics.h b/components/autofill/browser/autofill_metrics.h
new file mode 100644
index 0000000..36453b9
--- /dev/null
+++ b/components/autofill/browser/autofill_metrics.h
@@ -0,0 +1,248 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_METRICS_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_METRICS_H_
+
+#include <stddef.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
+#include "components/autofill/browser/field_types.h"
+
+namespace base {
+class TimeDelta;
+}
+
+class AutofillMetrics {
+ public:
+ // The success or failure of Autocheckout.
+ enum AutocheckoutCompletionStatus {
+ AUTOCHECKOUT_FAILED, // The user canceled out of the dialog after
+ // an Autocheckout failure.
+ AUTOCHECKOUT_SUCCEEDED, // The dialog was closed after Autocheckout
+ // succeeded.
+ };
+
+ // The action a user took to dismiss a bubble.
+ enum BubbleMetric {
+ BUBBLE_CREATED = 0, // The bubble was created.
+ BUBBLE_ACCEPTED, // The user accepted, i.e. confirmed, the bubble.
+ BUBBLE_DISMISSED, // The user dismissed the bubble.
+ BUBBLE_IGNORED, // The user did not interact with the bubble.
+ NUM_BUBBLE_METRICS,
+ };
+
+ enum DeveloperEngagementMetric {
+ // Parsed a form that is potentially autofillable.
+ FILLABLE_FORM_PARSED = 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,
+ NUM_DEVELOPER_ENGAGEMENT_METRICS,
+ };
+
+ // The action the user took to dismiss a dialog.
+ enum DialogDismissalAction {
+ DIALOG_ACCEPTED = 0, // The user accepted, i.e. confirmed, the dialog.
+ DIALOG_CANCELED, // The user canceled out of the dialog.
+ };
+
+ enum InfoBarMetric {
+ INFOBAR_SHOWN = 0, // We showed an infobar, e.g. prompting to save credit
+ // card info.
+ INFOBAR_ACCEPTED, // The user explicitly accepted the infobar.
+ INFOBAR_DENIED, // The user explicitly denied the infobar.
+ INFOBAR_IGNORED, // The user completely ignored the infobar (logged on
+ // tab close).
+ NUM_INFO_BAR_METRICS,
+ };
+
+ // Metrics measuring how well we predict field types. Exactly three such
+ // metrics are logged for each fillable field in a submitted form: for
+ // 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.
+ NUM_FIELD_TYPE_QUALITY_METRICS,
+ };
+
+ enum QualityMetric {
+ // Logged for each potentially fillable field in a submitted form.
+ FIELD_SUBMITTED = 0,
+
+ // A simple successs metric, logged for each field that returns true for
+ // |is_autofilled()|.
+ FIELD_AUTOFILLED,
+
+ // A simple failure metric, logged for each field that returns false for
+ // |is_autofilled()| but has a value that is present in the personal data
+ // manager.
+ FIELD_NOT_AUTOFILLED,
+
+ // The below are only logged when |FIELD_AUTOFILL_FAILED| is also logged.
+ NOT_AUTOFILLED_HEURISTIC_TYPE_UNKNOWN,
+ NOT_AUTOFILLED_HEURISTIC_TYPE_MATCH,
+ NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ NOT_AUTOFILLED_SERVER_TYPE_UNKNOWN,
+ NOT_AUTOFILLED_SERVER_TYPE_MATCH,
+ NOT_AUTOFILLED_SERVER_TYPE_MISMATCH,
+ NUM_QUALITY_METRICS,
+ };
+
+ // Each of these is logged at most once per query to the server, which in turn
+ // occurs at most once per page load.
+ enum ServerQueryMetric {
+ QUERY_SENT = 0, // Sent a query to the server.
+ QUERY_RESPONSE_RECEIVED, // Received a response.
+ QUERY_RESPONSE_PARSED, // Successfully parsed the server response.
+
+ // The response was parseable, but provided no improvements relative to our
+ // heuristics.
+ QUERY_RESPONSE_MATCHED_LOCAL_HEURISTICS,
+
+ // Our heuristics detected at least one auto-fillable field, and the server
+ // response overrode the type of at least one field.
+ QUERY_RESPONSE_OVERRODE_LOCAL_HEURISTICS,
+
+ // Our heuristics did not detect any auto-fillable fields, but the server
+ // response did detect at least one.
+ QUERY_RESPONSE_WITH_NO_LOCAL_HEURISTICS,
+ NUM_SERVER_QUERY_METRICS,
+ };
+
+ // Each of these metrics is logged only for potentially autofillable forms,
+ // i.e. forms with at least three fields, etc.
+ // These are used to derive certain "user happiness" metrics. For example, we
+ // can compute the ratio (USER_DID_EDIT_AUTOFILLED_FIELD / USER_DID_AUTOFILL)
+ // to see how often users have to correct autofilled data.
+ enum UserHappinessMetric {
+ // Loaded a page containing forms.
+ FORMS_LOADED,
+ // Submitted a fillable form -- i.e. one with at least three field values
+ // that match the user's stored Autofill data -- and all matching fields
+ // were autofilled.
+ SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL,
+ // Submitted a fillable form and some (but not all) matching fields were
+ // autofilled.
+ SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME,
+ // Submitted a fillable form and no fields were autofilled.
+ SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE,
+ // Submitted a non-fillable form.
+ SUBMITTED_NON_FILLABLE_FORM,
+
+ // User manually filled one of the form fields.
+ USER_DID_TYPE,
+ // We showed a popup containing Autofill suggestions.
+ SUGGESTIONS_SHOWN,
+ // Same as above, but only logged once per page load.
+ SUGGESTIONS_SHOWN_ONCE,
+ // User autofilled at least part of the form.
+ USER_DID_AUTOFILL,
+ // Same as above, but only logged once per page load.
+ USER_DID_AUTOFILL_ONCE,
+ // User edited a previously autofilled field.
+ USER_DID_EDIT_AUTOFILLED_FIELD,
+ // Same as above, but only logged once per page load.
+ USER_DID_EDIT_AUTOFILLED_FIELD_ONCE,
+ NUM_USER_HAPPINESS_METRICS,
+ };
+
+ AutofillMetrics();
+ virtual ~AutofillMetrics();
+
+ // Logs how the user interacted with the Autocheckout bubble.
+ virtual void LogAutocheckoutBubbleMetric(BubbleMetric metric) const;
+
+ virtual void LogCreditCardInfoBarMetric(InfoBarMetric metric) const;
+
+ virtual void LogDeveloperEngagementMetric(
+ DeveloperEngagementMetric metric) const;
+
+ virtual void LogHeuristicTypePrediction(
+ FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id) const;
+ virtual void LogOverallTypePrediction(
+ FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id) const;
+ virtual void LogServerTypePrediction(FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id) const;
+
+ virtual void LogQualityMetric(QualityMetric metric,
+ const std::string& experiment_id) const;
+
+ virtual void LogServerQueryMetric(ServerQueryMetric metric) const;
+
+ virtual void LogUserHappinessMetric(UserHappinessMetric metric) const;
+
+ // This should be called when the requestAutocomplete dialog, invoked by the
+ // |requester|, is closed. |duration| should be the time elapsed between the
+ // dialog being shown and it being closed. |dismissal_action| should indicate
+ // whether the user dismissed the dialog by submitting the form data or by
+ // cancelling.
+ virtual void LogRequestAutocompleteUiDuration(
+ const base::TimeDelta& duration,
+ autofill::DialogType dialog_type,
+ DialogDismissalAction dismissal_action) const;
+
+ virtual void LogAutocheckoutDuration(
+ const base::TimeDelta& duration,
+ AutocheckoutCompletionStatus status) const;
+
+ // This should be called when a form that has been Autofilled is submitted.
+ // |duration| should be the time elapsed between form load and submission.
+ virtual void LogFormFillDurationFromLoadWithAutofill(
+ const base::TimeDelta& duration) const;
+
+ // This should be called when a fillable form that has not been Autofilled is
+ // submitted. |duration| should be the time elapsed between form load and
+ // submission.
+ virtual void LogFormFillDurationFromLoadWithoutAutofill(
+ const base::TimeDelta& duration) const;
+
+ // This should be called when a form that has been Autofilled is submitted.
+ // |duration| should be the time elapsed between the initial form interaction
+ // and submission.
+ virtual void LogFormFillDurationFromInteractionWithAutofill(
+ const base::TimeDelta& duration) const;
+
+ // This should be called when a fillable form that has not been Autofilled is
+ // submitted. |duration| should be the time elapsed between the initial form
+ // interaction and submission.
+ virtual void LogFormFillDurationFromInteractionWithoutAutofill(
+ const base::TimeDelta& duration) const;
+
+ // This should be called each time a page containing forms is loaded.
+ virtual void LogIsAutofillEnabledAtPageLoad(bool enabled) const;
+
+ // This should be called each time a new profile is launched.
+ virtual void LogIsAutofillEnabledAtStartup(bool enabled) const;
+
+ // This should be called each time a new profile is launched.
+ virtual void LogStoredProfileCount(size_t num_profiles) const;
+
+ // Log the number of Autofill suggestions presented to the user when filling a
+ // form.
+ virtual void LogAddressSuggestionsCount(size_t num_suggestions) const;
+
+ // Logs the experiment id corresponding to a server query response.
+ virtual void LogServerExperimentIdForQuery(
+ const std::string& experiment_id) const;
+
+ // Logs the experiment id corresponding to an upload to the server.
+ virtual void LogServerExperimentIdForUpload(
+ const std::string& experiment_id) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillMetrics);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_METRICS_H_
diff --git a/components/autofill/browser/autofill_metrics_unittest.cc b/components/autofill/browser/autofill_metrics_unittest.cc
new file mode 100644
index 0000000..4a9efac
--- /dev/null
+++ b/components/autofill/browser/autofill_metrics_unittest.cc
@@ -0,0 +1,1578 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/autofill_cc_infobar_delegate.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/ui/autofill/tab_autofill_manager_delegate.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/autofill_manager.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "content/public/test/test_browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+
+using content::BrowserThread;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Mock;
+using base::TimeTicks;
+using base::TimeDelta;
+
+namespace {
+
+class MockAutofillMetrics : public AutofillMetrics {
+ public:
+ MockAutofillMetrics() {}
+ MOCK_CONST_METHOD1(LogCreditCardInfoBarMetric, void(InfoBarMetric metric));
+ MOCK_CONST_METHOD1(LogDeveloperEngagementMetric,
+ void(DeveloperEngagementMetric metric));
+ MOCK_CONST_METHOD3(LogHeuristicTypePrediction,
+ void(FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id));
+ MOCK_CONST_METHOD3(LogOverallTypePrediction,
+ void(FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id));
+ MOCK_CONST_METHOD3(LogServerTypePrediction,
+ void(FieldTypeQualityMetric metric,
+ AutofillFieldType field_type,
+ const std::string& experiment_id));
+ MOCK_CONST_METHOD2(LogQualityMetric, void(QualityMetric metric,
+ const std::string& experiment_id));
+ MOCK_CONST_METHOD1(LogServerQueryMetric, void(ServerQueryMetric metric));
+ MOCK_CONST_METHOD1(LogUserHappinessMetric, void(UserHappinessMetric metric));
+ MOCK_CONST_METHOD1(LogFormFillDurationFromLoadWithAutofill,
+ void(const TimeDelta& duration));
+ MOCK_CONST_METHOD1(LogFormFillDurationFromLoadWithoutAutofill,
+ void(const TimeDelta& duration));
+ MOCK_CONST_METHOD1(LogFormFillDurationFromInteractionWithAutofill,
+ void(const TimeDelta& duration));
+ MOCK_CONST_METHOD1(LogFormFillDurationFromInteractionWithoutAutofill,
+ void(const TimeDelta& duration));
+ MOCK_CONST_METHOD1(LogIsAutofillEnabledAtPageLoad, void(bool enabled));
+ MOCK_CONST_METHOD1(LogIsAutofillEnabledAtStartup, void(bool enabled));
+ MOCK_CONST_METHOD1(LogStoredProfileCount, void(size_t num_profiles));
+ MOCK_CONST_METHOD1(LogAddressSuggestionsCount, void(size_t num_suggestions));
+ MOCK_CONST_METHOD1(LogServerExperimentIdForQuery,
+ void(const std::string& experiment_id));
+ MOCK_CONST_METHOD1(LogServerExperimentIdForUpload,
+ void(const std::string& experiment_id));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics);
+};
+
+class TestPersonalDataManager : public PersonalDataManager {
+ public:
+ TestPersonalDataManager() : autofill_enabled_(true) {
+ set_metric_logger(new MockAutofillMetrics);
+ CreateTestAutofillProfiles(&web_profiles_);
+ }
+
+ void SetBrowserContext(content::BrowserContext* context) {
+ set_browser_context(context);
+ }
+
+ // Overridden to avoid a trip to the database. This should be a no-op except
+ // for the side-effect of logging the profile count.
+ virtual void LoadProfiles() OVERRIDE {
+ std::vector<AutofillProfile*> profiles;
+ web_profiles_.release(&profiles);
+ WDResult<std::vector<AutofillProfile*> > result(AUTOFILL_PROFILES_RESULT,
+ profiles);
+ ReceiveLoadedProfiles(0, &result);
+ }
+
+ // Overridden to avoid a trip to the database.
+ virtual void LoadCreditCards() OVERRIDE {}
+
+ const MockAutofillMetrics* metric_logger() const {
+ return static_cast<const MockAutofillMetrics*>(
+ PersonalDataManager::metric_logger());
+ }
+
+ void set_autofill_enabled(bool autofill_enabled) {
+ autofill_enabled_ = autofill_enabled;
+ }
+
+ virtual bool IsAutofillEnabled() const OVERRIDE {
+ return autofill_enabled_;
+ }
+
+ MOCK_METHOD1(SaveImportedCreditCard,
+ void(const CreditCard& imported_credit_card));
+
+ private:
+ void CreateTestAutofillProfiles(ScopedVector<AutofillProfile>* profiles) {
+ AutofillProfile* profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "Elvis", "Aaron",
+ "Presley", "theking@gmail.com", "RCA",
+ "3734 Elvis Presley Blvd.", "Apt. 10",
+ "Memphis", "Tennessee", "38116", "US",
+ "12345678901");
+ profile->set_guid("00000000-0000-0000-0000-000000000001");
+ profiles->push_back(profile);
+ profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(profile, "Charles", "Hardin",
+ "Holley", "buddy@gmail.com", "Decca",
+ "123 Apple St.", "unit 6", "Lubbock",
+ "Texas", "79401", "US", "2345678901");
+ profile->set_guid("00000000-0000-0000-0000-000000000002");
+ profiles->push_back(profile);
+ }
+
+ bool autofill_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPersonalDataManager);
+};
+
+class TestFormStructure : public FormStructure {
+ public:
+ explicit TestFormStructure(const FormData& form)
+ : FormStructure(form, std::string()) {}
+ virtual ~TestFormStructure() {}
+
+ void SetFieldTypes(const std::vector<AutofillFieldType>& heuristic_types,
+ const std::vector<AutofillFieldType>& server_types) {
+ ASSERT_EQ(field_count(), heuristic_types.size());
+ ASSERT_EQ(field_count(), server_types.size());
+
+ for (size_t i = 0; i < field_count(); ++i) {
+ AutofillField* form_field = field(i);
+ ASSERT_TRUE(form_field);
+ form_field->set_heuristic_type(heuristic_types[i]);
+ form_field->set_server_type(server_types[i]);
+ }
+
+ UpdateAutofillCount();
+ }
+
+ virtual std::string server_experiment_id() const OVERRIDE {
+ return server_experiment_id_;
+ }
+ void set_server_experiment_id(const std::string& server_experiment_id) {
+ server_experiment_id_ = server_experiment_id;
+ }
+
+ private:
+ std::string server_experiment_id_;
+ DISALLOW_COPY_AND_ASSIGN(TestFormStructure);
+};
+
+class TestAutofillManager : public AutofillManager {
+ public:
+ TestAutofillManager(content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* manager_delegate,
+ TestPersonalDataManager* personal_manager)
+ : AutofillManager(web_contents, manager_delegate, personal_manager),
+ autofill_enabled_(true),
+ did_finish_async_form_submit_(false),
+ message_loop_is_running_(false) {
+ set_metric_logger(new MockAutofillMetrics);
+ }
+ virtual ~TestAutofillManager() {}
+
+ virtual bool IsAutofillEnabled() const OVERRIDE { return autofill_enabled_; }
+
+ void set_autofill_enabled(bool autofill_enabled) {
+ autofill_enabled_ = autofill_enabled;
+ }
+
+ MockAutofillMetrics* metric_logger() {
+ return static_cast<MockAutofillMetrics*>(const_cast<AutofillMetrics*>(
+ AutofillManager::metric_logger()));
+ }
+
+ void AddSeenForm(const FormData& form,
+ const std::vector<AutofillFieldType>& heuristic_types,
+ const std::vector<AutofillFieldType>& server_types,
+ const std::string& experiment_id) {
+ FormData empty_form = form;
+ for (size_t i = 0; i < empty_form.fields.size(); ++i) {
+ empty_form.fields[i].value = string16();
+ }
+
+ // |form_structure| will be owned by |form_structures()|.
+ TestFormStructure* form_structure = new TestFormStructure(empty_form);
+ form_structure->SetFieldTypes(heuristic_types, server_types);
+ form_structure->set_server_experiment_id(experiment_id);
+ form_structures()->push_back(form_structure);
+ }
+
+ void FormSubmitted(const FormData& form, const TimeTicks& timestamp) {
+ if (!OnFormSubmitted(form, timestamp))
+ return;
+
+ // Wait for the asynchronous FormSubmitted() call to complete.
+ if (!did_finish_async_form_submit_) {
+ // TODO(isherman): It seems silly to need this variable. Is there some
+ // way I can just query the message loop's state?
+ message_loop_is_running_ = true;
+ MessageLoop::current()->Run();
+ } else {
+ did_finish_async_form_submit_ = false;
+ }
+ }
+
+ virtual void UploadFormDataAsyncCallback(
+ const FormStructure* submitted_form,
+ const base::TimeTicks& load_time,
+ const base::TimeTicks& interaction_time,
+ const base::TimeTicks& submission_time) OVERRIDE {
+ if (message_loop_is_running_) {
+ MessageLoop::current()->Quit();
+ message_loop_is_running_ = false;
+ } else {
+ did_finish_async_form_submit_ = true;
+ }
+
+ AutofillManager::UploadFormDataAsyncCallback(submitted_form,
+ load_time,
+ interaction_time,
+ submission_time);
+ }
+
+ private:
+ bool autofill_enabled_;
+ bool did_finish_async_form_submit_;
+ bool message_loop_is_running_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillManager);
+};
+
+} // namespace
+
+class AutofillMetricsTest : public ChromeRenderViewHostTestHarness {
+ public:
+ AutofillMetricsTest();
+ virtual ~AutofillMetricsTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ protected:
+ scoped_ptr<ConfirmInfoBarDelegate> CreateDelegate(
+ MockAutofillMetrics* metric_logger);
+
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread file_thread_;
+ content::TestBrowserThread io_thread_;
+
+ scoped_ptr<TestAutofillManager> autofill_manager_;
+ TestPersonalDataManager personal_data_;
+
+ private:
+ std::string default_gmock_verbosity_level_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillMetricsTest);
+};
+
+AutofillMetricsTest::AutofillMetricsTest()
+ : ChromeRenderViewHostTestHarness(),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE),
+ io_thread_(BrowserThread::IO) {
+}
+
+AutofillMetricsTest::~AutofillMetricsTest() {
+ // Order of destruction is important as AutofillManager relies on
+ // PersonalDataManager to be around when it gets destroyed.
+ autofill_manager_.reset();
+}
+
+void AutofillMetricsTest::SetUp() {
+ TestingProfile* profile = new TestingProfile();
+ profile->CreateRequestContext();
+ browser_context_.reset(profile);
+ PersonalDataManagerFactory::GetInstance()->SetTestingFactory(profile, NULL);
+
+ ChromeRenderViewHostTestHarness::SetUp();
+ io_thread_.StartIOThread();
+ autofill::TabAutofillManagerDelegate::CreateForWebContents(web_contents());
+ personal_data_.SetBrowserContext(profile);
+ autofill_manager_.reset(new TestAutofillManager(
+ web_contents(),
+ autofill::TabAutofillManagerDelegate::FromWebContents(web_contents()),
+ &personal_data_));
+
+ file_thread_.Start();
+
+ // Ignore any metrics that we haven't explicitly set expectations for.
+ // If we don't override the verbosity level, we'll get lots of log spew from
+ // mocked functions that aren't relevant to a test but happen to be called
+ // during the test's execution.
+ // CAUTION: This is a global variable. So as to not affect other tests, this
+ // _must_ be restored to its original value at the end of the test.
+ default_gmock_verbosity_level_ = ::testing::FLAGS_gmock_verbose;
+ ::testing::FLAGS_gmock_verbose = "error";
+}
+
+void AutofillMetricsTest::TearDown() {
+ // Restore the global Gmock verbosity level to its default value.
+ ::testing::FLAGS_gmock_verbose = default_gmock_verbosity_level_;
+
+ // Order of destruction is important as AutofillManager relies on
+ // PersonalDataManager to be around when it gets destroyed. Also, a real
+ // AutofillManager is tied to the lifetime of the WebContents, so it must
+ // be destroyed at the destruction of the WebContents.
+ autofill_manager_.reset();
+ profile()->ResetRequestContext();
+ file_thread_.Stop();
+ ChromeRenderViewHostTestHarness::TearDown();
+ io_thread_.Stop();
+}
+
+scoped_ptr<ConfirmInfoBarDelegate> AutofillMetricsTest::CreateDelegate(
+ MockAutofillMetrics* metric_logger) {
+ EXPECT_CALL(*metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_SHOWN));
+
+ CreditCard credit_card;
+ return AutofillCCInfoBarDelegate::CreateForTesting(
+ metric_logger,
+ base::Bind(&TestPersonalDataManager::SaveImportedCreditCard,
+ base::Unretained(&personal_data_), credit_card));
+}
+
+// Test that we log quality metrics appropriately.
+TEST_F(AutofillMetricsTest, QualityMetrics) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ std::vector<AutofillFieldType> heuristic_types, server_types;
+ FormFieldData field;
+
+ autofill_test::CreateTestFormField(
+ "Autofilled", "autofilled", "Elvis Aaron Presley", "text", &field);
+ field.is_autofilled = true;
+ form.fields.push_back(field);
+ heuristic_types.push_back(NAME_FULL);
+ server_types.push_back(NAME_FIRST);
+
+ autofill_test::CreateTestFormField(
+ "Autofill Failed", "autofillfailed", "buddy@gmail.com", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(PHONE_HOME_NUMBER);
+ server_types.push_back(EMAIL_ADDRESS);
+
+ autofill_test::CreateTestFormField(
+ "Empty", "empty", "", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(NAME_FULL);
+ server_types.push_back(NAME_FIRST);
+
+ autofill_test::CreateTestFormField(
+ "Unknown", "unknown", "garbage", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(PHONE_HOME_NUMBER);
+ server_types.push_back(EMAIL_ADDRESS);
+
+ autofill_test::CreateTestFormField(
+ "Select", "select", "USA", "select-one", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(UNKNOWN_TYPE);
+ server_types.push_back(NO_SERVER_DATA);
+
+ autofill_test::CreateTestFormField(
+ "Phone", "phone", "2345678901", "tel", &field);
+ field.is_autofilled = true;
+ form.fields.push_back(field);
+ heuristic_types.push_back(PHONE_HOME_CITY_AND_NUMBER);
+ server_types.push_back(PHONE_HOME_WHOLE_NUMBER);
+
+ // Simulate having seen this form on page load.
+ autofill_manager_->AddSeenForm(form, heuristic_types, server_types,
+ std::string());
+
+ // Establish our expectations.
+ ::testing::InSequence dummy;
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerExperimentIdForUpload(std::string()));
+ // Autofilled field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
+ NAME_FULL, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ NAME_FULL, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ NAME_FULL, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED,
+ std::string()));
+ // Non-autofilled field for which we had data
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ EMAIL_ADDRESS, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MATCH,
+ EMAIL_ADDRESS, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
+ EMAIL_ADDRESS, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH,
+ std::string()));
+ // Empty field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ // Unknown field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ // <select> field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_COUNTRY, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_COUNTRY, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_COUNTRY, std::string()));
+ // Phone field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
+ PHONE_HOME_WHOLE_NUMBER, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MATCH,
+ PHONE_HOME_WHOLE_NUMBER, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
+ PHONE_HOME_WHOLE_NUMBER, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME));
+
+ // Simulate form submission.
+ EXPECT_NO_FATAL_FAILURE(autofill_manager_->FormSubmitted(form,
+ TimeTicks::Now()));
+}
+
+// Test that we log the appropriate additional metrics when Autofill failed.
+TEST_F(AutofillMetricsTest, QualityMetricsForFailure) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ struct {
+ const char* label;
+ const char* name;
+ const char* value;
+ AutofillFieldType heuristic_type;
+ AutofillFieldType server_type;
+ AutofillMetrics::QualityMetric heuristic_metric;
+ AutofillMetrics::QualityMetric server_metric;
+ } failure_cases[] = {
+ {
+ "Heuristics unknown, server unknown", "0,0", "Elvis",
+ UNKNOWN_TYPE, NO_SERVER_DATA,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_UNKNOWN,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_UNKNOWN
+ },
+ {
+ "Heuristics match, server unknown", "1,0", "Aaron",
+ NAME_MIDDLE, NO_SERVER_DATA,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MATCH,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_UNKNOWN
+ },
+ {
+ "Heuristics mismatch, server unknown", "2,0", "Presley",
+ PHONE_HOME_NUMBER, NO_SERVER_DATA,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_UNKNOWN
+ },
+ {
+ "Heuristics unknown, server match", "0,1", "theking@gmail.com",
+ UNKNOWN_TYPE, EMAIL_ADDRESS,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_UNKNOWN,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH
+ },
+ {
+ "Heuristics match, server match", "1,1", "3734 Elvis Presley Blvd.",
+ ADDRESS_HOME_LINE1, ADDRESS_HOME_LINE1,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MATCH,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH
+ },
+ {
+ "Heuristics mismatch, server match", "2,1", "Apt. 10",
+ PHONE_HOME_NUMBER, ADDRESS_HOME_LINE2,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH
+ },
+ {
+ "Heuristics unknown, server mismatch", "0,2", "Memphis",
+ UNKNOWN_TYPE, PHONE_HOME_NUMBER,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_UNKNOWN,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH
+ },
+ {
+ "Heuristics match, server mismatch", "1,2", "Tennessee",
+ ADDRESS_HOME_STATE, PHONE_HOME_NUMBER,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MATCH,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH
+ },
+ {
+ "Heuristics mismatch, server mismatch", "2,2", "38116",
+ PHONE_HOME_NUMBER, PHONE_HOME_NUMBER,
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH
+ }
+ };
+
+ std::vector<AutofillFieldType> heuristic_types, server_types;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(failure_cases); ++i) {
+ FormFieldData field;
+ autofill_test::CreateTestFormField(failure_cases[i].label,
+ failure_cases[i].name,
+ failure_cases[i].value, "text", &field);
+ form.fields.push_back(field);
+ heuristic_types.push_back(failure_cases[i].heuristic_type);
+ server_types.push_back(failure_cases[i].server_type);
+
+ }
+
+ // Simulate having seen this form with the desired heuristic and server types.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, heuristic_types, server_types,
+ std::string());
+
+
+ // Establish our expectations.
+ ::testing::InSequence dummy;
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerExperimentIdForUpload(std::string()));
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(failure_cases); ++i) {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(failure_cases[i].heuristic_metric,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(failure_cases[i].server_metric,
+ std::string()));
+ }
+
+ // Simulate form submission.
+ EXPECT_NO_FATAL_FAILURE(autofill_manager_->FormSubmitted(form,
+ TimeTicks::Now()));
+}
+
+// Test that we behave sanely when the cached form differs from the submitted
+// one.
+TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ std::vector<AutofillFieldType> heuristic_types, server_types;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Both match", "match", "Elvis Aaron Presley", "text", &field);
+ field.is_autofilled = true;
+ form.fields.push_back(field);
+ heuristic_types.push_back(NAME_FULL);
+ server_types.push_back(NAME_FULL);
+ autofill_test::CreateTestFormField(
+ "Both mismatch", "mismatch", "buddy@gmail.com", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(PHONE_HOME_NUMBER);
+ server_types.push_back(PHONE_HOME_NUMBER);
+ autofill_test::CreateTestFormField(
+ "Only heuristics match", "mixed", "Memphis", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(ADDRESS_HOME_CITY);
+ server_types.push_back(PHONE_HOME_NUMBER);
+ autofill_test::CreateTestFormField(
+ "Unknown", "unknown", "garbage", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(UNKNOWN_TYPE);
+ server_types.push_back(UNKNOWN_TYPE);
+
+ // Simulate having seen this form with the desired heuristic and server types.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, heuristic_types, server_types,
+ std::string());
+
+
+ // Add a field and re-arrange the remaining form fields before submitting.
+ std::vector<FormFieldData> cached_fields = form.fields;
+ form.fields.clear();
+ autofill_test::CreateTestFormField(
+ "New field", "new field", "Tennessee", "text", &field);
+ form.fields.push_back(field);
+ form.fields.push_back(cached_fields[2]);
+ form.fields.push_back(cached_fields[1]);
+ form.fields.push_back(cached_fields[3]);
+ form.fields.push_back(cached_fields[0]);
+
+ // Establish our expectations.
+ ::testing::InSequence dummy;
+ // New field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerExperimentIdForUpload(std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_STATE, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_STATE, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_STATE, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_UNKNOWN,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_UNKNOWN,
+ std::string()));
+ // Only heuristics match
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
+ ADDRESS_HOME_CITY, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ ADDRESS_HOME_CITY, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ ADDRESS_HOME_CITY, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MATCH,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH,
+ std::string()));
+ // Both mismatch
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ EMAIL_ADDRESS, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ EMAIL_ADDRESS, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ EMAIL_ADDRESS, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH,
+ std::string()));
+ // Unknown
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ // Both match
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
+ NAME_FULL, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MATCH,
+ NAME_FULL, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
+ NAME_FULL, std::string()));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED,
+ std::string()));
+
+ // Simulate form submission.
+ EXPECT_NO_FATAL_FAILURE(autofill_manager_->FormSubmitted(form,
+ TimeTicks::Now()));
+}
+
+// Verify that we correctly log metrics regarding developer engagement.
+TEST_F(AutofillMetricsTest, DeveloperEngagement) {
+ // Start with a non-fillable form.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_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.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogDeveloperEngagementMetric(_)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+
+ // Add another field to the form, so that it becomes fillable.
+ autofill_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_CALL(
+ *autofill_manager_->metric_logger(),
+ LogDeveloperEngagementMetric(
+ AutofillMetrics::FILLABLE_FORM_PARSED)).Times(1);
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogDeveloperEngagementMetric(
+ AutofillMetrics::FILLABLE_FORM_CONTAINS_TYPE_HINTS)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+
+ // 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.
+ autofill_test::CreateTestFormField("", "", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ forms.back().fields.push_back(field);
+ autofill_test::CreateTestFormField("", "", "", "text", &field);
+ field.autocomplete_attribute = "email";
+ forms.back().fields.push_back(field);
+ autofill_test::CreateTestFormField("", "", "", "text", &field);
+ field.autocomplete_attribute = "street-address";
+ forms.back().fields.push_back(field);
+
+ // Expect both the "form parsed" metric and the author-specified field type
+ // hints metric to be logged.
+ {
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogDeveloperEngagementMetric(
+ AutofillMetrics::FILLABLE_FORM_PARSED)).Times(1);
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogDeveloperEngagementMetric(
+ AutofillMetrics::FILLABLE_FORM_CONTAINS_TYPE_HINTS)).Times(1);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+}
+
+// Test that we don't log quality metrics for non-autofillable forms.
+TEST_F(AutofillMetricsTest, NoQualityMetricsForNonAutofillableForms) {
+ // Forms must include at least three fields to be auto-fillable.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Autofilled", "autofilled", "Elvis Presley", "text", &field);
+ field.is_autofilled = true;
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Autofill Failed", "autofillfailed", "buddy@gmail.com", "text", &field);
+ form.fields.push_back(field);
+
+ // Simulate form submission.
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string())).Times(0);
+ EXPECT_NO_FATAL_FAILURE(autofill_manager_->FormSubmitted(form,
+ TimeTicks::Now()));
+
+ // Search forms are not auto-fillable.
+ form.action = GURL("http://example.com/search?q=Elvis%20Presley");
+ autofill_test::CreateTestFormField(
+ "Empty", "empty", "", "text", &field);
+ form.fields.push_back(field);
+
+ // Simulate form submission.
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ std::string())).Times(0);
+ EXPECT_NO_FATAL_FAILURE(autofill_manager_->FormSubmitted(form,
+ TimeTicks::Now()));
+}
+
+// Test that we recored the experiment id appropriately.
+TEST_F(AutofillMetricsTest, QualityMetricsWithExperimentId) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ std::vector<AutofillFieldType> heuristic_types, server_types;
+ FormFieldData field;
+
+ autofill_test::CreateTestFormField(
+ "Autofilled", "autofilled", "Elvis Aaron Presley", "text", &field);
+ field.is_autofilled = true;
+ form.fields.push_back(field);
+ heuristic_types.push_back(NAME_FULL);
+ server_types.push_back(NAME_FIRST);
+
+ autofill_test::CreateTestFormField(
+ "Autofill Failed", "autofillfailed", "buddy@gmail.com", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(PHONE_HOME_NUMBER);
+ server_types.push_back(EMAIL_ADDRESS);
+
+ autofill_test::CreateTestFormField(
+ "Empty", "empty", "", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(NAME_FULL);
+ server_types.push_back(NAME_FIRST);
+
+ autofill_test::CreateTestFormField(
+ "Unknown", "unknown", "garbage", "text", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(PHONE_HOME_NUMBER);
+ server_types.push_back(EMAIL_ADDRESS);
+
+ autofill_test::CreateTestFormField(
+ "Select", "select", "USA", "select-one", &field);
+ field.is_autofilled = false;
+ form.fields.push_back(field);
+ heuristic_types.push_back(UNKNOWN_TYPE);
+ server_types.push_back(NO_SERVER_DATA);
+
+ const std::string experiment_id = "ThatOughtaDoIt";
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, heuristic_types, server_types,
+ experiment_id);
+
+ // Establish our expectations.
+ ::testing::InSequence dummy;
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerExperimentIdForUpload(experiment_id));
+ // Autofilled field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
+ NAME_FULL, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ NAME_FULL, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ NAME_FULL, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED,
+ experiment_id));
+ // Non-autofilled field for which we had data
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ EMAIL_ADDRESS, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_MATCH,
+ EMAIL_ADDRESS, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
+ EMAIL_ADDRESS, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH,
+ experiment_id));
+ // Empty field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ experiment_id));
+ // Unknown field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ experiment_id));
+ // <select> field
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_COUNTRY, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogServerTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_COUNTRY, experiment_id));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogOverallTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ ADDRESS_HOME_COUNTRY, experiment_id));
+
+ // Simulate form submission.
+ EXPECT_NO_FATAL_FAILURE(autofill_manager_->FormSubmitted(form,
+ TimeTicks::Now()));
+}
+
+// Test that the profile count is logged correctly.
+TEST_F(AutofillMetricsTest, StoredProfileCount) {
+ // The metric should be logged when the profiles are first loaded.
+ EXPECT_CALL(*personal_data_.metric_logger(),
+ LogStoredProfileCount(2)).Times(1);
+ personal_data_.LoadProfiles();
+
+ // The metric should only be logged once.
+ EXPECT_CALL(*personal_data_.metric_logger(),
+ LogStoredProfileCount(::testing::_)).Times(0);
+ personal_data_.LoadProfiles();
+}
+
+// Test that we correctly log when Autofill is enabled.
+TEST_F(AutofillMetricsTest, AutofillIsEnabledAtStartup) {
+ personal_data_.set_autofill_enabled(true);
+ EXPECT_CALL(*personal_data_.metric_logger(),
+ LogIsAutofillEnabledAtStartup(true)).Times(1);
+ personal_data_.Init(profile());
+}
+
+// Test that we correctly log when Autofill is disabled.
+TEST_F(AutofillMetricsTest, AutofillIsDisabledAtStartup) {
+ personal_data_.set_autofill_enabled(false);
+ EXPECT_CALL(*personal_data_.metric_logger(),
+ LogIsAutofillEnabledAtStartup(false)).Times(1);
+ personal_data_.Init(profile());
+}
+
+// Test that we log the number of Autofill suggestions when filling a form.
+TEST_F(AutofillMetricsTest, AddressSuggestionsCount) {
+ // Set up our form data.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+ std::vector<AutofillFieldType> field_types;
+ autofill_test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(NAME_FULL);
+ autofill_test::CreateTestFormField("Email", "email", "", "email", &field);
+ form.fields.push_back(field);
+ field_types.push_back(EMAIL_ADDRESS);
+ autofill_test::CreateTestFormField("Phone", "phone", "", "tel", &field);
+ form.fields.push_back(field);
+ field_types.push_back(PHONE_HOME_NUMBER);
+
+ // Simulate having seen this form on page load.
+ // |form_structure| will be owned by |autofill_manager_|.
+ autofill_manager_->AddSeenForm(form, field_types, field_types,
+ std::string());
+
+ // Establish our expectations.
+ ::testing::InSequence dummy;
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogAddressSuggestionsCount(2)).Times(1);
+
+ // Simulate activating the autofill popup for the phone field.
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::Rect(), false);
+
+ // Simulate activating the autofill popup for the email field after typing.
+ // No new metric should be logged, since we're still on the same page.
+ autofill_test::CreateTestFormField("Email", "email", "b", "email", &field);
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::Rect(), false);
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types,
+ std::string());
+
+ // Establish our expectations.
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogAddressSuggestionsCount(1)).Times(1);
+
+ // Simulate activating the autofill popup for the email field after typing.
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::Rect(), false);
+
+ // Reset the autofill manager state again.
+ autofill_manager_->Reset();
+ autofill_manager_->AddSeenForm(form, field_types, field_types,
+ std::string());
+
+ // Establish our expectations.
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogAddressSuggestionsCount(::testing::_)).Times(0);
+
+ // Simulate activating the autofill popup for the email field after typing.
+ form.fields[0].is_autofilled = true;
+ autofill_manager_->OnQueryFormFieldAutofill(
+ 0, form, field, gfx::Rect(), false);
+}
+
+// Test that we log whether Autofill is enabled when filling a form.
+TEST_F(AutofillMetricsTest, AutofillIsEnabledAtPageLoad) {
+ // Establish our expectations.
+ ::testing::InSequence dummy;
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogIsAutofillEnabledAtPageLoad(true)).Times(1);
+
+ autofill_manager_->set_autofill_enabled(true);
+ autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks());
+
+ // Reset the autofill manager state.
+ autofill_manager_->Reset();
+
+ // Establish our expectations.
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogIsAutofillEnabledAtPageLoad(false)).Times(1);
+
+ autofill_manager_->set_autofill_enabled(false);
+ autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks());
+}
+
+// Test that credit card infobar metrics are logged correctly.
+TEST_F(AutofillMetricsTest, CreditCardInfoBar) {
+ MockAutofillMetrics metric_logger;
+ ::testing::InSequence dummy;
+
+ // Accept the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(CreateDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(personal_data_, SaveImportedCreditCard(_));
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_ACCEPTED)).Times(1);
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_IGNORED)).Times(0);
+ EXPECT_TRUE(infobar->Accept());
+ }
+
+ // Cancel the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(CreateDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_DENIED)).Times(1);
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_IGNORED)).Times(0);
+ EXPECT_TRUE(infobar->Cancel());
+ }
+
+ // Dismiss the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(CreateDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_DENIED)).Times(1);
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_IGNORED)).Times(0);
+ infobar->InfoBarDismissed();
+ }
+
+ // Ignore the infobar.
+ {
+ scoped_ptr<ConfirmInfoBarDelegate> infobar(CreateDelegate(&metric_logger));
+ ASSERT_TRUE(infobar);
+ EXPECT_CALL(metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_IGNORED)).Times(1);
+ }
+}
+
+// Test that server query response experiment id metrics are logged correctly.
+TEST_F(AutofillMetricsTest, ServerQueryExperimentIdForQuery) {
+ MockAutofillMetrics metric_logger;
+ ::testing::InSequence dummy;
+
+ // No experiment specified.
+ EXPECT_CALL(metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_RECEIVED));
+ EXPECT_CALL(metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED));
+ EXPECT_CALL(metric_logger,
+ LogServerExperimentIdForQuery(std::string()));
+ EXPECT_CALL(metric_logger,
+ LogServerQueryMetric(
+ AutofillMetrics::QUERY_RESPONSE_MATCHED_LOCAL_HEURISTICS));
+ autofill::AutocheckoutPageMetaData page_meta_data;
+ FormStructure::ParseQueryResponse(
+ "<autofillqueryresponse></autofillqueryresponse>",
+ std::vector<FormStructure*>(),
+ &page_meta_data,
+ metric_logger);
+
+ // Experiment "ar1" specified.
+ EXPECT_CALL(metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_RECEIVED));
+ EXPECT_CALL(metric_logger,
+ LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED));
+ EXPECT_CALL(metric_logger,
+ LogServerExperimentIdForQuery("ar1"));
+ EXPECT_CALL(metric_logger,
+ LogServerQueryMetric(
+ AutofillMetrics::QUERY_RESPONSE_MATCHED_LOCAL_HEURISTICS));
+ FormStructure::ParseQueryResponse(
+ "<autofillqueryresponse experimentid=\"ar1\"></autofillqueryresponse>",
+ std::vector<FormStructure*>(),
+ &page_meta_data,
+ metric_logger);
+}
+
+// Verify that we correctly log user happiness metrics dealing with form loading
+// and form submission.
+TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) {
+ // Start with a form with insufficiently many fields.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+
+ // Expect no notifications when the form is first seen.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ }
+
+
+ // Expect no notifications when the form is submitted.
+ {
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL)).Times(0);
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME)).Times(0);
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE)).Times(0);
+ EXPECT_CALL(
+ *autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM)).Times(0);
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+
+ // Add more fields to the form.
+ autofill_test::CreateTestFormField("Phone", "phone", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Unknown", "unknown", "", "text", &field);
+ form.fields.push_back(field);
+ forms.front() = form;
+
+ // Expect a notification when the form is first seen.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED));
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ }
+
+ // Expect a notification when the form is submitted.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM));
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+
+ // Fill in two of the fields.
+ form.fields[0].value = ASCIIToUTF16("Elvis Aaron Presley");
+ form.fields[1].value = ASCIIToUTF16("theking@gmail.com");
+ forms.front() = form;
+
+ // Expect a notification when the form is submitted.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM));
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+
+ // Fill in the third field.
+ form.fields[2].value = ASCIIToUTF16("12345678901");
+ forms.front() = form;
+
+ // Expect notifications when the form is submitted.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE));
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+
+
+ // Mark one of the fields as autofilled.
+ form.fields[1].is_autofilled = true;
+ forms.front() = form;
+
+ // Expect notifications when the form is submitted.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME));
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+
+ // Mark all of the fillable fields as autofilled.
+ form.fields[0].is_autofilled = true;
+ form.fields[2].is_autofilled = true;
+ forms.front() = form;
+
+ // Expect notifications when the form is submitted.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL));
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+
+ // Clear out the third field's value.
+ form.fields[2].value = string16();
+ forms.front() = form;
+
+ // Expect notifications when the form is submitted.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM));
+ autofill_manager_->FormSubmitted(form, TimeTicks::Now());
+ }
+}
+
+// Verify that we correctly log user happiness metrics dealing with form
+// interaction.
+TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) {
+ // Load a fillable form.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Phone", "phone", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+
+ // Expect a notification when the form is first seen.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED));
+ autofill_manager_->OnFormsSeen(forms, TimeTicks());
+ }
+
+ // Simulate typing.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE));
+ autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ TimeTicks());
+ }
+
+ // Simulate suggestions shown twice for a single edit (i.e. multiple
+ // keystrokes in a single field).
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUGGESTIONS_SHOWN)).Times(1);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUGGESTIONS_SHOWN_ONCE)).Times(1);
+ autofill_manager_->OnDidShowAutofillSuggestions(true);
+ autofill_manager_->OnDidShowAutofillSuggestions(false);
+ }
+
+ // Simulate suggestions shown for a different field.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::SUGGESTIONS_SHOWN));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::SUGGESTIONS_SHOWN_ONCE)).Times(0);
+ autofill_manager_->OnDidShowAutofillSuggestions(true);
+ }
+
+ // Simulate invoking autofill.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL_ONCE));
+ autofill_manager_->OnDidFillAutofillFormData(TimeTicks());
+ }
+
+ // Simulate editing an autofilled field.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD_ONCE));
+ PersonalDataManager::GUIDPair guid(
+ "00000000-0000-0000-0000-000000000001", 0);
+ PersonalDataManager::GUIDPair empty(std::string(), 0);
+ autofill_manager_->OnFillAutofillFormData(
+ 0, form, form.fields.front(),
+ autofill_manager_->PackGUIDs(empty, guid));
+ autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ TimeTicks());
+ // Simulate a second keystroke; make sure we don't log the metric twice.
+ autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ TimeTicks());
+ }
+
+ // Simulate invoking autofill again.
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(AutofillMetrics::USER_DID_AUTOFILL));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_AUTOFILL_ONCE)).Times(0);
+ autofill_manager_->OnDidFillAutofillFormData(TimeTicks());
+
+ // Simulate editing another autofilled field.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogUserHappinessMetric(
+ AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD));
+ autofill_manager_->OnTextFieldDidChange(form, form.fields[1], TimeTicks());
+ }
+}
+
+// Verify that we correctly log metrics tracking the duration of form fill.
+TEST_F(AutofillMetricsTest, FormFillDuration) {
+ // Load a fillable form.
+ FormData form;
+ form.name = ASCIIToUTF16("TestForm");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://example.com/form.html");
+ form.action = GURL("http://example.com/submit.html");
+ form.user_submitted = true;
+
+ FormFieldData field;
+ autofill_test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField("Phone", "phone", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+
+ // Fill the field values for form submission.
+ form.fields[0].value = ASCIIToUTF16("Elvis Aaron Presley");
+ form.fields[1].value = ASCIIToUTF16("theking@gmail.com");
+ form.fields[2].value = ASCIIToUTF16("12345678901");
+
+ // Expect only form load metrics to be logged if the form is submitted without
+ // user interaction.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithAutofill(_)).Times(0);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithoutAutofill(
+ TimeDelta::FromInternalValue(16)));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithAutofill(_)).Times(0);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithoutAutofill(_)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
+ autofill_manager_->FormSubmitted(form, TimeTicks::FromInternalValue(17));
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+
+ // Expect metric to be logged if the user manually edited a form field.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithAutofill(_)).Times(0);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithoutAutofill(
+ TimeDelta::FromInternalValue(16)));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithAutofill(_)).Times(0);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithoutAutofill(
+ TimeDelta::FromInternalValue(14)));
+ autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
+ autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ TimeTicks::FromInternalValue(3));
+ autofill_manager_->FormSubmitted(form, TimeTicks::FromInternalValue(17));
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+
+ // Expect metric to be logged if the user autofilled the form.
+ form.fields[0].is_autofilled = true;
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithAutofill(
+ TimeDelta::FromInternalValue(16)));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithoutAutofill(_)).Times(0);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithAutofill(
+ TimeDelta::FromInternalValue(12)));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithoutAutofill(_)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
+ autofill_manager_->OnDidFillAutofillFormData(
+ TimeTicks::FromInternalValue(5));
+ autofill_manager_->FormSubmitted(form, TimeTicks::FromInternalValue(17));
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+
+ // Expect metric to be logged if the user both manually filled some fields
+ // and autofilled others. Messages can arrive out of order, so make sure they
+ // take precedence appropriately.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithAutofill(
+ TimeDelta::FromInternalValue(16)));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromLoadWithoutAutofill(_)).Times(0);
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithAutofill(
+ TimeDelta::FromInternalValue(14)));
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogFormFillDurationFromInteractionWithoutAutofill(_)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1));
+ autofill_manager_->OnDidFillAutofillFormData(
+ TimeTicks::FromInternalValue(5));
+ autofill_manager_->OnTextFieldDidChange(form, form.fields.front(),
+ TimeTicks::FromInternalValue(3));
+ autofill_manager_->FormSubmitted(form, TimeTicks::FromInternalValue(17));
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+}
diff --git a/components/autofill/browser/autofill_popup_delegate.h b/components/autofill/browser/autofill_popup_delegate.h
new file mode 100644
index 0000000..ad1c990
--- /dev/null
+++ b/components/autofill/browser/autofill_popup_delegate.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
+
+#include "base/string16.h"
+
+namespace content {
+class KeyboardListener;
+}
+
+// An interface for interaction with AutofillPopupController. Will be notified
+// of events by the controller.
+class AutofillPopupDelegate {
+ public:
+ // Called when the Autofill popup is shown. |listener| may be used to pass
+ // keyboard events to the popup.
+ virtual void OnPopupShown(content::KeyboardListener* listener) = 0;
+
+ // Called when the Autofill popup is hidden. |listener| must be unregistered
+ // if it was registered in OnPopupShown.
+ virtual void OnPopupHidden(content::KeyboardListener* listener) = 0;
+
+ // Called when the autofill suggestion indicated by |identifier| has been
+ // temporarily selected (e.g., hovered).
+ virtual void DidSelectSuggestion(int identifier) = 0;
+
+ // Inform the delegate that a row in the popup has been chosen.
+ virtual void DidAcceptSuggestion(const string16& value, int identifier) = 0;
+
+ // Delete the described suggestion.
+ virtual void RemoveSuggestion(const string16& value, int identifier) = 0;
+
+ // Informs the delegate that the Autofill previewed form should be cleared.
+ virtual void ClearPreviewedForm() = 0;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
diff --git a/components/autofill/browser/autofill_profile.cc b/components/autofill/browser/autofill_profile.cc
new file mode 100644
index 0000000..f793fa4
--- /dev/null
+++ b/components/autofill/browser/autofill_profile.cc
@@ -0,0 +1,848 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_profile.h"
+
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <ostream>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/address.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/contact_info.h"
+#include "components/autofill/browser/phone_number.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+#include "components/autofill/common/form_field_data.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// Like |AutofillType::GetEquivalentFieldType()|, but also returns |NAME_FULL|
+// for first, middle, and last name field types.
+AutofillFieldType GetEquivalentFieldTypeCollapsingNames(
+ AutofillFieldType field_type) {
+ if (field_type == NAME_FIRST || field_type == NAME_MIDDLE ||
+ field_type == NAME_LAST || field_type == NAME_MIDDLE_INITIAL)
+ return NAME_FULL;
+
+ return AutofillType::GetEquivalentFieldType(field_type);
+}
+
+// Fills |distinguishing_fields| with a list of fields to use when creating
+// labels that can help to distinguish between two profiles. Draws fields from
+// |suggested_fields| if it is non-NULL; otherwise returns a default list.
+// If |suggested_fields| is non-NULL, does not include |excluded_field| in the
+// list. Otherwise, |excluded_field| is ignored, and should be set to
+// |UNKNOWN_TYPE| by convention. The resulting list of fields is sorted in
+// decreasing order of importance.
+void GetFieldsForDistinguishingProfiles(
+ const std::vector<AutofillFieldType>* suggested_fields,
+ AutofillFieldType excluded_field,
+ std::vector<AutofillFieldType>* distinguishing_fields) {
+ static const AutofillFieldType kDefaultDistinguishingFields[] = {
+ NAME_FULL,
+ ADDRESS_HOME_LINE1,
+ ADDRESS_HOME_LINE2,
+ ADDRESS_HOME_CITY,
+ ADDRESS_HOME_STATE,
+ ADDRESS_HOME_ZIP,
+ ADDRESS_HOME_COUNTRY,
+ EMAIL_ADDRESS,
+ PHONE_HOME_WHOLE_NUMBER,
+ COMPANY_NAME,
+ };
+
+ if (!suggested_fields) {
+ DCHECK_EQ(excluded_field, UNKNOWN_TYPE);
+ distinguishing_fields->assign(
+ kDefaultDistinguishingFields,
+ kDefaultDistinguishingFields + arraysize(kDefaultDistinguishingFields));
+ return;
+ }
+
+ // Keep track of which fields we've seen so that we avoid duplicate entries.
+ // Always ignore fields of unknown type and the excluded field.
+ std::set<AutofillFieldType> seen_fields;
+ seen_fields.insert(UNKNOWN_TYPE);
+ seen_fields.insert(GetEquivalentFieldTypeCollapsingNames(excluded_field));
+
+ distinguishing_fields->clear();
+ for (std::vector<AutofillFieldType>::const_iterator it =
+ suggested_fields->begin();
+ it != suggested_fields->end(); ++it) {
+ AutofillFieldType suggested_type =
+ GetEquivalentFieldTypeCollapsingNames(*it);
+ if (seen_fields.insert(suggested_type).second)
+ distinguishing_fields->push_back(suggested_type);
+ }
+
+ // Special case: If the excluded field is a partial name (e.g. first name) and
+ // the suggested fields include other name fields, include |NAME_FULL| in the
+ // list of distinguishing fields as a last-ditch fallback. This allows us to
+ // distinguish between profiles that are identical except for the name.
+ if (excluded_field != NAME_FULL &&
+ GetEquivalentFieldTypeCollapsingNames(excluded_field) == NAME_FULL) {
+ for (std::vector<AutofillFieldType>::const_iterator it =
+ suggested_fields->begin();
+ it != suggested_fields->end(); ++it) {
+ if (*it != excluded_field &&
+ GetEquivalentFieldTypeCollapsingNames(*it) == NAME_FULL) {
+ distinguishing_fields->push_back(NAME_FULL);
+ break;
+ }
+ }
+ }
+}
+
+// A helper function for string streaming. Concatenates multi-valued entries
+// stored for a given |type| into a single string. This string is returned.
+const string16 MultiString(const AutofillProfile& p, AutofillFieldType type) {
+ std::vector<string16> values;
+ p.GetRawMultiInfo(type, &values);
+ string16 accumulate;
+ for (size_t i = 0; i < values.size(); ++i) {
+ if (i > 0)
+ accumulate += ASCIIToUTF16(" ");
+ accumulate += values[i];
+ }
+ return accumulate;
+}
+
+string16 GetFormGroupInfo(const FormGroup& form_group,
+ AutofillFieldType type,
+ const std::string& app_locale) {
+ return app_locale.empty() ?
+ form_group.GetRawInfo(type) :
+ form_group.GetInfo(type, app_locale);
+}
+
+template <class T>
+void CopyValuesToItems(AutofillFieldType type,
+ const std::vector<string16>& values,
+ std::vector<T>* form_group_items,
+ const T& prototype) {
+ form_group_items->resize(values.size(), prototype);
+ for (size_t i = 0; i < form_group_items->size(); ++i) {
+ (*form_group_items)[i].SetRawInfo(type,
+ CollapseWhitespace(values[i], false));
+ }
+ // Must have at least one (possibly empty) element.
+ if (form_group_items->empty())
+ form_group_items->resize(1, prototype);
+}
+
+template <class T>
+void CopyItemsToValues(AutofillFieldType type,
+ const std::vector<T>& form_group_items,
+ const std::string& app_locale,
+ std::vector<string16>* values) {
+ values->resize(form_group_items.size());
+ for (size_t i = 0; i < values->size(); ++i) {
+ (*values)[i] = GetFormGroupInfo(form_group_items[i], type, app_locale);
+ }
+}
+
+// Collapse compound field types to their "full" type. I.e. First name
+// collapses to full name, area code collapses to full phone, etc.
+void CollapseCompoundFieldTypes(FieldTypeSet* type_set) {
+ FieldTypeSet collapsed_set;
+ for (FieldTypeSet::iterator iter = type_set->begin(); iter != type_set->end();
+ ++iter) {
+ switch (*iter) {
+ case NAME_FIRST:
+ case NAME_MIDDLE:
+ case NAME_LAST:
+ case NAME_MIDDLE_INITIAL:
+ case NAME_FULL:
+ case NAME_SUFFIX:
+ collapsed_set.insert(NAME_FULL);
+ break;
+
+ case PHONE_HOME_NUMBER:
+ case PHONE_HOME_CITY_CODE:
+ case PHONE_HOME_COUNTRY_CODE:
+ case PHONE_HOME_CITY_AND_NUMBER:
+ case PHONE_HOME_WHOLE_NUMBER:
+ collapsed_set.insert(PHONE_HOME_WHOLE_NUMBER);
+ break;
+
+ default:
+ collapsed_set.insert(*iter);
+ }
+ }
+ std::swap(*type_set, collapsed_set);
+}
+
+class FindByPhone {
+ public:
+ FindByPhone(const string16& phone, const std::string& country_code)
+ : phone_(phone),
+ country_code_(country_code) {
+ }
+
+ bool operator()(const string16& phone) {
+ return autofill_i18n::PhoneNumbersMatch(phone, phone_, country_code_);
+ }
+
+ bool operator()(const string16* phone) {
+ return autofill_i18n::PhoneNumbersMatch(*phone, phone_, country_code_);
+ }
+
+ private:
+ string16 phone_;
+ std::string country_code_;
+};
+
+// Functor used to check for case-insensitive equality of two strings.
+struct CaseInsensitiveStringEquals
+ : public std::binary_function<string16, string16, bool>
+{
+ bool operator()(const string16& x, const string16& y) const {
+ return
+ x.size() == y.size() && StringToLowerASCII(x) == StringToLowerASCII(y);
+ }
+};
+
+} // namespace
+
+AutofillProfile::AutofillProfile(const std::string& guid)
+ : guid_(guid),
+ name_(1),
+ email_(1),
+ home_number_(1, PhoneNumber(this)) {
+}
+
+AutofillProfile::AutofillProfile()
+ : guid_(base::GenerateGUID()),
+ name_(1),
+ email_(1),
+ home_number_(1, PhoneNumber(this)) {
+}
+
+AutofillProfile::AutofillProfile(const AutofillProfile& profile)
+ : FormGroup() {
+ operator=(profile);
+}
+
+AutofillProfile::~AutofillProfile() {
+}
+
+AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) {
+ if (this == &profile)
+ return *this;
+
+ label_ = profile.label_;
+ guid_ = profile.guid_;
+
+ name_ = profile.name_;
+ email_ = profile.email_;
+ company_ = profile.company_;
+ home_number_ = profile.home_number_;
+
+ for (size_t i = 0; i < home_number_.size(); ++i)
+ home_number_[i].set_profile(this);
+
+ address_ = profile.address_;
+
+ return *this;
+}
+
+std::string AutofillProfile::GetGUID() const {
+ return guid();
+}
+
+void AutofillProfile::GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const {
+ FormGroupList info = FormGroups();
+ for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it)
+ (*it)->GetMatchingTypes(text, app_locale, matching_types);
+}
+
+string16 AutofillProfile::GetRawInfo(AutofillFieldType type) const {
+ AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type);
+ const FormGroup* form_group = FormGroupForType(return_type);
+ if (!form_group)
+ return string16();
+
+ return form_group->GetRawInfo(return_type);
+}
+
+void AutofillProfile::SetRawInfo(AutofillFieldType type,
+ const string16& value) {
+ FormGroup* form_group = MutableFormGroupForType(type);
+ if (form_group)
+ form_group->SetRawInfo(type, CollapseWhitespace(value, false));
+}
+
+string16 AutofillProfile::GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const {
+ AutofillFieldType return_type = AutofillType::GetEquivalentFieldType(type);
+ const FormGroup* form_group = FormGroupForType(return_type);
+ if (!form_group)
+ return string16();
+
+ return form_group->GetInfo(return_type, app_locale);
+}
+
+bool AutofillProfile::SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) {
+ FormGroup* form_group = MutableFormGroupForType(type);
+ if (!form_group)
+ return false;
+
+ return
+ form_group->SetInfo(type, CollapseWhitespace(value, false), app_locale);
+}
+
+void AutofillProfile::SetRawMultiInfo(AutofillFieldType type,
+ const std::vector<string16>& values) {
+ switch (AutofillType(type).group()) {
+ case AutofillType::NAME:
+ CopyValuesToItems(type, values, &name_, NameInfo());
+ break;
+ case AutofillType::EMAIL:
+ CopyValuesToItems(type, values, &email_, EmailInfo());
+ break;
+ case AutofillType::PHONE:
+ CopyValuesToItems(type,
+ values,
+ &home_number_,
+ PhoneNumber(this));
+ break;
+ default:
+ if (values.size() == 1) {
+ SetRawInfo(type, values[0]);
+ } else if (values.size() == 0) {
+ SetRawInfo(type, string16());
+ } else {
+ // Shouldn't attempt to set multiple values on single-valued field.
+ NOTREACHED();
+ }
+ break;
+ }
+}
+
+void AutofillProfile::GetRawMultiInfo(AutofillFieldType type,
+ std::vector<string16>* values) const {
+ GetMultiInfoImpl(type, std::string(), values);
+}
+
+void AutofillProfile::GetMultiInfo(AutofillFieldType type,
+ const std::string& app_locale,
+ std::vector<string16>* values) const {
+ GetMultiInfoImpl(type, app_locale, values);
+}
+
+void AutofillProfile::FillFormField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const {
+ AutofillFieldType type = field.type();
+ DCHECK_NE(AutofillType::CREDIT_CARD, AutofillType(type).group());
+ DCHECK(field_data);
+
+ if (type == PHONE_HOME_NUMBER) {
+ FillPhoneNumberField(field, variant, field_data);
+ } else if (field_data->form_control_type == "select-one") {
+ FillSelectControl(type, field_data);
+ } else {
+ std::vector<string16> values;
+ GetMultiInfo(type, AutofillCountry::ApplicationLocale(), &values);
+ if (variant >= values.size()) {
+ // If the variant is unavailable, bail. This case is reachable, for
+ // example if Sync updates a profile during the filling process.
+ return;
+ }
+
+ field_data->value = values[variant];
+ }
+}
+
+void AutofillProfile::FillPhoneNumberField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const {
+ std::vector<string16> values;
+ GetMultiInfo(field.type(), AutofillCountry::ApplicationLocale(), &values);
+ DCHECK(variant < values.size());
+
+ // If we are filling a phone number, check to see if the size field
+ // matches the "prefix" or "suffix" sizes and fill accordingly.
+ string16 number = values[variant];
+ if (number.length() ==
+ PhoneNumber::kPrefixLength + PhoneNumber::kSuffixLength) {
+ if (field.phone_part() == AutofillField::PHONE_PREFIX ||
+ field_data->max_length == PhoneNumber::kPrefixLength) {
+ number = number.substr(PhoneNumber::kPrefixOffset,
+ PhoneNumber::kPrefixLength);
+ } else if (field.phone_part() == AutofillField::PHONE_SUFFIX ||
+ field_data->max_length == PhoneNumber::kSuffixLength) {
+ number = number.substr(PhoneNumber::kSuffixOffset,
+ PhoneNumber::kSuffixLength);
+ }
+ }
+
+ field_data->value = number;
+}
+
+const string16 AutofillProfile::Label() const {
+ return label_;
+}
+
+const std::string AutofillProfile::CountryCode() const {
+ return address_.country_code();
+}
+
+void AutofillProfile::SetCountryCode(const std::string& country_code) {
+ address_.set_country_code(country_code);
+}
+
+bool AutofillProfile::IsEmpty() const {
+ FieldTypeSet types;
+ GetNonEmptyTypes(AutofillCountry::ApplicationLocale(), &types);
+ return types.empty();
+}
+
+int AutofillProfile::Compare(const AutofillProfile& profile) const {
+ const AutofillFieldType single_value_types[] = { COMPANY_NAME,
+ ADDRESS_HOME_LINE1,
+ ADDRESS_HOME_LINE2,
+ ADDRESS_HOME_CITY,
+ ADDRESS_HOME_STATE,
+ ADDRESS_HOME_ZIP,
+ ADDRESS_HOME_COUNTRY };
+
+ for (size_t i = 0; i < arraysize(single_value_types); ++i) {
+ int comparison = GetRawInfo(single_value_types[i]).compare(
+ profile.GetRawInfo(single_value_types[i]));
+ if (comparison != 0)
+ return comparison;
+ }
+
+ const AutofillFieldType multi_value_types[] = { NAME_FIRST,
+ NAME_MIDDLE,
+ NAME_LAST,
+ EMAIL_ADDRESS,
+ PHONE_HOME_WHOLE_NUMBER };
+
+ for (size_t i = 0; i < arraysize(multi_value_types); ++i) {
+ std::vector<string16> values_a;
+ std::vector<string16> values_b;
+ GetRawMultiInfo(multi_value_types[i], &values_a);
+ profile.GetRawMultiInfo(multi_value_types[i], &values_b);
+ if (values_a.size() < values_b.size())
+ return -1;
+ if (values_a.size() > values_b.size())
+ return 1;
+ for (size_t j = 0; j < values_a.size(); ++j) {
+ int comparison = values_a[j].compare(values_b[j]);
+ if (comparison != 0)
+ return comparison;
+ }
+ }
+
+ return 0;
+}
+
+bool AutofillProfile::operator==(const AutofillProfile& profile) const {
+ return guid_ == profile.guid_ && Compare(profile) == 0;
+}
+
+bool AutofillProfile::operator!=(const AutofillProfile& profile) const {
+ return !operator==(profile);
+}
+
+const string16 AutofillProfile::PrimaryValue() const {
+ return GetRawInfo(ADDRESS_HOME_LINE1) + GetRawInfo(ADDRESS_HOME_CITY);
+}
+
+bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile) const {
+ FieldTypeSet types;
+ GetNonEmptyTypes(AutofillCountry::ApplicationLocale(), &types);
+
+ for (FieldTypeSet::const_iterator iter = types.begin(); iter != types.end();
+ ++iter) {
+ if (*iter == NAME_FULL) {
+ // Ignore the compound "full name" field type. We are only interested in
+ // comparing the constituent parts. For example, if |this| has a middle
+ // name saved, but |profile| lacks one, |profile| could still be a subset
+ // of |this|.
+ continue;
+ } else if (AutofillType(*iter).group() == AutofillType::PHONE) {
+ // Phone numbers should be canonicalized prior to being compared.
+ if (*iter != PHONE_HOME_WHOLE_NUMBER) {
+ continue;
+ } else if (!autofill_i18n::PhoneNumbersMatch(GetRawInfo(*iter),
+ profile.GetRawInfo(*iter),
+ CountryCode())) {
+ return false;
+ }
+ } else if (StringToLowerASCII(GetRawInfo(*iter)) !=
+ StringToLowerASCII(profile.GetRawInfo(*iter))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile) {
+ FieldTypeSet field_types;
+ profile.GetNonEmptyTypes(AutofillCountry::ApplicationLocale(), &field_types);
+
+ // Only transfer "full" types (e.g. full name) and not fragments (e.g.
+ // first name, last name).
+ CollapseCompoundFieldTypes(&field_types);
+
+ for (FieldTypeSet::const_iterator iter = field_types.begin();
+ iter != field_types.end(); ++iter) {
+ if (AutofillProfile::SupportsMultiValue(*iter)) {
+ std::vector<string16> new_values;
+ profile.GetRawMultiInfo(*iter, &new_values);
+ std::vector<string16> existing_values;
+ GetRawMultiInfo(*iter, &existing_values);
+
+ // GetMultiInfo always returns at least one element, even if the profile
+ // has no data stored for this field type.
+ if (existing_values.size() == 1 && existing_values.front().empty())
+ existing_values.clear();
+
+ FieldTypeGroup group = AutofillType(*iter).group();
+ for (std::vector<string16>::iterator value_iter = new_values.begin();
+ value_iter != new_values.end(); ++value_iter) {
+ // Don't add duplicates.
+ if (group == AutofillType::PHONE) {
+ AddPhoneIfUnique(*value_iter, &existing_values);
+ } else {
+ std::vector<string16>::const_iterator existing_iter = std::find_if(
+ existing_values.begin(), existing_values.end(),
+ std::bind1st(CaseInsensitiveStringEquals(), *value_iter));
+ if (existing_iter == existing_values.end())
+ existing_values.insert(existing_values.end(), *value_iter);
+ }
+ }
+ SetRawMultiInfo(*iter, existing_values);
+ } else {
+ string16 new_value = profile.GetRawInfo(*iter);
+ if (StringToLowerASCII(GetRawInfo(*iter)) !=
+ StringToLowerASCII(new_value)) {
+ SetRawInfo(*iter, new_value);
+ }
+ }
+ }
+}
+
+// static
+bool AutofillProfile::SupportsMultiValue(AutofillFieldType type) {
+ AutofillType::FieldTypeGroup group = AutofillType(type).group();
+ return group == AutofillType::NAME ||
+ group == AutofillType::EMAIL ||
+ group == AutofillType::PHONE;
+}
+
+// static
+bool AutofillProfile::AdjustInferredLabels(
+ std::vector<AutofillProfile*>* profiles) {
+ const size_t kMinimalFieldsShown = 2;
+
+ std::vector<string16> created_labels;
+ CreateInferredLabels(profiles, NULL, UNKNOWN_TYPE, kMinimalFieldsShown,
+ &created_labels);
+ DCHECK_EQ(profiles->size(), created_labels.size());
+
+ bool updated_labels = false;
+ for (size_t i = 0; i < profiles->size(); ++i) {
+ if ((*profiles)[i]->Label() != created_labels[i]) {
+ updated_labels = true;
+ (*profiles)[i]->label_ = created_labels[i];
+ }
+ }
+ return updated_labels;
+}
+
+// static
+void AutofillProfile::CreateInferredLabels(
+ const std::vector<AutofillProfile*>* profiles,
+ const std::vector<AutofillFieldType>* suggested_fields,
+ AutofillFieldType excluded_field,
+ size_t minimal_fields_shown,
+ std::vector<string16>* created_labels) {
+ DCHECK(profiles);
+ DCHECK(created_labels);
+
+ std::vector<AutofillFieldType> fields_to_use;
+ GetFieldsForDistinguishingProfiles(suggested_fields, excluded_field,
+ &fields_to_use);
+
+ // Construct the default label for each profile. Also construct a map that
+ // associates each label with the profiles that have this label. This map is
+ // then used to detect which labels need further differentiating fields.
+ std::map<string16, std::list<size_t> > labels;
+ for (size_t i = 0; i < profiles->size(); ++i) {
+ string16 label =
+ (*profiles)[i]->ConstructInferredLabel(fields_to_use,
+ minimal_fields_shown);
+ labels[label].push_back(i);
+ }
+
+ created_labels->resize(profiles->size());
+ for (std::map<string16, std::list<size_t> >::const_iterator it =
+ labels.begin();
+ it != labels.end(); ++it) {
+ if (it->second.size() == 1) {
+ // This label is unique, so use it without any further ado.
+ string16 label = it->first;
+ size_t profile_index = it->second.front();
+ (*created_labels)[profile_index] = label;
+ } else {
+ // We have more than one profile with the same label, so add
+ // differentiating fields.
+ CreateDifferentiatingLabels(*profiles, it->second, fields_to_use,
+ minimal_fields_shown, created_labels);
+ }
+ }
+}
+
+void AutofillProfile::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ FormGroupList info = FormGroups();
+ for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it)
+ (*it)->GetSupportedTypes(supported_types);
+}
+
+bool AutofillProfile::FillCountrySelectControl(FormFieldData* field_data)
+ const {
+ std::string country_code = CountryCode();
+ std::string app_locale = AutofillCountry::ApplicationLocale();
+
+ DCHECK_EQ(field_data->option_values.size(),
+ field_data->option_contents.size());
+ for (size_t i = 0; i < field_data->option_values.size(); ++i) {
+ // Canonicalize each <option> value to a country code, and compare to the
+ // target country code.
+ string16 value = field_data->option_values[i];
+ string16 contents = field_data->option_contents[i];
+ if (country_code == AutofillCountry::GetCountryCode(value, app_locale) ||
+ country_code == AutofillCountry::GetCountryCode(contents, app_locale)) {
+ field_data->value = value;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AutofillProfile::GetMultiInfoImpl(AutofillFieldType type,
+ const std::string& app_locale,
+ std::vector<string16>* values) const {
+ switch (AutofillType(type).group()) {
+ case AutofillType::NAME:
+ CopyItemsToValues(type, name_, app_locale, values);
+ break;
+ case AutofillType::EMAIL:
+ CopyItemsToValues(type, email_, app_locale, values);
+ break;
+ case AutofillType::PHONE:
+ CopyItemsToValues(type, home_number_, app_locale, values);
+ break;
+ default:
+ values->resize(1);
+ (*values)[0] = GetFormGroupInfo(*this, type, app_locale);
+ }
+}
+
+void AutofillProfile::AddPhoneIfUnique(const string16& phone,
+ std::vector<string16>* existing_phones) {
+ DCHECK(existing_phones);
+ // Phones allow "fuzzy" matching, so "1-800-FLOWERS", "18003569377",
+ // "(800)356-9377" and "356-9377" are considered the same.
+ if (std::find_if(existing_phones->begin(), existing_phones->end(),
+ FindByPhone(phone, CountryCode())) ==
+ existing_phones->end()) {
+ existing_phones->push_back(phone);
+ }
+}
+
+string16 AutofillProfile::ConstructInferredLabel(
+ const std::vector<AutofillFieldType>& included_fields,
+ size_t num_fields_to_use) const {
+ const string16 separator =
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR);
+
+ string16 label;
+ size_t num_fields_used = 0;
+ for (std::vector<AutofillFieldType>::const_iterator it =
+ included_fields.begin();
+ it != included_fields.end() && num_fields_used < num_fields_to_use;
+ ++it) {
+ string16 field = GetRawInfo(*it);
+ if (field.empty())
+ continue;
+
+ if (!label.empty())
+ label.append(separator);
+
+ label.append(field);
+ ++num_fields_used;
+ }
+ return label;
+}
+
+// static
+void AutofillProfile::CreateDifferentiatingLabels(
+ const std::vector<AutofillProfile*>& profiles,
+ const std::list<size_t>& indices,
+ const std::vector<AutofillFieldType>& fields,
+ size_t num_fields_to_include,
+ std::vector<string16>* created_labels) {
+ // For efficiency, we first construct a map of fields to their text values and
+ // each value's frequency.
+ std::map<AutofillFieldType,
+ std::map<string16, size_t> > field_text_frequencies_by_field;
+ for (std::vector<AutofillFieldType>::const_iterator field = fields.begin();
+ field != fields.end(); ++field) {
+ std::map<string16, size_t>& field_text_frequencies =
+ field_text_frequencies_by_field[*field];
+
+ for (std::list<size_t>::const_iterator it = indices.begin();
+ it != indices.end(); ++it) {
+ const AutofillProfile* profile = profiles[*it];
+ string16 field_text = profile->GetRawInfo(*field);
+
+ // If this label is not already in the map, add it with frequency 0.
+ if (!field_text_frequencies.count(field_text))
+ field_text_frequencies[field_text] = 0;
+
+ // Now, increment the frequency for this label.
+ ++field_text_frequencies[field_text];
+ }
+ }
+
+ // Now comes the meat of the algorithm. For each profile, we scan the list of
+ // fields to use, looking for two things:
+ // 1. A (non-empty) field that differentiates the profile from all others
+ // 2. At least |num_fields_to_include| non-empty fields
+ // Before we've satisfied condition (2), we include all fields, even ones that
+ // are identical across all the profiles. Once we've satisfied condition (2),
+ // we only include fields that that have at last two distinct values.
+ for (std::list<size_t>::const_iterator it = indices.begin();
+ it != indices.end(); ++it) {
+ const AutofillProfile* profile = profiles[*it];
+
+ std::vector<AutofillFieldType> label_fields;
+ bool found_differentiating_field = false;
+ for (std::vector<AutofillFieldType>::const_iterator field = fields.begin();
+ field != fields.end(); ++field) {
+ // Skip over empty fields.
+ string16 field_text = profile->GetRawInfo(*field);
+ if (field_text.empty())
+ continue;
+
+ std::map<string16, size_t>& field_text_frequencies =
+ field_text_frequencies_by_field[*field];
+ found_differentiating_field |=
+ !field_text_frequencies.count(string16()) &&
+ (field_text_frequencies[field_text] == 1);
+
+ // Once we've found enough non-empty fields, skip over any remaining
+ // fields that are identical across all the profiles.
+ if (label_fields.size() >= num_fields_to_include &&
+ (field_text_frequencies.size() == 1))
+ continue;
+
+ label_fields.push_back(*field);
+
+ // If we've (1) found a differentiating field and (2) found at least
+ // |num_fields_to_include| non-empty fields, we're done!
+ if (found_differentiating_field &&
+ label_fields.size() >= num_fields_to_include)
+ break;
+ }
+
+ (*created_labels)[*it] =
+ profile->ConstructInferredLabel(label_fields,
+ label_fields.size());
+ }
+}
+
+AutofillProfile::FormGroupList AutofillProfile::FormGroups() const {
+ FormGroupList v(5);
+ v[0] = &name_[0];
+ v[1] = &email_[0];
+ v[2] = &company_;
+ v[3] = &home_number_[0];
+ v[4] = &address_;
+ return v;
+}
+
+const FormGroup* AutofillProfile::FormGroupForType(
+ AutofillFieldType type) const {
+ return const_cast<AutofillProfile*>(this)->MutableFormGroupForType(type);
+}
+
+FormGroup* AutofillProfile::MutableFormGroupForType(AutofillFieldType type) {
+ FormGroup* form_group = NULL;
+ switch (AutofillType(type).group()) {
+ case AutofillType::NAME:
+ form_group = &name_[0];
+ break;
+ case AutofillType::EMAIL:
+ form_group = &email_[0];
+ break;
+ case AutofillType::COMPANY:
+ form_group = &company_;
+ break;
+ case AutofillType::PHONE:
+ form_group = &home_number_[0];
+ break;
+ case AutofillType::ADDRESS_HOME:
+ form_group = &address_;
+ break;
+ default:
+ break;
+ }
+
+ return form_group;
+}
+
+// So we can compare AutofillProfiles with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) {
+ return os
+ << UTF16ToUTF8(profile.Label())
+ << " "
+ << profile.guid()
+ << " "
+ << UTF16ToUTF8(MultiString(profile, NAME_FIRST))
+ << " "
+ << UTF16ToUTF8(MultiString(profile, NAME_MIDDLE))
+ << " "
+ << UTF16ToUTF8(MultiString(profile, NAME_LAST))
+ << " "
+ << UTF16ToUTF8(MultiString(profile, EMAIL_ADDRESS))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(COMPANY_NAME))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE1))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_LINE2))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_CITY))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_STATE))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_ZIP))
+ << " "
+ << UTF16ToUTF8(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))
+ << " "
+ << UTF16ToUTF8(MultiString(profile, PHONE_HOME_WHOLE_NUMBER));
+}
diff --git a/components/autofill/browser/autofill_profile.h b/components/autofill/browser/autofill_profile.h
new file mode 100644
index 0000000..8dbf63a
--- /dev/null
+++ b/components/autofill/browser/autofill_profile.h
@@ -0,0 +1,211 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_PROFILE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_PROFILE_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "components/autofill/browser/address.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/contact_info.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_group.h"
+#include "components/autofill/browser/phone_number.h"
+
+struct FormFieldData;
+
+// A collection of FormGroups stored in a profile. AutofillProfile also
+// implements the FormGroup interface so that owners of this object can request
+// form information from the profile, and the profile will delegate the request
+// to the requested form group type.
+class AutofillProfile : public FormGroup {
+ public:
+ explicit AutofillProfile(const std::string& guid);
+
+ // For use in STL containers.
+ AutofillProfile();
+ AutofillProfile(const AutofillProfile& profile);
+ virtual ~AutofillProfile();
+
+ AutofillProfile& operator=(const AutofillProfile& profile);
+
+ // FormGroup:
+ virtual std::string GetGUID() const OVERRIDE;
+ virtual void GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const OVERRIDE;
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+ virtual string16 GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) OVERRIDE;
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const OVERRIDE;
+
+ // Multi-value equivalents to |GetInfo| and |SetInfo|.
+ void SetRawMultiInfo(AutofillFieldType type,
+ const std::vector<string16>& values);
+ void GetRawMultiInfo(AutofillFieldType type,
+ std::vector<string16>* values) const;
+ void GetMultiInfo(AutofillFieldType type,
+ const std::string& app_locale,
+ std::vector<string16>* values) const;
+
+ // Set |field_data|'s value for phone number based on contents of |this|.
+ // The |field| specifies the type of the phone and whether this is a
+ // phone prefix or suffix. The |variant| parameter specifies which value in a
+ // multi-valued profile.
+ void FillPhoneNumberField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const;
+
+ // The user-visible label of the profile, generated in relation to other
+ // profiles. Shows at least 2 fields that differentiate profile from other
+ // profiles. See AdjustInferredLabels() further down for more description.
+ const string16 Label() const;
+
+ // This guid is the primary identifier for |AutofillProfile| objects.
+ // TODO(estade): remove this and just use GetGUID(). |guid_| can probably
+ // be moved to FormGroup.
+ const std::string guid() const { return guid_; }
+ void set_guid(const std::string& guid) { guid_ = guid; }
+
+ // Accessors for the stored address's country code.
+ const std::string CountryCode() const;
+ void SetCountryCode(const std::string& country_code);
+
+ // Returns true if there are no values (field types) set.
+ bool IsEmpty() const;
+
+ // Comparison for Sync. Returns 0 if the profile is the same as |this|,
+ // or < 0, or > 0 if it is different. The implied ordering can be used for
+ // culling duplicates. The ordering is based on collation order of the
+ // textual contents of the fields.
+ // GUIDs are not compared, only the values of the contents themselves.
+ // Full profile comparision, comparison includes multi-valued fields.
+ int Compare(const AutofillProfile& profile) const;
+
+ // Equality operators compare GUIDs and the contents in the comparison.
+ bool operator==(const AutofillProfile& profile) const;
+ virtual bool operator!=(const AutofillProfile& profile) const;
+
+ // Returns concatenation of full name and address line 1. This acts as the
+ // basis of comparison for new values that are submitted through forms to
+ // aid with correct aggregation of new data.
+ const string16 PrimaryValue() const;
+
+ // Returns true if the data in this AutofillProfile is a subset of the data in
+ // |profile|.
+ bool IsSubsetOf(const AutofillProfile& profile) const;
+
+ // Overwrites the single-valued field data in |profile| with this
+ // Profile. Or, for multi-valued fields append the new values.
+ void OverwriteWithOrAddTo(const AutofillProfile& profile);
+
+ // Returns |true| if |type| accepts multi-values.
+ static bool SupportsMultiValue(AutofillFieldType type);
+
+ // Adjusts the labels according to profile data.
+ // Labels contain minimal different combination of:
+ // 1. Full name.
+ // 2. Address.
+ // 3. E-mail.
+ // 4. Phone.
+ // 5. Company name.
+ // Profile labels are changed accordingly to these rules.
+ // Returns true if any of the profiles were updated.
+ // This function is useful if you want to adjust unique labels for all
+ // profiles. For non permanent situations (selection of profile, when user
+ // started typing in the field, for example) use CreateInferredLabels().
+ static bool AdjustInferredLabels(std::vector<AutofillProfile*>* profiles);
+
+ // Creates inferred labels for |profiles|, according to the rules above and
+ // stores them in |created_labels|. If |suggested_fields| is not NULL, the
+ // resulting label fields are drawn from |suggested_fields|, except excluding
+ // |excluded_field|. Otherwise, the label fields are drawn from a default set,
+ // and |excluded_field| is ignored; by convention, it should be of
+ // |UNKNOWN_TYPE| when |suggested_fields| is NULL. Each label includes at
+ // least |minimal_fields_shown| fields, if possible.
+ static void CreateInferredLabels(
+ const std::vector<AutofillProfile*>* profiles,
+ const std::vector<AutofillFieldType>* suggested_fields,
+ AutofillFieldType excluded_field,
+ size_t minimal_fields_shown,
+ std::vector<string16>* created_labels);
+
+ private:
+ typedef std::vector<const FormGroup*> FormGroupList;
+
+ // FormGroup:
+ virtual bool FillCountrySelectControl(FormFieldData* field) const OVERRIDE;
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ // Shared implementation for GetRawMultiInfo() and GetMultiInfo(). Pass an
+ // empty |app_locale| to get the raw info; otherwise, the returned info is
+ // canonicalized according to the given |app_locale|, if appropriate.
+ void GetMultiInfoImpl(AutofillFieldType type,
+ const std::string& app_locale,
+ std::vector<string16>* values) const;
+
+ // Checks if the |phone| is in the |existing_phones| using fuzzy matching:
+ // for example, "1-800-FLOWERS", "18003569377", "(800)356-9377" and "356-9377"
+ // are considered the same.
+ // Adds the |phone| to the |existing_phones| if not already there.
+ void AddPhoneIfUnique(const string16& phone,
+ std::vector<string16>* existing_phones);
+
+ // Builds inferred label from the first |num_fields_to_include| non-empty
+ // fields in |label_fields|. Uses as many fields as possible if there are not
+ // enough non-empty fields.
+ string16 ConstructInferredLabel(
+ const std::vector<AutofillFieldType>& label_fields,
+ size_t num_fields_to_include) const;
+
+ // Creates inferred labels for |profiles| at indices corresponding to
+ // |indices|, and stores the results to the corresponding elements of
+ // |created_labels|. These labels include enough fields to differentiate among
+ // the profiles, if possible; and also at least |num_fields_to_include|
+ // fields, if possible. The label fields are drawn from |fields|.
+ static void CreateDifferentiatingLabels(
+ const std::vector<AutofillProfile*>& profiles,
+ const std::list<size_t>& indices,
+ const std::vector<AutofillFieldType>& fields,
+ size_t num_fields_to_include,
+ std::vector<string16>* created_labels);
+
+ // Utilities for listing and lookup of the data members that constitute
+ // user-visible profile information.
+ FormGroupList FormGroups() const;
+ const FormGroup* FormGroupForType(AutofillFieldType type) const;
+ FormGroup* MutableFormGroupForType(AutofillFieldType type);
+
+ // The label presented to the user when selecting a profile.
+ string16 label_;
+
+ // The guid of this profile.
+ std::string guid_;
+
+ // Personal information for this profile.
+ std::vector<NameInfo> name_;
+ std::vector<EmailInfo> email_;
+ CompanyInfo company_;
+ std::vector<PhoneNumber> home_number_;
+ Address address_;
+};
+
+// So we can compare AutofillProfiles with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile);
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_PROFILE_H_
diff --git a/components/autofill/browser/autofill_profile_unittest.cc b/components/autofill/browser/autofill_profile_unittest.cc
new file mode 100644
index 0000000..bccb6e6
--- /dev/null
+++ b/components/autofill/browser/autofill_profile_unittest.cc
@@ -0,0 +1,848 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
+#include "base/string16.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/common/form_field_data.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+bool UpdateProfileLabel(AutofillProfile *profile) {
+ std::vector<AutofillProfile*> profiles;
+ profiles.push_back(profile);
+ return AutofillProfile::AdjustInferredLabels(&profiles);
+}
+
+} // namespace
+
+// Tests different possibilities for summary string generation.
+// Based on existence of first name, last name, and address line 1.
+TEST(AutofillProfileTest, PreviewSummaryString) {
+ // Case 0/null: ""
+ AutofillProfile profile0;
+ // Empty profile - nothing to update.
+ EXPECT_FALSE(UpdateProfileLabel(&profile0));
+ string16 summary0 = profile0.Label();
+ EXPECT_EQ(string16(), summary0);
+
+ // Case 0a/empty name and address, so the first two fields of the rest of the
+ // data is used: "Hollywood, CA"
+ AutofillProfile profile00;
+ autofill_test::SetProfileInfo(&profile00, "", "", "",
+ "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA", "91601", "US",
+ "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile00));
+ string16 summary00 = profile00.Label();
+ EXPECT_EQ(ASCIIToUTF16("Hollywood, CA"), summary00);
+
+ // Case 1: "<address>" without line 2.
+ AutofillProfile profile1;
+ autofill_test::SetProfileInfo(&profile1, "", "", "",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile1));
+ string16 summary1 = profile1.Label();
+ EXPECT_EQ(ASCIIToUTF16("123 Zoo St., Hollywood"), summary1);
+
+ // Case 1a: "<address>" with line 2.
+ AutofillProfile profile1a;
+ autofill_test::SetProfileInfo(&profile1a, "", "", "",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile1a));
+ string16 summary1a = profile1a.Label();
+ EXPECT_EQ(ASCIIToUTF16("123 Zoo St., unit 5"), summary1a);
+
+ // Case 2: "<lastname>"
+ AutofillProfile profile2;
+ autofill_test::SetProfileInfo(&profile2, "", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile2));
+ string16 summary2 = profile2.Label();
+ // Summary includes full name, to the maximal extent available.
+ EXPECT_EQ(ASCIIToUTF16("Mitchell Morrison, Hollywood"), summary2);
+
+ // Case 3: "<lastname>, <address>"
+ AutofillProfile profile3;
+ autofill_test::SetProfileInfo(&profile3, "", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "",
+ "Hollywood", "CA", "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile3));
+ string16 summary3 = profile3.Label();
+ EXPECT_EQ(ASCIIToUTF16("Mitchell Morrison, 123 Zoo St."), summary3);
+
+ // Case 4: "<firstname>"
+ AutofillProfile profile4;
+ autofill_test::SetProfileInfo(&profile4, "Marion", "Mitchell", "",
+ "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA", "91601", "US",
+ "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile4));
+ string16 summary4 = profile4.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell, Hollywood"), summary4);
+
+ // Case 5: "<firstname>, <address>"
+ AutofillProfile profile5;
+ autofill_test::SetProfileInfo(&profile5, "Marion", "Mitchell", "",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile5));
+ string16 summary5 = profile5.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell, 123 Zoo St."), summary5);
+
+ // Case 6: "<firstname> <lastname>"
+ AutofillProfile profile6;
+ autofill_test::SetProfileInfo(&profile6, "Marion", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile6));
+ string16 summary6 = profile6.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell Morrison, Hollywood"),
+ summary6);
+
+ // Case 7: "<firstname> <lastname>, <address>"
+ AutofillProfile profile7;
+ autofill_test::SetProfileInfo(&profile7, "Marion", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile7));
+ string16 summary7 = profile7.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell Morrison, 123 Zoo St."),
+ summary7);
+
+ // Case 7a: "<firstname> <lastname>, <address>" - same as #7, except for
+ // e-mail.
+ AutofillProfile profile7a;
+ autofill_test::SetProfileInfo(&profile7a, "Marion", "Mitchell",
+ "Morrison", "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US", "16505678910");
+ std::vector<AutofillProfile*> profiles;
+ profiles.push_back(&profile7);
+ profiles.push_back(&profile7a);
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles));
+ summary7 = profile7.Label();
+ string16 summary7a = profile7a.Label();
+ EXPECT_EQ(ASCIIToUTF16(
+ "Marion Mitchell Morrison, 123 Zoo St., johnwayne@me.xyz"), summary7);
+ EXPECT_EQ(ASCIIToUTF16(
+ "Marion Mitchell Morrison, 123 Zoo St., marion@me.xyz"), summary7a);
+}
+
+TEST(AutofillProfileTest, AdjustInferredLabels) {
+ std::vector<AutofillProfile*> profiles;
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(
+ profiles[0],
+ "John",
+ "",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(
+ profiles[1],
+ "Jane",
+ "",
+ "Doe",
+ "janedoe@tertium.com",
+ "Pluto Inc.",
+ "123 Letha Shore.",
+ "",
+ "Dis", "CA",
+ "91222",
+ "US",
+ "12345678910");
+ // As labels are empty they are adjusted the first time.
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles));
+ // No need to adjust them anymore.
+ EXPECT_FALSE(AutofillProfile::AdjustInferredLabels(&profiles));
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St."),
+ profiles[0]->Label());
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."),
+ profiles[1]->Label());
+
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(
+ profiles[2],
+ "John",
+ "",
+ "Doe",
+ "johndoe@tertium.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles));
+
+ // Profile 0 and 2 inferred label now includes an e-mail.
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., johndoe@hades.com"),
+ profiles[0]->Label());
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."),
+ profiles[1]->Label());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., johndoe@tertium.com"),
+ profiles[2]->Label());
+
+ delete profiles[2];
+ profiles.pop_back();
+
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(
+ profiles[2],
+ "John",
+ "",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CO", // State is different
+ "91111",
+ "US",
+ "16502111111");
+
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles));
+
+ // Profile 0 and 2 inferred label now includes a state.
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"),
+ profiles[0]->Label());
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."),
+ profiles[1]->Label());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO"),
+ profiles[2]->Label());
+
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(
+ profiles[3],
+ "John",
+ "",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CO", // State is different for some.
+ "91111",
+ "US",
+ "16504444444"); // Phone is different for some.
+
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles));
+
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"),
+ profiles[0]->Label());
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."),
+ profiles[1]->Label());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, 16502111111"),
+ profiles[2]->Label());
+ // This one differs from other ones by unique phone, so no need for extra
+ // information.
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, 16504444444"),
+ profiles[3]->Label());
+
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(
+ profiles[4],
+ "John",
+ "",
+ "Doe",
+ "johndoe@styx.com", // E-Mail is different for some.
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CO", // State is different for some.
+ "91111",
+ "US",
+ "16504444444"); // Phone is different for some.
+
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles));
+
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CA"),
+ profiles[0]->Label());
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."),
+ profiles[1]->Label());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, johndoe@hades.com,"
+ " 16502111111"),
+ profiles[2]->Label());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, johndoe@hades.com,"
+ " 16504444444"),
+ profiles[3]->Label());
+ // This one differs from other ones by unique e-mail, so no need for extra
+ // information.
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., CO, johndoe@styx.com"),
+ profiles[4]->Label());
+
+ EXPECT_FALSE(AutofillProfile::AdjustInferredLabels(&profiles));
+
+ // Clean up.
+ STLDeleteContainerPointers(profiles.begin(), profiles.end());
+}
+
+TEST(AutofillProfileTest, CreateInferredLabels) {
+ std::vector<AutofillProfile*> profiles;
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[0],
+ "John",
+ "",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[1],
+ "Jane",
+ "",
+ "Doe",
+ "janedoe@tertium.com",
+ "Pluto Inc.",
+ "123 Letha Shore.",
+ "",
+ "Dis", "CA",
+ "91222",
+ "US",
+ "12345678910");
+ std::vector<string16> labels;
+ // Two fields at least - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles, NULL, UNKNOWN_TYPE, 2,
+ &labels);
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St."), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore."), labels[1]);
+
+ // Three fields at least - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles, NULL, UNKNOWN_TYPE, 3,
+ &labels);
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 666 Erebus St., Elysium"),
+ labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe, 123 Letha Shore., Dis"),
+ labels[1]);
+
+ std::vector<AutofillFieldType> suggested_fields;
+ suggested_fields.push_back(ADDRESS_HOME_CITY);
+ suggested_fields.push_back(ADDRESS_HOME_STATE);
+ suggested_fields.push_back(ADDRESS_HOME_ZIP);
+
+ // Two fields at least, from suggested fields - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields,
+ UNKNOWN_TYPE, 2, &labels);
+ EXPECT_EQ(ASCIIToUTF16("Elysium, CA"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Dis, CA"), labels[1]);
+
+ // Three fields at least, from suggested fields - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields,
+ UNKNOWN_TYPE, 3, &labels);
+ EXPECT_EQ(ASCIIToUTF16("Elysium, CA, 91111"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Dis, CA, 91222"), labels[1]);
+
+ // Three fields at least, from suggested fields - but filter reduces available
+ // fields to two.
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields,
+ ADDRESS_HOME_STATE, 3, &labels);
+ EXPECT_EQ(ASCIIToUTF16("Elysium, 91111"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Dis, 91222"), labels[1]);
+
+ suggested_fields.clear();
+ // In our implementation we always display NAME_FULL for all NAME* fields...
+ suggested_fields.push_back(NAME_MIDDLE);
+ // One field at least, from suggested fields - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields,
+ UNKNOWN_TYPE, 1, &labels);
+ EXPECT_EQ(ASCIIToUTF16("John Doe"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe"), labels[1]);
+
+ // One field at least, from suggested fields - filter the same as suggested
+ // field.
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields,
+ NAME_MIDDLE, 1, &labels);
+ EXPECT_EQ(string16(), labels[0]);
+ EXPECT_EQ(string16(), labels[1]);
+
+ suggested_fields.clear();
+ // In our implementation we always display NAME_FULL for NAME_MIDDLE_INITIAL
+ suggested_fields.push_back(NAME_MIDDLE_INITIAL);
+ // One field at least, from suggested fields - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields,
+ UNKNOWN_TYPE, 1, &labels);
+ EXPECT_EQ(ASCIIToUTF16("John Doe"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("Jane Doe"), labels[1]);
+
+ // One field at least, from suggested fields - filter same as the first non-
+ // unknown suggested field.
+ suggested_fields.clear();
+ suggested_fields.push_back(UNKNOWN_TYPE);
+ suggested_fields.push_back(NAME_FULL);
+ suggested_fields.push_back(ADDRESS_HOME_LINE1);
+ AutofillProfile::CreateInferredLabels(&profiles, &suggested_fields, NAME_FULL,
+ 1, &labels);
+ EXPECT_EQ(string16(ASCIIToUTF16("666 Erebus St.")), labels[0]);
+ EXPECT_EQ(string16(ASCIIToUTF16("123 Letha Shore.")), labels[1]);
+
+ // Clean up.
+ STLDeleteContainerPointers(profiles.begin(), profiles.end());
+}
+
+// Test that we fall back to using the full name if there are no other
+// distinguishing fields, but only if it makes sense given the suggested fields.
+TEST(AutofillProfileTest, CreateInferredLabelsFallsBackToFullName) {
+ ScopedVector<AutofillProfile> profiles;
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[0],
+ "John", "", "Doe", "doe@example.com", "",
+ "88 Nowhere Ave.", "", "", "", "", "", "");
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[1],
+ "Johnny", "K", "Doe", "doe@example.com", "",
+ "88 Nowhere Ave.", "", "", "", "", "", "");
+
+ // If the only name field in the suggested fields is the excluded field, we
+ // should not fall back to the full name as a distinguishing field.
+ std::vector<AutofillFieldType> suggested_fields;
+ suggested_fields.push_back(NAME_LAST);
+ suggested_fields.push_back(ADDRESS_HOME_LINE1);
+ suggested_fields.push_back(EMAIL_ADDRESS);
+ std::vector<string16> labels;
+ AutofillProfile::CreateInferredLabels(&profiles.get(), &suggested_fields,
+ NAME_LAST, 1, &labels);
+ ASSERT_EQ(2U, labels.size());
+ EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave."), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave."), labels[1]);
+
+ // Otherwise, we should.
+ suggested_fields.push_back(NAME_FIRST);
+ AutofillProfile::CreateInferredLabels(&profiles.get(), &suggested_fields,
+ NAME_LAST, 1, &labels);
+ ASSERT_EQ(2U, labels.size());
+ EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., John Doe"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., Johnny K Doe"), labels[1]);
+}
+
+// Test that we do not show duplicate fields in the labels.
+TEST(AutofillProfileTest, CreateInferredLabelsNoDuplicatedFields) {
+ ScopedVector<AutofillProfile> profiles;
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[0],
+ "John", "", "Doe", "doe@example.com", "",
+ "88 Nowhere Ave.", "", "", "", "", "", "");
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[1],
+ "John", "", "Doe", "dojo@example.com", "",
+ "88 Nowhere Ave.", "", "", "", "", "", "");
+
+ // If the only name field in the suggested fields is the excluded field, we
+ // should not fall back to the full name as a distinguishing field.
+ std::vector<AutofillFieldType> suggested_fields;
+ suggested_fields.push_back(ADDRESS_HOME_LINE1);
+ suggested_fields.push_back(ADDRESS_BILLING_LINE1);
+ suggested_fields.push_back(EMAIL_ADDRESS);
+ std::vector<string16> labels;
+ AutofillProfile::CreateInferredLabels(&profiles.get(), &suggested_fields,
+ UNKNOWN_TYPE, 2, &labels);
+ ASSERT_EQ(2U, labels.size());
+ EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., doe@example.com"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("88 Nowhere Ave., dojo@example.com"), labels[1]);
+}
+
+// Make sure that empty fields are not treated as distinguishing fields.
+TEST(AutofillProfileTest, CreateInferredLabelsSkipsEmptyFields) {
+ ScopedVector<AutofillProfile> profiles;
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[0],
+ "John", "", "Doe", "doe@example.com",
+ "Gogole", "", "", "", "", "", "", "");
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[1],
+ "John", "", "Doe", "doe@example.com",
+ "Ggoole", "", "", "", "", "", "", "");
+ profiles.push_back(new AutofillProfile);
+ autofill_test::SetProfileInfo(profiles[2],
+ "John", "", "Doe", "john.doe@example.com",
+ "Goolge", "", "", "", "", "", "", "");
+
+ std::vector<string16> labels;
+ AutofillProfile::CreateInferredLabels(&profiles.get(), NULL, UNKNOWN_TYPE, 3,
+ &labels);
+ ASSERT_EQ(3U, labels.size());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Gogole"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Ggoole"), labels[1]);
+ EXPECT_EQ(ASCIIToUTF16("John Doe, john.doe@example.com, Goolge"), labels[2]);
+
+ // A field must have a non-empty value for each profile to be considered a
+ // distinguishing field.
+ profiles[1]->SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("88 Nowhere Ave."));
+ AutofillProfile::CreateInferredLabels(&profiles.get(), NULL, UNKNOWN_TYPE, 1,
+ &labels);
+ ASSERT_EQ(3U, labels.size());
+ EXPECT_EQ(ASCIIToUTF16("John Doe, doe@example.com, Gogole"), labels[0]);
+ EXPECT_EQ(ASCIIToUTF16("John Doe, 88 Nowhere Ave., doe@example.com, Ggoole"),
+ labels[1]) << labels[1];
+ EXPECT_EQ(ASCIIToUTF16("John Doe, john.doe@example.com"), labels[2]);
+}
+
+TEST(AutofillProfileTest, IsSubsetOf) {
+ scoped_ptr<AutofillProfile> a, b;
+
+ // |a| is a subset of |b|.
+ a.reset(new AutofillProfile);
+ b.reset(new AutofillProfile);
+ autofill_test::SetProfileInfo(a.get(), "Thomas", NULL, "Jefferson",
+ "declaration_guy@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ autofill_test::SetProfileInfo(b.get(), "Thomas", NULL, "Jefferson",
+ "declaration_guy@gmail.com", "United States Government", "Monticello",
+ NULL, "Charlottesville", "Virginia", "22902", NULL, NULL);
+ EXPECT_TRUE(a->IsSubsetOf(*b));
+
+ // |b| is not a subset of |a|.
+ EXPECT_FALSE(b->IsSubsetOf(*a));
+
+ // |a| is a subset of |a|.
+ EXPECT_TRUE(a->IsSubsetOf(*a));
+
+ // One field in |b| is different.
+ a.reset(new AutofillProfile);
+ b.reset(new AutofillProfile);
+ autofill_test::SetProfileInfo(a.get(), "Thomas", NULL, "Jefferson",
+ "declaration_guy@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ autofill_test::SetProfileInfo(a.get(), "Thomas", NULL, "Adams",
+ "declaration_guy@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ EXPECT_FALSE(a->IsSubsetOf(*b));
+}
+
+TEST(AutofillProfileTest, AssignmentOperator) {
+ AutofillProfile a, b;
+
+ // Result of assignment should be logically equal to the original profile.
+ autofill_test::SetProfileInfo(&a, "Marion", "Mitchell", "Morrison",
+ "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US",
+ "12345678910");
+ b = a;
+ EXPECT_TRUE(a == b);
+
+ // Assignment to self should not change the profile value.
+ a = a;
+ EXPECT_TRUE(a == b);
+}
+
+TEST(AutofillProfileTest, Copy) {
+ AutofillProfile a;
+
+ // Clone should be logically equal to the original.
+ autofill_test::SetProfileInfo(&a, "Marion", "Mitchell", "Morrison",
+ "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US",
+ "12345678910");
+ AutofillProfile b(a);
+ EXPECT_TRUE(a == b);
+}
+
+TEST(AutofillProfileTest, Compare) {
+ AutofillProfile a, b;
+
+ // Empty profiles are the same.
+ EXPECT_EQ(0, a.Compare(b));
+
+ // GUIDs don't count.
+ a.set_guid(base::GenerateGUID());
+ b.set_guid(base::GenerateGUID());
+ EXPECT_EQ(0, a.Compare(b));
+
+ // Different values produce non-zero results.
+ autofill_test::SetProfileInfo(&a, "Jimmy", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ autofill_test::SetProfileInfo(&b, "Ringo", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ EXPECT_GT(0, a.Compare(b));
+ EXPECT_LT(0, b.Compare(a));
+
+ // Phone numbers are compared by the full number, including the area code.
+ // This is a regression test for http://crbug.com/163024
+ autofill_test::SetProfileInfo(&a, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "650.555.4321");
+ autofill_test::SetProfileInfo(&b, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "408.555.4321");
+ EXPECT_GT(0, a.Compare(b));
+ EXPECT_LT(0, b.Compare(a));
+}
+
+TEST(AutofillProfileTest, CountryCode) {
+ AutofillProfile profile;
+ EXPECT_EQ(std::string(), profile.CountryCode());
+
+ profile.SetCountryCode("US");
+ EXPECT_EQ("US", profile.CountryCode());
+}
+
+TEST(AutofillProfileTest, MultiValueNames) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("John Doe"));
+ const string16 kJohnPDoe(ASCIIToUTF16("John P. Doe"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+
+ // Expect regular |GetInfo| returns the first element.
+ EXPECT_EQ(kJohnDoe, p.GetRawInfo(NAME_FULL));
+
+ // Ensure that we get out what we put in.
+ std::vector<string16> get_values;
+ p.GetRawMultiInfo(NAME_FULL, &get_values);
+ ASSERT_EQ(2UL, get_values.size());
+ EXPECT_EQ(kJohnDoe, get_values[0]);
+ EXPECT_EQ(kJohnPDoe, get_values[1]);
+
+ // Update the values.
+ AutofillProfile p2 = p;
+ EXPECT_EQ(0, p.Compare(p2));
+ const string16 kNoOne(ASCIIToUTF16("No One"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ p.GetRawMultiInfo(NAME_FULL, &get_values);
+ ASSERT_EQ(2UL, get_values.size());
+ EXPECT_EQ(kJohnDoe, get_values[0]);
+ EXPECT_EQ(kNoOne, get_values[1]);
+ EXPECT_NE(0, p.Compare(p2));
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ p.GetRawMultiInfo(NAME_FULL, &get_values);
+ ASSERT_EQ(1UL, get_values.size());
+ EXPECT_EQ(string16(), get_values[0]);
+
+ // Expect regular |GetInfo| returns empty value.
+ EXPECT_EQ(string16(), p.GetRawInfo(NAME_FULL));
+}
+
+TEST(AutofillProfileTest, MultiValueEmails) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("john@doe.com"));
+ const string16 kJohnPDoe(ASCIIToUTF16("john_p@doe.com"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+
+ // Expect regular |GetInfo| returns the first element.
+ EXPECT_EQ(kJohnDoe, p.GetRawInfo(EMAIL_ADDRESS));
+
+ // Ensure that we get out what we put in.
+ std::vector<string16> get_values;
+ p.GetRawMultiInfo(EMAIL_ADDRESS, &get_values);
+ ASSERT_EQ(2UL, get_values.size());
+ EXPECT_EQ(kJohnDoe, get_values[0]);
+ EXPECT_EQ(kJohnPDoe, get_values[1]);
+
+ // Update the values.
+ AutofillProfile p2 = p;
+ EXPECT_EQ(0, p.Compare(p2));
+ const string16 kNoOne(ASCIIToUTF16("no@one.com"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+ p.GetRawMultiInfo(EMAIL_ADDRESS, &get_values);
+ ASSERT_EQ(2UL, get_values.size());
+ EXPECT_EQ(kJohnDoe, get_values[0]);
+ EXPECT_EQ(kNoOne, get_values[1]);
+ EXPECT_NE(0, p.Compare(p2));
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+ p.GetRawMultiInfo(EMAIL_ADDRESS, &get_values);
+ ASSERT_EQ(1UL, get_values.size());
+ EXPECT_EQ(string16(), get_values[0]);
+
+ // Expect regular |GetInfo| returns empty value.
+ EXPECT_EQ(string16(), p.GetRawInfo(EMAIL_ADDRESS));
+}
+
+TEST(AutofillProfileTest, MultiValuePhone) {
+ AutofillProfile p;
+ const string16 kJohnDoe(ASCIIToUTF16("4151112222"));
+ const string16 kJohnPDoe(ASCIIToUTF16("4151113333"));
+ std::vector<string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+
+ // Expect regular |GetInfo| returns the first element.
+ EXPECT_EQ(kJohnDoe, p.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ // Ensure that we get out what we put in.
+ std::vector<string16> get_values;
+ p.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &get_values);
+ ASSERT_EQ(2UL, get_values.size());
+ EXPECT_EQ(kJohnDoe, get_values[0]);
+ EXPECT_EQ(kJohnPDoe, get_values[1]);
+
+ // Update the values.
+ AutofillProfile p2 = p;
+ EXPECT_EQ(0, p.Compare(p2));
+ const string16 kNoOne(ASCIIToUTF16("4152110000"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+ p.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &get_values);
+ ASSERT_EQ(2UL, get_values.size());
+ EXPECT_EQ(kJohnDoe, get_values[0]);
+ EXPECT_EQ(kNoOne, get_values[1]);
+ EXPECT_NE(0, p.Compare(p2));
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+ p.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &get_values);
+ ASSERT_EQ(1UL, get_values.size());
+ EXPECT_EQ(string16(), get_values[0]);
+
+ // Expect regular |GetInfo| returns empty value.
+ EXPECT_EQ(string16(), p.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+}
+
+TEST(AutofillProfileTest, AddressCountryFull) {
+ const char* const kCountries[] = {
+ "Albania", "Canada"
+ };
+ std::vector<string16> options(arraysize(kCountries));
+ for (size_t i = 0; i < arraysize(kCountries); ++i) {
+ options[i] = ASCIIToUTF16(kCountries[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("CA"));
+ profile.FillSelectControl(ADDRESS_HOME_COUNTRY, &field);
+ EXPECT_EQ(ASCIIToUTF16("Canada"), field.value);
+}
+
+TEST(AutofillProfileTest, AddressCountryAbbrev) {
+ const char* const kCountries[] = {
+ "AL", "CA"
+ };
+ std::vector<string16> options(arraysize(kCountries));
+ for (size_t i = 0; i < arraysize(kCountries); ++i) {
+ options[i] = ASCIIToUTF16(kCountries[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ AutofillProfile profile;
+ profile.SetInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("Canada"), "en-US");
+ profile.FillSelectControl(ADDRESS_HOME_COUNTRY, &field);
+ EXPECT_EQ(ASCIIToUTF16("CA"), field.value);
+}
+
+TEST(AutofillProfileTest, AddressStateFull) {
+ const char* const kStates[] = {
+ "Alabama", "California"
+ };
+ std::vector<string16> options(arraysize(kStates));
+ for (size_t i = 0; i < arraysize(kStates); ++i) {
+ options[i] = ASCIIToUTF16(kStates[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.FillSelectControl(ADDRESS_HOME_STATE, &field);
+ EXPECT_EQ(ASCIIToUTF16("California"), field.value);
+}
+
+TEST(AutofillProfileTest, AddressStateAbbrev) {
+ const char* const kStates[] = {
+ "AL", "CA"
+ };
+ std::vector<string16> options(arraysize(kStates));
+ for (size_t i = 0; i < arraysize(kStates); ++i) {
+ options[i] = ASCIIToUTF16(kStates[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.FillSelectControl(ADDRESS_HOME_STATE, &field);
+ EXPECT_EQ(ASCIIToUTF16("CA"), field.value);
+}
+
+TEST(AutofillProfileTest, FillByValue) {
+ const char* const kStates[] = {
+ "Alabama", "California"
+ };
+ std::vector<string16> values(arraysize(kStates));
+ std::vector<string16> contents(arraysize(kStates));
+ for (unsigned int i = 0; i < arraysize(kStates); ++i) {
+ values[i] = ASCIIToUTF16(kStates[i]);
+ contents[i] = ASCIIToUTF16(base::StringPrintf("%u", i));
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = values;
+ field.option_contents = contents;
+
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.FillSelectControl(ADDRESS_HOME_STATE, &field);
+ EXPECT_EQ(ASCIIToUTF16("California"), field.value);
+}
+
+TEST(AutofillProfileTest, FillByContents) {
+ const char* const kStates[] = {
+ "Alabama", "California"
+ };
+ std::vector<string16> values(arraysize(kStates));
+ std::vector<string16> contents(arraysize(kStates));
+ for (unsigned int i = 0; i < arraysize(kStates); ++i) {
+ values[i] = ASCIIToUTF16(base::StringPrintf("%u", i + 1));
+ contents[i] = ASCIIToUTF16(kStates[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = values;
+ field.option_contents = contents;
+
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.FillSelectControl(ADDRESS_HOME_STATE, &field);
+ EXPECT_EQ(ASCIIToUTF16("2"), field.value);
+}
diff --git a/components/autofill/browser/autofill_regex_constants.cc.utf8 b/components/autofill/browser/autofill_regex_constants.cc.utf8
new file mode 100644
index 0000000..53aa9d5
--- /dev/null
+++ b/components/autofill/browser/autofill_regex_constants.cc.utf8
@@ -0,0 +1,293 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains UTF8 strings that we want as char arrays. To avoid
+// different compilers, we use a script to convert the UTF8 strings into
+// numeric literals (\x##).
+
+#include "components/autofill/browser/autofill_regex_constants.h"
+
+namespace autofill {
+
+/////////////////////////////////////////////////////////////////////////////
+// address_field.cc
+/////////////////////////////////////////////////////////////////////////////
+const char kAttentionIgnoredRe[] = "attention|attn";
+const char kRegionIgnoredRe[] =
+ "province|region|other"
+ "|provincia" // es
+ "|bairro|suburb"; // pt-BR, pt-PT
+const char kCompanyRe[] =
+ "company|business|organization|organisation"
+ "|firma|firmenname" // de-DE
+ "|empresa" // es
+ "|societe|société" // fr-FR
+ "|ragione.?sociale" // it-IT
+ "|会社" // ja-JP
+ "|название.?компании" // ru
+ "|单位|公司" // zh-CN
+ "|회사|직장"; // ko-KR
+const char kAddressLine1Re[] =
+ "address.*line|address1|addr1|street"
+ "|strasse|straße|hausnummer|housenumber" // de-DE
+ "|house.?name" // en-GB
+ "|direccion|dirección" // es
+ "|adresse" // fr-FR
+ "|indirizzo" // it-IT
+ "|住所1" // ja-JP
+ "|morada|endereço" // pt-BR, pt-PT
+ "|Адрес" // ru
+ "|地址" // zh-CN
+ "|주소.?1"; // ko-KR
+const char kAddressLine1LabelRe[] =
+ "address"
+ "|adresse" // fr-FR
+ "|indirizzo" // it-IT
+ "|住所" // ja-JP
+ "|地址" // zh-CN
+ "|주소"; // ko-KR
+const char kAddressLine2Re[] =
+ "address.*line2|address2|addr2|street|suite|unit"
+ "|adresszusatz|ergänzende.?angaben" // de-DE
+ "|direccion2|colonia|adicional" // es
+ "|addresssuppl|complementnom|appartement" // fr-FR
+ "|indirizzo2" // it-IT
+ "|住所2" // ja-JP
+ "|complemento|addrcomplement" // pt-BR, pt-PT
+ "|Улица" // ru
+ "|地址2" // zh-CN
+ "|주소.?2"; // ko-KR
+const char kAddressLine2LabelRe[] =
+ "address"
+ "|adresse" // fr-FR
+ "|indirizzo" // it-IT
+ "|地址" // zh-CN
+ "|주소"; // ko-KR
+const char kAddressLine3Re[] =
+ "address.*line3|address3|addr3|street|line3"
+ "|municipio" // es
+ "|batiment|residence" // fr-FR
+ "|indirizzo3"; // it-IT
+const char kCountryRe[] =
+ "country|countries|location"
+ "|país|pais" // es
+ "|国" // ja-JP
+ "|国家" // zh-CN
+ "|국가|나라"; // ko-KR
+const char kZipCodeRe[] =
+ "zip|postal|post.*code|pcode|^1z$"
+ "|postleitzahl" // de-DE
+ "|\\bcp\\b" // es
+ "|\\bcdp\\b" // fr-FR
+ "|\\bcap\\b" // it-IT
+ "|郵便番号" // ja-JP
+ "|codigo|codpos|\\bcep\\b" // pt-BR, pt-PT
+ "|Почтовый.?Индекс" // ru
+ "|邮政编码|邮编" // zh-CN
+ "|郵遞區號" // zh-TW
+ "|우편.?번호"; // ko-KR
+const char kZip4Re[] =
+ "zip|^-$|post2"
+ "|codpos2"; // pt-BR, pt-PT
+const char kCityRe[] =
+ "city|town"
+ "|\\bort\\b|stadt" // de-DE
+ "|suburb" // en-AU
+ "|ciudad|provincia|localidad|poblacion" // es
+ "|ville|commune" // fr-FR
+ "|localita" // it-IT
+ "|市区町村" // ja-JP
+ "|cidade" // pt-BR, pt-PT
+ "|Город" // ru
+ "|市" // zh-CN
+ "|分區" // zh-TW
+ "|^시[^도·・]|시[·・]?군[·・]?구"; // ko-KR
+const char kStateRe[] =
+ "(?<!united )state|county|region|province"
+ "|land" // de-DE
+ "|county|principality" // en-UK
+ "|都道府県" // ja-JP
+ "|estado|provincia" // pt-BR, pt-PT
+ "|область" // ru
+ "|省" // zh-CN
+ "|地區" // zh-TW
+ "|^시[·・]?도"; // ko-KR
+const char kAddressTypeSameAsRe[] = "same as";
+const char kAddressTypeUseMyRe[] = "use my";
+const char kBillingDesignatorRe[] = "bill";
+const char kShippingDesignatorRe[] = "ship";
+
+/////////////////////////////////////////////////////////////////////////////
+// credit_card_field.cc
+/////////////////////////////////////////////////////////////////////////////
+const char kNameOnCardRe[] =
+ "card.?holder|name.*\\bon\\b.*card|cc.?name|cc.?full.?name|owner"
+ "|karteninhaber" // de-DE
+ "|nombre.*tarjeta" // es
+ "|nom.*carte" // fr-FR
+ "|nome.*cart" // it-IT
+ "|名前" // ja-JP
+ "|Имя.*карты" // ru
+ "|信用卡开户名|开户名|持卡人姓名" // zh-CN
+ "|持卡人姓名"; // zh-TW
+const char kNameOnCardContextualRe[] =
+ "name";
+const char kCardNumberRe[] =
+ "card.?number|card.?#|card.?no|cc.?num|acct.?num"
+ "|nummer" // de-DE
+ "|credito|numero|número" // es
+ "|numéro" // fr-FR
+ "|カード番号" // ja-JP
+ "|Номер.*карты" // ru
+ "|信用卡号|信用卡号码" // zh-CN
+ "|信用卡卡號" // zh-TW
+ "|카드"; // ko-KR
+const char kCardCvcRe[] =
+ "verification|card identification|security code|cvn|cvv|cvc|csc|\\bcid\\b";
+const char kCardTypeRe[] =
+ "card.?type|cc.?type|payment.?method";
+
+// "Expiration date" is the most common label here, but some pages have
+// "Expires", "exp. date" or "exp. month" and "exp. year". We also look
+// for the field names ccmonth and ccyear, which appear on at least 4 of
+// our test pages.
+
+// On at least one page (The China Shop2.html) we find only the labels
+// "month" and "year". So for now we match these words directly; we'll
+// see if this turns out to be too general.
+
+// Toolbar Bug 51451: indeed, simply matching "month" is too general for
+// https://rps.fidelity.com/ftgw/rps/RtlCust/CreatePIN/Init.
+// Instead, we match only words beginning with "month".
+const char kExpirationMonthRe[] =
+ "expir|exp.*mo|exp.*date|ccmonth|cardmonth"
+ "|gueltig|gültig|monat" // de-DE
+ "|fecha" // es
+ "|date.*exp" // fr-FR
+ "|scadenza" // it-IT
+ "|有効期限" // ja-JP
+ "|validade" // pt-BR, pt-PT
+ "|Срок действия карты" // ru
+ "|月"; // zh-CN
+const char kExpirationYearRe[] =
+ "exp|^/|year"
+ "|ablaufdatum|gueltig|gültig|yahr" // de-DE
+ "|fecha" // es
+ "|scadenza" // it-IT
+ "|有効期限" // ja-JP
+ "|validade" // pt-BR, pt-PT
+ "|Срок действия карты" // ru
+ "|年|有效期"; // zh-CN
+
+// This regex is a little bit nasty, but it is simply requiring exactly two
+// adjacent y's.
+const char kExpirationDate2DigitYearRe[] =
+ "exp.*date.*[^y]yy([^y]|$)";
+const char kExpirationDateRe[] =
+ "expir|exp.*date"
+ "|gueltig|gültig" // de-DE
+ "|fecha" // es
+ "|date.*exp" // fr-FR
+ "|scadenza" // it-IT
+ "|有効期限" // ja-JP
+ "|validade" // pt-BR, pt-PT
+ "|Срок действия карты"; // ru
+const char kCardIgnoredRe[] =
+ "^card";
+const char kGiftCardRe[] =
+ "gift.?card";
+
+
+/////////////////////////////////////////////////////////////////////////////
+// email_field.cc
+/////////////////////////////////////////////////////////////////////////////
+const char kEmailRe[] =
+ "e.?mail"
+ "|メールアドレス" // ja-JP
+ "|Электронной.?Почты" // ru
+ "|邮件|邮箱" // zh-CN
+ "|電郵地址" // zh-TW
+ "|(이메일|전자.?우편|[Ee]-?mail)(.?주소)?"; // ko-KR
+
+
+/////////////////////////////////////////////////////////////////////////////
+// name_field.cc
+/////////////////////////////////////////////////////////////////////////////
+const char kNameIgnoredRe[] =
+ "user.?name|user.?id|nickname|maiden name|title|prefix|suffix"
+ "|vollständiger.?name" // de-DE
+ "|用户名" // zh-CN
+ "|(사용자.?)?아이디|사용자.?ID"; // ko-KR
+const char kNameRe[] =
+ "^name|full.?name|your.?name|customer.?name|firstandlastname|bill.?name"
+ "|ship.?name"
+ "|nombre.*y.*apellidos" // es
+ "|^nom" // fr-FR
+ "|お名前|氏名" // ja-JP
+ "|^nome" // pt-BR, pt-PT
+ "|姓名" // zh-CN
+ "|성명"; // ko-KR
+const char kNameSpecificRe[] =
+ "^name"
+ "|^nom" // fr-FR
+ "|^nome"; // pt-BR, pt-PT
+const char kFirstNameRe[] =
+ "first.*name|initials|fname|first$"
+ "|vorname" // de-DE
+ "|nombre" // es
+ "|forename|prénom|prenom" // fr-FR
+ "|名" // ja-JP
+ "|nome" // pt-BR, pt-PT
+ "|Имя" // ru
+ "|이름"; // ko-KR
+const char kMiddleInitialRe[] = "middle.*initial|m\\.i\\.|mi$|\\bmi\\b";
+const char kMiddleNameRe[] =
+ "middle.*name|mname|middle$"
+ "|apellido.?materno|lastlastname"; // es
+const char kLastNameRe[] =
+ "last.*name|lname|surname|last$|secondname"
+ "|nachname" // de-DE
+ "|apellido" // es
+ "|famille|^nom" // fr-FR
+ "|cognome" // it-IT
+ "|姓" // ja-JP
+ "|morada|apelidos|surename|sobrenome" // pt-BR, pt-PT
+ "|Фамилия" // ru
+ "|성[^명]?"; // ko-KR
+
+/////////////////////////////////////////////////////////////////////////////
+// phone_field.cc
+/////////////////////////////////////////////////////////////////////////////
+const char kPhoneRe[] =
+ "phone|mobile"
+ "|telefonnummer" // de-DE
+ "|telefono|teléfono" // es
+ "|telfixe" // fr-FR
+ "|電話" // ja-JP
+ "|telefone|telemovel" // pt-BR, pt-PT
+ "|телефон" // ru
+ "|电话" // zh-CN
+ "|(전화|핸드폰|휴대폰|휴대전화)(.?번호)?"; // ko-KR
+const char kCountryCodeRe[] =
+ "country.*code|ccode|_cc";
+const char kAreaCodeNotextRe[] =
+ "^\\($";
+const char kAreaCodeRe[] =
+ "area.*code|acode|area"
+ "|지역.?번호"; // ko-KR
+const char kPhonePrefixSeparatorRe[] =
+ "^-$|^\\)$";
+const char kPhoneSuffixSeparatorRe[] =
+ "^-$";
+const char kPhonePrefixRe[] =
+ "prefix|exchange"
+ "|preselection" // fr-FR
+ "|ddd"; // pt-BR, pt-PT
+const char kPhoneSuffixRe[] =
+ "suffix";
+const char kPhoneExtensionRe[] =
+ "\\bext|ext\\b|extension"
+ "|ramal"; // pt-BR, pt-PT
+
+} // namespace autofill
diff --git a/components/autofill/browser/autofill_regex_constants.h b/components/autofill/browser/autofill_regex_constants.h
new file mode 100644
index 0000000..c2258c0
--- /dev/null
+++ b/components/autofill/browser/autofill_regex_constants.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_
+
+namespace autofill {
+
+extern const char kAttentionIgnoredRe[];
+extern const char kRegionIgnoredRe[];
+extern const char kCompanyRe[];
+extern const char kAddressLine1Re[];
+extern const char kAddressLine1LabelRe[];
+extern const char kAddressLine2Re[];
+extern const char kAddressLine2LabelRe[];
+extern const char kAddressLine3Re[];
+extern const char kCountryRe[];
+extern const char kZipCodeRe[];
+extern const char kZip4Re[];
+extern const char kCityRe[];
+extern const char kStateRe[];
+extern const char kAddressTypeSameAsRe[];
+extern const char kAddressTypeUseMyRe[];
+extern const char kBillingDesignatorRe[];
+extern const char kShippingDesignatorRe[];
+extern const char kNameOnCardRe[];
+extern const char kNameOnCardContextualRe[];
+extern const char kCardNumberRe[];
+extern const char kCardCvcRe[];
+extern const char kCardTypeRe[];
+extern const char kExpirationMonthRe[];
+extern const char kExpirationYearRe[];
+extern const char kExpirationDate2DigitYearRe[];
+extern const char kExpirationDateRe[];
+extern const char kCardIgnoredRe[];
+extern const char kGiftCardRe[];
+extern const char kEmailRe[];
+extern const char kNameIgnoredRe[];
+extern const char kNameRe[];
+extern const char kNameSpecificRe[];
+extern const char kFirstNameRe[];
+extern const char kMiddleInitialRe[];
+extern const char kMiddleNameRe[];
+extern const char kLastNameRe[];
+extern const char kPhoneRe[];
+extern const char kCountryCodeRe[];
+extern const char kAreaCodeNotextRe[];
+extern const char kAreaCodeRe[];
+extern const char kFaxRe[];
+extern const char kPhonePrefixSeparatorRe[];
+extern const char kPhoneSuffixSeparatorRe[];
+extern const char kPhonePrefixRe[];
+extern const char kPhoneSuffixRe[];
+extern const char kPhoneExtensionRe[];
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_
diff --git a/components/autofill/browser/autofill_regexes.cc b/components/autofill/browser/autofill_regexes.cc
new file mode 100644
index 0000000..64df56a
--- /dev/null
+++ b/components/autofill/browser/autofill_regexes.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 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/browser/autofill_regexes.h"
+
+#include <map>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/stl_util.h"
+#include "base/string16.h"
+#include "third_party/icu/public/i18n/unicode/regex.h"
+
+namespace {
+
+// A singleton class that serves as a cache of compiled regex patterns.
+class AutofillRegexes {
+ public:
+ static AutofillRegexes* GetInstance();
+
+ // Returns the compiled regex matcher corresponding to |pattern|.
+ icu::RegexMatcher* GetMatcher(const string16& pattern);
+
+ private:
+ AutofillRegexes();
+ ~AutofillRegexes();
+ friend struct DefaultSingletonTraits<AutofillRegexes>;
+
+ // Maps patterns to their corresponding regex matchers.
+ std::map<string16, icu::RegexMatcher*> matchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillRegexes);
+};
+
+// static
+AutofillRegexes* AutofillRegexes::GetInstance() {
+ return Singleton<AutofillRegexes>::get();
+}
+
+AutofillRegexes::AutofillRegexes() {
+}
+
+AutofillRegexes::~AutofillRegexes() {
+ STLDeleteContainerPairSecondPointers(matchers_.begin(),
+ matchers_.end());
+}
+
+icu::RegexMatcher* AutofillRegexes::GetMatcher(const string16& pattern) {
+ if (!matchers_.count(pattern)) {
+ const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
+
+ UErrorCode status = U_ZERO_ERROR;
+ icu::RegexMatcher* matcher = new icu::RegexMatcher(icu_pattern,
+ UREGEX_CASE_INSENSITIVE,
+ status);
+ DCHECK(U_SUCCESS(status));
+
+ matchers_.insert(std::make_pair(pattern, matcher));
+ }
+
+ return matchers_[pattern];
+}
+
+} // namespace
+
+namespace autofill {
+
+bool MatchesPattern(const string16& input, const string16& pattern) {
+ icu::RegexMatcher* matcher =
+ AutofillRegexes::GetInstance()->GetMatcher(pattern);
+ icu::UnicodeString icu_input(input.data(), input.length());
+ matcher->reset(icu_input);
+
+ UErrorCode status = U_ZERO_ERROR;
+ UBool match = matcher->find(0, status);
+ DCHECK(U_SUCCESS(status));
+ return !!match;
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/autofill_regexes.h b/components/autofill/browser/autofill_regexes.h
new file mode 100644
index 0000000..8d5a8f97
--- /dev/null
+++ b/components/autofill/browser/autofill_regexes.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_REGEXES_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_REGEXES_H_
+
+#include "base/string16.h"
+
+// Parsing utilities.
+namespace autofill {
+
+// Case-insensitive regular expression matching.
+// Returns true if |pattern| is found in |input|.
+bool MatchesPattern(const string16& input, const string16& pattern);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_REGEXES_H_
diff --git a/components/autofill/browser/autofill_regexes_unittest.cc b/components/autofill/browser/autofill_regexes_unittest.cc
new file mode 100644
index 0000000..1c2cbab
--- /dev/null
+++ b/components/autofill/browser/autofill_regexes_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/autofill_regexes.h"
+
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_regex_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(AutofillRegexesTest, AutofillRegexes) {
+ struct TestCase {
+ 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 (size_t i = 0; i < ARRAYSIZE_UNSAFE(kPositiveCases); ++i) {
+ const TestCase& test_case = kPositiveCases[i];
+ SCOPED_TRACE(test_case.input);
+ SCOPED_TRACE(test_case.pattern);
+ EXPECT_TRUE(autofill::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 (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNegativeCases); ++i) {
+ const TestCase& test_case = kNegativeCases[i];
+ SCOPED_TRACE(test_case.input);
+ SCOPED_TRACE(test_case.pattern);
+ EXPECT_FALSE(autofill::MatchesPattern(ASCIIToUTF16(test_case.input),
+ ASCIIToUTF16(test_case.pattern)));
+ }
+}
diff --git a/components/autofill/browser/autofill_scanner.cc b/components/autofill/browser/autofill_scanner.cc
new file mode 100644
index 0000000..fafdbab
--- /dev/null
+++ b/components/autofill/browser/autofill_scanner.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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/browser/autofill_scanner.h"
+
+#include "base/logging.h"
+#include "components/autofill/browser/autofill_field.h"
+
+AutofillScanner::AutofillScanner(
+ const std::vector<const AutofillField*>& fields)
+ : cursor_(fields.begin()),
+ saved_cursor_(fields.begin()),
+ begin_(fields.begin()),
+ end_(fields.end()) {
+}
+
+AutofillScanner::~AutofillScanner() {
+}
+
+void AutofillScanner::Advance() {
+ DCHECK(!IsEnd());
+ ++cursor_;
+}
+
+const AutofillField* AutofillScanner::Cursor() const {
+ if (IsEnd()) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return *cursor_;
+}
+
+bool AutofillScanner::IsEnd() const {
+ return cursor_ == end_;
+}
+
+void AutofillScanner::Rewind() {
+ DCHECK(saved_cursor_ != end_);
+ cursor_ = saved_cursor_;
+ saved_cursor_ = end_;
+}
+
+void AutofillScanner::RewindTo(size_t index) {
+ DCHECK(index < static_cast<size_t>(end_ - begin_));
+ cursor_ = begin_ + index;
+ saved_cursor_ = end_;
+}
+
+size_t AutofillScanner::SaveCursor() {
+ saved_cursor_ = cursor_;
+ return static_cast<size_t>(cursor_ - begin_);
+}
diff --git a/components/autofill/browser/autofill_scanner.h b/components/autofill/browser/autofill_scanner.h
new file mode 100644
index 0000000..c099910
--- /dev/null
+++ b/components/autofill/browser/autofill_scanner.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_SCANNER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_SCANNER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+
+class AutofillField;
+
+// A helper class for parsing a stream of |AutofillField|'s with lookahead.
+class AutofillScanner {
+ public:
+ explicit AutofillScanner(const std::vector<const AutofillField*>& fields);
+ ~AutofillScanner();
+
+ // Advances the cursor by one step, if possible.
+ void Advance();
+
+ // Returns the current field in the stream, or |NULL| if there are no more
+ // fields in the stream.
+ const AutofillField* Cursor() const;
+
+ // Returns |true| if the cursor has reached the end of the stream.
+ bool IsEnd() const;
+
+ // Restores the most recently saved cursor. See also |SaveCursor()|.
+ void Rewind();
+
+ // Repositions the cursor to the specified |index|. See also |SaveCursor()|.
+ void RewindTo(size_t index);
+
+ // Saves and returns the current cursor position. See also |Rewind()| and
+ // |RewindTo()|.
+ size_t SaveCursor();
+
+ private:
+ // Indicates the current position in the stream, represented as a vector.
+ std::vector<const AutofillField*>::const_iterator cursor_;
+
+ // The most recently saved cursor.
+ std::vector<const AutofillField*>::const_iterator saved_cursor_;
+
+ // The beginning pointer for the stream.
+ const std::vector<const AutofillField*>::const_iterator begin_;
+
+ // The past-the-end pointer for the stream.
+ const std::vector<const AutofillField*>::const_iterator end_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillScanner);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_SCANNER_H_
diff --git a/components/autofill/browser/autofill_server_field_info.h b/components/autofill/browser/autofill_server_field_info.h
new file mode 100644
index 0000000..34f7ea6
--- /dev/null
+++ b/components/autofill/browser/autofill_server_field_info.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_SERVER_FIELD_INFO_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_SERVER_FIELD_INFO_H_
+
+#include <string>
+
+#include "components/autofill/browser/field_types.h"
+
+struct AutofillServerFieldInfo {
+ // The predicted type returned by the Autofill server for this field.
+ AutofillFieldType field_type;
+ // Default value to be used for the field (only applies to
+ // FIELD_WITH_DEFAULT_TYPE field type)
+ std::string default_value;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_SERVER_FIELD_INFO_H_
diff --git a/components/autofill/browser/autofill_type.cc b/components/autofill/browser/autofill_type.cc
new file mode 100644
index 0000000..a715cbc
--- /dev/null
+++ b/components/autofill/browser/autofill_type.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2011 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/browser/autofill_type.h"
+
+#include <ostream>
+
+#include "base/logging.h"
+
+AutofillType::AutofillType(AutofillFieldType field_type) {
+ if ((field_type < NO_SERVER_DATA || field_type >= MAX_VALID_FIELD_TYPE) ||
+ (field_type >= 15 && field_type <= 19) ||
+ (field_type >= 25 && field_type <= 29) ||
+ (field_type >= 44 && field_type <= 50))
+ field_type_ = UNKNOWN_TYPE;
+ else
+ field_type_ = field_type;
+}
+
+AutofillType::AutofillType(const AutofillType& autofill_type) {
+ *this = autofill_type;
+}
+
+AutofillType& AutofillType::operator=(const AutofillType& autofill_type) {
+ if (this != &autofill_type)
+ this->field_type_ = autofill_type.field_type_;
+ return *this;
+}
+
+AutofillFieldType AutofillType::field_type() const {
+ return field_type_;
+}
+
+FieldTypeGroup AutofillType::group() const {
+ switch (field_type_) {
+ case NAME_FIRST:
+ case NAME_MIDDLE:
+ case NAME_LAST:
+ case NAME_MIDDLE_INITIAL:
+ case NAME_FULL:
+ case NAME_SUFFIX:
+ return NAME;
+
+ case EMAIL_ADDRESS:
+ return EMAIL;
+
+ case PHONE_HOME_NUMBER:
+ case PHONE_HOME_CITY_CODE:
+ case PHONE_HOME_COUNTRY_CODE:
+ case PHONE_HOME_CITY_AND_NUMBER:
+ case PHONE_HOME_WHOLE_NUMBER:
+ return PHONE;
+
+ case ADDRESS_HOME_LINE1:
+ case ADDRESS_HOME_LINE2:
+ case ADDRESS_HOME_APT_NUM:
+ case ADDRESS_HOME_CITY:
+ case ADDRESS_HOME_STATE:
+ case ADDRESS_HOME_ZIP:
+ case ADDRESS_HOME_COUNTRY:
+ return ADDRESS_HOME;
+
+ case ADDRESS_BILLING_LINE1:
+ case ADDRESS_BILLING_LINE2:
+ case ADDRESS_BILLING_APT_NUM:
+ case ADDRESS_BILLING_CITY:
+ case ADDRESS_BILLING_STATE:
+ case ADDRESS_BILLING_ZIP:
+ case ADDRESS_BILLING_COUNTRY:
+ return ADDRESS_BILLING;
+
+ case CREDIT_CARD_NAME:
+ case CREDIT_CARD_NUMBER:
+ case CREDIT_CARD_EXP_MONTH:
+ case CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ case CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ case CREDIT_CARD_TYPE:
+ case CREDIT_CARD_VERIFICATION_CODE:
+ return CREDIT_CARD;
+
+ case COMPANY_NAME:
+ return COMPANY;
+
+ default:
+ return NO_GROUP;
+ }
+}
+
+// static
+AutofillFieldType AutofillType::GetEquivalentFieldType(
+ AutofillFieldType field_type) {
+ // When billing information is requested from the profile we map to the
+ // home address equivalents.
+ switch (field_type) {
+ case ADDRESS_BILLING_LINE1:
+ return ADDRESS_HOME_LINE1;
+
+ case ADDRESS_BILLING_LINE2:
+ return ADDRESS_HOME_LINE2;
+
+ case ADDRESS_BILLING_APT_NUM:
+ return ADDRESS_HOME_APT_NUM;
+
+ case ADDRESS_BILLING_CITY:
+ return ADDRESS_HOME_CITY;
+
+ case ADDRESS_BILLING_STATE:
+ return ADDRESS_HOME_STATE;
+
+ case ADDRESS_BILLING_ZIP:
+ return ADDRESS_HOME_ZIP;
+
+ case ADDRESS_BILLING_COUNTRY:
+ return ADDRESS_HOME_COUNTRY;
+
+ default:
+ return field_type;
+ }
+}
+
+// static
+std::string AutofillType::FieldTypeToString(AutofillFieldType type) {
+ switch (type) {
+ case NO_SERVER_DATA:
+ return "NO_SERVER_DATA";
+ case UNKNOWN_TYPE:
+ return "UNKNOWN_TYPE";
+ case EMPTY_TYPE:
+ return "EMPTY_TYPE";
+ case NAME_FIRST:
+ return "NAME_FIRST";
+ case NAME_MIDDLE:
+ return "NAME_MIDDLE";
+ case NAME_LAST:
+ return "NAME_LAST";
+ case NAME_MIDDLE_INITIAL:
+ return "NAME_MIDDLE_INITIAL";
+ case NAME_FULL:
+ return "NAME_FULL";
+ case NAME_SUFFIX:
+ return "NAME_SUFFIX";
+ case EMAIL_ADDRESS:
+ return "EMAIL_ADDRESS";
+ case PHONE_HOME_NUMBER:
+ return "PHONE_HOME_NUMBER";
+ case PHONE_HOME_CITY_CODE:
+ return "PHONE_HOME_CITY_CODE";
+ case PHONE_HOME_COUNTRY_CODE:
+ return "PHONE_HOME_COUNTRY_CODE";
+ case PHONE_HOME_CITY_AND_NUMBER:
+ return "PHONE_HOME_CITY_AND_NUMBER";
+ case PHONE_HOME_WHOLE_NUMBER:
+ return "PHONE_HOME_WHOLE_NUMBER";
+ case ADDRESS_HOME_LINE1:
+ return "ADDRESS_HOME_LINE1";
+ case ADDRESS_HOME_LINE2:
+ return "ADDRESS_HOME_LINE2";
+ case ADDRESS_HOME_APT_NUM:
+ return "ADDRESS_HOME_APT_NUM";
+ case ADDRESS_HOME_CITY:
+ return "ADDRESS_HOME_CITY";
+ case ADDRESS_HOME_STATE:
+ return "ADDRESS_HOME_STATE";
+ case ADDRESS_HOME_ZIP:
+ return "ADDRESS_HOME_ZIP";
+ case ADDRESS_HOME_COUNTRY:
+ return "ADDRESS_HOME_COUNTRY";
+ case ADDRESS_BILLING_LINE1:
+ return "ADDRESS_BILLING_LINE1";
+ case ADDRESS_BILLING_LINE2:
+ return "ADDRESS_BILLING_LINE2";
+ case ADDRESS_BILLING_APT_NUM:
+ return "ADDRESS_BILLING_APT_NUM";
+ case ADDRESS_BILLING_CITY:
+ return "ADDRESS_BILLING_CITY";
+ case ADDRESS_BILLING_STATE:
+ return "ADDRESS_BILLING_STATE";
+ case ADDRESS_BILLING_ZIP:
+ return "ADDRESS_BILLING_ZIP";
+ case ADDRESS_BILLING_COUNTRY:
+ return "ADDRESS_BILLING_COUNTRY";
+ case CREDIT_CARD_NAME:
+ return "CREDIT_CARD_NAME";
+ case CREDIT_CARD_NUMBER:
+ return "CREDIT_CARD_NUMBER";
+ case CREDIT_CARD_EXP_MONTH:
+ return "CREDIT_CARD_EXP_MONTH";
+ case CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ return "CREDIT_CARD_EXP_2_DIGIT_YEAR";
+ case CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ return "CREDIT_CARD_EXP_4_DIGIT_YEAR";
+ case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ return "CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR";
+ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ return "CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR";
+ case CREDIT_CARD_TYPE:
+ return "CREDIT_CARD_TYPE";
+ case CREDIT_CARD_VERIFICATION_CODE:
+ return "CREDIT_CARD_VERIFICATION_CODE";
+ case COMPANY_NAME:
+ return "COMPANY_NAME";
+ default:
+ NOTREACHED() << "Invalid AutofillFieldType value.";
+ }
+ return std::string();
+}
+
+// static
+AutofillFieldType AutofillType::StringToFieldType(const std::string& str) {
+ if (str == "NO_SERVER_DATA")
+ return NO_SERVER_DATA;
+ if (str == "UNKNOWN_TYPE")
+ return UNKNOWN_TYPE;
+ if (str == "EMPTY_TYPE")
+ return EMPTY_TYPE;
+ if (str == "NAME_FIRST")
+ return NAME_FIRST;
+ if (str == "NAME_MIDDLE")
+ return NAME_MIDDLE;
+ if (str == "NAME_LAST")
+ return NAME_LAST;
+ if (str == "NAME_MIDDLE_INITIAL")
+ return NAME_MIDDLE_INITIAL;
+ if (str == "NAME_FULL")
+ return NAME_FULL;
+ if (str == "NAME_SUFFIX")
+ return NAME_SUFFIX;
+ if (str == "EMAIL_ADDRESS")
+ return EMAIL_ADDRESS;
+ if (str == "PHONE_HOME_NUMBER")
+ return PHONE_HOME_NUMBER;
+ if (str == "PHONE_HOME_CITY_CODE")
+ return PHONE_HOME_CITY_CODE;
+ if (str == "PHONE_HOME_COUNTRY_CODE")
+ return PHONE_HOME_COUNTRY_CODE;
+ if (str == "PHONE_HOME_CITY_AND_NUMBER")
+ return PHONE_HOME_CITY_AND_NUMBER;
+ if (str == "PHONE_HOME_WHOLE_NUMBER")
+ return PHONE_HOME_WHOLE_NUMBER;
+ if (str == "ADDRESS_HOME_LINE1")
+ return ADDRESS_HOME_LINE1;
+ if (str == "ADDRESS_HOME_LINE2")
+ return ADDRESS_HOME_LINE2;
+ if (str == "ADDRESS_HOME_APT_NUM")
+ return ADDRESS_HOME_APT_NUM;
+ if (str == "ADDRESS_HOME_CITY")
+ return ADDRESS_HOME_CITY;
+ if (str == "ADDRESS_HOME_STATE")
+ return ADDRESS_HOME_STATE;
+ if (str == "ADDRESS_HOME_ZIP")
+ return ADDRESS_HOME_ZIP;
+ if (str == "ADDRESS_HOME_COUNTRY")
+ return ADDRESS_HOME_COUNTRY;
+ if (str == "ADDRESS_BILLING_LINE1")
+ return ADDRESS_BILLING_LINE1;
+ if (str == "ADDRESS_BILLING_LINE2")
+ return ADDRESS_BILLING_LINE2;
+ if (str == "ADDRESS_BILLING_APT_NUM")
+ return ADDRESS_BILLING_APT_NUM;
+ if (str == "ADDRESS_BILLING_CITY")
+ return ADDRESS_BILLING_CITY;
+ if (str == "ADDRESS_BILLING_STATE")
+ return ADDRESS_BILLING_STATE;
+ if (str == "ADDRESS_BILLING_ZIP")
+ return ADDRESS_BILLING_ZIP;
+ if (str == "ADDRESS_BILLING_COUNTRY")
+ return ADDRESS_BILLING_COUNTRY;
+ if (str == "CREDIT_CARD_NAME")
+ return CREDIT_CARD_NAME;
+ if (str == "CREDIT_CARD_NUMBER")
+ return CREDIT_CARD_NUMBER;
+ if (str == "CREDIT_CARD_EXP_MONTH")
+ return CREDIT_CARD_EXP_MONTH;
+ if (str == "CREDIT_CARD_EXP_2_DIGIT_YEAR")
+ return CREDIT_CARD_EXP_2_DIGIT_YEAR;
+ if (str == "CREDIT_CARD_EXP_4_DIGIT_YEAR")
+ return CREDIT_CARD_EXP_4_DIGIT_YEAR;
+ if (str == "CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR")
+ return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
+ if (str == "CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR")
+ return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
+ if (str == "CREDIT_CARD_TYPE")
+ return CREDIT_CARD_TYPE;
+ if (str == "CREDIT_CARD_VERIFICATION_CODE")
+ return CREDIT_CARD_VERIFICATION_CODE;
+ if (str == "COMPANY_NAME")
+ return COMPANY_NAME;
+
+ NOTREACHED() << "Unknown AutofillFieldType " << str;
+ return UNKNOWN_TYPE;
+}
diff --git a/components/autofill/browser/autofill_type.h b/components/autofill/browser/autofill_type.h
new file mode 100644
index 0000000..a199c24
--- /dev/null
+++ b/components/autofill/browser/autofill_type.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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_BROWSER_AUTOFILL_TYPE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_TYPE_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/string16.h"
+#include "components/autofill/browser/field_types.h"
+
+// The high-level description of Autofill types, used to categorize form fields
+// and for associating form fields with form values in the Web Database.
+class AutofillType {
+ public:
+ enum FieldTypeGroup {
+ NO_GROUP,
+ NAME,
+ EMAIL,
+ COMPANY,
+ ADDRESS_HOME,
+ ADDRESS_BILLING,
+ PHONE,
+ CREDIT_CARD,
+ };
+
+ explicit AutofillType(AutofillFieldType field_type);
+ AutofillType(const AutofillType& autofill_type);
+ AutofillType& operator=(const AutofillType& autofill_type);
+
+ AutofillFieldType field_type() const;
+ FieldTypeGroup group() const;
+
+ // Maps |field_type| to a field type that can be directly stored in a profile
+ // (in the sense that it makes sense to call |AutofillProfile::SetInfo()| with
+ // the returned field type as the first parameter).
+ static AutofillFieldType GetEquivalentFieldType(AutofillFieldType field_type);
+
+ // Utilities for serializing and deserializing an |AutofillFieldType|.
+ static std::string FieldTypeToString(AutofillFieldType field_type);
+ static AutofillFieldType StringToFieldType(const std::string& str);
+
+ private:
+ AutofillFieldType field_type_;
+};
+
+typedef AutofillType::FieldTypeGroup FieldTypeGroup;
+typedef std::set<AutofillFieldType> FieldTypeSet;
+typedef std::map<string16, AutofillFieldType> FieldTypeMap;
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_TYPE_H_
diff --git a/components/autofill/browser/autofill_type_unittest.cc b/components/autofill/browser/autofill_type_unittest.cc
new file mode 100644
index 0000000..12654aa
--- /dev/null
+++ b/components/autofill/browser/autofill_type_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 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/browser/autofill_type.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(AutofillTypeTest, AutofillTypes) {
+ // No server data.
+ AutofillType none(NO_SERVER_DATA);
+ EXPECT_EQ(NO_SERVER_DATA, none.field_type());
+ EXPECT_EQ(AutofillType::NO_GROUP, none.group());
+
+ // Unknown type.
+ AutofillType unknown(UNKNOWN_TYPE);
+ EXPECT_EQ(UNKNOWN_TYPE, unknown.field_type());
+ EXPECT_EQ(AutofillType::NO_GROUP, unknown.group());
+
+ // Type with group but no subgroup.
+ AutofillType first(NAME_FIRST);
+ EXPECT_EQ(NAME_FIRST, first.field_type());
+ EXPECT_EQ(AutofillType::NAME, first.group());
+
+ // Type with group and subgroup.
+ AutofillType phone(PHONE_HOME_NUMBER);
+ EXPECT_EQ(PHONE_HOME_NUMBER, phone.field_type());
+ EXPECT_EQ(AutofillType::PHONE, phone.group());
+
+ // Last value, to check any offset errors.
+ AutofillType last(COMPANY_NAME);
+ EXPECT_EQ(COMPANY_NAME, last.field_type());
+ EXPECT_EQ(AutofillType::COMPANY, last.group());
+
+ // Boundary (error) condition.
+ AutofillType boundary(MAX_VALID_FIELD_TYPE);
+ EXPECT_EQ(UNKNOWN_TYPE, boundary.field_type());
+ EXPECT_EQ(AutofillType::NO_GROUP, boundary.group());
+
+ // Beyond the boundary (error) condition.
+ AutofillType beyond(static_cast<AutofillFieldType>(MAX_VALID_FIELD_TYPE+10));
+ EXPECT_EQ(UNKNOWN_TYPE, beyond.field_type());
+ EXPECT_EQ(AutofillType::NO_GROUP, beyond.group());
+
+ // In-between value. Missing from enum but within range. Error condition.
+ AutofillType between(static_cast<AutofillFieldType>(16));
+ EXPECT_EQ(UNKNOWN_TYPE, between.field_type());
+ EXPECT_EQ(AutofillType::NO_GROUP, between.group());
+}
+
+} // namespace
diff --git a/components/autofill/browser/autofill_xml_parser.cc b/components/autofill/browser/autofill_xml_parser.cc
new file mode 100644
index 0000000..a4e5a3d
--- /dev/null
+++ b/components/autofill/browser/autofill_xml_parser.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2011 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/browser/autofill_xml_parser.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "components/autofill/browser/autofill_server_field_info.h"
+#include "third_party/libjingle/source/talk/xmllite/qname.h"
+
+AutofillXmlParser::AutofillXmlParser()
+ : succeeded_(true) {
+}
+
+AutofillXmlParser::~AutofillXmlParser() {}
+
+void AutofillXmlParser::CharacterData(
+ buzz::XmlParseContext* context, const char* text, int len) {
+}
+
+void AutofillXmlParser::EndElement(buzz::XmlParseContext* context,
+ const char* name) {
+}
+
+void AutofillXmlParser::Error(buzz::XmlParseContext* context,
+ XML_Error error_code) {
+ succeeded_ = false;
+}
+
+AutofillQueryXmlParser::AutofillQueryXmlParser(
+ std::vector<AutofillServerFieldInfo>* field_infos,
+ UploadRequired* upload_required,
+ std::string* experiment_id)
+ : field_infos_(field_infos),
+ upload_required_(upload_required),
+ current_page_number_(-1),
+ total_pages_(-1),
+ experiment_id_(experiment_id) {
+ DCHECK(upload_required_);
+ DCHECK(experiment_id_);
+}
+
+AutofillQueryXmlParser::~AutofillQueryXmlParser() {}
+
+void AutofillQueryXmlParser::StartElement(buzz::XmlParseContext* context,
+ const char* name,
+ const char** attrs) {
+ buzz::QName qname = context->ResolveQName(name, false);
+ const std::string& element = qname.LocalPart();
+ if (element.compare("autofillqueryresponse") == 0) {
+ // We check for the upload required attribute below, but if it's not
+ // present, we use the default upload rates. Likewise, by default we assume
+ // an empty experiment id.
+ *upload_required_ = USE_UPLOAD_RATES;
+ *experiment_id_ = std::string();
+
+ // |attrs| is a NULL-terminated list of (attribute, value) pairs.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
+ ++attrs;
+ const std::string& attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("uploadrequired") == 0) {
+ if (strcmp(*attrs, "true") == 0)
+ *upload_required_ = UPLOAD_REQUIRED;
+ else if (strcmp(*attrs, "false") == 0)
+ *upload_required_ = UPLOAD_NOT_REQUIRED;
+ } else if (attribute_name.compare("experimentid") == 0) {
+ *experiment_id_ = *attrs;
+ }
+ ++attrs;
+ }
+ } else if (element.compare("field") == 0) {
+ if (!*attrs) {
+ // Missing the "autofilltype" attribute, abort.
+ context->RaiseError(XML_ERROR_ABORTED);
+ return;
+ }
+
+ // Determine the field type from the attribute value. There should be one
+ // attribute (autofilltype) with an integer value.
+ AutofillServerFieldInfo field_info;
+ field_info.field_type = UNKNOWN_TYPE;
+
+ // |attrs| is a NULL-terminated list of (attribute, value) pairs.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
+ ++attrs;
+ const std::string& attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("autofilltype") == 0) {
+ int value = GetIntValue(context, *attrs);
+ if (value >= 0 && value < MAX_VALID_FIELD_TYPE)
+ field_info.field_type = static_cast<AutofillFieldType>(value);
+ else
+ field_info.field_type = NO_SERVER_DATA;
+ } else if (field_info.field_type == FIELD_WITH_DEFAULT_VALUE &&
+ attribute_name.compare("defaultvalue") == 0) {
+ field_info.default_value = *attrs;
+ }
+ ++attrs;
+ }
+
+ // Record this field type, default value pair.
+ field_infos_->push_back(field_info);
+ } else if (element.compare("autofill_flow") == 0) {
+ // |attrs| is a NULL-terminated list of (attribute, value) pairs.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
+ ++attrs;
+ const std::string& attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("page_no") == 0)
+ current_page_number_ = GetIntValue(context, *attrs);
+ else if (attribute_name.compare("total_pages") == 0)
+ total_pages_ = GetIntValue(context, *attrs);
+ ++attrs;
+ }
+ } else if (element.compare("page_advance_button") == 0) {
+ // |attrs| is a NULL-terminated list of (attribute, value) pairs.
+ // If both id and css_selector are set, the first one to appear will take
+ // precedence.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(*attrs, true);
+ ++attrs;
+ const std::string& attribute_name = attribute_qname.LocalPart();
+ buzz::QName value_qname = context->ResolveQName(*attrs, true);
+ ++attrs;
+ const std::string& attribute_value = value_qname.LocalPart();
+ if (attribute_name.compare("id") == 0 && !attribute_value.empty()) {
+ proceed_element_descriptor_.reset(new autofill::WebElementDescriptor());
+ proceed_element_descriptor_->retrieval_method =
+ autofill::WebElementDescriptor::ID;
+ proceed_element_descriptor_->descriptor = attribute_value;
+ break;
+ } else if (attribute_name.compare("css_selector") == 0 &&
+ !attribute_value.empty()) {
+ proceed_element_descriptor_.reset(new autofill::WebElementDescriptor());
+ proceed_element_descriptor_->retrieval_method =
+ autofill::WebElementDescriptor::CSS_SELECTOR;
+ proceed_element_descriptor_->descriptor = attribute_value;
+ break;
+ }
+ }
+ }
+}
+
+int AutofillQueryXmlParser::GetIntValue(buzz::XmlParseContext* context,
+ const char* attribute) {
+ char* attr_end = NULL;
+ int value = strtol(attribute, &attr_end, 10);
+ if (attr_end != NULL && attr_end == attribute) {
+ context->RaiseError(XML_ERROR_SYNTAX);
+ return 0;
+ }
+ return value;
+}
+
+AutofillUploadXmlParser::AutofillUploadXmlParser(double* positive_upload_rate,
+ double* negative_upload_rate)
+ : succeeded_(false),
+ positive_upload_rate_(positive_upload_rate),
+ negative_upload_rate_(negative_upload_rate) {
+ DCHECK(positive_upload_rate_);
+ DCHECK(negative_upload_rate_);
+}
+
+void AutofillUploadXmlParser::StartElement(buzz::XmlParseContext* context,
+ const char* name,
+ const char** attrs) {
+ buzz::QName qname = context->ResolveQName(name, false);
+ const std::string &element = qname.LocalPart();
+ if (element.compare("autofilluploadresponse") == 0) {
+ // Loop over all attributes to get the upload rates.
+ while (*attrs) {
+ buzz::QName attribute_qname = context->ResolveQName(attrs[0], true);
+ const std::string &attribute_name = attribute_qname.LocalPart();
+ if (attribute_name.compare("positiveuploadrate") == 0) {
+ *positive_upload_rate_ = GetDoubleValue(context, attrs[1]);
+ } else if (attribute_name.compare("negativeuploadrate") == 0) {
+ *negative_upload_rate_ = GetDoubleValue(context, attrs[1]);
+ }
+ attrs += 2; // We peeked at attrs[0] and attrs[1], skip past both.
+ }
+ }
+}
+
+double AutofillUploadXmlParser::GetDoubleValue(buzz::XmlParseContext* context,
+ const char* attribute) {
+ char* attr_end = NULL;
+ double value = strtod(attribute, &attr_end);
+ if (attr_end != NULL && attr_end == attribute) {
+ context->RaiseError(XML_ERROR_SYNTAX);
+ return 0.0;
+ }
+ return value;
+}
diff --git a/components/autofill/browser/autofill_xml_parser.h b/components/autofill/browser/autofill_xml_parser.h
new file mode 100644
index 0000000..fc914f3
--- /dev/null
+++ b/components/autofill/browser/autofill_xml_parser.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_XML_PARSER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_XML_PARSER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/browser/autofill_server_field_info.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/common/web_element_descriptor.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
+
+// The base class that contains common functionality between
+// AutofillQueryXmlParser and AutofillUploadXmlParser.
+class AutofillXmlParser : public buzz::XmlParseHandler {
+ public:
+ AutofillXmlParser();
+ virtual ~AutofillXmlParser();
+
+ // Returns true if no parsing errors were encountered.
+ bool succeeded() const { return succeeded_; }
+
+ private:
+ // A callback for the end of an </element>, called by Expat.
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |name| is the name of the element.
+ virtual void EndElement(buzz::XmlParseContext* context,
+ const char* name) OVERRIDE;
+
+ // The callback for character data between tags (<element>text...</element>).
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |text| is a pointer to the beginning of character data (not null
+ // terminated).
+ // |len| is the length of the string pointed to by text.
+ virtual void CharacterData(buzz::XmlParseContext* context,
+ const char* text,
+ int len) OVERRIDE;
+
+ // The callback for parsing errors.
+ // |context| is a parsing context used to resolve names.
+ // |error_code| is a code representing the parsing error.
+ virtual void Error(buzz::XmlParseContext* context,
+ XML_Error error_code) OVERRIDE;
+
+ // True if parsing succeeded.
+ bool succeeded_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillXmlParser);
+};
+
+// The XML parse handler for parsing Autofill query responses. A typical
+// response looks like:
+//
+// <autofillqueryresponse experimentid="1">
+// <field autofilltype="0" />
+// <field autofilltype="1" />
+// <field autofilltype="3" />
+// <field autofilltype="2" />
+// </autofillqueryresponse>
+//
+// Fields are returned in the same order they were sent to the server.
+// autofilltype: The server's guess at what type of field this is. 0
+// is unknown, other types are documented in
+// components/autofill/browser/field_types.h.
+class AutofillQueryXmlParser : public AutofillXmlParser {
+ public:
+ AutofillQueryXmlParser(std::vector<AutofillServerFieldInfo>* field_infos,
+ UploadRequired* upload_required,
+ std::string* experiment_id);
+ virtual ~AutofillQueryXmlParser();
+
+ int current_page_number() const { return current_page_number_; }
+
+ int total_pages() const { return total_pages_; }
+
+ // Returns the proceed element for multipage Autofill flows if the current
+ // page is part of such a flow or NULL otherwise.
+ const autofill::WebElementDescriptor* proceed_element_descriptor() const {
+ return proceed_element_descriptor_.get();
+ }
+
+ private:
+ // A callback for the beginning of a new <element>, called by Expat.
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |name| is the name of the element.
+ // |attrs| is the list of attributes (names and values) for the element.
+ virtual void StartElement(buzz::XmlParseContext* context,
+ const char* name,
+ const char** attrs) OVERRIDE;
+
+ // A helper function to retrieve integer values from strings. Raises an
+ // XML parse error if it fails.
+ // |context| is the current parsing context.
+ // |value| is the string to convert.
+ int GetIntValue(buzz::XmlParseContext* context, const char* attribute);
+
+ // The parsed <field type, default value> pairs.
+ std::vector<AutofillServerFieldInfo>* field_infos_;
+
+ // A flag indicating whether the client should upload Autofill data when this
+ // form is submitted.
+ UploadRequired* upload_required_;
+
+ // Page number of present page in multipage autofill flow.
+ int current_page_number_;
+
+ // Total number of pages in multipage autofill flow.
+ int total_pages_;
+
+ // Proceed element for multipage Autofill flow.
+ scoped_ptr<autofill::WebElementDescriptor> proceed_element_descriptor_;
+
+ // The server experiment to which this query response belongs.
+ // For the default server implementation, this is empty.
+ std::string* experiment_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillQueryXmlParser);
+};
+
+// The XML parser for handling Autofill upload responses. Typical upload
+// responses look like:
+//
+// <autofilluploadresponse negativeuploadrate="0.00125" positiveuploadrate="1"/>
+//
+// The positive upload rate is the percentage of uploads to send to the server
+// when something in the users profile matches what they have entered in a form.
+// The negative upload rate is the percentage of uploads to send when nothing in
+// the form matches what's in the users profile.
+// The negative upload rate is typically much lower than the positive upload
+// rate.
+class AutofillUploadXmlParser : public AutofillXmlParser {
+ public:
+ AutofillUploadXmlParser(double* positive_upload_rate,
+ double* negative_upload_rate);
+
+ private:
+ // A callback for the beginning of a new <element>, called by Expat.
+ // |context| is a parsing context used to resolve element/attribute names.
+ // |name| is the name of the element.
+ // |attrs| is the list of attributes (names and values) for the element.
+ virtual void StartElement(buzz::XmlParseContext* context,
+ const char* name,
+ const char** attrs) OVERRIDE;
+
+ // A helper function to retrieve double values from strings. Raises an XML
+ // parse error if it fails.
+ // |context| is the current parsing context.
+ // |value| is the string to convert.
+ double GetDoubleValue(buzz::XmlParseContext* context, const char* attribute);
+
+ // True if parsing succeeded.
+ bool succeeded_;
+
+ double* positive_upload_rate_;
+ double* negative_upload_rate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillUploadXmlParser);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOFILL_XML_PARSER_H_
diff --git a/components/autofill/browser/autofill_xml_parser_unittest.cc b/components/autofill/browser/autofill_xml_parser_unittest.cc
new file mode 100644
index 0000000..3649e54
--- /dev/null
+++ b/components/autofill/browser/autofill_xml_parser_unittest.cc
@@ -0,0 +1,367 @@
+// Copyright (c) 2011 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 <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/autofill/browser/autofill_xml_parser.h"
+#include "components/autofill/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
+
+namespace {
+
+TEST(AutofillQueryXmlParserTest, BasicQuery) {
+ // An XML string representing a basic query response.
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "<field autofilltype=\"1\" />"
+ "<field autofilltype=\"3\" />"
+ "<field autofilltype=\"2\" />"
+ "<field autofilltype=\"61\" defaultvalue=\"default\"/>"
+ "</autofillqueryresponse>";
+
+ // Create a vector of AutofillServerFieldInfos, to assign the parsed field
+ // types to.
+ std::vector<AutofillServerFieldInfo> field_infos;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+ std::string experiment_id;
+
+ // Create a parser.
+ AutofillQueryXmlParser parse_handler(&field_infos, &upload_required,
+ &experiment_id);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler.succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(5U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(UNKNOWN_TYPE, field_infos[1].field_type);
+ EXPECT_EQ(NAME_FIRST, field_infos[2].field_type);
+ EXPECT_EQ(EMPTY_TYPE, field_infos[3].field_type);
+ EXPECT_EQ("", field_infos[3].default_value);
+ EXPECT_EQ(FIELD_WITH_DEFAULT_VALUE, field_infos[4].field_type);
+ EXPECT_EQ("default", field_infos[4].default_value);
+ EXPECT_EQ(std::string(), experiment_id);
+}
+
+// Test parsing the upload required attribute.
+TEST(AutofillQueryXmlParserTest, TestUploadRequired) {
+ std::vector<AutofillServerFieldInfo> field_infos;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+ std::string experiment_id;
+
+ std::string xml = "<autofillqueryresponse uploadrequired=\"true\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutofillQueryXmlParser> parse_handler(
+ new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(UPLOAD_REQUIRED, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+
+ field_infos.clear();
+ xml = "<autofillqueryresponse uploadrequired=\"false\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(UPLOAD_NOT_REQUIRED, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+
+ field_infos.clear();
+ xml = "<autofillqueryresponse uploadrequired=\"bad_value\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+}
+
+// Test parsing the experiment id attribute
+TEST(AutofillQueryXmlParserTest, ParseExperimentId) {
+ std::vector<AutofillServerFieldInfo> field_infos;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+ std::string experiment_id;
+
+ // When the attribute is missing, we should get back the default value -- the
+ // empty string.
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutofillQueryXmlParser> parse_handler(
+ new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+
+ field_infos.clear();
+
+ // When the attribute is present, make sure we parse it.
+ xml = "<autofillqueryresponse experimentid=\"FancyNewAlgorithm\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string("FancyNewAlgorithm"), experiment_id);
+
+ field_infos.clear();
+
+ // Make sure that we can handle parsing both the upload required and the
+ // experiment id attribute together.
+ xml = "<autofillqueryresponse uploadrequired=\"false\""
+ " experimentid=\"ServerSmartyPants\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(UPLOAD_NOT_REQUIRED, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string("ServerSmartyPants"), experiment_id);
+}
+
+// Test XML response with autofill_flow information.
+TEST(AutofillQueryXmlParserTest, ParseAutofillFlow) {
+ std::vector<AutofillServerFieldInfo> field_infos;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+ std::string experiment_id;
+
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+ "<page_advance_button id=\"foo\"/>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutofillQueryXmlParser> parse_handler(
+ new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(1U, field_infos.size());
+ EXPECT_EQ(1, parse_handler->current_page_number());
+ EXPECT_EQ(10, parse_handler->total_pages());
+ EXPECT_EQ("foo", parse_handler->proceed_element_descriptor()->descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::ID,
+ parse_handler->proceed_element_descriptor()->retrieval_method);
+
+ // Clear |field_infos| for the next test;
+ field_infos.clear();
+
+ // Test css_selector as page_advance_button.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+ "<page_advance_button css_selector=\"[name=&quot;foo&quot;]\"/>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos,
+ &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(1U, field_infos.size());
+ EXPECT_EQ(1, parse_handler->current_page_number());
+ EXPECT_EQ(10, parse_handler->total_pages());
+ EXPECT_EQ("[name=\"foo\"]",
+ parse_handler->proceed_element_descriptor()->descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ parse_handler->proceed_element_descriptor()->retrieval_method);
+
+ // Clear |field_infos| for the next test;
+ field_infos.clear();
+
+ // Test first attribute is always the one set.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+ "<page_advance_button css_selector=\"[name=&quot;foo&quot;]\""
+ " id=\"foo\"/>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos,
+ &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(1U, field_infos.size());
+ EXPECT_EQ(1, parse_handler->current_page_number());
+ EXPECT_EQ(10, parse_handler->total_pages());
+ EXPECT_EQ("[name=\"foo\"]",
+ parse_handler->proceed_element_descriptor()->descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ parse_handler->proceed_element_descriptor()->retrieval_method);
+}
+
+// Test badly formed XML queries.
+TEST(AutofillQueryXmlParserTest, ParseErrors) {
+ std::vector<AutofillServerFieldInfo> field_infos;
+ UploadRequired upload_required = USE_UPLOAD_RATES;
+ std::string experiment_id;
+
+ // Test no Autofill type.
+ std::string xml = "<autofillqueryresponse>"
+ "<field/>"
+ "</autofillqueryresponse>";
+
+ scoped_ptr<AutofillQueryXmlParser> parse_handler(
+ new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_FALSE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ EXPECT_EQ(0U, field_infos.size());
+ EXPECT_EQ(std::string(), experiment_id);
+
+ // Test an incorrect Autofill type.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"-1\"/>"
+ "</autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ // AutofillType was out of range and should be set to NO_SERVER_DATA.
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+
+ // Test upper bound for the field type, MAX_VALID_FIELD_TYPE.
+ field_infos.clear();
+ xml = "<autofillqueryresponse><field autofilltype=\"" +
+ base::IntToString(MAX_VALID_FIELD_TYPE) + "\"/></autofillqueryresponse>";
+
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ // AutofillType was out of range and should be set to NO_SERVER_DATA.
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+
+ // Test an incorrect Autofill type.
+ field_infos.clear();
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"No Type\"/>"
+ "</autofillqueryresponse>";
+
+ // Parse fails but an entry is still added to field_infos.
+ parse_handler.reset(new AutofillQueryXmlParser(&field_infos, &upload_required,
+ &experiment_id));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_FALSE(parse_handler->succeeded());
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required);
+ ASSERT_EQ(1U, field_infos.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos[0].field_type);
+ EXPECT_EQ(std::string(), experiment_id);
+}
+
+// Test successfull upload response.
+TEST(AutofillUploadXmlParser, TestSuccessfulResponse) {
+ std::string xml = "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"/>";
+ double positive = 0;
+ double negative = 0;
+ AutofillUploadXmlParser parse_handler(&positive, &negative);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(parse_handler.succeeded());
+ EXPECT_DOUBLE_EQ(0.5, positive);
+ EXPECT_DOUBLE_EQ(0.3, negative);
+}
+
+// Test failed upload response.
+TEST(AutofillUploadXmlParser, TestFailedResponse) {
+ std::string xml = "<autofilluploadresponse positiveuploadrate=\"\" "
+ "negativeuploadrate=\"0.3\"/>";
+ double positive = 0;
+ double negative = 0;
+ scoped_ptr<AutofillUploadXmlParser> parse_handler(
+ new AutofillUploadXmlParser(&positive, &negative));
+ scoped_ptr<buzz::XmlParser> parser(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0.3, negative); // Partially parsed.
+ negative = 0;
+
+ xml = "<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"";
+ parse_handler.reset(new AutofillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+
+ xml = "bad data";
+ parse_handler.reset(new AutofillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+
+ xml = "";
+ parse_handler.reset(new AutofillUploadXmlParser(&positive, &negative));
+ parser.reset(new buzz::XmlParser(parse_handler.get()));
+ parser->Parse(xml.c_str(), xml.length(), true);
+ EXPECT_TRUE(!parse_handler->succeeded());
+ EXPECT_DOUBLE_EQ(0, positive);
+ EXPECT_DOUBLE_EQ(0, negative);
+}
+
+} // namespace
diff --git a/components/autofill/browser/contact_info.cc b/components/autofill/browser/contact_info.cc
new file mode 100644
index 0000000..7911ea8
--- /dev/null
+++ b/components/autofill/browser/contact_info.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2011 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/browser/contact_info.h"
+
+#include <stddef.h>
+#include <ostream>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+
+static const AutofillFieldType kAutofillNameInfoTypes[] = {
+ NAME_FIRST,
+ NAME_MIDDLE,
+ NAME_LAST
+};
+
+static const size_t kAutofillNameInfoLength =
+ arraysize(kAutofillNameInfoTypes);
+
+NameInfo::NameInfo() {}
+
+NameInfo::NameInfo(const NameInfo& info) : FormGroup() {
+ *this = info;
+}
+
+NameInfo::~NameInfo() {}
+
+NameInfo& NameInfo::operator=(const NameInfo& info) {
+ if (this == &info)
+ return *this;
+
+ first_ = info.first_;
+ middle_ = info.middle_;
+ last_ = info.last_;
+ return *this;
+}
+
+void NameInfo::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ supported_types->insert(NAME_FIRST);
+ supported_types->insert(NAME_MIDDLE);
+ supported_types->insert(NAME_LAST);
+ supported_types->insert(NAME_MIDDLE_INITIAL);
+ supported_types->insert(NAME_FULL);
+}
+
+string16 NameInfo::GetRawInfo(AutofillFieldType type) const {
+ if (type == NAME_FIRST)
+ return first();
+
+ if (type == NAME_MIDDLE)
+ return middle();
+
+ if (type == NAME_LAST)
+ return last();
+
+ if (type == NAME_MIDDLE_INITIAL)
+ return MiddleInitial();
+
+ if (type == NAME_FULL)
+ return FullName();
+
+ return string16();
+}
+
+void NameInfo::SetRawInfo(AutofillFieldType type, const string16& value) {
+ DCHECK_EQ(AutofillType::NAME, AutofillType(type).group());
+ if (type == NAME_FIRST)
+ first_ = value;
+ else if (type == NAME_MIDDLE || type == NAME_MIDDLE_INITIAL)
+ middle_ = value;
+ else if (type == NAME_LAST)
+ last_ = value;
+ else if (type == NAME_FULL)
+ SetFullName(value);
+ else
+ NOTREACHED();
+}
+
+string16 NameInfo::FullName() const {
+ std::vector<string16> full_name;
+ if (!first_.empty())
+ full_name.push_back(first_);
+
+ if (!middle_.empty())
+ full_name.push_back(middle_);
+
+ if (!last_.empty())
+ full_name.push_back(last_);
+
+ return JoinString(full_name, ' ');
+}
+
+string16 NameInfo::MiddleInitial() const {
+ if (middle_.empty())
+ return string16();
+
+ string16 middle_name(middle());
+ string16 initial;
+ initial.push_back(middle_name[0]);
+ return initial;
+}
+
+void NameInfo::SetFullName(const string16& full) {
+ // Clear the names.
+ first_ = string16();
+ middle_ = string16();
+ last_ = string16();
+
+ std::vector<string16> full_name_tokens;
+ Tokenize(full, ASCIIToUTF16(" "), &full_name_tokens);
+
+ // There are four possibilities: empty; first name; first and last names;
+ // first, middle (possibly multiple strings) and then the last name.
+ if (full_name_tokens.size() > 0) {
+ first_ = full_name_tokens[0];
+ if (full_name_tokens.size() > 1) {
+ last_ = full_name_tokens.back();
+ if (full_name_tokens.size() > 2) {
+ full_name_tokens.erase(full_name_tokens.begin());
+ full_name_tokens.pop_back();
+ middle_ = JoinString(full_name_tokens, ' ');
+ }
+ }
+ }
+}
+
+EmailInfo::EmailInfo() {}
+
+EmailInfo::EmailInfo(const EmailInfo& info) : FormGroup() {
+ *this = info;
+}
+
+EmailInfo::~EmailInfo() {}
+
+EmailInfo& EmailInfo::operator=(const EmailInfo& info) {
+ if (this == &info)
+ return *this;
+
+ email_ = info.email_;
+ return *this;
+}
+
+void EmailInfo::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ supported_types->insert(EMAIL_ADDRESS);
+}
+
+string16 EmailInfo::GetRawInfo(AutofillFieldType type) const {
+ if (type == EMAIL_ADDRESS)
+ return email_;
+
+ return string16();
+}
+
+void EmailInfo::SetRawInfo(AutofillFieldType type, const string16& value) {
+ DCHECK_EQ(EMAIL_ADDRESS, type);
+ email_ = value;
+}
+
+CompanyInfo::CompanyInfo() {}
+
+CompanyInfo::CompanyInfo(const CompanyInfo& info) : FormGroup() {
+ *this = info;
+}
+
+CompanyInfo::~CompanyInfo() {}
+
+CompanyInfo& CompanyInfo::operator=(const CompanyInfo& info) {
+ if (this == &info)
+ return *this;
+
+ company_name_ = info.company_name_;
+ return *this;
+}
+
+void CompanyInfo::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ supported_types->insert(COMPANY_NAME);
+}
+
+string16 CompanyInfo::GetRawInfo(AutofillFieldType type) const {
+ if (type == COMPANY_NAME)
+ return company_name_;
+
+ return string16();
+}
+
+void CompanyInfo::SetRawInfo(AutofillFieldType type, const string16& value) {
+ DCHECK_EQ(COMPANY_NAME, type);
+ company_name_ = value;
+}
diff --git a/components/autofill/browser/contact_info.h b/components/autofill/browser/contact_info.h
new file mode 100644
index 0000000..7426627
--- /dev/null
+++ b/components/autofill/browser/contact_info.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2011 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_BROWSER_CONTACT_INFO_H_
+#define COMPONENTS_AUTOFILL_BROWSER_CONTACT_INFO_H_
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_group.h"
+
+// A form group that stores name information.
+class NameInfo : public FormGroup {
+ public:
+ NameInfo();
+ NameInfo(const NameInfo& info);
+ virtual ~NameInfo();
+
+ NameInfo& operator=(const NameInfo& info);
+
+ // FormGroup:
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ // Returns the full name, which can include up to the first, middle, and last
+ // name.
+ string16 FullName() const;
+
+ // Returns the middle initial if |middle_| is non-empty. Returns an empty
+ // string otherwise.
+ string16 MiddleInitial() const;
+
+ const string16& first() const { return first_; }
+ const string16& middle() const { return middle_; }
+ const string16& last() const { return last_; }
+
+ // Sets |first_|, |middle_|, and |last_| to the tokenized |full|.
+ // It is tokenized on a space only.
+ void SetFullName(const string16& full);
+
+ string16 first_;
+ string16 middle_;
+ string16 last_;
+};
+
+class EmailInfo : public FormGroup {
+ public:
+ EmailInfo();
+ EmailInfo(const EmailInfo& info);
+ virtual ~EmailInfo();
+
+ EmailInfo& operator=(const EmailInfo& info);
+
+ // FormGroup:
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ string16 email_;
+};
+
+class CompanyInfo : public FormGroup {
+ public:
+ CompanyInfo();
+ CompanyInfo(const CompanyInfo& info);
+ virtual ~CompanyInfo();
+
+ CompanyInfo& operator=(const CompanyInfo& info);
+
+ // FormGroup:
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ string16 company_name_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_CONTACT_INFO_H_
diff --git a/components/autofill/browser/contact_info_unittest.cc b/components/autofill/browser/contact_info_unittest.cc
new file mode 100644
index 0000000..377a219
--- /dev/null
+++ b/components/autofill/browser/contact_info_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2011 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/browser/contact_info.h"
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(NameInfoTest, SetFullName) {
+ NameInfo name;
+ name.SetRawInfo(NAME_FULL, ASCIIToUTF16("Virgil"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("Virgil"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("Virgil"));
+
+ name.SetRawInfo(NAME_FULL, ASCIIToUTF16("Murray Gell-Mann"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("Murray"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Gell-Mann"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("Murray Gell-Mann"));
+
+ name.SetRawInfo(NAME_FULL,
+ ASCIIToUTF16("Mikhail Yevgrafovich Saltykov-Shchedrin"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("Mikhail"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Yevgrafovich"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Saltykov-Shchedrin"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL),
+ ASCIIToUTF16("Mikhail Yevgrafovich Saltykov-Shchedrin"));
+
+ name.SetRawInfo(NAME_FULL, ASCIIToUTF16("Arthur Ignatius Conan Doyle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("Arthur"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Ignatius Conan"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Doyle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL),
+ ASCIIToUTF16("Arthur Ignatius Conan Doyle"));
+}
+
+TEST(NameInfoTest, GetFullName) {
+ NameInfo name;
+ name.SetRawInfo(NAME_FIRST, ASCIIToUTF16("First"));
+ name.SetRawInfo(NAME_MIDDLE, string16());
+ name.SetRawInfo(NAME_LAST, string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First"));
+
+ name.SetRawInfo(NAME_FIRST, string16());
+ name.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Middle"));
+ name.SetRawInfo(NAME_LAST, string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Middle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("Middle"));
+
+ name.SetRawInfo(NAME_FIRST, string16());
+ name.SetRawInfo(NAME_MIDDLE, string16());
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("Last"));
+
+ name.SetRawInfo(NAME_FIRST, ASCIIToUTF16("First"));
+ name.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Middle"));
+ name.SetRawInfo(NAME_LAST, string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Middle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First Middle"));
+
+ name.SetRawInfo(NAME_FIRST, ASCIIToUTF16("First"));
+ name.SetRawInfo(NAME_MIDDLE, string16());
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First Last"));
+
+ name.SetRawInfo(NAME_FIRST, string16());
+ name.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Middle"));
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Middle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("Middle Last"));
+
+ name.SetRawInfo(NAME_FIRST, ASCIIToUTF16("First"));
+ name.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Middle"));
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Middle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First Middle Last"));
+}
diff --git a/components/autofill/browser/credit_card.cc b/components/autofill/browser/credit_card.cc
new file mode 100644
index 0000000..c93f9a5
--- /dev/null
+++ b/components/autofill/browser/credit_card.cc
@@ -0,0 +1,678 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/credit_card.h"
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_regexes.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/validation.h"
+#include "components/autofill/common/form_field_data.h"
+#include "grit/generated_resources.h"
+#include "grit/webkit_resources.h"
+#include "third_party/icu/public/common/unicode/uloc.h"
+#include "third_party/icu/public/i18n/unicode/dtfmtsym.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+const char16 kCreditCardObfuscationSymbol = '*';
+
+// This is the maximum obfuscated symbols displayed.
+// It is introduced to avoid rare cases where the credit card number is
+// too large and fills the screen.
+const size_t kMaxObfuscationSize = 20;
+
+std::string GetCreditCardType(const string16& number) {
+ // Don't check for a specific type if this is not a credit card number.
+ if (!autofill::IsValidCreditCardNumber(number))
+ return kGenericCard;
+
+ // Credit card number specifications taken from:
+ // http://en.wikipedia.org/wiki/Credit_card_numbers and
+ // http://www.beachnet.com/~hstiles/cardtype.html
+ // Card Type Prefix(es) Length
+ // ---------------------------------------------------------------
+ // Visa 4 13,16
+ // American Express 34,37 15
+ // Diners Club 300-305,2014,2149,36, 14,15
+ // Discover Card 6011,65 16
+ // JCB 3 16
+ // JCB 2131,1800 15
+ // MasterCard 51-55 16
+ // Solo (debit card) 6334,6767 16,18,19
+
+ // We need at least 4 digits to work with.
+ if (number.length() < 4)
+ return kGenericCard;
+
+ int first_four_digits = 0;
+ if (!base::StringToInt(number.substr(0, 4), &first_four_digits))
+ return kGenericCard;
+
+ int first_three_digits = first_four_digits / 10;
+ int first_two_digits = first_three_digits / 10;
+ int first_digit = first_two_digits / 10;
+
+ switch (number.length()) {
+ case 13:
+ if (first_digit == 4)
+ return kVisaCard;
+
+ break;
+ case 14:
+ if (first_three_digits >= 300 && first_three_digits <= 305)
+ return kDinersCard;
+
+ if (first_digit == 36)
+ return kDinersCard;
+
+ break;
+ case 15:
+ if (first_two_digits == 34 || first_two_digits == 37)
+ return kAmericanExpressCard;
+
+ if (first_four_digits == 2131 || first_four_digits == 1800)
+ return kJCBCard;
+
+ if (first_four_digits == 2014 || first_four_digits == 2149)
+ return kDinersCard;
+
+ break;
+ case 16:
+ if (first_four_digits == 6011 || first_two_digits == 65)
+ return kDiscoverCard;
+
+ if (first_four_digits == 6334 || first_four_digits == 6767)
+ return kSoloCard;
+
+ if (first_two_digits >= 51 && first_two_digits <= 55)
+ return kMasterCard;
+
+ if (first_digit == 3)
+ return kJCBCard;
+
+ if (first_digit == 4)
+ return kVisaCard;
+
+ break;
+ case 18:
+ case 19:
+ if (first_four_digits == 6334 || first_four_digits == 6767)
+ return kSoloCard;
+
+ break;
+ }
+
+ return kGenericCard;
+}
+
+bool ConvertYear(const string16& year, int* num) {
+ // If the |year| is empty, clear the stored value.
+ if (year.empty()) {
+ *num = 0;
+ return true;
+ }
+
+ // Try parsing the |year| as a number.
+ if (base::StringToInt(year, num))
+ return true;
+
+ *num = 0;
+ return false;
+}
+
+bool ConvertMonth(const string16& month,
+ const std::string& app_locale,
+ int* num) {
+ // If the |month| is empty, clear the stored value.
+ if (month.empty()) {
+ *num = 0;
+ return true;
+ }
+
+ // Try parsing the |month| as a number.
+ if (base::StringToInt(month, num))
+ return true;
+
+ // If the locale is unknown, give up.
+ if (app_locale.empty())
+ return false;
+
+ // Otherwise, try parsing the |month| as a named month, e.g. "January" or
+ // "Jan".
+ string16 lowercased_month = StringToLowerASCII(month);
+
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Locale locale(app_locale.c_str());
+ icu::DateFormatSymbols date_format_symbols(locale, status);
+ DCHECK(status == U_ZERO_ERROR || status == U_USING_FALLBACK_WARNING ||
+ status == U_USING_DEFAULT_WARNING);
+
+ int32_t num_months;
+ const icu::UnicodeString* months = date_format_symbols.getMonths(num_months);
+ for (int32_t i = 0; i < num_months; ++i) {
+ const string16 icu_month = string16(months[i].getBuffer(),
+ months[i].length());
+ if (lowercased_month == StringToLowerASCII(icu_month)) {
+ *num = i + 1; // Adjust from 0-indexed to 1-indexed.
+ return true;
+ }
+ }
+
+ months = date_format_symbols.getShortMonths(num_months);
+ for (int32_t i = 0; i < num_months; ++i) {
+ const string16 icu_month = string16(months[i].getBuffer(),
+ months[i].length());
+ if (lowercased_month == StringToLowerASCII(icu_month)) {
+ *num = i + 1; // Adjust from 0-indexed to 1-indexed.
+ return true;
+ }
+ }
+
+ *num = 0;
+ return false;
+}
+
+} // namespace
+
+CreditCard::CreditCard(const std::string& guid)
+ : type_(kGenericCard),
+ expiration_month_(0),
+ expiration_year_(0),
+ guid_(guid) {
+}
+
+CreditCard::CreditCard()
+ : type_(kGenericCard),
+ expiration_month_(0),
+ expiration_year_(0),
+ guid_(base::GenerateGUID()) {
+}
+
+CreditCard::CreditCard(const CreditCard& credit_card) : FormGroup() {
+ operator=(credit_card);
+}
+
+CreditCard::~CreditCard() {}
+
+// static
+const string16 CreditCard::StripSeparators(const string16& number) {
+ const char16 kSeparators[] = {'-', ' ', '\0'};
+ string16 stripped;
+ RemoveChars(number, kSeparators, &stripped);
+ return stripped;
+}
+
+std::string CreditCard::GetGUID() const {
+ return guid();
+}
+
+string16 CreditCard::GetRawInfo(AutofillFieldType type) const {
+ switch (type) {
+ case CREDIT_CARD_NAME:
+ return name_on_card_;
+
+ case CREDIT_CARD_EXP_MONTH:
+ return ExpirationMonthAsString();
+
+ case CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ return Expiration2DigitYearAsString();
+
+ case CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ return Expiration4DigitYearAsString();
+
+ case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: {
+ string16 month = ExpirationMonthAsString();
+ string16 year = Expiration2DigitYearAsString();
+ if (!month.empty() && !year.empty())
+ return month + ASCIIToUTF16("/") + year;
+ return string16();
+ }
+
+ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
+ string16 month = ExpirationMonthAsString();
+ string16 year = Expiration4DigitYearAsString();
+ if (!month.empty() && !year.empty())
+ return month + ASCIIToUTF16("/") + year;
+ return string16();
+ }
+
+ case CREDIT_CARD_TYPE:
+ return TypeForDisplay();
+
+ case CREDIT_CARD_NUMBER:
+ return number_;
+
+ case CREDIT_CARD_VERIFICATION_CODE:
+ // Chrome doesn't store credit card verification codes.
+ return string16();
+
+ default:
+ // ComputeDataPresentForArray will hit this repeatedly.
+ return string16();
+ }
+}
+
+void CreditCard::SetRawInfo(AutofillFieldType type, const string16& value) {
+ switch (type) {
+ case CREDIT_CARD_NAME:
+ name_on_card_ = value;
+ break;
+
+ case CREDIT_CARD_EXP_MONTH:
+ SetExpirationMonthFromString(value, std::string());
+ break;
+
+ case CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ // This is a read-only attribute.
+ break;
+
+ case CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ SetExpirationYearFromString(value);
+ break;
+
+ case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ // This is a read-only attribute.
+ break;
+
+ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ // This is a read-only attribute.
+ break;
+
+ case CREDIT_CARD_TYPE:
+ // This is a read-only attribute, determined by the credit card number.
+ break;
+
+ case CREDIT_CARD_NUMBER: {
+ // Don't change the real value if the input is an obfuscated string.
+ if (value.size() > 0 && value[0] != kCreditCardObfuscationSymbol)
+ SetNumber(value);
+ break;
+ }
+
+ case CREDIT_CARD_VERIFICATION_CODE:
+ // Chrome doesn't store the credit card verification code.
+ break;
+
+ default:
+ NOTREACHED() << "Attempting to set unknown info-type " << type;
+ break;
+ }
+}
+
+string16 CreditCard::GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const {
+ if (type == CREDIT_CARD_NUMBER)
+ return StripSeparators(number_);
+
+ return GetRawInfo(type);
+}
+
+bool CreditCard::SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) {
+ if (type == CREDIT_CARD_NUMBER)
+ SetRawInfo(type, StripSeparators(value));
+ else if (type == CREDIT_CARD_EXP_MONTH)
+ SetExpirationMonthFromString(value, app_locale);
+ else
+ SetRawInfo(type, value);
+
+ return true;
+}
+
+void CreditCard::GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const {
+ FormGroup::GetMatchingTypes(text, app_locale, matching_types);
+
+ string16 card_number = GetInfo(CREDIT_CARD_NUMBER, app_locale);
+ if (!card_number.empty() && StripSeparators(text) == card_number)
+ matching_types->insert(CREDIT_CARD_NUMBER);
+
+ int month;
+ if (ConvertMonth(text, app_locale, &month) && month != 0 &&
+ month == expiration_month_) {
+ matching_types->insert(CREDIT_CARD_EXP_MONTH);
+ }
+}
+
+const string16 CreditCard::Label() const {
+ string16 label;
+ if (number().empty())
+ return name_on_card_; // No CC number, return name only.
+
+ string16 obfuscated_cc_number = ObfuscatedNumber();
+ if (!expiration_month_ || !expiration_year_)
+ return obfuscated_cc_number; // No expiration date set.
+
+ // TODO(georgey): Internationalize date.
+ string16 formatted_date(ExpirationMonthAsString());
+ formatted_date.append(ASCIIToUTF16("/"));
+ formatted_date.append(Expiration4DigitYearAsString());
+
+ label = l10n_util::GetStringFUTF16(IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT,
+ obfuscated_cc_number,
+ formatted_date);
+ return label;
+}
+
+void CreditCard::SetInfoForMonthInputType(const string16& value) {
+ // Check if |text| is "yyyy-mm" format first, and check normal month format.
+ if (!autofill::MatchesPattern(value, UTF8ToUTF16("^[0-9]{4}-[0-9]{1,2}$")))
+ return;
+
+ std::vector<string16> year_month;
+ base::SplitString(value, L'-', &year_month);
+ DCHECK_EQ((int)year_month.size(), 2);
+ int num = 0;
+ bool converted = false;
+ converted = base::StringToInt(year_month[0], &num);
+ DCHECK(converted);
+ SetExpirationYear(num);
+ converted = base::StringToInt(year_month[1], &num);
+ DCHECK(converted);
+ SetExpirationMonth(num);
+}
+
+string16 CreditCard::ObfuscatedNumber() const {
+ // If the number is shorter than four digits, there's no need to obfuscate it.
+ if (number_.size() < 4)
+ return number_;
+
+ string16 number = StripSeparators(number_);
+
+ // Avoid making very long obfuscated numbers.
+ size_t obfuscated_digits = std::min(kMaxObfuscationSize, number.size() - 4);
+ string16 result(obfuscated_digits, kCreditCardObfuscationSymbol);
+ return result.append(LastFourDigits());
+}
+
+string16 CreditCard::LastFourDigits() const {
+ static const size_t kNumLastDigits = 4;
+
+ string16 number = StripSeparators(number_);
+ if (number.size() < kNumLastDigits)
+ return string16();
+
+ return number.substr(number.size() - kNumLastDigits, kNumLastDigits);
+}
+
+string16 CreditCard::TypeForDisplay() const {
+ if (type_ == kAmericanExpressCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX);
+ if (type_ == kDinersCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_DINERS);
+ if (type_ == kDiscoverCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_DISCOVER);
+ if (type_ == kJCBCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_JCB);
+ if (type_ == kMasterCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_MASTERCARD);
+ if (type_ == kSoloCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_SOLO);
+ if (type_ == kVisaCard)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_VISA);
+
+ // If you hit this DCHECK, the above list of cases needs to be updated to
+ // include a new card.
+ DCHECK_EQ(kGenericCard, type_);
+ return string16();
+}
+
+string16 CreditCard::TypeAndLastFourDigits() const {
+ string16 type = TypeForDisplay();
+ // TODO(estade): type may be empty, we probably want to return
+ // "Card - 1234" or something in that case.
+
+ string16 digits = LastFourDigits();
+ if (digits.empty())
+ return type;
+
+ // TODO(estade): i18n.
+ return type + ASCIIToUTF16(" - ") + digits;
+}
+
+int CreditCard::IconResourceId() const {
+ if (type_ == kAmericanExpressCard)
+ return IDR_AUTOFILL_CC_AMEX;
+ if (type_ == kDinersCard)
+ return IDR_AUTOFILL_CC_DINERS;
+ if (type_ == kDiscoverCard)
+ return IDR_AUTOFILL_CC_DISCOVER;
+ if (type_ == kJCBCard)
+ return IDR_AUTOFILL_CC_JCB;
+ if (type_ == kMasterCard)
+ return IDR_AUTOFILL_CC_MASTERCARD;
+ if (type_ == kSoloCard)
+ return IDR_AUTOFILL_CC_SOLO;
+ if (type_ == kVisaCard)
+ return IDR_AUTOFILL_CC_VISA;
+
+ // If you hit this DCHECK, the above list of cases needs to be updated to
+ // include a new card.
+ DCHECK_EQ(kGenericCard, type_);
+ return IDR_AUTOFILL_CC_GENERIC;
+}
+
+void CreditCard::operator=(const CreditCard& credit_card) {
+ if (this == &credit_card)
+ return;
+
+ number_ = credit_card.number_;
+ name_on_card_ = credit_card.name_on_card_;
+ type_ = credit_card.type_;
+ expiration_month_ = credit_card.expiration_month_;
+ expiration_year_ = credit_card.expiration_year_;
+ guid_ = credit_card.guid_;
+}
+
+bool CreditCard::UpdateFromImportedCard(const CreditCard& imported_card,
+ const std::string& app_locale) {
+ if (this->GetInfo(CREDIT_CARD_NUMBER, app_locale) !=
+ imported_card.GetInfo(CREDIT_CARD_NUMBER, app_locale)) {
+ return false;
+ }
+
+ // Note that the card number is intentionally not updated, so as to preserve
+ // any formatting (i.e. separator characters). Since the card number is not
+ // updated, there is no reason to update the card type, either.
+ if (!imported_card.name_on_card_.empty())
+ name_on_card_ = imported_card.name_on_card_;
+
+ // The expiration date for |imported_card| should always be set.
+ DCHECK(imported_card.expiration_month_ && imported_card.expiration_year_);
+ expiration_month_ = imported_card.expiration_month_;
+ expiration_year_ = imported_card.expiration_year_;
+
+ return true;
+}
+
+void CreditCard::FillFormField(const AutofillField& field,
+ size_t /*variant*/,
+ FormFieldData* field_data) const {
+ DCHECK_EQ(AutofillType::CREDIT_CARD, AutofillType(field.type()).group());
+ DCHECK(field_data);
+
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+
+ if (field_data->form_control_type == "select-one") {
+ FillSelectControl(field.type(), field_data);
+ } else if (field_data->form_control_type == "month") {
+ // HTML5 input="month" consists of year-month.
+ string16 year = GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, app_locale);
+ string16 month = GetInfo(CREDIT_CARD_EXP_MONTH, app_locale);
+ if (!year.empty() && !month.empty()) {
+ // Fill the value only if |this| includes both year and month
+ // information.
+ field_data->value = year + ASCIIToUTF16("-") + month;
+ }
+ } else {
+ field_data->value = GetInfo(field.type(), app_locale);
+ }
+}
+
+int CreditCard::Compare(const CreditCard& credit_card) const {
+ // The following CreditCard field types are the only types we store in the
+ // WebDB so far, so we're only concerned with matching these types in the
+ // credit card.
+ const AutofillFieldType types[] = { CREDIT_CARD_NAME,
+ CREDIT_CARD_NUMBER,
+ CREDIT_CARD_EXP_MONTH,
+ CREDIT_CARD_EXP_4_DIGIT_YEAR };
+ for (size_t index = 0; index < arraysize(types); ++index) {
+ int comparison = GetRawInfo(types[index]).compare(
+ credit_card.GetRawInfo(types[index]));
+ if (comparison != 0)
+ return comparison;
+ }
+
+ return 0;
+}
+
+bool CreditCard::operator==(const CreditCard& credit_card) const {
+ if (guid_ != credit_card.guid_)
+ return false;
+
+ return Compare(credit_card) == 0;
+}
+
+bool CreditCard::operator!=(const CreditCard& credit_card) const {
+ return !operator==(credit_card);
+}
+
+bool CreditCard::IsEmpty() const {
+ FieldTypeSet types;
+ GetNonEmptyTypes(AutofillCountry::ApplicationLocale(), &types);
+ return types.empty();
+}
+
+bool CreditCard::IsComplete() const {
+ return
+ autofill::IsValidCreditCardNumber(number_) &&
+ expiration_month_ != 0 &&
+ expiration_year_ != 0;
+}
+
+void CreditCard::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ supported_types->insert(CREDIT_CARD_NAME);
+ supported_types->insert(CREDIT_CARD_NUMBER);
+ supported_types->insert(CREDIT_CARD_TYPE);
+ supported_types->insert(CREDIT_CARD_EXP_MONTH);
+ supported_types->insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+ supported_types->insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ supported_types->insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
+ supported_types->insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
+}
+
+string16 CreditCard::ExpirationMonthAsString() const {
+ if (expiration_month_ == 0)
+ return string16();
+
+ string16 month = base::IntToString16(expiration_month_);
+ if (expiration_month_ >= 10)
+ return month;
+
+ string16 zero = ASCIIToUTF16("0");
+ zero.append(month);
+ return zero;
+}
+
+string16 CreditCard::Expiration4DigitYearAsString() const {
+ if (expiration_year_ == 0)
+ return string16();
+
+ return base::IntToString16(Expiration4DigitYear());
+}
+
+string16 CreditCard::Expiration2DigitYearAsString() const {
+ if (expiration_year_ == 0)
+ return string16();
+
+ return base::IntToString16(Expiration2DigitYear());
+}
+
+void CreditCard::SetExpirationMonthFromString(const string16& text,
+ const std::string& app_locale) {
+ int month;
+ if (!ConvertMonth(text, app_locale, &month))
+ return;
+
+ SetExpirationMonth(month);
+}
+
+void CreditCard::SetExpirationYearFromString(const string16& text) {
+ int year;
+ if (!ConvertYear(text, &year))
+ return;
+
+ SetExpirationYear(year);
+}
+
+void CreditCard::SetNumber(const string16& number) {
+ number_ = number;
+ type_ = GetCreditCardType(StripSeparators(number_));
+}
+
+void CreditCard::SetExpirationMonth(int expiration_month) {
+ if (expiration_month < 0 || expiration_month > 12)
+ return;
+
+ expiration_month_ = expiration_month;
+}
+
+void CreditCard::SetExpirationYear(int expiration_year) {
+ if (expiration_year != 0 &&
+ (expiration_year < 2006 || expiration_year > 10000)) {
+ return;
+ }
+
+ expiration_year_ = expiration_year;
+}
+
+// So we can compare CreditCards with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card) {
+ return os
+ << UTF16ToUTF8(credit_card.Label())
+ << " "
+ << credit_card.guid()
+ << " "
+ << UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_NAME))
+ << " "
+ << UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_TYPE))
+ << " "
+ << UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_NUMBER))
+ << " "
+ << UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_EXP_MONTH))
+ << " "
+ << UTF16ToUTF8(credit_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+}
+
+// These values must match the values in WebKitPlatformSupportImpl in
+// webkit/glue. We send these strings to WebKit, which then asks
+// WebKitPlatformSupportImpl to load the image data.
+const char* const kAmericanExpressCard = "americanExpressCC";
+const char* const kDinersCard = "dinersCC";
+const char* const kDiscoverCard = "discoverCC";
+const char* const kGenericCard = "genericCC";
+const char* const kJCBCard = "jcbCC";
+const char* const kMasterCard = "masterCardCC";
+const char* const kSoloCard = "soloCC";
+const char* const kVisaCard = "visaCC";
diff --git a/components/autofill/browser/credit_card.h b/components/autofill/browser/credit_card.h
new file mode 100644
index 0000000..50700ad
--- /dev/null
+++ b/components/autofill/browser/credit_card.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_CREDIT_CARD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_CREDIT_CARD_H_
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_group.h"
+
+struct FormFieldData;
+
+// A form group that stores credit card information.
+class CreditCard : public FormGroup {
+ public:
+ explicit CreditCard(const std::string& guid);
+
+ // For use in STL containers.
+ CreditCard();
+ CreditCard(const CreditCard& credit_card);
+ virtual ~CreditCard();
+
+ // Returns a version of |number| that has any separator characters removed.
+ static const string16 StripSeparators(const string16& number);
+
+ // FormGroup implementation:
+ virtual std::string GetGUID() const OVERRIDE;
+ virtual void GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const OVERRIDE;
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+ virtual string16 GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) OVERRIDE;
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const OVERRIDE;
+
+ // Credit card preview summary, for example: ******1234, Exp: 01/2020
+ const string16 Label() const;
+
+ // Special method to set value for HTML5 month input type.
+ void SetInfoForMonthInputType(const string16& value);
+
+ // The number altered for display, for example: ******1234
+ string16 ObfuscatedNumber() const;
+ // The last four digits of the credit card number.
+ string16 LastFourDigits() const;
+ // The user-visible type of the card, e.g. 'Mastercard'.
+ string16 TypeForDisplay() const;
+ // A label for this credit card formatted as 'Cardname - 2345'.
+ string16 TypeAndLastFourDigits() const;
+ // The ResourceBundle ID for the appropriate credit card image.
+ int IconResourceId() const;
+
+ const std::string& type() const { return type_; }
+
+ // The guid is the primary identifier for |CreditCard| objects.
+ // TODO(estade): remove this and just use GetGUID().
+ const std::string guid() const { return guid_; }
+ void set_guid(const std::string& guid) { guid_ = guid; }
+
+ // For use in STL containers.
+ void operator=(const CreditCard& credit_card);
+
+ // If the card numbers for |this| and |imported_card| match, overwrites |this|
+ // card's data with the data in |credit_card| and returns true. Otherwise,
+ // returns false.
+ bool UpdateFromImportedCard(const CreditCard& imported_card,
+ const std::string& app_locale) WARN_UNUSED_RESULT;
+
+ // Comparison for Sync. Returns 0 if the credit card is the same as |this|,
+ // or < 0, or > 0 if it is different. The implied ordering can be used for
+ // culling duplicates. The ordering is based on collation order of the
+ // textual contents of the fields.
+ // GUIDs, labels, and unique IDs are not compared, only the values of the
+ // credit cards themselves.
+ int Compare(const CreditCard& credit_card) const;
+
+ // Used by tests.
+ bool operator==(const CreditCard& credit_card) const;
+ bool operator!=(const CreditCard& credit_card) const;
+
+ // Returns true if there are no values (field types) set.
+ bool IsEmpty() const;
+
+ // Returns true if all field types have valid values set.
+ bool IsComplete() const;
+
+ // Returns the credit card number.
+ const string16& number() const { return number_; }
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ // The month and year are zero if not present.
+ int Expiration4DigitYear() const { return expiration_year_; }
+ int Expiration2DigitYear() const { return expiration_year_ % 100; }
+ string16 ExpirationMonthAsString() const;
+ string16 Expiration4DigitYearAsString() const;
+ string16 Expiration2DigitYearAsString() const;
+
+ // Sets |expiration_month_| to the integer conversion of |text|.
+ void SetExpirationMonthFromString(const string16& text,
+ const std::string& app_locale);
+
+ // Sets |expiration_year_| to the integer conversion of |text|.
+ void SetExpirationYearFromString(const string16& text);
+
+ // Sets |number_| to |number| and computes the appropriate card |type_|.
+ void SetNumber(const string16& number);
+
+ // These setters verify that the month and year are within appropriate
+ // ranges.
+ void SetExpirationMonth(int expiration_month);
+ void SetExpirationYear(int expiration_year);
+
+ string16 number_; // The credit card number.
+ string16 name_on_card_; // The cardholder's name.
+ std::string type_; // The type of the card.
+
+ // These members are zero if not present.
+ int expiration_month_;
+ int expiration_year_;
+
+ // The guid of this credit card.
+ std::string guid_;
+};
+
+// So we can compare CreditCards with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const CreditCard& credit_card);
+
+// The string identifiers for credit card icon resources.
+extern const char* const kAmericanExpressCard;
+extern const char* const kDinersCard;
+extern const char* const kDiscoverCard;
+extern const char* const kGenericCard;
+extern const char* const kJCBCard;
+extern const char* const kMasterCard;
+extern const char* const kSoloCard;
+extern const char* const kVisaCard;
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_CREDIT_CARD_H_
diff --git a/components/autofill/browser/credit_card_field.cc b/components/autofill/browser/credit_card_field.cc
new file mode 100644
index 0000000..8baa9f1
--- /dev/null
+++ b/components/autofill/browser/credit_card_field.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2011 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/browser/credit_card_field.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_regex_constants.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/field_types.h"
+#include "ui/base/l10n/l10n_util.h"
+
+// static
+FormField* CreditCardField::Parse(AutofillScanner* scanner) {
+ if (scanner->IsEnd())
+ return NULL;
+
+ scoped_ptr<CreditCardField> credit_card_field(new CreditCardField);
+ size_t saved_cursor = scanner->SaveCursor();
+
+ // Credit card fields can appear in many different orders.
+ // We loop until no more credit card related fields are found, see |break| at
+ // bottom of the loop.
+ for (int fields = 0; !scanner->IsEnd(); ++fields) {
+ // Ignore gift card fields.
+ if (ParseField(scanner, UTF8ToUTF16(autofill::kGiftCardRe), NULL))
+ break;
+
+ // Sometimes the cardholder field is just labeled "name". Unfortunately this
+ // is a dangerously generic word to search for, since it will often match a
+ // name (not cardholder name) field before or after credit card fields. So
+ // we search for "name" only when we've already parsed at least one other
+ // credit card field and haven't yet parsed the expiration date (which
+ // usually appears at the end).
+ if (credit_card_field->cardholder_ == NULL) {
+ string16 name_pattern;
+ if (fields == 0 || credit_card_field->expiration_month_) {
+ // at beginning or end
+ name_pattern = UTF8ToUTF16(autofill::kNameOnCardRe);
+ } else {
+ name_pattern = UTF8ToUTF16(autofill::kNameOnCardContextualRe);
+ }
+
+ if (ParseField(scanner, name_pattern, &credit_card_field->cardholder_))
+ continue;
+
+ // As a hard-coded hack for Expedia's billing pages (expedia_checkout.html
+ // and ExpediaBilling.html in our test suite), recognize separate fields
+ // for the cardholder's first and last name if they have the labels "cfnm"
+ // and "clnm".
+ scanner->SaveCursor();
+ const AutofillField* first;
+ if (ParseField(scanner, ASCIIToUTF16("^cfnm"), &first) &&
+ ParseField(scanner, ASCIIToUTF16("^clnm"),
+ &credit_card_field->cardholder_last_)) {
+ credit_card_field->cardholder_ = first;
+ continue;
+ }
+ scanner->Rewind();
+ }
+
+ // Check for a credit card type (Visa, MasterCard, etc.) field.
+ string16 type_pattern = UTF8ToUTF16(autofill::kCardTypeRe);
+ if (!credit_card_field->type_ &&
+ ParseFieldSpecifics(scanner, type_pattern,
+ MATCH_DEFAULT | MATCH_SELECT,
+ &credit_card_field->type_)) {
+ continue;
+ }
+
+ // We look for a card security code before we look for a credit
+ // card number and match the general term "number". The security code
+ // has a plethora of names; we've seen "verification #",
+ // "verification number", "card identification number" and others listed
+ // in the |pattern| below.
+ string16 pattern = UTF8ToUTF16(autofill::kCardCvcRe);
+ if (!credit_card_field->verification_ &&
+ ParseField(scanner, pattern, &credit_card_field->verification_)) {
+ continue;
+ }
+
+ pattern = UTF8ToUTF16(autofill::kCardNumberRe);
+ if (!credit_card_field->number_ &&
+ ParseField(scanner, pattern, &credit_card_field->number_)) {
+ continue;
+ }
+
+ if (LowerCaseEqualsASCII(scanner->Cursor()->form_control_type, "month")) {
+ credit_card_field->expiration_month_ = scanner->Cursor();
+ scanner->Advance();
+ } else {
+ // First try to parse split month/year expiration fields.
+ scanner->SaveCursor();
+ pattern = UTF8ToUTF16(autofill::kExpirationMonthRe);
+ if (!credit_card_field->expiration_month_ &&
+ ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT,
+ &credit_card_field->expiration_month_)) {
+ pattern = UTF8ToUTF16(autofill::kExpirationYearRe);
+ if (ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT | MATCH_SELECT,
+ &credit_card_field->expiration_year_)) {
+ continue;
+ }
+ }
+
+ // If that fails, try to parse a combined expiration field.
+ if (!credit_card_field->expiration_date_) {
+ // Look for a 2-digit year first.
+ scanner->Rewind();
+ pattern = UTF8ToUTF16(autofill::kExpirationDate2DigitYearRe);
+ // We allow <select> fields, because they're used e.g. on qvc.com.
+ if (ParseFieldSpecifics(scanner, pattern,
+ MATCH_LABEL | MATCH_VALUE | MATCH_TEXT |
+ MATCH_SELECT,
+ &credit_card_field->expiration_date_)) {
+ credit_card_field->is_two_digit_year_ = true;
+ continue;
+ }
+
+ pattern = UTF8ToUTF16(autofill::kExpirationDateRe);
+ if (ParseFieldSpecifics(scanner, pattern,
+ MATCH_LABEL | MATCH_VALUE | MATCH_TEXT |
+ MATCH_SELECT,
+ &credit_card_field->expiration_date_)) {
+ continue;
+ }
+ }
+
+ if (credit_card_field->expiration_month_ &&
+ !credit_card_field->expiration_year_ &&
+ !credit_card_field->expiration_date_) {
+ // Parsed a month but couldn't parse a year; give up.
+ scanner->RewindTo(saved_cursor);
+ return NULL;
+ }
+ }
+
+ // Some pages (e.g. ExpediaBilling.html) have a "card description"
+ // field; we parse this field but ignore it.
+ // We also ignore any other fields within a credit card block that
+ // start with "card", under the assumption that they are related to
+ // the credit card section being processed but are uninteresting to us.
+ if (ParseField(scanner, UTF8ToUTF16(autofill::kCardIgnoredRe), NULL))
+ continue;
+
+ break;
+ }
+
+ // Some pages have a billing address field after the cardholder name field.
+ // For that case, allow only just the cardholder name field. The remaining
+ // CC fields will be picked up in a following CreditCardField.
+ if (credit_card_field->cardholder_)
+ return credit_card_field.release();
+
+ // On some pages, the user selects a card type using radio buttons
+ // (e.g. test page Apple Store Billing.html). We can't handle that yet,
+ // so we treat the card type as optional for now.
+ // The existence of a number or cvc in combination with expiration date is
+ // a strong enough signal that this is a credit card. It is possible that
+ // the number and name were parsed in a separate part of the form. So if
+ // the cvc and date were found independently they are returned.
+ if ((credit_card_field->number_ || credit_card_field->verification_) &&
+ (credit_card_field->expiration_date_ ||
+ (credit_card_field->expiration_month_ &&
+ (credit_card_field->expiration_year_ ||
+ (LowerCaseEqualsASCII(
+ credit_card_field->expiration_month_->form_control_type,
+ "month")))))) {
+ return credit_card_field.release();
+ }
+
+ scanner->RewindTo(saved_cursor);
+ return NULL;
+}
+
+CreditCardField::CreditCardField()
+ : cardholder_(NULL),
+ cardholder_last_(NULL),
+ type_(NULL),
+ number_(NULL),
+ verification_(NULL),
+ expiration_month_(NULL),
+ expiration_year_(NULL),
+ expiration_date_(NULL),
+ is_two_digit_year_(false) {
+}
+
+bool CreditCardField::ClassifyField(FieldTypeMap* map) const {
+ bool ok = AddClassification(number_, CREDIT_CARD_NUMBER, map);
+ ok = ok && AddClassification(type_, CREDIT_CARD_TYPE, map);
+ ok = ok && AddClassification(verification_, CREDIT_CARD_VERIFICATION_CODE,
+ map);
+
+ // If the heuristics detected first and last name in separate fields,
+ // then ignore both fields. Putting them into separate fields is probably
+ // wrong, because the credit card can also contain a middle name or middle
+ // initial.
+ if (cardholder_last_ == NULL)
+ ok = ok && AddClassification(cardholder_, CREDIT_CARD_NAME, map);
+
+ if (expiration_date_) {
+ if (is_two_digit_year_) {
+ ok = ok && AddClassification(expiration_date_,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, map);
+ } else {
+ ok = ok && AddClassification(expiration_date_,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, map);
+ }
+ } else {
+ ok = ok && AddClassification(expiration_month_, CREDIT_CARD_EXP_MONTH, map);
+ if (is_two_digit_year_) {
+ ok = ok && AddClassification(expiration_year_,
+ CREDIT_CARD_EXP_2_DIGIT_YEAR,
+ map);
+ } else {
+ ok = ok && AddClassification(expiration_year_,
+ CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ map);
+ }
+ }
+
+ return ok;
+}
diff --git a/components/autofill/browser/credit_card_field.h b/components/autofill/browser/credit_card_field.h
new file mode 100644
index 0000000..777aa16
--- /dev/null
+++ b/components/autofill/browser/credit_card_field.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 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_BROWSER_CREDIT_CARD_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_CREDIT_CARD_FIELD_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/form_field.h"
+
+class AutofillField;
+class AutofillScanner;
+
+class CreditCardField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseMiniumCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseFullCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseCreditCardType);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseExpMonthYear);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseExpMonthYear2);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseExpField);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest, ParseExpField2DigitYear);
+ FRIEND_TEST_ALL_PREFIXES(CreditCardFieldTest,
+ ParseCreditCardHolderNameWithCCFullName);
+
+ CreditCardField();
+
+ const AutofillField* cardholder_; // Optional.
+
+ // Occasionally pages have separate fields for the cardholder's first and
+ // last names; for such pages cardholder_ holds the first name field and
+ // we store the last name field here.
+ // (We could store an embedded NameField object here, but we don't do so
+ // because the text patterns for matching a cardholder name are different
+ // than for ordinary names, and because cardholder names never have titles,
+ // middle names or suffixes.)
+ const AutofillField* cardholder_last_;
+
+ // TODO(jhawkins): Parse the select control.
+ const AutofillField* type_; // Optional.
+ const AutofillField* number_; // Required.
+
+ // The 3-digit card verification number; we don't currently fill this.
+ const AutofillField* verification_;
+
+ // Either |expiration_date_| or both |expiration_month_| and
+ // |expiration_year_| are required.
+ const AutofillField* expiration_month_;
+ const AutofillField* expiration_year_;
+ const AutofillField* expiration_date_;
+
+ // True if the year is detected to be a 2-digit year; otherwise, we assume
+ // a 4-digit year.
+ bool is_two_digit_year_;
+
+ DISALLOW_COPY_AND_ASSIGN(CreditCardField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_CREDIT_CARD_FIELD_H_
diff --git a/components/autofill/browser/credit_card_field_unittest.cc b/components/autofill/browser/credit_card_field_unittest.cc
new file mode 100644
index 0000000..b077b70
--- /dev/null
+++ b/components/autofill/browser/credit_card_field_unittest.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/credit_card_field.h"
+#include "components/autofill/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CreditCardFieldTest : public testing::Test {
+ public:
+ CreditCardFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<CreditCardField> field_;
+ FieldTypeMap field_type_map_;
+
+ // Downcast for tests.
+ static CreditCardField* Parse(AutofillScanner* scanner) {
+ return static_cast<CreditCardField*>(CreditCardField::Parse(scanner));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CreditCardFieldTest);
+};
+
+TEST_F(CreditCardFieldTest, Empty) {
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get());
+}
+
+TEST_F(CreditCardFieldTest, NonParse) {
+ list_.push_back(new AutofillField);
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get());
+}
+
+TEST_F(CreditCardFieldTest, ParseCreditCardNoNumber) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Exp Month");
+ field.name = ASCIIToUTF16("ccmonth");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("month1")));
+
+ field.label = ASCIIToUTF16("Exp Year");
+ field.name = ASCIIToUTF16("ccyear");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("year2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get());
+}
+
+TEST_F(CreditCardFieldTest, ParseCreditCardNoDate) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<CreditCardField*>(NULL), field_.get());
+}
+
+TEST_F(CreditCardFieldTest, ParseMiniumCreditCard) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number1")));
+
+ field.label = ASCIIToUTF16("Exp Month");
+ field.name = ASCIIToUTF16("ccmonth");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("month2")));
+
+ field.label = ASCIIToUTF16("Exp Year");
+ field.name = ASCIIToUTF16("ccyear");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("year3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("number1")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NUMBER, field_type_map_[ASCIIToUTF16("number1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("month2")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, field_type_map_[ASCIIToUTF16("month2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("year3")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ field_type_map_[ASCIIToUTF16("year3")]);
+}
+
+TEST_F(CreditCardFieldTest, ParseFullCreditCard) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Card Type");
+ field.name = ASCIIToUTF16("card_type");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("type")));
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name")));
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number")));
+
+ field.label = ASCIIToUTF16("Exp Month");
+ field.name = ASCIIToUTF16("ccmonth");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("month")));
+
+ field.label = ASCIIToUTF16("Exp Year");
+ field.name = ASCIIToUTF16("ccyear");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("year")));
+
+ field.label = ASCIIToUTF16("Verification");
+ field.name = ASCIIToUTF16("verification");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("cvc")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("type")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_TYPE, field_type_map_[ASCIIToUTF16("type")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NAME, field_type_map_[ASCIIToUTF16("name")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("number")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NUMBER, field_type_map_[ASCIIToUTF16("number")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("month")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, field_type_map_[ASCIIToUTF16("month")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("year")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ field_type_map_[ASCIIToUTF16("year")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("cvc")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
+ field_type_map_[ASCIIToUTF16("cvc")]);
+}
+
+TEST_F(CreditCardFieldTest, ParseExpMonthYear) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+
+ field.label = ASCIIToUTF16("ExpDate Month / Year");
+ field.name = ASCIIToUTF16("ExpDate");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("month3")));
+
+ field.label = ASCIIToUTF16("ExpDate Month / Year");
+ field.name = ASCIIToUTF16("ExpDate");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("year4")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NAME, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("number2")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NUMBER, field_type_map_[ASCIIToUTF16("number2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("month3")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, field_type_map_[ASCIIToUTF16("month3")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("year4")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ field_type_map_[ASCIIToUTF16("year4")]);
+}
+
+TEST_F(CreditCardFieldTest, ParseExpMonthYear2) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+
+ field.label = ASCIIToUTF16("Expiration date Month / Year");
+ field.name = ASCIIToUTF16("ExpDate");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("month3")));
+
+ field.label = ASCIIToUTF16("Expiration date Month / Year");
+ field.name = ASCIIToUTF16("ExpDate");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("year4")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NAME, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("number2")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NUMBER, field_type_map_[ASCIIToUTF16("number2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("month3")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, field_type_map_[ASCIIToUTF16("month3")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("year4")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ field_type_map_[ASCIIToUTF16("year4")]);
+}
+
+TEST_F(CreditCardFieldTest, ParseExpField) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+
+ field.label = ASCIIToUTF16("Expiration Date (MM/YYYY)");
+ field.name = ASCIIToUTF16("cc_exp");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("exp3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NAME, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("number2")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NUMBER, field_type_map_[ASCIIToUTF16("number2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("exp3")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR,
+ field_type_map_[ASCIIToUTF16("exp3")]);
+}
+
+TEST_F(CreditCardFieldTest, ParseExpField2DigitYear) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("number2")));
+
+ field.label = ASCIIToUTF16("Expiration Date (MM/YY)");
+ field.name = ASCIIToUTF16("cc_exp");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("exp3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NAME, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("number2")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NUMBER, field_type_map_[ASCIIToUTF16("number2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("exp3")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR,
+ field_type_map_[ASCIIToUTF16("exp3")]);
+}
+
+TEST_F(CreditCardFieldTest, ParseCreditCardHolderNameWithCCFullName) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name");
+ field.name = ASCIIToUTF16("ccfullname");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<CreditCardField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(CREDIT_CARD_NAME, field_type_map_[ASCIIToUTF16("name1")]);
+}
diff --git a/components/autofill/browser/credit_card_unittest.cc b/components/autofill/browser/credit_card_unittest.cc
new file mode 100644
index 0000000..0da4c13
--- /dev/null
+++ b/components/autofill/browser/credit_card_unittest.cc
@@ -0,0 +1,359 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// From https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
+const char* const kValidNumbers[] = {
+ "378282246310005",
+ "3714 4963 5398 431",
+ "3787-3449-3671-000",
+ "5610591081018250",
+ "3056 9309 0259 04",
+ "3852-0000-0232-37",
+ "6011111111111117",
+ "6011 0009 9013 9424",
+ "3530-1113-3330-0000",
+ "3566002020360505",
+ "5555 5555 5555 4444",
+ "5105-1051-0510-5100",
+ "4111111111111111",
+ "4012 8888 8888 1881",
+ "4222-2222-2222-2",
+ "5019717010103742",
+ "6331101999990016",
+};
+const char* const kInvalidNumbers[] = {
+ "4111 1111 112", /* too short */
+ "41111111111111111115", /* too long */
+ "4111-1111-1111-1110", /* wrong Luhn checksum */
+ "3056 9309 0259 04aa", /* non-digit characters */
+};
+
+} // namespace
+
+// Tests credit card summary string generation. This test simulates a variety
+// of different possible summary strings. Variations occur based on the
+// existence of credit card number, month, and year fields.
+TEST(CreditCardTest, PreviewSummaryAndObfuscatedNumberStrings) {
+ // Case 0: empty credit card.
+ CreditCard credit_card0;
+ string16 summary0 = credit_card0.Label();
+ EXPECT_EQ(string16(), summary0);
+ string16 obfuscated0 = credit_card0.ObfuscatedNumber();
+ EXPECT_EQ(string16(), obfuscated0);
+
+ // Case 00: Empty credit card with empty strings.
+ CreditCard credit_card00;
+ autofill_test::SetCreditCardInfo(&credit_card00,
+ "John Dillinger", "", "", "");
+ string16 summary00 = credit_card00.Label();
+ EXPECT_EQ(string16(ASCIIToUTF16("John Dillinger")), summary00);
+ string16 obfuscated00 = credit_card00.ObfuscatedNumber();
+ EXPECT_EQ(string16(), obfuscated00);
+
+ // Case 1: No credit card number.
+ CreditCard credit_card1;
+ autofill_test::SetCreditCardInfo(&credit_card1,
+ "John Dillinger", "", "01", "2010");
+ string16 summary1 = credit_card1.Label();
+ EXPECT_EQ(string16(ASCIIToUTF16("John Dillinger")), summary1);
+ string16 obfuscated1 = credit_card1.ObfuscatedNumber();
+ EXPECT_EQ(string16(), obfuscated1);
+
+ // Case 2: No month.
+ CreditCard credit_card2;
+ autofill_test::SetCreditCardInfo(&credit_card2,
+ "John Dillinger", "5105 1051 0510 5100", "", "2010");
+ string16 summary2 = credit_card2.Label();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), summary2);
+ string16 obfuscated2 = credit_card2.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), obfuscated2);
+
+ // Case 3: No year.
+ CreditCard credit_card3;
+ autofill_test::SetCreditCardInfo(&credit_card3,
+ "John Dillinger", "5105 1051 0510 5100", "01", "");
+ string16 summary3 = credit_card3.Label();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), summary3);
+ string16 obfuscated3 = credit_card3.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), obfuscated3);
+
+ // Case 4: Have everything.
+ CreditCard credit_card4;
+ autofill_test::SetCreditCardInfo(&credit_card4,
+ "John Dillinger", "5105 1051 0510 5100", "01", "2010");
+ string16 summary4 = credit_card4.Label();
+ EXPECT_EQ(ASCIIToUTF16("************5100, Exp: 01/2010"), summary4);
+ string16 obfuscated4 = credit_card4.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), obfuscated4);
+
+ // Case 5: Very long credit card
+ CreditCard credit_card5;
+ autofill_test::SetCreditCardInfo(&credit_card5,
+ "John Dillinger",
+ "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010");
+ string16 summary5 = credit_card5.Label();
+ EXPECT_EQ(ASCIIToUTF16("********************5100, Exp: 01/2010"), summary5);
+ string16 obfuscated5 = credit_card5.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("********************5100"), obfuscated5);
+}
+
+TEST(CreditCardTest, AssignmentOperator) {
+ CreditCard a, b;
+
+ // Result of assignment should be logically equal to the original profile.
+ autofill_test::SetCreditCardInfo(&a, "John Dillinger",
+ "123456789012", "01", "2010");
+ b = a;
+ EXPECT_TRUE(a == b);
+
+ // Assignment to self should not change the profile value.
+ a = a;
+ EXPECT_TRUE(a == b);
+}
+
+TEST(CreditCardTest, IsComplete) {
+ CreditCard card;
+ EXPECT_FALSE(card.IsComplete());
+ card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Wally T. Walrus"));
+ EXPECT_FALSE(card.IsComplete());
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ EXPECT_FALSE(card.IsComplete());
+ card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2014"));
+
+ for (size_t i = 0; i < arraysize(kValidNumbers); ++i) {
+ SCOPED_TRACE(kValidNumbers[i]);
+ card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16(kValidNumbers[i]));
+ EXPECT_TRUE(card.IsComplete());
+ }
+ for (size_t i = 0; i < arraysize(kInvalidNumbers); ++i) {
+ SCOPED_TRACE(kInvalidNumbers[i]);
+ card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16(kInvalidNumbers[i]));
+ EXPECT_FALSE(card.IsComplete());
+ }
+}
+
+TEST(CreditCardTest, InvalidMastercardNumber) {
+ CreditCard card;
+
+ autofill_test::SetCreditCardInfo(&card, "Baby Face Nelson",
+ "5200000000000004", "01", "2010");
+ EXPECT_EQ("genericCC", card.type());
+}
+
+// Verify that we preserve exactly what the user typed for credit card numbers.
+TEST(CreditCardTest, SetRawInfoCreditCardNumber) {
+ CreditCard card;
+
+ autofill_test::SetCreditCardInfo(&card, "Bob Dylan",
+ "4321-5432-6543-xxxx", "07", "2013");
+ EXPECT_EQ(ASCIIToUTF16("4321-5432-6543-xxxx"),
+ card.GetRawInfo(CREDIT_CARD_NUMBER));
+}
+
+// Verify that we can handle both numeric and named months.
+TEST(CreditCardTest, SetExpirationMonth) {
+ CreditCard card;
+
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("05"));
+ EXPECT_EQ(ASCIIToUTF16("05"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("7"));
+ EXPECT_EQ(ASCIIToUTF16("07"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+
+ // This should fail, and preserve the previous value.
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("January"));
+ EXPECT_EQ(ASCIIToUTF16("07"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+
+ card.SetInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("January"), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("01"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+
+ card.SetInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("Apr"), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("04"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+}
+
+TEST(CreditCardTest, CreditCardType) {
+ CreditCard card;
+
+ // The card type cannot be set directly.
+ card.SetRawInfo(CREDIT_CARD_TYPE, ASCIIToUTF16("Visa"));
+ EXPECT_EQ(string16(), card.GetRawInfo(CREDIT_CARD_TYPE));
+
+ // Setting the number should implicitly set the type.
+ card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("4111 1111 1111 1111"));
+ EXPECT_EQ(ASCIIToUTF16("Visa"), card.GetRawInfo(CREDIT_CARD_TYPE));
+}
+
+TEST(CreditCardTest, CreditCardVerificationCode) {
+ CreditCard card;
+
+ // The verification code cannot be set, as Chrome does not store this data.
+ card.SetRawInfo(CREDIT_CARD_VERIFICATION_CODE, ASCIIToUTF16("999"));
+ EXPECT_EQ(string16(), card.GetRawInfo(CREDIT_CARD_VERIFICATION_CODE));
+}
+
+
+TEST(CreditCardTest, CreditCardMonthExact) {
+ const char* const kMonthsNumeric[] = {
+ "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12",
+ };
+ std::vector<string16> options(arraysize(kMonthsNumeric));
+ for (size_t i = 0; i < arraysize(kMonthsNumeric); ++i) {
+ options[i] = ASCIIToUTF16(kMonthsNumeric[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(CREDIT_CARD_EXP_MONTH, &field);
+ EXPECT_EQ(ASCIIToUTF16("01"), field.value);
+}
+
+TEST(CreditCardTest, CreditCardMonthAbbreviated) {
+ const char* const kMonthsAbbreviated[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+ std::vector<string16> options(arraysize(kMonthsAbbreviated));
+ for (size_t i = 0; i < arraysize(kMonthsAbbreviated); ++i) {
+ options[i] = ASCIIToUTF16(kMonthsAbbreviated[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(CREDIT_CARD_EXP_MONTH, &field);
+ EXPECT_EQ(ASCIIToUTF16("Jan"), field.value);
+}
+
+TEST(CreditCardTest, CreditCardMonthFull) {
+ const char* const kMonthsFull[] = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December",
+ };
+ std::vector<string16> options(arraysize(kMonthsFull));
+ for (size_t i = 0; i < arraysize(kMonthsFull); ++i) {
+ options[i] = ASCIIToUTF16(kMonthsFull[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(CREDIT_CARD_EXP_MONTH, &field);
+ EXPECT_EQ(ASCIIToUTF16("January"), field.value);
+}
+
+TEST(CreditCardTest, CreditCardMonthNumeric) {
+ const char* const kMonthsNumeric[] = {
+ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+ };
+ std::vector<string16> options(arraysize(kMonthsNumeric));
+ for (size_t i = 0; i < arraysize(kMonthsNumeric); ++i) {
+ options[i] = ASCIIToUTF16(kMonthsNumeric[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(CREDIT_CARD_EXP_MONTH, &field);
+ EXPECT_EQ(ASCIIToUTF16("1"), field.value);
+}
+
+TEST(CreditCardTest, CreditCardTwoDigitYear) {
+ const char* const kYears[] = {
+ "12", "13", "14", "15", "16", "17", "18", "19"
+ };
+ std::vector<string16> options(arraysize(kYears));
+ for (size_t i = 0; i < arraysize(kYears); ++i) {
+ options[i] = ASCIIToUTF16(kYears[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2017"));
+ credit_card.FillSelectControl(CREDIT_CARD_EXP_4_DIGIT_YEAR, &field);
+ EXPECT_EQ(ASCIIToUTF16("17"), field.value);
+}
+
+TEST(CreditCardTest, CreditCardTypeSelectControl) {
+ const char* const kCreditCardTypes[] = {
+ "Visa", "Master Card", "AmEx", "discover"
+ };
+ std::vector<string16> options(arraysize(kCreditCardTypes));
+ for (size_t i = 0; i < arraysize(kCreditCardTypes); ++i) {
+ options[i] = ASCIIToUTF16(kCreditCardTypes[i]);
+ }
+
+ FormFieldData field;
+ field.form_control_type = "select-one";
+ field.option_values = options;
+ field.option_contents = options;
+
+ // Credit card types are inferred from the numbers, so we use test numbers for
+ // each card type. Test card numbers are drawn from
+ // http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
+
+ {
+ // Normal case:
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("4111111111111111"));
+ credit_card.FillSelectControl(CREDIT_CARD_TYPE, &field);
+ EXPECT_EQ(ASCIIToUTF16("Visa"), field.value);
+ }
+
+ {
+ // Filling should be able to handle intervening whitespace:
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("5105105105105100"));
+ credit_card.FillSelectControl(CREDIT_CARD_TYPE, &field);
+ EXPECT_EQ(ASCIIToUTF16("Master Card"), field.value);
+ }
+
+ {
+ // American Express is sometimes abbreviated as AmEx:
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("371449635398431"));
+ credit_card.FillSelectControl(CREDIT_CARD_TYPE, &field);
+ EXPECT_EQ(ASCIIToUTF16("AmEx"), field.value);
+ }
+
+ {
+ // Case insensitivity:
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("6011111111111117"));
+ credit_card.FillSelectControl(CREDIT_CARD_TYPE, &field);
+ EXPECT_EQ(ASCIIToUTF16("discover"), field.value);
+ }
+}
diff --git a/components/autofill/browser/crypto/rc4_decryptor.h b/components/autofill/browser/crypto/rc4_decryptor.h
new file mode 100644
index 0000000..2dff640
--- /dev/null
+++ b/components/autofill/browser/crypto/rc4_decryptor.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 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_BROWSER_CRYPTO_RC4_DECRYPTOR_H_
+#define COMPONENTS_AUTOFILL_BROWSER_CRYPTO_RC4_DECRYPTOR_H_
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+// This is modified RC4 decryption used for import of Toolbar autofill data
+// only. The difference from the Crypto Api implementation is twofold:
+// First, it uses a non-standard key size (160 bit), not supported by Microsoft
+// (it supports only 40 and 128 bit for RC4). Second, it codes 128 words with
+// value 0x0020 at the beginning of the code to enhance security.
+//
+// This class used in
+// components/autofill/browser/autofill_ie_toolbar_import_win.cc.
+//
+// This class should not be used anywhere else!!!
+class RC4Decryptor {
+ public:
+ explicit RC4Decryptor(wchar_t const* password) {
+ PrepareKey(reinterpret_cast<const uint8 *>(password),
+ wcslen(password) * sizeof(wchar_t));
+ std::wstring data;
+ // First 128 bytes should be spaces.
+ data.resize(128, L' ');
+ Run(data.c_str());
+ }
+
+ // Run the algorithm
+ std::wstring Run(const std::wstring& data) {
+ int data_size = data.length() * sizeof(wchar_t);
+
+ scoped_array<wchar_t> buffer(new wchar_t[data.length() + 1]);
+ memset(buffer.get(), 0, (data.length() + 1) * sizeof(wchar_t));
+ memcpy(buffer.get(), data.c_str(), data_size);
+
+ RunInternal(reinterpret_cast<uint8 *>(buffer.get()), data_size);
+
+ std::wstring result(buffer.get());
+
+ // Clear the memory
+ memset(buffer.get(), 0, data_size);
+ return result;
+ }
+
+ private:
+ static const int kKeyDataSize = 256;
+ struct Rc4Key {
+ uint8 state[kKeyDataSize];
+ uint8 x;
+ uint8 y;
+ };
+
+ void SwapByte(uint8* byte1, uint8* byte2) {
+ uint8 temp = *byte1;
+ *byte1 = *byte2;
+ *byte2 = temp;
+ }
+
+ void PrepareKey(const uint8 *key_data, int key_data_len) {
+ uint8 index1 = 0;
+ uint8 index2 = 0;
+ uint8* state;
+ short counter;
+
+ state = &key_.state[0];
+ for (counter = 0; counter < kKeyDataSize; ++counter)
+ state[counter] = static_cast<uint8>(counter);
+
+ key_.x = key_.y = 0;
+
+ for (counter = 0; counter < kKeyDataSize; counter++) {
+ index2 = (key_data[index1] + state[counter] + index2) % kKeyDataSize;
+ SwapByte(&state[counter], &state[index2]);
+ index1 = (index1 + 1) % key_data_len;
+ }
+ }
+
+ void RunInternal(uint8 *buffer, int buffer_len) {
+ uint8 x, y;
+ uint8 xor_index = 0;
+ uint8* state;
+ int counter;
+
+ x = key_.x;
+ y = key_.y;
+ state = &key_.state[0];
+ for (counter = 0; counter < buffer_len; ++counter) {
+ x = (x + 1) % kKeyDataSize;
+ y = (state[x] + y) % kKeyDataSize;
+ SwapByte(&state[x], &state[y]);
+ xor_index = (state[x] + state[y]) % kKeyDataSize;
+ buffer[counter] ^= state[xor_index];
+ }
+ key_.x = x;
+ key_.y = y;
+ }
+
+ Rc4Key key_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_CRYPTO_RC4_DECRYPTOR_H_
diff --git a/components/autofill/browser/data_driven_test.cc b/components/autofill/browser/data_driven_test.cc
new file mode 100644
index 0000000..0936445
--- /dev/null
+++ b/components/autofill/browser/data_driven_test.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2011 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/browser/data_driven_test.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_paths.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Reads |file| into |content|, and converts Windows line-endings to Unix ones.
+// Returns true on success.
+bool ReadFile(const base::FilePath& file, std::string* content) {
+ if (!file_util::ReadFileToString(file, content))
+ return false;
+
+ ReplaceSubstringsAfterOffset(content, 0, "\r\n", "\n");
+ return true;
+}
+
+// Write |content| to |file|. Returns true on success.
+bool WriteFile(const base::FilePath& file, const std::string& content) {
+ int write_size = file_util::WriteFile(file, content.c_str(),
+ content.length());
+ return write_size == static_cast<int>(content.length());
+}
+
+} // namespace
+
+void DataDrivenTest::RunDataDrivenTest(
+ const base::FilePath& input_directory,
+ const base::FilePath& output_directory,
+ const base::FilePath::StringType& file_name_pattern) {
+ file_util::FileEnumerator input_files(input_directory,
+ false,
+ file_util::FileEnumerator::FILES,
+ file_name_pattern);
+
+ for (base::FilePath input_file = input_files.Next();
+ !input_file.empty();
+ input_file = input_files.Next()) {
+ SCOPED_TRACE(input_file.BaseName().value());
+
+ std::string input;
+ ASSERT_TRUE(ReadFile(input_file, &input));
+
+ std::string output;
+ GenerateResults(input, &output);
+
+ base::FilePath output_file = output_directory.Append(
+ input_file.BaseName().StripTrailingSeparators().ReplaceExtension(
+ FILE_PATH_LITERAL(".out")));
+
+ std::string output_file_contents;
+ if (ReadFile(output_file, &output_file_contents))
+ EXPECT_EQ(output_file_contents, output);
+ else
+ ASSERT_TRUE(WriteFile(output_file, output));
+ }
+}
+
+base::FilePath DataDrivenTest::GetInputDirectory(
+ const base::FilePath::StringType& test_name) {
+ base::FilePath test_data_dir_;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
+ test_data_dir_ = test_data_dir_.AppendASCII("autofill")
+ .Append(test_name)
+ .AppendASCII("input");
+ return test_data_dir_;
+}
+
+base::FilePath DataDrivenTest::GetOutputDirectory(
+ const base::FilePath::StringType& test_name) {
+ base::FilePath test_data_dir_;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
+ test_data_dir_ = test_data_dir_.AppendASCII("autofill")
+ .Append(test_name)
+ .AppendASCII("output");
+ return test_data_dir_;
+}
+
+DataDrivenTest::DataDrivenTest() {
+}
+
+DataDrivenTest::~DataDrivenTest() {
+}
diff --git a/components/autofill/browser/data_driven_test.h b/components/autofill/browser/data_driven_test.h
new file mode 100644
index 0000000..36cf872
--- /dev/null
+++ b/components/autofill/browser/data_driven_test.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 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_BROWSER_DATA_DRIVEN_TEST_H_
+#define COMPONENTS_AUTOFILL_BROWSER_DATA_DRIVEN_TEST_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/string16.h"
+
+// A convenience class for implementing data-driven tests. Subclassers need only
+// implement the conversion of serialized input data to serialized output data
+// and provide a set of input files. For each input file, on the first run, a
+// gold output file is generated; for subsequent runs, the test ouptut is
+// compared to this gold output.
+class DataDrivenTest {
+ public:
+ // For each file in |input_directory| whose filename matches
+ // |file_name_pattern|, slurps in the file contents and calls into
+ // |GenerateResults()|. If the corresponding output file already exists in
+ // the |output_directory|, verifies that the results match the file contents;
+ // otherwise, writes a gold result file to the |output_directory|.
+ void RunDataDrivenTest(const base::FilePath& input_directory,
+ const base::FilePath& output_directory,
+ const base::FilePath::StringType& file_name_pattern);
+
+ // Given the |input| data, generates the |output| results. The output results
+ // must be stable across runs.
+ // Note: The return type is |void| so that googletest |ASSERT_*| macros will
+ // compile.
+ virtual void GenerateResults(const std::string& input,
+ std::string* output) = 0;
+
+ // Return |base::FilePath|s to the test input and output subdirectories
+ // ../autofill/|test_name|/input and ../autofill/|test_name|/output.
+ base::FilePath GetInputDirectory(const base::FilePath::StringType& test_name);
+ base::FilePath GetOutputDirectory(
+ const base::FilePath::StringType& test_name);
+
+ protected:
+ DataDrivenTest();
+ virtual ~DataDrivenTest();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DataDrivenTest);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_DATA_DRIVEN_TEST_H_
diff --git a/components/autofill/browser/email_field.cc b/components/autofill/browser/email_field.cc
new file mode 100644
index 0000000..bf01266
--- /dev/null
+++ b/components/autofill/browser/email_field.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 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/browser/email_field.h"
+
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_regex_constants.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "ui/base/l10n/l10n_util.h"
+
+// static
+FormField* EmailField::Parse(AutofillScanner* scanner) {
+ const AutofillField* field;
+ if (ParseFieldSpecifics(scanner, UTF8ToUTF16(autofill::kEmailRe),
+ MATCH_DEFAULT | MATCH_EMAIL, &field)) {
+ return new EmailField(field);
+ }
+
+ return NULL;
+}
+
+EmailField::EmailField(const AutofillField* field) : field_(field) {
+}
+
+bool EmailField::ClassifyField(FieldTypeMap* map) const {
+ return AddClassification(field_, EMAIL_ADDRESS, map);
+}
diff --git a/components/autofill/browser/email_field.h b/components/autofill/browser/email_field.h
new file mode 100644
index 0000000..d04a1a1
--- /dev/null
+++ b/components/autofill/browser/email_field.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2011 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_BROWSER_EMAIL_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_EMAIL_FIELD_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "components/autofill/browser/form_field.h"
+
+class EmailField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ explicit EmailField(const AutofillField* field);
+
+ const AutofillField* field_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmailField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_EMAIL_FIELD_H_
diff --git a/components/autofill/browser/field_types.h b/components/autofill/browser/field_types.h
new file mode 100644
index 0000000..87dcfe8
--- /dev/null
+++ b/components/autofill/browser/field_types.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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_BROWSER_FIELD_TYPES_H_
+#define COMPONENTS_AUTOFILL_BROWSER_FIELD_TYPES_H_
+
+#include <set>
+#include <string>
+
+// NOTE: This list MUST not be modified. The server aggregates and stores these
+// types over several versions, so we must remain fully compatible with the
+// Autofill server, which is itself backward-compatible. The list must be kept
+// up to date with the Autofill server list.
+//
+// This is the list of all valid field types.
+enum AutofillFieldType {
+ // Server indication that it has no data for the requested field.
+ NO_SERVER_DATA = 0,
+ // Client indication that the text entered did not match anything in the
+ // personal data.
+ UNKNOWN_TYPE = 1,
+ // The "empty" type indicates that the user hasn't entered anything
+ // in this field.
+ EMPTY_TYPE = 2,
+ // Personal Information categorization types.
+ NAME_FIRST = 3,
+ NAME_MIDDLE = 4,
+ NAME_LAST = 5,
+ NAME_MIDDLE_INITIAL = 6,
+ NAME_FULL = 7,
+ NAME_SUFFIX = 8,
+ EMAIL_ADDRESS = 9,
+ PHONE_HOME_NUMBER = 10,
+ PHONE_HOME_CITY_CODE = 11,
+ PHONE_HOME_COUNTRY_CODE = 12,
+ PHONE_HOME_CITY_AND_NUMBER = 13,
+ PHONE_HOME_WHOLE_NUMBER = 14,
+
+ // Work phone numbers (values [15,19]) are deprecated.
+
+ // Fax numbers (values [20,24]) are deprecated in Chrome, but still supported
+ // by the server.
+ PHONE_FAX_NUMBER = 20,
+ PHONE_FAX_CITY_CODE = 21,
+ PHONE_FAX_COUNTRY_CODE = 22,
+ PHONE_FAX_CITY_AND_NUMBER = 23,
+ PHONE_FAX_WHOLE_NUMBER = 24,
+
+ // Cell phone numbers (values [25, 29]) are deprecated.
+
+ ADDRESS_HOME_LINE1 = 30,
+ ADDRESS_HOME_LINE2 = 31,
+ ADDRESS_HOME_APT_NUM = 32,
+ ADDRESS_HOME_CITY = 33,
+ ADDRESS_HOME_STATE = 34,
+ ADDRESS_HOME_ZIP = 35,
+ ADDRESS_HOME_COUNTRY = 36,
+ ADDRESS_BILLING_LINE1 = 37,
+ ADDRESS_BILLING_LINE2 = 38,
+ ADDRESS_BILLING_APT_NUM = 39,
+ ADDRESS_BILLING_CITY = 40,
+ ADDRESS_BILLING_STATE = 41,
+ ADDRESS_BILLING_ZIP = 42,
+ ADDRESS_BILLING_COUNTRY = 43,
+
+ // ADDRESS_SHIPPING values [44,50] are deprecated.
+
+ CREDIT_CARD_NAME = 51,
+ CREDIT_CARD_NUMBER = 52,
+ CREDIT_CARD_EXP_MONTH = 53,
+ CREDIT_CARD_EXP_2_DIGIT_YEAR = 54,
+ CREDIT_CARD_EXP_4_DIGIT_YEAR = 55,
+ CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR = 56,
+ CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR = 57,
+ CREDIT_CARD_TYPE = 58,
+ CREDIT_CARD_VERIFICATION_CODE = 59,
+
+ COMPANY_NAME = 60,
+
+ // Generic type whose default value is known.
+ FIELD_WITH_DEFAULT_VALUE = 61,
+
+ // No new types can be added.
+
+ MAX_VALID_FIELD_TYPE = 62,
+};
+
+typedef std::set<AutofillFieldType> FieldTypeSet;
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_FIELD_TYPES_H_
diff --git a/components/autofill/browser/form_field.cc b/components/autofill/browser/form_field.cc
new file mode 100644
index 0000000..2c1c5da
--- /dev/null
+++ b/components/autofill/browser/form_field.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2011 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/browser/form_field.h"
+
+#include <stddef.h>
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/address_field.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_regexes.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/credit_card_field.h"
+#include "components/autofill/browser/email_field.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/name_field.h"
+#include "components/autofill/browser/phone_field.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+bool IsTextField(const std::string& type) {
+ return type == "text";
+}
+
+bool IsEmailField(const std::string& type) {
+ return type == "email";
+}
+
+bool IsTelephoneField(const std::string& type) {
+ return type == "tel";
+}
+
+bool IsSelectField(const std::string& type) {
+ return type == "select-one";
+}
+
+bool IsCheckable(const AutofillField* field) {
+ return field->is_checkable;
+}
+
+} // namespace
+
+// static
+void FormField::ParseFormFields(const std::vector<AutofillField*>& fields,
+ FieldTypeMap* map) {
+ // Set up a working copy of the fields to be processed.
+ std::vector<const AutofillField*> remaining_fields(fields.size());
+ std::copy(fields.begin(), fields.end(), remaining_fields.begin());
+
+ // Ignore checkable fields as they interfere with parsers assuming context.
+ // Eg., while parsing address, "Is PO box" checkbox after ADDRESS_LINE1
+ // interferes with correctly understanding ADDRESS_LINE2.
+ remaining_fields.erase(
+ std::remove_if(remaining_fields.begin(), remaining_fields.end(),
+ IsCheckable),
+ remaining_fields.end());
+
+ // Email pass.
+ ParseFormFieldsPass(EmailField::Parse, &remaining_fields, map);
+
+ // Phone pass.
+ ParseFormFieldsPass(PhoneField::Parse, &remaining_fields, map);
+
+ // Address pass.
+ ParseFormFieldsPass(AddressField::Parse, &remaining_fields, map);
+
+ // Credit card pass.
+ ParseFormFieldsPass(CreditCardField::Parse, &remaining_fields, map);
+
+ // Name pass.
+ ParseFormFieldsPass(NameField::Parse, &remaining_fields, map);
+}
+
+// static
+bool FormField::ParseField(AutofillScanner* scanner,
+ const string16& pattern,
+ const AutofillField** match) {
+ return ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, match);
+}
+
+// static
+bool FormField::ParseFieldSpecifics(AutofillScanner* scanner,
+ const string16& pattern,
+ int match_type,
+ const AutofillField** match) {
+ if (scanner->IsEnd())
+ return false;
+
+ const AutofillField* field = scanner->Cursor();
+
+ if ((match_type & MATCH_TEXT) && IsTextField(field->form_control_type))
+ return MatchAndAdvance(scanner, pattern, match_type, match);
+
+ if ((match_type & MATCH_EMAIL) && IsEmailField(field->form_control_type))
+ return MatchAndAdvance(scanner, pattern, match_type, match);
+
+ if ((match_type & MATCH_TELEPHONE) &&
+ IsTelephoneField(field->form_control_type)) {
+ return MatchAndAdvance(scanner, pattern, match_type, match);
+ }
+
+ if ((match_type & MATCH_SELECT) && IsSelectField(field->form_control_type))
+ return MatchAndAdvance(scanner, pattern, match_type, match);
+
+ return false;
+}
+
+// static
+bool FormField::ParseEmptyLabel(AutofillScanner* scanner,
+ const AutofillField** match) {
+ return ParseFieldSpecifics(scanner,
+ ASCIIToUTF16("^$"),
+ MATCH_LABEL | MATCH_ALL_INPUTS,
+ match);
+}
+
+// static
+bool FormField::AddClassification(const AutofillField* field,
+ AutofillFieldType type,
+ FieldTypeMap* map) {
+ // Several fields are optional.
+ if (!field)
+ return true;
+
+ return map->insert(make_pair(field->unique_name(), type)).second;
+}
+
+// static.
+bool FormField::MatchAndAdvance(AutofillScanner* scanner,
+ const string16& pattern,
+ int match_type,
+ const AutofillField** match) {
+ const AutofillField* field = scanner->Cursor();
+ if (FormField::Match(field, pattern, match_type)) {
+ if (match)
+ *match = field;
+ scanner->Advance();
+ return true;
+ }
+
+ return false;
+}
+
+// static
+bool FormField::Match(const AutofillField* field,
+ const string16& pattern,
+ int match_type) {
+ if ((match_type & FormField::MATCH_LABEL) &&
+ autofill::MatchesPattern(field->label, pattern)) {
+ return true;
+ }
+
+ if ((match_type & FormField::MATCH_NAME) &&
+ autofill::MatchesPattern(field->name, pattern)) {
+ return true;
+ }
+
+ if ((match_type & FormField::MATCH_VALUE) &&
+ autofill::MatchesPattern(field->value, pattern)) {
+ return true;
+ }
+
+ return false;
+}
+
+// static
+void FormField::ParseFormFieldsPass(ParseFunction parse,
+ std::vector<const AutofillField*>* fields,
+ FieldTypeMap* map) {
+ // Store unmatched fields for further processing by the caller.
+ std::vector<const AutofillField*> remaining_fields;
+
+ AutofillScanner scanner(*fields);
+ while (!scanner.IsEnd()) {
+ scoped_ptr<FormField> form_field(parse(&scanner));
+ if (!form_field.get()) {
+ remaining_fields.push_back(scanner.Cursor());
+ scanner.Advance();
+ continue;
+ }
+
+ // Add entries into the map for each field type found in |form_field|.
+ bool ok = form_field->ClassifyField(map);
+ DCHECK(ok);
+ }
+
+ std::swap(*fields, remaining_fields);
+}
diff --git a/components/autofill/browser/form_field.h b/components/autofill/browser/form_field.h
new file mode 100644
index 0000000..8b9765d
--- /dev/null
+++ b/components/autofill/browser/form_field.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2011 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_BROWSER_FORM_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_FORM_FIELD_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "components/autofill/browser/autofill_type.h"
+
+class AutofillField;
+class AutofillScanner;
+
+// Represents a logical form field in a web form. Classes that implement this
+// interface can identify themselves as a particular type of form field, e.g.
+// name, phone number, or address field.
+class FormField {
+ public:
+ virtual ~FormField() {}
+
+ // Classifies each field in |fields| with its heuristically detected type.
+ // The association is stored into |map|. Each field has a derived unique name
+ // that is used as the key into the |map|.
+ static void ParseFormFields(const std::vector<AutofillField*>& fields,
+ FieldTypeMap* map);
+
+ protected:
+ // A bit-field used for matching specific parts of a field in question.
+ enum MatchType {
+ // Attributes.
+ MATCH_LABEL = 1 << 0,
+ MATCH_NAME = 1 << 1,
+ MATCH_VALUE = 1 << 2,
+
+ // Input types.
+ MATCH_TEXT = 1 << 3,
+ MATCH_EMAIL = 1 << 4,
+ MATCH_TELEPHONE = 1 << 5,
+ MATCH_SELECT = 1 << 6,
+ MATCH_ALL_INPUTS =
+ MATCH_TEXT | MATCH_EMAIL | MATCH_TELEPHONE | MATCH_SELECT,
+
+ // By default match label and name for input/text types.
+ MATCH_DEFAULT = MATCH_LABEL | MATCH_NAME | MATCH_VALUE | MATCH_TEXT,
+ };
+
+ // Only derived classes may instantiate.
+ FormField() {}
+
+ // Attempts to parse a form field with the given pattern. Returns true on
+ // success and fills |match| with a pointer to the field.
+ static bool ParseField(AutofillScanner* scanner,
+ const string16& pattern,
+ const AutofillField** match);
+
+ // Parses the stream of fields in |scanner| with regular expression |pattern|
+ // as specified in the |match_type| bit field (see |MatchType|). If |match|
+ // is non-NULL and the pattern matches, the matched field is returned.
+ // A |true| result is returned in the case of a successful match, false
+ // otherwise.
+ static bool ParseFieldSpecifics(AutofillScanner* scanner,
+ const string16& pattern,
+ int match_type,
+ const AutofillField** match);
+
+ // Attempts to parse a field with an empty label. Returns true
+ // on success and fills |match| with a pointer to the field.
+ static bool ParseEmptyLabel(AutofillScanner* scanner,
+ const AutofillField** match);
+
+ // Adds an association between a field and a type to |map|.
+ static bool AddClassification(const AutofillField* field,
+ AutofillFieldType type,
+ FieldTypeMap* map);
+
+ // Derived classes must implement this interface to supply field type
+ // information. |ParseFormFields| coordinates the parsing and extraction
+ // of types from an input vector of |AutofillField| objects and delegates
+ // the type extraction via this method.
+ virtual bool ClassifyField(FieldTypeMap* map) const = 0;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(FormFieldTest, Match);
+
+ // Function pointer type for the parsing function that should be passed to the
+ // ParseFormFieldsPass() helper function.
+ typedef FormField* ParseFunction(AutofillScanner* scanner);
+
+ // Matches |pattern| to the contents of the field at the head of the
+ // |scanner|.
+ // Returns |true| if a match is found according to |match_type|, and |false|
+ // otherwise.
+ static bool MatchAndAdvance(AutofillScanner* scanner,
+ const string16& pattern,
+ int match_type,
+ const AutofillField** match);
+
+ // Matches the regular expression |pattern| against the components of |field|
+ // as specified in the |match_type| bit field (see |MatchType|).
+ static bool Match(const AutofillField* field,
+ const string16& pattern,
+ int match_type);
+
+ // Perform a "pass" over the |fields| where each pass uses the supplied
+ // |parse| method to match content to a given field type.
+ // |fields| is both an input and an output parameter. Upon exit |fields|
+ // holds any remaining unclassified fields for further processing.
+ // Classification results of the processed fields are stored in |map|.
+ static void ParseFormFieldsPass(ParseFunction parse,
+ std::vector<const AutofillField*>* fields,
+ FieldTypeMap* map);
+
+ DISALLOW_COPY_AND_ASSIGN(FormField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_FORM_FIELD_H_
diff --git a/components/autofill/browser/form_field_unittest.cc b/components/autofill/browser/form_field_unittest.cc
new file mode 100644
index 0000000..b8994bc
--- /dev/null
+++ b/components/autofill/browser/form_field_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_vector.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/form_field.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(FormFieldTest, Match) {
+ AutofillField field;
+
+ // Empty strings match.
+ EXPECT_TRUE(FormField::Match(&field, string16(), FormField::MATCH_LABEL));
+
+ // Empty pattern matches non-empty string.
+ field.label = ASCIIToUTF16("a");
+ EXPECT_TRUE(FormField::Match(&field, string16(), FormField::MATCH_LABEL));
+
+ // Strictly empty pattern matches empty string.
+ field.label = string16();
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^$"),
+ FormField::MATCH_LABEL));
+
+ // Strictly empty pattern does not match non-empty string.
+ field.label = ASCIIToUTF16("a");
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^$"),
+ FormField::MATCH_LABEL));
+
+ // Non-empty pattern doesn't match empty string.
+ field.label = string16();
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("a"),
+ FormField::MATCH_LABEL));
+
+ // Beginning of line.
+ field.label = ASCIIToUTF16("head_tail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head"),
+ FormField::MATCH_LABEL));
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail"),
+ FormField::MATCH_LABEL));
+
+ // End of line.
+ field.label = ASCIIToUTF16("head_tail");
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head$"),
+ FormField::MATCH_LABEL));
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail$"),
+ FormField::MATCH_LABEL));
+
+ // Exact.
+ field.label = ASCIIToUTF16("head_tail");
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^head$"),
+ FormField::MATCH_LABEL));
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("^tail$"),
+ FormField::MATCH_LABEL));
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("^head_tail$"),
+ FormField::MATCH_LABEL));
+
+ // Escaped dots.
+ field.label = ASCIIToUTF16("m.i.");
+ // Note: This pattern is misleading as the "." characters are wild cards.
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."),
+ FormField::MATCH_LABEL));
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."),
+ FormField::MATCH_LABEL));
+ field.label = ASCIIToUTF16("mXiX");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("m.i."),
+ FormField::MATCH_LABEL));
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("m\\.i\\."),
+ FormField::MATCH_LABEL));
+
+ // Repetition.
+ field.label = ASCIIToUTF16("headtail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"),
+ FormField::MATCH_LABEL));
+ field.label = ASCIIToUTF16("headXtail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"),
+ FormField::MATCH_LABEL));
+ field.label = ASCIIToUTF16("headXXXtail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.*tail"),
+ FormField::MATCH_LABEL));
+ field.label = ASCIIToUTF16("headtail");
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("head.+tail"),
+ FormField::MATCH_LABEL));
+ field.label = ASCIIToUTF16("headXtail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.+tail"),
+ FormField::MATCH_LABEL));
+ field.label = ASCIIToUTF16("headXXXtail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head.+tail"),
+ FormField::MATCH_LABEL));
+
+ // Alternation.
+ field.label = ASCIIToUTF16("head_tail");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head|other"),
+ FormField::MATCH_LABEL));
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("tail|other"),
+ FormField::MATCH_LABEL));
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("bad|good"),
+ FormField::MATCH_LABEL));
+
+ // Case sensitivity.
+ field.label = ASCIIToUTF16("xxxHeAd_tAiLxxx");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("head_tail"),
+ FormField::MATCH_LABEL));
+
+ // Word boundaries.
+ field.label = ASCIIToUTF16("contains word:");
+ EXPECT_TRUE(FormField::Match(&field, ASCIIToUTF16("\\bword\\b"),
+ FormField::MATCH_LABEL));
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcon\\b"),
+ FormField::MATCH_LABEL));
+ // Make sure the circumflex in 'crepe' is not treated as a word boundary.
+ field.label = UTF8ToUTF16("cr" "\xC3\xAA" "pe");
+ EXPECT_FALSE(FormField::Match(&field, ASCIIToUTF16("\\bcr\\b"),
+ FormField::MATCH_LABEL));
+}
+
+// Test that we ignore checkable elements.
+TEST(FormFieldTest, ParseFormFields) {
+ ScopedVector<AutofillField> fields;
+ FormFieldData field_data;
+ field_data.form_control_type = "text";
+
+ field_data.label = ASCIIToUTF16("Address line1");
+ fields.push_back(new AutofillField(field_data, field_data.label));
+
+ field_data.is_checkable = true;
+ field_data.label = ASCIIToUTF16("Is PO Box");
+ fields.push_back(new AutofillField(field_data, field_data.label));
+
+ // reset is_checkable to false.
+ field_data.is_checkable = false;
+ field_data.label = ASCIIToUTF16("Address line2");
+ fields.push_back(new AutofillField(field_data, field_data.label));
+
+ FieldTypeMap field_type_map;
+ FormField::ParseFormFields(fields.get(), &field_type_map);
+ // Checkable element shouldn't interfere with inference of Address line2.
+ EXPECT_EQ(2U, field_type_map.size());
+
+ EXPECT_EQ(ADDRESS_HOME_LINE1,
+ field_type_map.find(ASCIIToUTF16("Address line1"))->second);
+ EXPECT_EQ(ADDRESS_HOME_LINE2,
+ field_type_map.find(ASCIIToUTF16("Address line2"))->second);
+}
diff --git a/components/autofill/browser/form_group.cc b/components/autofill/browser/form_group.cc
new file mode 100644
index 0000000..b2f21d2
--- /dev/null
+++ b/components/autofill/browser/form_group.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2011 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/browser/form_group.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/common/form_field_data.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// TODO(jhawkins): Add more states/provinces. See http://crbug.com/45039.
+
+class State {
+ public:
+ const char* name;
+ const char* abbreviation;
+
+ static const State all_states[];
+
+ static string16 Abbreviation(const string16& name);
+ static string16 FullName(const string16& abbreviation);
+};
+
+const State State::all_states[] = {
+ { "alabama", "al" },
+ { "alaska", "ak" },
+ { "arizona", "az" },
+ { "arkansas", "ar" },
+ { "california", "ca" },
+ { "colorado", "co" },
+ { "connecticut", "ct" },
+ { "delaware", "de" },
+ { "district of columbia", "dc" },
+ { "florida", "fl" },
+ { "georgia", "ga" },
+ { "hawaii", "hi" },
+ { "idaho", "id" },
+ { "illinois", "il" },
+ { "indiana", "in" },
+ { "iowa", "ia" },
+ { "kansas", "ks" },
+ { "kentucky", "ky" },
+ { "louisiana", "la" },
+ { "maine", "me" },
+ { "maryland", "md" },
+ { "massachusetts", "ma" },
+ { "michigan", "mi" },
+ { "minnesota", "mv" },
+ { "mississippi", "ms" },
+ { "missouri", "mo" },
+ { "montana", "mt" },
+ { "nebraska", "ne" },
+ { "nevada", "nv" },
+ { "new hampshire", "nh" },
+ { "new jersey", "nj" },
+ { "new mexico", "nm" },
+ { "new york", "ny" },
+ { "north carolina", "nc" },
+ { "north dakota", "nd" },
+ { "ohio", "oh" },
+ { "oklahoma", "ok" },
+ { "oregon", "or" },
+ { "pennsylvania", "pa" },
+ { "puerto rico", "pr" },
+ { "rhode island", "ri" },
+ { "south carolina", "sc" },
+ { "south dakota", "sd" },
+ { "tennessee", "tn" },
+ { "texas", "tx" },
+ { "utah", "ut" },
+ { "vermont", "vt" },
+ { "virginia", "va" },
+ { "washington", "wa" },
+ { "west virginia", "wv" },
+ { "wisconsin", "wi" },
+ { "wyoming", "wy" },
+ { NULL, NULL }
+};
+
+string16 State::Abbreviation(const string16& name) {
+ for (const State* state = all_states; state->name; ++state) {
+ if (LowerCaseEqualsASCII(name, state->name))
+ return ASCIIToUTF16(state->abbreviation);
+ }
+ return string16();
+}
+
+string16 State::FullName(const string16& abbreviation) {
+ for (const State* state = all_states; state->name; ++state) {
+ if (LowerCaseEqualsASCII(abbreviation, state->abbreviation))
+ return ASCIIToUTF16(state->name);
+ }
+ return string16();
+}
+
+const char* const kMonthsAbbreviated[] = {
+ NULL, // Padding so index 1 = month 1 = January.
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+};
+
+const char* const kMonthsFull[] = {
+ NULL, // Padding so index 1 = month 1 = January.
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December",
+};
+
+const char* const kMonthsNumeric[] = {
+ NULL, // Padding so index 1 = month 1 = January.
+ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
+};
+
+// Returns true if the value was successfully set, meaning |value| was found in
+// the list of select options in |field|.
+bool SetSelectControlValue(const string16& value,
+ FormFieldData* field) {
+ string16 value_lowercase = StringToLowerASCII(value);
+
+ DCHECK_EQ(field->option_values.size(), field->option_contents.size());
+ for (size_t i = 0; i < field->option_values.size(); ++i) {
+ if (value_lowercase == StringToLowerASCII(field->option_values[i]) ||
+ value_lowercase == StringToLowerASCII(field->option_contents[i])) {
+ field->value = field->option_values[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool FillStateSelectControl(const string16& value,
+ FormFieldData* field) {
+ string16 abbrev, full;
+ if (value.size() < 4U) {
+ abbrev = value;
+ full = State::FullName(value);
+ } else {
+ abbrev = State::Abbreviation(value);
+ full = value;
+ }
+
+ // Try the abbreviation name first.
+ if (!abbrev.empty() && SetSelectControlValue(abbrev, field))
+ return true;
+
+ if (full.empty())
+ return false;
+
+ return SetSelectControlValue(full, field);
+}
+
+bool FillExpirationMonthSelectControl(const string16& value,
+ FormFieldData* field) {
+ int index = 0;
+ if (!base::StringToInt(value, &index) ||
+ index <= 0 ||
+ static_cast<size_t>(index) >= arraysize(kMonthsFull))
+ return false;
+
+ bool filled =
+ SetSelectControlValue(ASCIIToUTF16(kMonthsAbbreviated[index]), field) ||
+ SetSelectControlValue(ASCIIToUTF16(kMonthsFull[index]), field) ||
+ SetSelectControlValue(ASCIIToUTF16(kMonthsNumeric[index]), field);
+ return filled;
+}
+
+// Try to fill a credit card type |value| (Visa, MasterCard, etc.) into the
+// given |field|.
+bool FillCreditCardTypeSelectControl(const string16& value,
+ FormFieldData* field) {
+ // Try stripping off spaces.
+ string16 value_stripped;
+ RemoveChars(StringToLowerASCII(value), kWhitespaceUTF16, &value_stripped);
+
+ for (size_t i = 0; i < field->option_values.size(); ++i) {
+ string16 option_value_lowercase;
+ RemoveChars(StringToLowerASCII(field->option_values[i]), kWhitespaceUTF16,
+ &option_value_lowercase);
+ string16 option_contents_lowercase;
+ RemoveChars(StringToLowerASCII(field->option_contents[i]), kWhitespaceUTF16,
+ &option_contents_lowercase);
+
+ // Perform a case-insensitive comparison; but fill the form with the
+ // original text, not the lowercased version.
+ if (value_stripped == option_value_lowercase ||
+ value_stripped == option_contents_lowercase) {
+ field->value = field->option_values[i];
+ return true;
+ }
+ }
+
+ // For American Express, also try filling as "AmEx".
+ if (value == l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_AMEX))
+ return FillCreditCardTypeSelectControl(ASCIIToUTF16("AmEx"), field);
+
+ return false;
+}
+
+} // namespace
+
+std::string FormGroup::GetGUID() const {
+ NOTREACHED();
+ return std::string();
+}
+
+void FormGroup::GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const {
+ if (text.empty()) {
+ matching_types->insert(EMPTY_TYPE);
+ return;
+ }
+
+ FieldTypeSet types;
+ GetSupportedTypes(&types);
+ for (FieldTypeSet::const_iterator type = types.begin();
+ type != types.end(); ++type) {
+ // TODO(isherman): Matches are case-sensitive for now. Let's keep an eye on
+ // this and decide whether there are compelling reasons to add case-
+ // insensitivity.
+ if (GetInfo(*type, app_locale) == text)
+ matching_types->insert(*type);
+ }
+}
+
+void FormGroup::GetNonEmptyTypes(const std::string& app_locale,
+ FieldTypeSet* non_empty_types) const {
+ FieldTypeSet types;
+ GetSupportedTypes(&types);
+ for (FieldTypeSet::const_iterator type = types.begin();
+ type != types.end(); ++type) {
+ if (!GetInfo(*type, app_locale).empty())
+ non_empty_types->insert(*type);
+ }
+}
+
+string16 FormGroup::GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const {
+ return GetRawInfo(type);
+}
+
+bool FormGroup::SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) {
+ SetRawInfo(type, value);
+ return true;
+}
+
+void FormGroup::FillFormField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const {
+ NOTREACHED();
+}
+
+void FormGroup::FillSelectControl(AutofillFieldType type,
+ FormFieldData* field) const {
+ DCHECK(field);
+ DCHECK_EQ("select-one", field->form_control_type);
+ DCHECK_EQ(field->option_values.size(), field->option_contents.size());
+
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ string16 field_text = GetInfo(type, app_locale);
+ string16 field_text_lower = StringToLowerASCII(field_text);
+ if (field_text.empty())
+ return;
+
+ string16 value;
+ for (size_t i = 0; i < field->option_values.size(); ++i) {
+ if (field_text == field->option_values[i] ||
+ field_text == field->option_contents[i]) {
+ // An exact match, use it.
+ value = field->option_values[i];
+ break;
+ }
+
+ if (field_text_lower == StringToLowerASCII(field->option_values[i]) ||
+ field_text_lower == StringToLowerASCII(field->option_contents[i])) {
+ // A match, but not in the same case. Save it in case an exact match is
+ // not found.
+ value = field->option_values[i];
+ }
+ }
+
+ if (!value.empty()) {
+ field->value = value;
+ return;
+ }
+
+ if (type == ADDRESS_HOME_STATE || type == ADDRESS_BILLING_STATE) {
+ FillStateSelectControl(field_text, field);
+ } else if (type == ADDRESS_HOME_COUNTRY || type == ADDRESS_BILLING_COUNTRY) {
+ FillCountrySelectControl(field);
+ } else if (type == CREDIT_CARD_EXP_MONTH) {
+ FillExpirationMonthSelectControl(field_text, field);
+ } else if (type == CREDIT_CARD_EXP_4_DIGIT_YEAR) {
+ // Attempt to fill the year as a 2-digit year. This compensates for the
+ // fact that our heuristics do not always correctly detect when a website
+ // requests a 2-digit rather than a 4-digit year.
+ FillSelectControl(CREDIT_CARD_EXP_2_DIGIT_YEAR, field);
+ } else if (type == CREDIT_CARD_TYPE) {
+ FillCreditCardTypeSelectControl(field_text, field);
+ }
+}
+
+bool FormGroup::FillCountrySelectControl(FormFieldData* field_data) const {
+ return false;
+}
+
+// static
+bool FormGroup::IsValidState(const string16& value) {
+ return !State::Abbreviation(value).empty() || !State::FullName(value).empty();
+}
diff --git a/components/autofill/browser/form_group.h b/components/autofill/browser/form_group.h
new file mode 100644
index 0000000..d7f0c8c
--- /dev/null
+++ b/components/autofill/browser/form_group.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2011 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_BROWSER_FORM_GROUP_H_
+#define COMPONENTS_AUTOFILL_BROWSER_FORM_GROUP_H_
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "components/autofill/browser/field_types.h"
+
+class AutofillField;
+struct FormFieldData;
+
+// This class is an interface for collections of form fields, grouped by type.
+// The information in objects of this class is managed by the
+// PersonalDataManager.
+class FormGroup {
+ public:
+ virtual ~FormGroup() {}
+
+ // Returns a globally unique ID for this object. It is an error to call the
+ // default implementation.
+ virtual std::string GetGUID() const;
+
+ // Used to determine the type of a field based on the text that a user enters
+ // into the field, interpreted in the given |app_locale| if appropriate. The
+ // field types can then be reported back to the server. This method is
+ // additive on |matching_types|.
+ virtual void GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const;
+
+ // Returns a set of AutofillFieldTypes for which this FormGroup has non-empty
+ // data. This method is additive on |non_empty_types|.
+ virtual void GetNonEmptyTypes(const std::string& app_locale,
+ FieldTypeSet* non_empty_types) const;
+
+ // Returns the string associated with |type|, without canonicalizing the
+ // returned value. For user-visible strings, use GetInfo() instead.
+ virtual string16 GetRawInfo(AutofillFieldType type) const = 0;
+
+ // Sets this FormGroup object's data for |type| to |value|, without
+ // canonicalizing the |value|. For data that has not already been
+ // canonicalized, use SetInfo() instead.
+ virtual void SetRawInfo(AutofillFieldType type, const string16& value) = 0;
+
+ // Returns the string that should be auto-filled into a text field given the
+ // type of that field, localized to the given |app_locale| if appropriate.
+ virtual string16 GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const;
+
+ // Used to populate this FormGroup object with data. Canonicalizes the data
+ // according to the specified |app_locale| prior to storing, if appropriate.
+ virtual bool SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale);
+
+ // Set |field_data|'s value based on |field| and contents of |this| (using
+ // data variant |variant|). It is an error to call the default implementation.
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ FormFieldData* field_data) const;
+
+ // Fills in select control with data matching |type| from |this|.
+ // Public for testing purposes.
+ void FillSelectControl(AutofillFieldType type,
+ FormFieldData* field_data) const;
+
+ // Returns true if |value| is a valid US state name or abbreviation. It is
+ // case insensitive. Valid for US states only.
+ // TODO(estade): this is a crappy place for this function.
+ static bool IsValidState(const string16& value);
+
+ protected:
+ // AutofillProfile needs to call into GetSupportedTypes() for objects of
+ // non-AutofillProfile type, for which mere inheritance is insufficient.
+ friend class AutofillProfile;
+
+ // Returns a set of AutofillFieldTypes for which this FormGroup can store
+ // data. This method is additive on |supported_types|.
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const = 0;
+
+ // Fills in a select control for a country from data in |this|. Returns true
+ // for success.
+ virtual bool FillCountrySelectControl(FormFieldData* field_data) const;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_FORM_GROUP_H_
diff --git a/components/autofill/browser/form_structure.cc b/components/autofill/browser/form_structure.cc
new file mode 100644
index 0000000..0b24bde
--- /dev/null
+++ b/components/autofill/browser/form_structure.cc
@@ -0,0 +1,1156 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/form_structure.h"
+
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/sha1.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/autofill_xml_parser.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/form_field.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_data_predictions.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/form_field_data_predictions.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+namespace {
+
+const char kFormMethodPost[] = "post";
+
+// XML elements and attributes.
+const char kAttributeAcceptedFeatures[] = "accepts";
+const char kAttributeAutofillUsed[] = "autofillused";
+const char kAttributeAutofillType[] = "autofilltype";
+const char kAttributeClientVersion[] = "clientversion";
+const char kAttributeDataPresent[] = "datapresent";
+const char kAttributeFormSignature[] = "formsignature";
+const char kAttributeSignature[] = "signature";
+const char kAttributeUrlprefixSignature[] = "urlprefixsignature";
+const char kAcceptedFeaturesExperiment[] = "e"; // e=experiments
+const char kAcceptedFeaturesAutocheckoutExperiment[] = "a,e"; // a=autocheckout
+const char kClientVersion[] = "6.1.1715.1442/en (GGLL)";
+const char kXMLDeclaration[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+const char kXMLElementAutofillQuery[] = "autofillquery";
+const char kXMLElementAutofillUpload[] = "autofillupload";
+const char kXMLElementForm[] = "form";
+const char kXMLElementField[] = "field";
+
+// The number of fillable fields necessary for a form to be fillable.
+const size_t kRequiredFillableFields = 3;
+
+// Helper for |EncodeUploadRequest()| that creates a bit field corresponding to
+// |available_field_types| and returns the hex representation as a string.
+std::string EncodeFieldTypes(const FieldTypeSet& available_field_types) {
+ // There are |MAX_VALID_FIELD_TYPE| different field types and 8 bits per byte,
+ // so we need ceil(MAX_VALID_FIELD_TYPE / 8) bytes to encode the bit field.
+ const size_t kNumBytes = (MAX_VALID_FIELD_TYPE + 0x7) / 8;
+
+ // Pack the types in |available_field_types| into |bit_field|.
+ std::vector<uint8> bit_field(kNumBytes, 0);
+ for (FieldTypeSet::const_iterator field_type = available_field_types.begin();
+ field_type != available_field_types.end();
+ ++field_type) {
+ // Set the appropriate bit in the field. The bit we set is the one
+ // |field_type| % 8 from the left of the byte.
+ const size_t byte = *field_type / 8;
+ const size_t bit = 0x80 >> (*field_type % 8);
+ DCHECK(byte < bit_field.size());
+ bit_field[byte] |= bit;
+ }
+
+ // Discard any trailing zeroes.
+ // If there are no available types, we return the empty string.
+ size_t data_end = bit_field.size();
+ for (; data_end > 0 && !bit_field[data_end - 1]; --data_end) {
+ }
+
+ // Print all meaningfull bytes into a string.
+ std::string data_presence;
+ data_presence.reserve(data_end * 2 + 1);
+ for (size_t i = 0; i < data_end; ++i) {
+ base::StringAppendF(&data_presence, "%02x", bit_field[i]);
+ }
+
+ return data_presence;
+}
+
+// Returns |true| iff the |token| is a type hint for a contact field, as
+// specified in the implementation section of http://is.gd/whatwg_autocomplete
+// Note that "fax" and "pager" are intentionally ignored, as Chrome does not
+// support filling either type of information.
+bool IsContactTypeHint(const std::string& token) {
+ return token == "home" || token == "work" || token == "mobile";
+}
+
+// Returns |true| iff the |token| is a type hint appropriate for a field of the
+// given |field_type|, as specified in the implementation section of
+// http://is.gd/whatwg_autocomplete
+bool ContactTypeHintMatchesFieldType(const std::string& token,
+ AutofillFieldType field_type) {
+ // The "home" and "work" type hints are only appropriate for email and phone
+ // number field types.
+ if (token == "home" || token == "work") {
+ return field_type == EMAIL_ADDRESS ||
+ (field_type >= PHONE_HOME_NUMBER &&
+ field_type <= PHONE_HOME_WHOLE_NUMBER);
+ }
+
+ // The "mobile" type hint is only appropriate for phone number field types.
+ // Note that "fax" and "pager" are intentionally ignored, as Chrome does not
+ // support filling either type of information.
+ if (token == "mobile") {
+ return field_type >= PHONE_HOME_NUMBER &&
+ field_type <= PHONE_HOME_WHOLE_NUMBER;
+ }
+
+ return false;
+}
+
+// Returns the Chrome Autofill-supported field type corresponding to the given
+// |autocomplete_type|, if there is one, in the context of the given |field|.
+// Chrome Autofill supports a subset of the field types listed at
+// http://is.gd/whatwg_autocomplete
+AutofillFieldType FieldTypeFromAutocompleteType(
+ const std::string& autocomplete_type,
+ const AutofillField& field) {
+ if (autocomplete_type == "name")
+ return NAME_FULL;
+
+ if (autocomplete_type == "given-name")
+ return NAME_FIRST;
+
+ if (autocomplete_type == "additional-name") {
+ if (field.max_length == 1)
+ return NAME_MIDDLE_INITIAL;
+ else
+ return NAME_MIDDLE;
+ }
+
+ if (autocomplete_type == "family-name")
+ return NAME_LAST;
+
+ if (autocomplete_type == "honorific-suffix")
+ return NAME_SUFFIX;
+
+ if (autocomplete_type == "organization")
+ return COMPANY_NAME;
+
+ if (autocomplete_type == "street-address" ||
+ autocomplete_type == "address-line1")
+ return ADDRESS_HOME_LINE1;
+
+ if (autocomplete_type == "address-line2")
+ return ADDRESS_HOME_LINE2;
+
+ if (autocomplete_type == "locality")
+ return ADDRESS_HOME_CITY;
+
+ if (autocomplete_type == "region")
+ return ADDRESS_HOME_STATE;
+
+ if (autocomplete_type == "country")
+ return ADDRESS_HOME_COUNTRY;
+
+ if (autocomplete_type == "postal-code")
+ return ADDRESS_HOME_ZIP;
+
+ if (autocomplete_type == "cc-name")
+ return CREDIT_CARD_NAME;
+
+ if (autocomplete_type == "cc-number")
+ return CREDIT_CARD_NUMBER;
+
+ if (autocomplete_type == "cc-exp") {
+ if (field.max_length == 5)
+ return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
+ else
+ return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
+ }
+
+ if (autocomplete_type == "cc-exp-month")
+ return CREDIT_CARD_EXP_MONTH;
+
+ if (autocomplete_type == "cc-exp-year") {
+ if (field.max_length == 2)
+ return CREDIT_CARD_EXP_2_DIGIT_YEAR;
+ else
+ return CREDIT_CARD_EXP_4_DIGIT_YEAR;
+ }
+
+ if (autocomplete_type == "cc-csc")
+ return CREDIT_CARD_VERIFICATION_CODE;
+
+ if (autocomplete_type == "cc-type")
+ return CREDIT_CARD_TYPE;
+
+ if (autocomplete_type == "tel")
+ return PHONE_HOME_WHOLE_NUMBER;
+
+ if (autocomplete_type == "tel-country-code")
+ return PHONE_HOME_COUNTRY_CODE;
+
+ if (autocomplete_type == "tel-national")
+ return PHONE_HOME_CITY_AND_NUMBER;
+
+ if (autocomplete_type == "tel-area-code")
+ return PHONE_HOME_CITY_CODE;
+
+ if (autocomplete_type == "tel-local")
+ return PHONE_HOME_NUMBER;
+
+ if (autocomplete_type == "tel-local-prefix")
+ return PHONE_HOME_NUMBER;
+
+ if (autocomplete_type == "tel-local-suffix")
+ return PHONE_HOME_NUMBER;
+
+ if (autocomplete_type == "email")
+ return EMAIL_ADDRESS;
+
+ return UNKNOWN_TYPE;
+}
+
+} // namespace
+
+FormStructure::FormStructure(const FormData& form,
+ const std::string& autocheckout_url_prefix)
+ : form_name_(form.name),
+ source_url_(form.origin),
+ target_url_(form.action),
+ autofill_count_(0),
+ checkable_field_count_(0),
+ upload_required_(USE_UPLOAD_RATES),
+ server_experiment_id_("no server response"),
+ has_author_specified_types_(false),
+ autocheckout_url_prefix_(autocheckout_url_prefix) {
+ // Copy the form fields.
+ std::map<string16, size_t> unique_names;
+ for (std::vector<FormFieldData>::const_iterator field =
+ form.fields.begin();
+ field != form.fields.end(); field++) {
+
+ // Skipping checkable elements when Autocheckout is not enabled, else
+ // these fields will interfere with existing field signatures with Autofill
+ // servers.
+ if (!field->is_checkable || IsAutocheckoutEnabled()) {
+ // Add all supported form fields (including with empty names) to the
+ // signature. This is a requirement for Autofill servers.
+ form_signature_field_names_.append("&");
+ form_signature_field_names_.append(UTF16ToUTF8(field->name));
+ }
+
+ // Generate a unique name for this field by appending a counter to the name.
+ // Make sure to prepend the counter with a non-numeric digit so that we are
+ // guaranteed to avoid collisions.
+ if (!unique_names.count(field->name))
+ unique_names[field->name] = 1;
+ else
+ ++unique_names[field->name];
+ string16 unique_name = field->name + ASCIIToUTF16("_") +
+ base::IntToString16(unique_names[field->name]);
+ fields_.push_back(new AutofillField(*field, unique_name));
+
+ if (field->is_checkable)
+ ++checkable_field_count_;
+ }
+
+ std::string method = UTF16ToUTF8(form.method);
+ if (StringToLowerASCII(method) == kFormMethodPost) {
+ method_ = POST;
+ } else {
+ // Either the method is 'get', or we don't know. In this case we default
+ // to GET.
+ method_ = GET;
+ }
+}
+
+FormStructure::~FormStructure() {}
+
+void FormStructure::DetermineHeuristicTypes(
+ const AutofillMetrics& metric_logger) {
+ // First, try to detect field types based on each field's |autocomplete|
+ // attribute value. If there is at least one form field that specifies an
+ // autocomplete type hint, don't try to apply other heuristics to match fields
+ // in this form.
+ bool has_author_specified_sections;
+ ParseFieldTypesFromAutocompleteAttributes(&has_author_specified_types_,
+ &has_author_specified_sections);
+
+ if (!has_author_specified_types_) {
+ FieldTypeMap field_type_map;
+ FormField::ParseFormFields(fields_.get(), &field_type_map);
+ for (size_t index = 0; index < field_count(); index++) {
+ AutofillField* field = fields_[index];
+ FieldTypeMap::iterator iter = field_type_map.find(field->unique_name());
+ if (iter != field_type_map.end())
+ field->set_heuristic_type(iter->second);
+ }
+ }
+
+ UpdateAutofillCount();
+ IdentifySections(has_author_specified_sections);
+
+ if (IsAutofillable(true)) {
+ metric_logger.LogDeveloperEngagementMetric(
+ AutofillMetrics::FILLABLE_FORM_PARSED);
+ if (has_author_specified_types_) {
+ metric_logger.LogDeveloperEngagementMetric(
+ AutofillMetrics::FILLABLE_FORM_CONTAINS_TYPE_HINTS);
+ }
+ }
+}
+
+bool FormStructure::EncodeUploadRequest(
+ const FieldTypeSet& available_field_types,
+ bool form_was_autofilled,
+ std::string* encoded_xml) const {
+ if (!ShouldBeCrowdsourced()) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Verify that |available_field_types| agrees with the possible field types we
+ // are uploading.
+ for (std::vector<AutofillField*>::const_iterator field = begin();
+ field != end();
+ ++field) {
+ for (FieldTypeSet::const_iterator type = (*field)->possible_types().begin();
+ type != (*field)->possible_types().end();
+ ++type) {
+ DCHECK(*type == UNKNOWN_TYPE ||
+ *type == EMPTY_TYPE ||
+ available_field_types.count(*type));
+ }
+ }
+
+ // Set up the <autofillupload> element and its attributes.
+ buzz::XmlElement autofill_request_xml(
+ (buzz::QName(kXMLElementAutofillUpload)));
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeClientVersion),
+ kClientVersion);
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeFormSignature),
+ FormSignature());
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeAutofillUsed),
+ form_was_autofilled ? "true" : "false");
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeDataPresent),
+ EncodeFieldTypes(available_field_types).c_str());
+
+ if (!EncodeFormRequest(FormStructure::UPLOAD, &autofill_request_xml))
+ return false; // Malformed form, skip it.
+
+ // Obtain the XML structure as a string.
+ encoded_xml->clear();
+ *encoded_xml = kXMLDeclaration;
+ *encoded_xml += autofill_request_xml.Str().c_str();
+
+ // To enable this logging, run with the flag --vmodule="form_structure=2".
+ VLOG(2) << "\n" << *encoded_xml;
+
+ return true;
+}
+
+// static
+bool FormStructure::EncodeQueryRequest(
+ const std::vector<FormStructure*>& forms,
+ std::vector<std::string>* encoded_signatures,
+ std::string* encoded_xml) {
+ DCHECK(encoded_signatures);
+ DCHECK(encoded_xml);
+ encoded_xml->clear();
+ encoded_signatures->clear();
+ encoded_signatures->reserve(forms.size());
+
+ // Set up the <autofillquery> element and attributes.
+ buzz::XmlElement autofill_request_xml(
+ (buzz::QName(kXMLElementAutofillQuery)));
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeClientVersion),
+ kClientVersion);
+
+ // autocheckout_url_prefix tells the Autofill server where the forms in the
+ // request came from, and the the Autofill server checks internal status and
+ // decide to enable Autocheckout or not and may return Autocheckout related
+ // data in the response accordingly.
+ // There is no page/frame level object associated with FormStructure that
+ // we could extract URL prefix from. But, all the forms should come from the
+ // same frame, so they should have the same Autocheckout URL prefix. Thus we
+ // use URL prefix from the first form with Autocheckout enabled.
+ std::string autocheckout_url_prefix;
+
+ // Some badly formatted web sites repeat forms - detect that and encode only
+ // one form as returned data would be the same for all the repeated forms.
+ std::set<std::string> processed_forms;
+ for (ScopedVector<FormStructure>::const_iterator it = forms.begin();
+ it != forms.end();
+ ++it) {
+ std::string signature((*it)->FormSignature());
+ if (processed_forms.find(signature) != processed_forms.end())
+ continue;
+ processed_forms.insert(signature);
+ scoped_ptr<buzz::XmlElement> encompassing_xml_element(
+ new buzz::XmlElement(buzz::QName(kXMLElementForm)));
+ encompassing_xml_element->SetAttr(buzz::QName(kAttributeSignature),
+ signature);
+
+ if (!(*it)->EncodeFormRequest(FormStructure::QUERY,
+ encompassing_xml_element.get()))
+ continue; // Malformed form, skip it.
+
+ if ((*it)->IsAutocheckoutEnabled()) {
+ if (autocheckout_url_prefix.empty()) {
+ autocheckout_url_prefix = (*it)->autocheckout_url_prefix_;
+ } else {
+ // Making sure all the forms in the request has the same url_prefix.
+ DCHECK_EQ(autocheckout_url_prefix, (*it)->autocheckout_url_prefix_);
+ }
+ }
+
+ autofill_request_xml.AddElement(encompassing_xml_element.release());
+ encoded_signatures->push_back(signature);
+ }
+
+ if (!encoded_signatures->size())
+ return false;
+
+ if (autocheckout_url_prefix.empty()) {
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeAcceptedFeatures),
+ kAcceptedFeaturesExperiment);
+ } else {
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeAcceptedFeatures),
+ kAcceptedFeaturesAutocheckoutExperiment);
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeUrlprefixSignature),
+ Hash64Bit(autocheckout_url_prefix));
+ }
+
+ // Obtain the XML structure as a string.
+ *encoded_xml = kXMLDeclaration;
+ *encoded_xml += autofill_request_xml.Str().c_str();
+
+ return true;
+}
+
+// static
+void FormStructure::ParseQueryResponse(
+ const std::string& response_xml,
+ const std::vector<FormStructure*>& forms,
+ autofill::AutocheckoutPageMetaData* page_meta_data,
+ const AutofillMetrics& metric_logger) {
+ metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_RECEIVED);
+
+ // Parse the field types from the server response to the query.
+ std::vector<AutofillServerFieldInfo> field_infos;
+ UploadRequired upload_required;
+ std::string experiment_id;
+ AutofillQueryXmlParser parse_handler(&field_infos, &upload_required,
+ &experiment_id);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(response_xml.c_str(), response_xml.length(), true);
+ if (!parse_handler.succeeded())
+ return;
+
+ page_meta_data->current_page_number = parse_handler.current_page_number();
+ page_meta_data->total_pages = parse_handler.total_pages();
+ if (parse_handler.proceed_element_descriptor()) {
+ page_meta_data->proceed_element_descriptor.reset(
+ new autofill::WebElementDescriptor(
+ *parse_handler.proceed_element_descriptor()));
+ } else {
+ page_meta_data->proceed_element_descriptor.reset();
+ }
+
+ metric_logger.LogServerQueryMetric(AutofillMetrics::QUERY_RESPONSE_PARSED);
+ metric_logger.LogServerExperimentIdForQuery(experiment_id);
+
+ bool heuristics_detected_fillable_field = false;
+ bool query_response_overrode_heuristics = false;
+
+ // Copy the field types into the actual form.
+ std::vector<AutofillServerFieldInfo>::iterator current_info =
+ field_infos.begin();
+ for (std::vector<FormStructure*>::const_iterator iter = forms.begin();
+ iter != forms.end(); ++iter) {
+ FormStructure* form = *iter;
+ form->upload_required_ = upload_required;
+ form->server_experiment_id_ = experiment_id;
+
+ for (std::vector<AutofillField*>::iterator field = form->fields_.begin();
+ field != form->fields_.end(); ++field, ++current_info) {
+ // In some cases *successful* response does not return all the fields.
+ // Quit the update of the types then.
+ if (current_info == field_infos.end())
+ break;
+
+ // UNKNOWN_TYPE is reserved for use by the client.
+ DCHECK_NE(current_info->field_type, UNKNOWN_TYPE);
+
+ AutofillFieldType heuristic_type = (*field)->type();
+ if (heuristic_type != UNKNOWN_TYPE)
+ heuristics_detected_fillable_field = true;
+
+ (*field)->set_server_type(current_info->field_type);
+ if (heuristic_type != (*field)->type())
+ query_response_overrode_heuristics = true;
+
+ // Copy default value into the field if available.
+ if (!current_info->default_value.empty())
+ (*field)->set_default_value(current_info->default_value);
+ }
+
+ form->UpdateAutofillCount();
+ form->IdentifySections(false);
+ }
+
+ AutofillMetrics::ServerQueryMetric metric;
+ if (query_response_overrode_heuristics) {
+ if (heuristics_detected_fillable_field) {
+ metric = AutofillMetrics::QUERY_RESPONSE_OVERRODE_LOCAL_HEURISTICS;
+ } else {
+ metric = AutofillMetrics::QUERY_RESPONSE_WITH_NO_LOCAL_HEURISTICS;
+ }
+ } else {
+ metric = AutofillMetrics::QUERY_RESPONSE_MATCHED_LOCAL_HEURISTICS;
+ }
+ metric_logger.LogServerQueryMetric(metric);
+}
+
+// static
+void FormStructure::GetFieldTypePredictions(
+ const std::vector<FormStructure*>& form_structures,
+ std::vector<FormDataPredictions>* forms) {
+ forms->clear();
+ forms->reserve(form_structures.size());
+ for (size_t i = 0; i < form_structures.size(); ++i) {
+ FormStructure* form_structure = form_structures[i];
+ FormDataPredictions form;
+ form.data.name = form_structure->form_name_;
+ form.data.method =
+ ASCIIToUTF16((form_structure->method_ == POST) ? "POST" : "GET");
+ form.data.origin = form_structure->source_url_;
+ form.data.action = form_structure->target_url_;
+ form.signature = form_structure->FormSignature();
+ form.experiment_id = form_structure->server_experiment_id_;
+
+ for (std::vector<AutofillField*>::const_iterator field =
+ form_structure->fields_.begin();
+ field != form_structure->fields_.end(); ++field) {
+ form.data.fields.push_back(FormFieldData(**field));
+
+ FormFieldDataPredictions annotated_field;
+ annotated_field.signature = (*field)->FieldSignature();
+ annotated_field.heuristic_type =
+ AutofillType::FieldTypeToString((*field)->heuristic_type());
+ annotated_field.server_type =
+ AutofillType::FieldTypeToString((*field)->server_type());
+ annotated_field.overall_type =
+ AutofillType::FieldTypeToString((*field)->type());
+ form.fields.push_back(annotated_field);
+ }
+
+ forms->push_back(form);
+ }
+}
+
+std::string FormStructure::FormSignature() const {
+ std::string scheme(target_url_.scheme());
+ std::string host(target_url_.host());
+
+ // If target host or scheme is empty, set scheme and host of source url.
+ // This is done to match the Toolbar's behavior.
+ if (scheme.empty() || host.empty()) {
+ scheme = source_url_.scheme();
+ host = source_url_.host();
+ }
+
+ std::string form_string = scheme + "://" + host + "&" +
+ UTF16ToUTF8(form_name_) +
+ form_signature_field_names_;
+
+ return Hash64Bit(form_string);
+}
+
+bool FormStructure::IsAutocheckoutEnabled() const {
+ return !autocheckout_url_prefix_.empty();
+}
+
+size_t FormStructure::RequiredFillableFields() const {
+ return IsAutocheckoutEnabled() ? 0 : kRequiredFillableFields;
+}
+
+bool FormStructure::IsAutofillable(bool require_method_post) const {
+ if (autofill_count() < RequiredFillableFields())
+ return false;
+
+ return ShouldBeParsed(require_method_post);
+}
+
+void FormStructure::UpdateAutofillCount() {
+ autofill_count_ = 0;
+ for (std::vector<AutofillField*>::const_iterator iter = begin();
+ iter != end(); ++iter) {
+ AutofillField* field = *iter;
+ if (field && field->IsFieldFillable())
+ ++autofill_count_;
+ }
+}
+
+bool FormStructure::ShouldBeParsed(bool require_method_post) const {
+ // Ignore counting checkable elements towards minimum number of elements
+ // required to parse. This avoids trying to crowdsource forms with few text
+ // or select elements.
+ if ((field_count() - checkable_field_count()) < RequiredFillableFields())
+ return false;
+
+ // Rule out http(s)://*/search?...
+ // e.g. http://www.google.com/search?q=...
+ // http://search.yahoo.com/search?p=...
+ if (target_url_.path() == "/search")
+ return false;
+
+ if (!IsAutocheckoutEnabled()) {
+ // Make sure there is at least one text field when Autocheckout is
+ // not enabled.
+ bool has_text_field = false;
+ for (std::vector<AutofillField*>::const_iterator it = begin();
+ it != end() && !has_text_field; ++it) {
+ has_text_field |= (*it)->form_control_type != "select-one";
+ }
+ if (!has_text_field)
+ return false;
+ }
+
+ return !require_method_post || (method_ == POST);
+}
+
+bool FormStructure::ShouldBeCrowdsourced() const {
+ return !has_author_specified_types_ && ShouldBeParsed(true);
+}
+
+void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
+ // Map from field signatures to cached fields.
+ std::map<std::string, 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->FieldSignature()] = field;
+ }
+
+ for (std::vector<AutofillField*>::const_iterator iter = begin();
+ iter != end(); ++iter) {
+ AutofillField* field = *iter;
+
+ std::map<std::string, const AutofillField*>::const_iterator
+ cached_field = cached_fields.find(field->FieldSignature());
+ if (cached_field != cached_fields.end()) {
+ if (field->form_control_type != "select-one" &&
+ field->value == cached_field->second->value) {
+ // From the perspective of learning user data, text fields containing
+ // default values are equivalent to empty fields.
+ field->value = string16();
+ }
+
+ field->set_heuristic_type(cached_field->second->heuristic_type());
+ field->set_server_type(cached_field->second->server_type());
+ }
+ }
+
+ UpdateAutofillCount();
+
+ server_experiment_id_ = cached_form.server_experiment_id();
+
+ // The form signature should match between query and upload requests to the
+ // server. On many websites, form elements are dynamically added, removed, or
+ // 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_field_names_ = cached_form.form_signature_field_names_;
+}
+
+void FormStructure::LogQualityMetrics(
+ const AutofillMetrics& metric_logger,
+ const base::TimeTicks& load_time,
+ const base::TimeTicks& interaction_time,
+ const base::TimeTicks& submission_time) const {
+ std::string experiment_id = server_experiment_id();
+ metric_logger.LogServerExperimentIdForUpload(experiment_id);
+
+ size_t num_detected_field_types = 0;
+ bool did_autofill_all_possible_fields = true;
+ bool did_autofill_some_possible_fields = false;
+ for (size_t i = 0; i < field_count(); ++i) {
+ const AutofillField* field = this->field(i);
+ metric_logger.LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED,
+ experiment_id);
+
+ // 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.
+ const FieldTypeSet& field_types = field->possible_types();
+ DCHECK(!field_types.empty());
+ if (field_types.count(EMPTY_TYPE) || field_types.count(UNKNOWN_TYPE))
+ continue;
+
+ ++num_detected_field_types;
+ if (field->is_autofilled)
+ did_autofill_some_possible_fields = true;
+ else
+ did_autofill_all_possible_fields = false;
+
+ // Collapse field types that Chrome treats as identical, e.g. home and
+ // billing address fields.
+ FieldTypeSet collapsed_field_types;
+ for (FieldTypeSet::const_iterator it = field_types.begin();
+ it != field_types.end();
+ ++it) {
+ // Since we currently only support US phone numbers, the (city code + main
+ // digits) number is almost always identical to the whole phone number.
+ // TODO(isherman): Improve this logic once we add support for
+ // international numbers.
+ if (*it == PHONE_HOME_CITY_AND_NUMBER)
+ collapsed_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
+ else
+ collapsed_field_types.insert(AutofillType::GetEquivalentFieldType(*it));
+ }
+
+ // Capture the field's type, if it is unambiguous.
+ AutofillFieldType field_type = UNKNOWN_TYPE;
+ if (collapsed_field_types.size() == 1)
+ field_type = *collapsed_field_types.begin();
+
+ AutofillFieldType heuristic_type = field->heuristic_type();
+ AutofillFieldType server_type = field->server_type();
+ AutofillFieldType predicted_type = field->type();
+
+ // Log heuristic, server, and overall type quality metrics, independently of
+ // whether the field was autofilled.
+ if (heuristic_type == UNKNOWN_TYPE) {
+ metric_logger.LogHeuristicTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ field_type, experiment_id);
+ } else if (field_types.count(heuristic_type)) {
+ metric_logger.LogHeuristicTypePrediction(AutofillMetrics::TYPE_MATCH,
+ field_type, experiment_id);
+ } else {
+ metric_logger.LogHeuristicTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ field_type, experiment_id);
+ }
+
+ if (server_type == NO_SERVER_DATA) {
+ metric_logger.LogServerTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ field_type, experiment_id);
+ } else if (field_types.count(server_type)) {
+ metric_logger.LogServerTypePrediction(AutofillMetrics::TYPE_MATCH,
+ field_type, experiment_id);
+ } else {
+ metric_logger.LogServerTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ field_type, experiment_id);
+ }
+
+ if (predicted_type == UNKNOWN_TYPE) {
+ metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_UNKNOWN,
+ field_type, experiment_id);
+ } else if (field_types.count(predicted_type)) {
+ metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MATCH,
+ field_type, experiment_id);
+ } else {
+ metric_logger.LogOverallTypePrediction(AutofillMetrics::TYPE_MISMATCH,
+ field_type, experiment_id);
+ }
+
+ // TODO(isherman): <select> fields don't support |is_autofilled()|, so we
+ // have to skip them for the remaining metrics.
+ if (field->form_control_type == "select-one")
+ continue;
+
+ if (field->is_autofilled) {
+ metric_logger.LogQualityMetric(AutofillMetrics::FIELD_AUTOFILLED,
+ experiment_id);
+ } else {
+ metric_logger.LogQualityMetric(AutofillMetrics::FIELD_NOT_AUTOFILLED,
+ experiment_id);
+
+ if (heuristic_type == UNKNOWN_TYPE) {
+ metric_logger.LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_UNKNOWN,
+ experiment_id);
+ } else if (field_types.count(heuristic_type)) {
+ metric_logger.LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MATCH,
+ experiment_id);
+ } else {
+ metric_logger.LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_HEURISTIC_TYPE_MISMATCH,
+ experiment_id);
+ }
+
+ if (server_type == NO_SERVER_DATA) {
+ metric_logger.LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_UNKNOWN,
+ experiment_id);
+ } else if (field_types.count(server_type)) {
+ metric_logger.LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MATCH,
+ experiment_id);
+ } else {
+ metric_logger.LogQualityMetric(
+ AutofillMetrics::NOT_AUTOFILLED_SERVER_TYPE_MISMATCH,
+ experiment_id);
+ }
+ }
+ }
+
+ if (num_detected_field_types < RequiredFillableFields()) {
+ metric_logger.LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM);
+ } else {
+ if (did_autofill_all_possible_fields) {
+ metric_logger.LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL);
+ } else if (did_autofill_some_possible_fields) {
+ metric_logger.LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME);
+ } else {
+ metric_logger.LogUserHappinessMetric(
+ AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE);
+ }
+
+ // Unlike the other times, the |submission_time| should always be available.
+ DCHECK(!submission_time.is_null());
+
+ // The |load_time| might be unset, in the case that the form was dynamically
+ // added to the DOM.
+ if (!load_time.is_null()) {
+ // Submission should always chronologically follow form load.
+ DCHECK(submission_time > load_time);
+ base::TimeDelta elapsed = submission_time - load_time;
+ if (did_autofill_some_possible_fields)
+ metric_logger.LogFormFillDurationFromLoadWithAutofill(elapsed);
+ else
+ metric_logger.LogFormFillDurationFromLoadWithoutAutofill(elapsed);
+ }
+
+ // The |interaction_time| might be unset, in the case that the user
+ // submitted a blank form.
+ if (!interaction_time.is_null()) {
+ // Submission should always chronologically follow interaction.
+ DCHECK(submission_time > interaction_time);
+ base::TimeDelta elapsed = submission_time - interaction_time;
+ if (did_autofill_some_possible_fields) {
+ metric_logger.LogFormFillDurationFromInteractionWithAutofill(elapsed);
+ } else {
+ metric_logger.LogFormFillDurationFromInteractionWithoutAutofill(
+ elapsed);
+ }
+ }
+ }
+}
+
+const AutofillField* FormStructure::field(size_t index) const {
+ if (index >= fields_.size()) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return fields_[index];
+}
+
+AutofillField* FormStructure::field(size_t index) {
+ return const_cast<AutofillField*>(
+ static_cast<const FormStructure*>(this)->field(index));
+}
+
+size_t FormStructure::field_count() const {
+ return fields_.size();
+}
+
+size_t FormStructure::checkable_field_count() const {
+ return checkable_field_count_;
+}
+
+std::string FormStructure::server_experiment_id() const {
+ return server_experiment_id_;
+}
+
+FormData FormStructure::ToFormData() const {
+ // |data.user_submitted| will always be false.
+ FormData data;
+ data.name = form_name_;
+ data.origin = source_url_;
+ data.action = target_url_;
+ data.method = ASCIIToUTF16(method_ == POST ? "POST" : "GET");
+
+ for (size_t i = 0; i < fields_.size(); ++i) {
+ data.fields.push_back(FormFieldData(*fields_[i]));
+ }
+
+ return data;
+}
+
+bool FormStructure::operator==(const FormData& form) const {
+ // TODO(jhawkins): Is this enough to differentiate a form?
+ if (form_name_ == form.name &&
+ source_url_ == form.origin &&
+ target_url_ == form.action) {
+ return true;
+ }
+
+ // TODO(jhawkins): Compare field names, IDs and labels once we have labels
+ // set up.
+
+ return false;
+}
+
+bool FormStructure::operator!=(const FormData& form) const {
+ return !operator==(form);
+}
+
+std::string FormStructure::Hash64Bit(const std::string& str) {
+ std::string hash_bin = base::SHA1HashString(str);
+ DCHECK_EQ(20U, hash_bin.length());
+
+ uint64 hash64 = (((static_cast<uint64>(hash_bin[0])) & 0xFF) << 56) |
+ (((static_cast<uint64>(hash_bin[1])) & 0xFF) << 48) |
+ (((static_cast<uint64>(hash_bin[2])) & 0xFF) << 40) |
+ (((static_cast<uint64>(hash_bin[3])) & 0xFF) << 32) |
+ (((static_cast<uint64>(hash_bin[4])) & 0xFF) << 24) |
+ (((static_cast<uint64>(hash_bin[5])) & 0xFF) << 16) |
+ (((static_cast<uint64>(hash_bin[6])) & 0xFF) << 8) |
+ ((static_cast<uint64>(hash_bin[7])) & 0xFF);
+
+ return base::Uint64ToString(hash64);
+}
+
+bool FormStructure::EncodeFormRequest(
+ FormStructure::EncodeRequestType request_type,
+ buzz::XmlElement* encompassing_xml_element) const {
+ if (!field_count()) // Nothing to add.
+ return false;
+
+ // Some badly formatted web sites repeat fields - limit number of fields to
+ // 48, which is far larger than any valid form and XML still fits into 2K.
+ // Do not send requests for forms with more than this many fields, as they are
+ // near certainly not valid/auto-fillable.
+ const size_t kMaxFieldsOnTheForm = 48;
+ if (field_count() > kMaxFieldsOnTheForm)
+ return false;
+
+ // Add the child nodes for the form fields.
+ for (size_t index = 0; index < field_count(); ++index) {
+ const AutofillField* field = fields_[index];
+ if (request_type == FormStructure::UPLOAD) {
+ // Don't upload checkable fields.
+ if (field->is_checkable)
+ continue;
+
+ FieldTypeSet types = field->possible_types();
+ // |types| could be empty in unit-tests only.
+ for (FieldTypeSet::iterator field_type = types.begin();
+ field_type != types.end(); ++field_type) {
+ buzz::XmlElement *field_element = new buzz::XmlElement(
+ buzz::QName(kXMLElementField));
+
+ field_element->SetAttr(buzz::QName(kAttributeSignature),
+ field->FieldSignature());
+ field_element->SetAttr(buzz::QName(kAttributeAutofillType),
+ base::IntToString(*field_type));
+ encompassing_xml_element->AddElement(field_element);
+ }
+ } else {
+ // Skip putting checkable fields in the request if Autocheckout is not
+ // enabled.
+ if (field->is_checkable && !IsAutocheckoutEnabled())
+ continue;
+
+ buzz::XmlElement *field_element = new buzz::XmlElement(
+ buzz::QName(kXMLElementField));
+ field_element->SetAttr(buzz::QName(kAttributeSignature),
+ field->FieldSignature());
+ encompassing_xml_element->AddElement(field_element);
+ }
+ }
+ return true;
+}
+
+void FormStructure::ParseFieldTypesFromAutocompleteAttributes(
+ bool* found_types,
+ bool* found_sections) {
+ const std::string kDefaultSection = "-default";
+
+ *found_types = false;
+ *found_sections = false;
+ for (std::vector<AutofillField*>::iterator it = fields_.begin();
+ it != fields_.end(); ++it) {
+ AutofillField* field = *it;
+
+ // To prevent potential section name collisions, add a default suffix for
+ // other fields. Without this, 'autocomplete' attribute values
+ // "section--shipping street-address" and "shipping street-address" would be
+ // parsed identically, given the section handling code below. We do this
+ // before any validation so that fields with invalid attributes still end up
+ // in the default section. These default section names will be overridden
+ // by subsequent heuristic parsing steps if there are no author-specified
+ // section names.
+ field->set_section(kDefaultSection);
+
+ // Canonicalize the attribute value by trimming whitespace, collapsing
+ // non-space characters (e.g. tab) to spaces, and converting to lowercase.
+ std::string autocomplete_attribute =
+ CollapseWhitespaceASCII(field->autocomplete_attribute, false);
+ autocomplete_attribute = StringToLowerASCII(autocomplete_attribute);
+
+ // The autocomplete attribute is overloaded: it can specify either a field
+ // type hint or whether autocomplete should be enabled at all. Ignore the
+ // latter type of attribute value.
+ if (autocomplete_attribute.empty() ||
+ autocomplete_attribute == "on" ||
+ autocomplete_attribute == "off") {
+ continue;
+ }
+
+ // Any other value, even it is invalid, is considered to be a type hint.
+ // This allows a website's author to specify an attribute like
+ // autocomplete="other" on a field to disable all Autofill heuristics for
+ // the form.
+ *found_types = true;
+
+ // Tokenize the attribute value. Per the spec, the tokens are parsed in
+ // reverse order.
+ std::vector<std::string> tokens;
+ Tokenize(autocomplete_attribute, " ", &tokens);
+
+ // The final token must be the field type.
+ // If it is not one of the known types, abort.
+ DCHECK(!tokens.empty());
+ std::string field_type_token = tokens.back();
+ tokens.pop_back();
+ AutofillFieldType field_type =
+ FieldTypeFromAutocompleteType(field_type_token, *field);
+ if (field_type == UNKNOWN_TYPE)
+ continue;
+
+ // The preceding token, if any, may be a type hint.
+ if (!tokens.empty() && IsContactTypeHint(tokens.back())) {
+ // If it is, it must match the field type; otherwise, abort.
+ // Note that an invalid token invalidates the entire attribute value, even
+ // if the other tokens are valid.
+ if (!ContactTypeHintMatchesFieldType(tokens.back(), field_type))
+ continue;
+
+ // Chrome Autofill ignores these type hints.
+ tokens.pop_back();
+ }
+
+ // The preceding token, if any, may be a fixed string that is either
+ // "shipping" or "billing". Chrome Autofill treats these as implicit
+ // section name suffixes.
+ DCHECK_EQ(kDefaultSection, field->section());
+ std::string section = field->section();
+ if (!tokens.empty() &&
+ (tokens.back() == "shipping" || tokens.back() == "billing")) {
+ section = "-" + tokens.back();
+ tokens.pop_back();
+ }
+
+ // The preceding token, if any, may be a named section.
+ const std::string kSectionPrefix = "section-";
+ if (!tokens.empty() &&
+ StartsWithASCII(tokens.back(), kSectionPrefix, true)) {
+ // Prepend this section name to the suffix set in the preceding block.
+ section = tokens.back().substr(kSectionPrefix.size()) + section;
+ tokens.pop_back();
+ }
+
+ // No other tokens are allowed. If there are any remaining, abort.
+ if (!tokens.empty())
+ continue;
+
+ if (section != kDefaultSection) {
+ *found_sections = true;
+ field->set_section(section);
+ }
+
+ // No errors encountered while parsing!
+ // Update the |field|'s type based on what was parsed from the attribute.
+ field->set_heuristic_type(field_type);
+ if (field_type_token == "tel-local-prefix")
+ field->set_phone_part(AutofillField::PHONE_PREFIX);
+ else if (field_type_token == "tel-local-suffix")
+ field->set_phone_part(AutofillField::PHONE_SUFFIX);
+ }
+}
+
+void FormStructure::IdentifySections(bool has_author_specified_sections) {
+ if (fields_.empty())
+ return;
+
+ if (!has_author_specified_sections) {
+ // Name sections after the first field in the section.
+ string16 current_section = fields_.front()->unique_name();
+
+ // Keep track of the types we've seen in this section.
+ std::set<AutofillFieldType> seen_types;
+ AutofillFieldType previous_type = UNKNOWN_TYPE;
+
+ for (std::vector<AutofillField*>::iterator field = fields_.begin();
+ field != fields_.end(); ++field) {
+ const AutofillFieldType current_type =
+ AutofillType::GetEquivalentFieldType((*field)->type());
+
+ bool already_saw_current_type = seen_types.count(current_type) > 0;
+
+ // Forms often ask for multiple phone numbers -- e.g. both a daytime and
+ // evening phone number. Our phone number detection is also generally a
+ // little off. Hence, ignore this field type as a signal here.
+ if (AutofillType(current_type).group() == AutofillType::PHONE)
+ already_saw_current_type = false;
+
+ // Some forms have adjacent fields of the same type. Two common examples:
+ // * Forms with two email fields, where the second is meant to "confirm"
+ // the first.
+ // * Forms with a <select> menu for states in some countries, and a
+ // freeform <input> field for states in other countries. (Usually,
+ // only one of these two will be visible for any given choice of
+ // country.)
+ // Generally, adjacent fields of the same type belong in the same logical
+ // section.
+ if (current_type == previous_type)
+ already_saw_current_type = false;
+
+ previous_type = current_type;
+
+ if (current_type != UNKNOWN_TYPE && already_saw_current_type) {
+ // We reached the end of a section, so start a new section.
+ seen_types.clear();
+ current_section = (*field)->unique_name();
+ }
+
+ seen_types.insert(current_type);
+ (*field)->set_section(UTF16ToUTF8(current_section));
+ }
+ }
+
+ // Ensure that credit card and address fields are in separate sections.
+ // This simplifies the section-aware logic in autofill_manager.cc.
+ for (std::vector<AutofillField*>::iterator field = fields_.begin();
+ field != fields_.end(); ++field) {
+ AutofillType::FieldTypeGroup field_type_group =
+ AutofillType((*field)->type()).group();
+ if (field_type_group == AutofillType::CREDIT_CARD)
+ (*field)->set_section((*field)->section() + "-cc");
+ else
+ (*field)->set_section((*field)->section() + "-default");
+ }
+}
diff --git a/components/autofill/browser/form_structure.h b/components/autofill/browser/form_structure.h
new file mode 100644
index 0000000..731fe5c
--- /dev/null
+++ b/components/autofill/browser/form_structure.h
@@ -0,0 +1,241 @@
+// Copyright (c) 2011 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_BROWSER_FORM_STRUCTURE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_FORM_STRUCTURE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/common/web_element_descriptor.h"
+#include "googleurl/src/gurl.h"
+
+struct FormData;
+struct FormDataPredictions;
+
+enum RequestMethod {
+ GET,
+ POST
+};
+
+enum UploadRequired {
+ UPLOAD_NOT_REQUIRED,
+ UPLOAD_REQUIRED,
+ USE_UPLOAD_RATES
+};
+
+class AutofillMetrics;
+
+namespace autofill {
+struct AutocheckoutPageMetaData;
+}
+
+namespace base {
+class TimeTicks;
+}
+
+namespace buzz {
+class XmlElement;
+}
+
+// FormStructure stores a single HTML form together with the values entered
+// in the fields along with additional information needed by Autofill.
+class FormStructure {
+ public:
+ FormStructure(const FormData& form,
+ const std::string& autocheckout_url_prefix);
+ virtual ~FormStructure();
+
+ // Runs several heuristics against the form fields to determine their possible
+ // types.
+ void DetermineHeuristicTypes(const AutofillMetrics& metric_logger);
+
+ // Encodes the XML upload request from this FormStructure.
+ bool EncodeUploadRequest(const FieldTypeSet& available_field_types,
+ bool form_was_autofilled,
+ std::string* encoded_xml) const;
+
+ // Encodes the XML query request for the set of forms.
+ // All fields are returned in one XML. For example, there are three forms,
+ // with 2, 4, and 3 fields. The returned XML would have type info for 9
+ // fields, first two of which would be for the first form, next 4 for the
+ // second, and the rest is for the third.
+ static bool EncodeQueryRequest(const std::vector<FormStructure*>& forms,
+ std::vector<std::string>* encoded_signatures,
+ std::string* encoded_xml);
+
+ // Parses the field types from the server query response. |forms| must be the
+ // same as the one passed to EncodeQueryRequest when constructing the query.
+ static void ParseQueryResponse(
+ const std::string& response_xml,
+ const std::vector<FormStructure*>& forms,
+ autofill::AutocheckoutPageMetaData* page_meta_data,
+ const AutofillMetrics& metric_logger);
+
+ // Fills |forms| with the details from the given |form_structures| and their
+ // fields' predicted types.
+ static void GetFieldTypePredictions(
+ const std::vector<FormStructure*>& form_structures,
+ std::vector<FormDataPredictions>* forms);
+
+ // 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.
+ std::string FormSignature() const;
+
+ // Runs a quick heuristic to rule out forms that are obviously not
+ // auto-fillable, like google/yahoo/msn search, etc. The requirement that the
+ // form's method be POST is only applied if |require_method_post| is true.
+ bool IsAutofillable(bool require_method_post) const;
+
+ // Resets |autofill_count_| and counts the number of auto-fillable fields.
+ // This is used when we receive server data for form fields. At that time,
+ // we may have more known fields than just the number of fields we matched
+ // heuristically.
+ void UpdateAutofillCount();
+
+ // Returns true if this form matches the structural requirements for Autofill.
+ // The requirement that the form's method be POST is only applied if
+ // |require_method_post| is true.
+ bool ShouldBeParsed(bool require_method_post) const;
+
+ // Returns true if we should query the crowdsourcing server to determine this
+ // form's field types. If the form includes author-specified types, this will
+ // return false.
+ bool ShouldBeCrowdsourced() const;
+
+ // Sets the field types and experiment id to be those set for |cached_form|.
+ void UpdateFromCache(const FormStructure& cached_form);
+
+ // 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
+ // set for each field. |interaction_time| should be a timestamp corresponding
+ // to the user's first interaction with the form. |submission_time| should be
+ // a timestamp corresponding to the form's submission.
+ void LogQualityMetrics(const AutofillMetrics& metric_logger,
+ const base::TimeTicks& load_time,
+ const base::TimeTicks& interaction_time,
+ const base::TimeTicks& submission_time) const;
+
+ // Classifies each field in |fields_| based upon its |autocomplete| attribute,
+ // if the attribute is available. The association is stored into the field's
+ // |heuristic_type|.
+ // Fills |found_types| with |true| if the attribute is available and neither
+ // empty nor set to the special values "on" or "off" for at least one field.
+ // Fills |found_sections| with |true| if the attribute specifies a section for
+ // at least one field.
+ void ParseFieldTypesFromAutocompleteAttributes(bool* found_types,
+ bool* found_sections);
+
+ const AutofillField* field(size_t index) const;
+ AutofillField* field(size_t index);
+ size_t field_count() const;
+ size_t checkable_field_count() const;
+
+ // Returns the number of fields that are able to be autofilled.
+ size_t autofill_count() const { return autofill_count_; }
+
+ // Used for iterating over the fields.
+ std::vector<AutofillField*>::const_iterator begin() const {
+ return fields_.begin();
+ }
+ std::vector<AutofillField*>::const_iterator end() const {
+ return fields_.end();
+ }
+
+ const GURL& source_url() const { return source_url_; }
+
+ UploadRequired upload_required() const { return upload_required_; }
+
+ virtual std::string server_experiment_id() const;
+
+ // Returns a FormData containing the data this form structure knows about.
+ // |user_submitted| is currently always false.
+ FormData ToFormData() const;
+
+ bool operator==(const FormData& form) const;
+ bool operator!=(const FormData& form) const;
+
+ private:
+ friend class FormStructureTest;
+ FRIEND_TEST_ALL_PREFIXES(AutofillDownloadTest, QueryAndUploadTest);
+
+ // 64-bit hash of the string - used in FormSignature and unit-tests.
+ static std::string Hash64Bit(const std::string& str);
+
+ enum EncodeRequestType {
+ QUERY,
+ UPLOAD,
+ };
+
+ // Adds form info to |encompassing_xml_element|. |request_type| indicates if
+ // it is a query or upload.
+ bool EncodeFormRequest(EncodeRequestType request_type,
+ buzz::XmlElement* encompassing_xml_element) const;
+
+ // Classifies each field in |fields_| into a logical section.
+ // Sections are identified by the heuristic that a logical section should not
+ // include multiple fields of the same autofill type (with some exceptions, as
+ // described in the implementation). Sections are furthermore distinguished
+ // as either credit card or non-credit card sections.
+ // If |has_author_specified_sections| is true, only the second pass --
+ // distinguishing credit card sections from non-credit card ones -- is made.
+ void IdentifySections(bool has_author_specified_sections);
+
+ bool IsAutocheckoutEnabled() const;
+
+ // Returns the minimal number of fillable fields required to start autofill.
+ size_t RequiredFillableFields() const;
+
+ // The name of the form.
+ string16 form_name_;
+
+ // The source URL.
+ GURL source_url_;
+
+ // The target URL.
+ GURL target_url_;
+
+ // The number of fields able to be auto-filled.
+ size_t autofill_count_;
+
+ // A vector of all the input fields in the form.
+ ScopedVector<AutofillField> fields_;
+
+ // The number of fields able to be checked.
+ size_t checkable_field_count_;
+
+ // The names of the form input elements, that are part of the form signature.
+ // The string starts with "&" and the names are also separated by the "&"
+ // character. E.g.: "&form_input1_name&form_input2_name&...&form_inputN_name"
+ std::string form_signature_field_names_;
+
+ // Whether the server expects us to always upload, never upload, or default
+ // to the stored upload rates.
+ UploadRequired upload_required_;
+
+ // The server experiment corresponding to the server types returned for this
+ // form.
+ std::string server_experiment_id_;
+
+ // GET or POST.
+ RequestMethod method_;
+
+ // Whether the form includes any field types explicitly specified by the site
+ // author, via the |autocompletetype| attribute.
+ bool has_author_specified_types_;
+
+ // The URL prefix matched in autocheckout whitelist. An empty string implies
+ // autocheckout is not enabled for this form.
+ std::string autocheckout_url_prefix_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormStructure);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_FORM_STRUCTURE_H_
diff --git a/components/autofill/browser/form_structure_unittest.cc b/components/autofill/browser/form_structure_unittest.cc
new file mode 100644
index 0000000..abbe1aa
--- /dev/null
+++ b/components/autofill/browser/form_structure_unittest.cc
@@ -0,0 +1,2202 @@
+// Copyright (c) 2011 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/browser/form_structure.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+
+using WebKit::WebInputElement;
+
+namespace {
+
+// Unlike the base AutofillMetrics, exposes copy and assignment constructors,
+// which are handy for briefer test code. The AutofillMetrics class is
+// stateless, so this is safe.
+class TestAutofillMetrics : public AutofillMetrics {
+ public:
+ TestAutofillMetrics() {}
+ virtual ~TestAutofillMetrics() {}
+};
+
+} // anonymous namespace
+
+
+namespace content {
+
+std::ostream& operator<<(std::ostream& os, const FormData& form) {
+ os << UTF16ToUTF8(form.name)
+ << " "
+ << UTF16ToUTF8(form.method)
+ << " "
+ << form.origin.spec()
+ << " "
+ << form.action.spec()
+ << " ";
+
+ for (std::vector<FormFieldData>::const_iterator iter =
+ form.fields.begin();
+ iter != form.fields.end(); ++iter) {
+ os << *iter
+ << " ";
+ }
+
+ return os;
+}
+
+} // namespace content
+
+class FormStructureTest {
+ public:
+ static std::string Hash64Bit(const std::string& str) {
+ return FormStructure::Hash64Bit(str);
+ }
+};
+
+TEST(FormStructureTest, FieldCount) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("address1");
+ field.name = ASCIIToUTF16("address1");
+ field.form_control_type = "text";
+ field.should_autocomplete = false;
+ form.fields.push_back(field);
+
+ // The render process sends all fields to browser including fields with
+ // autocomplete=off
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_EQ(4U, form_structure->field_count());
+
+ // We expect the same count when autocheckout is enabled.
+ form_structure.reset(new FormStructure(form, "http://fake_url"));
+ EXPECT_EQ(4U, form_structure->field_count());
+}
+
+TEST(FormStructureTest, AutofillCount) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("state");
+ field.name = ASCIIToUTF16("state");
+ field.form_control_type = "select-one";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ // Only text and select fields that are heuristically matched are counted.
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_EQ(1U, form_structure->autofill_count());
+
+ // Add a field with should_autocomplete=false.
+ field.label = ASCIIToUTF16("address1");
+ field.name = ASCIIToUTF16("address1");
+ field.form_control_type = "text";
+ field.should_autocomplete = false;
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ // DetermineHeuristicTypes also assign field type for fields with
+ // autocomplete=off thus autofill_count includes them. This is a bug,
+ // and they should not be counted. See http://crbug.com/176432 for details.
+ // TODO(benquan): change it to EXPECT_EQ(1U, ... when the bug is fixed.
+ EXPECT_EQ(2U, form_structure->autofill_count());
+
+ // All fields should be counted when Autocheckout is enabled.
+ form_structure.reset(new FormStructure(form, "http://fake_url"));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_EQ(2U, form_structure->autofill_count());
+}
+
+TEST(FormStructureTest, SourceURL) {
+ FormData form;
+ form.origin = GURL("http://www.foo.com/");
+ form.method = ASCIIToUTF16("post");
+ FormStructure form_structure(form, std::string());
+
+ EXPECT_EQ(form.origin, form_structure.source_url());
+}
+
+TEST(FormStructureTest, IsAutofillable) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+
+ // We need at least three text fields to be auto-fillable.
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ // When autocheckout is enabled, we enable autofill even the form has
+ // no fields
+ form_structure.reset(new FormStructure(form, "http://fake_url"));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(form_structure->IsAutofillable(true));
+
+ // We do not limit to three text fields when autocheckout is enabled.
+ form_structure.reset(new FormStructure(form, "http://fake_url"));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // We now have three text fields, but only two auto-fillable fields.
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(form_structure->IsAutofillable(true));
+
+ // We now have three auto-fillable fields.
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("email");
+ field.form_control_type = "email";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // The method must be 'post', though we can intentionally ignore this
+ // criterion for the sake of providing a helpful warning message to the user.
+ form.method = ASCIIToUTF16("get");
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(form_structure->IsAutofillable(true));
+ EXPECT_TRUE(form_structure->IsAutofillable(false));
+
+ // The target cannot include http(s)://*/search...
+ form.method = ASCIIToUTF16("post");
+ form.action = GURL("http://google.com/search?q=hello");
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(form_structure->IsAutofillable(true));
+
+ // But search can be in the URL.
+ form.action = GURL("http://search.com/?q=hello");
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+}
+
+TEST(FormStructureTest, ShouldBeParsed) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+
+ // We need at least three text fields to be parseable.
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ FormFieldData checkable_field;
+ checkable_field.is_checkable = true;
+ checkable_field.name = ASCIIToUTF16("radiobtn");
+ checkable_field.form_control_type = "radio";
+ form.fields.push_back(checkable_field);
+
+ checkable_field.name = ASCIIToUTF16("checkbox");
+ checkable_field.form_control_type = "checkbox";
+ form.fields.push_back(checkable_field);
+
+ // We have only one text field, should not be parsed.
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_FALSE(form_structure->ShouldBeParsed(true));
+
+ // The form should be parsed for autocheckout even it has less than three
+ // text fields.
+ form_structure.reset(new FormStructure(form, "http://fake_url"));
+ EXPECT_TRUE(form_structure->ShouldBeParsed(true));
+
+ // We now have three text fields, though only two are auto-fillable.
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_TRUE(form_structure->ShouldBeParsed(true));
+
+ // The method must be 'post', though we can intentionally ignore this
+ // criterion for the sake of providing a helpful warning message to the user.
+ form.method = ASCIIToUTF16("get");
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_FALSE(form_structure->IsAutofillable(true));
+ EXPECT_TRUE(form_structure->ShouldBeParsed(false));
+
+ // The target cannot include http(s)://*/search...
+ form.method = ASCIIToUTF16("post");
+ form.action = GURL("http://google.com/search?q=hello");
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_FALSE(form_structure->ShouldBeParsed(true));
+
+ // But search can be in the URL.
+ form.action = GURL("http://search.com/?q=hello");
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_TRUE(form_structure->ShouldBeParsed(true));
+
+ // The form need only have three fields, but at least one must be a text
+ // field.
+ form.fields.clear();
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("email");
+ field.form_control_type = "email";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State");
+ field.name = ASCIIToUTF16("state");
+ field.form_control_type = "select-one";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ field.form_control_type = "select-one";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_TRUE(form_structure->ShouldBeParsed(true));
+
+ form.fields[0].form_control_type = "select-one";
+ // Now, no text fields.
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_FALSE(form_structure->ShouldBeParsed(true));
+
+ // It should be parsed when autocheckout is enabled.
+ form_structure.reset(new FormStructure(form, "http://fake_url"));
+ EXPECT_TRUE(form_structure->ShouldBeParsed(true));
+}
+
+TEST(FormStructureTest, HeuristicsContactInfo) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Phone");
+ field.name = ASCIIToUTF16("phone");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Zip code");
+ field.name = ASCIIToUTF16("zipcode");
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(8U, form_structure->field_count());
+ ASSERT_EQ(7U, form_structure->autofill_count());
+
+ // First name.
+ EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
+ // Last name.
+ EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
+ // Email.
+ EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
+ // Phone.
+ EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
+ form_structure->field(3)->heuristic_type());
+ // Address.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(4)->heuristic_type());
+ // City.
+ EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(5)->heuristic_type());
+ // Zip.
+ EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(6)->heuristic_type());
+ // Submit.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(7)->heuristic_type());
+}
+
+// Verify that we can correctly process the |autocomplete| attribute.
+TEST(FormStructureTest, HeuristicsAutocompleteAttribute) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("field1");
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("field2");
+ field.autocomplete_attribute = "family-name";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("field3");
+ field.autocomplete_attribute = "email";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(3U, form_structure->field_count());
+ ASSERT_EQ(3U, form_structure->autofill_count());
+
+ EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
+ EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
+ EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
+}
+
+// Verify that we can correctly process the 'autocomplete' attribute for phone
+// number types (especially phone prefixes and suffixes).
+TEST(FormStructureTest, HeuristicsAutocompleteAttributePhoneTypes) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("field1");
+ field.autocomplete_attribute = "tel-local";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("field2");
+ field.autocomplete_attribute = "tel-local-prefix";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("field3");
+ field.autocomplete_attribute = "tel-local-suffix";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(3U, form_structure->field_count());
+ EXPECT_EQ(3U, form_structure->autofill_count());
+
+ EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(0)->heuristic_type());
+ EXPECT_EQ(AutofillField::IGNORED, form_structure->field(0)->phone_part());
+ EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(1)->heuristic_type());
+ EXPECT_EQ(AutofillField::PHONE_PREFIX,
+ form_structure->field(1)->phone_part());
+ EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(2)->heuristic_type());
+ EXPECT_EQ(AutofillField::PHONE_SUFFIX,
+ form_structure->field(2)->phone_part());
+}
+
+// If at least one field includes type hints in the 'autocomplete' attribute, we
+// should not try to apply any other heuristics.
+TEST(FormStructureTest, AutocompleteAttributeOverridesOtherHeuristics) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ // Start with a regular contact form.
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ EXPECT_TRUE(form_structure->ShouldBeCrowdsourced());
+
+ ASSERT_EQ(3U, form_structure->field_count());
+ ASSERT_EQ(3U, form_structure->autofill_count());
+
+ EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
+ EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
+ EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
+
+ // Now update the first form field to include an 'autocomplete' attribute.
+ form.fields.front().autocomplete_attribute = "x-other";
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(form_structure->IsAutofillable(true));
+ EXPECT_FALSE(form_structure->ShouldBeCrowdsourced());
+
+ ASSERT_EQ(3U, form_structure->field_count());
+ ASSERT_EQ(0U, form_structure->autofill_count());
+
+ 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());
+}
+
+// Verify that we can correctly process sections listed in the |autocomplete|
+// attribute.
+TEST(FormStructureTest, HeuristicsAutocompleteAttributeWithSections) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ // Some fields will have no section specified. These fall into the default
+ // section.
+ field.autocomplete_attribute = "email";
+ form.fields.push_back(field);
+
+ // We allow arbitrary section names.
+ field.autocomplete_attribute = "section-foo email";
+ form.fields.push_back(field);
+
+ // "shipping" and "billing" are special section tokens that don't require the
+ // "section-" prefix.
+ field.autocomplete_attribute = "shipping email";
+ form.fields.push_back(field);
+ field.autocomplete_attribute = "billing email";
+ form.fields.push_back(field);
+
+ // "shipping" and "billing" can be combined with other section names.
+ field.autocomplete_attribute = "section-foo shipping email";
+ form.fields.push_back(field);
+ field.autocomplete_attribute = "section-foo billing email";
+ form.fields.push_back(field);
+
+ // We don't do anything clever to try to coalesce sections; it's up to site
+ // authors to avoid typos.
+ field.autocomplete_attribute = "section--foo email";
+ form.fields.push_back(field);
+
+ // "shipping email" and "section--shipping" email should be parsed as
+ // different sections. This is only an interesting test due to how we
+ // implement implicit section names from attributes like "shipping email"; see
+ // the implementation for more details.
+ field.autocomplete_attribute = "section--shipping email";
+ form.fields.push_back(field);
+
+ // Credit card fields are implicitly in a separate section from other fields.
+ field.autocomplete_attribute = "section-foo cc-number";
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure.IsAutofillable(true));
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(9U, form_structure.field_count());
+ EXPECT_EQ(9U, form_structure.autofill_count());
+
+ // All of the fields in this form should be parsed as belonging to different
+ // sections.
+ std::set<std::string> section_names;
+ for (size_t i = 0; i < 9; ++i) {
+ section_names.insert(form_structure.field(i)->section());
+ }
+ EXPECT_EQ(9U, section_names.size());
+}
+
+// Verify that we can correctly process a degenerate section listed in the
+// |autocomplete| attribute.
+TEST(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsDegenerate) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ // Some fields will have no section specified. These fall into the default
+ // section.
+ field.autocomplete_attribute = "email";
+ form.fields.push_back(field);
+
+ // Specifying "section-" is equivalent to not specifying a section.
+ field.autocomplete_attribute = "section- email";
+ form.fields.push_back(field);
+
+ // Invalid tokens should prevent us from setting a section name.
+ field.autocomplete_attribute = "garbage section-foo email";
+ form.fields.push_back(field);
+ field.autocomplete_attribute = "garbage section-bar email";
+ form.fields.push_back(field);
+ field.autocomplete_attribute = "garbage shipping email";
+ form.fields.push_back(field);
+ field.autocomplete_attribute = "garbage billing email";
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(6U, form_structure.field_count());
+ EXPECT_EQ(2U, form_structure.autofill_count());
+
+ // All of the fields in this form should be parsed as belonging to the same
+ // section.
+ std::set<std::string> section_names;
+ for (size_t i = 0; i < 6; ++i) {
+ section_names.insert(form_structure.field(i)->section());
+ }
+ EXPECT_EQ(1U, section_names.size());
+}
+
+// Verify that we can correctly process repeated sections listed in the
+// |autocomplete| attribute.
+TEST(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsRepeated) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.autocomplete_attribute = "section-foo email";
+ form.fields.push_back(field);
+ field.autocomplete_attribute = "section-foo street-address";
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(2U, form_structure.field_count());
+ EXPECT_EQ(2U, form_structure.autofill_count());
+
+ // All of the fields in this form should be parsed as belonging to the same
+ // section.
+ std::set<std::string> section_names;
+ for (size_t i = 0; i < 2; ++i) {
+ section_names.insert(form_structure.field(i)->section());
+ }
+ EXPECT_EQ(1U, section_names.size());
+}
+
+// Verify that we do not override the author-specified sections from a form with
+// local heuristics.
+TEST(FormStructureTest, HeuristicsDontOverrideAutocompleteAttributeSections) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.name = ASCIIToUTF16("one");
+ field.autocomplete_attribute = "street-address";
+ form.fields.push_back(field);
+ field.name = string16();
+ field.autocomplete_attribute = "section-foo email";
+ form.fields.push_back(field);
+ field.name = string16();
+ field.autocomplete_attribute = "name";
+ form.fields.push_back(field);
+ field.name = ASCIIToUTF16("two");
+ field.autocomplete_attribute = "street-address";
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(4U, form_structure.field_count());
+ EXPECT_EQ(4U, form_structure.autofill_count());
+
+ // Normally, the two separate address fields would cause us to detect two
+ // separate sections; but because there is an author-specified section in this
+ // form, we do not apply these usual heuristics.
+ EXPECT_EQ(ASCIIToUTF16("one"), form_structure.field(0)->name);
+ EXPECT_EQ(ASCIIToUTF16("two"), form_structure.field(3)->name);
+ EXPECT_EQ(form_structure.field(0)->section(),
+ form_structure.field(3)->section());
+}
+
+TEST(FormStructureTest, HeuristicsSample8) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Your First Name:");
+ field.name = ASCIIToUTF16("bill.first");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Your Last Name:");
+ field.name = ASCIIToUTF16("bill.last");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Street Address Line 1:");
+ field.name = ASCIIToUTF16("bill.street1");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Street Address Line 2:");
+ field.name = ASCIIToUTF16("bill.street2");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("bill.city");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State (U.S.):");
+ field.name = ASCIIToUTF16("bill.state");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Zip/Postal Code:");
+ field.name = ASCIIToUTF16("BillTo.PostalCode");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country:");
+ field.name = ASCIIToUTF16("bill.country");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Phone Number:");
+ field.name = ASCIIToUTF16("BillTo.Phone");
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(10U, form_structure->field_count());
+ ASSERT_EQ(9U, form_structure->autofill_count());
+
+ // First name.
+ EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
+ // Last name.
+ EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
+ // Address.
+ EXPECT_EQ(ADDRESS_BILLING_LINE1, form_structure->field(2)->heuristic_type());
+ // Address.
+ EXPECT_EQ(ADDRESS_BILLING_LINE2, form_structure->field(3)->heuristic_type());
+ // City.
+ EXPECT_EQ(ADDRESS_BILLING_CITY, form_structure->field(4)->heuristic_type());
+ // State.
+ EXPECT_EQ(ADDRESS_BILLING_STATE, form_structure->field(5)->heuristic_type());
+ // Zip.
+ EXPECT_EQ(ADDRESS_BILLING_ZIP, form_structure->field(6)->heuristic_type());
+ // Country.
+ EXPECT_EQ(ADDRESS_BILLING_COUNTRY,
+ form_structure->field(7)->heuristic_type());
+ // Phone.
+ EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
+ form_structure->field(8)->heuristic_type());
+ // Submit.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(9)->heuristic_type());
+}
+
+TEST(FormStructureTest, HeuristicsSample6) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("E-mail address");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Full name");
+ field.name = ASCIIToUTF16("name");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Company");
+ field.name = ASCIIToUTF16("company");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Zip Code");
+ field.name = ASCIIToUTF16("Home.PostalCode");
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.value = ASCIIToUTF16("continue");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(7U, form_structure->field_count());
+ ASSERT_EQ(6U, form_structure->autofill_count());
+
+ // Email.
+ EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(0)->heuristic_type());
+ // Full name.
+ EXPECT_EQ(NAME_FULL, form_structure->field(1)->heuristic_type());
+ // Company
+ EXPECT_EQ(COMPANY_NAME, form_structure->field(2)->heuristic_type());
+ // Address.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(3)->heuristic_type());
+ // City.
+ EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(4)->heuristic_type());
+ // Zip.
+ EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(5)->heuristic_type());
+ // Submit.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(6)->heuristic_type());
+}
+
+// Tests a sequence of FormFields where only labels are supplied to heuristics
+// for matching. This works because FormFieldData labels are matched in the
+// case that input element ids (or |name| fields) are missing.
+TEST(FormStructureTest, HeuristicsLabelsOnly) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Phone");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Zip code");
+ field.name = string16();
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(8U, form_structure->field_count());
+ ASSERT_EQ(7U, form_structure->autofill_count());
+
+ // First name.
+ EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
+ // Last name.
+ EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
+ // Email.
+ EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
+ // Phone.
+ EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
+ form_structure->field(3)->heuristic_type());
+ // Address.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(4)->heuristic_type());
+ // Address Line 2.
+ EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(5)->heuristic_type());
+ // Zip.
+ EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(6)->heuristic_type());
+ // Submit.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(7)->heuristic_type());
+}
+
+TEST(FormStructureTest, HeuristicsCreditCardInfo) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Exp Month");
+ field.name = ASCIIToUTF16("ccmonth");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Exp Year");
+ field.name = ASCIIToUTF16("ccyear");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Verification");
+ field.name = ASCIIToUTF16("verification");
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(6U, form_structure->field_count());
+ ASSERT_EQ(5U, form_structure->autofill_count());
+
+ // Credit card name.
+ EXPECT_EQ(CREDIT_CARD_NAME, form_structure->field(0)->heuristic_type());
+ // Credit card number.
+ EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(1)->heuristic_type());
+ // Credit card expiration month.
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(2)->heuristic_type());
+ // Credit card expiration year.
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ form_structure->field(3)->heuristic_type());
+ // CVV.
+ EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
+ form_structure->field(4)->heuristic_type());
+ // Submit.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(5)->heuristic_type());
+}
+
+TEST(FormStructureTest, HeuristicsCreditCardInfoWithUnknownCardField) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ form.fields.push_back(field);
+
+ // This is not a field we know how to process. But we should skip over it
+ // and process the other fields in the card block.
+ field.label = ASCIIToUTF16("Card image");
+ field.name = ASCIIToUTF16("card_image");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Exp Month");
+ field.name = ASCIIToUTF16("ccmonth");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Exp Year");
+ field.name = ASCIIToUTF16("ccyear");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Verification");
+ field.name = ASCIIToUTF16("verification");
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(7U, form_structure->field_count());
+ ASSERT_EQ(5U, form_structure->autofill_count());
+
+ // Credit card name.
+ EXPECT_EQ(CREDIT_CARD_NAME, form_structure->field(0)->heuristic_type());
+ // Credit card type. This is an unknown type but related to the credit card.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(1)->heuristic_type());
+ // Credit card number.
+ EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(2)->heuristic_type());
+ // Credit card expiration month.
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
+ // Credit card expiration year.
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ form_structure->field(4)->heuristic_type());
+ // CVV.
+ EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
+ form_structure->field(5)->heuristic_type());
+ // Submit.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(6)->heuristic_type());
+}
+
+TEST(FormStructureTest, ThreeAddressLines) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address Line1");
+ field.name = ASCIIToUTF16("Address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line2");
+ field.name = ASCIIToUTF16("Address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line3");
+ field.name = ASCIIToUTF16("Address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(4U, form_structure->field_count());
+ ASSERT_EQ(3U, form_structure->autofill_count());
+
+ // Address Line 1.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
+ // Address Line 2.
+ EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
+ // Address Line 3.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type());
+ // City.
+ EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(3)->heuristic_type());
+}
+
+// This test verifies that "addressLine1" and "addressLine2" matches heuristics.
+// This occured in https://www.gorillaclothing.com/. http://crbug.com/52126.
+TEST(FormStructureTest, BillingAndShippingAddresses) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address Line1");
+ field.name = ASCIIToUTF16("shipping.address.addressLine1");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line2");
+ field.name = ASCIIToUTF16("shipping.address.addressLine2");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line1");
+ field.name = ASCIIToUTF16("billing.address.addressLine1");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line2");
+ field.name = ASCIIToUTF16("billing.address.addressLine2");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(4U, form_structure->field_count());
+ ASSERT_EQ(4U, form_structure->autofill_count());
+
+ // Address Line 1.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
+ // Address Line 2.
+ EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
+ // Address Line 1.
+ EXPECT_EQ(ADDRESS_BILLING_LINE1, form_structure->field(2)->heuristic_type());
+ // Address Line 2.
+ EXPECT_EQ(ADDRESS_BILLING_LINE2, form_structure->field(3)->heuristic_type());
+}
+
+
+// This example comes from expedia.com where they use a "Suite" label to
+// indicate a suite or apartment number. We interpret this as address line 2.
+// And the following "Street address second line" we interpret as address line
+// 3 and discard.
+// See http://crbug.com/48197 for details.
+TEST(FormStructureTest, ThreeAddressLinesExpedia) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Street:");
+ field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_ads1");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Suite or Apt:");
+ field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adap");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Street address second line");
+ field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_ads2");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City:");
+ field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adct");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(4U, form_structure->field_count());
+ EXPECT_EQ(3U, form_structure->autofill_count());
+
+ // Address Line 1.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
+ // Suite / Apt.
+ EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
+ // Address Line 3.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type());
+ // City.
+ EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(3)->heuristic_type());
+}
+
+// This example comes from ebay.com where the word "suite" appears in the label
+// and the name "address2" clearly indicates that this is the address line 2.
+// See http://crbug.com/48197 for details.
+TEST(FormStructureTest, TwoAddressLinesEbay) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address Line1");
+ field.name = ASCIIToUTF16("address1");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Floor number, suite number, etc");
+ field.name = ASCIIToUTF16("address2");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City:");
+ field.name = ASCIIToUTF16("city");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(3U, form_structure->field_count());
+ ASSERT_EQ(3U, form_structure->autofill_count());
+
+ // Address Line 1.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
+ // Address Line 2.
+ EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
+ // City.
+ EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(2)->heuristic_type());
+}
+
+TEST(FormStructureTest, HeuristicsStateWithProvince) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Address Line1");
+ field.name = ASCIIToUTF16("Address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line2");
+ field.name = ASCIIToUTF16("Address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State/Province/Region");
+ field.name = ASCIIToUTF16("State");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(3U, form_structure->field_count());
+ ASSERT_EQ(3U, form_structure->autofill_count());
+
+ // Address Line 1.
+ EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
+ // Address Line 2.
+ EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
+ // State.
+ EXPECT_EQ(ADDRESS_HOME_STATE, form_structure->field(2)->heuristic_type());
+}
+
+// This example comes from lego.com's checkout page.
+TEST(FormStructureTest, HeuristicsWithBilling) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name*:");
+ field.name = ASCIIToUTF16("editBillingAddress$firstNameBox");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name*:");
+ field.name = ASCIIToUTF16("editBillingAddress$lastNameBox");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Company Name:");
+ field.name = ASCIIToUTF16("editBillingAddress$companyBox");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address*:");
+ field.name = ASCIIToUTF16("editBillingAddress$addressLine1Box");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Apt/Suite :");
+ field.name = ASCIIToUTF16("editBillingAddress$addressLine2Box");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("City*:");
+ field.name = ASCIIToUTF16("editBillingAddress$cityBox");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("State/Province*:");
+ field.name = ASCIIToUTF16("editBillingAddress$stateDropDown");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Country*:");
+ field.name = ASCIIToUTF16("editBillingAddress$countryDropDown");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Postal Code*:");
+ field.name = ASCIIToUTF16("editBillingAddress$zipCodeBox");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Phone*:");
+ field.name = ASCIIToUTF16("editBillingAddress$phoneBox");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email Address*:");
+ field.name = ASCIIToUTF16("email$emailBox");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(11U, form_structure->field_count());
+ ASSERT_EQ(11U, form_structure->autofill_count());
+
+ EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
+ EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
+ EXPECT_EQ(COMPANY_NAME, form_structure->field(2)->heuristic_type());
+ EXPECT_EQ(ADDRESS_BILLING_LINE1, form_structure->field(3)->heuristic_type());
+ EXPECT_EQ(ADDRESS_BILLING_LINE2, form_structure->field(4)->heuristic_type());
+ EXPECT_EQ(ADDRESS_BILLING_CITY, form_structure->field(5)->heuristic_type());
+ EXPECT_EQ(ADDRESS_BILLING_STATE, form_structure->field(6)->heuristic_type());
+ EXPECT_EQ(ADDRESS_BILLING_COUNTRY,
+ form_structure->field(7)->heuristic_type());
+ EXPECT_EQ(ADDRESS_BILLING_ZIP, form_structure->field(8)->heuristic_type());
+ EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
+ form_structure->field(9)->heuristic_type());
+ EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(10)->heuristic_type());
+}
+
+TEST(FormStructureTest, ThreePartPhoneNumber) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Phone:");
+ field.name = ASCIIToUTF16("dayphone1");
+ field.max_length = 0;
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("-");
+ field.name = ASCIIToUTF16("dayphone2");
+ field.max_length = 3; // Size of prefix is 3.
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("-");
+ field.name = ASCIIToUTF16("dayphone3");
+ field.max_length = 4; // Size of suffix is 4. If unlimited size is
+ // passed, phone will be parsed as
+ // <country code> - <area code> - <phone>.
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("ext.:");
+ field.name = ASCIIToUTF16("dayphone4");
+ field.max_length = 0;
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ ASSERT_EQ(4U, form_structure->field_count());
+ ASSERT_EQ(3U, form_structure->autofill_count());
+
+ // Area code.
+ EXPECT_EQ(PHONE_HOME_CITY_CODE, form_structure->field(0)->heuristic_type());
+ // Phone number suffix.
+ EXPECT_EQ(PHONE_HOME_NUMBER,
+ form_structure->field(1)->heuristic_type());
+ // Phone number suffix.
+ EXPECT_EQ(PHONE_HOME_NUMBER,
+ form_structure->field(2)->heuristic_type());
+ // Unknown.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(3)->heuristic_type());
+}
+
+TEST(FormStructureTest, HeuristicsInfernoCC) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("billing_address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Expiration Date");
+ field.name = ASCIIToUTF16("expiration_month");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Expiration Year");
+ field.name = ASCIIToUTF16("expiration_year");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(5U, form_structure->field_count());
+ EXPECT_EQ(5U, form_structure->autofill_count());
+
+ // Name on Card.
+ EXPECT_EQ(CREDIT_CARD_NAME, form_structure->field(0)->heuristic_type());
+ // Address.
+ EXPECT_EQ(ADDRESS_BILLING_LINE1, form_structure->field(1)->heuristic_type());
+ // Card Number.
+ EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(2)->heuristic_type());
+ // Expiration Date.
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
+ // Expiration Year.
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ form_structure->field(4)->heuristic_type());
+}
+
+TEST(FormStructureTest, CVCCodeClash) {
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Card number");
+ field.name = ASCIIToUTF16("ccnumber");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("First name");
+ field.name = ASCIIToUTF16("first_name");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last name");
+ field.name = ASCIIToUTF16("last_name");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Expiration date");
+ field.name = ASCIIToUTF16("ccexpiresmonth");
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("ccexpiresyear");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("cvc number");
+ field.name = ASCIIToUTF16("csc");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+
+ // Expect the correct number of fields.
+ ASSERT_EQ(6U, form_structure->field_count());
+ ASSERT_EQ(5U, form_structure->autofill_count());
+
+ // Card Number.
+ EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(0)->heuristic_type());
+ // First name, taken as name on card.
+ EXPECT_EQ(CREDIT_CARD_NAME, form_structure->field(1)->heuristic_type());
+ // Last name is not merged.
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type());
+ // Expiration Date.
+ EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
+ // Expiration Year.
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ form_structure->field(4)->heuristic_type());
+ // CVC code.
+ EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
+ form_structure->field(5)->heuristic_type());
+}
+
+TEST(FormStructureTest, EncodeQueryRequest) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name on Card");
+ field.name = ASCIIToUTF16("name_on_card");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("billing_address");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Card Number");
+ field.name = ASCIIToUTF16("card_number");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Expiration Date");
+ field.name = ASCIIToUTF16("expiration_month");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Expiration Year");
+ field.name = ASCIIToUTF16("expiration_year");
+ form.fields.push_back(field);
+
+ // Add checkable field.
+ FormFieldData checkable_field;
+ checkable_field.is_checkable = true;
+ checkable_field.label = ASCIIToUTF16("Checkable1");
+ checkable_field.name = ASCIIToUTF16("Checkable1");
+ form.fields.push_back(checkable_field);
+
+ ScopedVector<FormStructure> forms;
+ forms.push_back(new FormStructure(form, std::string()));
+ std::vector<std::string> encoded_signatures;
+ std::string encoded_xml;
+ const char * const kSignature1 = "11337937696949187602";
+ const char * const kResponse1 =
+ "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillquery "
+ "clientversion=\"6.1.1715.1442/en (GGLL)\" accepts=\"e\"><form "
+ "signature=\"11337937696949187602\"><field signature=\"412125936\"/>"
+ "<field signature=\"1917667676\"/><field signature=\"2226358947\"/>"
+ "<field signature=\"747221617\"/><field signature=\"4108155786\"/></form>"
+ "</autofillquery>";
+ ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ ASSERT_EQ(1U, encoded_signatures.size());
+ EXPECT_EQ(kSignature1, encoded_signatures[0]);
+ EXPECT_EQ(kResponse1, encoded_xml);
+
+ // Add the same form, only one will be encoded, so EncodeQueryRequest() should
+ // return the same data.
+ forms.push_back(new FormStructure(form, std::string()));
+ ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ ASSERT_EQ(1U, encoded_signatures.size());
+ EXPECT_EQ(kSignature1, encoded_signatures[0]);
+ EXPECT_EQ(kResponse1, encoded_xml);
+ // Add 5 address fields - this should be still a valid form.
+ for (size_t i = 0; i < 5; ++i) {
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+ }
+
+ forms.push_back(new FormStructure(form, std::string()));
+ ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ ASSERT_EQ(2U, encoded_signatures.size());
+ EXPECT_EQ(kSignature1, encoded_signatures[0]);
+ const char * const kSignature2 = "8308881815906226214";
+ EXPECT_EQ(kSignature2, encoded_signatures[1]);
+ const char * const kResponse2 =
+ "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?><autofillquery "
+ "clientversion=\"6.1.1715.1442/en (GGLL)\" accepts=\"e\"><form "
+ "signature=\"11337937696949187602\"><field signature=\"412125936\"/>"
+ "<field signature=\"1917667676\"/><field signature=\"2226358947\"/>"
+ "<field signature=\"747221617\"/><field signature=\"4108155786\"/></form>"
+ "<form signature=\"8308881815906226214\"><field signature=\"412125936\"/>"
+ "<field signature=\"1917667676\"/><field signature=\"2226358947\"/>"
+ "<field signature=\"747221617\"/><field signature=\"4108155786\"/><field "
+ "signature=\"509334676\"/><field signature=\"509334676\"/><field "
+ "signature=\"509334676\"/><field signature=\"509334676\"/><field "
+ "signature=\"509334676\"/></form></autofillquery>";
+ EXPECT_EQ(kResponse2, encoded_xml);
+
+ FormData malformed_form(form);
+ // Add 50 address fields - the form is not valid anymore, but previous ones
+ // are. The result should be the same as in previous test.
+ for (size_t i = 0; i < 50; ++i) {
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ malformed_form.fields.push_back(field);
+ }
+
+ forms.push_back(new FormStructure(malformed_form, std::string()));
+ ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ ASSERT_EQ(2U, encoded_signatures.size());
+ EXPECT_EQ(kSignature1, encoded_signatures[0]);
+ EXPECT_EQ(kSignature2, encoded_signatures[1]);
+ EXPECT_EQ(kResponse2, encoded_xml);
+
+ // Check that we fail if there are only bad form(s).
+ ScopedVector<FormStructure> bad_forms;
+ bad_forms.push_back(new FormStructure(malformed_form, std::string()));
+ EXPECT_FALSE(FormStructure::EncodeQueryRequest(bad_forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ EXPECT_EQ(0U, encoded_signatures.size());
+ EXPECT_EQ("", encoded_xml);
+
+ // Check the behaviour with autocheckout enabled.
+ ScopedVector<FormStructure> checkable_forms;
+ checkable_forms.push_back(
+ new FormStructure(form, "https://www.sample1.com/query/path"));
+
+ ASSERT_TRUE(FormStructure::EncodeQueryRequest(checkable_forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ const char * const kSignature3 = "7747357776717901584";
+ const char * const kResponse3 =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><autofillquery "
+ "clientversion=\"6.1.1715.1442/en (GGLL)\" accepts=\"a,e\" "
+ "urlprefixsignature=\"7648393911063090788\">"
+ "<form signature=\"7747357776717901584\">"
+ "<field signature=\"412125936\"/>"
+ "<field signature=\"1917667676\"/><field signature=\"2226358947\"/><field"
+ " signature=\"747221617\"/><field signature=\"4108155786\"/><field "
+ "signature=\"3410250678\"/><field signature=\"509334676\"/><field "
+ "signature=\"509334676\"/><field signature=\"509334676\"/><field "
+ "signature=\"509334676\"/><field signature=\"509334676\"/></form>"
+ "</autofillquery>";
+ ASSERT_EQ(1U, encoded_signatures.size());
+ EXPECT_EQ(kSignature3, encoded_signatures[0]);
+ EXPECT_EQ(kResponse3, encoded_xml);
+}
+
+TEST(FormStructureTest, EncodeUploadRequest) {
+ scoped_ptr<FormStructure> form_structure;
+ std::vector<FieldTypeSet> possible_field_types;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("firstname");
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(NAME_FIRST);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("lastname");
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(NAME_LAST);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("email");
+ field.form_control_type = "email";
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(EMAIL_ADDRESS);
+
+ field.label = ASCIIToUTF16("Phone");
+ field.name = ASCIIToUTF16("phone");
+ field.form_control_type = "number";
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(PHONE_HOME_WHOLE_NUMBER);
+
+ field.label = ASCIIToUTF16("Country");
+ field.name = ASCIIToUTF16("country");
+ field.form_control_type = "select-one";
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(ADDRESS_HOME_COUNTRY);
+
+ // Add checkable field.
+ FormFieldData checkable_field;
+ checkable_field.is_checkable = true;
+ checkable_field.label = ASCIIToUTF16("Checkable1");
+ checkable_field.name = ASCIIToUTF16("Checkable1");
+ form.fields.push_back(checkable_field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(ADDRESS_HOME_COUNTRY);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+
+ ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
+ for (size_t i = 0; i < form_structure->field_count(); ++i)
+ form_structure->field(i)->set_possible_types(possible_field_types[i]);
+
+ FieldTypeSet available_field_types;
+ available_field_types.insert(NAME_FIRST);
+ available_field_types.insert(NAME_LAST);
+ available_field_types.insert(ADDRESS_HOME_LINE1);
+ available_field_types.insert(ADDRESS_HOME_LINE2);
+ available_field_types.insert(ADDRESS_HOME_COUNTRY);
+ available_field_types.insert(ADDRESS_BILLING_LINE1);
+ available_field_types.insert(ADDRESS_BILLING_LINE2);
+ available_field_types.insert(EMAIL_ADDRESS);
+ available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
+
+ std::string encoded_xml;
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\" "
+ "formsignature=\"8736493185895608956\" autofillused=\"false\" "
+ "datapresent=\"144200030e\">"
+ "<field signature=\"3763331450\" autofilltype=\"3\"/>"
+ "<field signature=\"3494530716\" autofilltype=\"5\"/>"
+ "<field signature=\"1029417091\" autofilltype=\"9\"/>"
+ "<field signature=\"466116101\" autofilltype=\"14\"/>"
+ "<field signature=\"2799270304\" autofilltype=\"36\"/>"
+ "</autofillupload>",
+ encoded_xml);
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, true,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\" "
+ "formsignature=\"8736493185895608956\" autofillused=\"true\" "
+ "datapresent=\"144200030e\">"
+ "<field signature=\"3763331450\" autofilltype=\"3\"/>"
+ "<field signature=\"3494530716\" autofilltype=\"5\"/>"
+ "<field signature=\"1029417091\" autofilltype=\"9\"/>"
+ "<field signature=\"466116101\" autofilltype=\"14\"/>"
+ "<field signature=\"2799270304\" autofilltype=\"36\"/>"
+ "</autofillupload>",
+ encoded_xml);
+
+ // Add 2 address fields - this should be still a valid form.
+ for (size_t i = 0; i < 2; ++i) {
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(ADDRESS_HOME_LINE1);
+ possible_field_types.back().insert(ADDRESS_HOME_LINE2);
+ possible_field_types.back().insert(ADDRESS_BILLING_LINE1);
+ possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
+ }
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
+ for (size_t i = 0; i < form_structure->field_count(); ++i)
+ form_structure->field(i)->set_possible_types(possible_field_types[i]);
+
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\" "
+ "formsignature=\"7816485729218079147\" autofillused=\"false\" "
+ "datapresent=\"144200030e\">"
+ "<field signature=\"3763331450\" autofilltype=\"3\"/>"
+ "<field signature=\"3494530716\" autofilltype=\"5\"/>"
+ "<field signature=\"1029417091\" autofilltype=\"9\"/>"
+ "<field signature=\"466116101\" autofilltype=\"14\"/>"
+ "<field signature=\"2799270304\" autofilltype=\"36\"/>"
+ "<field signature=\"509334676\" autofilltype=\"30\"/>"
+ "<field signature=\"509334676\" autofilltype=\"31\"/>"
+ "<field signature=\"509334676\" autofilltype=\"37\"/>"
+ "<field signature=\"509334676\" autofilltype=\"38\"/>"
+ "<field signature=\"509334676\" autofilltype=\"30\"/>"
+ "<field signature=\"509334676\" autofilltype=\"31\"/>"
+ "<field signature=\"509334676\" autofilltype=\"37\"/>"
+ "<field signature=\"509334676\" autofilltype=\"38\"/>"
+ "</autofillupload>",
+ encoded_xml);
+
+ // Add 50 address fields - now the form is invalid, as it has too many fields.
+ for (size_t i = 0; i < 50; ++i) {
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(ADDRESS_HOME_LINE1);
+ possible_field_types.back().insert(ADDRESS_HOME_LINE2);
+ possible_field_types.back().insert(ADDRESS_BILLING_LINE1);
+ possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
+ }
+ form_structure.reset(new FormStructure(form, std::string()));
+ ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
+ for (size_t i = 0; i < form_structure->field_count(); ++i)
+ form_structure->field(i)->set_possible_types(possible_field_types[i]);
+ EXPECT_FALSE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+}
+
+// Check that we compute the "datapresent" string correctly for the given
+// |available_types|.
+TEST(FormStructureTest, CheckDataPresence) {
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("first");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("last");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+
+ FieldTypeSet unknown_type;
+ unknown_type.insert(UNKNOWN_TYPE);
+ for (size_t i = 0; i < form_structure.field_count(); ++i)
+ form_structure.field(i)->set_possible_types(unknown_type);
+
+ // No available types.
+ // datapresent should be "" == trimmmed(0x0000000000000000) ==
+ // 0b0000000000000000000000000000000000000000000000000000000000000000
+ FieldTypeSet available_field_types;
+
+ std::string encoded_xml;
+ EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"6402244543831589061\" autofillused=\"false\""
+ " datapresent=\"\">"
+ "<field signature=\"1089846351\" autofilltype=\"1\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"1\"/>"
+ "<field signature=\"420638584\" autofilltype=\"1\"/>"
+ "</autofillupload>",
+ encoded_xml);
+
+ // Only a few types available.
+ // datapresent should be "1540000240" == trimmmed(0x1540000240000000) ==
+ // 0b0001010101000000000000000000001001000000000000000000000000000000
+ // The set bits are:
+ // 3 == NAME_FIRST
+ // 5 == NAME_LAST
+ // 7 == NAME_FULL
+ // 9 == EMAIL_ADDRESS
+ // 30 == ADDRESS_HOME_LINE1
+ // 33 == ADDRESS_HOME_CITY
+ available_field_types.clear();
+ available_field_types.insert(NAME_FIRST);
+ available_field_types.insert(NAME_LAST);
+ available_field_types.insert(NAME_FULL);
+ available_field_types.insert(EMAIL_ADDRESS);
+ available_field_types.insert(ADDRESS_HOME_LINE1);
+ available_field_types.insert(ADDRESS_HOME_CITY);
+
+ EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"6402244543831589061\" autofillused=\"false\""
+ " datapresent=\"1540000240\">"
+ "<field signature=\"1089846351\" autofilltype=\"1\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"1\"/>"
+ "<field signature=\"420638584\" autofilltype=\"1\"/>"
+ "</autofillupload>",
+ encoded_xml);
+
+ // All supported non-credit card types available.
+ // datapresent should be "1f7e000378000008" == trimmmed(0x1f7e000378000008) ==
+ // 0b0001111101111110000000000000001101111000000000000000000000001000
+ // The set bits are:
+ // 3 == NAME_FIRST
+ // 4 == NAME_MIDDLE
+ // 5 == NAME_LAST
+ // 6 == NAME_MIDDLE_INITIAL
+ // 7 == NAME_FULL
+ // 9 == EMAIL_ADDRESS
+ // 10 == PHONE_HOME_NUMBER,
+ // 11 == PHONE_HOME_CITY_CODE,
+ // 12 == PHONE_HOME_COUNTRY_CODE,
+ // 13 == PHONE_HOME_CITY_AND_NUMBER,
+ // 14 == PHONE_HOME_WHOLE_NUMBER,
+ // 30 == ADDRESS_HOME_LINE1
+ // 31 == ADDRESS_HOME_LINE2
+ // 33 == ADDRESS_HOME_CITY
+ // 34 == ADDRESS_HOME_STATE
+ // 35 == ADDRESS_HOME_ZIP
+ // 36 == ADDRESS_HOME_COUNTRY
+ // 60 == COMPANY_NAME
+ available_field_types.clear();
+ available_field_types.insert(NAME_FIRST);
+ available_field_types.insert(NAME_MIDDLE);
+ available_field_types.insert(NAME_LAST);
+ available_field_types.insert(NAME_MIDDLE_INITIAL);
+ available_field_types.insert(NAME_FULL);
+ available_field_types.insert(EMAIL_ADDRESS);
+ available_field_types.insert(PHONE_HOME_NUMBER);
+ available_field_types.insert(PHONE_HOME_CITY_CODE);
+ available_field_types.insert(PHONE_HOME_COUNTRY_CODE);
+ available_field_types.insert(PHONE_HOME_CITY_AND_NUMBER);
+ available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
+ available_field_types.insert(ADDRESS_HOME_LINE1);
+ available_field_types.insert(ADDRESS_HOME_LINE2);
+ available_field_types.insert(ADDRESS_HOME_CITY);
+ available_field_types.insert(ADDRESS_HOME_STATE);
+ available_field_types.insert(ADDRESS_HOME_ZIP);
+ available_field_types.insert(ADDRESS_HOME_COUNTRY);
+ available_field_types.insert(COMPANY_NAME);
+
+ EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"6402244543831589061\" autofillused=\"false\""
+ " datapresent=\"1f7e000378000008\">"
+ "<field signature=\"1089846351\" autofilltype=\"1\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"1\"/>"
+ "<field signature=\"420638584\" autofilltype=\"1\"/>"
+ "</autofillupload>",
+ encoded_xml);
+
+ // All supported credit card types available.
+ // datapresent should be "0000000000001fc0" == trimmmed(0x0000000000001fc0) ==
+ // 0b0000000000000000000000000000000000000000000000000001111111000000
+ // The set bits are:
+ // 51 == CREDIT_CARD_NAME
+ // 52 == CREDIT_CARD_NUMBER
+ // 53 == CREDIT_CARD_EXP_MONTH
+ // 54 == CREDIT_CARD_EXP_2_DIGIT_YEAR
+ // 55 == CREDIT_CARD_EXP_4_DIGIT_YEAR
+ // 56 == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
+ // 57 == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
+ available_field_types.clear();
+ available_field_types.insert(CREDIT_CARD_NAME);
+ available_field_types.insert(CREDIT_CARD_NUMBER);
+ available_field_types.insert(CREDIT_CARD_EXP_MONTH);
+ available_field_types.insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+ available_field_types.insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ available_field_types.insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
+ available_field_types.insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
+
+ EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"6402244543831589061\" autofillused=\"false\""
+ " datapresent=\"0000000000001fc0\">"
+ "<field signature=\"1089846351\" autofilltype=\"1\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"1\"/>"
+ "<field signature=\"420638584\" autofilltype=\"1\"/>"
+ "</autofillupload>",
+ encoded_xml);
+
+ // All supported types available.
+ // datapresent should be "1f7e000378001fc8" == trimmmed(0x1f7e000378001fc8) ==
+ // 0b0001111101111110000000000000001101111000000000000001111111001000
+ // The set bits are:
+ // 3 == NAME_FIRST
+ // 4 == NAME_MIDDLE
+ // 5 == NAME_LAST
+ // 6 == NAME_MIDDLE_INITIAL
+ // 7 == NAME_FULL
+ // 9 == EMAIL_ADDRESS
+ // 10 == PHONE_HOME_NUMBER,
+ // 11 == PHONE_HOME_CITY_CODE,
+ // 12 == PHONE_HOME_COUNTRY_CODE,
+ // 13 == PHONE_HOME_CITY_AND_NUMBER,
+ // 14 == PHONE_HOME_WHOLE_NUMBER,
+ // 30 == ADDRESS_HOME_LINE1
+ // 31 == ADDRESS_HOME_LINE2
+ // 33 == ADDRESS_HOME_CITY
+ // 34 == ADDRESS_HOME_STATE
+ // 35 == ADDRESS_HOME_ZIP
+ // 36 == ADDRESS_HOME_COUNTRY
+ // 51 == CREDIT_CARD_NAME
+ // 52 == CREDIT_CARD_NUMBER
+ // 53 == CREDIT_CARD_EXP_MONTH
+ // 54 == CREDIT_CARD_EXP_2_DIGIT_YEAR
+ // 55 == CREDIT_CARD_EXP_4_DIGIT_YEAR
+ // 56 == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
+ // 57 == CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR
+ // 60 == COMPANY_NAME
+ available_field_types.clear();
+ available_field_types.insert(NAME_FIRST);
+ available_field_types.insert(NAME_MIDDLE);
+ available_field_types.insert(NAME_LAST);
+ available_field_types.insert(NAME_MIDDLE_INITIAL);
+ available_field_types.insert(NAME_FULL);
+ available_field_types.insert(EMAIL_ADDRESS);
+ available_field_types.insert(PHONE_HOME_NUMBER);
+ available_field_types.insert(PHONE_HOME_CITY_CODE);
+ available_field_types.insert(PHONE_HOME_COUNTRY_CODE);
+ available_field_types.insert(PHONE_HOME_CITY_AND_NUMBER);
+ available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
+ available_field_types.insert(ADDRESS_HOME_LINE1);
+ available_field_types.insert(ADDRESS_HOME_LINE2);
+ available_field_types.insert(ADDRESS_HOME_CITY);
+ available_field_types.insert(ADDRESS_HOME_STATE);
+ available_field_types.insert(ADDRESS_HOME_ZIP);
+ available_field_types.insert(ADDRESS_HOME_COUNTRY);
+ available_field_types.insert(CREDIT_CARD_NAME);
+ available_field_types.insert(CREDIT_CARD_NUMBER);
+ available_field_types.insert(CREDIT_CARD_EXP_MONTH);
+ available_field_types.insert(CREDIT_CARD_EXP_2_DIGIT_YEAR);
+ available_field_types.insert(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ available_field_types.insert(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR);
+ available_field_types.insert(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR);
+ available_field_types.insert(COMPANY_NAME);
+
+ EXPECT_TRUE(form_structure.EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"6402244543831589061\" autofillused=\"false\""
+ " datapresent=\"1f7e000378001fc8\">"
+ "<field signature=\"1089846351\" autofilltype=\"1\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"1\"/>"
+ "<field signature=\"420638584\" autofilltype=\"1\"/>"
+ "</autofillupload>",
+ encoded_xml);
+}
+
+TEST(FormStructureTest, CheckMultipleTypes) {
+ // Throughout this test, datapresent should be
+ // 0x1440000360000008 ==
+ // 0b0001010001000000000000000000001101100000000000000000000000001000
+ // The set bits are:
+ // 3 == NAME_FIRST
+ // 5 == NAME_LAST
+ // 9 == EMAIL_ADDRESS
+ // 30 == ADDRESS_HOME_LINE1
+ // 31 == ADDRESS_HOME_LINE2
+ // 33 == ADDRESS_HOME_CITY
+ // 34 == ADDRESS_HOME_STATE
+ // 60 == COMPANY_NAME
+ FieldTypeSet available_field_types;
+ available_field_types.insert(NAME_FIRST);
+ available_field_types.insert(NAME_LAST);
+ available_field_types.insert(EMAIL_ADDRESS);
+ available_field_types.insert(ADDRESS_HOME_LINE1);
+ available_field_types.insert(ADDRESS_HOME_LINE2);
+ available_field_types.insert(ADDRESS_HOME_CITY);
+ available_field_types.insert(ADDRESS_HOME_STATE);
+ available_field_types.insert(COMPANY_NAME);
+
+ // Check that multiple types for the field are processed correctly.
+ scoped_ptr<FormStructure> form_structure;
+ std::vector<FieldTypeSet> possible_field_types;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("email");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(EMAIL_ADDRESS);
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("first");
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(NAME_FIRST);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("last");
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(NAME_LAST);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = ASCIIToUTF16("address");
+ form.fields.push_back(field);
+ possible_field_types.push_back(FieldTypeSet());
+ possible_field_types.back().insert(ADDRESS_HOME_LINE1);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+
+ for (size_t i = 0; i < form_structure->field_count(); ++i)
+ form_structure->field(i)->set_possible_types(possible_field_types[i]);
+ std::string encoded_xml;
+
+ // Now we matched both fields singularly.
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"18062476096658145866\" autofillused=\"false\""
+ " datapresent=\"1440000360000008\">"
+ "<field signature=\"420638584\" autofilltype=\"9\"/>"
+ "<field signature=\"1089846351\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"5\"/>"
+ "<field signature=\"509334676\" autofilltype=\"30\"/>"
+ "</autofillupload>",
+ encoded_xml);
+ // Match third field as both first and last.
+ possible_field_types[2].insert(NAME_FIRST);
+ form_structure->field(2)->set_possible_types(possible_field_types[2]);
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"18062476096658145866\" autofillused=\"false\""
+ " datapresent=\"1440000360000008\">"
+ "<field signature=\"420638584\" autofilltype=\"9\"/>"
+ "<field signature=\"1089846351\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"5\"/>"
+ "<field signature=\"509334676\" autofilltype=\"30\"/>"
+ "</autofillupload>",
+ encoded_xml);
+ possible_field_types[3].insert(ADDRESS_HOME_LINE2);
+ form_structure->field(form_structure->field_count() - 1)->set_possible_types(
+ possible_field_types[form_structure->field_count() - 1]);
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"18062476096658145866\" autofillused=\"false\""
+ " datapresent=\"1440000360000008\">"
+ "<field signature=\"420638584\" autofilltype=\"9\"/>"
+ "<field signature=\"1089846351\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"5\"/>"
+ "<field signature=\"509334676\" autofilltype=\"30\"/>"
+ "<field signature=\"509334676\" autofilltype=\"31\"/>"
+ "</autofillupload>",
+ encoded_xml);
+ possible_field_types[3].clear();
+ possible_field_types[3].insert(ADDRESS_HOME_LINE1);
+ possible_field_types[3].insert(COMPANY_NAME);
+ form_structure->field(form_structure->field_count() - 1)->set_possible_types(
+ possible_field_types[form_structure->field_count() - 1]);
+ EXPECT_TRUE(form_structure->EncodeUploadRequest(available_field_types, false,
+ &encoded_xml));
+ EXPECT_EQ("<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>"
+ "<autofillupload clientversion=\"6.1.1715.1442/en (GGLL)\""
+ " formsignature=\"18062476096658145866\" autofillused=\"false\""
+ " datapresent=\"1440000360000008\">"
+ "<field signature=\"420638584\" autofilltype=\"9\"/>"
+ "<field signature=\"1089846351\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"3\"/>"
+ "<field signature=\"2404144663\" autofilltype=\"5\"/>"
+ "<field signature=\"509334676\" autofilltype=\"30\"/>"
+ "<field signature=\"509334676\" autofilltype=\"60\"/>"
+ "</autofillupload>",
+ encoded_xml);
+}
+
+TEST(FormStructureTest, CheckFormSignature) {
+ // Check that form signature is created correctly.
+ scoped_ptr<FormStructure> form_structure;
+ FormData form;
+ form.method = ASCIIToUTF16("post");
+
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("email");
+ field.name = ASCIIToUTF16("email");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("first");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+
+ EXPECT_EQ(FormStructureTest::Hash64Bit(
+ std::string("://&&email&first")),
+ form_structure->FormSignature());
+
+ form.origin = GURL(std::string("http://www.facebook.com"));
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_EQ(FormStructureTest::Hash64Bit(
+ std::string("http://www.facebook.com&&email&first")),
+ form_structure->FormSignature());
+
+ form.action = GURL(std::string("https://login.facebook.com/path"));
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_EQ(FormStructureTest::Hash64Bit(
+ std::string("https://login.facebook.com&&email&first")),
+ form_structure->FormSignature());
+
+ form.name = ASCIIToUTF16("login_form");
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_EQ(FormStructureTest::Hash64Bit(
+ std::string("https://login.facebook.com&login_form&email&first")),
+ form_structure->FormSignature());
+}
+
+TEST(FormStructureTest, ToFormData) {
+ FormData form;
+ form.name = ASCIIToUTF16("the-name");
+ form.method = ASCIIToUTF16("POST");
+ form.origin = GURL("http://cool.com");
+ form.action = form.origin.Resolve("/login");
+
+ FormFieldData field;
+ field.label = ASCIIToUTF16("username");
+ field.name = ASCIIToUTF16("username");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ form.fields.push_back(field);
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("Submit");
+ field.form_control_type = "submit";
+ form.fields.push_back(field);
+
+ EXPECT_EQ(form, FormStructure(form, std::string()).ToFormData());
+
+ // Currently |FormStructure(form_data)ToFormData().user_submitted| is always
+ // false. This forces a future author that changes this to update this test.
+ form.user_submitted = true;
+ EXPECT_NE(form, FormStructure(form, std::string()).ToFormData());
+}
diff --git a/components/autofill/browser/name_field.cc b/components/autofill/browser/name_field.cc
new file mode 100644
index 0000000..8d29111
--- /dev/null
+++ b/components/autofill/browser/name_field.cc
@@ -0,0 +1,214 @@
+// Copyright (c) 2011 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/browser/name_field.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_regex_constants.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// A form field that can parse a full name field.
+class FullNameField : public NameField {
+ public:
+ static FullNameField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ explicit FullNameField(const AutofillField* field);
+
+ const AutofillField* field_;
+
+ DISALLOW_COPY_AND_ASSIGN(FullNameField);
+};
+
+// A form field that can parse a first and last name field.
+class FirstLastNameField : public NameField {
+ public:
+ static FirstLastNameField* ParseSpecificName(AutofillScanner* scanner);
+ static FirstLastNameField* ParseComponentNames(AutofillScanner* scanner);
+ static FirstLastNameField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ FirstLastNameField();
+
+ const AutofillField* first_name_;
+ const AutofillField* middle_name_; // Optional.
+ const AutofillField* last_name_;
+ bool middle_initial_; // True if middle_name_ is a middle initial.
+
+ DISALLOW_COPY_AND_ASSIGN(FirstLastNameField);
+};
+
+} // namespace
+
+FormField* NameField::Parse(AutofillScanner* scanner) {
+ if (scanner->IsEnd())
+ return NULL;
+
+ // Try FirstLastNameField first since it's more specific.
+ NameField* field = FirstLastNameField::Parse(scanner);
+ if (!field)
+ field = FullNameField::Parse(scanner);
+ return field;
+}
+
+// This is overriden in concrete subclasses.
+bool NameField::ClassifyField(FieldTypeMap* map) const {
+ return false;
+}
+
+FullNameField* FullNameField::Parse(AutofillScanner* scanner) {
+ // Exclude e.g. "username" or "nickname" fields.
+ scanner->SaveCursor();
+ bool should_ignore = ParseField(scanner,
+ UTF8ToUTF16(autofill::kNameIgnoredRe), NULL);
+ scanner->Rewind();
+ if (should_ignore)
+ return NULL;
+
+ // Searching for any label containing the word "name" is too general;
+ // for example, Travelocity_Edit travel profile.html contains a field
+ // "Travel Profile Name".
+ const AutofillField* field = NULL;
+ if (ParseField(scanner, UTF8ToUTF16(autofill::kNameRe), &field))
+ return new FullNameField(field);
+
+ return NULL;
+}
+
+bool FullNameField::ClassifyField(FieldTypeMap* map) const {
+ return AddClassification(field_, NAME_FULL, map);
+}
+
+FullNameField::FullNameField(const AutofillField* field)
+ : field_(field) {
+}
+
+FirstLastNameField* FirstLastNameField::ParseSpecificName(
+ AutofillScanner* scanner) {
+ // Some pages (e.g. Overstock_comBilling.html, SmithsonianCheckout.html)
+ // have the label "Name" followed by two or three text fields.
+ scoped_ptr<FirstLastNameField> v(new FirstLastNameField);
+ scanner->SaveCursor();
+
+ const AutofillField* next = NULL;
+ if (ParseField(scanner,
+ UTF8ToUTF16(autofill::kNameSpecificRe), &v->first_name_) &&
+ ParseEmptyLabel(scanner, &next)) {
+ if (ParseEmptyLabel(scanner, &v->last_name_)) {
+ // There are three name fields; assume that the middle one is a
+ // middle initial (it is, at least, on SmithsonianCheckout.html).
+ v->middle_name_ = next;
+ v->middle_initial_ = true;
+ } else { // only two name fields
+ v->last_name_ = next;
+ }
+
+ return v.release();
+ }
+
+ scanner->Rewind();
+ return NULL;
+}
+
+FirstLastNameField* FirstLastNameField::ParseComponentNames(
+ AutofillScanner* scanner) {
+ scoped_ptr<FirstLastNameField> v(new FirstLastNameField);
+ scanner->SaveCursor();
+
+ // A fair number of pages use the names "fname" and "lname" for naming
+ // first and last name fields (examples from the test suite:
+ // BESTBUY_COM - Sign In2.html; Crate and Barrel Check Out.html;
+ // dell_checkout1.html). At least one UK page (The China Shop2.html)
+ // asks, in stuffy English style, for just initials and a surname,
+ // so we match "initials" here (and just fill in a first name there,
+ // American-style).
+ // The ".*first$" matches fields ending in "first" (example in sample8.html).
+ // The ".*last$" matches fields ending in "last" (example in sample8.html).
+
+ // Allow name fields to appear in any order.
+ while (!scanner->IsEnd()) {
+ // Skip over any unrelated fields, e.g. "username" or "nickname".
+ if (ParseFieldSpecifics(scanner, UTF8ToUTF16(autofill::kNameIgnoredRe),
+ MATCH_DEFAULT | MATCH_SELECT, NULL)) {
+ continue;
+ }
+
+ if (!v->first_name_ &&
+ ParseField(scanner, UTF8ToUTF16(autofill::kFirstNameRe),
+ &v->first_name_)) {
+ continue;
+ }
+
+ // We check for a middle initial before checking for a middle name
+ // because at least one page (PC Connection.html) has a field marked
+ // as both (the label text is "MI" and the element name is
+ // "txtmiddlename"); such a field probably actually represents a
+ // middle initial.
+ if (!v->middle_name_ &&
+ ParseField(scanner, UTF8ToUTF16(autofill::kMiddleInitialRe),
+ &v->middle_name_)) {
+ v->middle_initial_ = true;
+ continue;
+ }
+
+ if (!v->middle_name_ &&
+ ParseField(scanner, UTF8ToUTF16(autofill::kMiddleNameRe),
+ &v->middle_name_)) {
+ continue;
+ }
+
+ if (!v->last_name_ &&
+ ParseField(scanner, UTF8ToUTF16(autofill::kLastNameRe),
+ &v->last_name_)) {
+ continue;
+ }
+
+ break;
+ }
+
+ // Consider the match to be successful if we detected both first and last name
+ // fields.
+ if (v->first_name_ && v->last_name_)
+ return v.release();
+
+ scanner->Rewind();
+ return NULL;
+}
+
+FirstLastNameField* FirstLastNameField::Parse(AutofillScanner* scanner) {
+ FirstLastNameField* field = ParseSpecificName(scanner);
+ if (!field)
+ field = ParseComponentNames(scanner);
+ return field;
+}
+
+FirstLastNameField::FirstLastNameField()
+ : first_name_(NULL),
+ middle_name_(NULL),
+ last_name_(NULL),
+ middle_initial_(false) {
+}
+
+bool FirstLastNameField::ClassifyField(FieldTypeMap* map) const {
+ bool ok = AddClassification(first_name_, NAME_FIRST, map);
+ ok = ok && AddClassification(last_name_, NAME_LAST, map);
+ AutofillFieldType type = middle_initial_ ? NAME_MIDDLE_INITIAL : NAME_MIDDLE;
+ ok = ok && AddClassification(middle_name_, type, map);
+ return ok;
+}
diff --git a/components/autofill/browser/name_field.h b/components/autofill/browser/name_field.h
new file mode 100644
index 0000000..e62b62f
--- /dev/null
+++ b/components/autofill/browser/name_field.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 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_BROWSER_NAME_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_NAME_FIELD_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/form_field.h"
+
+class AutofillScanner;
+
+// A form field that can parse either a FullNameField or a FirstLastNameField.
+class NameField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ NameField() {}
+
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstMiddleLast);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstMiddleLast2);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstLast);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstLast2);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstLastMiddleWithSpaces);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstLastEmpty);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, FirstMiddleLastEmpty);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, MiddleInitial);
+ FRIEND_TEST_ALL_PREFIXES(NameFieldTest, MiddleInitialAtEnd);
+
+ DISALLOW_COPY_AND_ASSIGN(NameField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_NAME_FIELD_H_
diff --git a/components/autofill/browser/name_field_unittest.cc b/components/autofill/browser/name_field_unittest.cc
new file mode 100644
index 0000000..2689191
--- /dev/null
+++ b/components/autofill/browser/name_field_unittest.cc
@@ -0,0 +1,307 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/name_field.h"
+#include "components/autofill/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class NameFieldTest : public testing::Test {
+ public:
+ NameFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<NameField> field_;
+ FieldTypeMap field_type_map_;
+
+ // Downcast for tests.
+ static NameField* Parse(AutofillScanner* scanner) {
+ return static_cast<NameField*>(NameField::Parse(scanner));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NameFieldTest);
+};
+
+TEST_F(NameFieldTest, FirstMiddleLast) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("First");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Middle Name");
+ field.name = ASCIIToUTF16("Middle");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("Last");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_MIDDLE, field_type_map_[ASCIIToUTF16("name2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name3")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name3")]);
+}
+
+TEST_F(NameFieldTest, FirstMiddleLast2) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("firstName");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("middleName");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("lastName");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_MIDDLE, field_type_map_[ASCIIToUTF16("name2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name3")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name3")]);
+}
+
+TEST_F(NameFieldTest, FirstLast) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("last_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name2")]);
+}
+
+TEST_F(NameFieldTest, FirstLast2) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name");
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Name");
+ field.name = ASCIIToUTF16("last_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name2")]);
+}
+
+TEST_F(NameFieldTest, FirstLastMiddleWithSpaces) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("Middle Name");
+ field.name = ASCIIToUTF16("middle_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("last_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_MIDDLE, field_type_map_[ASCIIToUTF16("name2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name3")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name3")]);
+}
+
+TEST_F(NameFieldTest, FirstLastEmpty) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name");
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("last_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name2")]);
+}
+
+TEST_F(NameFieldTest, FirstMiddleLastEmpty) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Name");
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("middle_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("last_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_MIDDLE_INITIAL, field_type_map_[ASCIIToUTF16("name2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name3")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name3")]);
+}
+
+TEST_F(NameFieldTest, MiddleInitial) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("MI");
+ field.name = ASCIIToUTF16("middle_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = ASCIIToUTF16("last_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_MIDDLE_INITIAL, field_type_map_[ASCIIToUTF16("name2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name3")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name3")]);
+}
+
+TEST_F(NameFieldTest, MiddleInitialNoLastName) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("First Name");
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = ASCIIToUTF16("MI");
+ field.name = ASCIIToUTF16("middle_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<NameField*>(NULL), field_.get());
+}
+
+// This case is from the dell.com checkout page. The middle initial "mi" string
+// came at the end following other descriptive text. http://crbug.com/45123.
+TEST_F(NameFieldTest, MiddleInitialAtEnd) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("XXXnameXXXfirst");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("XXXnameXXXmi");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("XXXnameXXXlast");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<NameField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name1")) != field_type_map_.end());
+ EXPECT_EQ(NAME_FIRST, field_type_map_[ASCIIToUTF16("name1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name2")) != field_type_map_.end());
+ EXPECT_EQ(NAME_MIDDLE_INITIAL, field_type_map_[ASCIIToUTF16("name2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("name3")) != field_type_map_.end());
+ EXPECT_EQ(NAME_LAST, field_type_map_[ASCIIToUTF16("name3")]);
+}
diff --git a/components/autofill/browser/password_autofill_manager.cc b/components/autofill/browser/password_autofill_manager.cc
new file mode 100644
index 0000000..6a641f6
--- /dev/null
+++ b/components/autofill/browser/password_autofill_manager.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/password_autofill_manager.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillManager, public:
+
+PasswordAutofillManager::PasswordAutofillManager(
+ content::WebContents* web_contents) : web_contents_(web_contents) {
+}
+
+PasswordAutofillManager::~PasswordAutofillManager() {
+}
+
+bool PasswordAutofillManager::DidAcceptAutofillSuggestion(
+ const FormFieldData& field,
+ const string16& value) {
+ PasswordFormFillData password;
+ if (!FindLoginInfo(field, &password))
+ return false;
+
+ if (WillFillUserNameAndPassword(value, password)) {
+ if (web_contents_) {
+ content::RenderViewHost* render_view_host =
+ web_contents_->GetRenderViewHost();
+ render_view_host->Send(new AutofillMsg_AcceptPasswordAutofillSuggestion(
+ render_view_host->GetRoutingID(),
+ value));
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void PasswordAutofillManager::AddPasswordFormMapping(
+ const FormFieldData& username_element,
+ const PasswordFormFillData& password) {
+ login_to_password_info_[username_element] = password;
+}
+
+void PasswordAutofillManager::Reset() {
+ login_to_password_info_.clear();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillManager, private:
+
+bool PasswordAutofillManager::WillFillUserNameAndPassword(
+ const string16& current_username,
+ const PasswordFormFillData& fill_data) {
+ // Look for any suitable matches to current field text.
+ if (fill_data.basic_data.fields[0].value == current_username) {
+ return true;
+ } else {
+ // Scan additional logins for a match.
+ PasswordFormFillData::LoginCollection::const_iterator iter;
+ for (iter = fill_data.additional_logins.begin();
+ iter != fill_data.additional_logins.end(); ++iter) {
+ if (iter->first == current_username)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PasswordAutofillManager::FindLoginInfo(
+ const FormFieldData& field,
+ PasswordFormFillData* found_password) {
+ LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(field);
+ if (iter == login_to_password_info_.end())
+ return false;
+
+ *found_password = iter->second;
+ return true;
+}
diff --git a/components/autofill/browser/password_autofill_manager.h b/components/autofill/browser/password_autofill_manager.h
new file mode 100644
index 0000000..3d16054
--- /dev/null
+++ b/components/autofill/browser/password_autofill_manager.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_PASSWORD_AUTOFILL_MANAGER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PASSWORD_AUTOFILL_MANAGER_H_
+
+// This file was contains some repeated code from
+// chrome/renderer/autofill/password_autofill_manager because as we move to the
+// new Autofill UI we needs these functions in both the browser and renderer.
+// Once the move is completed the repeated code in the renderer half should be
+// removed.
+// http://crbug.com/51644
+
+#include <map>
+
+#include "components/autofill/common/password_form_fill_data.h"
+
+namespace content {
+class WebContents;
+} // namespace content
+
+// This class is responsible for filling password forms.
+class PasswordAutofillManager {
+ public:
+ explicit PasswordAutofillManager(content::WebContents* web_contents);
+ virtual ~PasswordAutofillManager();
+
+ // Fills the password associated with user name |value|. Returns true if the
+ // username and password fields were filled, false otherwise.
+ bool DidAcceptAutofillSuggestion(const FormFieldData& field,
+ const string16& value);
+
+ // Invoked when a password mapping is added.
+ void AddPasswordFormMapping(
+ const FormFieldData& username_element,
+ const PasswordFormFillData& password);
+
+ // Invoked to clear any page specific cached values.
+ void Reset();
+
+ private:
+ // TODO(csharp): Modify the AutofillExternalDeletegate code so that it can
+ // figure out if a entry is a password one without using this mapping.
+ // crbug.com/118601
+ typedef std::map<FormFieldData,
+ PasswordFormFillData>
+ LoginToPasswordInfoMap;
+
+ // Returns true if |current_username| matches a username for one of the
+ // login mappings in |password|.
+ bool WillFillUserNameAndPassword(
+ const string16& current_username,
+ const PasswordFormFillData& password);
+
+ // Finds login information for a |node| that was previously filled.
+ bool FindLoginInfo(const FormFieldData& field,
+ PasswordFormFillData* found_password);
+
+ // The logins we have filled so far with their associated info.
+ LoginToPasswordInfoMap login_to_password_info_;
+
+ // We only need the RenderViewHost pointer in WebContents, but if we attempt
+ // to just store RenderViewHost on creation, it becomes invalid once we start
+ // using it. By having the WebContents we can always get a valid pointer.
+ content::WebContents* web_contents_; // Weak reference.
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordAutofillManager);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PASSWORD_AUTOFILL_MANAGER_H_
diff --git a/components/autofill/browser/password_autofill_manager_unittest.cc b/components/autofill/browser/password_autofill_manager_unittest.cc
new file mode 100644
index 0000000..bcdd6f0
--- /dev/null
+++ b/components/autofill/browser/password_autofill_manager_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/password_autofill_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// The name of the username/password element in the form.
+const char* const kUsernameName = "username";
+const char* const kInvalidUsername = "no-username";
+const char* const kPasswordName = "password";
+
+const char* const kAliceUsername = "alice";
+const char* const kAlicePassword = "password";
+
+const char* const kValue = "password";
+
+} // namespace
+
+class PasswordAutofillManagerTest : public testing::Test {
+ protected:
+ PasswordAutofillManagerTest() : password_autofill_manager_(NULL) {}
+
+ virtual void SetUp() OVERRIDE {
+ // Add a preferred login and an additional login to the FillData.
+ string16 username1 = ASCIIToUTF16(kAliceUsername);
+ string16 password1 = ASCIIToUTF16(kAlicePassword);
+
+ username_field_.name = ASCIIToUTF16(kUsernameName);
+ username_field_.value = username1;
+ fill_data_.basic_data.fields.push_back(username_field_);
+
+ FormFieldData password_field;
+ password_field.name = ASCIIToUTF16(kPasswordName);
+ password_field.value = password1;
+ fill_data_.basic_data.fields.push_back(password_field);
+
+ password_autofill_manager_.AddPasswordFormMapping(username_field_,
+ fill_data_);
+ }
+
+ PasswordAutofillManager* password_autofill_manager() {
+ return &password_autofill_manager_;
+ }
+
+ const FormFieldData& username_field() { return username_field_; }
+
+ private:
+ PasswordFormFillData fill_data_;
+ FormFieldData username_field_;
+
+ PasswordAutofillManager password_autofill_manager_;
+};
+
+TEST_F(PasswordAutofillManagerTest, DidAcceptAutofillSuggestion) {
+ EXPECT_TRUE(password_autofill_manager()->DidAcceptAutofillSuggestion(
+ username_field(), ASCIIToUTF16(kAliceUsername)));
+ EXPECT_FALSE(password_autofill_manager()->DidAcceptAutofillSuggestion(
+ username_field(), ASCIIToUTF16(kInvalidUsername)));
+
+ FormFieldData invalid_username_field;
+ invalid_username_field.name = ASCIIToUTF16(kInvalidUsername);
+
+ EXPECT_FALSE(password_autofill_manager()->DidAcceptAutofillSuggestion(
+ invalid_username_field, ASCIIToUTF16(kAliceUsername)));
+
+ password_autofill_manager()->Reset();
+ EXPECT_FALSE(password_autofill_manager()->DidAcceptAutofillSuggestion(
+ username_field(), ASCIIToUTF16(kAliceUsername)));
+}
diff --git a/components/autofill/browser/password_generator.cc b/components/autofill/browser/password_generator.cc
new file mode 100644
index 0000000..1967706
--- /dev/null
+++ b/components/autofill/browser/password_generator.cc
@@ -0,0 +1,125 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/password_generator.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+
+const int kMinUpper = 65; // First upper case letter 'A'
+const int kMaxUpper = 90; // Last upper case letter 'Z'
+const int kMinLower = 97; // First lower case letter 'a'
+const int kMaxLower = 122; // Last lower case letter 'z'
+const int kMinDigit = 48; // First digit '0'
+const int kMaxDigit = 57; // Last digit '9'
+// Copy of the other printable symbols from the ASCII table since they are
+// disjointed.
+const char kOtherSymbols[] =
+ {'!', '\"', '#', '$', '%', '&', '\'', '(',
+ ')', '*', '+', ',', '-', '.', '/', ':',
+ ';', '<', '=', '>', '?', '@', '[', '\\',
+ ']', '^', '_', '`', '{', '|', '}', '~'};
+const size_t kMinPasswordLength = 4;
+const size_t kMaxPasswordLength = 15;
+
+namespace {
+
+// A helper function to get the length of the generated password from
+// |max_length| retrieved from input password field.
+size_t GetLengthFromHint(size_t max_length, size_t default_length) {
+ if (max_length >= kMinPasswordLength && max_length <= kMaxPasswordLength)
+ return max_length;
+ else
+ return default_length;
+}
+
+void InitializeAlphaNumericCharacters(std::vector<char>* characters) {
+ for (int i = kMinDigit; i <= kMaxDigit; ++i)
+ characters->push_back(static_cast<char>(i));
+ for (int i = kMinUpper; i <= kMaxUpper; ++i)
+ characters->push_back(static_cast<char>(i));
+ for (int i = kMinLower; i <= kMaxLower; ++i)
+ characters->push_back(static_cast<char>(i));
+}
+
+// Classic algorithm to randomly select |num_select| elements out of
+// |num_total| elements. One description can be found at:
+// "http://stackoverflow.com/questions/48087/select-a-random-n-elements-from-listt-in-c-sharp/48089#48089"
+void GetRandomSelection(size_t num_to_select,
+ size_t num_total,
+ std::vector<size_t>* selections) {
+ DCHECK_GE(num_total, num_to_select);
+ size_t num_left = num_total;
+ size_t num_needed = num_to_select;
+ for (size_t i = 0; i < num_total && num_needed > 0; ++i) {
+ // we have probability = |num_needed| / |num_left| to select
+ // this position.
+ size_t probability = base::RandInt(0, num_left - 1);
+ if (probability < num_needed) {
+ selections->push_back(i);
+ --num_needed;
+ }
+ --num_left;
+ }
+ DCHECK_EQ(num_to_select, selections->size());
+}
+
+} // namespace
+
+namespace autofill {
+
+const size_t PasswordGenerator::kDefaultPasswordLength = 12;
+
+PasswordGenerator::PasswordGenerator(size_t max_length)
+ : password_length_(GetLengthFromHint(max_length, kDefaultPasswordLength)) {}
+PasswordGenerator::~PasswordGenerator() {}
+
+std::string PasswordGenerator::Generate() const {
+ std::string ret;
+ CR_DEFINE_STATIC_LOCAL(std::vector<char>, alphanumeric_characters, ());
+ if (alphanumeric_characters.empty())
+ InitializeAlphaNumericCharacters(&alphanumeric_characters);
+
+ // First, randomly select 4 positions to hold one upper case letter,
+ // one lower case letter, one digit, and one other symbol respectively,
+ // to make sure at least one of each category of characters will be
+ // included in the password.
+ std::vector<size_t> positions;
+ GetRandomSelection(4u, password_length_, &positions);
+
+ // To enhance the strengh of the password, we random suffle the positions so
+ // that the 4 catagories can be put at a random position in it.
+ std::random_shuffle(positions.begin(), positions.end());
+
+ // Next, generate each character of the password.
+ for (size_t i = 0; i < password_length_; ++i) {
+ if (i == positions[0]) {
+ // Generate random upper case letter.
+ ret.push_back(static_cast<char>(base::RandInt(kMinUpper, kMaxUpper)));
+ } else if (i == positions[1]) {
+ // Generate random lower case letter.
+ ret.push_back(static_cast<char>(base::RandInt(kMinLower, kMaxLower)));
+ } else if (i == positions[2]) {
+ // Generate random digit.
+ ret.push_back(static_cast<char>(base::RandInt(kMinDigit, kMaxDigit)));
+ } else if (i == positions[3]) {
+ // Generate random other symbol.
+ ret.push_back(
+ kOtherSymbols[base::RandInt(0, arraysize(kOtherSymbols) - 1)]);
+ } else {
+ // Generate random alphanumeric character. We don't use other symbols
+ // here as most sites don't allow a lot of non-alphanumeric characters.
+ ret.push_back(
+ alphanumeric_characters.at(
+ base::RandInt(0, alphanumeric_characters.size() - 1)));
+ }
+ }
+ return ret;
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/password_generator.h b/components/autofill/browser/password_generator.h
new file mode 100644
index 0000000..0872bee
--- /dev/null
+++ b/components/autofill/browser/password_generator.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_PASSWORD_GENERATOR_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PASSWORD_GENERATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+
+namespace autofill {
+
+// Class to generate random passwords. Currently we just use a generic algorithm
+// for all sites, but eventually we can incorporate additional information to
+// determine passwords that are likely to be accepted (i.e. use pattern field,
+// previous generated passwords, crowdsourcing, etc.)
+class PasswordGenerator {
+ public:
+ // |max_length| is used as a hint for the generated password's length.
+ explicit PasswordGenerator(size_t max_length);
+ ~PasswordGenerator();
+
+ // Returns a random password such that:
+ // (1) Each character is guaranteed to be a non-whitespace printable ASCII
+ // character.
+ // (2) The generated password will contain AT LEAST one upper case letter, one
+ // lower case letter, one digit, and EXACTLY one other symbol.
+ // (3) The password length will be equal to |password_length_| (see comment
+ // for the constructor).
+ // Not thread safe.
+ std::string Generate() const;
+
+ private:
+ // Unit test also need to access |kDefaultPasswordLength|.
+ static const size_t kDefaultPasswordLength;
+ FRIEND_TEST_ALL_PREFIXES(PasswordGeneratorTest, PasswordLength);
+
+ // The length of the generated password.
+ const size_t password_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordGenerator);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PASSWORD_GENERATOR_H_
diff --git a/components/autofill/browser/password_generator_unittest.cc b/components/autofill/browser/password_generator_unittest.cc
new file mode 100644
index 0000000..3be72a5
--- /dev/null
+++ b/components/autofill/browser/password_generator_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <locale>
+
+#include "components/autofill/browser/password_generator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(PasswordGeneratorTest, PasswordLength) {
+ PasswordGenerator pg1(10);
+ std::string password = pg1.Generate();
+ EXPECT_EQ(password.size(), 10u);
+
+ PasswordGenerator pg2(-1);
+ password = pg2.Generate();
+ EXPECT_EQ(password.size(), PasswordGenerator::kDefaultPasswordLength);
+
+ PasswordGenerator pg3(100);
+ password = pg3.Generate();
+ EXPECT_EQ(password.size(), PasswordGenerator::kDefaultPasswordLength);
+}
+
+TEST(PasswordGeneratorTest, PasswordPattern) {
+ PasswordGenerator pg(12);
+ std::string password = pg.Generate();
+ int num_upper_case_letters = 0;
+ int num_lower_case_letters = 0;
+ int num_digits = 0;
+ int num_other_symbols = 0;
+ for (size_t i = 0; i < password.size(); i++) {
+ if (isupper(password[i]))
+ ++num_upper_case_letters;
+ else if (islower(password[i]))
+ ++num_lower_case_letters;
+ else if (isdigit(password[i]))
+ ++num_digits;
+ else
+ ++num_other_symbols;
+ }
+ EXPECT_GT(num_upper_case_letters, 0);
+ EXPECT_GT(num_lower_case_letters, 0);
+ EXPECT_GT(num_digits, 0);
+ EXPECT_EQ(num_other_symbols, 1);
+}
+
+TEST(PasswordGeneratorTest, Printable) {
+ PasswordGenerator pg(12);
+ std::string password = pg.Generate();
+ for (size_t i = 0; i < password.size(); i++) {
+ // Make sure that the character is printable.
+ EXPECT_TRUE(isgraph(password[i]));
+ }
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/personal_data_manager.cc b/components/autofill/browser/personal_data_manager.cc
new file mode 100644
index 0000000..7317a6b
--- /dev/null
+++ b/components/autofill/browser/personal_data_manager.cc
@@ -0,0 +1,972 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/personal_data_manager.h"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/api/webdata/autofill_web_data_service.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "components/autofill/browser/autofill-inl.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/form_group.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/personal_data_manager_observer.h"
+#include "components/autofill/browser/phone_number.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+#include "components/autofill/browser/validation.h"
+#include "components/autofill/common/autofill_pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/notification_source.h"
+
+using content::BrowserContext;
+
+namespace {
+
+const string16::value_type kCreditCardPrefix[] = {'*', 0};
+
+template<typename T>
+class FormGroupMatchesByGUIDFunctor {
+ public:
+ explicit FormGroupMatchesByGUIDFunctor(const std::string& guid)
+ : guid_(guid) {
+ }
+
+ bool operator()(const T& form_group) {
+ return form_group.guid() == guid_;
+ }
+
+ bool operator()(const T* form_group) {
+ return form_group->guid() == guid_;
+ }
+
+ private:
+ std::string guid_;
+};
+
+template<typename T, typename C>
+bool FindByGUID(const C& container, const std::string& guid) {
+ return std::find_if(
+ container.begin(),
+ container.end(),
+ FormGroupMatchesByGUIDFunctor<T>(guid)) != container.end();
+}
+
+template<typename T>
+class DereferenceFunctor {
+ public:
+ template<typename T_Iterator>
+ const T& operator()(const T_Iterator& iterator) {
+ return *iterator;
+ }
+};
+
+template<typename T>
+T* address_of(T& v) {
+ return &v;
+}
+
+// Returns true if minimum requirements for import of a given |profile| have
+// been met. An address submitted via a form must have at least these fields
+// filled. No verification of validity of the contents is preformed. This is
+// and existence check only.
+bool IsMinimumAddress(const AutofillProfile& profile) {
+ return !profile.GetRawInfo(ADDRESS_HOME_LINE1).empty() &&
+ !profile.GetRawInfo(ADDRESS_HOME_CITY).empty() &&
+ !profile.GetRawInfo(ADDRESS_HOME_STATE).empty() &&
+ !profile.GetRawInfo(ADDRESS_HOME_ZIP).empty();
+}
+
+// Return true if the |field_type| and |value| are valid within the context
+// of importing a form.
+bool IsValidFieldTypeAndValue(const std::set<AutofillFieldType>& types_seen,
+ AutofillFieldType field_type,
+ const string16& value) {
+ // Abandon the import if two fields of the same type are encountered.
+ // This indicates ambiguous data or miscategorization of types.
+ // Make an exception for PHONE_HOME_NUMBER however as both prefix and
+ // suffix are stored against this type.
+ if (types_seen.count(field_type) && field_type != PHONE_HOME_NUMBER)
+ return false;
+
+ // Abandon the import if an email address value shows up in a field that is
+ // not an email address.
+ if (field_type != EMAIL_ADDRESS && autofill::IsValidEmailAddress(value))
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+PersonalDataManager::PersonalDataManager()
+ : browser_context_(NULL),
+ is_data_loaded_(false),
+ pending_profiles_query_(0),
+ pending_creditcards_query_(0),
+ metric_logger_(new AutofillMetrics),
+ has_logged_profile_count_(false) {}
+
+void PersonalDataManager::Init(BrowserContext* browser_context) {
+ browser_context_ = browser_context;
+ metric_logger_->LogIsAutofillEnabledAtStartup(IsAutofillEnabled());
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+
+ // WebDataService may not be available in tests.
+ if (!autofill_data.get())
+ return;
+
+ LoadProfiles();
+ LoadCreditCards();
+
+ notification_registrar_.Add(
+ this,
+ chrome::NOTIFICATION_AUTOFILL_MULTIPLE_CHANGED,
+ autofill_data->GetNotificationSource());
+}
+
+PersonalDataManager::~PersonalDataManager() {
+ CancelPendingQuery(&pending_profiles_query_);
+ CancelPendingQuery(&pending_creditcards_query_);
+}
+
+void PersonalDataManager::OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle h,
+ const WDTypedResult* result) {
+ DCHECK(pending_profiles_query_ || pending_creditcards_query_);
+
+ if (!result) {
+ // Error from the web database.
+ if (h == pending_creditcards_query_)
+ pending_creditcards_query_ = 0;
+ else if (h == pending_profiles_query_)
+ pending_profiles_query_ = 0;
+ return;
+ }
+
+ DCHECK(result->GetType() == AUTOFILL_PROFILES_RESULT ||
+ result->GetType() == AUTOFILL_CREDITCARDS_RESULT);
+
+ switch (result->GetType()) {
+ case AUTOFILL_PROFILES_RESULT:
+ ReceiveLoadedProfiles(h, result);
+ break;
+ case AUTOFILL_CREDITCARDS_RESULT:
+ ReceiveLoadedCreditCards(h, result);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // If both requests have responded, then all personal data is loaded.
+ if (pending_profiles_query_ == 0 && pending_creditcards_query_ == 0) {
+ is_data_loaded_ = true;
+ std::vector<AutofillProfile*> profile_pointers(web_profiles_.size());
+ std::copy(web_profiles_.begin(), web_profiles_.end(),
+ profile_pointers.begin());
+ AutofillProfile::AdjustInferredLabels(&profile_pointers);
+ FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_,
+ OnPersonalDataChanged());
+ }
+}
+
+void PersonalDataManager::AddObserver(PersonalDataManagerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void PersonalDataManager::RemoveObserver(
+ PersonalDataManagerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void PersonalDataManager::Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK_EQ(type, chrome::NOTIFICATION_AUTOFILL_MULTIPLE_CHANGED);
+
+ if (DCHECK_IS_ON()) {
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+
+ DCHECK(autofill_data.get() &&
+ autofill_data->GetNotificationSource() == source);
+ }
+
+ Refresh();
+}
+
+bool PersonalDataManager::ImportFormData(
+ const FormStructure& form,
+ const CreditCard** imported_credit_card) {
+ scoped_ptr<AutofillProfile> imported_profile(new AutofillProfile);
+ scoped_ptr<CreditCard> local_imported_credit_card(new CreditCard);
+
+ // Parse the form and construct a profile based on the information that is
+ // possible to import.
+ int importable_credit_card_fields = 0;
+
+ // Detect and discard forms with multiple fields of the same type.
+ std::set<AutofillFieldType> types_seen;
+
+ // We only set complete phone, so aggregate phone parts in these vars and set
+ // complete at the end.
+ PhoneNumber::PhoneCombineHelper home;
+
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ for (size_t i = 0; i < form.field_count(); ++i) {
+ const AutofillField* field = form.field(i);
+ string16 value = CollapseWhitespace(field->value, false);
+
+ // If we don't know the type of the field, or the user hasn't entered any
+ // information into the field, then skip it.
+ if (!field->IsFieldFillable() || value.empty())
+ continue;
+
+ AutofillFieldType field_type = field->type();
+ FieldTypeGroup group(AutofillType(field_type).group());
+
+ // If the |field_type| and |value| don't pass basic validity checks then
+ // abandon the import.
+ if (!IsValidFieldTypeAndValue(types_seen, field_type, value)) {
+ imported_profile.reset();
+ local_imported_credit_card.reset();
+ break;
+ }
+
+ types_seen.insert(field_type);
+
+ if (group == AutofillType::CREDIT_CARD) {
+ if (LowerCaseEqualsASCII(field->form_control_type, "month")) {
+ DCHECK_EQ(CREDIT_CARD_EXP_MONTH, field_type);
+ local_imported_credit_card->SetInfoForMonthInputType(value);
+ } else {
+ local_imported_credit_card->SetInfo(field_type, value, app_locale);
+ }
+ ++importable_credit_card_fields;
+ } else {
+ // We need to store phone data in the variables, before building the whole
+ // number at the end. The rest of the fields are set "as is".
+ // If the fields are not the phone fields in question home.SetInfo() is
+ // going to return false.
+ if (!home.SetInfo(field_type, value))
+ imported_profile->SetInfo(field_type, value, app_locale);
+
+ // Reject profiles with invalid country information.
+ if (field_type == ADDRESS_HOME_COUNTRY &&
+ !value.empty() && imported_profile->CountryCode().empty()) {
+ imported_profile.reset();
+ break;
+ }
+ }
+ }
+
+ // Construct the phone number. Reject the profile if the number is invalid.
+ if (imported_profile.get() && !home.IsEmpty()) {
+ string16 constructed_number;
+ if (!home.ParseNumber(*imported_profile, app_locale, &constructed_number) ||
+ !imported_profile->SetInfo(PHONE_HOME_WHOLE_NUMBER, constructed_number,
+ app_locale)) {
+ imported_profile.reset();
+ }
+ }
+
+ // Reject the profile if minimum address and validation requirements are not
+ // met.
+ if (imported_profile.get() && !IsValidLearnableProfile(*imported_profile))
+ imported_profile.reset();
+
+ // Reject the credit card if we did not detect enough filled credit card
+ // fields or if the credit card number does not seem to be valid.
+ if (local_imported_credit_card.get() &&
+ !local_imported_credit_card->IsComplete()) {
+ local_imported_credit_card.reset();
+ }
+
+ // Don't import if we already have this info.
+ // Don't present an infobar if we have already saved this card number.
+ bool merged_credit_card = false;
+ if (local_imported_credit_card.get()) {
+ for (std::vector<CreditCard*>::const_iterator iter = credit_cards_.begin();
+ iter != credit_cards_.end();
+ ++iter) {
+ if ((*iter)->UpdateFromImportedCard(*local_imported_credit_card.get(),
+ app_locale)) {
+ merged_credit_card = true;
+ UpdateCreditCard(**iter);
+ local_imported_credit_card.reset();
+ break;
+ }
+ }
+ }
+
+ if (imported_profile.get()) {
+ // We always save imported profiles.
+ SaveImportedProfile(*imported_profile);
+ }
+ *imported_credit_card = local_imported_credit_card.release();
+
+ if (imported_profile.get() || *imported_credit_card || merged_credit_card) {
+ return true;
+ } else {
+ FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_,
+ OnInsufficientFormData());
+ return false;
+ }
+}
+
+void PersonalDataManager::AddProfile(const AutofillProfile& profile) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ if (profile.IsEmpty())
+ return;
+
+ // Don't add an existing profile.
+ if (FindByGUID<AutofillProfile>(web_profiles_, profile.guid()))
+ return;
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ // Don't add a duplicate.
+ if (FindByContents(web_profiles_, profile))
+ return;
+
+ // Add the new profile to the web database.
+ autofill_data->AddAutofillProfile(profile);
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+void PersonalDataManager::UpdateProfile(const AutofillProfile& profile) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ if (!FindByGUID<AutofillProfile>(web_profiles_, profile.guid()))
+ return;
+
+ if (profile.IsEmpty()) {
+ RemoveByGUID(profile.guid());
+ return;
+ }
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ // Make the update.
+ autofill_data->UpdateAutofillProfile(profile);
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+AutofillProfile* PersonalDataManager::GetProfileByGUID(
+ const std::string& guid) {
+ const std::vector<AutofillProfile*>& profiles = GetProfiles();
+ for (std::vector<AutofillProfile*>::const_iterator iter = profiles.begin();
+ iter != profiles.end(); ++iter) {
+ if ((*iter)->guid() == guid)
+ return *iter;
+ }
+ return NULL;
+}
+
+void PersonalDataManager::AddCreditCard(const CreditCard& credit_card) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ if (credit_card.IsEmpty())
+ return;
+
+ if (FindByGUID<CreditCard>(credit_cards_, credit_card.guid()))
+ return;
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ // Don't add a duplicate.
+ if (FindByContents(credit_cards_, credit_card))
+ return;
+
+ // Add the new credit card to the web database.
+ autofill_data->AddCreditCard(credit_card);
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+void PersonalDataManager::UpdateCreditCard(const CreditCard& credit_card) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ if (!FindByGUID<CreditCard>(credit_cards_, credit_card.guid()))
+ return;
+
+ if (credit_card.IsEmpty()) {
+ RemoveByGUID(credit_card.guid());
+ return;
+ }
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ // Make the update.
+ autofill_data->UpdateCreditCard(credit_card);
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+void PersonalDataManager::RemoveByGUID(const std::string& guid) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ bool is_credit_card = FindByGUID<CreditCard>(credit_cards_, guid);
+ bool is_profile = !is_credit_card &&
+ FindByGUID<AutofillProfile>(web_profiles_, guid);
+ if (!is_credit_card && !is_profile)
+ return;
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ if (is_credit_card)
+ autofill_data->RemoveCreditCard(guid);
+ else
+ autofill_data->RemoveAutofillProfile(guid);
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+CreditCard* PersonalDataManager::GetCreditCardByGUID(const std::string& guid) {
+ for (std::vector<CreditCard*>::iterator iter = credit_cards_.begin();
+ iter != credit_cards_.end(); ++iter) {
+ if ((*iter)->guid() == guid)
+ return *iter;
+ }
+ return NULL;
+}
+
+void PersonalDataManager::GetNonEmptyTypes(
+ FieldTypeSet* non_empty_types) {
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ const std::vector<AutofillProfile*>& profiles = GetProfiles();
+ for (std::vector<AutofillProfile*>::const_iterator iter = profiles.begin();
+ iter != profiles.end(); ++iter) {
+ (*iter)->GetNonEmptyTypes(app_locale, non_empty_types);
+ }
+
+ for (ScopedVector<CreditCard>::const_iterator iter = credit_cards_.begin();
+ iter != credit_cards_.end(); ++iter) {
+ (*iter)->GetNonEmptyTypes(app_locale, non_empty_types);
+ }
+}
+
+bool PersonalDataManager::IsDataLoaded() const {
+ return is_data_loaded_;
+}
+
+const std::vector<AutofillProfile*>& PersonalDataManager::GetProfiles() {
+ if (!components::UserPrefs::Get(browser_context_)->GetBoolean(
+ prefs::kAutofillAuxiliaryProfilesEnabled)) {
+ return web_profiles();
+ }
+
+ profiles_.clear();
+
+ // Populates |auxiliary_profiles_|.
+ LoadAuxiliaryProfiles();
+
+ profiles_.insert(profiles_.end(), web_profiles_.begin(), web_profiles_.end());
+ profiles_.insert(profiles_.end(),
+ auxiliary_profiles_.begin(), auxiliary_profiles_.end());
+ return profiles_;
+}
+
+const std::vector<AutofillProfile*>& PersonalDataManager::web_profiles() const {
+ return web_profiles_.get();
+}
+
+const std::vector<CreditCard*>& PersonalDataManager::credit_cards() const {
+ return credit_cards_.get();
+}
+
+void PersonalDataManager::Refresh() {
+ LoadProfiles();
+ LoadCreditCards();
+}
+
+void PersonalDataManager::GetProfileSuggestions(
+ AutofillFieldType type,
+ const string16& field_contents,
+ bool field_is_autofilled,
+ std::vector<AutofillFieldType> other_field_types,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<GUIDPair>* guid_pairs) {
+ values->clear();
+ labels->clear();
+ icons->clear();
+ guid_pairs->clear();
+
+ const std::vector<AutofillProfile*>& profiles = GetProfiles();
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ std::vector<AutofillProfile*> matched_profiles;
+ for (std::vector<AutofillProfile*>::const_iterator iter = profiles.begin();
+ iter != profiles.end(); ++iter) {
+ AutofillProfile* profile = *iter;
+
+ // The value of the stored data for this field type in the |profile|.
+ std::vector<string16> multi_values;
+ profile->GetMultiInfo(type, app_locale, &multi_values);
+
+ for (size_t i = 0; i < multi_values.size(); ++i) {
+ if (!field_is_autofilled) {
+ // Suggest data that starts with what the user has typed.
+ if (!multi_values[i].empty() &&
+ StartsWith(multi_values[i], field_contents, false)) {
+ matched_profiles.push_back(profile);
+ values->push_back(multi_values[i]);
+ guid_pairs->push_back(GUIDPair(profile->guid(), i));
+ }
+ } else {
+ if (multi_values[i].empty())
+ continue;
+
+ string16 profile_value_lower_case(
+ StringToLowerASCII(multi_values[i]));
+ string16 field_value_lower_case(StringToLowerASCII(field_contents));
+ // Phone numbers could be split in US forms, so field value could be
+ // either prefix or suffix of the phone.
+ bool matched_phones = false;
+ if (type == PHONE_HOME_NUMBER && !field_value_lower_case.empty() &&
+ (profile_value_lower_case.find(field_value_lower_case) !=
+ string16::npos)) {
+ matched_phones = true;
+ }
+
+ // Suggest variants of the profile that's already been filled in.
+ if (matched_phones ||
+ profile_value_lower_case == field_value_lower_case) {
+ for (size_t j = 0; j < multi_values.size(); ++j) {
+ if (!multi_values[j].empty()) {
+ values->push_back(multi_values[j]);
+ guid_pairs->push_back(GUIDPair(profile->guid(), j));
+ }
+ }
+
+ // We've added all the values for this profile so move on to the
+ // next.
+ break;
+ }
+ }
+ }
+ }
+
+ if (!field_is_autofilled) {
+ AutofillProfile::CreateInferredLabels(
+ &matched_profiles, &other_field_types,
+ type, 1, labels);
+ } else {
+ // No sub-labels for previously filled fields.
+ labels->resize(values->size());
+ }
+
+ // No icons for profile suggestions.
+ icons->resize(values->size());
+}
+
+void PersonalDataManager::GetCreditCardSuggestions(
+ AutofillFieldType type,
+ const string16& field_contents,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<GUIDPair>* guid_pairs) {
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ for (std::vector<CreditCard*>::const_iterator iter = credit_cards().begin();
+ iter != credit_cards().end(); ++iter) {
+ CreditCard* credit_card = *iter;
+
+ // The value of the stored data for this field type in the |credit_card|.
+ string16 creditcard_field_value = credit_card->GetInfo(type, app_locale);
+ if (!creditcard_field_value.empty() &&
+ StartsWith(creditcard_field_value, field_contents, false)) {
+ if (type == CREDIT_CARD_NUMBER)
+ creditcard_field_value = credit_card->ObfuscatedNumber();
+
+ string16 label;
+ if (credit_card->number().empty()) {
+ // If there is no CC number, return name to show something.
+ label = credit_card->GetInfo(CREDIT_CARD_NAME, app_locale);
+ } else {
+ label = kCreditCardPrefix;
+ label.append(credit_card->LastFourDigits());
+ }
+
+ values->push_back(creditcard_field_value);
+ labels->push_back(label);
+ icons->push_back(UTF8ToUTF16(credit_card->type()));
+ guid_pairs->push_back(GUIDPair(credit_card->guid(), 0));
+ }
+ }
+}
+
+bool PersonalDataManager::IsAutofillEnabled() const {
+ return components::UserPrefs::Get(browser_context_)->GetBoolean(
+ prefs::kAutofillEnabled);
+}
+
+// static
+bool PersonalDataManager::IsValidLearnableProfile(
+ const AutofillProfile& profile) {
+ if (!IsMinimumAddress(profile))
+ return false;
+
+ string16 email = profile.GetRawInfo(EMAIL_ADDRESS);
+ if (!email.empty() && !autofill::IsValidEmailAddress(email))
+ return false;
+
+ // Reject profiles with invalid US state information.
+ string16 state = profile.GetRawInfo(ADDRESS_HOME_STATE);
+ if (profile.CountryCode() == "US" &&
+ !state.empty() && !FormGroup::IsValidState(state)) {
+ return false;
+ }
+
+ // Reject profiles with invalid US zip information.
+ string16 zip = profile.GetRawInfo(ADDRESS_HOME_ZIP);
+ if (profile.CountryCode() == "US" && !zip.empty() &&
+ !autofill::IsValidZip(zip))
+ return false;
+
+ return true;
+}
+
+// static
+bool PersonalDataManager::MergeProfile(
+ const AutofillProfile& profile,
+ const std::vector<AutofillProfile*>& existing_profiles,
+ std::vector<AutofillProfile>* merged_profiles) {
+ merged_profiles->clear();
+
+ // Set to true if |profile| is merged into |existing_profiles|.
+ bool merged = false;
+
+ // If we have already saved this address, merge in any missing values.
+ // Only merge with the first match.
+ for (std::vector<AutofillProfile*>::const_iterator iter =
+ existing_profiles.begin();
+ iter != existing_profiles.end(); ++iter) {
+ if (!merged) {
+ if (!profile.PrimaryValue().empty() &&
+ StringToLowerASCII((*iter)->PrimaryValue()) ==
+ StringToLowerASCII(profile.PrimaryValue())) {
+ merged = true;
+ (*iter)->OverwriteWithOrAddTo(profile);
+ }
+ }
+ merged_profiles->push_back(**iter);
+ }
+
+ // If the new profile was not merged with an existing one, add it to the list.
+ if (!merged)
+ merged_profiles->push_back(profile);
+
+ return merged;
+}
+
+void PersonalDataManager::SetProfiles(std::vector<AutofillProfile>* profiles) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ // Remove empty profiles from input.
+ profiles->erase(
+ std::remove_if(profiles->begin(), profiles->end(),
+ std::mem_fun_ref(&AutofillProfile::IsEmpty)),
+ profiles->end());
+
+ // Ensure that profile labels are up to date. Currently, sync relies on
+ // labels to identify a profile.
+ // TODO(dhollowa): We need to deprecate labels and update the way sync
+ // identifies profiles.
+ std::vector<AutofillProfile*> profile_pointers(profiles->size());
+ std::transform(profiles->begin(), profiles->end(), profile_pointers.begin(),
+ address_of<AutofillProfile>);
+ AutofillProfile::AdjustInferredLabels(&profile_pointers);
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ // Any profiles that are not in the new profile list should be removed from
+ // the web database.
+ for (std::vector<AutofillProfile*>::const_iterator iter =
+ web_profiles_.begin();
+ iter != web_profiles_.end(); ++iter) {
+ if (!FindByGUID<AutofillProfile>(*profiles, (*iter)->guid()))
+ autofill_data->RemoveAutofillProfile((*iter)->guid());
+ }
+
+ // Update the web database with the existing profiles.
+ for (std::vector<AutofillProfile>::iterator iter = profiles->begin();
+ iter != profiles->end(); ++iter) {
+ if (FindByGUID<AutofillProfile>(web_profiles_, iter->guid()))
+ autofill_data->UpdateAutofillProfile(*iter);
+ }
+
+ // Add the new profiles to the web database. Don't add a duplicate.
+ for (std::vector<AutofillProfile>::iterator iter = profiles->begin();
+ iter != profiles->end(); ++iter) {
+ if (!FindByGUID<AutofillProfile>(web_profiles_, iter->guid()) &&
+ !FindByContents(web_profiles_, *iter))
+ autofill_data->AddAutofillProfile(*iter);
+ }
+
+ // Copy in the new profiles.
+ web_profiles_.clear();
+ for (std::vector<AutofillProfile>::iterator iter = profiles->begin();
+ iter != profiles->end(); ++iter) {
+ web_profiles_.push_back(new AutofillProfile(*iter));
+ }
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+void PersonalDataManager::SetCreditCards(
+ std::vector<CreditCard>* credit_cards) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ // Remove empty credit cards from input.
+ credit_cards->erase(
+ std::remove_if(
+ credit_cards->begin(), credit_cards->end(),
+ std::mem_fun_ref(&CreditCard::IsEmpty)),
+ credit_cards->end());
+
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get())
+ return;
+
+ // Any credit cards that are not in the new credit card list should be
+ // removed.
+ for (std::vector<CreditCard*>::const_iterator iter = credit_cards_.begin();
+ iter != credit_cards_.end(); ++iter) {
+ if (!FindByGUID<CreditCard>(*credit_cards, (*iter)->guid()))
+ autofill_data->RemoveCreditCard((*iter)->guid());
+ }
+
+ // Update the web database with the existing credit cards.
+ for (std::vector<CreditCard>::iterator iter = credit_cards->begin();
+ iter != credit_cards->end(); ++iter) {
+ if (FindByGUID<CreditCard>(credit_cards_, iter->guid()))
+ autofill_data->UpdateCreditCard(*iter);
+ }
+
+ // Add the new credit cards to the web database. Don't add a duplicate.
+ for (std::vector<CreditCard>::iterator iter = credit_cards->begin();
+ iter != credit_cards->end(); ++iter) {
+ if (!FindByGUID<CreditCard>(credit_cards_, iter->guid()) &&
+ !FindByContents(credit_cards_, *iter))
+ autofill_data->AddCreditCard(*iter);
+ }
+
+ // Copy in the new credit cards.
+ credit_cards_.clear();
+ for (std::vector<CreditCard>::iterator iter = credit_cards->begin();
+ iter != credit_cards->end(); ++iter) {
+ credit_cards_.push_back(new CreditCard(*iter));
+ }
+
+ // Refresh our local cache and send notifications to observers.
+ Refresh();
+}
+
+void PersonalDataManager::LoadProfiles() {
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get()) {
+ NOTREACHED();
+ return;
+ }
+
+ CancelPendingQuery(&pending_profiles_query_);
+
+ pending_profiles_query_ = autofill_data->GetAutofillProfiles(this);
+}
+
+// Win and Linux implementations do nothing. Mac implementation fills in the
+// contents of |auxiliary_profiles_|.
+#if !defined(OS_MACOSX)
+void PersonalDataManager::LoadAuxiliaryProfiles() {
+}
+#endif
+
+void PersonalDataManager::LoadCreditCards() {
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get()) {
+ NOTREACHED();
+ return;
+ }
+
+ CancelPendingQuery(&pending_creditcards_query_);
+
+ pending_creditcards_query_ = autofill_data->GetCreditCards(this);
+}
+
+void PersonalDataManager::ReceiveLoadedProfiles(WebDataServiceBase::Handle h,
+ const WDTypedResult* result) {
+ DCHECK_EQ(pending_profiles_query_, h);
+
+ pending_profiles_query_ = 0;
+ web_profiles_.clear();
+
+ const WDResult<std::vector<AutofillProfile*> >* r =
+ static_cast<const WDResult<std::vector<AutofillProfile*> >*>(result);
+
+ std::vector<AutofillProfile*> profiles = r->GetValue();
+ for (std::vector<AutofillProfile*>::iterator iter = profiles.begin();
+ iter != profiles.end(); ++iter) {
+ web_profiles_.push_back(*iter);
+ }
+
+ LogProfileCount();
+}
+
+void PersonalDataManager::ReceiveLoadedCreditCards(
+ WebDataServiceBase::Handle h, const WDTypedResult* result) {
+ DCHECK_EQ(pending_creditcards_query_, h);
+
+ pending_creditcards_query_ = 0;
+ credit_cards_.clear();
+
+ const WDResult<std::vector<CreditCard*> >* r =
+ static_cast<const WDResult<std::vector<CreditCard*> >*>(result);
+
+ std::vector<CreditCard*> credit_cards = r->GetValue();
+ for (std::vector<CreditCard*>::iterator iter = credit_cards.begin();
+ iter != credit_cards.end(); ++iter) {
+ credit_cards_.push_back(*iter);
+ }
+}
+
+void PersonalDataManager::CancelPendingQuery(
+ WebDataServiceBase::Handle* handle) {
+ if (*handle) {
+ scoped_ptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (!autofill_data.get()) {
+ NOTREACHED();
+ return;
+ }
+ autofill_data->CancelRequest(*handle);
+ }
+ *handle = 0;
+}
+
+void PersonalDataManager::SaveImportedProfile(
+ const AutofillProfile& imported_profile) {
+ if (browser_context_->IsOffTheRecord()) {
+ // The |IsOffTheRecord| check should happen earlier in the import process,
+ // upon form submission.
+ NOTREACHED();
+ return;
+ }
+
+ // Don't save a web profile if the data in the profile is a subset of an
+ // auxiliary profile.
+ for (std::vector<AutofillProfile*>::const_iterator iter =
+ auxiliary_profiles_.begin();
+ iter != auxiliary_profiles_.end(); ++iter) {
+ if (imported_profile.IsSubsetOf(**iter))
+ return;
+ }
+
+ std::vector<AutofillProfile> profiles;
+ MergeProfile(imported_profile, web_profiles_.get(), &profiles);
+ SetProfiles(&profiles);
+}
+
+
+void PersonalDataManager::SaveImportedCreditCard(
+ const CreditCard& imported_card) {
+ DCHECK(!imported_card.number().empty());
+ if (browser_context_->IsOffTheRecord()) {
+ // The |IsOffTheRecord| check should happen earlier in the import process,
+ // upon form submission.
+ NOTREACHED();
+ return;
+ }
+
+ // Set to true if |imported_card| is merged into the credit card list.
+ bool merged = false;
+
+ const std::string app_locale = AutofillCountry::ApplicationLocale();
+ std::vector<CreditCard> credit_cards;
+ for (std::vector<CreditCard*>::const_iterator card = credit_cards_.begin();
+ card != credit_cards_.end();
+ ++card) {
+ // If |imported_card| has not yet been merged, check whether it should be
+ // with the current |card|.
+ if (!merged && (*card)->UpdateFromImportedCard(imported_card, app_locale))
+ merged = true;
+
+ credit_cards.push_back(**card);
+ }
+
+ if (!merged)
+ credit_cards.push_back(imported_card);
+
+ SetCreditCards(&credit_cards);
+}
+
+void PersonalDataManager::LogProfileCount() const {
+ if (!has_logged_profile_count_) {
+ metric_logger_->LogStoredProfileCount(web_profiles_.size());
+ has_logged_profile_count_ = true;
+ }
+}
+
+const AutofillMetrics* PersonalDataManager::metric_logger() const {
+ return metric_logger_.get();
+}
+
+void PersonalDataManager::set_metric_logger(
+ const AutofillMetrics* metric_logger) {
+ metric_logger_.reset(metric_logger);
+}
+
+void PersonalDataManager::set_browser_context(
+ content::BrowserContext* context) {
+ browser_context_ = context;
+}
diff --git a/components/autofill/browser/personal_data_manager.h b/components/autofill/browser/personal_data_manager.h
new file mode 100644
index 0000000..4a1292a
--- /dev/null
+++ b/components/autofill/browser/personal_data_manager.h
@@ -0,0 +1,282 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_PERSONAL_DATA_MANAGER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PERSONAL_DATA_MANAGER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/observer_list.h"
+#include "base/string16.h"
+#include "chrome/browser/api/webdata/web_data_service_consumer.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/credit_card.h"
+#include "components/autofill/browser/field_types.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+class AutofillMetrics;
+class FormStructure;
+class PersonalDataManagerObserver;
+
+namespace autofill_helper {
+void SetProfiles(int, std::vector<AutofillProfile>*);
+void SetCreditCards(int, std::vector<CreditCard>*);
+}
+
+namespace content {
+class BrowserContext;
+}
+
+// Handles loading and saving Autofill profile information to the web database.
+// This class also stores the profiles loaded from the database for use during
+// Autofill.
+class PersonalDataManager : public WebDataServiceConsumer,
+ public content::NotificationObserver {
+ public:
+ // A pair of GUID and variant index. Represents a single FormGroup and a
+ // specific data variant.
+ typedef std::pair<std::string, size_t> GUIDPair;
+
+ PersonalDataManager();
+ virtual ~PersonalDataManager();
+
+ // Kicks off asynchronous loading of profiles and credit cards.
+ void Init(content::BrowserContext* context);
+
+ // WebDataServiceConsumer:
+ virtual void OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle h,
+ const WDTypedResult* result) OVERRIDE;
+
+ // Adds a listener to be notified of PersonalDataManager events.
+ virtual void AddObserver(PersonalDataManagerObserver* observer);
+
+ // Removes |observer| as an observer of this PersonalDataManager.
+ virtual void RemoveObserver(PersonalDataManagerObserver* observer);
+
+ // content::NotificationObserver:
+ // Observes "batch" changes made by Sync and refreshes data from the
+ // WebDataServiceBase in response.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // Scans the given |form| for importable Autofill data. If the form includes
+ // sufficient address data, it is immediately imported. If the form includes
+ // sufficient credit card data, it is stored into |credit_card|, so that we
+ // can prompt the user whether to save this data.
+ // Returns |true| if sufficient address or credit card data was found.
+ bool ImportFormData(const FormStructure& form,
+ const CreditCard** credit_card);
+
+ // Saves |imported_profile| to the WebDB if it exists.
+ virtual void SaveImportedProfile(const AutofillProfile& imported_profile);
+
+ // Saves a credit card value detected in |ImportedFormData|.
+ virtual void SaveImportedCreditCard(const CreditCard& imported_credit_card);
+
+ // Adds |profile| to the web database.
+ void AddProfile(const AutofillProfile& profile);
+
+ // Updates |profile| which already exists in the web database.
+ void UpdateProfile(const AutofillProfile& profile);
+
+ // Removes the profile or credit card represented by |guid|.
+ virtual void RemoveByGUID(const std::string& guid);
+
+ // Returns the profile with the specified |guid|, or NULL if there is no
+ // profile with the specified |guid|. Both web and auxiliary profiles may
+ // be returned.
+ AutofillProfile* GetProfileByGUID(const std::string& guid);
+
+ // Adds |credit_card| to the web database.
+ void AddCreditCard(const CreditCard& credit_card);
+
+ // Updates |credit_card| which already exists in the web database.
+ void UpdateCreditCard(const CreditCard& credit_card);
+
+ // Returns the credit card with the specified |guid|, or NULL if there is
+ // no credit card with the specified |guid|.
+ CreditCard* GetCreditCardByGUID(const std::string& guid);
+
+ // Gets the field types availabe in the stored address and credit card data.
+ void GetNonEmptyTypes(FieldTypeSet* non_empty_types);
+
+ // Returns true if the credit card information is stored with a password.
+ bool HasPassword();
+
+ // Returns whether the personal data has been loaded from the web database.
+ virtual bool IsDataLoaded() const;
+
+ // This PersonalDataManager owns these profiles and credit cards. Their
+ // lifetime is until the web database is updated with new profile and credit
+ // card information, respectively. |profiles()| returns both web and
+ // auxiliary profiles. |web_profiles()| returns only web profiles.
+ virtual const std::vector<AutofillProfile*>& GetProfiles();
+ virtual const std::vector<AutofillProfile*>& web_profiles() const;
+ virtual const std::vector<CreditCard*>& credit_cards() const;
+
+ // Loads profiles that can suggest data for |type|. |field_contents| is the
+ // part the user has already typed. |field_is_autofilled| is true if the field
+ // has already been autofilled. |other_field_types| represents the rest of
+ // form. Identifying info is loaded into the last four outparams.
+ void GetProfileSuggestions(
+ AutofillFieldType type,
+ const string16& field_contents,
+ bool field_is_autofilled,
+ std::vector<AutofillFieldType> other_field_types,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<GUIDPair>* guid_pairs);
+
+ // Gets credit cards that can suggest data for |type|. See
+ // GetProfileSuggestions for argument descriptions. The variant in each
+ // GUID pair should be ignored.
+ void GetCreditCardSuggestions(
+ AutofillFieldType type,
+ const string16& field_contents,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<GUIDPair>* guid_pairs);
+
+ // Re-loads profiles and credit cards from the WebDatabase asynchronously.
+ // In the general case, this is a no-op and will re-create the same
+ // in-memory model as existed prior to the call. If any change occurred to
+ // profiles in the WebDatabase directly, as is the case if the browser sync
+ // engine processed a change from the cloud, we will learn of these as a
+ // result of this call.
+ //
+ // Also see SetProfile for more details.
+ virtual void Refresh();
+
+ // Checks suitability of |profile| for adding to the user's set of profiles.
+ static bool IsValidLearnableProfile(const AutofillProfile& profile);
+
+ // Merges |profile| into one of the |existing_profiles| if possible; otherwise
+ // appends |profile| to the end of that list. Fills |merged_profiles| with the
+ // result.
+ static bool MergeProfile(
+ const AutofillProfile& profile,
+ const std::vector<AutofillProfile*>& existing_profiles,
+ std::vector<AutofillProfile>* merged_profiles);
+
+ protected:
+ // Only PersonalDataManagerFactory and certain tests can create instances of
+ // PersonalDataManager.
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, FirstMiddleLast);
+ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtStartup);
+ FRIEND_TEST_ALL_PREFIXES(PersonalDataManagerTest,
+ AggregateExistingAuxiliaryProfile);
+ friend class AutofillTest;
+ friend class PersonalDataManagerFactory;
+ friend class PersonalDataManagerTest;
+ friend class ProfileSyncServiceAutofillTest;
+ friend class RemoveAutofillTester;
+ friend class TestingAutomationProvider;
+ friend struct base::DefaultDeleter<PersonalDataManager>;
+ friend void autofill_helper::SetProfiles(int, std::vector<AutofillProfile>*);
+ friend void autofill_helper::SetCreditCards(int, std::vector<CreditCard>*);
+
+ // Sets |web_profiles_| to the contents of |profiles| and updates the web
+ // database by adding, updating and removing profiles.
+ // The relationship between this and Refresh is subtle.
+ // A call to |SetProfiles| could include out-of-date data that may conflict
+ // if we didn't refresh-to-latest before an Autofill window was opened for
+ // editing. |SetProfiles| is implemented to make a "best effort" to apply the
+ // changes, but in extremely rare edge cases it is possible not all of the
+ // updates in |profiles| make it to the DB. This is why SetProfiles will
+ // invoke Refresh after finishing, to ensure we get into a
+ // consistent state. See Refresh for details.
+ void SetProfiles(std::vector<AutofillProfile>* profiles);
+
+ // Sets |credit_cards_| to the contents of |credit_cards| and updates the web
+ // database by adding, updating and removing credit cards.
+ void SetCreditCards(std::vector<CreditCard>* credit_cards);
+
+ // Loads the saved profiles from the web database.
+ virtual void LoadProfiles();
+
+ // Loads the auxiliary profiles. Currently Mac only.
+ virtual void LoadAuxiliaryProfiles();
+
+ // Loads the saved credit cards from the web database.
+ virtual void LoadCreditCards();
+
+ // Receives the loaded profiles from the web data service and stores them in
+ // |credit_cards_|.
+ void ReceiveLoadedProfiles(WebDataServiceBase::Handle h,
+ const WDTypedResult* result);
+
+ // Receives the loaded credit cards from the web data service and stores them
+ // in |credit_cards_|.
+ void ReceiveLoadedCreditCards(WebDataServiceBase::Handle h,
+ const WDTypedResult* result);
+
+ // Cancels a pending query to the web database. |handle| is a pointer to the
+ // query handle.
+ void CancelPendingQuery(WebDataServiceBase::Handle* handle);
+
+ // The first time this is called, logs an UMA metrics for the number of
+ // profiles the user has. On subsequent calls, does nothing.
+ void LogProfileCount() const;
+
+ // Returns the value of the AutofillEnabled pref.
+ virtual bool IsAutofillEnabled() const;
+
+ // For tests.
+ const AutofillMetrics* metric_logger() const;
+ void set_metric_logger(const AutofillMetrics* metric_logger);
+ void set_browser_context(content::BrowserContext* context);
+
+ // The browser context this PersonalDataManager is in.
+ content::BrowserContext* browser_context_;
+
+ // True if personal data has been loaded from the web database.
+ bool is_data_loaded_;
+
+ // The loaded web profiles.
+ ScopedVector<AutofillProfile> web_profiles_;
+
+ // Auxiliary profiles.
+ mutable ScopedVector<AutofillProfile> auxiliary_profiles_;
+
+ // Storage for combined web and auxiliary profiles. Contents are weak
+ // references. Lifetime managed by |web_profiles_| and |auxiliary_profiles_|.
+ mutable std::vector<AutofillProfile*> profiles_;
+
+ // The loaded credit cards.
+ ScopedVector<CreditCard> credit_cards_;
+
+ // When the manager makes a request from WebDataServiceBase, the database
+ // is queried on another thread, we record the query handle until we
+ // get called back. We store handles for both profile and credit card queries
+ // so they can be loaded at the same time.
+ WebDataServiceBase::Handle pending_profiles_query_;
+ WebDataServiceBase::Handle pending_creditcards_query_;
+
+ // The observers.
+ ObserverList<PersonalDataManagerObserver> observers_;
+
+ private:
+
+ // For logging UMA metrics. Overridden by metrics tests.
+ scoped_ptr<const AutofillMetrics> metric_logger_;
+
+ // Whether we have already logged the number of profiles this session.
+ mutable bool has_logged_profile_count_;
+
+ // Manages registration lifetime for NotificationObserver implementation.
+ content::NotificationRegistrar notification_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(PersonalDataManager);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PERSONAL_DATA_MANAGER_H_
diff --git a/components/autofill/browser/personal_data_manager_mac.mm b/components/autofill/browser/personal_data_manager_mac.mm
new file mode 100644
index 0000000..dbab8ed
--- /dev/null
+++ b/components/autofill/browser/personal_data_manager_mac.mm
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/personal_data_manager.h"
+
+#include <math.h>
+
+#import <AddressBook/AddressBook.h>
+
+#include "base/format_macros.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#import "base/mac/scoped_nsexception_enabler.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stringprintf.h"
+#include "base/sys_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/phone_number.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+namespace {
+
+// This implementation makes use of the Address Book API. Profiles are
+// generated that correspond to addresses in the "me" card that reside in the
+// user's Address Book. The caller passes a vector of profiles into the
+// the constructer and then initiate the fetch from the Mac Address Book "me"
+// card using the main |GetAddressBookMeCard()| method. This clears any
+// existing addresses and populates new addresses derived from the data found
+// in the "me" card.
+class AuxiliaryProfilesImpl {
+ public:
+ // Constructor takes a reference to the |profiles| that will be filled in
+ // by the subsequent call to |GetAddressBookMeCard()|. |profiles| may not
+ // be NULL.
+ explicit AuxiliaryProfilesImpl(ScopedVector<AutofillProfile>* profiles)
+ : profiles_(*profiles) {
+ }
+ virtual ~AuxiliaryProfilesImpl() {}
+
+ // Import the "me" card from the Mac Address Book and fill in |profiles_|.
+ void GetAddressBookMeCard();
+
+ private:
+ void GetAddressBookNames(ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile);
+ void GetAddressBookAddress(NSDictionary* address, AutofillProfile* profile);
+ void GetAddressBookEmail(ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile);
+ void GetAddressBookPhoneNumbers(ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile);
+
+ private:
+ // A reference to the profiles this class populates.
+ ScopedVector<AutofillProfile>& profiles_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesImpl);
+};
+
+// This method uses the |ABAddressBook| system service to fetch the "me" card
+// from the active user's address book. It looks for the user address
+// information and translates it to the internal list of |AutofillProfile| data
+// structures.
+void AuxiliaryProfilesImpl::GetAddressBookMeCard() {
+ profiles_.clear();
+
+ // +[ABAddressBook sharedAddressBook] throws an exception internally in
+ // circumstances that aren't clear. The exceptions are only observed in crash
+ // reports, so it is unknown whether they would be caught by AppKit and nil
+ // returned, or if they would take down the app. In either case, avoid
+ // crashing. http://crbug.com/129022
+ ABAddressBook* addressBook = base::mac::RunBlockIgnoringExceptions(^{
+ return [ABAddressBook sharedAddressBook];
+ });
+ ABPerson* me = [addressBook me];
+ if (!me)
+ return;
+
+ ABMultiValue* addresses = [me valueForProperty:kABAddressProperty];
+
+ // The number of characters at the end of the GUID to reserve for
+ // distinguishing addresses within the "me" card. Cap the number of addresses
+ // we will fetch to the number that can be distinguished by this fragment of
+ // the GUID.
+ const size_t kNumAddressGUIDChars = 2;
+ const size_t kNumHexDigits = 16;
+ const size_t kMaxAddressCount = pow(kNumHexDigits, kNumAddressGUIDChars);
+ NSUInteger count = MIN([addresses count], kMaxAddressCount);
+ for (NSUInteger i = 0; i < count; i++) {
+ NSDictionary* address = [addresses valueAtIndex:i];
+ NSString* addressLabelRaw = [addresses labelAtIndex:i];
+
+ // Create a new profile where the guid is set to the guid portion of the
+ // |kABUIDProperty| taken from from the "me" address. The format of
+ // the |kABUIDProperty| is "<guid>:ABPerson", so we're stripping off the
+ // raw guid here and using it directly, with one modification: we update the
+ // last |kNumAddressGUIDChars| characters in the GUID to reflect the address
+ // variant. Note that we capped the number of addresses above, so this is
+ // safe.
+ const size_t kGUIDLength = 36U;
+ const size_t kTrimmedGUIDLength = kGUIDLength - kNumAddressGUIDChars;
+ std::string guid = base::SysNSStringToUTF8(
+ [me valueForProperty:kABUIDProperty]).substr(0, kTrimmedGUIDLength);
+
+ // The format string to print |kNumAddressGUIDChars| hexadecimal characters,
+ // left-padded with 0's.
+ const std::string kAddressGUIDFormat =
+ base::StringPrintf("%%0%" PRIuS "X", kNumAddressGUIDChars);
+ guid += base::StringPrintf(kAddressGUIDFormat.c_str(), i);
+ DCHECK_EQ(kGUIDLength, guid.size());
+
+ scoped_ptr<AutofillProfile> profile(new AutofillProfile(guid));
+ DCHECK(base::IsValidGUID(profile->guid()));
+
+ // Fill in name and company information.
+ GetAddressBookNames(me, addressLabelRaw, profile.get());
+
+ // Fill in address information.
+ GetAddressBookAddress(address, profile.get());
+
+ // Fill in email information.
+ GetAddressBookEmail(me, addressLabelRaw, profile.get());
+
+ // Fill in phone number information.
+ GetAddressBookPhoneNumbers(me, addressLabelRaw, profile.get());
+
+ profiles_.push_back(profile.release());
+ }
+}
+
+// Name and company information is stored once in the Address Book against
+// multiple addresses. We replicate that information for each profile.
+// We only propagate the company name to work profiles.
+void AuxiliaryProfilesImpl::GetAddressBookNames(
+ ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile) {
+ NSString* firstName = [me valueForProperty:kABFirstNameProperty];
+ NSString* middleName = [me valueForProperty:kABMiddleNameProperty];
+ NSString* lastName = [me valueForProperty:kABLastNameProperty];
+ NSString* companyName = [me valueForProperty:kABOrganizationProperty];
+
+ profile->SetRawInfo(NAME_FIRST, base::SysNSStringToUTF16(firstName));
+ profile->SetRawInfo(NAME_MIDDLE, base::SysNSStringToUTF16(middleName));
+ profile->SetRawInfo(NAME_LAST, base::SysNSStringToUTF16(lastName));
+ if ([addressLabelRaw isEqualToString:kABAddressWorkLabel])
+ profile->SetRawInfo(COMPANY_NAME, base::SysNSStringToUTF16(companyName));
+}
+
+// Addresss information from the Address Book may span multiple lines.
+// If it does then we represent the address with two lines in the profile. The
+// second line we join with commas.
+// For example: "c/o John Doe\n1122 Other Avenue\nApt #7" translates to
+// line 1: "c/o John Doe", line 2: "1122 Other Avenue, Apt #7".
+void AuxiliaryProfilesImpl::GetAddressBookAddress(NSDictionary* address,
+ AutofillProfile* profile) {
+ if (NSString* addressField = [address objectForKey:kABAddressStreetKey]) {
+ // If there are newlines in the address, split into two lines.
+ if ([addressField rangeOfCharacterFromSet:
+ [NSCharacterSet newlineCharacterSet]].location != NSNotFound) {
+ NSArray* chunks = [addressField componentsSeparatedByCharactersInSet:
+ [NSCharacterSet newlineCharacterSet]];
+ DCHECK([chunks count] > 1);
+
+ NSString* separator = l10n_util::GetNSString(
+ IDS_AUTOFILL_MAC_ADDRESS_LINE_SEPARATOR);
+
+ NSString* addressField1 = [chunks objectAtIndex:0];
+ NSString* addressField2 =
+ [[chunks subarrayWithRange:NSMakeRange(1, [chunks count] - 1)]
+ componentsJoinedByString:separator];
+ profile->SetRawInfo(ADDRESS_HOME_LINE1,
+ base::SysNSStringToUTF16(addressField1));
+ profile->SetRawInfo(ADDRESS_HOME_LINE2,
+ base::SysNSStringToUTF16(addressField2));
+ } else {
+ profile->SetRawInfo(ADDRESS_HOME_LINE1,
+ base::SysNSStringToUTF16(addressField));
+ }
+ }
+
+ if (NSString* city = [address objectForKey:kABAddressCityKey])
+ profile->SetRawInfo(ADDRESS_HOME_CITY, base::SysNSStringToUTF16(city));
+
+ if (NSString* state = [address objectForKey:kABAddressStateKey])
+ profile->SetRawInfo(ADDRESS_HOME_STATE, base::SysNSStringToUTF16(state));
+
+ if (NSString* zip = [address objectForKey:kABAddressZIPKey])
+ profile->SetRawInfo(ADDRESS_HOME_ZIP, base::SysNSStringToUTF16(zip));
+
+ if (NSString* country = [address objectForKey:kABAddressCountryKey]) {
+ profile->SetInfo(ADDRESS_HOME_COUNTRY,
+ base::SysNSStringToUTF16(country),
+ AutofillCountry::ApplicationLocale());
+ }
+}
+
+// Fills in email address matching current address label. Note that there may
+// be multiple matching email addresses for a given label. We take the
+// first we find (topmost) as preferred.
+void AuxiliaryProfilesImpl::GetAddressBookEmail(
+ ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile) {
+ ABMultiValue* emailAddresses = [me valueForProperty:kABEmailProperty];
+ NSString* emailAddress = nil;
+ for (NSUInteger j = 0, emailCount = [emailAddresses count];
+ j < emailCount; j++) {
+ NSString* emailAddressLabelRaw = [emailAddresses labelAtIndex:j];
+ if ([emailAddressLabelRaw isEqualToString:addressLabelRaw]) {
+ emailAddress = [emailAddresses valueAtIndex:j];
+ break;
+ }
+ }
+ profile->SetRawInfo(EMAIL_ADDRESS, base::SysNSStringToUTF16(emailAddress));
+}
+
+// Fills in telephone numbers. Each of these are special cases.
+// We match two cases: home/tel, work/tel.
+// Note, we traverse in reverse order so that top values in address book
+// take priority.
+void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers(
+ ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile) {
+ ABMultiValue* phoneNumbers = [me valueForProperty:kABPhoneProperty];
+ for (NSUInteger k = 0, phoneCount = [phoneNumbers count];
+ k < phoneCount; k++) {
+ NSUInteger reverseK = phoneCount - k - 1;
+ NSString* phoneLabelRaw = [phoneNumbers labelAtIndex:reverseK];
+ if ([addressLabelRaw isEqualToString:kABAddressHomeLabel] &&
+ [phoneLabelRaw isEqualToString:kABPhoneHomeLabel]) {
+ string16 homePhone = base::SysNSStringToUTF16(
+ [phoneNumbers valueAtIndex:reverseK]);
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, homePhone);
+ } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] &&
+ [phoneLabelRaw isEqualToString:kABPhoneWorkLabel]) {
+ string16 workPhone = base::SysNSStringToUTF16(
+ [phoneNumbers valueAtIndex:reverseK]);
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, workPhone);
+ } else if ([phoneLabelRaw isEqualToString:kABPhoneMobileLabel] ||
+ [phoneLabelRaw isEqualToString:kABPhoneMainLabel]) {
+ string16 phone = base::SysNSStringToUTF16(
+ [phoneNumbers valueAtIndex:reverseK]);
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, phone);
+ }
+ }
+}
+
+} // namespace
+
+// Populate |auxiliary_profiles_| with the Address Book data.
+void PersonalDataManager::LoadAuxiliaryProfiles() {
+ AuxiliaryProfilesImpl impl(&auxiliary_profiles_);
+ impl.GetAddressBookMeCard();
+}
diff --git a/components/autofill/browser/personal_data_manager_observer.h b/components/autofill/browser/personal_data_manager_observer.h
new file mode 100644
index 0000000..f2becb6
--- /dev/null
+++ b/components/autofill/browser/personal_data_manager_observer.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 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_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
+
+// An interface the PersonalDataManager uses to notify its clients (observers)
+// when it has finished loading personal data from the web database. Register
+// observers via PersonalDataManager::AddObserver.
+class PersonalDataManagerObserver {
+ public:
+ // Notifies the observer that the PersonalDataManager changed in some way.
+ virtual void OnPersonalDataChanged() = 0;
+ virtual void OnInsufficientFormData() {}
+
+ protected:
+ virtual ~PersonalDataManagerObserver() {}
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
diff --git a/components/autofill/browser/personal_data_manager_unittest.cc b/components/autofill/browser/personal_data_manager_unittest.cc
new file mode 100644
index 0000000..db0146b
--- /dev/null
+++ b/components/autofill/browser/personal_data_manager_unittest.cc
@@ -0,0 +1,1975 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/password_manager/encryptor.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/browser/webdata/web_data_service_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/autofill_common_test.h"
+#include "components/autofill/browser/autofill_metrics.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/form_structure.h"
+#include "components/autofill/browser/personal_data_manager.h"
+#include "components/autofill/browser/personal_data_manager_observer.h"
+#include "components/autofill/common/form_data.h"
+#include "content/public/browser/notification_details.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_source.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/test/mock_notification_observer.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+namespace {
+
+ACTION(QuitUIMessageLoop) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ MessageLoop::current()->Quit();
+}
+
+class PersonalDataLoadedObserverMock : public PersonalDataManagerObserver {
+ public:
+ PersonalDataLoadedObserverMock() {}
+ virtual ~PersonalDataLoadedObserverMock() {}
+
+ MOCK_METHOD0(OnPersonalDataChanged, void());
+};
+
+// Unlike the base AutofillMetrics, exposes copy and assignment constructors,
+// which are handy for briefer test code. The AutofillMetrics class is
+// stateless, so this is safe.
+class TestAutofillMetrics : public AutofillMetrics {
+ public:
+ TestAutofillMetrics() {}
+ virtual ~TestAutofillMetrics() {}
+};
+
+} // anonymous namespace
+
+class PersonalDataManagerTest : public testing::Test {
+ protected:
+ PersonalDataManagerTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB) {
+ }
+
+ virtual void SetUp() {
+ db_thread_.Start();
+
+ profile_.reset(new TestingProfile);
+ profile_->CreateWebDatabaseService();
+ profile_->CreateWebDataService();
+
+ autofill_test::DisableSystemServices(profile_.get());
+ ResetPersonalDataManager();
+ }
+
+ virtual void TearDown() {
+ // Destruction order is imposed explicitly here.
+ personal_data_.reset(NULL);
+ profile_.reset(NULL);
+
+ db_thread_.Stop();
+ MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
+ MessageLoop::current()->Run();
+ }
+
+ void ResetPersonalDataManager() {
+ personal_data_.reset(new PersonalDataManager);
+ personal_data_->Init(profile_.get());
+ personal_data_->AddObserver(&personal_data_observer_);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+ }
+
+ MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ scoped_ptr<TestingProfile> profile_;
+ scoped_ptr<PersonalDataManager> personal_data_;
+ content::NotificationRegistrar registrar_;
+ content::MockNotificationObserver observer_;
+ PersonalDataLoadedObserverMock personal_data_observer_;
+};
+
+TEST_F(PersonalDataManagerTest, AddProfile) {
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "John", "Mitchell", "Smith",
+ "j@s.com", "Acme Inc.", "1 Main", "Apt A", "San Francisco", "CA",
+ "94102", "US", "4158889999");
+
+ // Add profile0 to the database.
+ personal_data_->AddProfile(profile0);
+
+ // Reload the database.
+ ResetPersonalDataManager();
+
+ // Verify the addition.
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, profile0.Compare(*results1[0]));
+
+ // Add profile with identical values. Duplicates should not get saved.
+ AutofillProfile profile0a = profile0;
+ profile0a.set_guid(base::GenerateGUID());
+ personal_data_->AddProfile(profile0a);
+
+ // Reload the database.
+ ResetPersonalDataManager();
+
+ // Verify the non-addition.
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, profile0.Compare(*results2[0]));
+
+ // New profile with different email.
+ AutofillProfile profile1 = profile0;
+ profile1.set_guid(base::GenerateGUID());
+ profile1.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("john@smith.com"));
+
+ // Add the different profile. This should save as a separate profile.
+ // Note that if this same profile was "merged" it would collapse to one
+ // profile with a multi-valued entry for email.
+ personal_data_->AddProfile(profile1);
+
+ // Reload the database.
+ ResetPersonalDataManager();
+
+ // Verify the addition.
+ const std::vector<AutofillProfile*>& results3 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results3.size());
+ EXPECT_EQ(0, profile0.Compare(*results3[0]));
+ EXPECT_EQ(0, profile1.Compare(*results3[1]));
+}
+
+TEST_F(PersonalDataManagerTest, AddUpdateRemoveProfiles) {
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+
+ AutofillProfile profile1;
+ autofill_test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "19482937549");
+
+ AutofillProfile profile2;
+ autofill_test::SetProfileInfo(&profile2,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
+ "32801", "US", "19482937549");
+
+ // Add two test profiles to the database.
+ personal_data_->AddProfile(profile0);
+ personal_data_->AddProfile(profile1);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results1.size());
+ EXPECT_EQ(0, profile0.Compare(*results1[0]));
+ EXPECT_EQ(0, profile1.Compare(*results1[1]));
+
+ // Update, remove, and add.
+ profile0.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ personal_data_->UpdateProfile(profile0);
+ personal_data_->RemoveByGUID(profile1.guid());
+ personal_data_->AddProfile(profile2);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results2.size());
+ EXPECT_EQ(0, profile0.Compare(*results2[0]));
+ EXPECT_EQ(0, profile2.Compare(*results2[1]));
+
+ // Reset the PersonalDataManager. This tests that the personal data was saved
+ // to the web database, and that we can load the profiles from the web
+ // database.
+ ResetPersonalDataManager();
+
+ // Verify that we've loaded the profiles from the web database.
+ const std::vector<AutofillProfile*>& results3 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results3.size());
+ EXPECT_EQ(0, profile0.Compare(*results3[0]));
+ EXPECT_EQ(0, profile2.Compare(*results3[1]));
+}
+
+TEST_F(PersonalDataManagerTest, AddUpdateRemoveCreditCards) {
+ CreditCard credit_card0;
+ autofill_test::SetCreditCardInfo(&credit_card0,
+ "John Dillinger", "423456789012" /* Visa */, "01", "2010");
+
+ CreditCard credit_card1;
+ autofill_test::SetCreditCardInfo(&credit_card1,
+ "Bonnie Parker", "518765432109" /* Mastercard */, "12", "2012");
+
+ CreditCard credit_card2;
+ autofill_test::SetCreditCardInfo(&credit_card2,
+ "Clyde Barrow", "347666888555" /* American Express */, "04", "2015");
+
+ // Add two test credit cards to the database.
+ personal_data_->AddCreditCard(credit_card0);
+ personal_data_->AddCreditCard(credit_card1);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results1 = personal_data_->credit_cards();
+ ASSERT_EQ(2U, results1.size());
+ EXPECT_EQ(0, credit_card0.Compare(*results1[0]));
+ EXPECT_EQ(0, credit_card1.Compare(*results1[1]));
+
+ // Update, remove, and add.
+ credit_card0.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Joe"));
+ personal_data_->UpdateCreditCard(credit_card0);
+ personal_data_->RemoveByGUID(credit_card1.guid());
+ personal_data_->AddCreditCard(credit_card2);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(2U, results2.size());
+ EXPECT_EQ(credit_card0, *results2[0]);
+ EXPECT_EQ(credit_card2, *results2[1]);
+
+ // Reset the PersonalDataManager. This tests that the personal data was saved
+ // to the web database, and that we can load the credit cards from the web
+ // database.
+ ResetPersonalDataManager();
+
+ // Verify that we've loaded the credit cards from the web database.
+ const std::vector<CreditCard*>& results3 = personal_data_->credit_cards();
+ ASSERT_EQ(2U, results3.size());
+ EXPECT_EQ(credit_card0, *results3[0]);
+ EXPECT_EQ(credit_card2, *results3[1]);
+}
+
+TEST_F(PersonalDataManagerTest, AddProfilesAndCreditCards) {
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+
+ AutofillProfile profile1;
+ autofill_test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "19482937549");
+
+ CreditCard credit_card0;
+ autofill_test::SetCreditCardInfo(&credit_card0,
+ "John Dillinger", "423456789012" /* Visa */, "01", "2010");
+
+ CreditCard credit_card1;
+ autofill_test::SetCreditCardInfo(&credit_card1,
+ "Bonnie Parker", "518765432109" /* Mastercard */, "12", "2012");
+
+ // Add two test profiles to the database.
+ personal_data_->AddProfile(profile0);
+ personal_data_->AddProfile(profile1);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results1.size());
+ EXPECT_EQ(0, profile0.Compare(*results1[0]));
+ EXPECT_EQ(0, profile1.Compare(*results1[1]));
+
+ // Add two test credit cards to the database.
+ personal_data_->AddCreditCard(credit_card0);
+ personal_data_->AddCreditCard(credit_card1);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(2U, results2.size());
+ EXPECT_EQ(credit_card0, *results2[0]);
+ EXPECT_EQ(credit_card1, *results2[1]);
+
+ // Determine uniqueness by inserting all of the GUIDs into a set and verifying
+ // the size of the set matches the number of GUIDs.
+ std::set<std::string> guids;
+ guids.insert(profile0.guid());
+ guids.insert(profile1.guid());
+ guids.insert(credit_card0.guid());
+ guids.insert(credit_card1.guid());
+ EXPECT_EQ(4U, guids.size());
+}
+
+// Test for http://crbug.com/50047. Makes sure that guids are populated
+// correctly on load.
+TEST_F(PersonalDataManagerTest, PopulateUniqueIDsOnLoad) {
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "y", "", "", "", "", "", "", "", "", "", "", "");
+
+ // Add the profile0 to the db.
+ personal_data_->AddProfile(profile0);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ // Verify that we've loaded the profiles from the web database.
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, profile0.Compare(*results2[0]));
+
+ // Add a new profile.
+ AutofillProfile profile1;
+ autofill_test::SetProfileInfo(&profile1,
+ "z", "", "", "", "", "", "", "", "", "", "", "");
+ personal_data_->AddProfile(profile1);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ // Make sure the two profiles have different GUIDs, both valid.
+ const std::vector<AutofillProfile*>& results3 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results3.size());
+ EXPECT_NE(results3[0]->guid(), results3[1]->guid());
+ EXPECT_TRUE(base::IsValidGUID(results3[0]->guid()));
+ EXPECT_TRUE(base::IsValidGUID(results3[1]->guid()));
+}
+
+TEST_F(PersonalDataManagerTest, SetEmptyProfile) {
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "", "", "", "", "", "", "", "", "", "", "", "");
+
+ // Add the empty profile to the database.
+ personal_data_->AddProfile(profile0);
+
+ // Note: no refresh here.
+
+ // Reset the PersonalDataManager. This tests that the personal data was saved
+ // to the web database, and that we can load the profiles from the web
+ // database.
+ ResetPersonalDataManager();
+
+ // Verify that we've loaded the profiles from the web database.
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+ ASSERT_EQ(0U, results2.size());
+}
+
+TEST_F(PersonalDataManagerTest, SetEmptyCreditCard) {
+ CreditCard credit_card0;
+ autofill_test::SetCreditCardInfo(&credit_card0, "", "", "", "");
+
+ // Add the empty credit card to the database.
+ personal_data_->AddCreditCard(credit_card0);
+
+ // Note: no refresh here.
+
+ // Reset the PersonalDataManager. This tests that the personal data was saved
+ // to the web database, and that we can load the credit cards from the web
+ // database.
+ ResetPersonalDataManager();
+
+ // Verify that we've loaded the credit cards from the web database.
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(0U, results2.size());
+}
+
+TEST_F(PersonalDataManagerTest, Refresh) {
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+
+ AutofillProfile profile1;
+ autofill_test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "19482937549");
+
+ // Add the test profiles to the database.
+ personal_data_->AddProfile(profile0);
+ personal_data_->AddProfile(profile1);
+
+ // Labels depend on other profiles in the list - update labels manually.
+ std::vector<AutofillProfile *> profile_pointers;
+ profile_pointers.push_back(&profile0);
+ profile_pointers.push_back(&profile1);
+ AutofillProfile::AdjustInferredLabels(&profile_pointers);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(2U, results1.size());
+ EXPECT_EQ(profile0, *results1[0]);
+ EXPECT_EQ(profile1, *results1[1]);
+
+ AutofillProfile profile2;
+ autofill_test::SetProfileInfo(&profile2,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
+ "32801", "US", "19482937549");
+
+ // Adjust all labels.
+ profile_pointers.push_back(&profile2);
+ AutofillProfile::AdjustInferredLabels(&profile_pointers);
+
+ scoped_refptr<WebDataService> wds = WebDataServiceFactory::GetForProfile(
+ profile_.get(), Profile::EXPLICIT_ACCESS);
+ ASSERT_TRUE(wds.get());
+ wds->AddAutofillProfile(profile2);
+
+ personal_data_->Refresh();
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+ ASSERT_EQ(3U, results2.size());
+ EXPECT_EQ(profile0, *results2[0]);
+ EXPECT_EQ(profile1, *results2[1]);
+ EXPECT_EQ(profile2, *results2[2]);
+
+ wds->RemoveAutofillProfile(profile1.guid());
+ wds->RemoveAutofillProfile(profile2.guid());
+
+ // Before telling the PDM to refresh, simulate an edit to one of the profiles
+ // via a SetProfile update (this would happen if the Autofill window was
+ // open with a previous snapshot of the profiles, and something [e.g. sync]
+ // removed a profile from the browser. In this edge case, we will end up
+ // in a consistent state by dropping the write).
+ profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jo"));
+ personal_data_->UpdateProfile(profile0);
+ personal_data_->AddProfile(profile1);
+ personal_data_->AddProfile(profile2);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results3 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results3.size());
+ EXPECT_EQ(profile0, *results2[0]);
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormData) {
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", "theprez@gmail.com", NULL, "21 Laussat St", NULL,
+ "San Francisco", "California", "94102", NULL, NULL);
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormDataBadEmail) {
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "bogus", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ ASSERT_EQ(static_cast<CreditCard*>(NULL), imported_credit_card);
+
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(0U, results.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormDataNotEnoughFilledFields) {
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card number:", "card_number", "4111 1111 1111 1111", "text", &field);
+ form.fields.push_back(field);
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ ASSERT_EQ(0U, profiles.size());
+ const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards();
+ ASSERT_EQ(0U, credit_cards.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportPhoneNumberSplitAcrossMultipleFields) {
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone #:", "home_phone_area_code", "650", "text", &field);
+ field.max_length = 3;
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone #:", "home_phone_prefix", "555", "text", &field);
+ field.max_length = 3;
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone #:", "home_phone_suffix", "0000", "text", &field);
+ field.max_length = 4;
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", NULL, NULL, "21 Laussat St", NULL,
+ "San Francisco", "California", "94102", NULL, "(650) 555-0000");
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+}
+
+TEST_F(PersonalDataManagerTest, SetUniqueCreditCardLabels) {
+ CreditCard credit_card0;
+ credit_card0.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("John"));
+ CreditCard credit_card1;
+ credit_card1.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Paul"));
+ CreditCard credit_card2;
+ credit_card2.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Ringo"));
+ CreditCard credit_card3;
+ credit_card3.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Other"));
+ CreditCard credit_card4;
+ credit_card4.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Ozzy"));
+ CreditCard credit_card5;
+ credit_card5.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Dio"));
+
+ // Add the test credit cards to the database.
+ personal_data_->AddCreditCard(credit_card0);
+ personal_data_->AddCreditCard(credit_card1);
+ personal_data_->AddCreditCard(credit_card2);
+ personal_data_->AddCreditCard(credit_card3);
+ personal_data_->AddCreditCard(credit_card4);
+ personal_data_->AddCreditCard(credit_card5);
+
+ // Reset the PersonalDataManager. This tests that the personal data was saved
+ // to the web database, and that we can load the credit cards from the web
+ // database.
+ ResetPersonalDataManager();
+
+ const std::vector<CreditCard*>& results = personal_data_->credit_cards();
+ ASSERT_EQ(6U, results.size());
+ EXPECT_EQ(credit_card0.guid(), results[0]->guid());
+ EXPECT_EQ(credit_card1.guid(), results[1]->guid());
+ EXPECT_EQ(credit_card2.guid(), results[2]->guid());
+ EXPECT_EQ(credit_card3.guid(), results[3]->guid());
+ EXPECT_EQ(credit_card4.guid(), results[4]->guid());
+ EXPECT_EQ(credit_card5.guid(), results[5]->guid());
+}
+
+TEST_F(PersonalDataManagerTest, AggregateTwoDifferentProfiles) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", "theprez@gmail.com", NULL, "21 Laussat St", NULL,
+ "San Francisco", "California", "94102", NULL, NULL);
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, expected.Compare(*results1[0]));
+
+ // Now create a completely different profile.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "John", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Adams", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "second@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "22 Laussat St", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ AutofillProfile expected2;
+ autofill_test::SetProfileInfo(&expected2, "John", NULL,
+ "Adams", "second@gmail.com", NULL, "22 Laussat St", NULL,
+ "San Francisco", "California", "94102", NULL, NULL);
+ ASSERT_EQ(2U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+ EXPECT_EQ(0, expected2.Compare(*results2[1]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateTwoProfilesWithMultiValue) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", "theprez@gmail.com", NULL, "21 Laussat St", NULL,
+ "San Francisco", "California", "94102", NULL, NULL);
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, expected.Compare(*results1[0]));
+
+ // Now create a completely different profile.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "John", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Adams", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "second@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Modify expected to include multi-valued fields.
+ std::vector<string16> values;
+ expected.GetRawMultiInfo(NAME_FULL, &values);
+ values.push_back(ASCIIToUTF16("John Adams"));
+ expected.SetRawMultiInfo(NAME_FULL, values);
+ expected.GetRawMultiInfo(EMAIL_ADDRESS, &values);
+ values.push_back(ASCIIToUTF16("second@gmail.com"));
+ expected.SetRawMultiInfo(EMAIL_ADDRESS, values);
+
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateSameProfileWithConflict) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address", "1600 Pennsylvania Avenue", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 2:", "address2", "Suite A", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone:", "phone", "6505556666", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(
+ &expected, "George", NULL, "Washington", "theprez@gmail.com", NULL,
+ "1600 Pennsylvania Avenue", "Suite A", "San Francisco", "California",
+ "94102", NULL, "(650) 555-6666");
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, expected.Compare(*results1[0]));
+
+ // Now create an updated profile.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address", "1600 Pennsylvania Avenue", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 2:", "address2", "Suite A", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ // Country gets added.
+ autofill_test::CreateTestFormField(
+ "Country:", "country", "USA", "text", &field);
+ form2.fields.push_back(field);
+ // Phone gets updated.
+ autofill_test::CreateTestFormField(
+ "Phone:", "phone", "6502231234", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Add multi-valued phone number to expectation. Also, country gets added.
+ std::vector<string16> values;
+ expected.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &values);
+ values.push_back(ASCIIToUTF16("(650) 223-1234"));
+ expected.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, values);
+ expected.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInOld) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "Philadelphia", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "Pennsylvania", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zipcode", "19106", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", NULL, NULL, "190 High Street", NULL,
+ "Philadelphia", "Pennsylvania", "19106", NULL, NULL);
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, expected.Compare(*results1[0]));
+
+ // Submit a form with new data for the first profile.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "Philadelphia", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "Pennsylvania", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zipcode", "19106", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ AutofillProfile expected2;
+ autofill_test::SetProfileInfo(&expected2, "George", NULL,
+ "Washington", "theprez@gmail.com", NULL, "190 High Street", NULL,
+ "Philadelphia", "Pennsylvania", "19106", NULL, NULL);
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected2.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInNew) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Company:", "company", "Government", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "Philadelphia", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "Pennsylvania", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zipcode", "19106", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", "theprez@gmail.com", "Government", "190 High Street", NULL,
+ "Philadelphia", "Pennsylvania", "19106", NULL, NULL);
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, expected.Compare(*results1[0]));
+
+ // Submit a form with new data for the first profile.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ // Note missing Company field.
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "Philadelphia", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "Pennsylvania", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zipcode", "19106", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Expect no change.
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateProfileWithInsufficientAddress) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Company:", "company", "Government", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "Philadelphia", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_FALSE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Note: no refresh here.
+
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ ASSERT_EQ(0U, profiles.size());
+ const std::vector<CreditCard*>& credit_cards = personal_data_->credit_cards();
+ ASSERT_EQ(0U, credit_cards.size());
+}
+
+TEST_F(PersonalDataManagerTest, AggregateExistingAuxiliaryProfile) {
+ // Simulate having access to an auxiliary profile.
+ // |auxiliary_profile| will be owned by |personal_data_|.
+ AutofillProfile* auxiliary_profile = new AutofillProfile;
+ autofill_test::SetProfileInfo(auxiliary_profile,
+ "Tester", "Frederick", "McAddressBookTesterson",
+ "tester@example.com", "Acme Inc.", "1 Main", "Apt A", "San Francisco",
+ "CA", "94102", "US", "1.415.888.9999");
+ ScopedVector<AutofillProfile>& auxiliary_profiles =
+ personal_data_->auxiliary_profiles_;
+ auxiliary_profiles.push_back(auxiliary_profile);
+
+ // Simulate a form submission with a subset of the info.
+ // Note that the phone number format is different from the saved format.
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "Tester", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "McAddressBookTesterson", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "tester@example.com", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "1 Main", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "CA", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone:", "phone", "4158889999", "text", &field);
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Note: No refresh.
+
+ // Expect no change.
+ const std::vector<AutofillProfile*>& web_profiles =
+ personal_data_->web_profiles();
+ EXPECT_EQ(0U, web_profiles.size());
+ ASSERT_EQ(1U, auxiliary_profiles.size());
+ EXPECT_EQ(0, auxiliary_profile->Compare(*auxiliary_profiles[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateTwoDifferentCreditCards) {
+ FormData form1;
+
+ // Start with a single valid credit card form.
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_->SaveImportedCreditCard(*imported_credit_card);
+ delete imported_credit_card;
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ CreditCard expected;
+ autofill_test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second different valid credit card.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "5500 0000 0000 0004", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "02", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2012", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_->SaveImportedCreditCard(*imported_credit_card);
+ delete imported_credit_card;
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ CreditCard expected2;
+ autofill_test::SetCreditCardInfo(&expected2,
+ "", "5500000000000004", "02", "2012");
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(2U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+ EXPECT_EQ(0, expected2.Compare(*results2[1]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateInvalidCreditCard) {
+ FormData form1;
+
+ // Start with a single valid credit card form.
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_->SaveImportedCreditCard(*imported_credit_card);
+ delete imported_credit_card;
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ CreditCard expected;
+ autofill_test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second different invalid credit card.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Jim Johansen", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "1000000000000000", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "02", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2012", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Note: no refresh here.
+
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithConflict) {
+ FormData form1;
+
+ // Start with a single valid credit card form.
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_->SaveImportedCreditCard(*imported_credit_card);
+ delete imported_credit_card;
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ CreditCard expected;
+ autofill_test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second different valid credit card where the year is different but
+ // the credit card number matches.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111 1111 1111 1111", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2012", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ // Expect that the newer information is saved. In this case the year is
+ // updated to "2012".
+ CreditCard expected2;
+ autofill_test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2012");
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected2.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateEmptyCreditCardWithConflict) {
+ FormData form1;
+
+ // Start with a single valid credit card form.
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_->SaveImportedCreditCard(*imported_credit_card);
+ delete imported_credit_card;
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ CreditCard expected;
+ autofill_test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second credit card with no number.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2012", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Note: no refresh here.
+
+ // No change is expected.
+ CreditCard expected2;
+ autofill_test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected2.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInNew) {
+ FormData form1;
+
+ // Start with a single valid credit card form.
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_TRUE(imported_credit_card);
+ personal_data_->SaveImportedCreditCard(*imported_credit_card);
+ delete imported_credit_card;
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ CreditCard expected;
+ autofill_test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second different valid credit card where the name is missing but
+ // the credit card number matches.
+ FormData form2;
+ // Note missing name.
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111111111111111", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Wait for the refresh, which in this case is a no-op.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ // No change is expected.
+ CreditCard expected2;
+ autofill_test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected2.Compare(*results2[0]));
+
+ // Add a third credit card where the expiration date is missing.
+ FormData form3;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Johnny McEnroe", "text", &field);
+ form3.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "5555555555554444", "text", &field);
+ form3.fields.push_back(field);
+ // Note missing expiration month and year..
+
+ FormStructure form_structure3(form3, std::string());
+ form_structure3.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_FALSE(personal_data_->ImportFormData(form_structure3,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Note: no refresh here.
+
+ // No change is expected.
+ CreditCard expected3;
+ autofill_test::SetCreditCardInfo(&expected3,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results3 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results3.size());
+ EXPECT_EQ(0, expected3.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInOld) {
+ // Start with a single valid credit card stored via the preferences.
+ // Note the empty name.
+ CreditCard saved_credit_card;
+ autofill_test::SetCreditCardInfo(&saved_credit_card,
+ "", "4111111111111111" /* Visa */, "01", "2011");
+ personal_data_->AddCreditCard(saved_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results1 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(saved_credit_card, *results1[0]);
+
+
+ // Add a second different valid credit card where the year is different but
+ // the credit card number matches.
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2012", "text", &field);
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ // Expect that the newer information is saved. In this case the year is
+ // added to the existing credit card.
+ CreditCard expected2;
+ autofill_test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2012");
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected2.Compare(*results2[0]));
+}
+
+// We allow the user to store a credit card number with separators via the UI.
+// We should not try to re-aggregate the same card with the separators stripped.
+TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithSeparators) {
+ // Start with a single valid credit card stored via the preferences.
+ // Note the separators in the credit card number.
+ CreditCard saved_credit_card;
+ autofill_test::SetCreditCardInfo(&saved_credit_card,
+ "Biggie Smalls", "4111 1111 1111 1111" /* Visa */, "01", "2011");
+ personal_data_->AddCreditCard(saved_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results1 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, saved_credit_card.Compare(*results1[0]));
+
+ // Import the same card info, but with different separators in the number.
+ FormData form;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Month:", "exp_month", "01", "text", &field);
+ form.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Exp Year:", "exp_year", "2011", "text", &field);
+ form.fields.push_back(field);
+
+ FormStructure form_structure(form, std::string());
+ form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
+ &imported_credit_card));
+ EXPECT_FALSE(imported_credit_card);
+
+ // Wait for the refresh, which in this case is a no-op.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ // Expect that no new card is saved.
+ const std::vector<CreditCard*>& results2 = personal_data_->credit_cards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, saved_credit_card.Compare(*results2[0]));
+}
+
+TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) {
+ // Check that there are no available types with no profiles stored.
+ FieldTypeSet non_empty_types;
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+ EXPECT_EQ(0U, non_empty_types.size());
+
+ // Test with one profile stored.
+ AutofillProfile profile0;
+ autofill_test::SetProfileInfo(&profile0,
+ "Marion", NULL, "Morrison",
+ "johnwayne@me.xyz", NULL, "123 Zoo St.", NULL, "Hollywood", "CA",
+ "91601", "US", "14155678910");
+
+ personal_data_->AddProfile(profile0);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+ EXPECT_EQ(14U, non_empty_types.size());
+ EXPECT_TRUE(non_empty_types.count(NAME_FIRST));
+ EXPECT_TRUE(non_empty_types.count(NAME_LAST));
+ EXPECT_TRUE(non_empty_types.count(NAME_FULL));
+ EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_CITY));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STATE));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_ZIP));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_COUNTRY));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_COUNTRY_CODE));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_CODE));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_AND_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_WHOLE_NUMBER));
+
+ // Test with multiple profiles stored.
+ AutofillProfile profile1;
+ autofill_test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "16502937549");
+
+ AutofillProfile profile2;
+ autofill_test::SetProfileInfo(&profile2,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
+ "32801", "US", "16502937549");
+
+ personal_data_->AddProfile(profile1);
+ personal_data_->AddProfile(profile2);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+ EXPECT_EQ(18U, non_empty_types.size());
+ EXPECT_TRUE(non_empty_types.count(NAME_FIRST));
+ EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE));
+ EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE_INITIAL));
+ EXPECT_TRUE(non_empty_types.count(NAME_LAST));
+ EXPECT_TRUE(non_empty_types.count(NAME_FULL));
+ EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS));
+ EXPECT_TRUE(non_empty_types.count(COMPANY_NAME));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE2));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_CITY));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STATE));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_ZIP));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_COUNTRY));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_CODE));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_COUNTRY_CODE));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_AND_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_WHOLE_NUMBER));
+
+ // Test with credit card information also stored.
+ CreditCard credit_card;
+ autofill_test::SetCreditCardInfo(&credit_card,
+ "John Dillinger", "423456789012" /* Visa */,
+ "01", "2010");
+ personal_data_->AddCreditCard(credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+ EXPECT_EQ(25U, non_empty_types.size());
+ EXPECT_TRUE(non_empty_types.count(NAME_FIRST));
+ EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE));
+ EXPECT_TRUE(non_empty_types.count(NAME_MIDDLE_INITIAL));
+ EXPECT_TRUE(non_empty_types.count(NAME_LAST));
+ EXPECT_TRUE(non_empty_types.count(NAME_FULL));
+ EXPECT_TRUE(non_empty_types.count(EMAIL_ADDRESS));
+ EXPECT_TRUE(non_empty_types.count(COMPANY_NAME));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE1));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_LINE2));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_CITY));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_STATE));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_ZIP));
+ EXPECT_TRUE(non_empty_types.count(ADDRESS_HOME_COUNTRY));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_CODE));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_COUNTRY_CODE));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_CITY_AND_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(PHONE_HOME_WHOLE_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_NAME));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_NUMBER));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_MONTH));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_2_DIGIT_YEAR));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR));
+ EXPECT_TRUE(non_empty_types.count(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR));
+}
+
+TEST_F(PersonalDataManagerTest, CaseInsensitiveMultiValueAggregation) {
+ FormData form1;
+ FormFieldData field;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form1.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone number:", "phone_number", "817-555-6789", "text", &field);
+ form1.fields.push_back(field);
+
+ FormStructure form_structure1(form1, std::string());
+ form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
+ const CreditCard* imported_credit_card;
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ AutofillProfile expected;
+ autofill_test::SetProfileInfo(&expected, "George", NULL,
+ "Washington", "theprez@gmail.com", NULL, "21 Laussat St", NULL,
+ "San Francisco", "California", "94102", NULL, "(817) 555-6789");
+ const std::vector<AutofillProfile*>& results1 = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results1.size());
+ EXPECT_EQ(0, expected.Compare(*results1[0]));
+
+ // Upper-case the first name and change the phone number.
+ FormData form2;
+ autofill_test::CreateTestFormField(
+ "First name:", "first_name", "GEORGE", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form2.fields.push_back(field);
+ autofill_test::CreateTestFormField(
+ "Phone number:", "phone_number", "214-555-1234", "text", &field);
+ form2.fields.push_back(field);
+
+ FormStructure form_structure2(form2, std::string());
+ form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
+ &imported_credit_card));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Modify expected to include multi-valued fields.
+ std::vector<string16> values;
+ expected.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &values);
+ values.push_back(ASCIIToUTF16("(214) 555-1234"));
+ expected.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, values);
+
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, expected.Compare(*results2[0]));
+}
diff --git a/components/autofill/browser/phone_field.cc b/components/autofill/browser/phone_field.cc
new file mode 100644
index 0000000..e7415ba
--- /dev/null
+++ b/components/autofill/browser/phone_field.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2011 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/browser/phone_field.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_regex_constants.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// This string includes all area code separators, including NoText.
+string16 GetAreaRegex() {
+ string16 area_code = UTF8ToUTF16(autofill::kAreaCodeRe);
+ area_code.append(ASCIIToUTF16("|")); // Regexp separator.
+ area_code.append(UTF8ToUTF16(autofill::kAreaCodeNotextRe));
+ return area_code;
+}
+
+} // namespace
+
+PhoneField::~PhoneField() {}
+
+// Phone field grammars - first matched grammar will be parsed. Grammars are
+// separated by { REGEX_SEPARATOR, FIELD_NONE, 0 }. Suffix and extension are
+// parsed separately unless they are necessary parts of the match.
+// The following notation is used to describe the patterns:
+// <cc> - country code field.
+// <ac> - area code field.
+// <phone> - phone or prefix.
+// <suffix> - suffix.
+// <ext> - extension.
+// :N means field is limited to N characters, otherwise it is unlimited.
+// (pattern <field>)? means pattern is optional and matched separately.
+const PhoneField::Parser PhoneField::kPhoneFieldGrammars[] = {
+ // Country code: <cc> Area Code: <ac> Phone: <phone> (- <suffix>
+ // (Ext: <ext>)?)?
+ { REGEX_COUNTRY, FIELD_COUNTRY_CODE, 0 },
+ { REGEX_AREA, FIELD_AREA_CODE, 0 },
+ { REGEX_PHONE, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // \( <ac> \) <phone>:3 <suffix>:4 (Ext: <ext>)?
+ { REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 3 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3 },
+ { REGEX_PHONE, FIELD_SUFFIX, 4 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <cc> <ac>:3 - <phone>:3 - <suffix>:4 (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 0 },
+ { REGEX_PHONE, FIELD_AREA_CODE, 3 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3 },
+ { REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <cc>:3 <ac>:3 <phone>:3 <suffix>:4 (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 3 },
+ { REGEX_PHONE, FIELD_AREA_CODE, 3 },
+ { REGEX_PHONE, FIELD_PHONE, 3 },
+ { REGEX_PHONE, FIELD_SUFFIX, 4 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Area Code: <ac> Phone: <phone> (- <suffix> (Ext: <ext>)?)?
+ { REGEX_AREA, FIELD_AREA_CODE, 0 },
+ { REGEX_PHONE, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <ac> <phone>:3 <suffix>:4 (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_AREA_CODE, 0 },
+ { REGEX_PHONE, FIELD_PHONE, 3 },
+ { REGEX_PHONE, FIELD_SUFFIX, 4 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <cc> \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 0 },
+ { REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 0 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: \( <ac> \) <phone> (- <suffix> (Ext: <ext>)?)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 0 },
+ { REGEX_AREA_NOTEXT, FIELD_AREA_CODE, 0 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <cc> - <ac> - <phone> - <suffix> (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 0 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE, 0 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 0 },
+ { REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <ac> Prefix: <phone> Suffix: <suffix> (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_AREA_CODE, 0 },
+ { REGEX_PREFIX, FIELD_PHONE, 0 },
+ { REGEX_SUFFIX, FIELD_SUFFIX, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <ac> - <phone>:3 - <suffix>:4 (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_AREA_CODE, 0 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_PHONE, 3 },
+ { REGEX_SUFFIX_SEPARATOR, FIELD_SUFFIX, 4 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <cc> - <ac> - <phone> (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 0 },
+ { REGEX_PREFIX_SEPARATOR, FIELD_AREA_CODE, 0 },
+ { REGEX_SUFFIX_SEPARATOR, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <ac> - <phone> (Ext: <ext>)?
+ { REGEX_AREA, FIELD_AREA_CODE, 0 },
+ { REGEX_PHONE, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <cc>:3 - <phone>:10 (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_COUNTRY_CODE, 3 },
+ { REGEX_PHONE, FIELD_PHONE, 10 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+ // Phone: <phone> (Ext: <ext>)?
+ { REGEX_PHONE, FIELD_PHONE, 0 },
+ { REGEX_SEPARATOR, FIELD_NONE, 0 },
+};
+
+// static
+FormField* PhoneField::Parse(AutofillScanner* scanner) {
+ if (scanner->IsEnd())
+ return NULL;
+
+ scanner->SaveCursor();
+
+ // The form owns the following variables, so they should not be deleted.
+ const AutofillField* parsed_fields[FIELD_MAX];
+
+ for (size_t i = 0; i < arraysize(kPhoneFieldGrammars); ++i) {
+ memset(parsed_fields, 0, sizeof(parsed_fields));
+ scanner->SaveCursor();
+
+ // Attempt to parse according to the next grammar.
+ for (; i < arraysize(kPhoneFieldGrammars) &&
+ kPhoneFieldGrammars[i].regex != REGEX_SEPARATOR; ++i) {
+ if (!ParseFieldSpecifics(
+ scanner,
+ GetRegExp(kPhoneFieldGrammars[i].regex),
+ MATCH_DEFAULT | MATCH_TELEPHONE,
+ &parsed_fields[kPhoneFieldGrammars[i].phone_part]))
+ break;
+ if (kPhoneFieldGrammars[i].max_size &&
+ (!parsed_fields[kPhoneFieldGrammars[i].phone_part]->max_length ||
+ kPhoneFieldGrammars[i].max_size <
+ parsed_fields[kPhoneFieldGrammars[i].phone_part]->max_length)) {
+ break;
+ }
+ }
+
+ if (i >= arraysize(kPhoneFieldGrammars)) {
+ scanner->Rewind();
+ return NULL; // Parsing failed.
+ }
+ if (kPhoneFieldGrammars[i].regex == REGEX_SEPARATOR)
+ break; // Parsing succeeded.
+
+ // Proceed to the next grammar.
+ do {
+ ++i;
+ } while (i < arraysize(kPhoneFieldGrammars) &&
+ kPhoneFieldGrammars[i].regex != REGEX_SEPARATOR);
+
+ if (i + 1 == arraysize(kPhoneFieldGrammars)) {
+ scanner->Rewind();
+ return NULL; // Tried through all the possibilities - did not match.
+ }
+
+ scanner->Rewind();
+ }
+
+ if (!parsed_fields[FIELD_PHONE]) {
+ scanner->Rewind();
+ return NULL;
+ }
+
+ scoped_ptr<PhoneField> phone_field(new PhoneField);
+ for (int i = 0; i < FIELD_MAX; ++i)
+ phone_field->parsed_phone_fields_[i] = parsed_fields[i];
+
+ // Look for optional fields.
+
+ // Look for a third text box.
+ if (!phone_field->parsed_phone_fields_[FIELD_SUFFIX]) {
+ if (!ParseField(scanner, UTF8ToUTF16(autofill::kPhoneSuffixRe),
+ &phone_field->parsed_phone_fields_[FIELD_SUFFIX])) {
+ ParseField(scanner, UTF8ToUTF16(autofill::kPhoneSuffixSeparatorRe),
+ &phone_field->parsed_phone_fields_[FIELD_SUFFIX]);
+ }
+ }
+
+ // Now look for an extension.
+ ParseField(scanner, UTF8ToUTF16(autofill::kPhoneExtensionRe),
+ &phone_field->parsed_phone_fields_[FIELD_EXTENSION]);
+
+ return phone_field.release();
+}
+
+bool PhoneField::ClassifyField(FieldTypeMap* map) const {
+ bool ok = true;
+
+ DCHECK(parsed_phone_fields_[FIELD_PHONE]); // Phone was correctly parsed.
+
+ if ((parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) ||
+ (parsed_phone_fields_[FIELD_AREA_CODE] != NULL) ||
+ (parsed_phone_fields_[FIELD_SUFFIX] != NULL)) {
+ if (parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) {
+ ok = ok && AddClassification(parsed_phone_fields_[FIELD_COUNTRY_CODE],
+ PHONE_HOME_COUNTRY_CODE,
+ map);
+ }
+
+ AutofillFieldType field_number_type = PHONE_HOME_NUMBER;
+ if (parsed_phone_fields_[FIELD_AREA_CODE] != NULL) {
+ ok = ok && AddClassification(parsed_phone_fields_[FIELD_AREA_CODE],
+ PHONE_HOME_CITY_CODE,
+ map);
+ } else if (parsed_phone_fields_[FIELD_COUNTRY_CODE] != NULL) {
+ // Only if we can find country code without city code, it means the phone
+ // number include city code.
+ field_number_type = PHONE_HOME_CITY_AND_NUMBER;
+ }
+ // We tag the prefix as PHONE_HOME_NUMBER, then when filling the form
+ // we fill only the prefix depending on the size of the input field.
+ ok = ok && AddClassification(parsed_phone_fields_[FIELD_PHONE],
+ field_number_type,
+ map);
+ // We tag the suffix as PHONE_HOME_NUMBER, then when filling the form
+ // we fill only the suffix depending on the size of the input field.
+ if (parsed_phone_fields_[FIELD_SUFFIX] != NULL) {
+ ok = ok && AddClassification(parsed_phone_fields_[FIELD_SUFFIX],
+ PHONE_HOME_NUMBER,
+ map);
+ }
+ } else {
+ ok = AddClassification(parsed_phone_fields_[FIELD_PHONE],
+ PHONE_HOME_WHOLE_NUMBER,
+ map);
+ }
+
+ return ok;
+}
+
+PhoneField::PhoneField() {
+ memset(parsed_phone_fields_, 0, sizeof(parsed_phone_fields_));
+}
+
+// static
+string16 PhoneField::GetRegExp(RegexType regex_id) {
+ switch (regex_id) {
+ case REGEX_COUNTRY:
+ return UTF8ToUTF16(autofill::kCountryCodeRe);
+ case REGEX_AREA:
+ return GetAreaRegex();
+ case REGEX_AREA_NOTEXT:
+ return UTF8ToUTF16(autofill::kAreaCodeNotextRe);
+ case REGEX_PHONE:
+ return UTF8ToUTF16(autofill::kPhoneRe);
+ case REGEX_PREFIX_SEPARATOR:
+ return UTF8ToUTF16(autofill::kPhonePrefixSeparatorRe);
+ case REGEX_PREFIX:
+ return UTF8ToUTF16(autofill::kPhonePrefixRe);
+ case REGEX_SUFFIX_SEPARATOR:
+ return UTF8ToUTF16(autofill::kPhoneSuffixSeparatorRe);
+ case REGEX_SUFFIX:
+ return UTF8ToUTF16(autofill::kPhoneSuffixRe);
+ case REGEX_EXTENSION:
+ return UTF8ToUTF16(autofill::kPhoneExtensionRe);
+ default:
+ NOTREACHED();
+ break;
+ }
+ return string16();
+}
diff --git a/components/autofill/browser/phone_field.h b/components/autofill/browser/phone_field.h
new file mode 100644
index 0000000..ee3cb0a
--- /dev/null
+++ b/components/autofill/browser/phone_field.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_PHONE_FIELD_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PHONE_FIELD_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/form_field.h"
+#include "components/autofill/browser/phone_number.h"
+
+class AutofillField;
+class AutofillScanner;
+
+// A phone number in one of the following formats:
+// - area code, prefix, suffix
+// - area code, number
+// - number
+class PhoneField : public FormField {
+ public:
+ virtual ~PhoneField();
+
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(FieldTypeMap* map) const OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(PhoneFieldTest, ParseOneLinePhone);
+ FRIEND_TEST_ALL_PREFIXES(PhoneFieldTest, ParseTwoLinePhone);
+ FRIEND_TEST_ALL_PREFIXES(PhoneFieldTest, ThreePartPhoneNumber);
+ FRIEND_TEST_ALL_PREFIXES(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix);
+ FRIEND_TEST_ALL_PREFIXES(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2);
+ FRIEND_TEST_ALL_PREFIXES(PhoneFieldTest, CountryAndCityAndPhoneNumber);
+
+ // This is for easy description of the possible parsing paths of the phone
+ // fields.
+ enum RegexType {
+ REGEX_COUNTRY,
+ REGEX_AREA,
+ REGEX_AREA_NOTEXT,
+ REGEX_PHONE,
+ REGEX_PREFIX_SEPARATOR,
+ REGEX_PREFIX,
+ REGEX_SUFFIX_SEPARATOR,
+ REGEX_SUFFIX,
+ REGEX_EXTENSION,
+
+ // Separates regexps in grammar.
+ REGEX_SEPARATOR,
+ };
+
+ // Parsed fields.
+ enum PhonePart {
+ FIELD_NONE = -1,
+ FIELD_COUNTRY_CODE,
+ FIELD_AREA_CODE,
+ FIELD_PHONE,
+ FIELD_SUFFIX,
+ FIELD_EXTENSION,
+
+ FIELD_MAX,
+ };
+
+ struct Parser {
+ RegexType regex; // Field matching reg-ex.
+ PhonePart phone_part; // Index of the field.
+ size_t max_size; // Max size of the field to match. 0 means any.
+ };
+
+ static const Parser kPhoneFieldGrammars[];
+
+ PhoneField();
+
+ // Returns the regular expression string correspoding to |regex_id|
+ static string16 GetRegExp(RegexType regex_id);
+
+ // FIELD_PHONE is always present; holds suffix if prefix is present.
+ // The rest could be NULL.
+ const AutofillField* parsed_phone_fields_[FIELD_MAX];
+
+ DISALLOW_COPY_AND_ASSIGN(PhoneField);
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PHONE_FIELD_H_
diff --git a/components/autofill/browser/phone_field_unittest.cc b/components/autofill/browser/phone_field_unittest.cc
new file mode 100644
index 0000000..cec2f84
--- /dev/null
+++ b/components/autofill/browser/phone_field_unittest.cc
@@ -0,0 +1,226 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_field.h"
+#include "components/autofill/browser/autofill_scanner.h"
+#include "components/autofill/browser/phone_field.h"
+#include "components/autofill/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class PhoneFieldTest : public testing::Test {
+ public:
+ PhoneFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<PhoneField> field_;
+ FieldTypeMap field_type_map_;
+
+ // Downcast for tests.
+ static PhoneField* Parse(AutofillScanner* scanner) {
+ return static_cast<PhoneField*>(PhoneField::Parse(scanner));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PhoneFieldTest);
+};
+
+TEST_F(PhoneFieldTest, Empty) {
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<PhoneField*>(NULL), field_.get());
+}
+
+TEST_F(PhoneFieldTest, NonParse) {
+ list_.push_back(new AutofillField);
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_EQ(static_cast<PhoneField*>(NULL), field_.get());
+}
+
+TEST_F(PhoneFieldTest, ParseOneLinePhone) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Phone");
+ field.name = ASCIIToUTF16("phone");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("phone1")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER, field_type_map_[ASCIIToUTF16("phone1")]);
+}
+
+TEST_F(PhoneFieldTest, ParseTwoLinePhone) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Area Code");
+ field.name = ASCIIToUTF16("area code");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+
+ field.label = ASCIIToUTF16("Phone");
+ field.name = ASCIIToUTF16("phone");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("areacode1")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("areacode1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("phone2")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("phone2")]);
+}
+
+TEST_F(PhoneFieldTest, ThreePartPhoneNumber) {
+ // Phone in format <field> - <field> - <field> could be either
+ // <area code> - <prefix> - <suffix>, or
+ // <country code> - <area code> - <phone>. The only distinguishing feature is
+ // size: <prefix> is no bigger than 3 characters, and <suffix> is no bigger
+ // than 4.
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Phone:");
+ field.name = ASCIIToUTF16("dayphone1");
+ field.max_length = 0;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+
+ field.label = ASCIIToUTF16("-");
+ field.name = ASCIIToUTF16("dayphone2");
+ field.max_length = 3;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
+
+ field.label = ASCIIToUTF16("-");
+ field.name = ASCIIToUTF16("dayphone3");
+ field.max_length = 4;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+
+ field.label = ASCIIToUTF16("ext.:");
+ field.name = ASCIIToUTF16("dayphone4");
+ field.max_length = 0;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("ext4")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("areacode1")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("areacode1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("prefix2")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("prefix2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("suffix3")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("suffix3")]);
+ EXPECT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("ext4")) == field_type_map_.end());
+}
+
+// This scenario of explicitly labeled "prefix" and "suffix" phone numbers
+// encountered in http://crbug.com/40694 with page
+// https://www.wrapables.com/jsp/Signup.jsp.
+TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Phone:");
+ field.name = ASCIIToUTF16("area");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("areacode1")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("prefix");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("prefix2")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("suffix");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("suffix3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("areacode1")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("areacode1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("prefix2")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("prefix2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("suffix3")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("suffix3")]);
+}
+
+TEST_F(PhoneFieldTest, ThreePartPhoneNumberPrefixSuffix2) {
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("(");
+ field.name = ASCIIToUTF16("phone1");
+ field.max_length = 3;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("phone1")));
+
+ field.label = ASCIIToUTF16(")");
+ field.name = ASCIIToUTF16("phone2");
+ field.max_length = 3;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("phone2")));
+
+ field.label = string16();
+ field.name = ASCIIToUTF16("phone3");
+ field.max_length = 4;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("phone3")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("phone1")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_CITY_CODE, field_type_map_[ASCIIToUTF16("phone1")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("phone2")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("phone2")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("phone3")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_NUMBER, field_type_map_[ASCIIToUTF16("phone3")]);
+}
+
+TEST_F(PhoneFieldTest, CountryAndCityAndPhoneNumber) {
+ // Phone in format <country code>:3 - <city and number>:10
+ // The |maxlength| is considered, otherwise it's too broad.
+ FormFieldData field;
+ field.form_control_type = "text";
+
+ field.label = ASCIIToUTF16("Phone Number");
+ field.name = ASCIIToUTF16("CountryCode");
+ field.max_length = 3;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("country")));
+
+ field.label = ASCIIToUTF16("Phone Number");
+ field.name = ASCIIToUTF16("PhoneNumber");
+ field.max_length = 10;
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("phone")));
+
+ AutofillScanner scanner(list_.get());
+ field_.reset(Parse(&scanner));
+ ASSERT_NE(static_cast<PhoneField*>(NULL), field_.get());
+ ASSERT_TRUE(field_->ClassifyField(&field_type_map_));
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("country")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_COUNTRY_CODE, field_type_map_[ASCIIToUTF16("country")]);
+ ASSERT_TRUE(
+ field_type_map_.find(ASCIIToUTF16("phone")) != field_type_map_.end());
+ EXPECT_EQ(PHONE_HOME_CITY_AND_NUMBER, field_type_map_[ASCIIToUTF16("phone")]);
+}
diff --git a/components/autofill/browser/phone_number.cc b/components/autofill/browser/phone_number.cc
new file mode 100644
index 0000000..0a785e5
--- /dev/null
+++ b/components/autofill/browser/phone_number.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2011 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/browser/phone_number.h"
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+
+namespace {
+
+const char16 kPhoneNumberSeparators[] = { ' ', '.', '(', ')', '-', 0 };
+
+// The number of digits in a phone number.
+const size_t kPhoneNumberLength = 7;
+
+// The number of digits in an area code.
+const size_t kPhoneCityCodeLength = 3;
+
+void StripPunctuation(string16* number) {
+ RemoveChars(*number, kPhoneNumberSeparators, number);
+}
+
+// Returns the region code for this phone number, which is an ISO 3166 2-letter
+// country code. The returned value is based on the |profile|; if the |profile|
+// does not have a country code associated with it, falls back to the country
+// code corresponding to the |app_locale|.
+std::string GetRegion(const AutofillProfile& profile,
+ const std::string& app_locale) {
+ std::string country_code = profile.CountryCode();
+ if (!country_code.empty())
+ return country_code;
+
+ return AutofillCountry::CountryCodeForLocale(app_locale);
+}
+
+} // namespace
+
+PhoneNumber::PhoneNumber(AutofillProfile* profile)
+ : profile_(profile) {
+}
+
+PhoneNumber::PhoneNumber(const PhoneNumber& number)
+ : profile_(NULL) {
+ *this = number;
+}
+
+PhoneNumber::~PhoneNumber() {}
+
+PhoneNumber& PhoneNumber::operator=(const PhoneNumber& number) {
+ if (this == &number)
+ return *this;
+
+ number_ = number.number_;
+ profile_ = number.profile_;
+ cached_parsed_phone_ = number.cached_parsed_phone_;
+ return *this;
+}
+
+void PhoneNumber::GetSupportedTypes(FieldTypeSet* supported_types) const {
+ supported_types->insert(PHONE_HOME_WHOLE_NUMBER);
+ supported_types->insert(PHONE_HOME_NUMBER);
+ supported_types->insert(PHONE_HOME_CITY_CODE);
+ supported_types->insert(PHONE_HOME_CITY_AND_NUMBER);
+ supported_types->insert(PHONE_HOME_COUNTRY_CODE);
+}
+
+string16 PhoneNumber::GetRawInfo(AutofillFieldType type) const {
+ if (type == PHONE_HOME_WHOLE_NUMBER)
+ return number_;
+
+ // Only the whole number is available as raw data. All of the other types are
+ // parsed from this raw info, and parsing requires knowledge of the phone
+ // number's region, which is only available via GetInfo().
+ return string16();
+}
+
+void PhoneNumber::SetRawInfo(AutofillFieldType type, const string16& value) {
+ if (type != PHONE_HOME_CITY_AND_NUMBER &&
+ type != PHONE_HOME_WHOLE_NUMBER) {
+ // Only full phone numbers should be set directly. The remaining field
+ // field types are read-only.
+ return;
+ }
+
+ number_ = value;
+
+ // Invalidate the cached number.
+ cached_parsed_phone_ = autofill_i18n::PhoneObject();
+}
+
+// Normalize phones if |type| is a whole number:
+// (650)2345678 -> 6502345678
+// 1-800-FLOWERS -> 18003569377
+// If the phone cannot be normalized, returns the stored value verbatim.
+string16 PhoneNumber::GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const {
+ UpdateCacheIfNeeded(app_locale);
+
+ // Queries for whole numbers will return the non-normalized number if
+ // normalization for the number fails. All other field types require
+ // normalization.
+ if (type != PHONE_HOME_WHOLE_NUMBER && !cached_parsed_phone_.IsValidNumber())
+ return string16();
+
+ switch (type) {
+ case PHONE_HOME_WHOLE_NUMBER:
+ return cached_parsed_phone_.GetWholeNumber();
+
+ case PHONE_HOME_NUMBER:
+ return cached_parsed_phone_.number();
+
+ case PHONE_HOME_CITY_CODE:
+ return cached_parsed_phone_.city_code();
+
+ case PHONE_HOME_COUNTRY_CODE:
+ return cached_parsed_phone_.country_code();
+
+ case PHONE_HOME_CITY_AND_NUMBER:
+ return
+ cached_parsed_phone_.city_code() + cached_parsed_phone_.number();
+
+ default:
+ NOTREACHED();
+ return string16();
+ }
+}
+
+bool PhoneNumber::SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) {
+ SetRawInfo(type, value);
+
+ if (number_.empty())
+ return true;
+
+ // Store a formatted (i.e., pretty printed) version of the number.
+ UpdateCacheIfNeeded(app_locale);
+ number_ = cached_parsed_phone_.GetFormattedNumber();
+ return !number_.empty();
+}
+
+void PhoneNumber::GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const {
+ string16 stripped_text = text;
+ StripPunctuation(&stripped_text);
+ FormGroup::GetMatchingTypes(stripped_text, app_locale, matching_types);
+
+ // For US numbers, also compare to the three-digit prefix and the four-digit
+ // suffix, since web sites often split numbers into these two fields.
+ string16 number = GetInfo(PHONE_HOME_NUMBER, app_locale);
+ if (GetRegion(*profile_, app_locale) == "US" &&
+ number.size() == (kPrefixLength + kSuffixLength)) {
+ string16 prefix = number.substr(kPrefixOffset, kPrefixLength);
+ string16 suffix = number.substr(kSuffixOffset, kSuffixLength);
+ if (text == prefix || text == suffix)
+ matching_types->insert(PHONE_HOME_NUMBER);
+ }
+
+ string16 whole_number = GetInfo(PHONE_HOME_WHOLE_NUMBER, app_locale);
+ if (!whole_number.empty()) {
+ string16 normalized_number =
+ autofill_i18n::NormalizePhoneNumber(text,
+ GetRegion(*profile_, app_locale));
+ if (normalized_number == whole_number)
+ matching_types->insert(PHONE_HOME_WHOLE_NUMBER);
+ }
+}
+
+void PhoneNumber::UpdateCacheIfNeeded(const std::string& app_locale) const {
+ std::string region = GetRegion(*profile_, app_locale);
+ if (!number_.empty() && cached_parsed_phone_.region() != region)
+ cached_parsed_phone_ = autofill_i18n::PhoneObject(number_, region);
+}
+
+PhoneNumber::PhoneCombineHelper::PhoneCombineHelper() {
+}
+
+PhoneNumber::PhoneCombineHelper::~PhoneCombineHelper() {
+}
+
+bool PhoneNumber::PhoneCombineHelper::SetInfo(AutofillFieldType field_type,
+ const string16& value) {
+ if (field_type == PHONE_HOME_COUNTRY_CODE) {
+ country_ = value;
+ return true;
+ }
+
+ if (field_type == PHONE_HOME_CITY_CODE) {
+ city_ = value;
+ return true;
+ }
+
+ if (field_type == PHONE_HOME_CITY_AND_NUMBER) {
+ phone_ = value;
+ return true;
+ }
+
+ if (field_type == PHONE_HOME_WHOLE_NUMBER) {
+ whole_number_ = value;
+ return true;
+ }
+
+ if (field_type == PHONE_HOME_NUMBER) {
+ phone_.append(value);
+ return true;
+ }
+
+ return false;
+}
+
+bool PhoneNumber::PhoneCombineHelper::ParseNumber(
+ const AutofillProfile& profile,
+ const std::string& app_locale,
+ string16* value) {
+ if (IsEmpty())
+ return false;
+
+ if (!whole_number_.empty()) {
+ *value = whole_number_;
+ return true;
+ }
+
+ return autofill_i18n::ConstructPhoneNumber(
+ country_, city_, phone_, GetRegion(profile, app_locale), value);
+}
+
+bool PhoneNumber::PhoneCombineHelper::IsEmpty() const {
+ return phone_.empty() && whole_number_.empty();
+}
diff --git a/components/autofill/browser/phone_number.h b/components/autofill/browser/phone_number.h
new file mode 100644
index 0000000..79b780d
--- /dev/null
+++ b/components/autofill/browser/phone_number.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2011 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_BROWSER_PHONE_NUMBER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PHONE_NUMBER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/string16.h"
+#include "components/autofill/browser/autofill_type.h"
+#include "components/autofill/browser/form_group.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+
+class AutofillProfile;
+
+// A form group that stores phone number information.
+class PhoneNumber : public FormGroup {
+ public:
+ explicit PhoneNumber(AutofillProfile* profile);
+ PhoneNumber(const PhoneNumber& number);
+ virtual ~PhoneNumber();
+
+ PhoneNumber& operator=(const PhoneNumber& number);
+
+ void set_profile(AutofillProfile* profile) { profile_ = profile; }
+
+ // FormGroup implementation:
+ virtual void GetMatchingTypes(const string16& text,
+ const std::string& app_locale,
+ FieldTypeSet* matching_types) const OVERRIDE;
+ virtual string16 GetRawInfo(AutofillFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(AutofillFieldType type,
+ const string16& value) OVERRIDE;
+ virtual string16 GetInfo(AutofillFieldType type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(AutofillFieldType type,
+ const string16& value,
+ const std::string& app_locale) OVERRIDE;
+
+ // Size and offset of the prefix and suffix portions of phone numbers.
+ static const size_t kPrefixOffset = 0;
+ static const size_t kPrefixLength = 3;
+ static const size_t kSuffixOffset = 3;
+ static const size_t kSuffixLength = 4;
+
+ // The class used to combine home phone parts into a whole number.
+ class PhoneCombineHelper {
+ public:
+ PhoneCombineHelper();
+ ~PhoneCombineHelper();
+
+ // If |type| is a phone field type, saves the |value| accordingly and
+ // returns true. For all other field types returs false.
+ bool SetInfo(AutofillFieldType type, const string16& value);
+
+ // Parses the number built up from pieces stored via SetInfo() according to
+ // the specified |profile|'s country code, falling back to the given
+ // |app_locale| if the |profile| has no associated country code. Returns
+ // true if parsing was successful, false otherwise.
+ bool ParseNumber(const AutofillProfile& profile,
+ const std::string& app_locale,
+ string16* value);
+
+ // Returns true if both |phone_| and |whole_number_| are empty.
+ bool IsEmpty() const;
+
+ private:
+ string16 country_;
+ string16 city_;
+ string16 phone_;
+ string16 whole_number_;
+ };
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(FieldTypeSet* supported_types) const OVERRIDE;
+
+ // Updates the cached parsed number if the profile's region has changed
+ // since the last time the cache was updated.
+ void UpdateCacheIfNeeded(const std::string& app_locale) const;
+
+ // The phone number.
+ string16 number_;
+ // Profile which stores the region used as hint when normalizing the number.
+ const AutofillProfile* profile_; // WEAK
+
+ // Cached number.
+ mutable autofill_i18n::PhoneObject cached_parsed_phone_;
+};
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PHONE_NUMBER_H_
diff --git a/components/autofill/browser/phone_number_i18n.cc b/components/autofill/browser/phone_number_i18n.cc
new file mode 100644
index 0000000..0f71deb
--- /dev/null
+++ b/components/autofill/browser/phone_number_i18n.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2011 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/browser/phone_number_i18n.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_country.h"
+#include "third_party/libphonenumber/src/phonenumber_api.h"
+
+using i18n::phonenumbers::PhoneNumber;
+using i18n::phonenumbers::PhoneNumberUtil;
+
+namespace {
+
+std::string SanitizeRegion(const std::string& region) {
+ if (region.length() == 2)
+ return region;
+
+ return AutofillCountry::CountryCodeForLocale(
+ AutofillCountry::ApplicationLocale());
+}
+
+// Returns true if |phone_number| is valid.
+bool IsValidPhoneNumber(const PhoneNumber& phone_number) {
+ PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
+ if (!phone_util->IsPossibleNumber(phone_number))
+ return false;
+
+ // Verify that the number has a valid area code (that in some cases could be
+ // empty) for the parsed country code. Also verify that this is a valid
+ // number (for example, in the US 1234567 is not valid, because numbers do not
+ // start with 1).
+ if (!phone_util->IsValidNumber(phone_number))
+ return false;
+
+ return true;
+}
+
+// Formats the given |number| as a human-readable string, and writes the result
+// into |formatted_number|. Also, normalizes the formatted number, and writes
+// that result into |normalized_number|. This function should only be called
+// with numbers already known to be valid, i.e. validation should be done prior
+// to calling this function. Note that the |country_code|, which determines
+// whether to format in the national or in the international format, is passed
+// in explicitly, as |number| might have an implicit country code set, even
+// though the original input lacked a country code.
+void FormatValidatedNumber(const PhoneNumber& number,
+ const string16& country_code,
+ string16* formatted_number,
+ string16* normalized_number) {
+ PhoneNumberUtil::PhoneNumberFormat format =
+ country_code.empty() ?
+ PhoneNumberUtil::NATIONAL :
+ PhoneNumberUtil::INTERNATIONAL;
+
+ PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
+ std::string processed_number;
+ phone_util->Format(number, format, &processed_number);
+
+ if (formatted_number)
+ *formatted_number = UTF8ToUTF16(processed_number);
+
+ if (normalized_number) {
+ phone_util->NormalizeDigitsOnly(&processed_number);
+ *normalized_number = UTF8ToUTF16(processed_number);
+ }
+}
+
+} // namespace
+
+namespace autofill_i18n {
+
+// Parses the number stored in |value| as it should be interpreted in the given
+// |region|, and stores the results into the remaining arguments. The |region|
+// should be sanitized prior to calling this function.
+bool ParsePhoneNumber(const string16& value,
+ const std::string& region,
+ string16* country_code,
+ string16* city_code,
+ string16* number,
+ PhoneNumber* i18n_number) {
+ country_code->clear();
+ city_code->clear();
+ number->clear();
+ *i18n_number = PhoneNumber();
+
+ std::string number_text(UTF16ToUTF8(value));
+
+ // Parse phone number based on the region.
+ PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
+
+ // The |region| should already be sanitized.
+ DCHECK_EQ(2U, region.size());
+ if (phone_util->Parse(number_text, region.c_str(), i18n_number) !=
+ PhoneNumberUtil::NO_PARSING_ERROR) {
+ return false;
+ }
+
+ if (!IsValidPhoneNumber(*i18n_number))
+ return false;
+
+ std::string national_significant_number;
+ phone_util->GetNationalSignificantNumber(*i18n_number,
+ &national_significant_number);
+
+ int area_length = phone_util->GetLengthOfGeographicalAreaCode(*i18n_number);
+ int destination_length =
+ phone_util->GetLengthOfNationalDestinationCode(*i18n_number);
+ // Some phones have a destination code in lieu of area code: mobile operators
+ // in Europe, toll and toll-free numbers in USA, etc. From our point of view
+ // these two types of codes are the same.
+ if (destination_length > area_length)
+ area_length = destination_length;
+
+ std::string area_code;
+ std::string subscriber_number;
+ if (area_length > 0) {
+ area_code = national_significant_number.substr(0, area_length);
+ subscriber_number = national_significant_number.substr(area_length);
+ } else {
+ subscriber_number = national_significant_number;
+ }
+ *number = UTF8ToUTF16(subscriber_number);
+ *city_code = UTF8ToUTF16(area_code);
+ *country_code = string16();
+
+ phone_util->NormalizeDigitsOnly(&number_text);
+ string16 normalized_number(UTF8ToUTF16(number_text));
+
+ // Check if parsed number has a country code that was not inferred from the
+ // region.
+ if (i18n_number->has_country_code()) {
+ *country_code = UTF8ToUTF16(
+ base::StringPrintf("%d", i18n_number->country_code()));
+ if (normalized_number.length() <= national_significant_number.length() &&
+ !StartsWith(normalized_number, *country_code,
+ true /* case_sensitive */)) {
+ country_code->clear();
+ }
+ }
+
+ return true;
+}
+
+string16 NormalizePhoneNumber(const string16& value,
+ std::string const& region) {
+ string16 country_code;
+ string16 unused_city_code;
+ string16 unused_number;
+ PhoneNumber phone_number;
+ if (!ParsePhoneNumber(value, SanitizeRegion(region), &country_code,
+ &unused_city_code, &unused_number, &phone_number)) {
+ return string16(); // Parsing failed - do not store phone.
+ }
+
+ string16 normalized_number;
+ FormatValidatedNumber(phone_number, country_code, NULL, &normalized_number);
+ return normalized_number;
+}
+
+bool ConstructPhoneNumber(const string16& country_code,
+ const string16& city_code,
+ const string16& number,
+ const std::string& region,
+ string16* whole_number) {
+ whole_number->clear();
+
+ string16 unused_country_code;
+ string16 unused_city_code;
+ string16 unused_number;
+ PhoneNumber phone_number;
+ if (!ParsePhoneNumber(country_code + city_code + number,
+ SanitizeRegion(region),
+ &unused_country_code, &unused_city_code, &unused_number,
+ &phone_number)) {
+ return false;
+ }
+
+ FormatValidatedNumber(phone_number, country_code, whole_number, NULL);
+ return true;
+}
+
+bool PhoneNumbersMatch(const string16& number_a,
+ const string16& number_b,
+ const std::string& raw_region) {
+ // Sanitize the provided |raw_region| before trying to use it for parsing.
+ const std::string region = SanitizeRegion(raw_region);
+
+ PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
+
+ // Parse phone numbers based on the region
+ PhoneNumber i18n_number1;
+ if (phone_util->Parse(UTF16ToUTF8(number_a), region.c_str(), &i18n_number1) !=
+ PhoneNumberUtil::NO_PARSING_ERROR) {
+ return false;
+ }
+
+ PhoneNumber i18n_number2;
+ if (phone_util->Parse(UTF16ToUTF8(number_b), region.c_str(), &i18n_number2) !=
+ PhoneNumberUtil::NO_PARSING_ERROR) {
+ return false;
+ }
+
+ switch (phone_util->IsNumberMatch(i18n_number1, i18n_number2)) {
+ case PhoneNumberUtil::INVALID_NUMBER:
+ case PhoneNumberUtil::NO_MATCH:
+ return false;
+ case PhoneNumberUtil::SHORT_NSN_MATCH:
+ return false;
+ case PhoneNumberUtil::NSN_MATCH:
+ case PhoneNumberUtil::EXACT_MATCH:
+ return true;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+PhoneObject::PhoneObject(const string16& number, const std::string& region)
+ : region_(SanitizeRegion(region)),
+ i18n_number_(NULL) {
+ // TODO(isherman): Autofill profiles should always have a |region| set, but in
+ // some cases it should be marked as implicit. Otherwise, phone numbers
+ // might behave differently when they are synced across computers:
+ // [ http://crbug.com/100845 ]. Once the bug is fixed, add a DCHECK here to
+ // verify.
+
+ scoped_ptr<PhoneNumber> i18n_number(new PhoneNumber);
+ if (ParsePhoneNumber(number, region_, &country_code_, &city_code_, &number_,
+ i18n_number.get())) {
+ // The phone number was successfully parsed, so store the parsed version.
+ // The formatted and normalized versions will be set on the first call to
+ // the coresponding methods.
+ i18n_number_.reset(i18n_number.release());
+ } else {
+ // Parsing failed. Store passed phone "as is" into |whole_number_|.
+ whole_number_ = number;
+ }
+}
+
+PhoneObject::PhoneObject(const PhoneObject& other) : i18n_number_(NULL) {
+ *this = other;
+}
+
+PhoneObject::PhoneObject() : i18n_number_(NULL) {
+}
+
+PhoneObject::~PhoneObject() {
+}
+
+string16 PhoneObject::GetFormattedNumber() const {
+ if (i18n_number_ && formatted_number_.empty()) {
+ FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
+ &whole_number_);
+ }
+
+ return formatted_number_;
+}
+
+string16 PhoneObject::GetWholeNumber() const {
+ if (i18n_number_ && whole_number_.empty()) {
+ FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
+ &whole_number_);
+ }
+
+ return whole_number_;
+}
+
+PhoneObject& PhoneObject::operator=(const PhoneObject& other) {
+ if (this == &other)
+ return *this;
+
+ region_ = other.region_;
+
+ if (other.i18n_number_.get())
+ i18n_number_.reset(new PhoneNumber(*other.i18n_number_));
+ else
+ i18n_number_.reset();
+
+ country_code_ = other.country_code_;
+ city_code_ = other.city_code_;
+ number_ = other.number_;
+
+ formatted_number_ = other.formatted_number_;
+ whole_number_ = other.whole_number_;
+
+ return *this;
+}
+
+} // namespace autofill_i18n
diff --git a/components/autofill/browser/phone_number_i18n.h b/components/autofill/browser/phone_number_i18n.h
new file mode 100644
index 0000000..3a35c3b
--- /dev/null
+++ b/components/autofill/browser/phone_number_i18n.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2011 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_BROWSER_PHONE_NUMBER_I18N_H_
+#define COMPONENTS_AUTOFILL_BROWSER_PHONE_NUMBER_I18N_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+
+namespace i18n {
+namespace phonenumbers {
+class PhoneNumber;
+}
+}
+
+// Utilities to process, normalize and compare international phone numbers.
+namespace autofill_i18n {
+
+// Most of the following functions require |region| to operate. The |region| is
+// a ISO 3166 standard code ("US" for USA, "CZ" for Czech Republic, etc.).
+
+// Parses the number stored in |value| as a phone number interpreted in the
+// given |region|, and stores the results into the remaining arguments. The
+// |region| should be a 2-letter country code. This is an internal function,
+// exposed in the header file so that it can be tested.
+bool ParsePhoneNumber(
+ const string16& value,
+ const std::string& region,
+ string16* country_code,
+ string16* city_code,
+ string16* number,
+ i18n::phonenumbers::PhoneNumber* i18n_number) WARN_UNUSED_RESULT;
+
+// Normalizes phone number, by changing digits in the extended fonts
+// (such as \xFF1x) into '0'-'9'. Also strips out non-digit characters.
+string16 NormalizePhoneNumber(const string16& value,
+ const std::string& region);
+
+// Constructs whole phone number from parts.
+// |city_code| - area code, could be empty.
+// |country_code| - country code, could be empty.
+// |number| - local number, should not be empty.
+// |region| - current region, the parsing is based on.
+// |whole_number| - constructed whole number.
+// Separator characters are stripped before parsing the digits.
+// Returns true if parsing was successful, false otherwise.
+bool ConstructPhoneNumber(const string16& country_code,
+ const string16& city_code,
+ const string16& number,
+ const std::string& region,
+ string16* whole_number) WARN_UNUSED_RESULT;
+
+// Returns true if |number_a| and |number_b| parse to the same phone number in
+// the given |region|.
+bool PhoneNumbersMatch(const string16& number_a,
+ const string16& number_b,
+ const std::string& region);
+
+// The cached phone number, does parsing only once, improves performance.
+class PhoneObject {
+ public:
+ PhoneObject(const string16& number, const std::string& region);
+ PhoneObject(const PhoneObject&);
+ PhoneObject();
+ ~PhoneObject();
+
+ std::string region() const { return region_; }
+
+ string16 country_code() const { return country_code_; }
+ string16 city_code() const { return city_code_; }
+ string16 number() const { return number_; }
+
+ string16 GetFormattedNumber() const;
+ string16 GetWholeNumber() const;
+
+ PhoneObject& operator=(const PhoneObject& other);
+
+ bool IsValidNumber() const { return i18n_number_ != NULL; }
+
+ private:
+ // The region code used to parse this number.
+ std::string region_;
+
+ // The parsed number and its components.
+ scoped_ptr<i18n::phonenumbers::PhoneNumber> i18n_number_;
+ string16 city_code_;
+ string16 country_code_;
+ string16 number_;
+
+ // Pretty printed version of the whole number, or empty if parsing failed.
+ // Set on first request.
+ mutable string16 formatted_number_;
+
+ // The whole number, normalized to contain only digits if possible.
+ // Set on first request.
+ mutable string16 whole_number_;
+};
+
+} // namespace autofill_i18n
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_PHONE_NUMBER_I18N_H_
diff --git a/components/autofill/browser/phone_number_i18n_unittest.cc b/components/autofill/browser/phone_number_i18n_unittest.cc
new file mode 100644
index 0000000..8e25520
--- /dev/null
+++ b/components/autofill/browser/phone_number_i18n_unittest.cc
@@ -0,0 +1,372 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libphonenumber/src/phonenumber_api.h"
+
+using autofill_i18n::NormalizePhoneNumber;
+using autofill_i18n::ParsePhoneNumber;
+using autofill_i18n::ConstructPhoneNumber;
+using autofill_i18n::PhoneNumbersMatch;
+using content::BrowserThread;
+
+TEST(PhoneNumberI18NTest, NormalizePhoneNumber) {
+ // "Large" digits.
+ string16 phone1(UTF8ToUTF16("\xEF\xBC\x91\xEF\xBC\x96\xEF\xBC\x95\xEF\xBC\x90"
+ "\xEF\xBC\x97\xEF\xBC\x94\xEF\xBC\x99\xEF\xBC\x98"
+ "\xEF\xBC\x93\xEF\xBC\x92\xEF\xBC\x93"));
+ EXPECT_EQ(NormalizePhoneNumber(phone1, "US"), ASCIIToUTF16("16507498323"));
+
+ // Devanagari script digits.
+ string16 phone2(UTF8ToUTF16("\xD9\xA1\xD9\xA6\xD9\xA5\xD9\xA0\xD9\xA8\xD9\xA3"
+ "\xD9\xA2\xD9\xA3\xD9\xA7\xD9\xA4\xD9\xA9"));
+ EXPECT_EQ(NormalizePhoneNumber(phone2, "US"), ASCIIToUTF16("16508323749"));
+
+ string16 phone3(UTF8ToUTF16("16503334\xef\xbc\x92\x35\xd9\xa5"));
+ EXPECT_EQ(NormalizePhoneNumber(phone3, "US"), ASCIIToUTF16("16503334255"));
+
+ string16 phone4(UTF8ToUTF16("+1(650)2346789"));
+ EXPECT_EQ(NormalizePhoneNumber(phone4, "US"), ASCIIToUTF16("16502346789"));
+
+ string16 phone5(UTF8ToUTF16("6502346789"));
+ EXPECT_EQ(NormalizePhoneNumber(phone5, "US"), ASCIIToUTF16("6502346789"));
+}
+
+TEST(PhoneNumberI18NTest, ParsePhoneNumber) {
+ string16 number;
+ string16 city_code;
+ string16 country_code;
+ i18n::phonenumbers::PhoneNumber unused_i18n_number;
+
+ // Test for empty string. Should give back empty strings.
+ string16 phone0;
+ EXPECT_FALSE(ParsePhoneNumber(phone0, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // Test for string with less than 7 digits. Should give back empty strings.
+ string16 phone1(ASCIIToUTF16("1234"));
+ EXPECT_FALSE(ParsePhoneNumber(phone1, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // Test for string with exactly 7 digits.
+ // Not a valid number - starts with 1
+ string16 phone2(ASCIIToUTF16("1234567"));
+ EXPECT_FALSE(ParsePhoneNumber(phone2, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // Not a valid number - does not have area code.
+ string16 phone3(ASCIIToUTF16("2234567"));
+ EXPECT_FALSE(ParsePhoneNumber(phone3, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // Test for string with greater than 7 digits but less than 10 digits.
+ // Should fail parsing in US.
+ string16 phone4(ASCIIToUTF16("123456789"));
+ EXPECT_FALSE(ParsePhoneNumber(phone4, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // Test for string with greater than 7 digits but less than 10 digits and
+ // separators.
+ // Should fail parsing in US.
+ string16 phone_separator4(ASCIIToUTF16("12.345-6789"));
+ EXPECT_FALSE(ParsePhoneNumber(phone_separator4, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // 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.
+ string16 phone5(ASCIIToUTF16("1234567890"));
+ EXPECT_FALSE(ParsePhoneNumber(phone5, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ string16 phone6(ASCIIToUTF16("6501567890"));
+ // This one going to fail because of the incorrect number (starts with 1).
+ EXPECT_FALSE(ParsePhoneNumber(phone6, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ string16 phone7(ASCIIToUTF16("6504567890"));
+ EXPECT_TRUE(ParsePhoneNumber(phone7, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("4567890"), number);
+ EXPECT_EQ(ASCIIToUTF16("650"), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // Test for string with exactly 10 digits and separators.
+ // Should give back phone number and city code.
+ string16 phone_separator7(ASCIIToUTF16("(650) 456-7890"));
+ EXPECT_TRUE(ParsePhoneNumber(phone_separator7, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("4567890"), number);
+ EXPECT_EQ(ASCIIToUTF16("650"), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // 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.
+ string16 phone8(ASCIIToUTF16("0116504567890"));
+ EXPECT_FALSE(ParsePhoneNumber(phone8, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(string16(), number);
+ EXPECT_EQ(string16(), city_code);
+ EXPECT_EQ(string16(), country_code);
+
+ // 011 is a correct "dial out" prefix in the USA - the parsing should succeed.
+ string16 phone9(ASCIIToUTF16("01116504567890"));
+ EXPECT_TRUE(ParsePhoneNumber(phone9, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("4567890"), number);
+ EXPECT_EQ(ASCIIToUTF16("650"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("1"), country_code);
+
+ // 011 is a correct "dial out" prefix in the USA - the parsing should succeed.
+ string16 phone10(ASCIIToUTF16("01178124567890"));
+ EXPECT_TRUE(ParsePhoneNumber(phone10, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("4567890"), number);
+ EXPECT_EQ(ASCIIToUTF16("812"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("7"), country_code);
+
+ // 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.
+ string16 phone11(ASCIIToUTF16("(0111) 650-456.7890"));
+ EXPECT_TRUE(ParsePhoneNumber(phone11, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("4567890"), number);
+ EXPECT_EQ(ASCIIToUTF16("650"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("1"), country_code);
+
+ // Now try phone from Chech republic - it has 00 dial out code, 420 country
+ // code and variable length area codes.
+ string16 phone12(ASCIIToUTF16("+420 27-89.10.112"));
+ EXPECT_TRUE(ParsePhoneNumber(phone12, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("910112"), number);
+ EXPECT_EQ(ASCIIToUTF16("278"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("420"), country_code);
+
+ EXPECT_TRUE(ParsePhoneNumber(phone12, "CZ",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("910112"), number);
+ EXPECT_EQ(ASCIIToUTF16("278"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("420"), country_code);
+
+ string16 phone13(ASCIIToUTF16("420 57-89.10.112"));
+ EXPECT_FALSE(ParsePhoneNumber(phone13, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_TRUE(ParsePhoneNumber(phone13, "CZ",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("910112"), number);
+ EXPECT_EQ(ASCIIToUTF16("578"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("420"), country_code);
+
+ string16 phone14(ASCIIToUTF16("1-650-FLOWERS"));
+ EXPECT_TRUE(ParsePhoneNumber(phone14, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("3569377"), number);
+ EXPECT_EQ(ASCIIToUTF16("650"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("1"), country_code);
+
+ // 800 is not an area code, but the destination code. In our library these
+ // codes should be treated the same as area codes.
+ string16 phone15(ASCIIToUTF16("1-800-FLOWERS"));
+ EXPECT_TRUE(ParsePhoneNumber(phone15, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(ASCIIToUTF16("3569377"), number);
+ EXPECT_EQ(ASCIIToUTF16("800"), city_code);
+ EXPECT_EQ(ASCIIToUTF16("1"), country_code);
+}
+
+TEST(PhoneNumberI18NTest, ConstructPhoneNumber) {
+ string16 number;
+ EXPECT_TRUE(ConstructPhoneNumber(ASCIIToUTF16("1"),
+ ASCIIToUTF16("650"),
+ ASCIIToUTF16("2345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("+1 650-234-5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(string16(),
+ ASCIIToUTF16("650"),
+ ASCIIToUTF16("2345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("(650) 234-5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(ASCIIToUTF16("1"),
+ string16(),
+ ASCIIToUTF16("6502345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("+1 650-234-5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(string16(),
+ string16(),
+ ASCIIToUTF16("6502345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("(650) 234-5678"));
+
+ EXPECT_FALSE(ConstructPhoneNumber(string16(),
+ ASCIIToUTF16("650"),
+ ASCIIToUTF16("234567890"),
+ "US",
+ &number));
+ EXPECT_EQ(number, string16());
+ // Italian number
+ EXPECT_TRUE(ConstructPhoneNumber(ASCIIToUTF16("39"),
+ ASCIIToUTF16("347"),
+ ASCIIToUTF16("2345678"),
+ "IT",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("+39 347 234 5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(string16(),
+ ASCIIToUTF16("347"),
+ ASCIIToUTF16("2345678"),
+ "IT",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("347 234 5678"));
+ // German number.
+ EXPECT_TRUE(ConstructPhoneNumber(ASCIIToUTF16("49"),
+ ASCIIToUTF16("024"),
+ ASCIIToUTF16("2345678901"),
+ "DE",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("+49 2423/45678901"));
+ EXPECT_TRUE(ConstructPhoneNumber(string16(),
+ ASCIIToUTF16("024"),
+ ASCIIToUTF16("2345678901"),
+ "DE",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("02423/45678901"));
+}
+
+TEST(PhoneNumberI18NTest, PhoneNumbersMatch) {
+ // Same numbers, defined country code.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("4158889999"),
+ "US"));
+ // Same numbers, undefined country code.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("4158889999"),
+ ""));
+
+ // Numbers differ by country code only.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("14158889999"),
+ ASCIIToUTF16("4158889999"),
+ "US"));
+
+ // Same numbers, different formats.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("415-888-9999"),
+ "US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("(415)888-9999"),
+ "US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("415 888 9999"),
+ "US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("415 TUV WXYZ"),
+ "US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("1(415)888-99-99"),
+ ASCIIToUTF16("+14158889999"),
+ "US"));
+
+ // Partial matches don't count.
+ EXPECT_FALSE(PhoneNumbersMatch(ASCIIToUTF16("14158889999"),
+ ASCIIToUTF16("8889999"),
+ "US"));
+
+ // Different numbers don't match.
+ EXPECT_FALSE(PhoneNumbersMatch(ASCIIToUTF16("14158889999"),
+ ASCIIToUTF16("1415888"),
+ "US"));
+}
diff --git a/components/autofill/browser/phone_number_unittest.cc b/components/autofill/browser/phone_number_unittest.cc
new file mode 100644
index 0000000..6772e75
--- /dev/null
+++ b/components/autofill/browser/phone_number_unittest.cc
@@ -0,0 +1,203 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_profile.h"
+#include "components/autofill/browser/field_types.h"
+#include "components/autofill/browser/phone_number.h"
+#include "components/autofill/browser/phone_number_i18n.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PhoneNumberTest, Matcher) {
+ AutofillProfile profile;
+ profile.SetCountryCode("US");
+ // Set phone number so country_code == 1, city_code = 650, number = 2345678.
+ string16 phone(ASCIIToUTF16("1 [650] 234-5678"));
+ PhoneNumber phone_number(&profile);
+ phone_number.SetInfo(PHONE_HOME_WHOLE_NUMBER, phone, "US");
+
+ FieldTypeSet matching_types;
+ phone_number.GetMatchingTypes(string16(), "US", &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(EMPTY_TYPE) != matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("1"), "US", &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_COUNTRY_CODE) !=
+ matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("16"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ phone_number.GetMatchingTypes(ASCIIToUTF16("165"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ phone_number.GetMatchingTypes(ASCIIToUTF16("1650"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ phone_number.GetMatchingTypes(ASCIIToUTF16("16502"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ phone_number.GetMatchingTypes(ASCIIToUTF16("165023"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ phone_number.GetMatchingTypes(ASCIIToUTF16("1650234"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("16502345678"), "US",
+ &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_WHOLE_NUMBER) !=
+ matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("650"), "US", &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_CITY_CODE) !=
+ matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("2345678"), "US", &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_NUMBER) != matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("234"), "US", &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_NUMBER) != matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("5678"), "US", &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_NUMBER) != matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("2345"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("6502345678"), "US",
+ &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_CITY_AND_NUMBER) !=
+ matching_types.end());
+ matching_types.clear();
+ phone_number.GetMatchingTypes(ASCIIToUTF16("(650)2345678"), "US",
+ &matching_types);
+ EXPECT_EQ(1U, matching_types.size());
+ EXPECT_TRUE(matching_types.find(PHONE_HOME_CITY_AND_NUMBER) !=
+ matching_types.end());
+}
+
+// Verify that PhoneNumber::SetInfo() correctly formats the incoming number.
+TEST(PhoneNumberTest, SetInfo) {
+ AutofillProfile profile;
+ profile.SetCountryCode("US");
+
+ PhoneNumber phone(&profile);
+ EXPECT_EQ(string16(), phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ // Set the formatted info directly.
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("(650) 234-5678"), "US"));
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5678"),
+ phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ // Unformatted numbers should be formatted.
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("8887776666"), "US"));
+ EXPECT_EQ(ASCIIToUTF16("(888) 777-6666"),
+ phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ // Differently formatted numbers should be re-formatted.
+ EXPECT_TRUE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("800-432-8765"), "US"));
+ EXPECT_EQ(ASCIIToUTF16("(800) 432-8765"),
+ phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ // Invalid numbers should not be stored. In the US, phone numbers cannot
+ // start with the digit '1'.
+ EXPECT_FALSE(phone.SetInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("650111111"), "US"));
+ EXPECT_EQ(string16(), phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+}
+
+// Test that cached phone numbers are correctly invalidated and updated.
+TEST(PhoneNumberTest, UpdateCachedPhoneNumber) {
+ AutofillProfile profile;
+ profile.SetCountryCode("US");
+
+ PhoneNumber phone(&profile);
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("6502345678"));
+ EXPECT_EQ(ASCIIToUTF16("650"), phone.GetInfo(PHONE_HOME_CITY_CODE, "US"));
+
+ // Update the area code.
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("8322345678"));
+ EXPECT_EQ(ASCIIToUTF16("832"), phone.GetInfo(PHONE_HOME_CITY_CODE, "US"));
+
+ // Change the phone number to have a UK format, but try to parse with the
+ // wrong locale.
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("07023456789"));
+ EXPECT_EQ(string16(), phone.GetInfo(PHONE_HOME_CITY_CODE, "US"));
+
+ // Now try parsing using the correct locale. Note that the profile's country
+ // code should override the app locale, which is still set to "US".
+ profile.SetCountryCode("GB");
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("07023456789"));
+ EXPECT_EQ(ASCIIToUTF16("70"), phone.GetInfo(PHONE_HOME_CITY_CODE, "US"));
+}
+
+TEST(PhoneNumberTest, PhoneCombineHelper) {
+ AutofillProfile profile;
+ profile.SetCountryCode("US");
+
+ PhoneNumber::PhoneCombineHelper number1;
+ EXPECT_FALSE(number1.SetInfo(ADDRESS_BILLING_CITY,
+ ASCIIToUTF16("1")));
+ EXPECT_TRUE(number1.SetInfo(PHONE_HOME_COUNTRY_CODE,
+ ASCIIToUTF16("1")));
+ EXPECT_TRUE(number1.SetInfo(PHONE_HOME_CITY_CODE,
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number1.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("2345678")));
+ string16 parsed_phone;
+ EXPECT_TRUE(number1.ParseNumber(profile, "en-US", &parsed_phone));
+ // International format as it has a country code.
+ EXPECT_EQ(ASCIIToUTF16("+1 650-234-5678"), parsed_phone);
+
+ PhoneNumber::PhoneCombineHelper number3;
+ EXPECT_TRUE(number3.SetInfo(PHONE_HOME_CITY_CODE,
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number3.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("2345680")));
+ EXPECT_TRUE(number3.ParseNumber(profile, "en-US", &parsed_phone));
+ // National format as it does not have a country code.
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5680"), parsed_phone);
+
+ PhoneNumber::PhoneCombineHelper number4;
+ EXPECT_TRUE(number4.SetInfo(PHONE_HOME_CITY_CODE,
+ ASCIIToUTF16("123"))); // Incorrect city code.
+ EXPECT_TRUE(number4.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("2345680")));
+ EXPECT_FALSE(number4.ParseNumber(profile, "en-US", &parsed_phone));
+ EXPECT_EQ(string16(), parsed_phone);
+
+ PhoneNumber::PhoneCombineHelper number5;
+ EXPECT_TRUE(number5.SetInfo(PHONE_HOME_CITY_AND_NUMBER,
+ ASCIIToUTF16("6502345681")));
+ EXPECT_TRUE(number5.ParseNumber(profile, "en-US", &parsed_phone));
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5681"), parsed_phone);
+
+ PhoneNumber::PhoneCombineHelper number6;
+ EXPECT_TRUE(number6.SetInfo(PHONE_HOME_CITY_CODE,
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number6.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("234")));
+ EXPECT_TRUE(number6.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("5682")));
+ EXPECT_TRUE(number6.ParseNumber(profile, "en-US", &parsed_phone));
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5682"), parsed_phone);
+
+ // Ensure parsing is possible when falling back to detecting the country code
+ // based on the app locale.
+ PhoneNumber::PhoneCombineHelper number7;
+ EXPECT_TRUE(number7.SetInfo(PHONE_HOME_CITY_CODE,
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number7.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("234")));
+ EXPECT_TRUE(number7.SetInfo(PHONE_HOME_NUMBER,
+ ASCIIToUTF16("5682")));
+ EXPECT_TRUE(number7.ParseNumber(AutofillProfile(), "en-US", &parsed_phone));
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5682"), parsed_phone);
+}
diff --git a/components/autofill/browser/risk/fingerprint.cc b/components/autofill/browser/risk/fingerprint.cc
new file mode 100644
index 0000000..3e13e39
--- /dev/null
+++ b/components/autofill/browser/risk/fingerprint.cc
@@ -0,0 +1,422 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/risk/fingerprint.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/cpu.h"
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "base/sys_info.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/risk/proto/fingerprint.pb.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/font_list_async.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/gpu_data_manager_observer.h"
+#include "content/public/browser/plugin_service.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/gpu_info.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebScreenInfo.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/screen.h"
+#include "webkit/plugins/webplugininfo.h"
+
+using WebKit::WebScreenInfo;
+
+namespace autofill {
+namespace risk {
+
+namespace {
+
+const int32 kFingerprinterVersion = 1;
+
+// Returns the delta between the local timezone and UTC.
+base::TimeDelta GetTimezoneOffset() {
+ base::Time utc = base::Time::Now();
+
+ base::Time::Exploded local;
+ utc.LocalExplode(&local);
+
+ return base::Time::FromUTCExploded(local) - utc;
+}
+
+// Returns the concatenation of the operating system name and version, e.g.
+// "Mac OS X 10.6.8".
+std::string GetOperatingSystemVersion() {
+ return base::SysInfo::OperatingSystemName() + " " +
+ base::SysInfo::OperatingSystemVersion();
+}
+
+// Adds the list of |fonts| to the |machine|.
+void AddFontsToFingerprint(const base::ListValue& fonts,
+ Fingerprint_MachineCharacteristics* machine) {
+ for (base::ListValue::const_iterator it = fonts.begin();
+ it != fonts.end(); ++it) {
+ // Each item in the list is a two-element list such that the first element
+ // is the font family and the second is the font name.
+ const base::ListValue* font_description;
+ bool success = (*it)->GetAsList(&font_description);
+ DCHECK(success);
+
+ std::string font_name;
+ success = font_description->GetString(1, &font_name);
+ DCHECK(success);
+
+ machine->add_font(font_name);
+ }
+}
+
+// Adds the list of |plugins| to the |machine|.
+void AddPluginsToFingerprint(const std::vector<webkit::WebPluginInfo>& plugins,
+ Fingerprint_MachineCharacteristics* machine) {
+ for (std::vector<webkit::WebPluginInfo>::const_iterator it = plugins.begin();
+ it != plugins.end(); ++it) {
+ Fingerprint_MachineCharacteristics_Plugin* plugin =
+ machine->add_plugin();
+ plugin->set_name(UTF16ToUTF8(it->name));
+ plugin->set_description(UTF16ToUTF8(it->desc));
+ for (std::vector<webkit::WebPluginMimeType>::const_iterator mime_type =
+ it->mime_types.begin();
+ mime_type != it->mime_types.end(); ++mime_type) {
+ plugin->add_mime_type(mime_type->mime_type);
+ }
+ plugin->set_version(UTF16ToUTF8(it->version));
+ }
+}
+
+// Adds the list of HTTP accept languages to the |machine|.
+void AddAcceptLanguagesToFingerprint(
+ const std::string& accept_languages_str,
+ Fingerprint_MachineCharacteristics* machine) {
+ std::vector<std::string> accept_languages;
+ base::SplitString(accept_languages_str, ',', &accept_languages);
+ for (std::vector<std::string>::const_iterator it = accept_languages.begin();
+ it != accept_languages.end(); ++it) {
+ machine->add_requested_language(*it);
+ }
+}
+
+// Writes
+// (a) the number of screens,
+// (b) the primary display's screen size,
+// (c) the screen's color depth, and
+// (d) the size of the screen unavailable to web page content,
+// i.e. the Taskbar size on Windows
+// into the |machine|.
+void AddScreenInfoToFingerprint(const WebScreenInfo& screen_info,
+ Fingerprint_MachineCharacteristics* machine) {
+ // TODO(scottmg): NativeScreen maybe wrong. http://crbug.com/133312
+ machine->set_screen_count(
+ gfx::Screen::GetNativeScreen()->GetNumDisplays());
+
+ gfx::Size screen_size =
+ gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().GetSizeInPixel();
+ machine->mutable_screen_size()->set_width(screen_size.width());
+ machine->mutable_screen_size()->set_height(screen_size.height());
+
+ machine->set_screen_color_depth(screen_info.depth);
+
+ gfx::Rect screen_rect(screen_info.rect);
+ gfx::Rect available_rect(screen_info.availableRect);
+ gfx::Rect unavailable_rect = gfx::SubtractRects(screen_rect, available_rect);
+ machine->mutable_unavailable_screen_size()->set_width(
+ unavailable_rect.width());
+ machine->mutable_unavailable_screen_size()->set_height(
+ unavailable_rect.height());
+}
+
+// Writes info about the machine's CPU into the |machine|.
+void AddCpuInfoToFingerprint(Fingerprint_MachineCharacteristics* machine) {
+ base::CPU cpu;
+ machine->mutable_cpu()->set_vendor_name(cpu.vendor_name());
+ machine->mutable_cpu()->set_brand(cpu.cpu_brand());
+}
+
+// Writes info about the machine's GPU into the |machine|.
+void AddGpuInfoToFingerprint(Fingerprint_MachineCharacteristics* machine) {
+ const content::GPUInfo& gpu_info =
+ content::GpuDataManager::GetInstance()->GetGPUInfo();
+ DCHECK(gpu_info.finalized);
+
+ Fingerprint_MachineCharacteristics_Graphics* graphics =
+ machine->mutable_graphics_card();
+ graphics->set_vendor_id(gpu_info.gpu.vendor_id);
+ graphics->set_device_id(gpu_info.gpu.device_id);
+ graphics->set_driver_version(gpu_info.driver_version);
+ graphics->set_driver_date(gpu_info.driver_date);
+
+ Fingerprint_MachineCharacteristics_Graphics_PerformanceStatistics*
+ gpu_performance = graphics->mutable_performance_statistics();
+ gpu_performance->set_graphics_score(gpu_info.performance_stats.graphics);
+ gpu_performance->set_gaming_score(gpu_info.performance_stats.gaming);
+ gpu_performance->set_overall_score(gpu_info.performance_stats.overall);
+}
+
+// Waits for all asynchronous data required for the fingerprint to be loaded;
+// then fills out the fingerprint.
+class FingerprintDataLoader : public content::GpuDataManagerObserver {
+ public:
+ FingerprintDataLoader(
+ int64 gaia_id,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& content_bounds,
+ const WebScreenInfo& screen_info,
+ const std::string& version,
+ const std::string& charset,
+ const std::string& accept_languages,
+ const base::Time& install_time,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
+
+ private:
+ virtual ~FingerprintDataLoader();
+
+ // content::GpuDataManagerObserver:
+ virtual void OnGpuInfoUpdate() OVERRIDE;
+
+ // Callbacks for asynchronously loaded data.
+ void OnGotFonts(scoped_ptr<base::ListValue> fonts);
+ void OnGotPlugins(const std::vector<webkit::WebPluginInfo>& plugins);
+
+ // If all of the asynchronous data has been loaded, calls |callback_| with
+ // the fingerprint data.
+ void MaybeFillFingerprint();
+
+ // Calls |callback_| with the fingerprint data.
+ void FillFingerprint();
+
+ // The GPU data provider.
+ content::GpuDataManager* const gpu_data_manager_;
+
+ // Data that will be passed on to the next loading phase.
+ const int64 gaia_id_;
+ const gfx::Rect window_bounds_;
+ const gfx::Rect content_bounds_;
+ const WebScreenInfo screen_info_;
+ const std::string version_;
+ const std::string charset_;
+ const std::string accept_languages_;
+ const base::Time install_time_;
+
+ // Data that will be loaded asynchronously.
+ scoped_ptr<base::ListValue> fonts_;
+ std::vector<webkit::WebPluginInfo> plugins_;
+ bool has_loaded_plugins_;
+
+ // The callback that will be called once all the data is available.
+ base::Callback<void(scoped_ptr<Fingerprint>)> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(FingerprintDataLoader);
+};
+
+FingerprintDataLoader::FingerprintDataLoader(
+ int64 gaia_id,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& content_bounds,
+ const WebScreenInfo& screen_info,
+ const std::string& version,
+ const std::string& charset,
+ const std::string& accept_languages,
+ const base::Time& install_time,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback)
+ : gpu_data_manager_(content::GpuDataManager::GetInstance()),
+ gaia_id_(gaia_id),
+ window_bounds_(window_bounds),
+ content_bounds_(content_bounds),
+ screen_info_(screen_info),
+ version_(version),
+ charset_(charset),
+ accept_languages_(accept_languages),
+ install_time_(install_time),
+ has_loaded_plugins_(false),
+ callback_(callback) {
+ DCHECK(!install_time_.is_null());
+
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "Loading fingerprint data.";
+
+ // Load GPU data if needed.
+ if (!gpu_data_manager_->IsCompleteGpuInfoAvailable()) {
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "Loading GPU data.";
+
+ gpu_data_manager_->AddObserver(this);
+ gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
+ } else {
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "GPU data already loaded.";
+ }
+
+ // Load plugin data.
+ content::PluginService::GetInstance()->GetPlugins(
+ base::Bind(&FingerprintDataLoader::OnGotPlugins, base::Unretained(this)));
+
+ // Load font data.
+ content::GetFontListAsync(
+ base::Bind(&FingerprintDataLoader::OnGotFonts, base::Unretained(this)));
+}
+
+FingerprintDataLoader::~FingerprintDataLoader() {
+}
+
+void FingerprintDataLoader::OnGpuInfoUpdate() {
+ if (!gpu_data_manager_->IsCompleteGpuInfoAvailable()) {
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "OnGpuInfoUpdate() called without complete GPU info.";
+ return;
+ }
+
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "Loaded GPU data.";
+
+ gpu_data_manager_->RemoveObserver(this);
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::OnGotFonts(scoped_ptr<base::ListValue> fonts) {
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "Loaded fonts.";
+
+ DCHECK(!fonts_);
+ fonts_.reset(fonts.release());
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::OnGotPlugins(
+ const std::vector<webkit::WebPluginInfo>& plugins) {
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "Loaded plugins.";
+
+ DCHECK(!has_loaded_plugins_);
+ has_loaded_plugins_ = true;
+ plugins_ = plugins;
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::MaybeFillFingerprint() {
+ // TODO(isherman): Investigating http://crbug.com/174296
+ LOG(WARNING) << "GPU data: "
+ << (gpu_data_manager_->IsCompleteGpuInfoAvailable() ?
+ "loaded" :
+ "waiting");
+ LOG(WARNING) << "Fonts: "
+ << (fonts_ ? "loaded" : "waiting");
+ LOG(WARNING) << "Plugins: "
+ << (has_loaded_plugins_ ? "loaded" : "waiting");
+
+ // If all of the data has been loaded, fill the fingerprint and clean up.
+ if (gpu_data_manager_->IsCompleteGpuInfoAvailable() &&
+ fonts_ &&
+ has_loaded_plugins_) {
+ FillFingerprint();
+ delete this;
+ }
+}
+
+void FingerprintDataLoader::FillFingerprint() {
+ scoped_ptr<Fingerprint> fingerprint(new Fingerprint);
+ Fingerprint_MachineCharacteristics* machine =
+ fingerprint->mutable_machine_characteristics();
+
+ machine->set_operating_system_build(GetOperatingSystemVersion());
+ // We use the delta between the install time and the Unix epoch, in hours.
+ machine->set_browser_install_time_hours(
+ (install_time_ - base::Time::UnixEpoch()).InHours());
+ machine->set_utc_offset_ms(GetTimezoneOffset().InMilliseconds());
+ machine->set_browser_language(
+ content::GetContentClient()->browser()->GetApplicationLocale());
+ machine->set_charset(charset_);
+ machine->set_user_agent(content::GetContentClient()->GetUserAgent());
+ machine->set_ram(base::SysInfo::AmountOfPhysicalMemory());
+ machine->set_browser_build(version_);
+ AddFontsToFingerprint(*fonts_, machine);
+ AddPluginsToFingerprint(plugins_, machine);
+ AddAcceptLanguagesToFingerprint(accept_languages_, machine);
+ AddScreenInfoToFingerprint(screen_info_, machine);
+ AddCpuInfoToFingerprint(machine);
+ AddGpuInfoToFingerprint(machine);
+
+ // TODO(isherman): Store the partition size of the hard drives?
+
+ Fingerprint_TransientState* transient_state =
+ fingerprint->mutable_transient_state();
+ Fingerprint_Dimension* inner_window_size =
+ transient_state->mutable_inner_window_size();
+ inner_window_size->set_width(content_bounds_.width());
+ inner_window_size->set_height(content_bounds_.height());
+ Fingerprint_Dimension* outer_window_size =
+ transient_state->mutable_outer_window_size();
+ outer_window_size->set_width(window_bounds_.width());
+ outer_window_size->set_height(window_bounds_.height());
+
+ // TODO(isherman): Record network performance data, which is theoretically
+ // available to JS.
+
+ // TODO(isherman): Record user behavior data.
+
+ Fingerprint_Metadata* metadata = fingerprint->mutable_metadata();
+ metadata->set_timestamp_ms(
+ (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds());
+ metadata->set_gaia_id(gaia_id_);
+ metadata->set_fingerprinter_version(kFingerprinterVersion);
+
+ callback_.Run(fingerprint.Pass());
+}
+
+} // namespace
+
+void GetFingerprint(
+ int64 gaia_id,
+ const gfx::Rect& window_bounds,
+ const content::WebContents& web_contents,
+ const std::string& version,
+ const std::string& charset,
+ const std::string& accept_languages,
+ const base::Time& install_time,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) {
+ gfx::Rect content_bounds;
+ web_contents.GetView()->GetContainerBounds(&content_bounds);
+
+ WebKit::WebScreenInfo screen_info;
+ content::RenderWidgetHostView* host_view =
+ web_contents.GetRenderWidgetHostView();
+ if (host_view)
+ host_view->GetRenderWidgetHost()->GetWebScreenInfo(&screen_info);
+
+ internal::GetFingerprintInternal(
+ gaia_id, window_bounds, content_bounds, screen_info, version, charset,
+ accept_languages, install_time, callback);
+}
+
+namespace internal {
+
+void GetFingerprintInternal(
+ int64 gaia_id,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& content_bounds,
+ const WebKit::WebScreenInfo& screen_info,
+ const std::string& version,
+ const std::string& charset,
+ const std::string& accept_languages,
+ const base::Time& install_time,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback) {
+ // Begin loading all of the data that we need to load asynchronously.
+ // This class is responsible for freeing its own memory.
+ new FingerprintDataLoader(gaia_id, window_bounds, content_bounds, screen_info,
+ version, charset, accept_languages, install_time,
+ callback);
+}
+
+} // namespace internal
+
+} // namespace risk
+} // namespace autofill
diff --git a/components/autofill/browser/risk/fingerprint.h b/components/autofill/browser/risk/fingerprint.h
new file mode 100644
index 0000000..75c34d4
--- /dev/null
+++ b/components/autofill/browser/risk/fingerprint.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_RISK_FINGERPRINT_H_
+#define COMPONENTS_AUTOFILL_BROWSER_RISK_FINGERPRINT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+
+class PrefService;
+
+namespace base {
+class Time;
+}
+
+namespace content {
+class WebContents;
+}
+
+namespace gfx {
+class Rect;
+}
+
+namespace WebKit {
+struct WebScreenInfo;
+}
+
+namespace autofill {
+namespace risk {
+
+class Fingerprint;
+
+// Asynchronously calls |callback| with statistics that, collectively, provide a
+// unique fingerprint for this (machine, user) pair, used for fraud prevention.
+// |gaia_id| should be the user id for Google's authentication system.
+// |window_bounds| should be the bounds of the containing Chrome window.
+// |web_contents| should be the host for the page the user is interacting with.
+// |version| is the version number of the application.
+// |charset| is the default character set.
+// |accept_languages| is the Accept-Languages setting.
+// |install_time| is the absolute time of installation.
+void GetFingerprint(
+ int64 gaia_id,
+ const gfx::Rect& window_bounds,
+ const content::WebContents& web_contents,
+ const std::string& version,
+ const std::string& charset,
+ const std::string& accept_languages,
+ const base::Time& install_time,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
+
+// Exposed for testing:
+namespace internal {
+
+void GetFingerprintInternal(
+ int64 gaia_id,
+ const gfx::Rect& window_bounds,
+ const gfx::Rect& content_bounds,
+ const WebKit::WebScreenInfo& screen_info,
+ const std::string& version,
+ const std::string& charset,
+ const std::string& accept_languages,
+ const base::Time& install_time,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
+
+} // namespace internal
+
+} // namespace risk
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_RISK_FINGERPRINT_H_
diff --git a/components/autofill/browser/risk/proto/fingerprint.proto b/components/autofill/browser/risk/proto/fingerprint.proto
new file mode 100644
index 0000000..bd23575
--- /dev/null
+++ b/components/autofill/browser/risk/proto/fingerprint.proto
@@ -0,0 +1,218 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file contains the definition of protocol buffers for native browser
+// fingerprinting.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package autofill.risk;
+
+message Fingerprint {
+ // A simple protocol message to represent objects with width and height.
+ message Dimension {
+ optional int32 width = 1;
+ optional int32 height = 2;
+ }
+
+ // Characteristics of the user's machine that are relatively durable,
+ // i.e. that are expected to change relatively infrequently.
+ message MachineCharacteristics {
+ // A simple protocol message that represents a plugin.
+ // e.g. flash, shockwave, acrobat reader, gears, picasa
+ message Plugin {
+ optional string name = 1;
+ optional string description = 2;
+ repeated string mime_type = 3;
+ optional string version = 4;
+ }
+
+ // Information on the CPU.
+ message Cpu {
+ // e.g. "GenuineIntel"
+ optional string vendor_name = 1;
+ // e.g. "Intel(R) Xeon(R) CPU X5650 @ 2.67GHz\000"
+ optional string brand = 2;
+ }
+
+ // Information on the GPU.
+ message Graphics {
+ // The GPU manufacturer's vendor id.
+ optional uint32 vendor_id = 1;
+
+ // The GPU manufacturer's device id for the chip set.
+ optional uint32 device_id = 2;
+
+ // The driver version on the GPU.
+ optional string driver_version = 3;
+
+ // The driver date on the GPU.
+ optional string driver_date = 4;
+
+ // The GPU performance statistics.
+ message PerformanceStatistics {
+ optional float graphics_score = 1;
+ optional float gaming_score = 2;
+ optional float overall_score = 3;
+ }
+ optional PerformanceStatistics performance_statistics = 5;
+ }
+
+ // Username currently logged into computer / device.
+ // TODO(isherman): This seems like TMI.
+ optional string user_name = 1;
+
+ // Build version string for the current operating system.
+ optional string operating_system_build = 2;
+
+ // e.g. User-assigned computer name.
+ // TODO(isherman): This seems like TMI.
+ optional string device_name = 3;
+
+ // Browser install time (hours since epoch).
+ optional int64 browser_install_time_hours = 4;
+
+ // Fonts installed on the machine.
+ repeated string font = 5;
+
+ // Plug-ins installed on the machine.
+ repeated Plugin plugin = 6;
+
+ // Delta in ms of the device's time zone from UTC.
+ optional int64 utc_offset_ms = 7;
+
+ // IETF-formatted language tag. e.g. "en", "en-US", "es-419", etc.
+ // http://en.wikipedia.org/wiki/IETF_language_tag
+ optional string browser_language = 8;
+
+ // User-requested language code of viewed sites. Languages in
+ // accept-languages.
+ repeated string requested_language = 9;
+
+ // Default charset of the browser. (e.g. ISO-8859-1, obtained from
+ // document.defaultCharset)
+ optional string charset = 10;
+
+ // The number of physical screens.
+ optional int32 screen_count = 11;
+
+ // Information about the user's monitor's physical screen size.
+ // (e.g. 1024 x 768)
+ optional Dimension screen_size = 12;
+
+ // The color depth of the user's screen (obtained from screen.colorDepth
+ // or screen.pixelDepth)
+ optional int32 screen_color_depth = 13;
+
+ // Information about the size of the portion of the screen that is unusable
+ // to a program (i.e. on Windows, the portion of the screen that is taken
+ // up by the taskbar)
+ optional Dimension unavailable_screen_size = 14;
+
+ optional string user_agent = 15;
+
+ // Total size of each hard drive partition.
+ repeated int32 partition_size = 16;
+
+ optional Cpu cpu = 17;
+
+ // Total RAM in bytes.
+ optional int64 ram = 18;
+
+ // Graphics card being used.
+ optional Graphics graphics_card = 19;
+
+ // Build version string for browser.
+ optional string browser_build = 20;
+
+ }
+
+ // Contains properties relating to more transient computer / browser state.
+ message TransientState {
+ // Corresponds to window.innerWidth / innertHeight
+ optional Dimension inner_window_size = 1;
+
+ // Corresponds to window.outerWidth / outerHeight
+ optional Dimension outer_window_size = 2;
+ }
+
+ // Measures computer / network performance.
+ message Performance {
+ // Bandwidth in MB/s. network.connection.bandwidth
+ optional float bandwidth = 1;
+ // Whether bandwidth cost is metered. network.connection.metered
+ optional bool metered = 2;
+ // Whether it's wifi, 3g, 2g, etc. network.connection.type
+ optional string network_type = 3;
+ }
+
+ // Properties describing the user -- especially the user's state in the
+ // physical world.
+ message UserCharacteristics {
+ message Vector {
+ optional int32 x = 1;
+ optional int32 y = 2;
+ optional int32 z = 3;
+ }
+
+ message Location {
+ // Meters above sea level.
+ optional double altitude = 1;
+ // Latitude in degrees.
+ optional double latitude = 2;
+ // Longitude in degrees.
+ optional double longitude = 3;
+ // Accuracy in meters. 95% probability of being in this radius of
+ // lat / long.
+ optional float accuracy = 4;
+ // Milliseconds since epoch since measurement.
+ optional double time_in_ms = 5;
+ }
+
+ // Average force by finger presses. TouchEvent.force
+ optional float force = 1;
+ // Average finger width. TouchEvent.radiusX
+ optional float touch_width = 2;
+ // Average finger height. TouchEvent.radiusY
+ optional float touch_height = 3;
+ // TouchEvent.rotationAngle
+ optional int32 touch_rotation = 4;
+ // Orientation while user is navigating flow and the device is roughly
+ // stable. x for alpha, y for beta, z for gamma
+ // TODO(isherman): Orientation data is only available asynchronously in
+ // Chrome.
+ optional Vector device_orientation = 5;
+ // Acceleration while measuring orientation.
+ // TODO(isherman): Acceleration data is not available in Chrome.
+ optional Vector device_acceleration = 6;
+ optional Location location = 7;
+ }
+
+ // Metadata associated with data collection or the user that doesn't actually
+ // fingerprint the device.
+ message Metadata {
+ // When this data was collected / received, in milliseconds since the epoch.
+ optional int64 timestamp_ms = 1;
+ // Gaia id associated with transaction.
+ optional int64 gaia_id = 2;
+ // Version of the native library generating this proto.
+ // This may be manually bumped when the code populating the proto has
+ // significantly changed.
+ optional int32 fingerprinter_version = 3;
+ }
+
+ // Computer / browser fingerprint.
+ optional MachineCharacteristics machine_characteristics = 1;
+
+ optional Performance performance = 2;
+
+ optional UserCharacteristics user_characteristics = 3;
+
+ optional TransientState transient_state = 4;
+
+ // Metadata associated with data collection.
+ optional Metadata metadata = 5;
+} \ No newline at end of file
diff --git a/components/autofill/browser/test_autofill_external_delegate.cc b/components/autofill/browser/test_autofill_external_delegate.cc
new file mode 100644
index 0000000..31bb02b
--- /dev/null
+++ b/components/autofill/browser/test_autofill_external_delegate.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/test_autofill_external_delegate.h"
+
+#include "ui/gfx/rect.h"
+
+namespace autofill {
+
+void GenerateTestAutofillPopup(
+ AutofillExternalDelegate* autofill_external_delegate) {
+ int query_id = 1;
+ FormData form;
+ FormFieldData field;
+ field.is_focusable = true;
+ field.should_autocomplete = true;
+ gfx::RectF bounds(100.f, 100.f);
+ autofill_external_delegate->OnQuery(query_id, form, field, bounds, false);
+
+ std::vector<string16> autofill_item;
+ autofill_item.push_back(string16());
+ std::vector<int> autofill_id;
+ autofill_id.push_back(0);
+ autofill_external_delegate->OnSuggestionsReturned(
+ query_id, autofill_item, autofill_item, autofill_item, autofill_id);
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/test_autofill_external_delegate.h b/components/autofill/browser/test_autofill_external_delegate.h
new file mode 100644
index 0000000..81268d7
--- /dev/null
+++ b/components/autofill/browser/test_autofill_external_delegate.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_
+
+#include "components/autofill/browser/autofill_external_delegate.h"
+
+class AutofillManager;
+
+namespace autofill {
+
+// Calls the required functions on the given external delegate to cause the
+// delegate to display a popup.
+void GenerateTestAutofillPopup(
+ AutofillExternalDelegate* autofill_external_delegate);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_
diff --git a/components/autofill/browser/test_autofill_manager_delegate.cc b/components/autofill/browser/test_autofill_manager_delegate.cc
new file mode 100644
index 0000000..7c8d834
--- /dev/null
+++ b/components/autofill/browser/test_autofill_manager_delegate.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2013 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/browser/test_autofill_manager_delegate.h"
+
+namespace autofill {
+
+TestAutofillManagerDelegate::TestAutofillManagerDelegate() {}
+TestAutofillManagerDelegate::~TestAutofillManagerDelegate() {}
+
+PersonalDataManager* TestAutofillManagerDelegate::GetPersonalDataManager() {
+ return NULL;
+}
+
+PrefService* TestAutofillManagerDelegate::GetPrefs() {
+ return NULL;
+}
+
+void TestAutofillManagerDelegate::HideRequestAutocompleteDialog() {}
+
+bool TestAutofillManagerDelegate::IsSavingPasswordsEnabled() const {
+ return false;
+}
+
+bool TestAutofillManagerDelegate::IsPasswordSyncEnabled() const {
+ return false;
+}
+
+void TestAutofillManagerDelegate::SetSyncStateChangedCallback(
+ const base::Closure& callback) { }
+
+void TestAutofillManagerDelegate::OnAutocheckoutError() {}
+
+void TestAutofillManagerDelegate::ShowAutofillSettings() {}
+
+void TestAutofillManagerDelegate::ConfirmSaveCreditCard(
+ const AutofillMetrics& metric_logger,
+ const CreditCard& credit_card,
+ const base::Closure& save_card_callback) {}
+
+void TestAutofillManagerDelegate::ShowPasswordGenerationBubble(
+ const gfx::Rect& bounds,
+ const content::PasswordForm& form,
+ autofill::PasswordGenerator* generator) {}
+
+void TestAutofillManagerDelegate::ShowAutocheckoutBubble(
+ const gfx::RectF& bounding_box,
+ const gfx::NativeView& native_view,
+ const base::Closure& callback) {}
+
+void TestAutofillManagerDelegate::ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ const AutofillMetrics& metric_logger,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*)>& callback) {}
+
+void TestAutofillManagerDelegate::RequestAutocompleteDialogClosed() {}
+
+void TestAutofillManagerDelegate::ShowAutofillPopup(
+ const gfx::RectF& element_bounds,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& identifiers,
+ AutofillPopupDelegate* delegate) {}
+
+void TestAutofillManagerDelegate::HideAutofillPopup() {}
+
+void TestAutofillManagerDelegate::UpdateProgressBar(double value) {}
+
+} // namespace autofill
diff --git a/components/autofill/browser/test_autofill_manager_delegate.h b/components/autofill/browser/test_autofill_manager_delegate.h
new file mode 100644
index 0000000..64873c02
--- /dev/null
+++ b/components/autofill/browser/test_autofill_manager_delegate.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2013 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_BROWSER_TEST_AUTOFILL_MANAGER_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_BROWSER_TEST_AUTOFILL_MANAGER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
+
+namespace autofill {
+
+// This class is only for easier writing of testings. All pure virtual functions
+// have been giving empty methods.
+class TestAutofillManagerDelegate : public AutofillManagerDelegate {
+ public:
+ TestAutofillManagerDelegate();
+ virtual ~TestAutofillManagerDelegate();
+
+ // AutofillManagerDelegate implementation.
+ virtual PersonalDataManager* GetPersonalDataManager() OVERRIDE;
+ virtual PrefService* GetPrefs() OVERRIDE;
+ virtual void HideRequestAutocompleteDialog() OVERRIDE;
+ virtual bool IsSavingPasswordsEnabled() const OVERRIDE;
+ virtual bool IsPasswordSyncEnabled() const OVERRIDE;
+ virtual void SetSyncStateChangedCallback(
+ const base::Closure& callback) OVERRIDE;
+ virtual void OnAutocheckoutError() OVERRIDE;
+ virtual void ShowAutofillSettings() OVERRIDE;
+ virtual void ConfirmSaveCreditCard(
+ const AutofillMetrics& metric_logger,
+ const CreditCard& credit_card,
+ const base::Closure& save_card_callback) OVERRIDE;
+ virtual void ShowPasswordGenerationBubble(
+ const gfx::Rect& bounds,
+ const content::PasswordForm& form,
+ autofill::PasswordGenerator* generator) OVERRIDE;
+ virtual void ShowAutocheckoutBubble(
+ const gfx::RectF& bounding_box,
+ const gfx::NativeView& native_view,
+ const base::Closure& callback) OVERRIDE;
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const content::SSLStatus& ssl_status,
+ const AutofillMetrics& metric_logger,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*)>& callback) OVERRIDE;
+ virtual void RequestAutocompleteDialogClosed() OVERRIDE;
+ virtual void ShowAutofillPopup(const gfx::RectF& element_bounds,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& identifiers,
+ AutofillPopupDelegate* delegate) OVERRIDE;
+ virtual void HideAutofillPopup() OVERRIDE;
+ virtual void UpdateProgressBar(double value) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillManagerDelegate);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_TEST_AUTOFILL_MANAGER_DELEGATE_H_
diff --git a/components/autofill/browser/validation.cc b/components/autofill/browser/validation.cc
new file mode 100644
index 0000000..1053845
--- /dev/null
+++ b/components/autofill/browser/validation.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2013 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/browser/validation.h"
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/autofill_regexes.h"
+#include "components/autofill/browser/credit_card.h"
+
+namespace autofill {
+
+bool IsValidCreditCardNumber(const string16& text) {
+ string16 number = CreditCard::StripSeparators(text);
+
+ // Credit card numbers are at most 19 digits in length [1]. 12 digits seems to
+ // be a fairly safe lower-bound [2].
+ // [1] http://www.merriampark.com/anatomycc.htm
+ // [2] http://en.wikipedia.org/wiki/Bank_card_number
+ const size_t kMinCreditCardDigits = 12;
+ const size_t kMaxCreditCardDigits = 19;
+ if (number.size() < kMinCreditCardDigits ||
+ number.size() > kMaxCreditCardDigits)
+ return false;
+
+ // Use the Luhn formula [3] to validate the number.
+ // [3] http://en.wikipedia.org/wiki/Luhn_algorithm
+ int sum = 0;
+ bool odd = false;
+ for (string16::reverse_iterator iter = number.rbegin();
+ iter != number.rend();
+ ++iter) {
+ if (!IsAsciiDigit(*iter))
+ return false;
+
+ int digit = *iter - '0';
+ if (odd) {
+ digit *= 2;
+ sum += digit / 10 + digit % 10;
+ } else {
+ sum += digit;
+ }
+ odd = !odd;
+ }
+
+ return (sum % 10) == 0;
+}
+
+bool IsValidCreditCardSecurityCode(const string16& text) {
+ if (text.size() < 3U || text.size() > 4U)
+ return false;
+
+ for (string16::const_iterator iter = text.begin();
+ iter != text.end();
+ ++iter) {
+ if (!IsAsciiDigit(*iter))
+ return false;
+ }
+ return true;
+}
+
+bool IsValidEmailAddress(const string16& text) {
+ // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state)
+ const string16 kEmailPattern = ASCIIToUTF16(
+ "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@"
+ "[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$");
+ return MatchesPattern(text, kEmailPattern);
+}
+
+bool IsValidZip(const string16& value) {
+ const string16 kZipPattern = ASCIIToUTF16("^\\d{5}(-\\d{4})?$");
+ return MatchesPattern(value, kZipPattern);
+}
+
+} // namespace autofill
diff --git a/components/autofill/browser/validation.h b/components/autofill/browser/validation.h
new file mode 100644
index 0000000..3f6ce9a
--- /dev/null
+++ b/components/autofill/browser/validation.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 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_BROWSER_VALIDATION_H_
+#define COMPONENTS_AUTOFILL_BROWSER_VALIDATION_H_
+
+#include "base/string16.h"
+
+namespace autofill {
+
+// Returns true if |text| looks like a valid credit card number.
+// Uses the Luhn formula to validate the number.
+bool IsValidCreditCardNumber(const string16& text);
+
+// Returns true if |text| looks like a valid credit card security code.
+bool IsValidCreditCardSecurityCode(const string16& text);
+
+// Returns true if |text| looks like a valid e-mail address.
+bool IsValidEmailAddress(const string16& text);
+
+// Returns true if |text| looks like a valid zip code.
+// Valid for US zip codes only.
+bool IsValidZip(const string16& text);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_VALIDATION_H_
diff --git a/components/autofill/browser/validation_unittest.cc b/components/autofill/browser/validation_unittest.cc
new file mode 100644
index 0000000..3c1475b
--- /dev/null
+++ b/components/autofill/browser/validation_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/validation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// From https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
+const char* const kValidNumbers[] = {
+ "378282246310005",
+ "3714 4963 5398 431",
+ "3787-3449-3671-000",
+ "5610591081018250",
+ "3056 9309 0259 04",
+ "3852-0000-0232-37",
+ "6011111111111117",
+ "6011 0009 9013 9424",
+ "3530-1113-3330-0000",
+ "3566002020360505",
+ "5555 5555 5555 4444",
+ "5105-1051-0510-5100",
+ "4111111111111111",
+ "4012 8888 8888 1881",
+ "4222-2222-2222-2",
+ "5019717010103742",
+ "6331101999990016",
+};
+const char* const kInvalidNumbers[] = {
+ "4111 1111 112", /* too short */
+ "41111111111111111115", /* too long */
+ "4111-1111-1111-1110", /* wrong Luhn checksum */
+ "3056 9309 0259 04aa", /* non-digit characters */
+};
+const char* const kValidCreditCardSecurityCode[] = {
+ "323", // 3-digit CSC.
+ "3234", // 4-digit CSC.
+};
+const char* const kInvalidCreditCardSecurityCode[] = {
+ "32", // CSC too short.
+ "12345", // CSC too long.
+ "asd", // non-numeric CSC.
+};
+const char* const kValidEmailAddress[] = {
+ "user@example",
+ "user@example.com",
+ "user@subdomain.example.com",
+ "user+postfix@example.com",
+};
+const char* const kInvalidEmailAddress[] = {
+ "user",
+ "foo.com",
+ "user@",
+ "user@=example.com"
+};
+} // namespace
+
+TEST(AutofillValidation, IsValidCreditCardNumber) {
+ for (size_t i = 0; i < arraysize(kValidNumbers); ++i) {
+ SCOPED_TRACE(kValidNumbers[i]);
+ EXPECT_TRUE(
+ autofill::IsValidCreditCardNumber(ASCIIToUTF16(kValidNumbers[i])));
+ }
+ for (size_t i = 0; i < arraysize(kInvalidNumbers); ++i) {
+ SCOPED_TRACE(kInvalidNumbers[i]);
+ EXPECT_FALSE(
+ autofill::IsValidCreditCardNumber(ASCIIToUTF16(kInvalidNumbers[i])));
+ }
+}
+
+TEST(AutofillValidation, IsValidCreditCardSecurityCode) {
+ for (size_t i = 0; i < arraysize(kValidCreditCardSecurityCode); ++i) {
+ SCOPED_TRACE(kValidCreditCardSecurityCode[i]);
+ EXPECT_TRUE(
+ autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kValidCreditCardSecurityCode[i])));
+ }
+ for (size_t i = 0; i < arraysize(kInvalidCreditCardSecurityCode); ++i) {
+ SCOPED_TRACE(kInvalidCreditCardSecurityCode[i]);
+ EXPECT_FALSE(
+ autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kInvalidCreditCardSecurityCode[i])));
+ }
+}
+
+TEST(AutofillValidation, IsValidEmailAddress) {
+ for (size_t i = 0; i < arraysize(kValidEmailAddress); ++i) {
+ SCOPED_TRACE(kValidEmailAddress[i]);
+ EXPECT_TRUE(
+ autofill::IsValidEmailAddress(ASCIIToUTF16(kValidEmailAddress[i])));
+ }
+ for (size_t i = 0; i < arraysize(kInvalidEmailAddress); ++i) {
+ SCOPED_TRACE(kInvalidEmailAddress[i]);
+ EXPECT_FALSE(
+ autofill::IsValidEmailAddress(ASCIIToUTF16(kInvalidEmailAddress[i])));
+ }
+}
+
+
diff --git a/components/autofill/browser/wallet/cart.cc b/components/autofill/browser/wallet/cart.cc
new file mode 100644
index 0000000..b6f41a4
--- /dev/null
+++ b/components/autofill/browser/wallet/cart.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/cart.h"
+
+#include "base/values.h"
+
+namespace autofill {
+namespace wallet {
+
+Cart::Cart(const std::string& total_price, const std::string& currency_code)
+ : total_price_(total_price), currency_code_(currency_code) {}
+
+Cart::~Cart() {}
+
+scoped_ptr<base::DictionaryValue> Cart::ToDictionary() const {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("total_price", total_price_);
+ dict->SetString("currency_code", currency_code_);
+ return scoped_ptr<base::DictionaryValue>(dict);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/cart.h b/components/autofill/browser/wallet/cart.h
new file mode 100644
index 0000000..fc106c1
--- /dev/null
+++ b/components/autofill/browser/wallet/cart.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_CART_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_CART_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+namespace wallet {
+
+// Container object for purchase data provided by the browser. The enclosed data
+// is required to request a FullWallet from Online Wallet in order to set
+// spending limits on the generated proxy card. If the actual amount is not
+// available, the maximum allowable value should be used: $1850 USD. Online
+// Wallet is designed to accept price information in addition to information
+// about the items being purchased hence the name Cart.
+class Cart {
+ public:
+ Cart(const std::string& total_price, const std::string& currency_code);
+ ~Cart();
+
+ scoped_ptr<base::DictionaryValue> ToDictionary() const;
+
+ const std::string& total_price() const { return total_price_; }
+ const std::string& currency_code() const { return currency_code_; }
+
+ private:
+ // |total_price_| must be a formatted as a double with no more than two
+ // decimals, e.g. 100.99
+ std::string total_price_;
+
+ // |currency_code_| must be one of the ISO 4217 currency codes, e.g. USD.
+ std::string currency_code_;
+
+ DISALLOW_ASSIGN(Cart);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_CART_H_
diff --git a/components/autofill/browser/wallet/cart_unittest.cc b/components/autofill/browser/wallet/cart_unittest.cc
new file mode 100644
index 0000000..ae84c1b
--- /dev/null
+++ b/components/autofill/browser/wallet/cart_unittest.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/values.h"
+#include "components/autofill/browser/wallet/cart.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace wallet {
+
+TEST(Cart, ToDictionary) {
+ base::DictionaryValue expected;
+ expected.SetString("total_price", "total_price");
+ expected.SetString("currency_code", "currency_code");
+ Cart cart("total_price", "currency_code");
+ ASSERT_TRUE(expected.Equals(cart.ToDictionary().get()));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/encryption_escrow_client.cc b/components/autofill/browser/wallet/encryption_escrow_client.cc
new file mode 100644
index 0000000..b75e792
--- /dev/null
+++ b/components/autofill/browser/wallet/encryption_escrow_client.cc
@@ -0,0 +1,178 @@
+// Copyright 2013 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/browser/wallet/encryption_escrow_client.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "base/strings/string_split.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/wallet/encryption_escrow_client_observer.h"
+#include "components/autofill/browser/wallet/instrument.h"
+#include "components/autofill/browser/wallet/wallet_service_url.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace {
+
+const char kEncryptOtpBodyFormat[] = "cvv=%s:%s";
+const char kEscrowInstrumentInformationFormat[] = "gid=%s&cardNumber=%s&cvv=%s";
+const char kEscrowCardVerficationNumberFormat[] = "gid=%s&cvv=%s";
+const char kApplicationMimeType[] = "application/x-www-form-urlencoded";
+
+// The maximum number of bits in the one time pad that the server is willing to
+// accept.
+const size_t kMaxBits = 56;
+
+// The minimum number of bits in the one time pad that the server is willing to
+// accept.
+const size_t kMinBits = 40;
+
+} // anonymous namespace
+
+namespace autofill {
+namespace wallet {
+
+EncryptionEscrowClient::EncryptionEscrowClient(
+ net::URLRequestContextGetter* context_getter,
+ EncryptionEscrowClientObserver* observer)
+ : context_getter_(context_getter),
+ observer_(observer),
+ request_type_(NO_PENDING_REQUEST) {
+ DCHECK(context_getter_);
+ DCHECK(observer_);
+}
+
+EncryptionEscrowClient::~EncryptionEscrowClient() {}
+
+void EncryptionEscrowClient::EncryptOneTimePad(
+ const std::vector<uint8>& one_time_pad) {
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ size_t num_bits = one_time_pad.size() * 8;
+ DCHECK_LE(num_bits, kMaxBits);
+ DCHECK_GE(num_bits, kMinBits);
+
+ request_type_ = ENCRYPT_ONE_TIME_PAD;
+
+ std::string post_body = StringPrintf(
+ kEncryptOtpBodyFormat,
+ base::HexEncode(&num_bits, 1).c_str(),
+ base::HexEncode(&(one_time_pad[0]), one_time_pad.size()).c_str());
+
+ MakeRequest(GetEncryptionUrl(), post_body);
+}
+
+void EncryptionEscrowClient::EscrowInstrumentInformation(
+ const Instrument& new_instrument,
+ const std::string& obfuscated_gaia_id) {
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = ESCROW_INSTRUMENT_INFORMATION;
+
+ const std::string& primary_account_number =
+ net::EscapeUrlEncodedData(
+ UTF16ToUTF8(new_instrument.primary_account_number()), true);
+ const std::string& card_verification_number =
+ net::EscapeUrlEncodedData(
+ UTF16ToUTF8(new_instrument.card_verification_number()), true);
+
+ std::string post_body = StringPrintf(
+ kEscrowInstrumentInformationFormat,
+ obfuscated_gaia_id.c_str(),
+ primary_account_number.c_str(),
+ card_verification_number.c_str());
+
+ MakeRequest(GetEscrowUrl(), post_body);
+}
+
+void EncryptionEscrowClient::EscrowCardVerificationNumber(
+ const std::string& card_verification_number,
+ const std::string& obfuscated_gaia_id) {
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = ESCROW_CARD_VERIFICATION_NUMBER;
+
+ std::string post_body = StringPrintf(
+ kEscrowCardVerficationNumberFormat,
+ obfuscated_gaia_id.c_str(),
+ card_verification_number.c_str());
+
+ MakeRequest(GetEscrowUrl(), post_body);
+}
+
+void EncryptionEscrowClient::MakeRequest(const GURL& url,
+ const std::string& post_body) {
+ DCHECK(!request_.get());
+
+ request_.reset(net::URLFetcher::Create(
+ 1, url, net::URLFetcher::POST, this));
+ request_->SetRequestContext(context_getter_);
+ DVLOG(1) << "url=" << url << ", post_body=" << post_body;
+ request_->SetUploadData(kApplicationMimeType, post_body);
+ request_->Start();
+}
+
+// TODO(ahutter): Add manual retry logic if it's necessary.
+void EncryptionEscrowClient::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ DCHECK(observer_);
+ scoped_ptr<net::URLFetcher> old_request = request_.Pass();
+ DCHECK_EQ(source, old_request.get());
+
+ DVLOG(1) << "Got response from " << source->GetOriginalURL();
+
+ RequestType type = request_type_;
+ request_type_ = NO_PENDING_REQUEST;
+
+ std::string data;
+ source->GetResponseAsString(&data);
+ DVLOG(1) << "Response body: " << data;
+
+ if (source->GetResponseCode() != net::HTTP_OK) {
+ observer_->OnNetworkError(source->GetResponseCode());
+ return;
+ }
+
+ if (data.empty()) {
+ HandleMalformedResponse(old_request.get());
+ return;
+ }
+
+ switch (type) {
+ case ENCRYPT_ONE_TIME_PAD: {
+ std::vector<std::string> splits;
+ // The response from the server should be formatted as
+ // "<session material>|<encrypted one time pad>".
+ base::SplitString(data, '|', &splits);
+ if (splits.size() == 2)
+ observer_->OnDidEncryptOneTimePad(splits[1], splits[0]);
+ else
+ HandleMalformedResponse(old_request.get());
+ break;
+ }
+
+ case ESCROW_INSTRUMENT_INFORMATION:
+ observer_->OnDidEscrowInstrumentInformation(data);
+ break;
+
+ case ESCROW_CARD_VERIFICATION_NUMBER:
+ observer_->OnDidEscrowCardVerificationNumber(data);
+ break;
+
+ case NO_PENDING_REQUEST:
+ NOTREACHED();
+ }
+}
+
+void EncryptionEscrowClient::HandleMalformedResponse(net::URLFetcher* request) {
+ // Called to inform exponential backoff logic of the error.
+ request->ReceivedContentWasMalformed();
+ observer_->OnMalformedResponse();
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/encryption_escrow_client.h b/components/autofill/browser/wallet/encryption_escrow_client.h
new file mode 100644
index 0000000..12d6078
--- /dev/null
+++ b/components/autofill/browser/wallet/encryption_escrow_client.h
@@ -0,0 +1,91 @@
+// Copyright 2013 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_BROWSER_WALLET_ENCRYPTION_ESCROW_CLIENT_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_ENCRYPTION_ESCROW_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+class GURL;
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace autofill {
+namespace wallet {
+
+class EncryptionEscrowClientObserver;
+class Instrument;
+
+// EncrytionEscrowClient is responsible for making calls to the Online Wallet
+// encryption and escrow backend.
+class EncryptionEscrowClient : public net::URLFetcherDelegate {
+ public:
+ // |observer| must outlive |this|.
+ EncryptionEscrowClient(net::URLRequestContextGetter* context_getter,
+ EncryptionEscrowClientObserver* observer);
+ virtual ~EncryptionEscrowClient();
+
+ // Sends |one_time_pad|, a vector of cryptographically secure random bytes, to
+ // Online Wallet to be encrypted. These bytes must be generated using
+ // crypto/random.h.
+ void EncryptOneTimePad(const std::vector<uint8>& one_time_pad);
+
+ // Escrows the card verfication number of an existing instrument with Online
+ // Wallet. The escrow is keyed off of |obfuscated_gaia_id|.
+ void EscrowCardVerificationNumber(const std::string& card_verification_number,
+ const std::string& obfuscated_gaia_id);
+
+ // Escrows the primary account number and card verfication number of
+ // |new_instrument| with Online Wallet. The escrow is keyed off of
+ // |obfuscated_gaia_id|.
+ void EscrowInstrumentInformation(const Instrument& new_instrument,
+ const std::string& obfuscated_gaia_id);
+
+ private:
+ enum RequestType {
+ NO_PENDING_REQUEST,
+ ENCRYPT_ONE_TIME_PAD,
+ ESCROW_INSTRUMENT_INFORMATION,
+ ESCROW_CARD_VERIFICATION_NUMBER,
+ };
+
+ // Posts |post_body| to |url|. When the request is complete, |observer_| is
+ // notified of the result.
+ void MakeRequest(const GURL& url, const std::string& post_body);
+
+ // Performs bookkeeping tasks for any invalid requests.
+ void HandleMalformedResponse(net::URLFetcher* request);
+
+ // net::URLFetcherDelegate:
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // The context for the request. Ensures the gdToken cookie is set as a header
+ // in the requests to Online Wallet if it is present.
+ scoped_refptr<net::URLRequestContextGetter> context_getter_;
+
+ // Observer class that has its various On* methods called based on the results
+ // of a request to Online Wallet.
+ EncryptionEscrowClientObserver* const observer_;
+
+ // The current request object.
+ scoped_ptr<net::URLFetcher> request_;
+
+ // The type of the current request. Must be NO_PENDING_REQUEST for a request
+ // to be initiated as only one request may be running at a given time.
+ RequestType request_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(EncryptionEscrowClient);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_ENCRYPTION_ESCROW_CLIENT_H_
diff --git a/components/autofill/browser/wallet/encryption_escrow_client_observer.h b/components/autofill/browser/wallet/encryption_escrow_client_observer.h
new file mode 100644
index 0000000..0e05926
--- /dev/null
+++ b/components/autofill/browser/wallet/encryption_escrow_client_observer.h
@@ -0,0 +1,50 @@
+// Copyright 2013 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_BROWSER_WALLET_ENCRYPTION_ESCROW_CLIENT_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_ENCRYPTION_ESCROW_CLIENT_OBSERVER_H_
+
+#include <string>
+
+namespace autofill {
+namespace wallet {
+
+// EncryptionEscrowClientObserver is to be implemented by any classes making
+// calls with EncryptionEscrowClient. The appropriate callback method will be
+// called on EncryptionEscrowClientObserver with the response from the Online
+// Wallet encryption and escrow backend.
+class EncryptionEscrowClientObserver {
+ public:
+ // Called when an EncryptOneTimePad request finishes successfully.
+ // |encrypted_one_time_pad| and |session_material| must be used when getting a
+ // FullWallet.
+ virtual void OnDidEncryptOneTimePad(const std::string& encrypted_one_time_pad,
+ const std::string& session_material) = 0;
+
+ // Called when an EscrowCardVerificationNumber request finishes
+ // successfully. |escrow_handle| must be used when authenticating an
+ // instrument.
+ virtual void OnDidEscrowCardVerificationNumber(
+ const std::string& escrow_handle) = 0;
+
+ // Called when an EscrowInstrumentInformation request finishes successfully.
+ // |escrow_handle| must be used when saving a new instrument.
+ virtual void OnDidEscrowInstrumentInformation(
+ const std::string& escrow_handle) = 0;
+
+ // Called when a request fails due to a network error or if the response was
+ // invalid.
+ virtual void OnNetworkError(int response_code) = 0;
+
+ // Called when a request fails due to a malformed response.
+ virtual void OnMalformedResponse() = 0;
+
+ protected:
+ virtual ~EncryptionEscrowClientObserver() {}
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_ENCRYPTION_ESCROW_CLIENT_OBSERVER_H_
diff --git a/components/autofill/browser/wallet/encryption_escrow_client_unittest.cc b/components/autofill/browser/wallet/encryption_escrow_client_unittest.cc
new file mode 100644
index 0000000..13c2b16
--- /dev/null
+++ b/components/autofill/browser/wallet/encryption_escrow_client_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/wallet/encryption_escrow_client.h"
+#include "components/autofill/browser/wallet/encryption_escrow_client_observer.h"
+#include "components/autofill/browser/wallet/instrument.h"
+#include "components/autofill/browser/wallet/wallet_test_util.h"
+#include "content/public/test/test_browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kEncryptOtpRequest[] = "cvv=30:000102030405";
+const char kEncryptOtpResponse[] = "session_material|encrypted_one_time_pad";
+const char kEscrowInstrumentInformationRequest[] =
+ "gid=obfuscated_gaia_id&cardNumber=4444444444444448&cvv=123";
+const char kEscrowCardVerificationNumberRequest[] =
+ "gid=obfuscated_gaia_id&cvv=123";
+
+} // namespace
+
+namespace autofill {
+namespace wallet {
+
+class MockEncryptionEscrowClientObserver :
+ public EncryptionEscrowClientObserver,
+ public base::SupportsWeakPtr<MockEncryptionEscrowClientObserver> {
+ public:
+ MockEncryptionEscrowClientObserver() {}
+ ~MockEncryptionEscrowClientObserver() {}
+
+ MOCK_METHOD2(OnDidEncryptOneTimePad,
+ void(const std::string& encrypted_one_time_pad,
+ const std::string& session_material));
+ MOCK_METHOD1(OnDidEscrowCardVerificationNumber,
+ void(const std::string& escrow_handle));
+ MOCK_METHOD1(OnDidEscrowInstrumentInformation,
+ void(const std::string& escrow_handle));
+ MOCK_METHOD0(OnMalformedResponse, void());
+ MOCK_METHOD1(OnNetworkError, void(int response_code));
+};
+
+class EncryptionEscrowClientTest : public testing::Test {
+ public:
+ EncryptionEscrowClientTest() : io_thread_(content::BrowserThread::IO) {}
+
+ virtual void SetUp() {
+ io_thread_.StartIOThread();
+ profile_.CreateRequestContext();
+ }
+
+ std::vector<uint8> MakeOneTimePad() {
+ std::vector<uint8> one_time_pad;
+ one_time_pad.push_back(0);
+ one_time_pad.push_back(1);
+ one_time_pad.push_back(2);
+ one_time_pad.push_back(3);
+ one_time_pad.push_back(4);
+ one_time_pad.push_back(5);
+ return one_time_pad;
+ }
+
+ virtual void TearDown() {
+ profile_.ResetRequestContext();
+ io_thread_.Stop();
+ }
+
+ void VerifyAndFinishRequest(const net::TestURLFetcherFactory& fetcher_factory,
+ net::HttpStatusCode response_code,
+ const std::string& request_body,
+ const std::string& response_body) {
+ net::TestURLFetcher* fetcher = fetcher_factory.GetFetcherByID(1);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ(request_body, fetcher->upload_data());
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response_body);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+
+ protected:
+ TestingProfile profile_;
+
+ private:
+ // The profile's request context must be released on the IO thread.
+ content::TestBrowserThread io_thread_;
+};
+
+TEST_F(EncryptionEscrowClientTest, NetworkError) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer, OnNetworkError(net::HTTP_UNAUTHORIZED)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EscrowInstrumentInformation(*instrument,
+ "obfuscated_gaia_id");
+ VerifyAndFinishRequest(factory,
+ net::HTTP_UNAUTHORIZED,
+ kEscrowInstrumentInformationRequest,
+ std::string());
+}
+
+TEST_F(EncryptionEscrowClientTest, EscrowInstrumentInformationSuccess) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer, OnDidEscrowInstrumentInformation("abc")).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EscrowInstrumentInformation(*instrument,
+ "obfuscated_gaia_id");
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kEscrowInstrumentInformationRequest,
+ "abc");
+}
+
+TEST_F(EncryptionEscrowClientTest, EscrowInstrumentInformationFailure) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EscrowInstrumentInformation(*instrument,
+ "obfuscated_gaia_id");
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kEscrowInstrumentInformationRequest,
+ std::string());
+}
+
+TEST_F(EncryptionEscrowClientTest, EscrowCardVerificationNumberSuccess) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer, OnDidEscrowCardVerificationNumber("abc")).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EscrowCardVerificationNumber("123",
+ "obfuscated_gaia_id");
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kEscrowCardVerificationNumberRequest,
+ "abc");
+}
+
+TEST_F(EncryptionEscrowClientTest, EscrowCardVerificationNumberFailure) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EscrowCardVerificationNumber("123",
+ "obfuscated_gaia_id");
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kEscrowCardVerificationNumberRequest,
+ std::string());
+}
+
+TEST_F(EncryptionEscrowClientTest, EncryptOneTimePadSuccess) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer,
+ OnDidEncryptOneTimePad("encrypted_one_time_pad",
+ "session_material")).Times(1);
+
+ net::TestURLFetcherFactory factory;
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EncryptOneTimePad(MakeOneTimePad());
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kEncryptOtpRequest,
+ kEncryptOtpResponse);
+}
+
+TEST_F(EncryptionEscrowClientTest, EncryptOneTimePadFailure) {
+ MockEncryptionEscrowClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+ EncryptionEscrowClient encryption_escrow_client(profile_.GetRequestContext(),
+ &observer);
+ encryption_escrow_client.EncryptOneTimePad(MakeOneTimePad());
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kEncryptOtpRequest,
+ std::string());
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/full_wallet.cc b/components/autofill/browser/wallet/full_wallet.cc
new file mode 100644
index 0000000..875ce6c
--- /dev/null
+++ b/components/autofill/browser/wallet/full_wallet.cc
@@ -0,0 +1,224 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/full_wallet.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+
+namespace {
+
+const size_t kPanSize = 16;
+const size_t kBinSize = 6;
+const size_t kCvnSize = 3;
+
+} // anonymous namespace
+
+namespace autofill {
+namespace wallet {
+
+FullWallet::FullWallet(int expiration_month,
+ int expiration_year,
+ const std::string& iin,
+ const std::string& encrypted_rest,
+ scoped_ptr<Address> billing_address,
+ scoped_ptr<Address> shipping_address,
+ const std::vector<RequiredAction>& required_actions)
+ : expiration_month_(expiration_month),
+ expiration_year_(expiration_year),
+ iin_(iin),
+ encrypted_rest_(encrypted_rest),
+ billing_address_(billing_address.Pass()),
+ shipping_address_(shipping_address.Pass()),
+ required_actions_(required_actions) {
+ DCHECK(required_actions_.size() > 0 || billing_address_.get());
+}
+
+FullWallet::~FullWallet() {}
+
+scoped_ptr<FullWallet>
+ FullWallet::CreateFullWallet(const DictionaryValue& dictionary) {
+ const ListValue* required_actions_list;
+ std::vector<RequiredAction> required_actions;
+ if (dictionary.GetList("required_action", &required_actions_list)) {
+ for (size_t i = 0; i < required_actions_list->GetSize(); ++i) {
+ std::string action_string;
+ if (required_actions_list->GetString(i, &action_string)) {
+ RequiredAction action = ParseRequiredActionFromString(action_string);
+ if (!ActionAppliesToFullWallet(action)) {
+ DLOG(ERROR) << "Response from Google wallet with bad required action:"
+ " \"" << action_string << "\"";
+ return scoped_ptr<FullWallet>();
+ }
+ required_actions.push_back(action);
+ }
+ }
+ if (required_actions.size() > 0) {
+ return scoped_ptr<FullWallet>(new FullWallet(-1,
+ -1,
+ std::string(),
+ std::string(),
+ scoped_ptr<Address>(),
+ scoped_ptr<Address>(),
+ required_actions));
+ }
+ } else {
+ DVLOG(1) << "Response from Google wallet missing required actions";
+ }
+
+ int expiration_month;
+ if (!dictionary.GetInteger("expiration_month", &expiration_month)) {
+ DLOG(ERROR) << "Response from Google wallet missing expiration month";
+ return scoped_ptr<FullWallet>();
+ }
+
+ int expiration_year;
+ if (!dictionary.GetInteger("expiration_year", &expiration_year)) {
+ DLOG(ERROR) << "Response from Google wallet missing expiration year";
+ return scoped_ptr<FullWallet>();
+ }
+
+ std::string iin;
+ if (!dictionary.GetString("iin", &iin)) {
+ DLOG(ERROR) << "Response from Google wallet missing iin";
+ return scoped_ptr<FullWallet>();
+ }
+
+ std::string encrypted_rest;
+ if (!dictionary.GetString("rest", &encrypted_rest)) {
+ DLOG(ERROR) << "Response from Google wallet missing rest";
+ return scoped_ptr<FullWallet>();
+ }
+
+ const DictionaryValue* billing_address_dict;
+ if (!dictionary.GetDictionary("billing_address", &billing_address_dict)) {
+ DLOG(ERROR) << "Response from Google wallet missing billing address";
+ return scoped_ptr<FullWallet>();
+ }
+
+ scoped_ptr<Address> billing_address =
+ Address::CreateAddress(*billing_address_dict);
+ if (!billing_address.get()) {
+ DLOG(ERROR) << "Response from Google wallet has malformed billing address";
+ return scoped_ptr<FullWallet>();
+ }
+
+ const DictionaryValue* shipping_address_dict;
+ scoped_ptr<Address> shipping_address;
+ if (dictionary.GetDictionary("shipping_address", &shipping_address_dict)) {
+ shipping_address =
+ Address::CreateAddressWithID(*shipping_address_dict);
+ } else {
+ DVLOG(1) << "Response from Google wallet missing shipping address";
+ }
+
+ return scoped_ptr<FullWallet>(new FullWallet(expiration_month,
+ expiration_year,
+ iin,
+ encrypted_rest,
+ billing_address.Pass(),
+ shipping_address.Pass(),
+ required_actions));
+}
+
+bool FullWallet::HasRequiredAction(RequiredAction action) const {
+ DCHECK(ActionAppliesToFullWallet(action));
+ return std::find(required_actions_.begin(),
+ required_actions_.end(),
+ action) != required_actions_.end();
+}
+
+bool FullWallet::operator==(const FullWallet& other) const {
+ if (expiration_month_ != other.expiration_month_)
+ return false;
+
+ if (expiration_year_ != other.expiration_year_)
+ return false;
+
+ if (iin_ != other.iin_)
+ return false;
+
+ if (encrypted_rest_ != other.encrypted_rest_)
+ return false;
+
+ if (billing_address_.get() && other.billing_address_.get()) {
+ if (*billing_address_.get() != *other.billing_address_.get())
+ return false;
+ } else if (billing_address_.get() || other.billing_address_.get()) {
+ return false;
+ }
+
+ if (shipping_address_.get() && other.shipping_address_.get()) {
+ if (*shipping_address_.get() != *other.shipping_address_.get())
+ return false;
+ } else if (shipping_address_.get() || other.shipping_address_.get()) {
+ return false;
+ }
+
+ if (required_actions_ != other.required_actions_)
+ return false;
+
+ return true;
+}
+
+bool FullWallet::operator!=(const FullWallet& other) const {
+ return !(*this == other);
+}
+
+const std::string& FullWallet::GetPan() {
+ if (pan_.empty())
+ DecryptCardInfo();
+ return pan_;
+}
+
+const std::string& FullWallet::GetCvn() {
+ if (cvn_.empty())
+ DecryptCardInfo();
+ return cvn_;
+}
+
+void FullWallet::DecryptCardInfo() {
+ std::vector<uint8> operating_data;
+ // Convert |encrypted_rest_| to bytes so we can decrypt it with |otp|.
+ if (!base::HexStringToBytes(encrypted_rest_, &operating_data)) {
+ DLOG(ERROR) << "Failed to parse encrypted rest";
+ return;
+ }
+
+ // Ensure |one_time_pad_| and |encrypted_rest_| are of the same length
+ // otherwise something has gone wrong and we can't decrypt the data.
+ DCHECK_EQ(one_time_pad_.size(), operating_data.size());
+
+ std::vector<uint8> results;
+ // XOR |otp| with the encrypted data to decrypt.
+ for (size_t i = 0; i < one_time_pad_.size(); ++i)
+ results.push_back(one_time_pad_[i] ^ operating_data[i]);
+
+ // There is no uint8* to int64 so convert the decrypted data to hex and then
+ // parse the hex to an int64 before getting the int64 as a string.
+ std::string hex_decrypted = base::HexEncode(&(results[0]), results.size());
+
+ int64 decrypted;
+ if (!base::HexStringToInt64(hex_decrypted, &decrypted)) {
+ DLOG(ERROR) << "Failed to parse decrypted data in hex to int64";
+ return;
+ }
+ std::string card_info = base::Int64ToString(decrypted);
+
+ size_t padded_length = kPanSize - kBinSize + kCvnSize;
+ // |card_info| is PAN without the IIN concatenated with the CVN, i.e.
+ // PANPANPANPCVN. If what was decrypted is not of that size the front needs
+ // to be padded with 0's until it is.
+ if (card_info.size() != padded_length)
+ card_info.insert(card_info.begin(), padded_length - card_info.size(), '0');
+
+ // Separate out the PAN from the CVN.
+ size_t split = kPanSize - kBinSize;
+ cvn_ = card_info.substr(split);
+ pan_ = iin_ + card_info.substr(0, split);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/full_wallet.h b/components/autofill/browser/wallet/full_wallet.h
new file mode 100644
index 0000000..c6f2ab5
--- /dev/null
+++ b/components/autofill/browser/wallet/full_wallet.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_FULL_WALLET_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_FULL_WALLET_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/browser/wallet/required_action.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+namespace wallet {
+
+class FullWalletTest;
+
+// FullWallet contains all the information a merchant requires from a user for
+// that user to make a purchase. This includes:
+// - billing information
+// - shipping information
+// - a proxy card for the backing card selected from a user's wallet items
+class FullWallet {
+ public:
+ ~FullWallet();
+
+ // Returns an empty scoped_ptr if the input invalid, an empty wallet with
+ // required actions if there are any, or a valid wallet.
+ static scoped_ptr<FullWallet>
+ CreateFullWallet(const base::DictionaryValue& dictionary);
+
+ // Decrypts and returns the primary account number (PAN) using the generated
+ // one time pad, |one_time_pad_|.
+ const std::string& GetPan();
+
+ // Decrypts and returns the card verification number (CVN) using the generated
+ // one time pad, |one_time_pad_|.
+ const std::string& GetCvn();
+
+ // Whether or not |action| is in |required_actions_|.
+ bool HasRequiredAction(RequiredAction action) const;
+
+ bool operator==(const FullWallet& other) const;
+ bool operator!=(const FullWallet& other) const;
+
+ // If there are required actions |billing_address_| might contain NULL.
+ const Address* billing_address() const { return billing_address_.get(); }
+
+ // If there are required actions or shipping address is not required
+ // |shipping_address_| might contain NULL.
+ const Address* shipping_address() const { return shipping_address_.get(); }
+
+ const std::vector<RequiredAction>& required_actions() const {
+ return required_actions_;
+ }
+ int expiration_month() const { return expiration_month_; }
+ int expiration_year() const { return expiration_year_; }
+
+ void set_one_time_pad(const std::vector<uint8>& one_time_pad) {
+ one_time_pad_ = one_time_pad;
+ }
+
+ private:
+ friend class FullWalletTest;
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, CreateFullWallet);
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, CreateFullWalletWithRequiredActions);
+ FullWallet(int expiration_month,
+ int expiration_year,
+ const std::string& iin,
+ const std::string& encrypted_rest,
+ scoped_ptr<Address> billing_address,
+ scoped_ptr<Address> shipping_address,
+ const std::vector<RequiredAction>& required_actions);
+ void DecryptCardInfo();
+
+ // The expiration month of the proxy card. It should be 1-12.
+ int expiration_month_;
+
+ // The expiration year of the proxy card. It should be a 4-digit year.
+ int expiration_year_;
+
+ // Primary account number (PAN). Its format is \d{16}.
+ std::string pan_;
+
+ // Card verification number (CVN). Its format is \d{3}.
+ std::string cvn_;
+
+ // Issuer identification number (IIN). Its format is \d{6}.
+ std::string iin_;
+
+ // Encrypted concatentation of CVN and PAN without IIN
+ std::string encrypted_rest_;
+
+ // The billing address of the backing instrument.
+ scoped_ptr<Address> billing_address_;
+
+ // The shipping address for the transaction.
+ scoped_ptr<Address> shipping_address_;
+
+ // Actions that must be completed by the user before a FullWallet can be
+ // issued to them by the Online Wallet service.
+ std::vector<RequiredAction> required_actions_;
+
+ // The one time pad used for FullWallet encryption.
+ std::vector<uint8> one_time_pad_;
+
+ DISALLOW_COPY_AND_ASSIGN(FullWallet);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_FULL_WALLET_H_
diff --git a/components/autofill/browser/wallet/full_wallet_unittest.cc b/components/autofill/browser/wallet/full_wallet_unittest.cc
new file mode 100644
index 0000000..346b31c
--- /dev/null
+++ b/components/autofill/browser/wallet/full_wallet_unittest.cc
@@ -0,0 +1,465 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/wallet/full_wallet.h"
+#include "components/autofill/browser/wallet/required_action.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kFullWalletValidResponse[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"iin\":\"iin\","
+ " \"rest\":\"rest\","
+ " \"billing_address\":"
+ " {"
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kFullWalletMissingExpirationMonth[] =
+ "{"
+ " \"expiration_year\":2012,"
+ " \"iin\":\"iin\","
+ " \"rest\":\"rest\","
+ " \"billing_address\":"
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kFullWalletMissingExpirationYear[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"iin\":\"iin\","
+ " \"rest\":\"rest\","
+ " \"billing_address\":"
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kFullWalletMissingIin[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"rest\":\"rest\","
+ " \"billing_address\":"
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kFullWalletMissingRest[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"iin\":\"iin\","
+ " \"billing_address\":"
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kFullWalletMissingBillingAddress[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"iin\":\"iin\","
+ " \"rest\":\"rest\","
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kFullWalletWithRequiredActions[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \"CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS\","
+ " \"update_EXPIRATION_date\","
+ " \"verify_CVV\","
+ " \" REQuIrE_PHONE_NumBER\t\n\r \""
+ " ]"
+ "}";
+
+const char kFullWalletWithInvalidRequiredActions[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \" setup_wallet\","
+ " \"AcCePt_ToS \","
+ " \"UPGRADE_MIN_ADDRESS\","
+ " \"INVALID_form_field\","
+ " \" \\tGAIA_auth \\n\\r\","
+ " \"PASSIVE_GAIA_AUTH\","
+ " \" 忍者の正体 \""
+ " ]"
+ "}";
+
+const char kFullWalletMalformedBillingAddress[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"iin\":\"iin\","
+ " \"rest\":\"rest\","
+ " \"billing_address\":"
+ " {"
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_admin_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+} // anonymous namespace
+
+namespace autofill {
+namespace wallet {
+
+class FullWalletTest : public testing::Test {
+ public:
+ FullWalletTest() {}
+ protected:
+ void SetUpDictionary(const std::string& json) {
+ scoped_ptr<Value> value(base::JSONReader::Read(json));
+ ASSERT_TRUE(value.get());
+ ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY));
+ dict.reset(static_cast<DictionaryValue*>(value.release()));
+ }
+ scoped_ptr<DictionaryValue> dict;
+};
+
+TEST_F(FullWalletTest, CreateFullWalletMissingExpirationMonth) {
+ SetUpDictionary(kFullWalletMissingExpirationMonth);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWalletMissingExpirationYear) {
+ SetUpDictionary(kFullWalletMissingExpirationYear);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWalletMissingIin) {
+ SetUpDictionary(kFullWalletMissingIin);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWalletMissingRest) {
+ SetUpDictionary(kFullWalletMissingRest);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWalletMissingBillingAddress) {
+ SetUpDictionary(kFullWalletMissingBillingAddress);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWalletMalformedBillingAddress) {
+ SetUpDictionary(kFullWalletMalformedBillingAddress);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWalletWithRequiredActions) {
+ SetUpDictionary(kFullWalletWithRequiredActions);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS);
+ required_actions.push_back(UPDATE_EXPIRATION_DATE);
+ required_actions.push_back(VERIFY_CVV);
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+
+ FullWallet full_wallet(-1,
+ -1,
+ "",
+ "",
+ scoped_ptr<Address>(),
+ scoped_ptr<Address>(),
+ required_actions);
+ EXPECT_EQ(full_wallet, *FullWallet::CreateFullWallet(*dict));
+
+ ASSERT_FALSE(required_actions.empty());
+ required_actions.pop_back();
+ FullWallet different_required_actions(
+ -1,
+ -1,
+ "",
+ "",
+ scoped_ptr<Address>(),
+ scoped_ptr<Address>(),
+ required_actions);
+ EXPECT_NE(full_wallet, different_required_actions);
+}
+
+TEST_F(FullWalletTest, CreateFullWalletWithInvalidRequiredActions) {
+ SetUpDictionary(kFullWalletWithInvalidRequiredActions);
+ EXPECT_EQ(NULL, FullWallet::CreateFullWallet(*dict).get());
+}
+
+TEST_F(FullWalletTest, CreateFullWallet) {
+ SetUpDictionary(kFullWalletValidResponse);
+ // NOTE: FullWallet billing address doesn't require an ID.
+ scoped_ptr<Address> billing_address(new Address(
+ "country_name_code",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ ""));
+ scoped_ptr<Address> shipping_address(new Address(
+ "ship_country_name_code",
+ ASCIIToUTF16("ship_recipient_name"),
+ ASCIIToUTF16("ship_address_line_1"),
+ ASCIIToUTF16("ship_address_line_2"),
+ ASCIIToUTF16("ship_locality_name"),
+ ASCIIToUTF16("ship_admin_area_name"),
+ ASCIIToUTF16("ship_postal_code_number"),
+ ASCIIToUTF16("ship_phone_number"),
+ "ship_id"));
+ std::vector<RequiredAction> required_actions;
+ FullWallet full_wallet(12,
+ 2012,
+ "iin",
+ "rest",
+ billing_address.Pass(),
+ shipping_address.Pass(),
+ required_actions);
+ EXPECT_EQ(full_wallet, *FullWallet::CreateFullWallet(*dict));
+}
+
+// TODO(ahutter): Add tests for GetPan and GetCvn.
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/instrument.cc b/components/autofill/browser/wallet/instrument.cc
new file mode 100644
index 0000000..44c9d6748
--- /dev/null
+++ b/components/autofill/browser/wallet/instrument.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 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/browser/wallet/instrument.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/validation.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+std::string FormOfPaymentToString(Instrument::FormOfPayment form_of_payment) {
+ switch (form_of_payment) {
+ case Instrument::UNKNOWN:
+ return "UNKNOWN";
+ case Instrument::VISA:
+ return "VISA";
+ case Instrument::MASTER_CARD:
+ return "MASTER_CARD";
+ case Instrument::AMEX:
+ return "AMEX";
+ case Instrument::DISCOVER:
+ return "DISCOVER";
+ case Instrument::JCB:
+ return "JCB";
+ }
+ NOTREACHED();
+ return "NOT_POSSIBLE";
+}
+
+} // namespace
+
+Instrument::Instrument(const string16& primary_account_number,
+ const string16& card_verification_number,
+ int expiration_month,
+ int expiration_year,
+ FormOfPayment form_of_payment,
+ scoped_ptr<Address> address)
+ : primary_account_number_(primary_account_number),
+ card_verification_number_(card_verification_number),
+ expiration_month_(expiration_month),
+ expiration_year_(expiration_year),
+ form_of_payment_(form_of_payment),
+ address_(address.Pass()) {
+ DCHECK(address_);
+ Init();
+}
+
+Instrument::Instrument(const Instrument& instrument)
+ : primary_account_number_(instrument.primary_account_number()),
+ card_verification_number_(instrument.card_verification_number()),
+ expiration_month_(instrument.expiration_month()),
+ expiration_year_(instrument.expiration_year()),
+ form_of_payment_(instrument.form_of_payment()),
+ address_(new Address(instrument.address())) {
+ Init();
+}
+
+Instrument::~Instrument() {}
+
+scoped_ptr<base::DictionaryValue> Instrument::ToDictionary() const {
+ // |primary_account_number_| and |card_verification_number_| can never be
+ // sent the server in way that would require putting them into a dictionary.
+ // Never add them to this function.
+
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ dict->SetString("type", "CREDIT_CARD");
+ dict->SetInteger("credit_card.exp_month", expiration_month_);
+ dict->SetInteger("credit_card.exp_year", expiration_year_);
+ dict->SetString("credit_card.fop_type",
+ FormOfPaymentToString(form_of_payment_));
+ dict->SetString("credit_card.last_4_digits", last_four_digits_);
+ dict->Set("credit_card.address",
+ address_.get()->ToDictionaryWithoutID().release());
+
+ return dict.Pass();
+}
+
+bool Instrument::IsValid() const {
+ if (!IsStringASCII(primary_account_number_))
+ return false;
+ bool primary_account_number_valid =
+ autofill::IsValidCreditCardNumber(primary_account_number_);
+ bool card_verification_number_valid = card_verification_number_.size() == 3 ||
+ card_verification_number_.size() == 4;
+ bool exp_month_valid = expiration_month_ >= 1 && expiration_month_ <= 12;
+ bool exp_year_valid = expiration_year_ >= 2013 && expiration_year_ <= 2100;
+
+ return primary_account_number_valid &&
+ card_verification_number_valid &&
+ exp_month_valid &&
+ exp_year_valid;
+}
+
+void Instrument::Init() {
+ if (primary_account_number_.size() >= 4) {
+ last_four_digits_ =
+ primary_account_number_.substr(primary_account_number_.size() - 4);
+ }
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/instrument.h b/components/autofill/browser/wallet/instrument.h
new file mode 100644
index 0000000..779ac75
--- /dev/null
+++ b/components/autofill/browser/wallet/instrument.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2013 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_BROWSER_WALLET_INSTRUMENT_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_INSTRUMENT_H_
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+namespace wallet {
+
+class Address;
+
+// This class contains all the data necessary to save a new instrument to a
+// user's Google Wallet using WalletClient::SaveInstrument or
+// WalletClient::SaveInstrumentAndAddress.
+class Instrument {
+ public:
+ enum FormOfPayment {
+ UNKNOWN,
+ VISA,
+ MASTER_CARD,
+ AMEX,
+ DISCOVER,
+ JCB,
+ };
+
+ Instrument(const string16& primary_account_number,
+ const string16& card_verification_number,
+ int expiration_month,
+ int expiration_year,
+ FormOfPayment form_of_payment,
+ scoped_ptr<Address> address);
+ Instrument(const Instrument& instrument);
+ ~Instrument();
+
+ scoped_ptr<base::DictionaryValue> ToDictionary() const;
+
+ // Users of this class should call IsValid to check that the inputs provided
+ // in the constructor were valid for use with Google Wallet.
+ bool IsValid() const;
+
+ const string16& primary_account_number() const {
+ return primary_account_number_;
+ }
+ const string16& card_verification_number() const {
+ return card_verification_number_;
+ }
+ int expiration_month() const { return expiration_month_; }
+ int expiration_year() const { return expiration_year_; }
+ const Address& address() const { return *address_; }
+ FormOfPayment form_of_payment() const { return form_of_payment_; }
+ const string16& last_four_digits() { return last_four_digits_; }
+
+ private:
+ void Init();
+
+ // |primary_account_number_| is expected to be \d{12-19}.
+ string16 primary_account_number_;
+
+ // |card_verification_number_| is expected to be \d{3-4}.
+ string16 card_verification_number_;
+
+ // |expiration month_| should be 1-12.
+ int expiration_month_;
+
+ // |expiration_year_| should be a 4-digit year.
+ int expiration_year_;
+
+ // The payment network of the instrument, e.g. Visa.
+ FormOfPayment form_of_payment_;
+
+ // The billing address of the instrument.
+ scoped_ptr<Address> address_;
+
+ // The last four digits of |primary_account_number_|.
+ string16 last_four_digits_;
+
+ DISALLOW_ASSIGN(Instrument);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_INSTRUMENT_H_
diff --git a/components/autofill/browser/wallet/instrument_unittest.cc b/components/autofill/browser/wallet/instrument_unittest.cc
new file mode 100644
index 0000000..e2b945b
--- /dev/null
+++ b/components/autofill/browser/wallet/instrument_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/wallet/instrument.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+#include "components/autofill/browser/wallet/wallet_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kPrimaryAccountNumber[] = "4444444444444448";
+const char kCardVerificationNumber[] = "123";
+const char kLastFourDigits[] = "4448";
+
+}
+
+namespace autofill {
+namespace wallet {
+
+TEST(Instrument, LastFourDigits) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_EQ(ASCIIToUTF16(kLastFourDigits), instrument.last_four_digits());
+ EXPECT_TRUE(instrument.IsValid());
+}
+
+TEST(Instrument, NoPrimaryAccountNumberIsInvalid) {
+ Instrument instrument(string16(),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooShortPrimaryAccountNumberIsInvalid) {
+ Instrument instrument(ASCIIToUTF16("44447"),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooLongPrimaryAccountNumberIsInvalid) {
+ Instrument instrument(ASCIIToUTF16("44444444444444444448"),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, PrimaryAccountNumberNotPassingLuhnIsInvalid) {
+ Instrument instrument(ASCIIToUTF16("4444444444444444"),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, NoCardVerificationNumberIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ string16(),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooShortCardVerificationNumberIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16("12"),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooLongCardVerificationNumberIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16("12345"),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, ZeroAsExpirationMonthIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 0,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooLargeExpirationMonthIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 13,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooSmallExpirationYearIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 999,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, TooLargeExpirationYearIsInvalid) {
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 10000,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_FALSE(instrument.IsValid());
+}
+
+TEST(Instrument, ToDictionary) {
+ base::DictionaryValue expected;
+ expected.SetString("type", "CREDIT_CARD");
+ expected.SetInteger("credit_card.exp_month", 12);
+ expected.SetInteger("credit_card.exp_year", 2015);
+ expected.SetString("credit_card.last_4_digits", kLastFourDigits);
+ expected.SetString("credit_card.fop_type", "VISA");
+ expected.SetString("credit_card.address.country_name_code",
+ "ship_country_name_code");
+ expected.SetString("credit_card.address.recipient_name",
+ "ship_recipient_name");
+ expected.SetString("credit_card.address.locality_name",
+ "ship_locality_name");
+ expected.SetString("credit_card.address.administrative_area_name",
+ "ship_admin_area_name");
+ expected.SetString("credit_card.address.postal_code_number",
+ "ship_postal_code_number");
+ base::ListValue* address_lines = new base::ListValue();
+ address_lines->AppendString("ship_address_line_1");
+ address_lines->AppendString("ship_address_line_2");
+ expected.Set("credit_card.address.address_line", address_lines);
+
+ Instrument instrument(ASCIIToUTF16(kPrimaryAccountNumber),
+ ASCIIToUTF16(kCardVerificationNumber),
+ 12,
+ 2015,
+ Instrument::VISA,
+ GetTestShippingAddress().Pass());
+
+ EXPECT_TRUE(expected.Equals(instrument.ToDictionary().get()));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/required_action.cc b/components/autofill/browser/wallet/required_action.cc
new file mode 100644
index 0000000..029f5f1
--- /dev/null
+++ b/components/autofill/browser/wallet/required_action.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/required_action.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+
+namespace autofill {
+namespace wallet {
+
+bool ActionAppliesToFullWallet(RequiredAction action) {
+ return action == UPDATE_EXPIRATION_DATE ||
+ action == VERIFY_CVV ||
+ action == CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS ||
+ action == REQUIRE_PHONE_NUMBER;
+}
+
+bool ActionAppliesToSaveToWallet(RequiredAction action) {
+ return action == INVALID_FORM_FIELD ||
+ action == REQUIRE_PHONE_NUMBER;
+}
+
+bool ActionAppliesToWalletItems(RequiredAction action) {
+ return action == SETUP_WALLET ||
+ action == ACCEPT_TOS ||
+ action == GAIA_AUTH ||
+ action == REQUIRE_PHONE_NUMBER ||
+ action == UPDATE_EXPIRATION_DATE ||
+ action == UPGRADE_MIN_ADDRESS ||
+ action == PASSIVE_GAIA_AUTH;
+}
+
+RequiredAction ParseRequiredActionFromString(const std::string& str) {
+ std::string str_lower;
+ TrimWhitespaceASCII(StringToLowerASCII(str), TRIM_ALL, &str_lower);
+
+ if (str_lower == "setup_wallet")
+ return SETUP_WALLET;
+ else if (str_lower == "accept_tos")
+ return ACCEPT_TOS;
+ else if (str_lower == "gaia_auth")
+ return GAIA_AUTH;
+ else if (str_lower == "update_expiration_date")
+ return UPDATE_EXPIRATION_DATE;
+ else if (str_lower == "upgrade_min_address")
+ return UPGRADE_MIN_ADDRESS;
+ else if (str_lower == "invalid_form_field")
+ return INVALID_FORM_FIELD;
+ else if (str_lower == "verify_cvv")
+ return VERIFY_CVV;
+ else if (str_lower == "passive_gaia_auth")
+ return PASSIVE_GAIA_AUTH;
+ else if (str_lower == "require_phone_number")
+ return REQUIRE_PHONE_NUMBER;
+ else if (str_lower == "choose_another_instrument_or_address")
+ return CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS;
+
+ DLOG(ERROR) << "Failed to parse: \"" << str << "\" as a required action";
+ return UNKNOWN_TYPE;
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/required_action.h b/components/autofill/browser/wallet/required_action.h
new file mode 100644
index 0000000..d62efef
--- /dev/null
+++ b/components/autofill/browser/wallet/required_action.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_REQUIRED_ACTION_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_REQUIRED_ACTION_H_
+
+#include <string>
+
+namespace autofill {
+namespace wallet {
+
+// Required actions are steps that must be taken before the current transaction
+// can proceed. Examples of this is include accepting the Terms of Service to
+// use Google Wallet (happens on first use or when the ToS are updated) or
+// typing a CVC when it's necessary verify the current user has access to the
+// backing card.
+enum RequiredAction {
+ UNKNOWN_TYPE = 0, // Catch all type.
+ CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS,
+ SETUP_WALLET,
+ ACCEPT_TOS,
+ GAIA_AUTH,
+ UPDATE_EXPIRATION_DATE,
+ UPGRADE_MIN_ADDRESS,
+ INVALID_FORM_FIELD,
+ VERIFY_CVV,
+ PASSIVE_GAIA_AUTH,
+ REQUIRE_PHONE_NUMBER,
+};
+
+// Static helper functions to determine if an RequiredAction applies to a
+// FullWallet, WalletItems, or SaveToWallet response.
+bool ActionAppliesToFullWallet(RequiredAction action);
+bool ActionAppliesToSaveToWallet(RequiredAction action);
+bool ActionAppliesToWalletItems(RequiredAction action);
+
+// Turn a string value of the parsed JSON response into an RequiredAction.
+RequiredAction ParseRequiredActionFromString(const std::string& str);
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_REQUIRED_ACTION_H_
diff --git a/components/autofill/browser/wallet/wallet_address.cc b/components/autofill/browser/wallet/wallet_address.cc
new file mode 100644
index 0000000..7269ab9
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_address.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/wallet_address.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/autofill_country.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+Address* CreateAddressInternal(const base::DictionaryValue& dictionary,
+ const std::string& object_id) {
+ std::string country_name_code;
+ if (!dictionary.GetString("postal_address.country_name_code",
+ &country_name_code)) {
+ DLOG(ERROR) << "Response from Google Wallet missing country name";
+ return NULL;
+ }
+
+ string16 recipient_name;
+ if (!dictionary.GetString("postal_address.recipient_name",
+ &recipient_name)) {
+ DLOG(ERROR) << "Response from Google Wallet recipient name";
+ return NULL;
+ }
+
+ string16 postal_code_number;
+ if (!dictionary.GetString("postal_address.postal_code_number",
+ &postal_code_number)) {
+ DLOG(ERROR) << "Response from Google Wallet missing postal code number";
+ return NULL;
+ }
+
+ string16 phone_number;
+ if (!dictionary.GetString("phone_number", &phone_number))
+ DVLOG(1) << "Response from Google Wallet missing phone number";
+
+ string16 address_line_1;
+ string16 address_line_2;
+ const ListValue* address_line_list;
+ if (dictionary.GetList("postal_address.address_line", &address_line_list)) {
+ if (!address_line_list->GetString(0, &address_line_1))
+ DVLOG(1) << "Response from Google Wallet missing address line 1";
+ if (!address_line_list->GetString(1, &address_line_2))
+ DVLOG(1) << "Response from Google Wallet missing address line 2";
+ } else {
+ DVLOG(1) << "Response from Google Wallet missing address lines";
+ }
+
+ string16 locality_name;
+ if (!dictionary.GetString("postal_address.locality_name",
+ &locality_name)) {
+ DVLOG(1) << "Response from Google Wallet missing locality name";
+ }
+
+ string16 administrative_area_name;
+ if (!dictionary.GetString("postal_address.administrative_area_name",
+ &administrative_area_name)) {
+ DVLOG(1) << "Response from Google Wallet missing administrative area name";
+ }
+
+ return new Address(country_name_code,
+ recipient_name ,
+ address_line_1,
+ address_line_2,
+ locality_name,
+ administrative_area_name,
+ postal_code_number,
+ phone_number,
+ object_id);
+}
+
+} // namespace
+
+Address::Address() {}
+
+Address::Address(const std::string& country_name_code,
+ const string16& recipient_name,
+ const string16& address_line_1,
+ const string16& address_line_2,
+ const string16& locality_name,
+ const string16& administrative_area_name,
+ const string16& postal_code_number,
+ const string16& phone_number,
+ const std::string& object_id)
+ : country_name_code_(country_name_code),
+ recipient_name_(recipient_name),
+ address_line_1_(address_line_1),
+ address_line_2_(address_line_2),
+ locality_name_(locality_name),
+ administrative_area_name_(administrative_area_name),
+ postal_code_number_(postal_code_number),
+ phone_number_(phone_number),
+ object_id_(object_id) {}
+
+Address::~Address() {}
+
+// static
+scoped_ptr<Address> Address::CreateAddressWithID(
+ const base::DictionaryValue& dictionary) {
+ std::string object_id;
+ if (!dictionary.GetString("id", &object_id)) {
+ DLOG(ERROR) << "Response from Google Wallet missing object id";
+ return scoped_ptr<Address>();
+ }
+ return scoped_ptr<Address>(CreateAddressInternal(dictionary, object_id));
+}
+
+// static
+scoped_ptr<Address> Address::CreateAddress(
+ const base::DictionaryValue& dictionary) {
+ std::string object_id;
+ dictionary.GetString("id", &object_id);
+ return scoped_ptr<Address>(CreateAddressInternal(dictionary, object_id));
+}
+
+// static
+scoped_ptr<Address> Address::CreateDisplayAddress(
+ const base::DictionaryValue& dictionary) {
+ std::string country_code;
+ if (!dictionary.GetString("country_code", &country_code)) {
+ DLOG(ERROR) << "Reponse from Google Wallet missing country code";
+ return scoped_ptr<Address>();
+ }
+
+ string16 name;
+ if (!dictionary.GetString("name", &name)) {
+ DLOG(ERROR) << "Reponse from Google Wallet missing name";
+ return scoped_ptr<Address>();
+ }
+
+ string16 postal_code;
+ if (!dictionary.GetString("postal_code", &postal_code)) {
+ DLOG(ERROR) << "Reponse from Google Wallet missing postal code";
+ return scoped_ptr<Address>();
+ }
+
+ string16 address1;
+ if (!dictionary.GetString("address1", &address1))
+ DVLOG(1) << "Reponse from Google Wallet missing address1";
+
+ string16 address2;
+ if (!dictionary.GetString("address2", &address2))
+ DVLOG(1) << "Reponse from Google Wallet missing address2";
+
+ string16 city;
+ if (!dictionary.GetString("city", &city))
+ DVLOG(1) << "Reponse from Google Wallet missing city";
+
+ string16 state;
+ if (!dictionary.GetString("state", &state))
+ DVLOG(1) << "Reponse from Google Wallet missing state";
+
+ string16 phone_number;
+ if (!dictionary.GetString("phone_number", &phone_number))
+ DVLOG(1) << "Reponse from Google Wallet missing phone number";
+
+ return scoped_ptr<Address>(new Address(country_code,
+ name,
+ address1,
+ address2,
+ city,
+ state,
+ postal_code,
+ phone_number,
+ std::string()));
+}
+
+scoped_ptr<base::DictionaryValue> Address::ToDictionaryWithID() const {
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+
+ if (!object_id_.empty())
+ dict->SetString("id", object_id_);
+ dict->SetString("phone_number", phone_number_);
+ dict->Set("postal_address", ToDictionaryWithoutID().release());
+
+ return dict.Pass();
+}
+
+scoped_ptr<base::DictionaryValue> Address::ToDictionaryWithoutID() const {
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+
+ scoped_ptr<base::ListValue> address_lines(new base::ListValue());
+ address_lines->AppendString(address_line_1_);
+ if (!address_line_2_.empty())
+ address_lines->AppendString(address_line_2_);
+ dict->Set("address_line", address_lines.release());
+
+ dict->SetString("country_name_code", country_name_code_);
+ dict->SetString("recipient_name", recipient_name_);
+ dict->SetString("locality_name", locality_name_);
+ dict->SetString("administrative_area_name",
+ administrative_area_name_);
+ dict->SetString("postal_code_number", postal_code_number_);
+
+ return dict.Pass();
+}
+
+string16 Address::DisplayName() const {
+ // TODO(estade): improve this stub implementation.
+ return recipient_name() + ASCIIToUTF16(", ") + address_line_1();
+}
+
+string16 Address::GetInfo(AutofillFieldType type) const {
+ switch (type) {
+ case NAME_FULL:
+ return recipient_name();
+
+ case ADDRESS_HOME_LINE1:
+ return address_line_1();
+
+ case ADDRESS_HOME_LINE2:
+ return address_line_2();
+
+ case ADDRESS_HOME_CITY:
+ return locality_name();
+
+ case ADDRESS_HOME_STATE:
+ return administrative_area_name();
+
+ case ADDRESS_HOME_ZIP:
+ return postal_code_number();
+
+ case ADDRESS_HOME_COUNTRY: {
+ AutofillCountry country(country_name_code(),
+ AutofillCountry::ApplicationLocale());
+ return country.name();
+ }
+
+ case PHONE_HOME_WHOLE_NUMBER:
+ return phone_number();
+
+ // TODO(estade): implement more.
+ default:
+ NOTREACHED();
+ return string16();
+ }
+}
+
+bool Address::operator==(const Address& other) const {
+ return country_name_code_ == other.country_name_code_ &&
+ recipient_name_ == other.recipient_name_ &&
+ address_line_1_ == other.address_line_1_ &&
+ address_line_2_ == other.address_line_2_ &&
+ locality_name_ == other.locality_name_ &&
+ administrative_area_name_ == other.administrative_area_name_ &&
+ postal_code_number_ == other.postal_code_number_ &&
+ phone_number_ == other.phone_number_ &&
+ object_id_ == other.object_id_;
+}
+
+bool Address::operator!=(const Address& other) const {
+ return !(*this == other);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_address.h b/components/autofill/browser/wallet/wallet_address.h
new file mode 100644
index 0000000..9aa8314
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_address.h
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_ADDRESS_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_ADDRESS_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string16.h"
+#include "components/autofill/browser/field_types.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+namespace wallet {
+
+// TODO(ahutter): This address is a lot like
+// components/autofill/browser/address.h. There should be a super
+// class that both extend from to clean up duplicated code. See
+// http://crbug.com/164463.
+
+// Address contains various address fields that have been populated from the
+// user's Online Wallet. It is loosely modeled as a subet of the OASIS
+// "extensible Address Language" (xAL); see
+// http://www.oasis-open.org/committees/ciq/download.shtml.
+class Address {
+ public:
+ Address();
+ // TODO(ahutter): Use additional fields (descriptive_name, is_post_box,
+ // is_minimal_address, is_valid, is_default) when SaveToWallet is implemented.
+ // See http://crbug.com/164284.
+ Address(const std::string& country_name_code,
+ const string16& recipient_name,
+ const string16& address_line_1,
+ const string16& address_line_2,
+ const string16& locality_name,
+ const string16& administrative_area_name,
+ const string16& postal_code_number,
+ const string16& phone_number,
+ const std::string& object_id);
+ ~Address();
+
+ // Returns an empty scoped_ptr if input is invalid or a valid address that is
+ // selectable for Google Wallet use. Does not require "id" in |dictionary|.
+ // IDs are not required for billing addresses.
+ static scoped_ptr<Address> CreateAddress(
+ const base::DictionaryValue& dictionary);
+
+ // Builds an Address from |dictionary|, which must have an "id" field. This
+ // function is designed for use with shipping addresses. The function may fail
+ // and return an empty pointer if its input is invalid.
+ static scoped_ptr<Address> CreateAddressWithID(
+ const base::DictionaryValue& dictionary);
+
+ // Returns an empty scoped_ptr if input in invalid or a valid address that
+ // can only be used for displaying to the user.
+ static scoped_ptr<Address> CreateDisplayAddress(
+ const base::DictionaryValue& dictionary);
+
+ // If an address is being upgraded, it will be sent to the server in a
+ // different format and with a few additional fields set, most importantly
+ // |object_id_|.
+ scoped_ptr<base::DictionaryValue> ToDictionaryWithID() const;
+
+ // Newly created addresses will not have an associated |object_id_| and are
+ // sent to the server in a slightly different format.
+ scoped_ptr<base::DictionaryValue> ToDictionaryWithoutID() const;
+
+ // Returns a string that summarizes this address, suitable for display to
+ // the user.
+ string16 DisplayName() const;
+
+ // Returns data appropriate for |type|.
+ string16 GetInfo(AutofillFieldType type) const;
+
+ const std::string& country_name_code() const { return country_name_code_; }
+ const string16& recipient_name() const { return recipient_name_; }
+ const string16& address_line_1() const { return address_line_1_; }
+ const string16& address_line_2() const { return address_line_2_; }
+ const string16& locality_name() const { return locality_name_; }
+ const string16& administrative_area_name() const {
+ return administrative_area_name_;
+ }
+ const string16& postal_code_number() const { return postal_code_number_; }
+ const string16& phone_number() const { return phone_number_; }
+ const std::string& object_id() const { return object_id_; }
+
+ void set_country_name_code(const std::string& country_name_code) {
+ country_name_code_ = country_name_code;
+ }
+ void set_recipient_name(const string16& recipient_name) {
+ recipient_name_ = recipient_name;
+ }
+ void set_address_line_1(const string16& address_line_1) {
+ address_line_1_ = address_line_1;
+ }
+ void set_address_line_2(const string16& address_line_2) {
+ address_line_2_ = address_line_2;
+ }
+ void set_locality_name(const string16& locality_name) {
+ locality_name_ = locality_name;
+ }
+ void set_administrative_area_name(const string16& administrative_area_name) {
+ administrative_area_name_ = administrative_area_name;
+ }
+ void set_postal_code_number(const string16& postal_code_number) {
+ postal_code_number_ = postal_code_number;
+ }
+ void set_phone_number(const string16& phone_number) {
+ phone_number_ = phone_number;
+ }
+ void set_object_id(const std::string& object_id) {
+ object_id_ = object_id;
+ }
+
+ bool operator==(const Address& other) const;
+ bool operator!=(const Address& other) const;
+
+ private:
+ // |country_name_code_| should be an ISO 3166-1-alpha-2 (two letter codes, as
+ // used in DNS). For example, "GB".
+ std::string country_name_code_;
+
+ // The recipient's name. For example "John Doe".
+ string16 recipient_name_;
+
+ // |address_line_1| and |address_line_2| correspond to the "AddressLine"
+ // elements in xAL, which are used to hold unstructured text.
+ string16 address_line_1_;
+ string16 address_line_2_;
+
+ // Locality. This is something of a fuzzy term, but it generally refers to
+ // the city/town portion of an address. In regions of the world where
+ // localities are not well defined or do not fit into this structure well
+ // (for example, Japan and China), leave locality_name empty and use
+ // |address_line_2|.
+ // Examples: US city, IT comune, UK post town.
+ string16 locality_name_;
+
+ // Top-level administrative subdivision of this country.
+ // Examples: US state, IT region, UK constituent nation, JP prefecture.
+ string16 administrative_area_name_;
+
+ // Despite the name, |postal_code_number_| values are frequently alphanumeric.
+ // Examples: "94043", "SW1W", "SW1W 9TQ".
+ string16 postal_code_number_;
+
+ // A valid international phone number. If |phone_number_| is a user provided
+ // value, it should have been validated using libphonenumber by clients of
+ // this class before being set; see http://code.google.com/p/libphonenumber/.
+ string16 phone_number_;
+
+ // Externalized Online Wallet id for this address.
+ std::string object_id_;
+
+ DISALLOW_ASSIGN(Address);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_ADDRESS_H_
diff --git a/components/autofill/browser/wallet/wallet_address_unittest.cc b/components/autofill/browser/wallet/wallet_address_unittest.cc
new file mode 100644
index 0000000..0949c6a
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_address_unittest.cc
@@ -0,0 +1,313 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kAddressMissingObjectId[] =
+ "{"
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ "}";
+
+const char kAddressMissingCountryNameCode[] =
+ "{"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\""
+ " }"
+ "}";
+
+const char kAddressMissingRecipientName[] =
+ "{"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ "}";
+
+const char kAddressMissingPostalCodeNumber[] =
+ "{"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ "}";
+
+const char kValidAddress[] =
+ "{"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"country_name_code\":\"country_name_code\","
+ " \"postal_code_number\":\"postal_code_number\""
+ " }"
+ "}";
+
+const char kClientAddressMissingCountryCode[] =
+ "{"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\""
+ "}";
+
+const char kClientAddressMissingPostalCode[] =
+ "{"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ "}";
+
+const char kClientAddressMissingName[] =
+ "{"
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ "}";
+
+const char kClientValidAddress[] =
+ "{"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ "}";
+
+} // anonymous namespace
+
+namespace autofill {
+namespace wallet {
+
+class WalletAddressTest : public testing::Test {
+ public:
+ WalletAddressTest() {}
+ protected:
+ void SetUpDictionary(const std::string& json) {
+ scoped_ptr<Value> value(base::JSONReader::Read(json));
+ DCHECK(value.get());
+ DCHECK(value->IsType(Value::TYPE_DICTIONARY));
+ dict_.reset(static_cast<DictionaryValue*>(value.release()));
+ }
+ scoped_ptr<const DictionaryValue> dict_;
+};
+
+TEST_F(WalletAddressTest, CreateAddressMissingObjectId) {
+ SetUpDictionary(kAddressMissingObjectId);
+ Address address("country_name_code",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "");
+ ASSERT_EQ(address, *Address::CreateAddress(*dict_));
+}
+
+TEST_F(WalletAddressTest, CreateAddressWithIDMissingObjectId) {
+ SetUpDictionary(kAddressMissingObjectId);
+ ASSERT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressMissingCountryNameCode) {
+ SetUpDictionary(kAddressMissingCountryNameCode);
+ ASSERT_EQ(NULL, Address::CreateAddress(*dict_).get());
+ ASSERT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressMissingRecipientName) {
+ SetUpDictionary(kAddressMissingRecipientName);
+ ASSERT_EQ(NULL, Address::CreateAddress(*dict_).get());
+ ASSERT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressMissingPostalCodeNumber) {
+ SetUpDictionary(kAddressMissingPostalCodeNumber);
+ ASSERT_EQ(NULL, Address::CreateAddress(*dict_).get());
+ ASSERT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressWithID) {
+ SetUpDictionary(kValidAddress);
+ Address address("country_name_code",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "id");
+ ASSERT_EQ(address, *Address::CreateAddress(*dict_));
+ ASSERT_EQ(address, *Address::CreateAddressWithID(*dict_));
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddressMissingCountryNameCode) {
+ SetUpDictionary(kClientAddressMissingCountryCode);
+ ASSERT_EQ(NULL, Address::CreateDisplayAddress(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddressMissingName) {
+ SetUpDictionary(kClientAddressMissingName);
+ ASSERT_EQ(NULL, Address::CreateDisplayAddress(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddressMissingPostalCode) {
+ SetUpDictionary(kClientAddressMissingPostalCode);
+ ASSERT_EQ(NULL, Address::CreateDisplayAddress(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddress) {
+ SetUpDictionary(kClientValidAddress);
+ Address address("country_code",
+ ASCIIToUTF16("name"),
+ ASCIIToUTF16("address1"),
+ ASCIIToUTF16("address2"),
+ ASCIIToUTF16("city"),
+ ASCIIToUTF16("state"),
+ ASCIIToUTF16("postal_code"),
+ ASCIIToUTF16("phone_number"),
+ "");
+ ASSERT_EQ(address, *Address::CreateDisplayAddress(*dict_));
+}
+
+TEST_F(WalletAddressTest, ToDictionaryWithoutID) {
+ base::DictionaryValue expected;
+ expected.SetString("country_name_code",
+ "country_name_code");
+ expected.SetString("recipient_name",
+ "recipient_name");
+ expected.SetString("locality_name",
+ "locality_name");
+ expected.SetString("administrative_area_name",
+ "administrative_area_name");
+ expected.SetString("postal_code_number",
+ "postal_code_number");
+ base::ListValue* address_lines = new base::ListValue();
+ address_lines->AppendString("address_line_1");
+ address_lines->AppendString("address_line_2");
+ expected.Set("address_line", address_lines);
+
+ Address address("country_name_code",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "");
+
+ EXPECT_TRUE(expected.Equals(address.ToDictionaryWithoutID().get()));
+}
+
+TEST_F(WalletAddressTest, ToDictionaryWithID) {
+ base::DictionaryValue expected;
+ expected.SetString("id", "id");
+ expected.SetString("phone_number", "phone_number");
+ expected.SetString("postal_address.country_name_code",
+ "country_name_code");
+ expected.SetString("postal_address.recipient_name",
+ "recipient_name");
+ expected.SetString("postal_address.locality_name",
+ "locality_name");
+ expected.SetString("postal_address.administrative_area_name",
+ "administrative_area_name");
+ expected.SetString("postal_address.postal_code_number",
+ "postal_code_number");
+ base::ListValue* address_lines = new base::ListValue();
+ address_lines->AppendString("address_line_1");
+ address_lines->AppendString("address_line_2");
+ expected.Set("postal_address.address_line", address_lines);
+
+ Address address("country_name_code",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "id");
+
+ EXPECT_TRUE(expected.Equals(address.ToDictionaryWithID().get()));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_client.cc b/components/autofill/browser/wallet/wallet_client.cc
new file mode 100644
index 0000000..fbf17af
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_client.cc
@@ -0,0 +1,664 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/wallet_client.h"
+
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "components/autofill/browser/wallet/cart.h"
+#include "components/autofill/browser/wallet/instrument.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+#include "components/autofill/browser/wallet/wallet_client_observer.h"
+#include "components/autofill/browser/wallet/wallet_items.h"
+#include "components/autofill/browser/wallet/wallet_service_url.h"
+#include "crypto/random.h"
+#include "google_apis/google_api_keys.h"
+#include "googleurl/src/gurl.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+const char kJsonMimeType[] = "application/json";
+const size_t kOneTimePadLength = 6;
+
+std::string AutocheckoutStatusToString(AutocheckoutStatus status) {
+ switch (status) {
+ case MISSING_FIELDMAPPING:
+ return "MISSING_FIELDMAPPING";
+ case MISSING_ADVANCE:
+ return "MISSING_ADVANCE";
+ case CANNOT_PROCEED:
+ return "CANNOT_PROCEED";
+ case SUCCESS:
+ // SUCCESS cannot be sent to the server as it will result in a failure.
+ NOTREACHED();
+ return "ERROR";
+ }
+ NOTREACHED();
+ return "NOT_POSSIBLE";
+}
+
+std::string DialogTypeToFeatureString(autofill::DialogType dialog_type) {
+ switch (dialog_type) {
+ case DIALOG_TYPE_REQUEST_AUTOCOMPLETE:
+ return "REQUEST_AUTOCOMPLETE";
+ case DIALOG_TYPE_AUTOCHECKOUT:
+ return "AUTOCHECKOUT";
+ }
+ NOTREACHED();
+ return "NOT_POSSIBLE";
+}
+
+// Gets and parses required actions from a SaveToWallet response. Returns
+// false if any unknown required actions are seen and true otherwise.
+void GetRequiredActionsForSaveToWallet(
+ const base::DictionaryValue& dict,
+ std::vector<RequiredAction>* required_actions) {
+ const ListValue* required_action_list;
+ if (!dict.GetList("required_action", &required_action_list))
+ return;
+
+ for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
+ std::string action_string;
+ if (required_action_list->GetString(i, &action_string)) {
+ RequiredAction action = ParseRequiredActionFromString(action_string);
+ if (!ActionAppliesToSaveToWallet(action)) {
+ DLOG(ERROR) << "Response from Google wallet with bad required action:"
+ " \"" << action_string << "\"";
+ required_actions->clear();
+ return;
+ }
+ required_actions->push_back(action);
+ }
+ }
+}
+
+// Keys for JSON communication with the Online Wallet server.
+const char kAcceptedLegalDocumentKey[] = "accepted_legal_document";
+const char kApiKeyKey[] = "api_key";
+const char kAuthResultKey[] = "auth_result";
+const char kCartKey[] = "cart";
+const char kEncryptedOtpKey[] = "encrypted_otp";
+const char kFeatureKey[] = "feature";
+const char kGoogleTransactionIdKey[] = "google_transaction_id";
+const char kInstrumentIdKey[] = "instrument_id";
+const char kInstrumentKey[] = "instrument";
+const char kInstrumentEscrowHandleKey[] = "instrument_escrow_handle";
+const char kInstrumentPhoneNumberKey[] = "instrument_phone_number";
+const char kMerchantDomainKey[] = "merchant_domain";
+const char kReasonKey[] = "reason";
+const char kRiskParamsKey[] = "risk_params";
+const char kSelectedAddressIdKey[] = "selected_address_id";
+const char kSelectedInstrumentIdKey[] = "selected_instrument_id";
+const char kSessionMaterialKey[] = "session_material";
+const char kShippingAddressIdKey[] = "shipping_address_id";
+const char kShippingAddressKey[] = "shipping_address";
+const char kSuccessKey[] = "success";
+const char kUpgradedBillingAddressKey[] = "upgraded_billing_address";
+const char kUpgradedInstrumentIdKey[] = "upgraded_instrument_id";
+
+} // namespace
+
+
+WalletClient::WalletClient(net::URLRequestContextGetter* context_getter,
+ WalletClientObserver* observer)
+ : context_getter_(context_getter),
+ observer_(observer),
+ request_type_(NO_PENDING_REQUEST),
+ one_time_pad_(kOneTimePadLength),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ encryption_escrow_client_(context_getter, this)) {
+ DCHECK(context_getter_);
+ DCHECK(observer_);
+}
+
+WalletClient::~WalletClient() {}
+
+void WalletClient::AcceptLegalDocuments(
+ const std::vector<std::string>& document_ids,
+ const std::string& google_transaction_id,
+ const GURL& source_url) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::AcceptLegalDocuments,
+ base::Unretained(this),
+ document_ids,
+ google_transaction_id,
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = ACCEPT_LEGAL_DOCUMENTS;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kGoogleTransactionIdKey, google_transaction_id);
+ request_dict.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+ ListValue* docs_list = new ListValue();
+ for (std::vector<std::string>::const_iterator it = document_ids.begin();
+ it != document_ids.end();
+ ++it) {
+ docs_list->AppendString(*it);
+ }
+ request_dict.Set(kAcceptedLegalDocumentKey, docs_list);
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetAcceptLegalDocumentsUrl(), post_body);
+}
+
+void WalletClient::AuthenticateInstrument(
+ const std::string& instrument_id,
+ const std::string& card_verification_number,
+ const std::string& obfuscated_gaia_id) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::AuthenticateInstrument,
+ base::Unretained(this),
+ instrument_id,
+ card_verification_number,
+ obfuscated_gaia_id));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ DCHECK(pending_request_body_.empty());
+ request_type_ = AUTHENTICATE_INSTRUMENT;
+
+ pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ pending_request_body_.SetString(kRiskParamsKey, GetRiskParams());
+ pending_request_body_.SetString(kInstrumentIdKey, instrument_id);
+
+ encryption_escrow_client_.EscrowCardVerificationNumber(
+ card_verification_number, obfuscated_gaia_id);
+}
+
+void WalletClient::GetFullWallet(const std::string& instrument_id,
+ const std::string& address_id,
+ const GURL& source_url,
+ const Cart& cart,
+ const std::string& google_transaction_id,
+ autofill::DialogType dialog_type) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::GetFullWallet,
+ base::Unretained(this),
+ instrument_id,
+ address_id,
+ source_url,
+ cart,
+ google_transaction_id,
+ dialog_type));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ DCHECK(pending_request_body_.empty());
+ request_type_ = GET_FULL_WALLET;
+
+ pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ pending_request_body_.SetString(kRiskParamsKey, GetRiskParams());
+ pending_request_body_.SetString(kSelectedInstrumentIdKey, instrument_id);
+ pending_request_body_.SetString(kSelectedAddressIdKey, address_id);
+ pending_request_body_.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+ pending_request_body_.SetString(kGoogleTransactionIdKey,
+ google_transaction_id);
+ pending_request_body_.Set(kCartKey, cart.ToDictionary().release());
+ pending_request_body_.SetString(kFeatureKey,
+ DialogTypeToFeatureString(dialog_type));
+
+ crypto::RandBytes(&(one_time_pad_[0]), one_time_pad_.size());
+ encryption_escrow_client_.EncryptOneTimePad(one_time_pad_);
+}
+
+void WalletClient::GetWalletItems(const GURL& source_url) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::GetWalletItems,
+ base::Unretained(this),
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = GET_WALLET_ITEMS;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kRiskParamsKey, GetRiskParams());
+ request_dict.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetGetWalletItemsUrl(), post_body);
+}
+
+void WalletClient::SaveAddress(const Address& shipping_address,
+ const GURL& source_url) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::SaveAddress,
+ base::Unretained(this),
+ shipping_address,
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = SAVE_ADDRESS;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kRiskParamsKey, GetRiskParams());
+ request_dict.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+
+ request_dict.Set(kShippingAddressKey,
+ shipping_address.ToDictionaryWithID().release());
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetSaveToWalletUrl(), post_body);
+}
+
+void WalletClient::SaveInstrument(
+ const Instrument& instrument,
+ const std::string& obfuscated_gaia_id,
+ const GURL& source_url) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::SaveInstrument,
+ base::Unretained(this),
+ instrument,
+ obfuscated_gaia_id,
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ DCHECK(pending_request_body_.empty());
+ request_type_ = SAVE_INSTRUMENT;
+
+ pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ pending_request_body_.SetString(kRiskParamsKey, GetRiskParams());
+ pending_request_body_.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+
+ pending_request_body_.Set(kInstrumentKey,
+ instrument.ToDictionary().release());
+ pending_request_body_.SetString(kInstrumentPhoneNumberKey,
+ instrument.address().phone_number());
+
+ encryption_escrow_client_.EscrowInstrumentInformation(instrument,
+ obfuscated_gaia_id);
+}
+
+void WalletClient::SaveInstrumentAndAddress(
+ const Instrument& instrument,
+ const Address& address,
+ const std::string& obfuscated_gaia_id,
+ const GURL& source_url) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::SaveInstrumentAndAddress,
+ base::Unretained(this),
+ instrument,
+ address,
+ obfuscated_gaia_id,
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ DCHECK(pending_request_body_.empty());
+ request_type_ = SAVE_INSTRUMENT_AND_ADDRESS;
+
+ pending_request_body_.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ pending_request_body_.SetString(kRiskParamsKey, GetRiskParams());
+ pending_request_body_.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+
+ pending_request_body_.Set(kInstrumentKey,
+ instrument.ToDictionary().release());
+ pending_request_body_.SetString(kInstrumentPhoneNumberKey,
+ instrument.address().phone_number());
+
+ pending_request_body_.Set(kShippingAddressKey,
+ address.ToDictionaryWithID().release());
+
+ encryption_escrow_client_.EscrowInstrumentInformation(instrument,
+ obfuscated_gaia_id);
+}
+
+void WalletClient::SendAutocheckoutStatus(
+ AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::string& google_transaction_id) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::SendAutocheckoutStatus,
+ base::Unretained(this),
+ status,
+ source_url,
+ google_transaction_id));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = SEND_STATUS;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ bool success = status == SUCCESS;
+ request_dict.SetBoolean(kSuccessKey, success);
+ request_dict.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+ if (!success)
+ request_dict.SetString(kReasonKey, AutocheckoutStatusToString(status));
+ request_dict.SetString(kGoogleTransactionIdKey, google_transaction_id);
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetSendStatusUrl(), post_body);
+}
+
+void WalletClient::UpdateInstrument(
+ const std::string& instrument_id,
+ const Address& billing_address,
+ const GURL& source_url) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::UpdateInstrument,
+ base::Unretained(this),
+ instrument_id,
+ billing_address,
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = UPDATE_INSTRUMENT;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kRiskParamsKey, GetRiskParams());
+ request_dict.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+
+ request_dict.SetString(kUpgradedInstrumentIdKey, instrument_id);
+ request_dict.SetString(kInstrumentPhoneNumberKey,
+ billing_address.phone_number());
+ request_dict.Set(kUpgradedBillingAddressKey,
+ billing_address.ToDictionaryWithoutID().release());
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetSaveToWalletUrl(), post_body);
+}
+
+bool WalletClient::HasRequestInProgress() const {
+ return request_.get() != NULL;
+}
+
+void WalletClient::CancelPendingRequests() {
+ while (!pending_requests_.empty()) {
+ pending_requests_.pop();
+ }
+}
+
+void WalletClient::MakeWalletRequest(const GURL& url,
+ const std::string& post_body) {
+ DCHECK(!HasRequestInProgress());
+
+ request_.reset(net::URLFetcher::Create(
+ 0, url, net::URLFetcher::POST, this));
+ request_->SetRequestContext(context_getter_);
+ DVLOG(1) << "url=" << url << ", post_body=" << post_body;
+ request_->SetUploadData(kJsonMimeType, post_body);
+ request_->Start();
+}
+
+// TODO(ahutter): Add manual retry logic if it's necessary.
+void WalletClient::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ DCHECK_EQ(source, request_.get());
+ DVLOG(1) << "Got response from " << source->GetOriginalURL();
+
+ std::string data;
+ source->GetResponseAsString(&data);
+ DVLOG(1) << "Response body: " << data;
+
+ scoped_ptr<base::DictionaryValue> response_dict;
+
+ int response_code = source->GetResponseCode();
+ switch (response_code) {
+ // HTTP_BAD_REQUEST means the arguments are invalid. No point retrying.
+ case net::HTTP_BAD_REQUEST: {
+ request_type_ = NO_PENDING_REQUEST;
+ observer_->OnWalletError();
+ return;
+ }
+ // HTTP_OK holds a valid response and HTTP_INTERNAL_SERVER_ERROR holds an
+ // error code and message for the user.
+ case net::HTTP_OK:
+ case net::HTTP_INTERNAL_SERVER_ERROR: {
+ scoped_ptr<Value> message_value(base::JSONReader::Read(data));
+ if (message_value.get() &&
+ message_value->IsType(Value::TYPE_DICTIONARY)) {
+ response_dict.reset(
+ static_cast<base::DictionaryValue*>(message_value.release()));
+ }
+ if (response_code == net::HTTP_INTERNAL_SERVER_ERROR) {
+ request_type_ = NO_PENDING_REQUEST;
+ // TODO(ahutter): Do something with the response. See
+ // http://crbug.com/164410.
+ observer_->OnWalletError();
+ return;
+ }
+ break;
+ }
+ // Anything else is an error.
+ default: {
+ request_type_ = NO_PENDING_REQUEST;
+ observer_->OnNetworkError(response_code);
+ return;
+ }
+ }
+
+ RequestType type = request_type_;
+ request_type_ = NO_PENDING_REQUEST;
+
+ if (!(type == ACCEPT_LEGAL_DOCUMENTS || type == SEND_STATUS) &&
+ !response_dict) {
+ HandleMalformedResponse();
+ return;
+ }
+
+ switch (type) {
+ case ACCEPT_LEGAL_DOCUMENTS:
+ observer_->OnDidAcceptLegalDocuments();
+ break;
+
+ case AUTHENTICATE_INSTRUMENT: {
+ std::string auth_result;
+ if (response_dict->GetString(kAuthResultKey, &auth_result)) {
+ std::string trimmed;
+ TrimWhitespaceASCII(auth_result,
+ TRIM_ALL,
+ &trimmed);
+ observer_->OnDidAuthenticateInstrument(
+ LowerCaseEqualsASCII(trimmed, "success"));
+ } else {
+ HandleMalformedResponse();
+ }
+ break;
+ }
+
+ case SEND_STATUS:
+ observer_->OnDidSendAutocheckoutStatus();
+ break;
+
+ case GET_FULL_WALLET: {
+ scoped_ptr<FullWallet> full_wallet(
+ FullWallet::CreateFullWallet(*response_dict));
+ if (full_wallet) {
+ full_wallet->set_one_time_pad(one_time_pad_);
+ observer_->OnDidGetFullWallet(full_wallet.Pass());
+ } else {
+ HandleMalformedResponse();
+ }
+ break;
+ }
+
+ case GET_WALLET_ITEMS: {
+ scoped_ptr<WalletItems> wallet_items(
+ WalletItems::CreateWalletItems(*response_dict));
+ if (wallet_items)
+ observer_->OnDidGetWalletItems(wallet_items.Pass());
+ else
+ HandleMalformedResponse();
+ break;
+ }
+
+ case SAVE_ADDRESS: {
+ std::string shipping_address_id;
+ std::vector<RequiredAction> required_actions;
+ GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
+ if (response_dict->GetString(kShippingAddressIdKey,
+ &shipping_address_id) ||
+ !required_actions.empty()) {
+ observer_->OnDidSaveAddress(shipping_address_id, required_actions);
+ } else {
+ HandleMalformedResponse();
+ }
+ break;
+ }
+
+ case SAVE_INSTRUMENT: {
+ std::string instrument_id;
+ std::vector<RequiredAction> required_actions;
+ GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
+ if (response_dict->GetString(kInstrumentIdKey, &instrument_id) ||
+ !required_actions.empty()) {
+ observer_->OnDidSaveInstrument(instrument_id, required_actions);
+ } else {
+ HandleMalformedResponse();
+ }
+ break;
+ }
+
+ case SAVE_INSTRUMENT_AND_ADDRESS: {
+ std::string instrument_id;
+ response_dict->GetString(kInstrumentIdKey, &instrument_id);
+ std::string shipping_address_id;
+ response_dict->GetString(kShippingAddressIdKey,
+ &shipping_address_id);
+ std::vector<RequiredAction> required_actions;
+ GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
+ if ((!instrument_id.empty() && !shipping_address_id.empty()) ||
+ !required_actions.empty()) {
+ observer_->OnDidSaveInstrumentAndAddress(instrument_id,
+ shipping_address_id,
+ required_actions);
+ } else {
+ HandleMalformedResponse();
+ }
+ break;
+ }
+
+ case UPDATE_INSTRUMENT: {
+ std::string instrument_id;
+ std::vector<RequiredAction> required_actions;
+ GetRequiredActionsForSaveToWallet(*response_dict, &required_actions);
+ if (response_dict->GetString(kInstrumentIdKey, &instrument_id) ||
+ !required_actions.empty()) {
+ observer_->OnDidUpdateInstrument(instrument_id, required_actions);
+ } else {
+ HandleMalformedResponse();
+ }
+ break;
+ }
+
+ case NO_PENDING_REQUEST:
+ NOTREACHED();
+ }
+
+ request_.reset();
+ StartNextPendingRequest();
+}
+
+void WalletClient::StartNextPendingRequest() {
+ if (pending_requests_.empty())
+ return;
+
+ base::Closure next_request = pending_requests_.front();
+ pending_requests_.pop();
+ next_request.Run();
+}
+
+void WalletClient::HandleMalformedResponse() {
+ // Called to inform exponential backoff logic of the error.
+ request_->ReceivedContentWasMalformed();
+ observer_->OnMalformedResponse();
+}
+
+void WalletClient::OnDidEncryptOneTimePad(
+ const std::string& encrypted_one_time_pad,
+ const std::string& session_material) {
+ DCHECK_EQ(GET_FULL_WALLET, request_type_);
+ pending_request_body_.SetString(kEncryptedOtpKey, encrypted_one_time_pad);
+ pending_request_body_.SetString(kSessionMaterialKey, session_material);
+
+ std::string post_body;
+ base::JSONWriter::Write(&pending_request_body_, &post_body);
+ pending_request_body_.Clear();
+
+ MakeWalletRequest(GetGetFullWalletUrl(), post_body);
+}
+
+void WalletClient::OnDidEscrowInstrumentInformation(
+ const std::string& escrow_handle) {
+ DCHECK(request_type_ == SAVE_INSTRUMENT ||
+ request_type_ == SAVE_INSTRUMENT_AND_ADDRESS);
+
+ pending_request_body_.SetString(kInstrumentEscrowHandleKey, escrow_handle);
+
+ std::string post_body;
+ base::JSONWriter::Write(&pending_request_body_, &post_body);
+ pending_request_body_.Clear();
+
+ MakeWalletRequest(GetSaveToWalletUrl(), post_body);
+}
+
+void WalletClient::OnDidEscrowCardVerificationNumber(
+ const std::string& escrow_handle) {
+ DCHECK_EQ(AUTHENTICATE_INSTRUMENT, request_type_);
+ pending_request_body_.SetString(kInstrumentEscrowHandleKey, escrow_handle);
+
+ std::string post_body;
+ base::JSONWriter::Write(&pending_request_body_, &post_body);
+ pending_request_body_.Clear();
+
+ MakeWalletRequest(GetAuthenticateInstrumentUrl(), post_body);
+}
+
+void WalletClient::OnNetworkError(int response_code) {
+ observer_->OnNetworkError(response_code);
+}
+
+void WalletClient::OnMalformedResponse() {
+ observer_->OnMalformedResponse();
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_client.h b/components/autofill/browser/wallet/wallet_client.h
new file mode 100644
index 0000000..009dc90
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_client.h
@@ -0,0 +1,229 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_H_
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/callback.h" // For base::Closure.
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "components/autofill/browser/autofill_manager_delegate.h"
+#include "components/autofill/browser/wallet/encryption_escrow_client.h"
+#include "components/autofill/browser/wallet/encryption_escrow_client_observer.h"
+#include "components/autofill/browser/wallet/full_wallet.h"
+#include "components/autofill/common/autocheckout_status.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class GURL;
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace autofill {
+namespace wallet {
+
+class Address;
+class Cart;
+class FullWallet;
+class Instrument;
+class WalletClientObserver;
+class WalletItems;
+
+// WalletClient is responsible for making calls to the Online Wallet backend on
+// the user's behalf. The normal flow for using this class is as follows:
+// 1) GetWalletItems should be called to retrieve the user's Wallet.
+// a) If the user does not have a Wallet, they must AcceptLegalDocuments and
+// SaveInstrumentAndAddress before continuing.
+// b) If the user has not acccepte the most recent legal documents for
+// Wallet, they must AcceptLegalDocuments.
+// 2) The user then chooses what instrument and shipping address to use for the
+// current transaction.
+// a) If they choose an instrument with a zip code only address, the billing
+// address will need to be updated using UpdateInstrument.
+// b) The user may also choose to add a new instrument or address using
+// SaveAddress, SaveInstrument, or SaveInstrumentAndAddress.
+// 3) Once the user has selected the backing instrument and shipping address
+// for this transaction, a FullWallet with the fronting card is generated
+// using GetFullWallet.
+// a) GetFullWallet may return a Risk challenge for the user. In that case,
+// the user will need to verify who they are by authenticating their
+// chosen backing instrument through AuthenticateInstrument
+// 4) If the user initiated Autocheckout, SendAutocheckoutStatus to notify
+// Online Wallet of the status flow to record various metrics.
+//
+// WalletClient is designed so only one request to Online Wallet can be outgoing
+// at any one time. If |HasRequestInProgress()| is true while calling e.g.
+// GetWalletItems(), the request will be queued and started later. Queued
+// requests start in the order they were received.
+
+class WalletClient
+ : public net::URLFetcherDelegate,
+ public EncryptionEscrowClientObserver {
+ public:
+ // |context_getter| is reference counted so it has no lifetime or ownership
+ // requirements. |observer| must outlive |this|.
+ WalletClient(net::URLRequestContextGetter* context_getter,
+ WalletClientObserver* observer);
+
+ virtual ~WalletClient();
+
+ // GetWalletItems retrieves the user's online wallet. The WalletItems
+ // returned may require additional action such as presenting legal documents
+ // to the user to be accepted.
+ void GetWalletItems(const GURL& source_url);
+
+ // The GetWalletItems call to the Online Wallet backend may require the user
+ // to accept various legal documents before a FullWallet can be generated.
+ // The |document_ids| and |google_transaction_id| are provided in the response
+ // to the GetWalletItems call.
+ void AcceptLegalDocuments(const std::vector<std::string>& document_ids,
+ const std::string& google_transaction_id,
+ const GURL& source_url);
+
+ // Authenticates that |card_verification_number| is for the backing instrument
+ // with |instrument_id|. |obfuscated_gaia_id| is used as a key when escrowing
+ // |card_verification_number|. |observer| is notified when the request is
+ // complete. Used to respond to Risk challenges.
+ void AuthenticateInstrument(const std::string& instrument_id,
+ const std::string& card_verification_number,
+ const std::string& obfuscated_gaia_id);
+
+ // GetFullWallet retrieves the a FullWallet for the user. |instrument_id| and
+ // |adddress_id| should have been selected by the user in some UI,
+ // |merchant_domain| should come from the BrowserContext, the |cart|
+ // information will have been provided by the browser, |dialog_type| indicates
+ // which dialog requests the full wallet, RequestAutocomplete or Autocheckout,
+ // and |google_transaction_id| is the same one that GetWalletItems returns.
+ void GetFullWallet(const std::string& instrument_id,
+ const std::string& address_id,
+ const GURL& source_url,
+ const Cart& cart,
+ const std::string& google_transaction_id,
+ autofill::DialogType dialog_type);
+
+ // SaveAddress saves a new shipping address.
+ void SaveAddress(const Address& address, const GURL& source_url);
+
+ // SaveInstrument saves a new instrument.
+ void SaveInstrument(const Instrument& instrument,
+ const std::string& obfuscated_gaia_id,
+ const GURL& source_url);
+
+ // SaveInstrumentAndAddress saves a new instrument and address.
+ void SaveInstrumentAndAddress(const Instrument& instrument,
+ const Address& shipping_address,
+ const std::string& obfuscated_gaia_id,
+ const GURL& source_url);
+
+ // SendAutocheckoutStatus is used for tracking the success of Autocheckout
+ // flows. |status| is the result of the flow, |merchant_domain| is the domain
+ // where the purchase occured, and |google_transaction_id| is the same as the
+ // one provided by GetWalletItems.
+ void SendAutocheckoutStatus(autofill::AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::string& google_transaction_id);
+
+ // UpdateInstrument changes the instrument with id |instrument_id| with the
+ // information in |billing_address|. Its primary use is for upgrading ZIP code
+ // only addresses or those missing phone numbers. DO NOT change the name on
+ // |billing_address| from the one returned by Online Wallet or this call will
+ // fail.
+ void UpdateInstrument(const std::string& instrument_id,
+ const Address& billing_address,
+ const GURL& source_url);
+
+ // Whether there is a currently running request (i.e. |request_| != NULL).
+ bool HasRequestInProgress() const;
+
+ // Cancels and clears all |pending_requests_|.
+ void CancelPendingRequests();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WalletClientTest, PendingRequest);
+ FRIEND_TEST_ALL_PREFIXES(WalletClientTest, CancelPendingRequests);
+
+ // TODO(ahutter): Implement this.
+ std::string GetRiskParams() { return std::string(); }
+
+ enum RequestType {
+ NO_PENDING_REQUEST,
+ ACCEPT_LEGAL_DOCUMENTS,
+ AUTHENTICATE_INSTRUMENT,
+ GET_FULL_WALLET,
+ GET_WALLET_ITEMS,
+ SAVE_ADDRESS,
+ SAVE_INSTRUMENT,
+ SAVE_INSTRUMENT_AND_ADDRESS,
+ SEND_STATUS,
+ UPDATE_INSTRUMENT,
+ };
+
+ // Posts |post_body| to |url| and notifies |observer| when the request is
+ // complete.
+ void MakeWalletRequest(const GURL& url, const std::string& post_body);
+
+ // Performs bookkeeping tasks for any invalid requests.
+ void HandleMalformedResponse();
+
+ // Start the next pending request (if any).
+ void StartNextPendingRequest();
+
+ // net::URLFetcherDelegate:
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // EncryptionEscrowClientObserver:
+ virtual void OnDidEncryptOneTimePad(
+ const std::string& encrypted_one_time_pad,
+ const std::string& session_material) OVERRIDE;
+ virtual void OnDidEscrowInstrumentInformation(
+ const std::string& escrow_handle) OVERRIDE;
+ virtual void OnDidEscrowCardVerificationNumber(
+ const std::string& escrow_handle) OVERRIDE;
+ virtual void OnNetworkError(int response_code) OVERRIDE;
+ virtual void OnMalformedResponse() OVERRIDE;
+
+ // The context for the request. Ensures the gdToken cookie is set as a header
+ // in the requests to Online Wallet if it is present.
+ scoped_refptr<net::URLRequestContextGetter> context_getter_;
+
+ // Observer class that has its various On* methods called based on the results
+ // of a request to Online Wallet.
+ WalletClientObserver* const observer_; // must outlive |this|.
+
+ // The current request object.
+ scoped_ptr<net::URLFetcher> request_;
+
+ // The type of the current request. Must be NO_PENDING_REQUEST for a request
+ // to be initiated as only one request may be running at a given time.
+ RequestType request_type_;
+
+ // The one time pad used for GetFullWallet encryption.
+ std::vector<uint8> one_time_pad_;
+
+ // GetFullWallet requests and requests that alter instruments rely on requests
+ // made through the |encryption_escrow_client_| finishing first. The request
+ // body is saved here while that those requests are in flight.
+ base::DictionaryValue pending_request_body_;
+
+ // Requests that are waiting to be run.
+ std::queue<base::Closure> pending_requests_;
+
+ // This client is repsonsible for making encryption and escrow calls to Online
+ // Wallet.
+ EncryptionEscrowClient encryption_escrow_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(WalletClient);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_H_
diff --git a/components/autofill/browser/wallet/wallet_client_observer.h b/components/autofill/browser/wallet/wallet_client_observer.h
new file mode 100644
index 0000000..bca839b
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_client_observer.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2013 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_BROWSER_WALLET_WALLET_CLIENT_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_OBSERVER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace autofill {
+namespace wallet {
+
+class FullWallet;
+class WalletItems;
+
+// WalletClientObserver is to be implemented any classes making calls with
+// WalletClient. The appropriate callback method will be called on
+// WalletClientObserver with the response from the Online Wallet backend.
+class WalletClientObserver {
+ public:
+ // Called when an AcceptLegalDocuments request finishes successfully.
+ virtual void OnDidAcceptLegalDocuments() = 0;
+
+ // Called when an AuthenticateInstrument request finishes successfully.
+ virtual void OnDidAuthenticateInstrument(bool success) = 0;
+
+ // Called when a GetFullWallet request finishes successfully. Ownership is
+ // transferred to implementer of this interface.
+ virtual void OnDidGetFullWallet(scoped_ptr<FullWallet> full_wallet) = 0;
+
+ // Called when a GetWalletItems request finishes successfully. Ownership is
+ // transferred to implementer of this interface.
+ virtual void OnDidGetWalletItems(scoped_ptr<WalletItems> wallet_items) = 0;
+
+ // Called when a SaveAddress request finishes successfully. |address_id| can
+ // be used in subsequent GetFullWallet calls. |required_actions| is populated
+ // if there was a validation error with the data being saved.
+ virtual void OnDidSaveAddress(
+ const std::string& address_id,
+ const std::vector<RequiredAction>& required_actions) = 0;
+
+ // Called when a SaveInstrument request finishes sucessfully. |instrument_id|
+ // can be used in subsequent GetFullWallet calls. |required_actions| is
+ // populated if there was a validation error with the data being saved.
+ virtual void OnDidSaveInstrument(
+ const std::string& instrument_id,
+ const std::vector<RequiredAction>& required_actions) = 0;
+
+ // Called when a SaveInstrumentAndAddress request finishes succesfully.
+ // |instrument_id| and |address_id| can be used in subsequent
+ // GetFullWallet calls. |required_actions| is populated if there was a
+ // validation error with the data being saved.
+ virtual void OnDidSaveInstrumentAndAddress(
+ const std::string& instrument_id,
+ const std::string& address_id,
+ const std::vector<RequiredAction>& required_actions) = 0;
+
+ // Called when a SendAutocheckoutStatus request finishes successfully.
+ virtual void OnDidSendAutocheckoutStatus() = 0;
+
+ // Called when an UpdateInstrument request finishes successfully.
+ // |required_actions| is populated if there was a validation error with the
+ // data being saved.
+ virtual void OnDidUpdateInstrument(
+ const std::string& instrument_id,
+ const std::vector<RequiredAction>& required_actions) = 0;
+
+ // TODO(ahutter): This is going to need more arguments, probably an error
+ // code and a message for the user.
+ // Called when a request fails due to an Online Wallet error.
+ virtual void OnWalletError() = 0;
+
+ // Called when a request fails due to a malformed response.
+ virtual void OnMalformedResponse() = 0;
+
+ // Called when a request fails due to a network error.
+ virtual void OnNetworkError(int response_code) = 0;
+
+ protected:
+ virtual ~WalletClientObserver() {}
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_OBSERVER_H_
diff --git a/components/autofill/browser/wallet/wallet_client_unittest.cc b/components/autofill/browser/wallet/wallet_client_unittest.cc
new file mode 100644
index 0000000..d074bf9
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_client_unittest.cc
@@ -0,0 +1,1441 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/browser/wallet/cart.h"
+#include "components/autofill/browser/wallet/full_wallet.h"
+#include "components/autofill/browser/wallet/instrument.h"
+#include "components/autofill/browser/wallet/wallet_client.h"
+#include "components/autofill/browser/wallet/wallet_client_observer.h"
+#include "components/autofill/browser/wallet/wallet_items.h"
+#include "components/autofill/browser/wallet/wallet_test_util.h"
+#include "components/autofill/common/autocheckout_status.h"
+#include "content/public/test/test_browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kGoogleTransactionId[] = "google-transaction-id";
+const char kMerchantUrl[] = "https://example.com/path?key=value";
+
+const char kGetFullWalletValidResponse[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"iin\":\"iin\","
+ " \"rest\":\"rest\","
+ " \"billing_address\":"
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"address_line_1\","
+ " \"address_line_2\""
+ " ],"
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"ship_id\","
+ " \"phone_number\":\"ship_phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"ship_recipient_name\","
+ " \"address_line\":"
+ " ["
+ " \"ship_address_line_1\","
+ " \"ship_address_line_2\""
+ " ],"
+ " \"locality_name\":\"ship_locality_name\","
+ " \"administrative_area_name\":\"ship_administrative_area_name\","
+ " \"postal_code_number\":\"ship_postal_code_number\","
+ " \"country_name_code\":\"ship_country_name_code\""
+ " }"
+ " },"
+ " \"required_action\":"
+ " ["
+ " ]"
+ "}";
+
+const char kGetFullWalletInvalidResponse[] =
+ "{"
+ " \"garbage\":123"
+ "}";
+
+const char kGetWalletItemsValidResponse[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " ],"
+ " \"google_transaction_id\":\"google_transaction_id\","
+ " \"instrument\":"
+ " ["
+ " {"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":\"currency_code\","
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"brand\":\"monkeys\","
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"default_instrument_id\""
+ " }"
+ " ],"
+ " \"default_instrument_id\":\"default_instrument_id\","
+ " \"obfuscated_gaia_id\":\"obfuscated_gaia_id\","
+ " \"address\":"
+ " ["
+ " ],"
+ " \"default_address_id\":\"default_address_id\","
+ " \"required_legal_document\":"
+ " ["
+ " ]"
+ "}";
+
+const char kSaveAddressValidResponse[] =
+ "{"
+ " \"shipping_address_id\":\"shipping_address_id\""
+ "}";
+
+const char kSaveAddressWithRequiredActionsValidResponse[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kSaveWithInvalidRequiredActionsResponse[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \" setup_wallet\","
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kSaveInvalidResponse[] =
+ "{"
+ " \"garbage\":123"
+ "}";
+
+const char kSaveInstrumentValidResponse[] =
+ "{"
+ " \"instrument_id\":\"instrument_id\""
+ "}";
+
+const char kSaveInstrumentWithRequiredActionsValidResponse[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kSaveInstrumentAndAddressValidResponse[] =
+ "{"
+ " \"shipping_address_id\":\"shipping_address_id\","
+ " \"instrument_id\":\"instrument_id\""
+ "}";
+
+const char kSaveInstrumentAndAddressWithRequiredActionsValidResponse[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kSaveInstrumentAndAddressMissingAddressResponse[] =
+ "{"
+ " \"instrument_id\":\"instrument_id\""
+ "}";
+
+const char kSaveInstrumentAndAddressMissingInstrumentResponse[] =
+ "{"
+ " \"shipping_address_id\":\"shipping_address_id\""
+ "}";
+
+const char kUpdateInstrumentValidResponse[] =
+ "{"
+ " \"instrument_id\":\"instrument_id\""
+ "}";
+
+const char kUpdateInstrumentWithRequiredActionsValidResponse[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kUpdateInstrumentMalformedResponse[] =
+ "{"
+ " \"cheese\":\"monkeys\""
+ "}";
+
+const char kAuthenticateInstrumentFailureResponse[] =
+ "{"
+ " \"auth_result\":\"anything else\""
+ "}";
+
+const char kAuthenticateInstrumentSuccessResponse[] =
+ "{"
+ " \"auth_result\":\"SUCCESS\""
+ "}";
+
+// The JSON below is used to test against the request payload being sent to
+// Online Wallet. It's indented differently since JSONWriter creates compact
+// JSON from DictionaryValues.
+
+const char kAcceptLegalDocumentsValidRequest[] =
+ "{"
+ "\"accepted_legal_document\":"
+ "["
+ "\"doc_1\","
+ "\"doc_2\""
+ "],"
+ "\"google_transaction_id\":\"google-transaction-id\","
+ "\"merchant_domain\":\"https://example.com/\""
+ "}";
+
+const char kAuthenticateInstrumentValidRequest[] =
+ "{"
+ "\"instrument_escrow_handle\":\"escrow_handle\","
+ "\"instrument_id\":\"instrument_id\","
+ "\"risk_params\":\"\""
+ "}";
+
+const char kGetFullWalletValidRequest[] =
+ "{"
+ "\"cart\":"
+ "{"
+ "\"currency_code\":\"currency_code\","
+ "\"total_price\":\"currency_code\""
+ "},"
+ "\"encrypted_otp\":\"encrypted_one_time_pad\","
+ "\"feature\":\"REQUEST_AUTOCOMPLETE\","
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"risk_params\":\"\","
+ "\"selected_address_id\":\"shipping_address_id\","
+ "\"selected_instrument_id\":\"instrument_id\","
+ "\"session_material\":\"session_material\""
+ "}";
+
+const char kGetWalletItemsValidRequest[] =
+ "{"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"risk_params\":\"\""
+ "}";
+
+const char kSaveAddressValidRequest[] =
+ "{"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"risk_params\":\"\","
+ "\"shipping_address\":"
+ "{"
+ "\"phone_number\":\"ship_phone_number\","
+ "\"postal_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"ship_address_line_1\","
+ "\"ship_address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"ship_admin_area_name\","
+ "\"country_name_code\":\"ship_country_name_code\","
+ "\"locality_name\":\"ship_locality_name\","
+ "\"postal_code_number\":\"ship_postal_code_number\","
+ "\"recipient_name\":\"ship_recipient_name\""
+ "}"
+ "}"
+ "}";
+
+const char kSaveInstrumentValidRequest[] =
+ "{"
+ "\"instrument\":"
+ "{"
+ "\"credit_card\":"
+ "{"
+ "\"address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"country_name_code\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"exp_month\":12,"
+ "\"exp_year\":2012,"
+ "\"fop_type\":\"VISA\","
+ "\"last_4_digits\":\"4448\""
+ "},"
+ "\"type\":\"CREDIT_CARD\""
+ "},"
+ "\"instrument_escrow_handle\":\"escrow_handle\","
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"risk_params\":\"\""
+ "}";
+
+const char kSaveInstrumentAndAddressValidRequest[] =
+ "{"
+ "\"instrument\":"
+ "{"
+ "\"credit_card\":"
+ "{"
+ "\"address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"country_name_code\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"exp_month\":12,"
+ "\"exp_year\":2012,"
+ "\"fop_type\":\"VISA\","
+ "\"last_4_digits\":\"4448\""
+ "},"
+ "\"type\":\"CREDIT_CARD\""
+ "},"
+ "\"instrument_escrow_handle\":\"escrow_handle\","
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"risk_params\":\"\","
+ "\"shipping_address\":"
+ "{"
+ "\"phone_number\":\"ship_phone_number\","
+ "\"postal_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"ship_address_line_1\","
+ "\"ship_address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"ship_admin_area_name\","
+ "\"country_name_code\":\"ship_country_name_code\","
+ "\"locality_name\":\"ship_locality_name\","
+ "\"postal_code_number\":\"ship_postal_code_number\","
+ "\"recipient_name\":\"ship_recipient_name\""
+ "}"
+ "}"
+ "}";
+
+const char kSendAutocheckoutStatusOfSuccessValidRequest[] =
+ "{"
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"success\":true"
+ "}";
+
+const char kSendAutocheckoutStatusOfFailureValidRequest[] =
+ "{"
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"reason\":\"CANNOT_PROCEED\","
+ "\"success\":false"
+ "}";
+
+const char kUpdateInstrumentValidRequest[] =
+ "{"
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"risk_params\":\"\","
+ "\"upgraded_billing_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"country_name_code\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"upgraded_instrument_id\":\"instrument_id\""
+ "}";
+
+} // namespace
+
+namespace autofill {
+namespace wallet {
+
+class WalletClientTest : public testing::Test {
+ public:
+ WalletClientTest() : io_thread_(content::BrowserThread::IO) {}
+
+ virtual void SetUp() {
+ io_thread_.StartIOThread();
+ profile_.CreateRequestContext();
+ }
+
+ virtual void TearDown() {
+ profile_.ResetRequestContext();
+ io_thread_.Stop();
+ }
+
+ std::string GetData(net::TestURLFetcher* fetcher) {
+ std::string data = fetcher->upload_data();
+ scoped_ptr<Value> root(base::JSONReader::Read(data));
+
+ // If this is not a JSON dictionary, return plain text.
+ if (root.get() == NULL || !root->IsType(Value::TYPE_DICTIONARY))
+ return data;
+
+ // Remove api_key entry (to prevent accidental leak), return JSON as text.
+ DictionaryValue* dict = static_cast<DictionaryValue*>(root.get());
+ dict->Remove("api_key", NULL);
+ base::JSONWriter::Write(dict, &data);
+ return data;
+ }
+
+ void VerifyAndFinishRequest(const net::TestURLFetcherFactory& fetcher_factory,
+ net::HttpStatusCode response_code,
+ const std::string& request_body,
+ const std::string& response_body) {
+ net::TestURLFetcher* fetcher = fetcher_factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ(request_body, GetData(fetcher));
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response_body);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+ protected:
+ TestingProfile profile_;
+
+ private:
+ // The profile's request context must be released on the IO thread.
+ content::TestBrowserThread io_thread_;
+};
+
+class MockWalletClientObserver : public WalletClientObserver {
+ public:
+ MockWalletClientObserver()
+ : full_wallets_received_(0), wallet_items_received_(0) {}
+ ~MockWalletClientObserver() {}
+
+ MOCK_METHOD0(OnDidAcceptLegalDocuments, void());
+ MOCK_METHOD1(OnDidAuthenticateInstrument, void(bool success));
+ MOCK_METHOD2(OnDidSaveAddress,
+ void(const std::string& address_id,
+ const std::vector<RequiredAction>& required_actions));
+ MOCK_METHOD2(OnDidSaveInstrument,
+ void(const std::string& instrument_id,
+ const std::vector<RequiredAction>& required_actions));
+ MOCK_METHOD3(OnDidSaveInstrumentAndAddress,
+ void(const std::string& instrument_id,
+ const std::string& shipping_address_id,
+ const std::vector<RequiredAction>& required_actions));
+ MOCK_METHOD0(OnDidSendAutocheckoutStatus, void());
+ MOCK_METHOD2(OnDidUpdateInstrument,
+ void(const std::string& instrument_id,
+ const std::vector<RequiredAction>& required_actions));
+ MOCK_METHOD0(OnWalletError, void());
+ MOCK_METHOD0(OnMalformedResponse, void());
+ MOCK_METHOD1(OnNetworkError, void(int response_code));
+
+ virtual void OnDidGetFullWallet(scoped_ptr<FullWallet> full_wallet) OVERRIDE {
+ EXPECT_TRUE(full_wallet);
+ ++full_wallets_received_;
+ }
+ virtual void OnDidGetWalletItems(scoped_ptr<WalletItems> wallet_items)
+ OVERRIDE {
+ EXPECT_TRUE(wallet_items);
+ ++wallet_items_received_;
+ }
+ size_t full_wallets_received() const { return full_wallets_received_; }
+ size_t wallet_items_received() const { return wallet_items_received_; }
+
+ private:
+ size_t full_wallets_received_;
+ size_t wallet_items_received_;
+};
+
+// TODO(ahutter): Implement API compatibility tests. See
+// http://crbug.com/164465.
+
+// TODO(ahutter): Improve this when the error body is captured. See
+// http://crbug.com/164410.
+TEST_F(WalletClientTest, WalletErrorOnExpectedVoidResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnWalletError()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ "");
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+// TODO(ahutter): Improve this when the error body is captured. See
+// http://crbug.com/164410.
+TEST_F(WalletClientTest, WalletErrorOnExpectedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnWalletError()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, NetworkFailureOnExpectedVoidResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnNetworkError(net::HTTP_UNAUTHORIZED)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ "");
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, NetworkFailureOnExpectedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnNetworkError(net::HTTP_UNAUTHORIZED)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_UNAUTHORIZED);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, RequestError) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnWalletError()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ "");
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ fetcher->set_response_code(net::HTTP_BAD_REQUEST);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, GetFullWalletSuccess) {
+ MockWalletClientObserver observer;
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ Cart cart("currency_code", "currency_code");
+ wallet_client.GetFullWallet("instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ cart,
+ "google_transaction_id",
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString(
+ "session_material|encrypted_one_time_pad");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kGetFullWalletValidRequest,
+ kGetFullWalletValidResponse);
+ EXPECT_EQ(1U, observer.full_wallets_received());
+}
+
+TEST_F(WalletClientTest, GetFullWalletEncryptionDown) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnNetworkError(net::HTTP_INTERNAL_SERVER_ERROR)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ Cart cart("currency_code", "currency_code");
+ wallet_client.GetFullWallet("instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ cart,
+ "google_transaction_id",
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ encryption_fetcher->SetResponseString(std::string());
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ EXPECT_EQ(0U, observer.full_wallets_received());
+}
+
+TEST_F(WalletClientTest, GetFullWalletEncryptionMalformed) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ Cart cart("currency_code", "currency_code");
+ wallet_client.GetFullWallet("instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ cart,
+ "google_transaction_id",
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString(
+ "session_material:encrypted_one_time_pad");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ EXPECT_EQ(0U, observer.full_wallets_received());
+}
+
+TEST_F(WalletClientTest, GetFullWalletMalformedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ Cart cart("currency_code", "currency_code");
+ wallet_client.GetFullWallet("instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ cart,
+ "google_transaction_id",
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE);
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString(
+ "session_material|encrypted_one_time_pad");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kGetFullWalletValidRequest,
+ kGetFullWalletInvalidResponse);
+ EXPECT_EQ(0U, observer.full_wallets_received());
+}
+
+TEST_F(WalletClientTest, AcceptLegalDocuments) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnDidAcceptLegalDocuments()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ std::vector<std::string> doc_ids;
+ doc_ids.push_back("doc_1");
+ doc_ids.push_back("doc_2");
+ wallet_client.AcceptLegalDocuments(doc_ids,
+ kGoogleTransactionId,
+ GURL(kMerchantUrl));
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ(kAcceptLegalDocumentsValidRequest, GetData(fetcher));
+ fetcher->SetResponseString(")]}'"); // Invalid JSON. Should be ignored.
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentSucceeded) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnDidAuthenticateInstrument(true)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.AuthenticateInstrument("instrument_id",
+ "cvv",
+ "obfuscated_gaia_id");
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kAuthenticateInstrumentValidRequest,
+ kAuthenticateInstrumentSuccessResponse);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentFailed) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnDidAuthenticateInstrument(false)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.AuthenticateInstrument("instrument_id",
+ "cvv",
+ "obfuscated_gaia_id");
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kAuthenticateInstrumentValidRequest,
+ kAuthenticateInstrumentFailureResponse);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentEscrowDown) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnNetworkError(net::HTTP_INTERNAL_SERVER_ERROR)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.AuthenticateInstrument("instrument_id",
+ "cvv",
+ "obfuscated_gaia_id");
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentEscrowMalformed) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.AuthenticateInstrument("instrument_id",
+ "cvv",
+ "obfuscated_gaia_id");
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentFailedMalformedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.AuthenticateInstrument("instrument_id",
+ "cvv",
+ "obfuscated_gaia_id");
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kAuthenticateInstrumentValidRequest,
+ kSaveInvalidResponse);
+}
+
+// TODO(ahutter): Add failure tests for GetWalletItems.
+
+TEST_F(WalletClientTest, GetWalletItems) {
+ MockWalletClientObserver observer;
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ(kGetWalletItemsValidRequest, GetData(fetcher));
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(kGetWalletItemsValidResponse);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+ EXPECT_EQ(1U, observer.wallet_items_received());
+}
+
+TEST_F(WalletClientTest, SaveAddressSucceeded) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnDidSaveAddress("shipping_address_id",
+ std::vector<RequiredAction>())).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveAddress(*address, GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveAddressValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveAddressWithRequiredActionsSucceeded) {
+ MockWalletClientObserver observer;
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ EXPECT_CALL(observer,
+ OnDidSaveAddress(std::string(),
+ required_actions)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveAddress(*address, GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveAddressWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveAddressFailedInvalidRequiredAction) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveAddress(*address, GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, SaveAddressFailedMalformedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveAddress(*address, GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveInvalidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentSucceeded) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnDidSaveInstrument("instrument_id",
+ std::vector<RequiredAction>())).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrument(*instrument,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveInstrumentValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentWithRequiredActionsSucceeded) {
+ MockWalletClientObserver observer;
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ EXPECT_CALL(observer,
+ OnDidSaveInstrument(std::string(),
+ required_actions)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrument(*instrument,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveInstrumentWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentFailedInvalidRequiredActions) {
+ MockWalletClientObserver observer;
+
+ EXPECT_CALL(observer, OnMalformedResponse());
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrument(*instrument,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentEscrowDown) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnNetworkError(net::HTTP_INTERNAL_SERVER_ERROR)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrument(*instrument,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ encryption_fetcher->SetResponseString(std::string());
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentEscrowMalformed) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrument(*instrument,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString(std::string());
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentFailedMalformedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrument(*instrument,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveInvalidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressSucceeded) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnDidSaveInstrumentAndAddress(
+ "instrument_id",
+ "shipping_address_id",
+ std::vector<RequiredAction>())).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveInstrumentAndAddressValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressWithRequiredActionsSucceeded) {
+ MockWalletClientObserver observer;
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ EXPECT_CALL(observer,
+ OnDidSaveInstrumentAndAddress(
+ std::string(),
+ std::string(),
+ required_actions)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+ VerifyAndFinishRequest(
+ factory,
+ net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveInstrumentAndAddressWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressFailedInvalidRequiredAction) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressEscrowDown) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnNetworkError(net::HTTP_INTERNAL_SERVER_ERROR)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_INTERNAL_SERVER_ERROR);
+ encryption_fetcher->SetResponseString(std::string());
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressEscrowMalformed) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString(std::string());
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressFailedAddressMissing) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveInstrumentAndAddressMissingAddressResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressFailedInstrumentMissing) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SaveInstrumentAndAddress(*instrument,
+ *address,
+ "obfuscated_gaia_id",
+ GURL(kMerchantUrl));
+
+ net::TestURLFetcher* encryption_fetcher = factory.GetFetcherByID(1);
+ ASSERT_TRUE(encryption_fetcher);
+ encryption_fetcher->set_response_code(net::HTTP_OK);
+ encryption_fetcher->SetResponseString("escrow_handle");
+ encryption_fetcher->delegate()->OnURLFetchComplete(encryption_fetcher);
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveInstrumentAndAddressMissingInstrumentResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentSucceeded) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer,
+ OnDidUpdateInstrument("instrument_id",
+ std::vector<RequiredAction>())).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.UpdateInstrument("instrument_id",
+ *address,
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kUpdateInstrumentValidRequest,
+ kUpdateInstrumentValidResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentWithRequiredActionsSucceeded) {
+ MockWalletClientObserver observer;
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ EXPECT_CALL(observer,
+ OnDidUpdateInstrument(std::string(),
+ required_actions)).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.UpdateInstrument("instrument_id",
+ *address,
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kUpdateInstrumentValidRequest,
+ kUpdateInstrumentWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentFailedInvalidRequiredAction) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.UpdateInstrument("instrument_id",
+ *address,
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kUpdateInstrumentValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentMalformedResponse) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnMalformedResponse()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ scoped_ptr<Address> address = GetTestAddress();
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.UpdateInstrument("instrument_id",
+ *address,
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kUpdateInstrumentValidRequest,
+ kUpdateInstrumentMalformedResponse);
+}
+
+TEST_F(WalletClientTest, SendAutocheckoutOfStatusSuccess) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnDidSendAutocheckoutStatus()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ "google_transaction_id");
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ(kSendAutocheckoutStatusOfSuccessValidRequest, GetData(fetcher));
+ fetcher->SetResponseString(")]}'"); // Invalid JSON. Should be ignored.
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, SendAutocheckoutStatusOfFailure) {
+ MockWalletClientObserver observer;
+ EXPECT_CALL(observer, OnDidSendAutocheckoutStatus()).Times(1);
+
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ wallet_client.SendAutocheckoutStatus(autofill::CANNOT_PROCEED,
+ GURL(kMerchantUrl),
+ "google_transaction_id");
+ net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ EXPECT_EQ(kSendAutocheckoutStatusOfFailureValidRequest, GetData(fetcher));
+ fetcher->set_response_code(net::HTTP_OK);
+ fetcher->SetResponseString(")]}'"); // Invalid JSON. Should be ignored.
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+}
+
+TEST_F(WalletClientTest, HasRequestInProgress) {
+ MockWalletClientObserver observer;
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ EXPECT_FALSE(wallet_client.HasRequestInProgress());
+
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_TRUE(wallet_client.HasRequestInProgress());
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kGetWalletItemsValidRequest,
+ kGetWalletItemsValidResponse);
+ EXPECT_FALSE(wallet_client.HasRequestInProgress());
+}
+
+TEST_F(WalletClientTest, PendingRequest) {
+ MockWalletClientObserver observer;
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ ASSERT_EQ(0U, wallet_client.pending_requests_.size());
+
+ // Shouldn't queue the first request.
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_EQ(0U, wallet_client.pending_requests_.size());
+
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_EQ(1U, wallet_client.pending_requests_.size());
+
+ VerifyAndFinishRequest(factory,
+ net::HTTP_OK,
+ kGetWalletItemsValidRequest,
+ kGetWalletItemsValidResponse);
+ EXPECT_EQ(0U, wallet_client.pending_requests_.size());
+
+ EXPECT_CALL(observer, OnWalletError()).Times(1);
+ VerifyAndFinishRequest(factory,
+ net::HTTP_INTERNAL_SERVER_ERROR,
+ kGetWalletItemsValidRequest,
+ std::string());
+}
+
+TEST_F(WalletClientTest, CancelPendingRequests) {
+ MockWalletClientObserver observer;
+ net::TestURLFetcherFactory factory;
+
+ WalletClient wallet_client(profile_.GetRequestContext(), &observer);
+ ASSERT_EQ(0U, wallet_client.pending_requests_.size());
+
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ wallet_client.GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_EQ(2U, wallet_client.pending_requests_.size());
+
+ wallet_client.CancelPendingRequests();
+ EXPECT_EQ(0U, wallet_client.pending_requests_.size());
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_items.cc b/components/autofill/browser/wallet/wallet_items.cc
new file mode 100644
index 0000000..6a779ac
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_items.cc
@@ -0,0 +1,411 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/wallet_items.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "googleurl/src/gurl.h"
+#include "grit/webkit_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+const char kLegalDocumentUrl[] =
+ "https://wallet.google.com/customer/gadget/legaldocument.html?docId=";
+
+WalletItems::MaskedInstrument::Type
+ TypeFromString(const std::string& type_string) {
+ if (type_string == "VISA")
+ return WalletItems::MaskedInstrument::VISA;
+ if (type_string == "MASTER_CARD")
+ return WalletItems::MaskedInstrument::MASTER_CARD;
+ if (type_string == "AMEX")
+ return WalletItems::MaskedInstrument::AMEX;
+ if (type_string == "DISCOVER")
+ return WalletItems::MaskedInstrument::DISCOVER;
+ if (type_string == "SOLO")
+ return WalletItems::MaskedInstrument::SOLO;
+ if (type_string == "MAESTRO")
+ return WalletItems::MaskedInstrument::MAESTRO;
+ if (type_string == "SWITCH")
+ return WalletItems::MaskedInstrument::SWITCH;
+ return WalletItems::MaskedInstrument::UNKNOWN;
+}
+
+WalletItems::MaskedInstrument::Status
+ StatusFromString(const std::string& status_string) {
+ if (status_string == "PENDING")
+ return WalletItems::MaskedInstrument::PENDING;
+ if (status_string == "VALID")
+ return WalletItems::MaskedInstrument::VALID;
+ if (status_string == "DECLINED")
+ return WalletItems::MaskedInstrument::DECLINED;
+ if (status_string == "DISABLED_FOR_THIS_MERCHANT")
+ return WalletItems::MaskedInstrument::DISABLED_FOR_THIS_MERCHANT;
+ if (status_string == "UNSUPPORTED_COUNTRY")
+ return WalletItems::MaskedInstrument::UNSUPPORTED_COUNTRY;
+ if (status_string == "EXPIRED")
+ return WalletItems::MaskedInstrument::EXPIRED;
+ if (status_string == "BILLING_INCOMPLETE")
+ return WalletItems::MaskedInstrument::BILLING_INCOMPLETE;
+ return WalletItems::MaskedInstrument::INAPPLICABLE;
+}
+
+} // anonymous namespace
+
+WalletItems::MaskedInstrument::MaskedInstrument(
+ const string16& descriptive_name,
+ const WalletItems::MaskedInstrument::Type& type,
+ const std::vector<string16>& supported_currencies,
+ const string16& last_four_digits,
+ int expiration_month,
+ int expiration_year,
+ scoped_ptr<Address> address,
+ const WalletItems::MaskedInstrument::Status& status,
+ const std::string& object_id)
+ : descriptive_name_(descriptive_name),
+ type_(type),
+ supported_currencies_(supported_currencies),
+ last_four_digits_(last_four_digits),
+ expiration_month_(expiration_month),
+ expiration_year_(expiration_year),
+ address_(address.Pass()),
+ status_(status),
+ object_id_(object_id) {
+ DCHECK(address_.get());
+}
+
+WalletItems::MaskedInstrument::~MaskedInstrument() {}
+
+scoped_ptr<WalletItems::MaskedInstrument>
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(
+ const base::DictionaryValue& dictionary) {
+ std::string type_string;
+ Type type;
+ if (dictionary.GetString("type", &type_string)) {
+ type = TypeFromString(type_string);
+ } else {
+ DLOG(ERROR) << "Response from Google Wallet missing card type";
+ return scoped_ptr<MaskedInstrument>();
+ }
+
+ string16 last_four_digits;
+ if (!dictionary.GetString("last_four_digits", &last_four_digits)) {
+ DLOG(ERROR) << "Response from Google Wallet missing last four digits";
+ return scoped_ptr<MaskedInstrument>();
+ }
+
+ std::string status_string;
+ Status status;
+ if (dictionary.GetString("status", &status_string)) {
+ status = StatusFromString(status_string);
+ } else {
+ DLOG(ERROR) << "Response from Google Wallet missing status";
+ return scoped_ptr<MaskedInstrument>();
+ }
+
+ std::string object_id;
+ if (!dictionary.GetString("object_id", &object_id)) {
+ DLOG(ERROR) << "Response from Google Wallet missing object id";
+ return scoped_ptr<MaskedInstrument>();
+ }
+
+ const DictionaryValue* address_dict;
+ if (!dictionary.GetDictionary("billing_address", &address_dict)) {
+ DLOG(ERROR) << "Response from Google wallet missing address";
+ return scoped_ptr<MaskedInstrument>();
+ }
+ scoped_ptr<Address> address = Address::CreateDisplayAddress(*address_dict);
+
+ if (!address.get()) {
+ DLOG(ERROR) << "Response from Google wallet contained malformed address";
+ return scoped_ptr<MaskedInstrument>();
+ }
+
+ std::vector<string16> supported_currencies;
+ const ListValue* supported_currency_list;
+ if (dictionary.GetList("supported_currency", &supported_currency_list)) {
+ for (size_t i = 0; i < supported_currency_list->GetSize(); ++i) {
+ string16 currency;
+ if (supported_currency_list->GetString(i, &currency))
+ supported_currencies.push_back(currency);
+ }
+ } else {
+ DVLOG(1) << "Response from Google Wallet missing supported currency";
+ }
+
+ int expiration_month;
+ if (!dictionary.GetInteger("expiration_month", &expiration_month))
+ DVLOG(1) << "Response from Google Wallet missing expiration month";
+
+ int expiration_year;
+ if (!dictionary.GetInteger("expiration_year", &expiration_year))
+ DVLOG(1) << "Response from Google Wallet missing expiration year";
+
+ string16 descriptive_name;
+ if (!dictionary.GetString("descriptive_name", &descriptive_name))
+ DVLOG(1) << "Response from Google Wallet missing descriptive name";
+
+ return scoped_ptr<MaskedInstrument>(new MaskedInstrument(descriptive_name,
+ type,
+ supported_currencies,
+ last_four_digits,
+ expiration_month,
+ expiration_year,
+ address.Pass(),
+ status,
+ object_id));
+}
+
+bool WalletItems::MaskedInstrument::operator==(
+ const WalletItems::MaskedInstrument& other) const {
+ if (descriptive_name_ != other.descriptive_name_)
+ return false;
+ if (type_ != other.type_)
+ return false;
+ if (supported_currencies_ != other.supported_currencies_)
+ return false;
+ if (last_four_digits_ != other.last_four_digits_)
+ return false;
+ if (expiration_month_ != other.expiration_month_)
+ return false;
+ if (expiration_year_ != other.expiration_year_)
+ return false;
+ if (address_.get()) {
+ if (other.address_.get()) {
+ if (*address_.get() != *other.address_.get())
+ return false;
+ } else {
+ return false;
+ }
+ } else if (other.address_.get()) {
+ return false;
+ }
+ if (status_ != other.status_)
+ return false;
+ if (object_id_ != other.object_id_)
+ return false;
+ return true;
+}
+
+bool WalletItems::MaskedInstrument::operator!=(
+ const WalletItems::MaskedInstrument& other) const {
+ return !(*this == other);
+}
+
+bool WalletItems::HasRequiredAction(RequiredAction action) const {
+ DCHECK(ActionAppliesToWalletItems(action));
+ return std::find(required_actions_.begin(),
+ required_actions_.end(),
+ action) != required_actions_.end();
+}
+
+const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
+ int idr = 0;
+ switch (type_) {
+ case AMEX:
+ idr = IDR_AUTOFILL_CC_AMEX;
+ break;
+
+ case DISCOVER:
+ idr = IDR_AUTOFILL_CC_DISCOVER;
+ break;
+
+ case MASTER_CARD:
+ idr = IDR_AUTOFILL_CC_MASTERCARD;
+ break;
+
+ case SOLO:
+ idr = IDR_AUTOFILL_CC_SOLO;
+ break;
+
+ case VISA:
+ idr = IDR_AUTOFILL_CC_VISA;
+ break;
+
+ case MAESTRO:
+ case SWITCH:
+ case UNKNOWN:
+ idr = IDR_AUTOFILL_CC_GENERIC;
+ break;
+ }
+
+ return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
+}
+
+WalletItems::LegalDocument::LegalDocument(const std::string& document_id,
+ const std::string& display_name)
+ : document_id_(document_id),
+ display_name_(display_name) {}
+
+WalletItems::LegalDocument::~LegalDocument() {}
+
+scoped_ptr<WalletItems::LegalDocument>
+ WalletItems::LegalDocument::CreateLegalDocument(
+ const base::DictionaryValue& dictionary) {
+ std::string document_id;
+ if (!dictionary.GetString("legal_document_id", &document_id)) {
+ DLOG(ERROR) << "Response from Google Wallet missing legal document id";
+ return scoped_ptr<LegalDocument>();
+ }
+
+ std::string display_name;
+ if (!dictionary.GetString("display_name", &display_name)) {
+ DLOG(ERROR) << "Response from Google Wallet missing display name";
+ return scoped_ptr<LegalDocument>();
+ }
+
+ return scoped_ptr<LegalDocument>(new LegalDocument(document_id,
+ display_name));
+}
+
+GURL WalletItems::LegalDocument::GetUrl() {
+ return GURL(kLegalDocumentUrl + document_id_);
+}
+
+bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
+ return document_id_ == other.document_id_ &&
+ display_name_ == other.display_name_;
+}
+
+bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
+ return !(*this == other);
+}
+
+WalletItems::WalletItems(const std::vector<RequiredAction>& required_actions,
+ const std::string& google_transaction_id,
+ const std::string& default_instrument_id,
+ const std::string& default_address_id,
+ const std::string& obfuscated_gaia_id)
+ : required_actions_(required_actions),
+ google_transaction_id_(google_transaction_id),
+ default_instrument_id_(default_instrument_id),
+ default_address_id_(default_address_id),
+ obfuscated_gaia_id_(obfuscated_gaia_id) {}
+
+WalletItems::~WalletItems() {}
+
+scoped_ptr<WalletItems>
+ WalletItems::CreateWalletItems(const base::DictionaryValue& dictionary) {
+ std::vector<RequiredAction> required_action;
+ const ListValue* required_action_list;
+ if (dictionary.GetList("required_action", &required_action_list)) {
+ for (size_t i = 0; i < required_action_list->GetSize(); ++i) {
+ std::string action_string;
+ if (required_action_list->GetString(i, &action_string)) {
+ RequiredAction action = ParseRequiredActionFromString(action_string);
+ if (!ActionAppliesToWalletItems(action)) {
+ DLOG(ERROR) << "Response from Google wallet with bad required action:"
+ " \"" << action_string << "\"";
+ return scoped_ptr<WalletItems>();
+ }
+ required_action.push_back(action);
+ }
+ }
+ } else {
+ DVLOG(1) << "Response from Google wallet missing required actions";
+ }
+
+ std::string google_transaction_id;
+ if (!dictionary.GetString("google_transaction_id", &google_transaction_id) &&
+ required_action.empty()) {
+ DLOG(ERROR) << "Response from Google wallet missing google transaction id";
+ return scoped_ptr<WalletItems>();
+ }
+
+ std::string default_instrument_id;
+ if (!dictionary.GetString("default_instrument_id", &default_instrument_id))
+ DVLOG(1) << "Response from Google wallet missing default instrument id";
+
+ std::string default_address_id;
+ if (!dictionary.GetString("default_address_id", &default_address_id))
+ DVLOG(1) << "Response from Google wallet missing default_address_id";
+
+ std::string obfuscated_gaia_id;
+ if (!dictionary.GetString("obfuscated_gaia_id", &obfuscated_gaia_id))
+ DVLOG(1) << "Response from Google wallet missing obfuscated gaia id";
+
+ scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
+ google_transaction_id,
+ default_instrument_id,
+ default_address_id,
+ obfuscated_gaia_id));
+
+ const ListValue* legal_docs;
+ if (dictionary.GetList("required_legal_document", &legal_docs)) {
+ for (size_t i = 0; i < legal_docs->GetSize(); ++i) {
+ const DictionaryValue* legal_doc_dict;
+ if (legal_docs->GetDictionary(i, &legal_doc_dict)) {
+ scoped_ptr<LegalDocument> legal_doc(
+ LegalDocument::CreateLegalDocument(*legal_doc_dict));
+ if (legal_doc.get()) {
+ wallet_items->AddLegalDocument(legal_doc.Pass());
+ } else {
+ DLOG(ERROR) << "Malformed legal document in response from "
+ "Google wallet";
+ return scoped_ptr<WalletItems>();
+ }
+ }
+ }
+ } else {
+ DVLOG(1) << "Response from Google wallet missing legal docs";
+ }
+
+ const ListValue* instruments;
+ if (dictionary.GetList("instrument", &instruments)) {
+ for (size_t i = 0; i < instruments->GetSize(); ++i) {
+ const DictionaryValue* instrument_dict;
+ if (instruments->GetDictionary(i, &instrument_dict)) {
+ scoped_ptr<MaskedInstrument> instrument(
+ MaskedInstrument::CreateMaskedInstrument(*instrument_dict));
+ if (instrument.get())
+ wallet_items->AddInstrument(instrument.Pass());
+ else
+ DLOG(ERROR) << "Malformed instrument in response from Google Wallet";
+ }
+ }
+ } else {
+ DVLOG(1) << "Response from Google wallet missing instruments";
+ }
+
+ const ListValue* addresses;
+ if (dictionary.GetList("address", &addresses)) {
+ for (size_t i = 0; i < addresses->GetSize(); ++i) {
+ const DictionaryValue* address_dict;
+ if (addresses->GetDictionary(i, &address_dict)) {
+ scoped_ptr<Address> address(
+ Address::CreateAddressWithID(*address_dict));
+ if (address.get())
+ wallet_items->AddAddress(address.Pass());
+ else
+ DLOG(ERROR) << "Malformed address in response from Google Wallet";
+ }
+ }
+ } else {
+ DVLOG(1) << "Response from Google wallet missing addresses";
+ }
+
+ return wallet_items.Pass();
+}
+
+bool WalletItems::operator==(const WalletItems& other) const {
+ // TODO(ahutter): Check scoped vector equality.
+ return google_transaction_id_ == other.google_transaction_id_ &&
+ default_instrument_id_ == other.default_instrument_id_ &&
+ default_address_id_ == other.default_address_id_ &&
+ required_actions_ == other.required_actions_ &&
+ obfuscated_gaia_id_ == other.obfuscated_gaia_id_;
+}
+
+bool WalletItems::operator!=(const WalletItems& other) const {
+ return !(*this == other);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_items.h b/components/autofill/browser/wallet/wallet_items.h
new file mode 100644
index 0000000..f507b88
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_items.h
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_ITEMS_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_ITEMS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/string16.h"
+#include "components/autofill/browser/wallet/required_action.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+
+class GURL;
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace gfx {
+class Image;
+}
+
+namespace autofill {
+namespace wallet {
+
+class WalletItemsTest;
+
+// WalletItems is a collection of cards and addresses that a user picks from to
+// construct a full wallet. However, it also provides a transaction ID which
+// must be used throughout all API calls being made using this data.
+// Additionally, user actions may be required before a purchase can be completed
+// using Online Wallet and those actions are present in the object as well.
+class WalletItems {
+ public:
+ // Container for all information about a credit card except for it's card
+ // verfication number (CVN) and it's complete primary account number (PAN).
+ class MaskedInstrument {
+ public:
+ enum Type {
+ AMEX,
+ DISCOVER,
+ MAESTRO,
+ MASTER_CARD,
+ SOLO,
+ SWITCH,
+ UNKNOWN, // Catch all type.
+ VISA,
+ };
+ enum Status {
+ BILLING_INCOMPLETE,
+ DECLINED,
+ DISABLED_FOR_THIS_MERCHANT,
+ EXPIRED,
+ INAPPLICABLE, // Catch all status.
+ PENDING,
+ UNSUPPORTED_COUNTRY,
+ VALID,
+ };
+
+ ~MaskedInstrument();
+
+ // Returns an empty scoped_ptr if input is invalid or a valid masked
+ // instrument.
+ static scoped_ptr<MaskedInstrument>
+ CreateMaskedInstrument(const base::DictionaryValue& dictionary);
+
+ bool operator==(const MaskedInstrument& other) const;
+ bool operator!=(const MaskedInstrument& other) const;
+
+ // Gets an image to display for this instrument.
+ const gfx::Image& CardIcon() const;
+
+ const string16& descriptive_name() const { return descriptive_name_; }
+ const Type& type() const { return type_; }
+ const std::vector<string16>& supported_currencies() const {
+ return supported_currencies_;
+ }
+ const string16& last_four_digits() const { return last_four_digits_; }
+ int expiration_month() const { return expiration_month_; }
+ int expiration_year() const { return expiration_year_; }
+ const Address& address() const { return *address_; }
+ const Status& status() const { return status_; }
+ const std::string& object_id() const { return object_id_; }
+
+ private:
+ friend class WalletItemsTest;
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateMaskedInstrument);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateWalletItems);
+ MaskedInstrument(const string16& descriptve_name,
+ const Type& type,
+ const std::vector<string16>& supported_currencies,
+ const string16& last_four_digits,
+ int expiration_month,
+ int expiration_year,
+ scoped_ptr<Address> address,
+ const Status& status,
+ const std::string& object_id);
+
+ // A user-provided description of the instrument. For example, "Google Visa
+ // Card".
+ string16 descriptive_name_;
+
+ // The payment network of the instrument. For example, Visa.
+ Type type_;
+
+ // |supported_currencies_| are ISO 4217 currency codes, e.g. USD.
+ std::vector<string16> supported_currencies_;
+
+ // The last four digits of the primary account number of the instrument.
+ string16 last_four_digits_;
+
+ // |expiration month_| should be 1-12.
+ int expiration_month_;
+
+ // |expiration_year_| should be a 4-digit year.
+ int expiration_year_;
+
+ // The billing address for the instrument.
+ scoped_ptr<Address> address_;
+
+ // The current status of the instrument. For example, expired or declined.
+ Status status_;
+
+ // Externalized Online Wallet id for this instrument.
+ std::string object_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(MaskedInstrument);
+ };
+
+ // Class representing a legal document that the user must accept before they
+ // can use Online Wallet.
+ class LegalDocument {
+ public:
+ ~LegalDocument();
+
+ // Returns null if input is invalid or a valid legal document. Caller owns
+ // returned pointer.
+ static scoped_ptr<LegalDocument>
+ CreateLegalDocument(const base::DictionaryValue& dictionary);
+
+ // Get the url where this legal document is hosted.
+ GURL GetUrl();
+
+ bool operator==(const LegalDocument& other) const;
+ bool operator!=(const LegalDocument& other) const;
+
+ const std::string& document_id() const { return document_id_; }
+ const std::string& display_name() const { return display_name_; }
+
+ private:
+ friend class WalletItemsTest;
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateLegalDocument);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateWalletItems);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, LegalDocumentGetUrl);
+ LegalDocument(const std::string& document_id,
+ const std::string& display_name);
+
+ // Externalized Online Wallet id for the document.
+ std::string document_id_;
+
+ // User displayable name for the document.
+ std::string display_name_;
+ DISALLOW_COPY_AND_ASSIGN(LegalDocument);
+ };
+
+ ~WalletItems();
+
+ // Returns null on invalid input, an empty wallet items with required
+ // actions if any are present, and a populated wallet items otherwise. Caller
+ // owns returned pointer.
+ static scoped_ptr<WalletItems>
+ CreateWalletItems(const base::DictionaryValue& dictionary);
+
+ bool operator==(const WalletItems& other) const;
+ bool operator!=(const WalletItems& other) const;
+
+ void AddInstrument(scoped_ptr<MaskedInstrument> instrument) {
+ DCHECK(instrument.get());
+ instruments_.push_back(instrument.release());
+ }
+ void AddAddress(scoped_ptr<Address> address) {
+ DCHECK(address.get());
+ addresses_.push_back(address.release());
+ }
+ void AddLegalDocument(scoped_ptr<LegalDocument> legal_document) {
+ DCHECK(legal_document.get());
+ legal_documents_.push_back(legal_document.release());
+ }
+
+ // Whether or not |action| is in |required_actions_|.
+ bool HasRequiredAction(RequiredAction action) const;
+
+ const std::vector<RequiredAction>& required_actions() const {
+ return required_actions_;
+ }
+ const std::string& google_transaction_id() const {
+ return google_transaction_id_;
+ }
+ const std::vector<MaskedInstrument*>& instruments() const {
+ return instruments_.get();
+ }
+ const std::string& default_instrument_id() const {
+ return default_instrument_id_;
+ }
+ const std::vector<Address*>& addresses() const { return addresses_.get(); }
+ const std::string& default_address_id() const { return default_address_id_; }
+ const std::string& obfuscated_gaia_id() const { return obfuscated_gaia_id_; }
+ const std::vector<LegalDocument*>& legal_documents() const {
+ return legal_documents_.get();
+ }
+
+ private:
+ friend class WalletItemsTest;
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateWalletItems);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest,
+ CreateWalletItemsWithRequiredActions);
+
+ WalletItems(const std::vector<RequiredAction>& required_actions,
+ const std::string& google_transaction_id,
+ const std::string& default_instrument_id,
+ const std::string& default_address_id,
+ const std::string& obfuscated_gaia_id);
+
+ // Actions that must be completed by the user before a FullWallet can be
+ // issued to them by the Online Wallet service.
+ std::vector<RequiredAction> required_actions_;
+
+ // The id for this transaction issued by Google.
+ std::string google_transaction_id_;
+
+ // The id of the user's default instrument.
+ std::string default_instrument_id_;
+
+ // The id of the user's default address.
+ std::string default_address_id_;
+
+ // The externalized Gaia id of the user.
+ std::string obfuscated_gaia_id_;
+
+ // The user's backing instruments.
+ ScopedVector<MaskedInstrument> instruments_;
+
+ // The user's shipping addresses.
+ ScopedVector<Address> addresses_;
+
+ // Legal documents the user must accept before using Online Wallet.
+ ScopedVector<LegalDocument> legal_documents_;
+
+ DISALLOW_COPY_AND_ASSIGN(WalletItems);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_ITEMS_H_
diff --git a/components/autofill/browser/wallet/wallet_items_unittest.cc b/components/autofill/browser/wallet/wallet_items_unittest.cc
new file mode 100644
index 0000000..185db7f
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_items_unittest.cc
@@ -0,0 +1,543 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/browser/wallet/required_action.h"
+#include "components/autofill/browser/wallet/wallet_items.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kMaskedInstrument[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ "}";
+
+const char kMaskedInstrumentMissingStatus[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"object_id\":\"object_id\""
+ "}";
+
+const char kMaskedInstrumentMissingType[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ "}";
+
+const char kMaskedInstrumentMissingLastFourDigits[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ "}";
+
+const char kMaskedInstrumentMissingAddress[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ "}";
+
+const char kMaskedInstrumentMalformedAddress[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ "}";
+
+const char kMaskedInstrumentMissingObjectId[] =
+ "{"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\""
+ "}";
+
+const char kLegalDocument[] =
+ "{"
+ " \"legal_document_id\":\"doc_id\","
+ " \"display_name\":\"display_name\""
+ "}";
+
+const char kLegalDocumentMissingDocumentId[] =
+ "{"
+ " \"display_name\":\"display_name\""
+ "}";
+
+const char kLegalDocumentMissingDisplayName[] =
+ "{"
+ " \"legal_document_id\":\"doc_id\""
+ "}";
+
+const char kWalletItemsWithRequiredActions[] =
+ "{"
+ " \"obfuscated_gaia_id\":\"\","
+ " \"required_action\":"
+ " ["
+ " \" setup_wallet\","
+ " \"AcCePt_ToS \","
+ " \" \\tGAIA_auth \\n\\r\","
+ " \"UPDATE_expiration_date\","
+ " \"UPGRADE_min_ADDRESS \","
+ " \" pAsSiVe_GAIA_auth \","
+ " \" REQUIRE_PHONE_NUMBER\\t \""
+ " ]"
+ "}";
+
+const char kWalletItemsWithInvalidRequiredActions[] =
+ "{"
+ " \"obfuscated_gaia_id\":\"\","
+ " \"required_action\":"
+ " ["
+ " \"verify_CVV\","
+ " \"invalid_FORM_FIELD\","
+ " \" 忍者の正体 \""
+ " ]"
+ "}";
+
+const char kWalletItemsMissingGoogleTransactionId[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " ],"
+ " \"instrument\":"
+ " ["
+ " {"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ " }"
+ " ],"
+ " \"default_instrument_id\":\"default_instrument_id\","
+ " \"address\":"
+ " ["
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line_1\":\"address_line_1\","
+ " \"address_line_2\":\"address_line_2\","
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " }"
+ " ],"
+ " \"default_address_id\":\"default_address_id\","
+ " \"obfuscated_gaia_id\":\"obfuscated_gaia_id\","
+ " \"required_legal_document\":"
+ " ["
+ " {"
+ " \"legal_document_id\":\"doc_id\","
+ " \"display_name\":\"display_name\""
+ " }"
+ " ]"
+ "}";
+
+const char kWalletItems[] =
+ "{"
+ " \"required_action\":"
+ " ["
+ " ],"
+ " \"google_transaction_id\":\"google_transaction_id\","
+ " \"instrument\":"
+ " ["
+ " {"
+ " \"descriptive_name\":\"descriptive_name\","
+ " \"type\":\"VISA\","
+ " \"supported_currency\":"
+ " ["
+ " \"currency\""
+ " ],"
+ " \"last_four_digits\":\"last_four_digits\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":2012,"
+ " \"billing_address\":"
+ " {"
+ " \"name\":\"name\","
+ " \"address1\":\"address1\","
+ " \"address2\":\"address2\","
+ " \"city\":\"city\","
+ " \"state\":\"state\","
+ " \"postal_code\":\"postal_code\","
+ " \"phone_number\":\"phone_number\","
+ " \"country_code\":\"country_code\""
+ " },"
+ " \"status\":\"VALID\","
+ " \"object_id\":\"object_id\""
+ " }"
+ " ],"
+ " \"default_instrument_id\":\"default_instrument_id\","
+ " \"address\":"
+ " ["
+ " {"
+ " \"id\":\"id\","
+ " \"phone_number\":\"phone_number\","
+ " \"postal_address\":"
+ " {"
+ " \"recipient_name\":\"recipient_name\","
+ " \"address_line_1\":\"address_line_1\","
+ " \"address_line_2\":\"address_line_2\","
+ " \"locality_name\":\"locality_name\","
+ " \"administrative_area_name\":\"administrative_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"country_name_code\""
+ " }"
+ " }"
+ " ],"
+ " \"default_address_id\":\"default_address_id\","
+ " \"obfuscated_gaia_id\":\"obfuscated_gaia_id\","
+ " \"required_legal_document\":"
+ " ["
+ " {"
+ " \"legal_document_id\":\"doc_id\","
+ " \"display_name\":\"display_name\""
+ " }"
+ " ]"
+ "}";
+
+} // anonymous namespace
+
+namespace autofill {
+namespace wallet {
+
+class WalletItemsTest : public testing::Test {
+ public:
+ WalletItemsTest() {}
+ protected:
+ void SetUpDictionary(const std::string& json) {
+ scoped_ptr<Value> value(base::JSONReader::Read(json));
+ ASSERT_TRUE(value.get());
+ ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY));
+ dict.reset(static_cast<DictionaryValue*>(value.release()));
+ }
+ scoped_ptr<DictionaryValue> dict;
+};
+
+TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingStatus) {
+ SetUpDictionary(kMaskedInstrumentMissingStatus);
+ EXPECT_EQ(NULL,
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingType) {
+ SetUpDictionary(kMaskedInstrumentMissingType);
+ EXPECT_EQ(NULL,
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingLastFourDigits) {
+ SetUpDictionary(kMaskedInstrumentMissingLastFourDigits);
+ EXPECT_EQ(NULL,
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingAddress) {
+ SetUpDictionary(kMaskedInstrumentMissingAddress);
+ EXPECT_EQ(NULL,
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateMaskedInstrumentMalformedAddress) {
+ SetUpDictionary(kMaskedInstrumentMalformedAddress);
+ EXPECT_EQ(NULL,
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateMaskedInstrumentMissingObjectId) {
+ SetUpDictionary(kMaskedInstrumentMissingObjectId);
+ EXPECT_EQ(NULL,
+ WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateMaskedInstrument) {
+ SetUpDictionary(kMaskedInstrument);
+ scoped_ptr<Address> address(new Address("country_code",
+ ASCIIToUTF16("name"),
+ ASCIIToUTF16("address1"),
+ ASCIIToUTF16("address2"),
+ ASCIIToUTF16("city"),
+ ASCIIToUTF16("state"),
+ ASCIIToUTF16("postal_code"),
+ ASCIIToUTF16("phone_number"),
+ ""));
+ std::vector<string16> supported_currencies;
+ supported_currencies.push_back(ASCIIToUTF16("currency"));
+ WalletItems::MaskedInstrument masked_instrument(
+ ASCIIToUTF16("descriptive_name"),
+ WalletItems::MaskedInstrument::VISA,
+ supported_currencies,
+ ASCIIToUTF16("last_four_digits"),
+ 12,
+ 2012,
+ address.Pass(),
+ WalletItems::MaskedInstrument::VALID,
+ "object_id");
+ EXPECT_EQ(masked_instrument,
+ *WalletItems::MaskedInstrument::CreateMaskedInstrument(*dict));
+}
+
+TEST_F(WalletItemsTest, CreateLegalDocumentMissingDocId) {
+ SetUpDictionary(kLegalDocumentMissingDocumentId);
+ EXPECT_EQ(NULL, WalletItems::LegalDocument::CreateLegalDocument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateLegalDocumentMissingDisplayName) {
+ SetUpDictionary(kLegalDocumentMissingDisplayName);
+ EXPECT_EQ(NULL, WalletItems::LegalDocument::CreateLegalDocument(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateLegalDocument) {
+ SetUpDictionary(kLegalDocument);
+ WalletItems::LegalDocument expected("doc_id", "display_name");
+ EXPECT_EQ(expected,
+ *WalletItems::LegalDocument::CreateLegalDocument(*dict));
+}
+
+TEST_F(WalletItemsTest, LegalDocumentGetUrl) {
+ WalletItems::LegalDocument legal_doc("doc_id", "display_name");
+ EXPECT_EQ("https://wallet.google.com/customer/gadget/legaldocument.html?"
+ "docId=doc_id",
+ legal_doc.GetUrl().spec());
+}
+
+TEST_F(WalletItemsTest, CreateWalletItemsWithRequiredActions) {
+ SetUpDictionary(kWalletItemsWithRequiredActions);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(SETUP_WALLET);
+ required_actions.push_back(ACCEPT_TOS);
+ required_actions.push_back(GAIA_AUTH);
+ required_actions.push_back(UPDATE_EXPIRATION_DATE);
+ required_actions.push_back(UPGRADE_MIN_ADDRESS);
+ required_actions.push_back(PASSIVE_GAIA_AUTH);
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+
+ WalletItems expected(required_actions,
+ std::string(),
+ std::string(),
+ std::string(),
+ std::string());
+ EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict));
+
+ ASSERT_FALSE(required_actions.empty());
+ required_actions.pop_back();
+ WalletItems different_required_actions(required_actions,
+ std::string(),
+ std::string(),
+ std::string(),
+ std::string());
+ EXPECT_NE(expected, different_required_actions);
+}
+
+TEST_F(WalletItemsTest, CreateWalletItemsWithInvalidRequiredActions) {
+ SetUpDictionary(kWalletItemsWithInvalidRequiredActions);
+ EXPECT_EQ(NULL, WalletItems::CreateWalletItems(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateWalletItemsMissingGoogleTransactionId) {
+ SetUpDictionary(kWalletItemsMissingGoogleTransactionId);
+ EXPECT_EQ(NULL, WalletItems::CreateWalletItems(*dict).get());
+}
+
+TEST_F(WalletItemsTest, CreateWalletItems) {
+ SetUpDictionary(kWalletItems);
+ std::vector<RequiredAction> required_actions;
+ WalletItems expected(required_actions,
+ "google_transaction_id",
+ "default_instrument_id",
+ "default_address_id",
+ "obfuscated_gaia_id");
+
+ scoped_ptr<Address> billing_address(new Address("country_code",
+ ASCIIToUTF16("name"),
+ ASCIIToUTF16("address1"),
+ ASCIIToUTF16("address2"),
+ ASCIIToUTF16("city"),
+ ASCIIToUTF16("state"),
+ ASCIIToUTF16("postal_code"),
+ ASCIIToUTF16("phone_number"),
+ ""));
+ std::vector<string16> supported_currencies;
+ supported_currencies.push_back(ASCIIToUTF16("currency"));
+ scoped_ptr<WalletItems::MaskedInstrument> masked_instrument(
+ new WalletItems::MaskedInstrument(ASCIIToUTF16("descriptive_name"),
+ WalletItems::MaskedInstrument::VISA,
+ supported_currencies,
+ ASCIIToUTF16("last_four_digits"),
+ 12,
+ 2012,
+ billing_address.Pass(),
+ WalletItems::MaskedInstrument::VALID,
+ "object_id"));
+ expected.AddInstrument(masked_instrument.Pass());
+
+ scoped_ptr<Address> shipping_address(new Address("country_code",
+ ASCIIToUTF16("name"),
+ ASCIIToUTF16("address1"),
+ ASCIIToUTF16("address2"),
+ ASCIIToUTF16("city"),
+ ASCIIToUTF16("state"),
+ ASCIIToUTF16("postal_code"),
+ ASCIIToUTF16("phone_number"),
+ "id"));
+ expected.AddAddress(shipping_address.Pass());
+
+ scoped_ptr<WalletItems::LegalDocument> legal_document(
+ new WalletItems::LegalDocument("doc_id",
+ "display_name"));
+ expected.AddLegalDocument(legal_document.Pass());
+
+ EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_service_url.cc b/components/autofill/browser/wallet/wallet_service_url.cc
new file mode 100644
index 0000000..47bc105
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_service_url.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/wallet_service_url.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "components/autofill/common/autofill_switches.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/url_util.h"
+
+namespace {
+
+const char kDefaultWalletServiceUrl[] = "https://wallet.google.com/";
+
+GURL GetWalletHostUrl() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ std::string wallet_service_hostname =
+ command_line.GetSwitchValueASCII(switches::kWalletServiceUrl);
+ return !wallet_service_hostname.empty() ? GURL(wallet_service_hostname) :
+ GURL(kDefaultWalletServiceUrl);
+}
+
+GURL GetBaseWalletUrl() {
+ return GetWalletHostUrl().Resolve("online/v2/");
+}
+
+GURL GetBaseAutocheckoutUrl() {
+ return GetBaseWalletUrl().Resolve("wallet/autocheckout/v1/");
+}
+
+GURL GetBaseSecureUrl() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ std::string wallet_secure_url =
+ command_line.GetSwitchValueASCII(switches::kWalletSecureServiceUrl);
+ return !wallet_secure_url.empty() ? GURL(wallet_secure_url) :
+ GURL(kDefaultWalletServiceUrl);
+}
+
+} // anonymous namespace
+
+namespace autofill {
+namespace wallet {
+
+GURL GetGetWalletItemsUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("getWalletItemsJwtless");
+}
+
+GURL GetGetFullWalletUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("getFullWalletJwtless");
+}
+
+GURL GetAcceptLegalDocumentsUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("acceptLegalDocument");
+}
+
+GURL GetAuthenticateInstrumentUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("authenticateInstrument");
+}
+
+GURL GetSendStatusUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("reportStatus");
+}
+
+GURL GetSaveToWalletUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("saveToWallet");
+}
+
+GURL GetPassiveAuthUrl() {
+ return GetBaseWalletUrl().Resolve("passiveauth");
+}
+
+GURL GetEncryptionUrl() {
+ return GetWalletHostUrl().Resolve("online-secure/temporarydata/cvv?s7e=cvv");
+}
+
+GURL GetEscrowUrl() {
+ return GetBaseSecureUrl().Resolve("dehEfe?s7e=cardNumber%3Bcvv");
+}
+
+GURL GetSignInUrl() {
+ GURL url(GaiaUrls::GetInstance()->service_login_url());
+ url = net::AppendQueryParameter(url, "service", "sierra");
+ url = net::AppendQueryParameter(url, "btmpl", "popup");
+ url = net::AppendQueryParameter(url,
+ "continue",
+ GetSignInContinueUrl().spec());
+ return url;
+}
+
+// The continue url portion of the sign-in URL.
+GURL GetSignInContinueUrl() {
+ return GetPassiveAuthUrl();
+}
+
+bool IsSignInContinueUrl(const GURL& url) {
+ GURL final_url = wallet::GetSignInContinueUrl();
+ return url.SchemeIsSecure() &&
+ url.host() == final_url.host() &&
+ url.path() == final_url.path();
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_service_url.h b/components/autofill/browser/wallet/wallet_service_url.h
new file mode 100644
index 0000000..3caef2e
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_service_url.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_SERVICE_URL_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_SERVICE_URL_H_
+
+class GURL;
+
+namespace autofill {
+namespace wallet {
+
+GURL GetGetWalletItemsUrl();
+GURL GetGetFullWalletUrl();
+GURL GetAcceptLegalDocumentsUrl();
+GURL GetAuthenticateInstrumentUrl();
+GURL GetEncryptionUrl();
+GURL GetEscrowUrl();
+GURL GetSendStatusUrl();
+GURL GetSaveToWalletUrl();
+GURL GetPassiveAuthUrl();
+
+// URL to visit for presenting the user with a sign-in dialog.
+GURL GetSignInUrl();
+
+// The the URL to use as a continue parameter in the sign-in URL.
+// A redirect to this URL will occur once sign-in is complete.
+GURL GetSignInContinueUrl();
+
+// Returns true if |url| is an acceptable variant of the sign-in continue
+// url. Can be used for detection of navigation to the continue url.
+bool IsSignInContinueUrl(const GURL& url);
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_SERVICE_URL_H_
diff --git a/components/autofill/browser/wallet/wallet_service_url_unittest.cc b/components/autofill/browser/wallet/wallet_service_url_unittest.cc
new file mode 100644
index 0000000..c303be4
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_service_url_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/browser/wallet/wallet_service_url.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace wallet {
+
+TEST(WalletServiceUrl, CheckDefaultUrls) {
+ ASSERT_EQ("https://wallet.google.com/online/v2/wallet/autocheckout/v1/"
+ "getWalletItemsJwtless",
+ GetGetWalletItemsUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online/v2/wallet/autocheckout/v1/"
+ "getFullWalletJwtless",
+ GetGetFullWalletUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online/v2/wallet/autocheckout/v1/"
+ "acceptLegalDocument",
+ GetAcceptLegalDocumentsUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online/v2/wallet/autocheckout/v1/"
+ "authenticateInstrument",
+ GetAuthenticateInstrumentUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online/v2/wallet/autocheckout/v1/"
+ "reportStatus",
+ GetSendStatusUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online/v2/wallet/autocheckout/v1/"
+ "saveToWallet",
+ GetSaveToWalletUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online/v2/passiveauth",
+ GetPassiveAuthUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/online-secure/temporarydata/cvv?s7e=cvv",
+ GetEncryptionUrl().spec());
+ ASSERT_EQ("https://wallet.google.com/dehEfe?s7e=cardNumber%3Bcvv",
+ GetEscrowUrl().spec());
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_test_util.cc b/components/autofill/browser/wallet/wallet_test_util.cc
new file mode 100644
index 0000000..eb4bd6c
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_test_util.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 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/browser/wallet/wallet_test_util.h"
+
+#include "base/utf_string_conversions.h"
+#include "components/autofill/browser/wallet/instrument.h"
+#include "components/autofill/browser/wallet/wallet_address.h"
+
+namespace autofill {
+namespace wallet {
+
+scoped_ptr<Instrument> GetTestInstrument() {
+ return scoped_ptr<Instrument>(new Instrument(ASCIIToUTF16("4444444444444448"),
+ ASCIIToUTF16("123"),
+ 12,
+ 2012,
+ Instrument::VISA,
+ GetTestAddress().Pass()));
+}
+
+scoped_ptr<Address> GetTestShippingAddress() {
+ return scoped_ptr<Address>(new Address(
+ "ship_country_name_code",
+ ASCIIToUTF16("ship_recipient_name"),
+ ASCIIToUTF16("ship_address_line_1"),
+ ASCIIToUTF16("ship_address_line_2"),
+ ASCIIToUTF16("ship_locality_name"),
+ ASCIIToUTF16("ship_admin_area_name"),
+ ASCIIToUTF16("ship_postal_code_number"),
+ ASCIIToUTF16("ship_phone_number"),
+ std::string()));
+}
+
+scoped_ptr<Address> GetTestAddress() {
+ return scoped_ptr<Address>(new Address("country_name_code",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("admin_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ std::string()));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/components/autofill/browser/wallet/wallet_test_util.h b/components/autofill/browser/wallet/wallet_test_util.h
new file mode 100644
index 0000000..cb899c8
--- /dev/null
+++ b/components/autofill/browser/wallet/wallet_test_util.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2013 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_BROWSER_WALLET_WALLET_TEST_UTIL_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_TEST_UTIL_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace autofill {
+namespace wallet {
+
+class Instrument;
+class Address;
+
+scoped_ptr<Instrument> GetTestInstrument();
+scoped_ptr<Address> GetTestShippingAddress();
+scoped_ptr<Address> GetTestAddress();
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_TEST_UTIL_H_
diff --git a/components/autofill/renderer/DEPS b/components/autofill/renderer/DEPS
new file mode 100644
index 0000000..3279b26
--- /dev/null
+++ b/components/autofill/renderer/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ # TODO(joi): Get rid of this dependency.
+ "!chrome/common/password_generation_util.h",
+
+ "+content/public/common",
+ "+content/public/renderer",
+]
diff --git a/components/autofill/renderer/autofill_agent.cc b/components/autofill/renderer/autofill_agent.cc
new file mode 100644
index 0000000..fb3cf4a
--- /dev/null
+++ b/components/autofill/renderer/autofill_agent.cc
@@ -0,0 +1,858 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/autofill_agent.h"
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/strings/string_split.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/common/autocheckout_status.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_data_predictions.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/web_element_descriptor.h"
+#include "components/autofill/renderer/form_autofill_util.h"
+#include "components/autofill/renderer/password_autofill_manager.h"
+#include "content/public/common/password_form.h"
+#include "content/public/common/ssl_status.h"
+#include "content/public/renderer/render_view.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeCollection.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebOptionElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using WebKit::WebAutofillClient;
+using WebKit::WebFormControlElement;
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebNode;
+using WebKit::WebNodeCollection;
+using WebKit::WebOptionElement;
+using WebKit::WebString;
+
+namespace {
+
+// The size above which we stop triggering autofill for an input text field
+// (so to avoid sending long strings through IPC).
+const size_t kMaximumTextSizeForAutofill = 1000;
+
+// The maximum number of data list elements to send to the browser process
+// via IPC (to prevent long IPC messages).
+const size_t kMaximumDataListSizeForAutofill = 30;
+
+const int kAutocheckoutClickTimeout = 3;
+
+void AppendDataListSuggestions(const WebKit::WebInputElement& element,
+ std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* item_ids) {
+ WebNodeCollection options = element.dataListOptions();
+ if (options.isNull())
+ return;
+
+ string16 prefix = element.editingValue();
+ if (element.isMultiple() &&
+ element.formControlType() == WebString::fromUTF8("email")) {
+ std::vector<string16> parts;
+ base::SplitStringDontTrim(prefix, ',', &parts);
+ if (parts.size() > 0)
+ TrimWhitespace(parts[parts.size() - 1], TRIM_LEADING, &prefix);
+ }
+ for (WebOptionElement option = options.firstItem().to<WebOptionElement>();
+ !option.isNull(); option = options.nextItem().to<WebOptionElement>()) {
+ if (!StartsWith(option.value(), prefix, false) ||
+ option.value() == prefix ||
+ !element.isValidValue(option.value()))
+ continue;
+
+ values->push_back(option.value());
+ if (option.value() != option.label())
+ labels->push_back(option.label());
+ else
+ labels->push_back(string16());
+ icons->push_back(string16());
+ item_ids->push_back(WebAutofillClient::MenuItemIDDataListEntry);
+ }
+}
+
+// Trim the vectors before sending them to the browser process to ensure we
+// don't send too much data through the IPC.
+void TrimDataListsForIPC(std::vector<string16>* values,
+ std::vector<string16>* labels,
+ std::vector<string16>* icons,
+ std::vector<int>* unique_ids) {
+ // Limit the size of the vectors.
+ if (values->size() > kMaximumDataListSizeForAutofill) {
+ values->resize(kMaximumDataListSizeForAutofill);
+ labels->resize(kMaximumDataListSizeForAutofill);
+ icons->resize(kMaximumDataListSizeForAutofill);
+ unique_ids->resize(kMaximumDataListSizeForAutofill);
+ }
+
+ // Limit the size of the strings in the vectors
+ for (size_t i = 0; i < values->size(); ++i) {
+ if ((*values)[i].length() > kMaximumTextSizeForAutofill)
+ (*values)[i].resize(kMaximumTextSizeForAutofill);
+
+ if ((*labels)[i].length() > kMaximumTextSizeForAutofill)
+ (*labels)[i].resize(kMaximumTextSizeForAutofill);
+
+ if ((*icons)[i].length() > kMaximumTextSizeForAutofill)
+ (*icons)[i].resize(kMaximumTextSizeForAutofill);
+ }
+}
+
+} // namespace
+
+namespace autofill {
+
+AutofillAgent::AutofillAgent(
+ content::RenderView* render_view,
+ PasswordAutofillManager* password_autofill_manager)
+ : content::RenderViewObserver(render_view),
+ password_autofill_manager_(password_autofill_manager),
+ autofill_query_id_(0),
+ autofill_action_(AUTOFILL_NONE),
+ topmost_frame_(NULL),
+ web_view_(render_view->GetWebView()),
+ display_warning_if_disabled_(false),
+ was_query_node_autofilled_(false),
+ has_shown_autofill_popup_for_current_edit_(false),
+ did_set_node_text_(false),
+ autocheckout_click_in_progress_(false),
+ ignore_text_changes_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+ render_view->GetWebView()->setAutofillClient(this);
+}
+
+AutofillAgent::~AutofillAgent() {}
+
+bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message)
+ IPC_MESSAGE_HANDLER(AutofillMsg_SuggestionsReturned, OnSuggestionsReturned)
+ IPC_MESSAGE_HANDLER(AutofillMsg_FormDataFilled, OnFormDataFilled)
+ IPC_MESSAGE_HANDLER(AutofillMsg_FieldTypePredictionsAvailable,
+ OnFieldTypePredictionsAvailable)
+ IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionFill,
+ OnSetAutofillActionFill)
+ IPC_MESSAGE_HANDLER(AutofillMsg_ClearForm,
+ OnClearForm)
+ IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionPreview,
+ OnSetAutofillActionPreview)
+ IPC_MESSAGE_HANDLER(AutofillMsg_ClearPreviewedForm,
+ OnClearPreviewedForm)
+ IPC_MESSAGE_HANDLER(AutofillMsg_SetNodeText,
+ OnSetNodeText)
+ IPC_MESSAGE_HANDLER(AutofillMsg_AcceptDataListSuggestion,
+ OnAcceptDataListSuggestion)
+ IPC_MESSAGE_HANDLER(AutofillMsg_AcceptPasswordAutofillSuggestion,
+ OnAcceptPasswordAutofillSuggestion)
+ IPC_MESSAGE_HANDLER(AutofillMsg_RequestAutocompleteResult,
+ OnRequestAutocompleteResult)
+ IPC_MESSAGE_HANDLER(AutofillMsg_FillFormsAndClick,
+ OnFillFormsAndClick)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) {
+ // The document has now been fully loaded. Scan for forms to be sent up to
+ // the browser.
+ std::vector<FormData> forms;
+
+ if (!frame->parent()) {
+ topmost_frame_ = frame;
+ form_elements_.clear();
+ form_cache_.ExtractFormsAndFormElements(*frame, &forms, &form_elements_);
+ } else {
+ form_cache_.ExtractForms(*frame, &forms);
+ }
+
+ if (!forms.empty()) {
+ Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
+ base::TimeTicks::Now()));
+ }
+}
+
+void AutofillAgent::DidStartProvisionalLoad(WebFrame* frame) {
+ if (!frame->parent()) {
+ topmost_frame_ = NULL;
+ WebKit::WebURL provisional_url =
+ frame->provisionalDataSource()->request().url();
+ WebKit::WebURL current_url = frame->dataSource()->request().url();
+ // If the URL of the topmost frame is changing and the current page is part
+ // of an Autocheckout flow, the click was successful as long as the
+ // provisional load is committed.
+ if (provisional_url != current_url && click_timer_.IsRunning()) {
+ click_timer_.Stop();
+ autocheckout_click_in_progress_ = true;
+ }
+ }
+}
+
+void AutofillAgent::DidFailProvisionalLoad(WebFrame* frame,
+ const WebKit::WebURLError& error) {
+ if (autocheckout_click_in_progress_) {
+ autocheckout_click_in_progress_ = false;
+ ClickFailed();
+ }
+}
+
+void AutofillAgent::DidCommitProvisionalLoad(WebFrame* frame,
+ bool is_new_navigation) {
+ autocheckout_click_in_progress_ = false;
+ in_flight_request_form_.reset();
+}
+
+void AutofillAgent::FrameDetached(WebFrame* frame) {
+ form_cache_.ResetFrame(*frame);
+ if (!frame->parent()) {
+ // |frame| is about to be destroyed so we need to clear |top_most_frame_|.
+ topmost_frame_ = NULL;
+ click_timer_.Stop();
+ }
+}
+
+void AutofillAgent::WillSubmitForm(WebFrame* frame,
+ const WebFormElement& form) {
+ FormData form_data;
+ if (WebFormElementToFormData(form,
+ WebFormControlElement(),
+ REQUIRE_AUTOCOMPLETE,
+ static_cast<ExtractMask>(
+ EXTRACT_VALUE | EXTRACT_OPTION_TEXT),
+ &form_data,
+ NULL)) {
+ Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data,
+ base::TimeTicks::Now()));
+ }
+}
+
+void AutofillAgent::ZoomLevelChanged() {
+ // Any time the zoom level changes, the page's content moves, so any Autofill
+ // popups should be hidden. This is only needed for the new Autofill UI
+ // because WebKit already knows to hide the old UI when this occurs.
+ HideHostPopups();
+}
+
+void AutofillAgent::DidChangeScrollOffset(WebKit::WebFrame*) {
+ HidePopups();
+}
+
+void AutofillAgent::didRequestAutocomplete(WebKit::WebFrame* frame,
+ const WebFormElement& form) {
+ FormData form_data;
+ if (!in_flight_request_form_.isNull() ||
+ !WebFormElementToFormData(form,
+ WebFormControlElement(),
+ REQUIRE_AUTOCOMPLETE,
+ EXTRACT_OPTIONS,
+ &form_data,
+ NULL)) {
+ WebFormElement(form).finishRequestAutocomplete(
+ WebFormElement::AutocompleteResultErrorDisabled);
+ return;
+ }
+
+ // Cancel any pending Autofill requests and hide any currently showing popups.
+ ++autofill_query_id_;
+ HidePopups();
+
+ in_flight_request_form_ = form;
+ // TODO(ramankk): Include SSLStatus within form_data and update the IPC.
+ Send(new AutofillHostMsg_RequestAutocomplete(
+ routing_id(),
+ form_data,
+ frame->document().url(),
+ render_view()->GetSSLStatusOfFrame(frame)));
+}
+
+void AutofillAgent::setIgnoreTextChanges(bool ignore) {
+ ignore_text_changes_ = ignore;
+}
+
+bool AutofillAgent::InputElementClicked(const WebInputElement& element,
+ bool was_focused,
+ bool is_focused) {
+ if (was_focused)
+ ShowSuggestions(element, true, false, true);
+
+ return false;
+}
+
+bool AutofillAgent::InputElementLostFocus() {
+ HideHostPopups();
+
+ return false;
+}
+
+void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node,
+ const WebString& value,
+ const WebString& label,
+ int item_id,
+ unsigned index) {
+ if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value))
+ return;
+
+ DCHECK(node == element_);
+
+ switch (item_id) {
+ case WebAutofillClient::MenuItemIDWarningMessage:
+ case WebAutofillClient::MenuItemIDSeparator:
+ NOTREACHED();
+ break;
+ case WebAutofillClient::MenuItemIDAutofillOptions:
+ // User selected 'Autofill Options'.
+ Send(new AutofillHostMsg_ShowAutofillDialog(routing_id()));
+ break;
+ case WebAutofillClient::MenuItemIDClearForm:
+ // User selected 'Clear form'.
+ form_cache_.ClearFormWithElement(element_);
+ break;
+ case WebAutofillClient::MenuItemIDAutocompleteEntry:
+ case WebAutofillClient::MenuItemIDPasswordEntry:
+ // User selected an Autocomplete or password entry, so we fill directly.
+ SetNodeText(value, &element_);
+ break;
+ case WebAutofillClient::MenuItemIDDataListEntry:
+ AcceptDataListSuggestion(value);
+ break;
+ default:
+ // A positive item_id is a unique id for an autofill (vs. autocomplete)
+ // suggestion.
+ DCHECK_GT(item_id, 0);
+ // Fill the values for the whole form.
+ FillAutofillFormData(node, item_id, AUTOFILL_FILL);
+ }
+}
+
+void AutofillAgent::didSelectAutofillSuggestion(const WebNode& node,
+ const WebString& value,
+ const WebString& label,
+ int item_id) {
+ if (password_autofill_manager_->DidSelectAutofillSuggestion(node))
+ return;
+
+ didClearAutofillSelection(node);
+
+ if (item_id > 0)
+ FillAutofillFormData(node, item_id, AUTOFILL_PREVIEW);
+}
+
+void AutofillAgent::didClearAutofillSelection(const WebNode& node) {
+ if (password_autofill_manager_->DidClearAutofillSelection(node))
+ return;
+
+ if (!element_.isNull() && node == element_) {
+ ClearPreviewedFormWithElement(element_, was_query_node_autofilled_);
+ } else {
+ // TODO(isherman): There seem to be rare cases where this code *is*
+ // reachable: see [ http://crbug.com/96321#c6 ]. Ideally we would
+ // understand those cases and fix the code to avoid them. However, so far I
+ // have been unable to reproduce such a case locally. If you hit this
+ // NOTREACHED(), please file a bug against me.
+ NOTREACHED();
+ }
+}
+
+void AutofillAgent::removeAutocompleteSuggestion(const WebString& name,
+ const WebString& value) {
+ Send(new AutofillHostMsg_RemoveAutocompleteEntry(routing_id(), name, value));
+}
+
+void AutofillAgent::textFieldDidEndEditing(const WebInputElement& element) {
+ password_autofill_manager_->TextFieldDidEndEditing(element);
+ has_shown_autofill_popup_for_current_edit_ = false;
+ Send(new AutofillHostMsg_DidEndTextFieldEditing(routing_id()));
+}
+
+void AutofillAgent::textFieldDidChange(const WebInputElement& element) {
+ if (ignore_text_changes_)
+ return;
+
+ if (did_set_node_text_) {
+ did_set_node_text_ = false;
+ return;
+ }
+
+ // We post a task for doing the Autofill as the caret position is not set
+ // properly at this point (http://bugs.webkit.org/show_bug.cgi?id=16976) and
+ // it is needed to trigger autofill.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&AutofillAgent::TextFieldDidChangeImpl,
+ weak_ptr_factory_.GetWeakPtr(), element));
+}
+
+void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) {
+ // If the element isn't focused then the changes don't matter. This check is
+ // required to properly handle IME interactions.
+ if (!element.focused())
+ return;
+
+ if (password_autofill_manager_->TextDidChangeInTextField(element)) {
+ element_ = element;
+ return;
+ }
+
+ ShowSuggestions(element, false, true, false);
+
+ FormData form;
+ FormFieldData field;
+ if (FindFormAndFieldForInputElement(element, &form, &field, REQUIRE_NONE)) {
+ Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field,
+ base::TimeTicks::Now()));
+ }
+}
+
+void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element,
+ const WebKeyboardEvent& event) {
+ if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event)) {
+ element_ = element;
+ return;
+ }
+
+ if (event.windowsKeyCode == ui::VKEY_DOWN ||
+ event.windowsKeyCode == ui::VKEY_UP)
+ ShowSuggestions(element, true, true, true);
+}
+
+void AutofillAgent::OnSuggestionsReturned(int query_id,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& unique_ids) {
+ if (query_id != autofill_query_id_)
+ return;
+
+ if (element_.isNull() || !element_.isFocusable())
+ return;
+
+ std::vector<string16> v(values);
+ std::vector<string16> l(labels);
+ std::vector<string16> i(icons);
+ std::vector<int> ids(unique_ids);
+
+ if (!element_.autoComplete() && !v.empty()) {
+ // If autofill is disabled and we had suggestions, show a warning instead.
+ v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED));
+ l.assign(1, string16());
+ i.assign(1, string16());
+ ids.assign(1, WebAutofillClient::MenuItemIDWarningMessage);
+ } else if (ids.size() > 1 &&
+ ids[0] == WebAutofillClient::MenuItemIDWarningMessage) {
+ // If we received an autofill warning plus some autocomplete suggestions,
+ // remove the autofill warning.
+ v.erase(v.begin());
+ l.erase(l.begin());
+ i.erase(i.begin());
+ ids.erase(ids.begin());
+ }
+
+ // If we were about to show a warning and we shouldn't, don't.
+ if (!display_warning_if_disabled_ && !v.empty() &&
+ ids[0] == WebAutofillClient::MenuItemIDWarningMessage) {
+ v.clear();
+ l.clear();
+ i.clear();
+ ids.clear();
+ }
+
+ // Only include "Autofill Options" special menu item if we have Autofill
+ // items, identified by |unique_ids| having at least one valid value.
+ bool has_autofill_item = false;
+ for (size_t i = 0; i < ids.size(); ++i) {
+ if (ids[i] > 0) {
+ has_autofill_item = true;
+ break;
+ }
+ }
+
+ if (has_autofill_item) {
+ v.push_back(string16());
+ l.push_back(string16());
+ i.push_back(string16());
+ ids.push_back(WebAutofillClient::MenuItemIDSeparator);
+
+ if (FormWithElementIsAutofilled(element_)) {
+ // The form has been auto-filled, so give the user the chance to clear the
+ // form. Append the 'Clear form' menu item.
+ v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
+ l.push_back(string16());
+ i.push_back(string16());
+ ids.push_back(WebAutofillClient::MenuItemIDClearForm);
+ }
+
+ // Append the 'Chrome Autofill options' menu item;
+ v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP));
+ l.push_back(string16());
+ i.push_back(string16());
+ ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
+ }
+
+ CombineDataListEntriesAndShow(element_, v, l, i, ids, has_autofill_item);
+}
+
+void AutofillAgent::CombineDataListEntriesAndShow(
+ const WebKit::WebInputElement& element,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& item_ids,
+ bool has_autofill_item) {
+ std::vector<string16> v;
+ std::vector<string16> l;
+ std::vector<string16> i;
+ std::vector<int> ids;
+
+ AppendDataListSuggestions(element, &v, &l, &i, &ids);
+
+ // If there are both <datalist> items and Autofill suggestions, add a
+ // separator between them.
+ if (!v.empty() && !values.empty()) {
+ v.push_back(string16());
+ l.push_back(string16());
+ i.push_back(string16());
+ ids.push_back(WebAutofillClient::MenuItemIDSeparator);
+ }
+
+ // Append the Autofill suggestions.
+ v.insert(v.end(), values.begin(), values.end());
+ l.insert(l.end(), labels.begin(), labels.end());
+ i.insert(i.end(), icons.begin(), icons.end());
+ ids.insert(ids.end(), item_ids.begin(), item_ids.end());
+
+ if (v.empty()) {
+ // No suggestions, any popup currently showing is obsolete.
+ HidePopups();
+ return;
+ }
+
+ WebKit::WebView* web_view = render_view()->GetWebView();
+ if (!web_view)
+ return;
+
+ // Send to WebKit for display.
+ web_view->applyAutofillSuggestions(element, v, l, i, ids);
+
+ Send(new AutofillHostMsg_DidShowAutofillSuggestions(
+ routing_id(),
+ has_autofill_item && !has_shown_autofill_popup_for_current_edit_));
+ has_shown_autofill_popup_for_current_edit_ |= has_autofill_item;
+}
+
+void AutofillAgent::AcceptDataListSuggestion(const string16& suggested_value) {
+ string16 new_value = suggested_value;
+ // If this element takes multiple values then replace the last part with
+ // the suggestion.
+ if (element_.isMultiple() &&
+ element_.formControlType() == WebString::fromUTF8("email")) {
+ std::vector<string16> parts;
+
+ base::SplitStringDontTrim(element_.editingValue(), ',', &parts);
+ if (parts.size() == 0)
+ parts.push_back(string16());
+
+ string16 last_part = parts.back();
+ // We want to keep just the leading whitespace.
+ for (size_t i = 0; i < last_part.size(); ++i) {
+ if (!IsWhitespace(last_part[i])) {
+ last_part = last_part.substr(0, i);
+ break;
+ }
+ }
+ last_part.append(suggested_value);
+ parts[parts.size() - 1] = last_part;
+
+ new_value = JoinString(parts, ',');
+ }
+ SetNodeText(new_value, &element_);
+}
+
+void AutofillAgent::OnFormDataFilled(int query_id,
+ const FormData& form) {
+ if (!render_view()->GetWebView() || query_id != autofill_query_id_)
+ return;
+
+ was_query_node_autofilled_ = element_.isAutofilled();
+
+ switch (autofill_action_) {
+ case AUTOFILL_FILL:
+ FillForm(form, element_);
+ Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(),
+ base::TimeTicks::Now()));
+ break;
+ case AUTOFILL_PREVIEW:
+ PreviewForm(form, element_);
+ Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id()));
+ break;
+ default:
+ NOTREACHED();
+ }
+ autofill_action_ = AUTOFILL_NONE;
+}
+
+void AutofillAgent::OnFieldTypePredictionsAvailable(
+ const std::vector<FormDataPredictions>& forms) {
+ for (size_t i = 0; i < forms.size(); ++i) {
+ form_cache_.ShowPredictions(forms[i]);
+ }
+}
+
+void AutofillAgent::OnSetAutofillActionFill() {
+ autofill_action_ = AUTOFILL_FILL;
+}
+
+void AutofillAgent::OnClearForm() {
+ form_cache_.ClearFormWithElement(element_);
+}
+
+void AutofillAgent::OnSetAutofillActionPreview() {
+ autofill_action_ = AUTOFILL_PREVIEW;
+}
+
+void AutofillAgent::OnClearPreviewedForm() {
+ didClearAutofillSelection(element_);
+}
+
+void AutofillAgent::OnSetNodeText(const string16& value) {
+ SetNodeText(value, &element_);
+}
+
+void AutofillAgent::OnAcceptDataListSuggestion(const string16& value) {
+ AcceptDataListSuggestion(value);
+}
+
+void AutofillAgent::OnAcceptPasswordAutofillSuggestion(const string16& value) {
+ // We need to make sure this is handled here because the browser process
+ // skipped it handling because it believed it would be handled here. If it
+ // isn't handled here then the browser logic needs to be updated.
+ bool handled = password_autofill_manager_->DidAcceptAutofillSuggestion(
+ element_,
+ value);
+ DCHECK(handled);
+}
+
+void AutofillAgent::OnRequestAutocompleteResult(
+ WebFormElement::AutocompleteResult result, const FormData& form_data) {
+ if (in_flight_request_form_.isNull())
+ return;
+
+ if (result == WebFormElement::AutocompleteResultSuccess)
+ FillFormIncludingNonFocusableElements(form_data, in_flight_request_form_);
+
+ in_flight_request_form_.finishRequestAutocomplete(result);
+ in_flight_request_form_.reset();
+}
+
+void AutofillAgent::OnFillFormsAndClick(
+ const std::vector<FormData>& forms,
+ const WebElementDescriptor& click_element_descriptor) {
+ DCHECK_EQ(forms.size(), form_elements_.size());
+
+ // Fill the form.
+ for (size_t i = 0; i < forms.size(); ++i)
+ FillFormIncludingNonFocusableElements(forms[i], form_elements_[i]);
+
+ // It's possible that clicking the element to proceed in an Autocheckout
+ // flow will not actually proceed to the next step in the flow, e.g. there
+ // is a new required field that Autocheckout does not know how to fill. In
+ // order to capture this case and present the user with an error a timer is
+ // set that informs the browser of the error. |click_timer_| has to be started
+ // before clicking so it can start before DidStartProvisionalLoad started.
+ click_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromSeconds(kAutocheckoutClickTimeout),
+ this,
+ &AutofillAgent::ClickFailed);
+ if (!ClickElement(topmost_frame_->document(),
+ click_element_descriptor)) {
+ click_timer_.Stop();
+ Send(new AutofillHostMsg_ClickFailed(routing_id(),
+ MISSING_ADVANCE));
+ }
+}
+
+void AutofillAgent::ClickFailed() {
+ Send(new AutofillHostMsg_ClickFailed(routing_id(),
+ CANNOT_PROCEED));
+}
+
+void AutofillAgent::ShowSuggestions(const WebInputElement& element,
+ bool autofill_on_empty_values,
+ bool requires_caret_at_end,
+ bool display_warning_if_disabled) {
+ if (!element.isEnabled() || element.isReadOnly() || !element.isTextField() ||
+ element.isPasswordField() || !element.suggestedValue().isEmpty())
+ return;
+
+ // Don't attempt to autofill with values that are too large or if filling
+ // criteria are not met.
+ WebString value = element.editingValue();
+ if (value.length() > kMaximumTextSizeForAutofill ||
+ (!autofill_on_empty_values && value.isEmpty()) ||
+ (requires_caret_at_end &&
+ (element.selectionStart() != element.selectionEnd() ||
+ element.selectionEnd() != static_cast<int>(value.length())))) {
+ // Any popup currently showing is obsolete.
+ HidePopups();
+ return;
+ }
+
+ element_ = element;
+
+ // If autocomplete is disabled at the form level, then we might want to show a
+ // warning in place of suggestions. However, if autocomplete is disabled
+ // specifically for this field, we never want to show a warning. Otherwise,
+ // we might interfere with custom popups (e.g. search suggestions) used by the
+ // website. Note that we cannot use the WebKit method element.autoComplete()
+ // as it does not allow us to distinguish the case where autocomplete is
+ // disabled for *both* the element and for the form.
+ // Also, if the field has no name, then we won't have values.
+ const string16 autocomplete_attribute = element.getAttribute("autocomplete");
+ if (LowerCaseEqualsASCII(autocomplete_attribute, "off") ||
+ element.nameForAutofill().isEmpty()) {
+ CombineDataListEntriesAndShow(element, std::vector<string16>(),
+ std::vector<string16>(),
+ std::vector<string16>(),
+ std::vector<int>(), false);
+ return;
+ }
+
+ QueryAutofillSuggestions(element, display_warning_if_disabled);
+}
+
+void AutofillAgent::QueryAutofillSuggestions(const WebInputElement& element,
+ bool display_warning_if_disabled) {
+ if (!element.document().frame())
+ return;
+
+ static int query_counter = 0;
+ autofill_query_id_ = query_counter++;
+ display_warning_if_disabled_ = display_warning_if_disabled;
+
+ // If autocomplete is disabled at the form level, we want to see if there
+ // would have been any suggestions were it enabled, so that we can show a
+ // warning. Otherwise, we want to ignore fields that disable autocomplete, so
+ // that the suggestions list does not include suggestions for these form
+ // fields -- see comment 1 on http://crbug.com/69914
+ // Rather than testing the form's autocomplete enabled state, we test the
+ // element's state. The DCHECK below ensures that this is equivalent.
+ DCHECK(element.autoComplete() || !element.form().autoComplete());
+ const RequirementsMask requirements =
+ element.autoComplete() ? REQUIRE_AUTOCOMPLETE : REQUIRE_NONE;
+
+ FormData form;
+ FormFieldData field;
+ if (!FindFormAndFieldForInputElement(element, &form, &field, requirements)) {
+ // If we didn't find the cached form, at least let autocomplete have a shot
+ // at providing suggestions.
+ WebFormControlElementToFormField(element, EXTRACT_VALUE, &field);
+ }
+
+ gfx::Rect bounding_box(element_.boundsInViewportSpace());
+
+ float scale = web_view_->pageScaleFactor();
+ gfx::RectF bounding_box_scaled(bounding_box.x() * scale,
+ bounding_box.y() * scale,
+ bounding_box.width() * scale,
+ bounding_box.height() * scale);
+
+ // Find the datalist values and send them to the browser process.
+ std::vector<string16> data_list_values;
+ std::vector<string16> data_list_labels;
+ std::vector<string16> data_list_icons;
+ std::vector<int> data_list_unique_ids;
+ AppendDataListSuggestions(element_,
+ &data_list_values,
+ &data_list_labels,
+ &data_list_icons,
+ &data_list_unique_ids);
+
+ TrimDataListsForIPC(&data_list_values,
+ &data_list_labels,
+ &data_list_icons,
+ &data_list_unique_ids);
+
+ Send(new AutofillHostMsg_SetDataList(routing_id(),
+ data_list_values,
+ data_list_labels,
+ data_list_icons,
+ data_list_unique_ids));
+
+ // Add SSL Status in the formdata to let browser process alert user
+ // appropriately using browser UI.
+ form.ssl_status = render_view()->GetSSLStatusOfFrame(
+ element.document().frame());
+ Send(new AutofillHostMsg_QueryFormFieldAutofill(routing_id(),
+ autofill_query_id_,
+ form,
+ field,
+ bounding_box_scaled,
+ display_warning_if_disabled));
+}
+
+void AutofillAgent::FillAutofillFormData(const WebNode& node,
+ int unique_id,
+ AutofillAction action) {
+ DCHECK_GT(unique_id, 0);
+
+ static int query_counter = 0;
+ autofill_query_id_ = query_counter++;
+
+ FormData form;
+ FormFieldData field;
+ if (!FindFormAndFieldForInputElement(node.toConst<WebInputElement>(), &form,
+ &field, REQUIRE_AUTOCOMPLETE)) {
+ return;
+ }
+
+ autofill_action_ = action;
+ Send(new AutofillHostMsg_FillAutofillFormData(
+ routing_id(), autofill_query_id_, form, field, unique_id));
+}
+
+void AutofillAgent::SetNodeText(const string16& value,
+ WebKit::WebInputElement* node) {
+ did_set_node_text_ = true;
+ string16 substring = value;
+ substring = substring.substr(0, node->maxLength());
+
+ node->setEditingValue(substring);
+}
+
+void AutofillAgent::HidePopups() {
+ WebKit::WebView* web_view = render_view()->GetWebView();
+ if (web_view)
+ web_view->hidePopups();
+
+ HideHostPopups();
+}
+
+void AutofillAgent::HideHostPopups() {
+ Send(new AutofillHostMsg_HideAutofillPopup(routing_id()));
+}
+
+} // namespace autofill
diff --git a/components/autofill/renderer/autofill_agent.h b/components/autofill/renderer/autofill_agent.h
new file mode 100644
index 0000000..5b24013
--- /dev/null
+++ b/components/autofill/renderer/autofill_agent.h
@@ -0,0 +1,270 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_RENDERER_AUTOFILL_AGENT_H_
+#define COMPONENTS_AUTOFILL_RENDERER_AUTOFILL_AGENT_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer.h"
+#include "components/autofill/renderer/form_cache.h"
+#include "components/autofill/renderer/page_click_listener.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+
+struct FormFieldData;
+
+namespace WebKit {
+class WebNode;
+class WebView;
+}
+
+namespace autofill {
+
+struct WebElementDescriptor;
+class PasswordAutofillManager;
+
+// AutofillAgent deals with Autofill related communications between WebKit and
+// the browser. There is one AutofillAgent per RenderView.
+// This code was originally part of RenderView.
+// Note that Autofill encompasses:
+// - single text field suggestions, that we usually refer to as Autocomplete,
+// - password form fill, refered to as Password Autofill, and
+// - entire form fill based on one field entry, referred to as Form Autofill.
+
+class AutofillAgent : public content::RenderViewObserver,
+ public PageClickListener,
+ public WebKit::WebAutofillClient {
+ public:
+ // PasswordAutofillManager is guaranteed to outlive AutofillAgent.
+ AutofillAgent(content::RenderView* render_view,
+ PasswordAutofillManager* password_autofill_manager);
+ virtual ~AutofillAgent();
+
+ private:
+ enum AutofillAction {
+ AUTOFILL_NONE, // No state set.
+ AUTOFILL_FILL, // Fill the Autofill form data.
+ AUTOFILL_PREVIEW, // Preview the Autofill form data.
+ };
+
+ // RenderView::Observer:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void DidFinishDocumentLoad(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void DidStartProvisionalLoad(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void DidFailProvisionalLoad(
+ WebKit::WebFrame* frame,
+ const WebKit::WebURLError& error) OVERRIDE;
+ virtual void DidCommitProvisionalLoad(WebKit::WebFrame* frame,
+ bool is_new_navigation) OVERRIDE;
+ virtual void FrameDetached(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void WillSubmitForm(WebKit::WebFrame* frame,
+ const WebKit::WebFormElement& form) OVERRIDE;
+ virtual void ZoomLevelChanged() OVERRIDE;
+ virtual void DidChangeScrollOffset(WebKit::WebFrame* frame) OVERRIDE;
+
+ // PageClickListener:
+ virtual bool InputElementClicked(const WebKit::WebInputElement& element,
+ bool was_focused,
+ bool is_focused) OVERRIDE;
+ virtual bool InputElementLostFocus() OVERRIDE;
+
+ // WebKit::WebAutofillClient:
+ virtual void didAcceptAutofillSuggestion(const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label,
+ int item_id,
+ unsigned index) OVERRIDE;
+ virtual void didSelectAutofillSuggestion(const WebKit::WebNode& node,
+ const WebKit::WebString& value,
+ const WebKit::WebString& label,
+ int item_id) OVERRIDE;
+ virtual void didClearAutofillSelection(const WebKit::WebNode& node) OVERRIDE;
+ virtual void removeAutocompleteSuggestion(
+ const WebKit::WebString& name,
+ const WebKit::WebString& value) OVERRIDE;
+ virtual void textFieldDidEndEditing(
+ const WebKit::WebInputElement& element) OVERRIDE;
+ virtual void textFieldDidChange(
+ const WebKit::WebInputElement& element) OVERRIDE;
+ virtual void textFieldDidReceiveKeyDown(
+ const WebKit::WebInputElement& element,
+ const WebKit::WebKeyboardEvent& event) OVERRIDE;
+ virtual void didRequestAutocomplete(
+ WebKit::WebFrame* frame,
+ const WebKit::WebFormElement& form) OVERRIDE;
+ virtual void setIgnoreTextChanges(bool ignore) OVERRIDE;
+
+ void OnSuggestionsReturned(int query_id,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& unique_ids);
+ void OnFormDataFilled(int query_id, const FormData& form);
+ void OnFieldTypePredictionsAvailable(
+ const std::vector<FormDataPredictions>& forms);
+
+ // For external Autofill selection.
+ void OnSetAutofillActionFill();
+ void OnClearForm();
+ void OnSetAutofillActionPreview();
+ void OnClearPreviewedForm();
+ void OnSetNodeText(const string16& value);
+ void OnAcceptDataListSuggestion(const string16& value);
+ void OnAcceptPasswordAutofillSuggestion(const string16& value);
+
+ // Called when interactive autocomplete finishes.
+ void OnRequestAutocompleteResult(
+ WebKit::WebFormElement::AutocompleteResult result,
+ const FormData& form_data);
+
+ // Called when an autocomplete request succeeds or fails with the |result|.
+ void FinishAutocompleteRequest(
+ WebKit::WebFormElement::AutocompleteResult result);
+
+ // Called when the Autofill server hints that this page should be filled using
+ // Autocheckout. All the relevant form fields in |form_data| will be filled
+ // and then element specified by |element_descriptor| will be clicked to
+ // proceed to the next step of the form.
+ void OnFillFormsAndClick(const std::vector<FormData>& form_data,
+ const WebElementDescriptor& element_descriptor);
+
+ // Called when clicking an Autocheckout proceed element fails to do anything.
+ void ClickFailed();
+
+ // Called in a posted task by textFieldDidChange() to work-around a WebKit bug
+ // http://bugs.webkit.org/show_bug.cgi?id=16976
+ void TextFieldDidChangeImpl(const WebKit::WebInputElement& element);
+
+ // Shows the autofill suggestions for |element|.
+ // This call is asynchronous and may or may not lead to the showing of a
+ // suggestion popup (no popup is shown if there are no available suggestions).
+ // |autofill_on_empty_values| specifies whether suggestions should be shown
+ // when |element| contains no text.
+ // |requires_caret_at_end| specifies whether suggestions should be shown when
+ // the caret is not after the last character in |element|.
+ // |display_warning_if_disabled| specifies whether a warning should be
+ // displayed to the user if Autofill has suggestions available, but cannot
+ // fill them because it is disabled (e.g. when trying to fill a credit card
+ // form on a non-secure website).
+ void ShowSuggestions(const WebKit::WebInputElement& element,
+ bool autofill_on_empty_values,
+ bool requires_caret_at_end,
+ bool display_warning_if_disabled);
+
+ // Queries the browser for Autocomplete and Autofill suggestions for the given
+ // |element|.
+ void QueryAutofillSuggestions(const WebKit::WebInputElement& element,
+ bool display_warning_if_disabled);
+
+ // Combines DataList suggestion entries with the autofill ones and show them
+ // to the user.
+ void CombineDataListEntriesAndShow(const WebKit::WebInputElement& element,
+ const std::vector<string16>& values,
+ const std::vector<string16>& labels,
+ const std::vector<string16>& icons,
+ const std::vector<int>& item_ids,
+ bool has_autofill_item);
+
+ // Sets the element value to reflect the selected |suggested_value|.
+ void AcceptDataListSuggestion(const string16& suggested_value);
+
+ // Queries the AutofillManager for form data for the form containing |node|.
+ // |value| is the current text in the field, and |unique_id| is the selected
+ // profile's unique ID. |action| specifies whether to Fill or Preview the
+ // values returned from the AutofillManager.
+ void FillAutofillFormData(const WebKit::WebNode& node,
+ int unique_id,
+ AutofillAction action);
+
+ // Fills |form| and |field| with the FormData and FormField corresponding to
+ // |node|. Returns true if the data was found; and false otherwise.
+ bool FindFormAndFieldForNode(
+ const WebKit::WebNode& node,
+ FormData* form,
+ FormFieldData* field) WARN_UNUSED_RESULT;
+
+ // Set |node| to display the given |value|.
+ void SetNodeText(const string16& value, WebKit::WebInputElement* node);
+
+ // Hides any currently showing Autofill popups in the renderer or browser.
+ void HidePopups();
+
+ // Hides any currently showing Autofill popups in the browser only.
+ void HideHostPopups();
+
+ FormCache form_cache_;
+
+ PasswordAutofillManager* password_autofill_manager_; // WEAK reference.
+
+ // The ID of the last request sent for form field Autofill. Used to ignore
+ // out of date responses.
+ int autofill_query_id_;
+
+ // The element corresponding to the last request sent for form field Autofill.
+ WebKit::WebInputElement element_;
+
+ // The form element currently requesting an interactive autocomplete.
+ WebKit::WebFormElement in_flight_request_form_;
+
+ // All the form elements seen in the top frame.
+ std::vector<WebKit::WebFormElement> form_elements_;
+
+ // The action to take when receiving Autofill data from the AutofillManager.
+ AutofillAction autofill_action_;
+
+ // Pointer to the current topmost frame. Used in autocheckout flows so
+ // elements can be clicked.
+ WebKit::WebFrame* topmost_frame_;
+
+ // Pointer to the WebView. Used to access page scale factor.
+ WebKit::WebView* web_view_;
+
+ // Should we display a warning if autofill is disabled?
+ bool display_warning_if_disabled_;
+
+ // Was the query node autofilled prior to previewing the form?
+ bool was_query_node_autofilled_;
+
+ // Have we already shown Autofill suggestions for the field the user is
+ // currently editing? Used to keep track of state for metrics logging.
+ bool has_shown_autofill_popup_for_current_edit_;
+
+ // If true we just set the node text so we shouldn't show the popup.
+ bool did_set_node_text_;
+
+ // Watchdog timer for clicking in Autocheckout flows.
+ base::OneShotTimer<AutofillAgent> click_timer_;
+
+ // Used to signal that we need to watch for loading failures in an
+ // Autocheckout flow.
+ bool autocheckout_click_in_progress_;
+
+ // Whether or not to ignore text changes. Useful for when we're committing
+ // a composition when we are defocusing the WebView and we don't want to
+ // trigger an autofill popup to show.
+ bool ignore_text_changes_;
+
+ base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_;
+
+ friend class PasswordAutofillManagerTest;
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, FillFormElement);
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, SendForms);
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, ShowAutofillWarning);
+ FRIEND_TEST_ALL_PREFIXES(PasswordAutofillManagerTest, WaitUsername);
+ FRIEND_TEST_ALL_PREFIXES(PasswordAutofillManagerTest, SuggestionAccept);
+ FRIEND_TEST_ALL_PREFIXES(PasswordAutofillManagerTest, SuggestionSelect);
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillAgent);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_AUTOFILL_AGENT_H_
diff --git a/components/autofill/renderer/form_autofill_util.cc b/components/autofill/renderer/form_autofill_util.cc
new file mode 100644
index 0000000..8b0e6fb
--- /dev/null
+++ b/components/autofill/renderer/form_autofill_util.cc
@@ -0,0 +1,1032 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/form_autofill_util.h"
+
+#include <map>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/common/autofill_switches.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/web_element_descriptor.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebExceptionCode.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebLabelElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNodeList.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebOptionElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSelectElement.h"
+
+using WebKit::WebDocument;
+using WebKit::WebElement;
+using WebKit::WebExceptionCode;
+using WebKit::WebFormControlElement;
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebLabelElement;
+using WebKit::WebNode;
+using WebKit::WebNodeList;
+using WebKit::WebOptionElement;
+using WebKit::WebSelectElement;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+namespace {
+
+using autofill::ExtractAutofillableElements;
+using autofill::IsAutofillableInputElement;
+using autofill::IsCheckableElement;
+using autofill::IsSelectElement;
+using autofill::IsTextInput;
+
+// The maximum length allowed for form data.
+const size_t kMaxDataLength = 1024;
+
+bool IsOptionElement(const WebElement& element) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kOption, ("option"));
+ return element.hasTagName(kOption);
+}
+
+bool IsScriptElement(const WebElement& element) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kScript, ("script"));
+ return element.hasTagName(kScript);
+}
+
+bool IsNoScriptElement(const WebElement& element) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kNoScript, ("noscript"));
+ return element.hasTagName(kNoScript);
+}
+
+bool HasTagName(const WebNode& node, const WebKit::WebString& tag) {
+ return node.isElementNode() && node.toConst<WebElement>().hasTagName(tag);
+}
+
+bool IsAutofillableElement(const WebFormControlElement& element) {
+ const WebInputElement* input_element = toWebInputElement(&element);
+ return IsAutofillableInputElement(input_element) || IsSelectElement(element);
+}
+
+// Check whether the given field satisfies the REQUIRE_AUTOCOMPLETE requirement.
+// When Autocheckout is enabled, this requirement is enforced in the browser
+// process rather than in the renderer process, and hence all fields are
+// considered to satisfy this requirement.
+bool SatisfiesRequireAutocomplete(const WebInputElement& input_element) {
+ return input_element.autoComplete() ||
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalFormFilling);
+}
+
+// Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed
+// to a single space. If |force_whitespace| is true, then the resulting string
+// is guaranteed to have a space between |prefix| and |suffix|. Otherwise, the
+// result includes a space only if |prefix| has trailing whitespace or |suffix|
+// has leading whitespace.
+// A few examples:
+// * CombineAndCollapseWhitespace("foo", "bar", false) -> "foobar"
+// * CombineAndCollapseWhitespace("foo", "bar", true) -> "foo bar"
+// * CombineAndCollapseWhitespace("foo ", "bar", false) -> "foo bar"
+// * CombineAndCollapseWhitespace("foo", " bar", false) -> "foo bar"
+// * CombineAndCollapseWhitespace("foo", " bar", true) -> "foo bar"
+// * CombineAndCollapseWhitespace("foo ", " bar", false) -> "foo bar"
+// * CombineAndCollapseWhitespace(" foo", "bar ", false) -> " foobar "
+// * CombineAndCollapseWhitespace(" foo", "bar ", true) -> " foo bar "
+const string16 CombineAndCollapseWhitespace(const string16& prefix,
+ const string16& suffix,
+ bool force_whitespace) {
+ string16 prefix_trimmed;
+ TrimPositions prefix_trailing_whitespace =
+ TrimWhitespace(prefix, TRIM_TRAILING, &prefix_trimmed);
+
+ // Recursively compute the children's text.
+ string16 suffix_trimmed;
+ TrimPositions suffix_leading_whitespace =
+ TrimWhitespace(suffix, TRIM_LEADING, &suffix_trimmed);
+
+ if (prefix_trailing_whitespace || suffix_leading_whitespace ||
+ force_whitespace) {
+ return prefix_trimmed + ASCIIToUTF16(" ") + suffix_trimmed;
+ } else {
+ return prefix_trimmed + suffix_trimmed;
+ }
+}
+
+// This is a helper function for the FindChildText() function (see below).
+// Search depth is limited with the |depth| parameter.
+string16 FindChildTextInner(const WebNode& node, int depth) {
+ if (depth <= 0 || node.isNull())
+ return string16();
+
+ // Skip over comments.
+ if (node.nodeType() == WebNode::CommentNode)
+ return FindChildTextInner(node.nextSibling(), depth - 1);
+
+ if (node.nodeType() != WebNode::ElementNode &&
+ node.nodeType() != WebNode::TextNode)
+ return string16();
+
+ // Ignore elements known not to contain inferable labels.
+ if (node.isElementNode()) {
+ const WebElement element = node.toConst<WebElement>();
+ if (IsOptionElement(element) ||
+ IsScriptElement(element) ||
+ IsNoScriptElement(element) ||
+ (element.isFormControlElement() &&
+ IsAutofillableElement(element.toConst<WebFormControlElement>()))) {
+ return string16();
+ }
+ }
+
+ // Extract the text exactly at this node.
+ string16 node_text = node.nodeValue();
+
+ // Recursively compute the children's text.
+ // Preserve inter-element whitespace separation.
+ string16 child_text = FindChildTextInner(node.firstChild(), depth - 1);
+ bool add_space = node.nodeType() == WebNode::TextNode && node_text.empty();
+ node_text = CombineAndCollapseWhitespace(node_text, child_text, add_space);
+
+ // Recursively compute the siblings' text.
+ // Again, preserve inter-element whitespace separation.
+ string16 sibling_text = FindChildTextInner(node.nextSibling(), depth - 1);
+ add_space = node.nodeType() == WebNode::TextNode && node_text.empty();
+ node_text = CombineAndCollapseWhitespace(node_text, sibling_text, add_space);
+
+ return node_text;
+}
+
+// Returns the aggregated values of the descendants of |element| that are
+// non-empty text nodes. This is a faster alternative to |innerText()| for
+// performance critical operations. It does a full depth-first search so can be
+// used when the structure is not directly known. However, unlike with
+// |innerText()|, the search depth and breadth are limited to a fixed threshold.
+// Whitespace is trimmed from text accumulated at descendant nodes.
+string16 FindChildText(const WebNode& node) {
+ if (node.isTextNode())
+ return node.nodeValue();
+
+ WebNode child = node.firstChild();
+
+ const int kChildSearchDepth = 10;
+ string16 node_text = FindChildTextInner(child, kChildSearchDepth);
+ TrimWhitespace(node_text, TRIM_ALL, &node_text);
+ return node_text;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a previous sibling of |element|,
+// e.g. Some Text <input ...>
+// or Some <span>Text</span> <input ...>
+// or <p>Some Text</p><input ...>
+// or <label>Some Text</label> <input ...>
+// or Some Text <img><input ...>
+// or <b>Some Text</b><br/> <input ...>.
+string16 InferLabelFromPrevious(const WebFormControlElement& element) {
+ string16 inferred_label;
+ WebNode previous = element;
+ while (true) {
+ previous = previous.previousSibling();
+ if (previous.isNull())
+ break;
+
+ // Skip over comments.
+ WebNode::NodeType node_type = previous.nodeType();
+ if (node_type == WebNode::CommentNode)
+ continue;
+
+ // Otherwise, only consider normal HTML elements and their contents.
+ if (node_type != WebNode::TextNode &&
+ node_type != WebNode::ElementNode)
+ break;
+
+ // A label might be split across multiple "lightweight" nodes.
+ // Coalesce any text contained in multiple consecutive
+ // (a) plain text nodes or
+ // (b) inline HTML elements that are essentially equivalent to text nodes.
+ CR_DEFINE_STATIC_LOCAL(WebString, kBold, ("b"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kStrong, ("strong"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kSpan, ("span"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kFont, ("font"));
+ if (previous.isTextNode() ||
+ HasTagName(previous, kBold) || HasTagName(previous, kStrong) ||
+ HasTagName(previous, kSpan) || HasTagName(previous, kFont)) {
+ string16 value = FindChildText(previous);
+ // A text node's value will be empty if it is for a line break.
+ bool add_space = previous.isTextNode() && value.empty();
+ inferred_label =
+ CombineAndCollapseWhitespace(value, inferred_label, add_space);
+ continue;
+ }
+
+ // If we have identified a partial label and have reached a non-lightweight
+ // element, consider the label to be complete.
+ string16 trimmed_label;
+ TrimWhitespace(inferred_label, TRIM_ALL, &trimmed_label);
+ if (!trimmed_label.empty())
+ break;
+
+ // <img> and <br> tags often appear between the input element and its
+ // label text, so skip over them.
+ CR_DEFINE_STATIC_LOCAL(WebString, kImage, ("img"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kBreak, ("br"));
+ if (HasTagName(previous, kImage) || HasTagName(previous, kBreak))
+ continue;
+
+ // We only expect <p> and <label> tags to contain the full label text.
+ CR_DEFINE_STATIC_LOCAL(WebString, kPage, ("p"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kLabel, ("label"));
+ if (HasTagName(previous, kPage) || HasTagName(previous, kLabel))
+ inferred_label = FindChildText(previous);
+
+ break;
+ }
+
+ TrimWhitespace(inferred_label, TRIM_ALL, &inferred_label);
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// enclosing list item,
+// e.g. <li>Some Text<input ...><input ...><input ...></tr>
+string16 InferLabelFromListItem(const WebFormControlElement& element) {
+ WebNode parent = element.parentNode();
+ CR_DEFINE_STATIC_LOCAL(WebString, kListItem, ("li"));
+ while (!parent.isNull() && parent.isElementNode() &&
+ !parent.to<WebElement>().hasTagName(kListItem)) {
+ parent = parent.parentNode();
+ }
+
+ if (!parent.isNull() && HasTagName(parent, kListItem))
+ return FindChildText(parent);
+
+ return string16();
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// surrounding table structure,
+// e.g. <tr><td>Some Text</td><td><input ...></td></tr>
+// or <tr><th>Some Text</th><td><input ...></td></tr>
+// or <tr><td><b>Some Text</b></td><td><b><input ...></b></td></tr>
+// or <tr><th><b>Some Text</b></th><td><b><input ...></b></td></tr>
+string16 InferLabelFromTableColumn(const WebFormControlElement& element) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kTableCell, ("td"));
+ WebNode parent = element.parentNode();
+ while (!parent.isNull() && parent.isElementNode() &&
+ !parent.to<WebElement>().hasTagName(kTableCell)) {
+ parent = parent.parentNode();
+ }
+
+ if (parent.isNull())
+ return string16();
+
+ // Check all previous siblings, skipping non-element nodes, until we find a
+ // non-empty text block.
+ string16 inferred_label;
+ WebNode previous = parent.previousSibling();
+ CR_DEFINE_STATIC_LOCAL(WebString, kTableHeader, ("th"));
+ while (inferred_label.empty() && !previous.isNull()) {
+ if (HasTagName(previous, kTableCell) || HasTagName(previous, kTableHeader))
+ inferred_label = FindChildText(previous);
+
+ previous = previous.previousSibling();
+ }
+
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// surrounding table structure,
+// e.g. <tr><td>Some Text</td></tr><tr><td><input ...></td></tr>
+string16 InferLabelFromTableRow(const WebFormControlElement& element) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kTableRow, ("tr"));
+ WebNode parent = element.parentNode();
+ while (!parent.isNull() && parent.isElementNode() &&
+ !parent.to<WebElement>().hasTagName(kTableRow)) {
+ parent = parent.parentNode();
+ }
+
+ if (parent.isNull())
+ return string16();
+
+ // Check all previous siblings, skipping non-element nodes, until we find a
+ // non-empty text block.
+ string16 inferred_label;
+ WebNode previous = parent.previousSibling();
+ while (inferred_label.empty() && !previous.isNull()) {
+ if (HasTagName(previous, kTableRow))
+ inferred_label = FindChildText(previous);
+
+ previous = previous.previousSibling();
+ }
+
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a surrounding div table,
+// e.g. <div>Some Text<span><input ...></span></div>
+// e.g. <div>Some Text</div><div><input ...></div>
+string16 InferLabelFromDivTable(const WebFormControlElement& element) {
+ WebNode node = element.parentNode();
+ bool looking_for_parent = true;
+
+ // Search the sibling and parent <div>s until we find a candidate label.
+ string16 inferred_label;
+ CR_DEFINE_STATIC_LOCAL(WebString, kDiv, ("div"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kTable, ("table"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kFieldSet, ("fieldset"));
+ while (inferred_label.empty() && !node.isNull()) {
+ if (HasTagName(node, kDiv)) {
+ looking_for_parent = false;
+ inferred_label = FindChildText(node);
+ } else if (looking_for_parent &&
+ (HasTagName(node, kTable) || HasTagName(node, kFieldSet))) {
+ // If the element is in a table or fieldset, its label most likely is too.
+ break;
+ }
+
+ if (node.previousSibling().isNull()) {
+ // If there are no more siblings, continue walking up the tree.
+ looking_for_parent = true;
+ }
+
+ if (looking_for_parent)
+ node = node.parentNode();
+ else
+ node = node.previousSibling();
+ }
+
+ return inferred_label;
+}
+
+// Helper for |InferLabelForElement()| that infers a label, if possible, from
+// a surrounding definition list,
+// e.g. <dl><dt>Some Text</dt><dd><input ...></dd></dl>
+// e.g. <dl><dt><b>Some Text</b></dt><dd><b><input ...></b></dd></dl>
+string16 InferLabelFromDefinitionList(const WebFormControlElement& element) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kDefinitionData, ("dd"));
+ WebNode parent = element.parentNode();
+ while (!parent.isNull() && parent.isElementNode() &&
+ !parent.to<WebElement>().hasTagName(kDefinitionData))
+ parent = parent.parentNode();
+
+ if (parent.isNull() || !HasTagName(parent, kDefinitionData))
+ return string16();
+
+ // Skip by any intervening text nodes.
+ WebNode previous = parent.previousSibling();
+ while (!previous.isNull() && previous.isTextNode())
+ previous = previous.previousSibling();
+
+ CR_DEFINE_STATIC_LOCAL(WebString, kDefinitionTag, ("dt"));
+ if (previous.isNull() || !HasTagName(previous, kDefinitionTag))
+ return string16();
+
+ return FindChildText(previous);
+}
+
+// Infers corresponding label for |element| from surrounding context in the DOM,
+// e.g. the contents of the preceding <p> tag or text element.
+string16 InferLabelForElement(const WebFormControlElement& element) {
+ string16 inferred_label = InferLabelFromPrevious(element);
+ if (!inferred_label.empty())
+ return inferred_label;
+
+ // If we didn't find a label, check for list item case.
+ inferred_label = InferLabelFromListItem(element);
+ if (!inferred_label.empty())
+ return inferred_label;
+
+ // If we didn't find a label, check for table cell case.
+ inferred_label = InferLabelFromTableColumn(element);
+ if (!inferred_label.empty())
+ return inferred_label;
+
+ // If we didn't find a label, check for table row case.
+ inferred_label = InferLabelFromTableRow(element);
+ if (!inferred_label.empty())
+ return inferred_label;
+
+ // If we didn't find a label, check for definition list case.
+ inferred_label = InferLabelFromDefinitionList(element);
+ if (!inferred_label.empty())
+ return inferred_label;
+
+ // If we didn't find a label, check for div table case.
+ return InferLabelFromDivTable(element);
+}
+
+// Fills |option_strings| with the values of the <option> elements present in
+// |select_element|.
+void GetOptionStringsFromElement(const WebSelectElement& select_element,
+ std::vector<string16>* option_values,
+ std::vector<string16>* option_contents) {
+ DCHECK(!select_element.isNull());
+
+ option_values->clear();
+ option_contents->clear();
+ WebVector<WebElement> list_items = select_element.listItems();
+ option_values->reserve(list_items.size());
+ option_contents->reserve(list_items.size());
+ for (size_t i = 0; i < list_items.size(); ++i) {
+ if (IsOptionElement(list_items[i])) {
+ const WebOptionElement option = list_items[i].toConst<WebOptionElement>();
+ option_values->push_back(option.value());
+ option_contents->push_back(option.text());
+ }
+ }
+}
+
+// The callback type used by |ForEachMatchingFormField()|.
+typedef void (*Callback)(const FormFieldData&,
+ bool, /* is_initiating_element */
+ WebKit::WebFormControlElement*);
+
+// For each autofillable field in |data| that matches a field in the |form|,
+// the |callback| is invoked with the corresponding |form| field data.
+void ForEachMatchingFormField(const WebFormElement& form_element,
+ const WebElement& initiating_element,
+ const FormData& data,
+ bool only_focusable_elements,
+ bool force_override,
+ Callback callback) {
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_AUTOCOMPLETE,
+ &control_elements);
+
+ if (control_elements.size() != data.fields.size()) {
+ // This case should be reachable only for pathological websites and tests,
+ // which add or remove form fields while the user is interacting with the
+ // Autofill popup.
+ return;
+ }
+
+ // It's possible that the site has injected fields into the form after the
+ // page has loaded, so we can't assert that the size of the cached control
+ // elements is equal to the size of the fields in |form|. Fortunately, the
+ // one case in the wild where this happens, paypal.com signup form, the fields
+ // are appended to the end of the form and are not visible.
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebFormControlElement* element = &control_elements[i];
+
+ if (string16(element->nameForAutofill()) != data.fields[i].name) {
+ // This case should be reachable only for pathological websites, which
+ // rename form fields while the user is interacting with the Autofill
+ // popup. I (isherman) am not aware of any such websites, and so am
+ // optimistically including a NOTREACHED(). If you ever trip this check,
+ // please file a bug against me.
+ NOTREACHED();
+ continue;
+ }
+
+ bool is_initiating_element = (*element == initiating_element);
+
+ // Only autofill empty fields and the field that initiated the filling,
+ // i.e. the field the user is currently editing and interacting with.
+ const WebInputElement* input_element = toWebInputElement(element);
+ if (!force_override && IsTextInput(input_element) &&
+ !is_initiating_element && !input_element->value().isEmpty())
+ continue;
+
+ if (!element->isEnabled() || element->isReadOnly() ||
+ (only_focusable_elements && !element->isFocusable()))
+ continue;
+
+ callback(data.fields[i], is_initiating_element, element);
+ }
+}
+
+// Sets the |field|'s value to the value in |data|.
+// Also sets the "autofilled" attribute, causing the background to be yellow.
+void FillFormField(const FormFieldData& data,
+ bool is_initiating_node,
+ WebKit::WebFormControlElement* field) {
+ // Nothing to fill.
+ if (data.value.empty())
+ return;
+
+ WebInputElement* input_element = toWebInputElement(field);
+ if (IsTextInput(input_element)) {
+ // If the maxlength attribute contains a negative value, maxLength()
+ // returns the default maxlength value.
+ input_element->setValue(
+ data.value.substr(0, input_element->maxLength()), true);
+ input_element->setAutofilled(true);
+ if (is_initiating_node) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ // Clear the current IME composition (the underline), if there is one.
+ input_element->document().frame()->unmarkText();
+ }
+ } else if (IsSelectElement(*field)) {
+ WebSelectElement select_element = field->to<WebSelectElement>();
+ if (select_element.value() != data.value) {
+ select_element.setValue(data.value);
+ select_element.dispatchFormControlChangeEvent();
+ }
+ } else {
+ DCHECK(IsCheckableElement(input_element));
+ input_element->setChecked(data.is_checked, true);
+ }
+}
+
+// Sets the |field|'s "suggested" (non JS visible) value to the value in |data|.
+// Also sets the "autofilled" attribute, causing the background to be yellow.
+void PreviewFormField(const FormFieldData& data,
+ bool is_initiating_node,
+ WebKit::WebFormControlElement* field) {
+ // Nothing to preview.
+ if (data.value.empty())
+ return;
+
+ // Only preview input fields. Excludes checkboxes and radio buttons, as there
+ // is no provision for setSuggestedCheckedValue in WebInputElement.
+ WebInputElement* input_element = toWebInputElement(field);
+ if (!IsTextInput(input_element))
+ return;
+
+ // If the maxlength attribute contains a negative value, maxLength()
+ // returns the default maxlength value.
+ input_element->setSuggestedValue(
+ data.value.substr(0, input_element->maxLength()));
+ input_element->setAutofilled(true);
+ if (is_initiating_node) {
+ // Select the part of the text that the user didn't type.
+ input_element->setSelectionRange(input_element->value().length(),
+ input_element->suggestedValue().length());
+ }
+}
+
+std::string RetrievalMethodToString(
+ const autofill::WebElementDescriptor::RetrievalMethod& method) {
+ switch (method) {
+ case autofill::WebElementDescriptor::CSS_SELECTOR:
+ return "CSS_SELECTOR";
+ case autofill::WebElementDescriptor::ID:
+ return "ID";
+ case autofill::WebElementDescriptor::NONE:
+ return "NONE";
+ }
+ NOTREACHED();
+ return "UNKNOWN";
+}
+
+} // namespace
+
+namespace autofill {
+
+const size_t kMaxParseableFields = 100;
+
+// In HTML5, all text fields except password are text input fields to
+// autocomplete.
+bool IsTextInput(const WebInputElement* element) {
+ if (!element)
+ return false;
+
+ return element->isTextField() && !element->isPasswordField();
+}
+
+bool IsSelectElement(const WebFormControlElement& element) {
+ // Is static for improving performance.
+ CR_DEFINE_STATIC_LOCAL(WebString, kSelectOne, ("select-one"));
+ return element.formControlType() == kSelectOne;
+}
+
+bool IsCheckableElement(const WebInputElement* element) {
+ if (!element)
+ return false;
+
+ return element->isCheckbox() || element->isRadioButton();
+}
+
+bool IsAutofillableInputElement(const WebInputElement* element) {
+ return IsTextInput(element) || IsCheckableElement(element);
+}
+
+const string16 GetFormIdentifier(const WebFormElement& form) {
+ string16 identifier = form.name();
+ CR_DEFINE_STATIC_LOCAL(WebString, kId, ("id"));
+ if (identifier.empty())
+ identifier = form.getAttribute(kId);
+
+ return identifier;
+}
+
+bool ClickElement(const WebDocument& document,
+ const WebElementDescriptor& element_descriptor) {
+ WebString web_descriptor = WebString::fromUTF8(element_descriptor.descriptor);
+ WebKit::WebElement element;
+
+ switch (element_descriptor.retrieval_method) {
+ case WebElementDescriptor::CSS_SELECTOR: {
+ WebExceptionCode ec = 0;
+ element = document.querySelector(web_descriptor, ec);
+ if (ec)
+ DVLOG(1) << "Query selector failed. Error code: " << ec << ".";
+ break;
+ }
+ case WebElementDescriptor::ID:
+ element = document.getElementById(web_descriptor);
+ break;
+ case WebElementDescriptor::NONE:
+ return true;
+ }
+
+ if (element.isNull()) {
+ DVLOG(1) << "Could not find "
+ << element_descriptor.descriptor
+ << " by "
+ << RetrievalMethodToString(element_descriptor.retrieval_method)
+ << ".";
+ return false;
+ }
+
+ element.simulateClick();
+ return true;
+}
+
+// Fills |autofillable_elements| with all the auto-fillable form control
+// elements in |form_element|.
+void ExtractAutofillableElements(
+ const WebFormElement& form_element,
+ RequirementsMask requirements,
+ std::vector<WebFormControlElement>* autofillable_elements) {
+ WebVector<WebFormControlElement> control_elements;
+ form_element.getFormControlElements(control_elements);
+
+ autofillable_elements->clear();
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebFormControlElement element = control_elements[i];
+ if (!IsAutofillableElement(element))
+ continue;
+
+ if (requirements & REQUIRE_AUTOCOMPLETE) {
+ // TODO(jhawkins): WebKit currently doesn't handle the autocomplete
+ // attribute for select control elements, but it probably should.
+ WebInputElement* input_element = toWebInputElement(&control_elements[i]);
+ if (IsAutofillableInputElement(input_element) &&
+ !SatisfiesRequireAutocomplete(*input_element))
+ continue;
+ }
+
+ autofillable_elements->push_back(element);
+ }
+}
+
+void WebFormControlElementToFormField(const WebFormControlElement& element,
+ ExtractMask extract_mask,
+ FormFieldData* field) {
+ DCHECK(field);
+ DCHECK(!element.isNull());
+ CR_DEFINE_STATIC_LOCAL(WebString, kAutocomplete, ("autocomplete"));
+
+ // The label is not officially part of a WebFormControlElement; however, the
+ // labels for all form control elements are scraped from the DOM and set in
+ // WebFormElementToFormData.
+ field->name = element.nameForAutofill();
+ field->form_control_type = UTF16ToUTF8(element.formControlType());
+ field->autocomplete_attribute =
+ UTF16ToUTF8(element.getAttribute(kAutocomplete));
+ if (field->autocomplete_attribute.size() > kMaxDataLength) {
+ // Discard overly long attribute values to avoid DOS-ing the browser
+ // process. However, send over a default string to indicate that the
+ // attribute was present.
+ field->autocomplete_attribute = "x-max-data-length-exceeded";
+ }
+
+ if (!IsAutofillableElement(element))
+ return;
+
+ const WebInputElement* input_element = toWebInputElement(&element);
+ if (IsAutofillableInputElement(input_element)) {
+ if (IsTextInput(input_element))
+ field->max_length = input_element->maxLength();
+
+ field->is_autofilled = input_element->isAutofilled();
+ field->is_focusable = input_element->isFocusable();
+ field->should_autocomplete = input_element->autoComplete();
+ field->is_checkable = IsCheckableElement(input_element);
+ } else if (extract_mask & EXTRACT_OPTIONS) {
+ // Set option strings on the field if available.
+ DCHECK(IsSelectElement(element));
+ const WebSelectElement select_element = element.toConst<WebSelectElement>();
+ GetOptionStringsFromElement(select_element,
+ &field->option_values,
+ &field->option_contents);
+ }
+
+ if (!(extract_mask & EXTRACT_VALUE))
+ return;
+
+ string16 value;
+ if (IsAutofillableInputElement(input_element)) {
+ value = input_element->value();
+ } else {
+ DCHECK(IsSelectElement(element));
+ const WebSelectElement select_element = element.toConst<WebSelectElement>();
+ value = select_element.value();
+
+ // Convert the |select_element| value to text if requested.
+ if (extract_mask & EXTRACT_OPTION_TEXT) {
+ WebVector<WebElement> list_items = select_element.listItems();
+ for (size_t i = 0; i < list_items.size(); ++i) {
+ if (IsOptionElement(list_items[i])) {
+ const WebOptionElement option_element =
+ list_items[i].toConst<WebOptionElement>();
+ if (option_element.value() == value) {
+ value = option_element.text();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Constrain the maximum data length to prevent a malicious site from DOS'ing
+ // the browser: http://crbug.com/49332
+ if (value.size() > kMaxDataLength)
+ value = value.substr(0, kMaxDataLength);
+
+ field->value = value;
+}
+
+bool WebFormElementToFormData(
+ const WebKit::WebFormElement& form_element,
+ const WebKit::WebFormControlElement& form_control_element,
+ RequirementsMask requirements,
+ ExtractMask extract_mask,
+ FormData* form,
+ FormFieldData* field) {
+ CR_DEFINE_STATIC_LOCAL(WebString, kLabel, ("label"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kFor, ("for"));
+ CR_DEFINE_STATIC_LOCAL(WebString, kHidden, ("hidden"));
+
+ const WebFrame* frame = form_element.document().frame();
+ if (!frame)
+ return false;
+
+ if (requirements & REQUIRE_AUTOCOMPLETE && !form_element.autoComplete())
+ return false;
+
+ form->name = GetFormIdentifier(form_element);
+ form->method = form_element.method();
+ form->origin = frame->document().url();
+ form->action = frame->document().completeURL(form_element.action());
+ form->user_submitted = form_element.wasUserSubmitted();
+
+ // If the completed URL is not valid, just use the action we get from
+ // WebKit.
+ if (!form->action.is_valid())
+ form->action = GURL(form_element.action());
+
+ // A map from a FormFieldData's name to the FormFieldData itself.
+ std::map<string16, FormFieldData*> name_map;
+
+ // The extracted FormFields. We use pointers so we can store them in
+ // |name_map|.
+ ScopedVector<FormFieldData> form_fields;
+
+ WebVector<WebFormControlElement> control_elements;
+ form_element.getFormControlElements(control_elements);
+
+ // A vector of bools that indicate whether each field in the form meets the
+ // requirements and thus will be in the resulting |form|.
+ std::vector<bool> fields_extracted(control_elements.size(), false);
+
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ const WebFormControlElement& control_element = control_elements[i];
+
+ if (!IsAutofillableElement(control_element))
+ continue;
+
+ const WebInputElement* input_element = toWebInputElement(&control_element);
+ if (requirements & REQUIRE_AUTOCOMPLETE &&
+ IsAutofillableInputElement(input_element) &&
+ !SatisfiesRequireAutocomplete(*input_element))
+ continue;
+
+ // Create a new FormFieldData, fill it out and map it to the field's name.
+ FormFieldData* form_field = new FormFieldData;
+ WebFormControlElementToFormField(control_element, extract_mask, form_field);
+ form_fields.push_back(form_field);
+ // TODO(jhawkins): A label element is mapped to a form control element's id.
+ // field->name() will contain the id only if the name does not exist. Add
+ // an id() method to WebFormControlElement and use that here.
+ name_map[form_field->name] = form_field;
+ fields_extracted[i] = true;
+ }
+
+ // If we failed to extract any fields, give up. Also, to avoid overly
+ // expensive computation, we impose a maximum number of allowable fields.
+ if (form_fields.empty() || form_fields.size() > kMaxParseableFields)
+ return false;
+
+ // Loop through the label elements inside the form element. For each label
+ // element, get the corresponding form control element, use the form control
+ // element's name as a key into the <name, FormFieldData> map to find the
+ // previously created FormFieldData and set the FormFieldData's label to the
+ // label.firstChild().nodeValue() of the label element.
+ WebNodeList labels = form_element.getElementsByTagName(kLabel);
+ for (unsigned i = 0; i < labels.length(); ++i) {
+ WebLabelElement label = labels.item(i).to<WebLabelElement>();
+ WebFormControlElement field_element =
+ label.correspondingControl().to<WebFormControlElement>();
+
+ string16 element_name;
+ if (field_element.isNull()) {
+ // Sometimes site authors will incorrectly specify the corresponding
+ // field element's name rather than its id, so we compensate here.
+ element_name = label.getAttribute(kFor);
+ } else if (
+ !field_element.isFormControlElement() ||
+ field_element.formControlType() == kHidden) {
+ continue;
+ } else {
+ element_name = field_element.nameForAutofill();
+ }
+
+ std::map<string16, FormFieldData*>::iterator iter =
+ name_map.find(element_name);
+ if (iter != name_map.end()) {
+ string16 label_text = FindChildText(label);
+
+ // Concatenate labels because some sites might have multiple label
+ // candidates.
+ if (!iter->second->label.empty() && !label_text.empty())
+ iter->second->label += ASCIIToUTF16(" ");
+ iter->second->label += label_text;
+ }
+ }
+
+ // Loop through the form control elements, extracting the label text from
+ // the DOM. We use the |fields_extracted| vector to make sure we assign the
+ // extracted label to the correct field, as it's possible |form_fields| will
+ // not contain all of the elements in |control_elements|.
+ for (size_t i = 0, field_idx = 0;
+ i < control_elements.size() && field_idx < form_fields.size(); ++i) {
+ // This field didn't meet the requirements, so don't try to find a label
+ // for it.
+ if (!fields_extracted[i])
+ continue;
+
+ const WebFormControlElement& control_element = control_elements[i];
+ if (form_fields[field_idx]->label.empty())
+ form_fields[field_idx]->label = InferLabelForElement(control_element);
+
+ if (field && form_control_element == control_element)
+ *field = *form_fields[field_idx];
+
+ ++field_idx;
+ }
+
+ // Copy the created FormFields into the resulting FormData object.
+ for (ScopedVector<FormFieldData>::const_iterator iter = form_fields.begin();
+ iter != form_fields.end(); ++iter) {
+ form->fields.push_back(**iter);
+ }
+
+ return true;
+}
+
+bool FindFormAndFieldForInputElement(const WebInputElement& element,
+ FormData* form,
+ FormFieldData* field,
+ RequirementsMask requirements) {
+ if (!IsAutofillableElement(element))
+ return false;
+
+ const WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return false;
+
+ ExtractMask extract_mask =
+ static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
+ return WebFormElementToFormData(form_element,
+ element,
+ requirements,
+ extract_mask,
+ form,
+ field);
+}
+
+void FillForm(const FormData& form, const WebInputElement& element) {
+ WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return;
+
+ ForEachMatchingFormField(form_element,
+ element,
+ form,
+ true, /* only_focusable_elements */
+ false, /* dont force override */
+ &FillFormField);
+}
+
+void FillFormIncludingNonFocusableElements(const FormData& form_data,
+ const WebFormElement& form_element) {
+ if (form_element.isNull())
+ return;
+
+ ForEachMatchingFormField(form_element,
+ WebInputElement(),
+ form_data,
+ false, /* only_focusable_elements */
+ true, /* force override */
+ &FillFormField);
+}
+
+void PreviewForm(const FormData& form, const WebInputElement& element) {
+ WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return;
+
+ ForEachMatchingFormField(form_element,
+ element,
+ form,
+ true, /* only_focusable_elements */
+ false, /* dont force override */
+ &PreviewFormField);
+}
+
+bool ClearPreviewedFormWithElement(const WebInputElement& element,
+ bool was_autofilled) {
+ WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return false;
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE,
+ &control_elements);
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ // Only text input elements can be previewed.
+ WebInputElement* input_element = toWebInputElement(&control_elements[i]);
+ if (!IsTextInput(input_element))
+ continue;
+
+ // If the input element is not auto-filled, we did not preview it, so there
+ // is nothing to reset.
+ if (!input_element->isAutofilled())
+ continue;
+
+ // There might be unrelated elements in this form which have already been
+ // auto-filled. For example, the user might have already filled the address
+ // part of a form and now be dealing with the credit card section. We only
+ // want to reset the auto-filled status for fields that were previewed.
+ if (input_element->suggestedValue().isEmpty())
+ continue;
+
+ // Clear the suggested value. For the initiating node, also restore the
+ // original value.
+ input_element->setSuggestedValue(WebString());
+ bool is_initiating_node = (element == *input_element);
+ if (is_initiating_node)
+ input_element->setAutofilled(was_autofilled);
+ else
+ input_element->setAutofilled(false);
+
+ // Clearing the suggested value in the focused node (above) can cause
+ // selection to be lost. We force selection range to restore the text
+ // cursor.
+ if (is_initiating_node) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ }
+ }
+
+ return true;
+}
+
+bool FormWithElementIsAutofilled(const WebInputElement& element) {
+ WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return false;
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, REQUIRE_AUTOCOMPLETE,
+ &control_elements);
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebInputElement* input_element = toWebInputElement(&control_elements[i]);
+ if (!IsAutofillableInputElement(input_element))
+ continue;
+
+ if (input_element->isAutofilled())
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace autofill
diff --git a/components/autofill/renderer/form_autofill_util.h b/components/autofill/renderer/form_autofill_util.h
new file mode 100644
index 0000000..a7579ce
--- /dev/null
+++ b/components/autofill/renderer/form_autofill_util.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_RENDERER_FORM_AUTOFILL_UTIL_H_
+#define COMPONENTS_AUTOFILL_RENDERER_FORM_AUTOFILL_UTIL_H_
+
+#include <vector>
+
+#include "base/string16.h"
+
+struct FormData;
+struct FormFieldData;
+
+namespace WebKit {
+class WebDocument;
+class WebFormElement;
+class WebFormControlElement;
+class WebInputElement;
+}
+
+namespace autofill {
+
+struct WebElementDescriptor;
+
+// A bit field mask for form or form element requirements.
+enum RequirementsMask {
+ REQUIRE_NONE = 0, // No requirements.
+ REQUIRE_AUTOCOMPLETE = 1, // Require that autocomplete != off.
+};
+
+// A bit field mask to extract data from WebFormControlElement.
+enum ExtractMask {
+ EXTRACT_NONE = 0,
+ EXTRACT_VALUE = 1 << 0, // Extract value from WebFormControlElement.
+ EXTRACT_OPTION_TEXT = 1 << 1, // Extract option text from
+ // WebFormSelectElement. Only valid when
+ // |EXTRACT_VALUE| is set.
+ // This is used for form submission where
+ // human readable value is captured.
+ EXTRACT_OPTIONS = 1 << 2, // Extract options from
+ // WebFormControlElement.
+};
+
+// The maximum number of form fields we are willing to parse, due to
+// computational costs. Several examples of forms with lots of fields that are
+// not relevant to Autofill: (1) the Netflix queue; (2) the Amazon wishlist;
+// (3) router configuration pages; and (4) other configuration pages, e.g. for
+// Google code project settings.
+extern const size_t kMaxParseableFields;
+
+// Returns true if |element| is a text input element.
+bool IsTextInput(const WebKit::WebInputElement* element);
+
+// Returns true if |element| is a select element.
+bool IsSelectElement(const WebKit::WebFormControlElement& element);
+
+// Returns true if |element| is a checkbox or a radio button element.
+bool IsCheckableElement(const WebKit::WebInputElement* element);
+
+// Returns true if |element| is one of the input element types that can be
+// autofilled. {Text, Radiobutton, Checkbox}.
+bool IsAutofillableInputElement(const WebKit::WebInputElement* element);
+
+// Returns the form's |name| attribute if non-empty; otherwise the form's |id|
+// attribute.
+const string16 GetFormIdentifier(const WebKit::WebFormElement& form);
+
+// Returns true if the element specified by |click_element| was successfully
+// clicked.
+bool ClickElement(const WebKit::WebDocument& document,
+ const WebElementDescriptor& element_descriptor);
+
+// Fills |autofillable_elements| with all the auto-fillable form control
+// elements in |form_element|.
+void ExtractAutofillableElements(
+ const WebKit::WebFormElement& form_element,
+ RequirementsMask requirements,
+ std::vector<WebKit::WebFormControlElement>* autofillable_elements);
+
+// Fills out a FormField object from a given WebFormControlElement.
+// |extract_mask|: See the enum ExtractMask above for details.
+void WebFormControlElementToFormField(
+ const WebKit::WebFormControlElement& element,
+ ExtractMask extract_mask,
+ FormFieldData* field);
+
+// Fills |form| with the FormData object corresponding to the |form_element|.
+// If |field| is non-NULL, also fills |field| with the FormField object
+// corresponding to the |form_control_element|.
+// |extract_mask| controls what data is extracted.
+// Returns true if |form| is filled out; it's possible that the |form_element|
+// won't meet the |requirements|. Also returns false if there are no fields or
+// too many fields in the |form|.
+bool WebFormElementToFormData(
+ const WebKit::WebFormElement& form_element,
+ const WebKit::WebFormControlElement& form_control_element,
+ RequirementsMask requirements,
+ ExtractMask extract_mask,
+ FormData* form,
+ FormFieldData* field);
+
+// Finds the form that contains |element| and returns it in |form|. Fills
+// |field| with the |FormField| representation for element.
+// Returns false if the form is not found or cannot be serialized.
+bool FindFormAndFieldForInputElement(const WebKit::WebInputElement& element,
+ FormData* form,
+ FormFieldData* field,
+ RequirementsMask requirements);
+
+// Fills the form represented by |form|. |element| is the input element that
+// initiated the auto-fill process.
+void FillForm(const FormData& form,
+ const WebKit::WebInputElement& element);
+
+// Fills focusable and non-focusable form control elements within |form_element|
+// with field data from |form_data|.
+void FillFormIncludingNonFocusableElements(
+ const FormData& form_data,
+ const WebKit::WebFormElement& form_element);
+
+// Previews the form represented by |form|. |element| is the input element that
+// initiated the preview process.
+void PreviewForm(const FormData& form,
+ const WebKit::WebInputElement& element);
+
+// Clears the placeholder values and the auto-filled background for any fields
+// in the form containing |node| that have been previewed. Resets the
+// autofilled state of |node| to |was_autofilled|. Returns false if the form is
+// not found.
+bool ClearPreviewedFormWithElement(const WebKit::WebInputElement& element,
+ bool was_autofilled);
+
+// Returns true if |form| has any auto-filled fields.
+bool FormWithElementIsAutofilled(const WebKit::WebInputElement& element);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_FORM_AUTOFILL_UTIL_H_
diff --git a/components/autofill/renderer/form_cache.cc b/components/autofill/renderer/form_cache.cc
new file mode 100644
index 0000000..ab92f46
--- /dev/null
+++ b/components/autofill/renderer/form_cache.cc
@@ -0,0 +1,295 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/form_cache.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "components/autofill/common/form_data.h"
+#include "components/autofill/common/form_data_predictions.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/form_field_data_predictions.h"
+#include "components/autofill/renderer/form_autofill_util.h"
+#include "grit/generated_resources.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSelectElement.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using WebKit::WebDocument;
+using WebKit::WebFormControlElement;
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebSelectElement;
+using WebKit::WebString;
+using WebKit::WebVector;
+
+namespace {
+
+// The number of fields required by Autofill. Ideally we could send the forms
+// to Autofill no matter how many fields are in the forms; however, finding the
+// label for each field is a costly operation and we can't spare the cycles if
+// it's not necessary.
+const size_t kRequiredAutofillFields = 3;
+
+} // namespace
+
+namespace autofill {
+
+// Helper function to discard state of various WebFormElements when they go out
+// of web frame's scope. This is done to release memory that we no longer need
+// to hold.
+// K should inherit from WebFormControlElement as the function looks to extract
+// WebFormElement for K.form().
+template <class K, class V>
+void RemoveOldElements(const WebFrame& frame, std::map<const K, V>* states) {
+ std::vector<K> to_remove;
+ for (typename std::map<const K, V>::const_iterator it = states->begin();
+ it != states->end(); ++it) {
+ WebFormElement form_element = it->first.form();
+ if (form_element.isNull()) {
+ to_remove.push_back(it->first);
+ } else {
+ const WebFrame* element_frame = form_element.document().frame();
+ if (!element_frame || element_frame == &frame)
+ to_remove.push_back(it->first);
+ }
+ }
+
+ for (typename std::vector<K>::const_iterator it = to_remove.begin();
+ it != to_remove.end(); ++it) {
+ states->erase(*it);
+ }
+}
+
+FormCache::FormCache() {
+}
+
+FormCache::~FormCache() {
+}
+
+void FormCache::ExtractForms(const WebFrame& frame,
+ std::vector<FormData>* forms) {
+ ExtractFormsAndFormElements(frame, forms, NULL);
+}
+
+void FormCache::ExtractFormsAndFormElements(
+ const WebFrame& frame,
+ std::vector<FormData>* forms,
+ std::vector<WebFormElement>* web_form_elements) {
+ // Reset the cache for this frame.
+ ResetFrame(frame);
+
+ WebDocument document = frame.document();
+ if (document.isNull())
+ return;
+
+ web_documents_.insert(document);
+
+ WebVector<WebFormElement> web_forms;
+ document.forms(web_forms);
+
+ size_t num_fields_seen = 0;
+ for (size_t i = 0; i < web_forms.size(); ++i) {
+ WebFormElement form_element = web_forms[i];
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
+ &control_elements);
+
+ size_t num_editable_elements = 0;
+ for (size_t j = 0; j < control_elements.size(); ++j) {
+ WebFormControlElement element = control_elements[j];
+
+ // Save original values of <select> elements so we can restore them
+ // when |ClearFormWithNode()| is invoked.
+ if (IsSelectElement(element)) {
+ const WebSelectElement select_element =
+ element.toConst<WebSelectElement>();
+ initial_select_values_.insert(std::make_pair(select_element,
+ select_element.value()));
+ ++num_editable_elements;
+ } else {
+ const WebInputElement input_element =
+ element.toConst<WebInputElement>();
+ if (IsCheckableElement(&input_element)) {
+ initial_checked_state_.insert(
+ std::make_pair(input_element, input_element.isChecked()));
+ } else {
+ ++num_editable_elements;
+ }
+ }
+ }
+
+ // To avoid overly expensive computation, we impose a minimum number of
+ // allowable fields. The corresponding maximum number of allowable fields
+ // is imposed by WebFormElementToFormData().
+ if (num_editable_elements < kRequiredAutofillFields)
+ continue;
+
+ FormData form;
+ ExtractMask extract_mask =
+ static_cast<ExtractMask>(EXTRACT_VALUE | EXTRACT_OPTIONS);
+
+ if (!WebFormElementToFormData(form_element, WebFormControlElement(),
+ REQUIRE_NONE, extract_mask, &form, NULL)) {
+ continue;
+ }
+
+ num_fields_seen += form.fields.size();
+ if (num_fields_seen > kMaxParseableFields)
+ break;
+
+ if (form.fields.size() >= kRequiredAutofillFields) {
+ forms->push_back(form);
+ if (web_form_elements)
+ web_form_elements->push_back(form_element);
+ }
+ }
+}
+
+void FormCache::ResetFrame(const WebFrame& frame) {
+ std::vector<WebDocument> documents_to_delete;
+ for (std::set<WebDocument>::const_iterator it = web_documents_.begin();
+ it != web_documents_.end(); ++it) {
+ const WebFrame* document_frame = it->frame();
+ if (!document_frame || document_frame == &frame)
+ documents_to_delete.push_back(*it);
+ }
+
+ for (std::vector<WebDocument>::const_iterator it =
+ documents_to_delete.begin();
+ it != documents_to_delete.end(); ++it) {
+ web_documents_.erase(*it);
+ }
+
+ RemoveOldElements(frame, &initial_select_values_);
+ RemoveOldElements(frame, &initial_checked_state_);
+}
+
+bool FormCache::ClearFormWithElement(const WebInputElement& element) {
+ WebFormElement form_element = element.form();
+ if (form_element.isNull())
+ return false;
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
+ &control_elements);
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebFormControlElement control_element = control_elements[i];
+ WebInputElement* input_element = toWebInputElement(&control_element);
+ if (IsTextInput(input_element)) {
+ // We don't modify the value of disabled fields.
+ if (!input_element->isEnabled())
+ continue;
+
+ input_element->setValue(string16(), true);
+ input_element->setAutofilled(false);
+
+ // Clearing the value in the focused node (above) can cause selection
+ // to be lost. We force selection range to restore the text cursor.
+ if (element == *input_element) {
+ int length = input_element->value().length();
+ input_element->setSelectionRange(length, length);
+ }
+ } else if (IsSelectElement(control_element)) {
+ WebSelectElement select_element = control_element.to<WebSelectElement>();
+
+ std::map<const WebSelectElement, string16>::const_iterator
+ initial_value_iter = initial_select_values_.find(select_element);
+ if (initial_value_iter != initial_select_values_.end() &&
+ select_element.value() != initial_value_iter->second) {
+ select_element.setValue(initial_value_iter->second);
+ select_element.dispatchFormControlChangeEvent();
+ }
+ } else {
+ WebInputElement input_element = control_element.to<WebInputElement>();
+ DCHECK(IsCheckableElement(&input_element));
+ std::map<const WebInputElement, bool>::const_iterator it =
+ initial_checked_state_.find(input_element);
+ if (it != initial_checked_state_.end() &&
+ input_element.isChecked() != it->second) {
+ input_element.setChecked(it->second, true);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool FormCache::ShowPredictions(const FormDataPredictions& form) {
+ DCHECK_EQ(form.data.fields.size(), form.fields.size());
+
+ // Find the form.
+ bool found_form = false;
+ WebFormElement form_element;
+ for (std::set<WebDocument>::const_iterator it = web_documents_.begin();
+ it != web_documents_.end() && !found_form; ++it) {
+ WebVector<WebFormElement> web_forms;
+ it->forms(web_forms);
+
+ for (size_t i = 0; i < web_forms.size(); ++i) {
+ form_element = web_forms[i];
+
+ // Note: matching on the form name here which is not guaranteed to be
+ // unique for the page, nor is it guaranteed to be non-empty. Ideally, we
+ // would have a way to uniquely identify the form cross-process. For now,
+ // we'll check form name and form action for identity.
+ // Also note that WebString() == WebString(string16()) does not evaluate
+ // to |true| -- WebKit distinguishes between a "null" string (lhs) and an
+ // "empty" string (rhs). We don't want that distinction, so forcing to
+ // string16.
+ string16 element_name = GetFormIdentifier(form_element);
+ GURL action(form_element.document().completeURL(form_element.action()));
+ if (element_name == form.data.name && action == form.data.action) {
+ found_form = true;
+ break;
+ }
+ }
+ }
+
+ if (!found_form)
+ return false;
+
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, autofill::REQUIRE_NONE,
+ &control_elements);
+ if (control_elements.size() != form.fields.size()) {
+ // Keep things simple. Don't show predictions for forms that were modified
+ // between page load and the server's response to our query.
+ return false;
+ }
+
+ for (size_t i = 0; i < control_elements.size(); ++i) {
+ WebFormControlElement* element = &control_elements[i];
+
+ if (string16(element->nameForAutofill()) != form.data.fields[i].name) {
+ // Keep things simple. Don't show predictions for elements whose names
+ // were modified between page load and the server's response to our query.
+ continue;
+ }
+
+ std::string placeholder = form.fields[i].overall_type;
+ string16 title = l10n_util::GetStringFUTF16(
+ IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE,
+ UTF8ToUTF16(form.fields[i].heuristic_type),
+ UTF8ToUTF16(form.fields[i].server_type),
+ UTF8ToUTF16(form.fields[i].signature),
+ UTF8ToUTF16(form.signature),
+ UTF8ToUTF16(form.experiment_id));
+ if (!element->hasAttribute("placeholder"))
+ element->setAttribute("placeholder", WebString(UTF8ToUTF16(placeholder)));
+ element->setAttribute("title", WebString(title));
+ }
+
+ return true;
+}
+
+} // namespace autofill
diff --git a/components/autofill/renderer/form_cache.h b/components/autofill/renderer/form_cache.h
new file mode 100644
index 0000000..eb0ea3a
--- /dev/null
+++ b/components/autofill/renderer/form_cache.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 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_RENDERER_FORM_CACHE_H_
+#define COMPONENTS_AUTOFILL_RENDERER_FORM_CACHE_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/string16.h"
+
+struct FormData;
+struct FormDataPredictions;
+
+namespace WebKit {
+class WebDocument;
+class WebFormElement;
+class WebFrame;
+class WebInputElement;
+class WebSelectElement;
+}
+
+namespace autofill {
+
+// Manages the forms in a RenderView.
+class FormCache {
+ public:
+ FormCache();
+ ~FormCache();
+
+ // Scans the DOM in |frame| extracting and storing forms.
+ // Returns a vector of the extracted forms.
+ void ExtractForms(const WebKit::WebFrame& frame,
+ std::vector<FormData>* forms);
+
+ // Scans the DOM in |frame| extracting and storing forms.
+ // Returns a vector of the extracted forms and vector of associated web
+ // form elements.
+ void ExtractFormsAndFormElements(
+ const WebKit::WebFrame& frame,
+ std::vector<FormData>* forms,
+ std::vector<WebKit::WebFormElement>* web_form_elements);
+
+ // Resets the forms for the specified |frame|.
+ void ResetFrame(const WebKit::WebFrame& frame);
+
+ // Clears the values of all input elements in the form that contains
+ // |element|. Returns false if the form is not found.
+ bool ClearFormWithElement(const WebKit::WebInputElement& element);
+
+ // For each field in the |form|, sets the field's placeholder text to the
+ // field's overall predicted type. Also sets the title to include the field's
+ // heuristic type, server type, and signature; as well as the form's signature
+ // and the experiment id for the server predictions.
+ bool ShowPredictions(const FormDataPredictions& form);
+
+ private:
+ // The cached web frames.
+ std::set<WebKit::WebDocument> web_documents_;
+
+ // The cached initial values for <select> elements.
+ std::map<const WebKit::WebSelectElement, string16> initial_select_values_;
+
+ // The cached initial values for checkable <input> elements.
+ std::map<const WebKit::WebInputElement, bool> initial_checked_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormCache);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_FORM_CACHE_H_
diff --git a/components/autofill/renderer/page_click_listener.h b/components/autofill/renderer/page_click_listener.h
new file mode 100644
index 0000000..83c41d4
--- /dev/null
+++ b/components/autofill/renderer/page_click_listener.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 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_RENDERER_PAGE_CLICK_LISTENER_H_
+#define COMPONENTS_AUTOFILL_RENDERER_PAGE_CLICK_LISTENER_H_
+
+namespace WebKit {
+class WebInputElement;
+}
+
+// Interface that should be implemented by classes interested in getting
+// notifications for clicks on a page.
+// Register on the PageListenerTracker object.
+class PageClickListener {
+ public:
+ // Notification that |element| was clicked.
+ // |was_focused| is true if |element| had focus BEFORE the click.
+ // |is_focused| is true if |element| has focus AFTER the click was processed.
+ // If this method returns true, the notification will not be propagated to
+ // other listeners.
+ virtual bool InputElementClicked(const WebKit::WebInputElement& element,
+ bool was_focused,
+ bool is_focused) = 0;
+
+ // If the previously focused element was an input field, listeners are
+ // informed that the text field has lost its focus.
+ // If this method returns true, the notification will not be propagated to
+ // other listeners.
+ virtual bool InputElementLostFocus() = 0;
+
+ protected:
+ virtual ~PageClickListener() {}
+};
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_PAGE_CLICK_LISTENER_H_
diff --git a/components/autofill/renderer/page_click_tracker.cc b/components/autofill/renderer/page_click_tracker.cc
new file mode 100644
index 0000000..21e28a2
--- /dev/null
+++ b/components/autofill/renderer/page_click_tracker.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/page_click_tracker.h"
+
+#include "components/autofill/renderer/form_autofill_util.h"
+#include "components/autofill/renderer/page_click_listener.h"
+#include "content/public/renderer/render_view.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMMouseEvent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+
+using WebKit::WebDOMEvent;
+using WebKit::WebDOMMouseEvent;
+using WebKit::WebElement;
+using WebKit::WebFormControlElement;
+using WebKit::WebFrame;
+using WebKit::WebInputElement;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebNode;
+using WebKit::WebString;
+using WebKit::WebView;
+
+namespace {
+
+// Casts |node| to a WebInputElement.
+// Returns an empty (isNull()) WebInputElement if |node| is not a text field.
+const WebInputElement GetTextWebInputElement(const WebNode& node) {
+ if (!node.isElementNode())
+ return WebInputElement();
+ const WebElement element = node.toConst<WebElement>();
+ if (!element.hasTagName("input"))
+ return WebInputElement();
+ const WebInputElement* input = WebKit::toWebInputElement(&element);
+ if (!autofill::IsTextInput(input))
+ return WebInputElement();
+ return *input;
+}
+
+// Checks to see if a text field was the previously selected node and is now
+// losing its focus.
+bool DidSelectedTextFieldLoseFocus(const WebNode& newly_clicked_node) {
+ WebKit::WebNode focused_node = newly_clicked_node.document().focusedNode();
+
+ if (focused_node.isNull() || GetTextWebInputElement(focused_node).isNull())
+ return false;
+
+ return focused_node != newly_clicked_node;
+}
+
+} // namespace
+
+PageClickTracker::PageClickTracker(content::RenderView* render_view)
+ : content::RenderViewObserver(render_view),
+ was_focused_(false) {
+}
+
+PageClickTracker::~PageClickTracker() {
+ // Note that even though RenderView calls FrameDetached when notified that
+ // a frame was closed, it might not always get that notification from WebKit
+ // for all frames.
+ // By the time we get here, the frame could have been destroyed so we cannot
+ // unregister listeners in frames remaining in tracked_frames_ as they might
+ // be invalid.
+}
+
+void PageClickTracker::DidHandleMouseEvent(const WebMouseEvent& event) {
+ if (event.type != WebInputEvent::MouseDown ||
+ last_node_clicked_.isNull()) {
+ return;
+ }
+
+ // We are only interested in text field clicks.
+ const WebInputElement input_element =
+ GetTextWebInputElement(last_node_clicked_);
+ if (input_element.isNull())
+ return;
+
+ bool is_focused = (last_node_clicked_ == render_view()->GetFocusedNode());
+ ObserverListBase<PageClickListener>::Iterator it(listeners_);
+ PageClickListener* listener;
+ while ((listener = it.GetNext()) != NULL) {
+ if (listener->InputElementClicked(input_element, was_focused_, is_focused))
+ break;
+ }
+}
+
+void PageClickTracker::AddListener(PageClickListener* listener) {
+ listeners_.AddObserver(listener);
+}
+
+void PageClickTracker::RemoveListener(PageClickListener* listener) {
+ listeners_.RemoveObserver(listener);
+}
+
+void PageClickTracker::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
+ tracked_frames_.push_back(frame);
+ frame->document().addEventListener("mousedown", this, false);
+}
+
+void PageClickTracker::FrameDetached(WebKit::WebFrame* frame) {
+ FrameList::iterator iter =
+ std::find(tracked_frames_.begin(), tracked_frames_.end(), frame);
+ if (iter == tracked_frames_.end()) {
+ // Some frames might never load contents so we may not have a listener on
+ // them. Calling removeEventListener() on them would trigger an assert, so
+ // we need to keep track of which frames we are listening to.
+ return;
+ }
+ tracked_frames_.erase(iter);
+}
+
+void PageClickTracker::handleEvent(const WebDOMEvent& event) {
+ last_node_clicked_.reset();
+
+ if (!event.isMouseEvent())
+ return;
+
+ const WebDOMMouseEvent mouse_event = event.toConst<WebDOMMouseEvent>();
+ DCHECK(mouse_event.buttonDown());
+ if (mouse_event.button() != 0)
+ return; // We are only interested in left clicks.
+
+ // Remember which node has focus before the click is processed.
+ // We'll get a notification once the mouse event has been processed
+ // (DidHandleMouseEvent), we'll notify the listener at that point.
+ WebNode node = mouse_event.target();
+ if (node.isNull())
+ // Node may be null if the target was an SVG instance element from a <use>
+ // tree and the tree has been rebuilt due to an earlier event.
+ return;
+
+ HandleTextFieldMaybeLosingFocus(node);
+
+ // We are only interested in text field clicks.
+ if (GetTextWebInputElement(node).isNull())
+ return;
+
+ last_node_clicked_ = node;
+ was_focused_ = (node.document().focusedNode() == last_node_clicked_);
+}
+
+void PageClickTracker::HandleTextFieldMaybeLosingFocus(
+ const WebNode& newly_clicked_node) {
+ if (!DidSelectedTextFieldLoseFocus(newly_clicked_node))
+ return;
+
+ ObserverListBase<PageClickListener>::Iterator it(listeners_);
+ PageClickListener* listener;
+ while ((listener = it.GetNext()) != NULL) {
+ if (listener->InputElementLostFocus())
+ break;
+ }
+}
diff --git a/components/autofill/renderer/page_click_tracker.h b/components/autofill/renderer/page_click_tracker.h
new file mode 100644
index 0000000..e4aedb2
--- /dev/null
+++ b/components/autofill/renderer/page_click_tracker.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 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_RENDERER_PAGE_CLICK_TRACKER_H_
+#define COMPONENTS_AUTOFILL_RENDERER_PAGE_CLICK_TRACKER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDOMEventListener.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
+
+class PageClickListener;
+
+
+// This class is responsible for tracking clicks on elements in web pages and
+// notifiying the associated listener when a node is clicked.
+// Compared to a simple WebDOMEventListener, it offers the added capability of
+// notifying the listeners of whether the clicked node was already focused
+// before it was clicked.
+//
+// This is useful for password/form autofill where we want to trigger a
+// suggestion popup when a text input is clicked.
+// It only notifies of WebInputElement that are text inputs being clicked, but
+// could easily be changed to report click on any type of WebNode.
+//
+// There is one PageClickTracker per RenderView.
+class PageClickTracker : public content::RenderViewObserver,
+ public WebKit::WebDOMEventListener {
+ public:
+ explicit PageClickTracker(content::RenderView* render_view);
+ virtual ~PageClickTracker();
+
+ // Adds/removes a listener for getting notification when an element is
+ // clicked. Note that the order of insertion is important as a listener when
+ // notified can decide to stop the propagation of the event (so that listeners
+ // inserted after don't get the notification).
+ void AddListener(PageClickListener* listener);
+ void RemoveListener(PageClickListener* listener);
+
+ private:
+ // RenderView::Observer implementation.
+ virtual void DidFinishDocumentLoad(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void FrameDetached(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event) OVERRIDE;
+
+ // WebKit::WebDOMEventListener implementation.
+ virtual void handleEvent(const WebKit::WebDOMEvent& event);
+
+ // Checks to see if a text field is losing focus and inform listeners if
+ // it is.
+ void HandleTextFieldMaybeLosingFocus(
+ const WebKit::WebNode& newly_clicked_node);
+
+ // The last node that was clicked and had focus.
+ WebKit::WebNode last_node_clicked_;
+
+ // Whether the last clicked node had focused before it was clicked.
+ bool was_focused_;
+
+ // The frames we are listening to for mouse events.
+ typedef std::vector<WebKit::WebFrame*> FrameList;
+ FrameList tracked_frames_;
+
+ // The listener getting the actual notifications.
+ ObserverList<PageClickListener> listeners_;
+
+ DISALLOW_COPY_AND_ASSIGN(PageClickTracker);
+};
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_PAGE_CLICK_TRACKER_H_
diff --git a/components/autofill/renderer/password_autofill_manager.cc b/components/autofill/renderer/password_autofill_manager.cc
new file mode 100644
index 0000000..1ab3d59
--- /dev/null
+++ b/components/autofill/renderer/password_autofill_manager.cc
@@ -0,0 +1,649 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/password_autofill_manager.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "components/autofill/common/form_field_data.h"
+#include "components/autofill/common/password_form_fill_data.h"
+#include "components/autofill/renderer/form_autofill_util.h"
+#include "content/public/common/password_form.h"
+#include "content/public/renderer/password_form_conversion_utils.h"
+#include "content/public/renderer/render_view.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebAutofillClient.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+namespace {
+
+// The size above which we stop triggering autocomplete.
+static const size_t kMaximumTextSizeForAutocomplete = 1000;
+
+// Maps element names to the actual elements to simplify form filling.
+typedef std::map<string16, WebKit::WebInputElement>
+ FormInputElementMap;
+
+// Utility struct for form lookup and autofill. When we parse the DOM to look up
+// a form, in addition to action and origin URL's we have to compare all
+// necessary form elements. To avoid having to look these up again when we want
+// to fill the form, the FindFormElements function stores the pointers
+// in a FormElements* result, referenced to ensure they are safe to use.
+struct FormElements {
+ WebKit::WebFormElement form_element;
+ FormInputElementMap input_elements;
+};
+
+typedef std::vector<FormElements*> FormElementsList;
+
+// Helper to search the given form element for the specified input elements
+// in |data|, and add results to |result|.
+static bool FindFormInputElements(WebKit::WebFormElement* fe,
+ const FormData& data,
+ FormElements* result) {
+ // Loop through the list of elements we need to find on the form in order to
+ // autofill it. If we don't find any one of them, abort processing this
+ // form; it can't be the right one.
+ for (size_t j = 0; j < data.fields.size(); j++) {
+ WebKit::WebVector<WebKit::WebNode> temp_elements;
+ fe->getNamedElements(data.fields[j].name, temp_elements);
+
+ // Match the first input element, if any.
+ // |getNamedElements| may return non-input elements where the names match,
+ // so the results are filtered for input elements.
+ // If more than one match is made, then we have ambiguity (due to misuse
+ // of "name" attribute) so is it considered not found.
+ bool found_input = false;
+ for (size_t i = 0; i < temp_elements.size(); ++i) {
+ if (temp_elements[i].to<WebKit::WebElement>().hasTagName("input")) {
+ // Check for a non-unique match.
+ if (found_input) {
+ found_input = false;
+ break;
+ }
+
+ // This element matched, add it to our temporary result. It's possible
+ // there are multiple matches, but for purposes of identifying the form
+ // one suffices and if some function needs to deal with multiple
+ // matching elements it can get at them through the FormElement*.
+ // Note: This assignment adds a reference to the InputElement.
+ result->input_elements[data.fields[j].name] =
+ temp_elements[i].to<WebKit::WebInputElement>();
+ found_input = true;
+ }
+ }
+
+ // A required element was not found. This is not the right form.
+ // Make sure no input elements from a partially matched form in this
+ // iteration remain in the result set.
+ // Note: clear will remove a reference from each InputElement.
+ if (!found_input) {
+ result->input_elements.clear();
+ return false;
+ }
+ }
+ return true;
+}
+
+// Helper to locate form elements identified by |data|.
+void FindFormElements(WebKit::WebView* view,
+ const FormData& data,
+ FormElementsList* results) {
+ DCHECK(view);
+ DCHECK(results);
+ WebKit::WebFrame* main_frame = view->mainFrame();
+ if (!main_frame)
+ return;
+
+ GURL::Replacements rep;
+ rep.ClearQuery();
+ rep.ClearRef();
+
+ // Loop through each frame.
+ for (WebKit::WebFrame* f = main_frame; f; f = f->traverseNext(false)) {
+ WebKit::WebDocument doc = f->document();
+ if (!doc.isHTMLDocument())
+ continue;
+
+ GURL full_origin(doc.url());
+ if (data.origin != full_origin.ReplaceComponents(rep))
+ continue;
+
+ WebKit::WebVector<WebKit::WebFormElement> forms;
+ doc.forms(forms);
+
+ for (size_t i = 0; i < forms.size(); ++i) {
+ WebKit::WebFormElement fe = forms[i];
+
+ GURL full_action(f->document().completeURL(fe.action()));
+ if (full_action.is_empty()) {
+ // The default action URL is the form's origin.
+ full_action = full_origin;
+ }
+
+ // Action URL must match.
+ if (data.action != full_action.ReplaceComponents(rep))
+ continue;
+
+ scoped_ptr<FormElements> curr_elements(new FormElements);
+ if (!FindFormInputElements(&fe, data, curr_elements.get()))
+ continue;
+
+ // We found the right element.
+ // Note: this assignment adds a reference to |fe|.
+ curr_elements->form_element = fe;
+ results->push_back(curr_elements.release());
+ }
+ }
+}
+
+bool IsElementEditable(const WebKit::WebInputElement& element) {
+ return element.isEnabled() && !element.isReadOnly();
+}
+
+void FillForm(FormElements* fe, const FormData& data) {
+ if (!fe->form_element.autoComplete())
+ return;
+
+ std::map<string16, string16> data_map;
+ for (size_t i = 0; i < data.fields.size(); i++)
+ data_map[data.fields[i].name] = data.fields[i].value;
+
+ for (FormInputElementMap::iterator it = fe->input_elements.begin();
+ it != fe->input_elements.end(); ++it) {
+ WebKit::WebInputElement element = it->second;
+ // Don't fill a form that has pre-filled values distinct from the ones we
+ // want to fill with.
+ if (!element.value().isEmpty() && element.value() != data_map[it->first])
+ return;
+ }
+
+ for (FormInputElementMap::iterator it = fe->input_elements.begin();
+ it != fe->input_elements.end(); ++it) {
+ WebKit::WebInputElement element = it->second;
+ if (!IsElementEditable(element))
+ continue; // Don't fill uneditable fields.
+
+ // TODO(tkent): Check maxlength and pattern.
+ element.setValue(data_map[it->first]);
+ element.setAutofilled(true);
+ element.dispatchFormControlChangeEvent();
+ }
+}
+
+void SetElementAutofilled(WebKit::WebInputElement* element, bool autofilled) {
+ if (element->isAutofilled() == autofilled)
+ return;
+ element->setAutofilled(autofilled);
+ // Notify any changeEvent listeners.
+ element->dispatchFormControlChangeEvent();
+}
+
+bool DoUsernamesMatch(const string16& username1,
+ const string16& username2,
+ bool exact_match) {
+ if (exact_match)
+ return username1 == username2;
+ return StartsWith(username1, username2, true);
+}
+
+} // namespace
+
+namespace autofill {
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillManager, public:
+
+PasswordAutofillManager::PasswordAutofillManager(
+ content::RenderView* render_view)
+ : content::RenderViewObserver(render_view),
+ disable_popup_(false),
+ web_view_(render_view->GetWebView()),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
+}
+
+PasswordAutofillManager::~PasswordAutofillManager() {
+}
+
+bool PasswordAutofillManager::TextFieldDidEndEditing(
+ const WebKit::WebInputElement& element) {
+ LoginToPasswordInfoMap::const_iterator iter =
+ login_to_password_info_.find(element);
+ if (iter == login_to_password_info_.end())
+ return false;
+
+ const PasswordFormFillData& fill_data =
+ iter->second.fill_data;
+
+ // If wait_for_username is false, we should have filled when the text changed.
+ if (!fill_data.wait_for_username)
+ return false;
+
+ WebKit::WebInputElement password = iter->second.password_field;
+ if (!IsElementEditable(password))
+ return false;
+
+ WebKit::WebInputElement username = element; // We need a non-const.
+
+ // Do not set selection when ending an editing session, otherwise it can
+ // mess with focus.
+ FillUserNameAndPassword(&username, &password, fill_data, true, false);
+ return true;
+}
+
+bool PasswordAutofillManager::TextDidChangeInTextField(
+ const WebKit::WebInputElement& element) {
+ LoginToPasswordInfoMap::const_iterator iter =
+ login_to_password_info_.find(element);
+ if (iter == login_to_password_info_.end())
+ return false;
+
+ // The input text is being changed, so any autofilled password is now
+ // outdated.
+ WebKit::WebInputElement username = element; // We need a non-const.
+ WebKit::WebInputElement password = iter->second.password_field;
+ SetElementAutofilled(&username, false);
+ if (password.isAutofilled()) {
+ password.setValue(string16());
+ SetElementAutofilled(&password, false);
+ }
+
+ // If wait_for_username is true we will fill when the username loses focus.
+ if (iter->second.fill_data.wait_for_username)
+ return false;
+
+ if (!IsElementEditable(element) ||
+ !element.isText() ||
+ !element.autoComplete()) {
+ return false;
+ }
+
+ // Don't inline autocomplete if the user is deleting, that would be confusing.
+ // But refresh the popup. Note, since this is ours, return true to signal
+ // no further processing is required.
+ if (iter->second.backspace_pressed_last) {
+ ShowSuggestionPopup(iter->second.fill_data, username);
+ return true;
+ }
+
+ WebKit::WebString name = element.nameForAutofill();
+ if (name.isEmpty())
+ return false; // If the field has no name, then we won't have values.
+
+ // Don't attempt to autofill with values that are too large.
+ if (element.value().length() > kMaximumTextSizeForAutocomplete)
+ return false;
+
+ // The caret position should have already been updated.
+ PerformInlineAutocomplete(element, password, iter->second.fill_data);
+ return true;
+}
+
+bool PasswordAutofillManager::TextFieldHandlingKeyDown(
+ const WebKit::WebInputElement& element,
+ const WebKit::WebKeyboardEvent& event) {
+ // If using the new Autofill UI that lives in the browser, it will handle
+ // keypresses before this function. This is not currently an issue but if
+ // the keys handled there or here change, this issue may appear.
+
+ LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element);
+ if (iter == login_to_password_info_.end())
+ return false;
+
+ int win_key_code = event.windowsKeyCode;
+ iter->second.backspace_pressed_last =
+ (win_key_code == ui::VKEY_BACK || win_key_code == ui::VKEY_DELETE);
+ return true;
+}
+
+bool PasswordAutofillManager::DidAcceptAutofillSuggestion(
+ const WebKit::WebNode& node,
+ const WebKit::WebString& value) {
+ WebKit::WebInputElement input;
+ PasswordInfo password;
+ if (!FindLoginInfo(node, &input, &password))
+ return false;
+
+ // Set the incoming |value| in the text field and |FillUserNameAndPassword|
+ // will do the rest.
+ input.setValue(value, true);
+ return FillUserNameAndPassword(&input, &password.password_field,
+ password.fill_data, true, true);
+}
+
+bool PasswordAutofillManager::DidSelectAutofillSuggestion(
+ const WebKit::WebNode& node) {
+ WebKit::WebInputElement input;
+ PasswordInfo password;
+ return FindLoginInfo(node, &input, &password);
+}
+
+bool PasswordAutofillManager::DidClearAutofillSelection(
+ const WebKit::WebNode& node) {
+ WebKit::WebInputElement input;
+ PasswordInfo password;
+ return FindLoginInfo(node, &input, &password);
+}
+
+void PasswordAutofillManager::SendPasswordForms(WebKit::WebFrame* frame,
+ bool only_visible) {
+ // Make sure that this security origin is allowed to use password manager.
+ WebKit::WebSecurityOrigin origin = frame->document().securityOrigin();
+ if (!origin.canAccessPasswordManager())
+ return;
+
+ WebKit::WebVector<WebKit::WebFormElement> forms;
+ frame->document().forms(forms);
+
+ std::vector<content::PasswordForm> password_forms;
+ for (size_t i = 0; i < forms.size(); ++i) {
+ const WebKit::WebFormElement& form = forms[i];
+
+ // If requested, ignore non-rendered forms, e.g. those styled with
+ // display:none.
+ if (only_visible && !form.hasNonEmptyBoundingBox())
+ continue;
+
+ scoped_ptr<content::PasswordForm> password_form(
+ content::CreatePasswordForm(form));
+ if (password_form.get())
+ password_forms.push_back(*password_form);
+ }
+
+ if (password_forms.empty() && !only_visible) {
+ // We need to send the PasswordFormsRendered message regardless of whether
+ // there are any forms visible, as this is also the code path that triggers
+ // showing the infobar.
+ return;
+ }
+
+ if (only_visible) {
+ Send(new AutofillHostMsg_PasswordFormsRendered(
+ routing_id(), password_forms));
+ } else {
+ Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms));
+ }
+}
+
+bool PasswordAutofillManager::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PasswordAutofillManager, message)
+ IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void PasswordAutofillManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
+ // The |frame| contents have been parsed, but not yet rendered. Let the
+ // PasswordManager know that forms are loaded, even though we can't yet tell
+ // whether they're visible.
+ SendPasswordForms(frame, false);
+}
+
+void PasswordAutofillManager::DidFinishLoad(WebKit::WebFrame* frame) {
+ // The |frame| contents have been rendered. Let the PasswordManager know
+ // which of the loaded frames are actually visible to the user. This also
+ // triggers the "Save password?" infobar if the user just submitted a password
+ // form.
+ SendPasswordForms(frame, true);
+}
+
+void PasswordAutofillManager::FrameDetached(WebKit::WebFrame* frame) {
+ FrameClosing(frame);
+}
+
+void PasswordAutofillManager::FrameWillClose(WebKit::WebFrame* frame) {
+ FrameClosing(frame);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// PageClickListener implementation:
+
+bool PasswordAutofillManager::InputElementClicked(
+ const WebKit::WebInputElement& element,
+ bool was_focused,
+ bool is_focused) {
+ // TODO(jcivelli): http://crbug.com/51644 Implement behavior.
+ return false;
+}
+
+bool PasswordAutofillManager::InputElementLostFocus() {
+ return false;
+}
+
+void PasswordAutofillManager::OnFillPasswordForm(
+ const PasswordFormFillData& form_data,
+ bool disable_popup) {
+ disable_popup_ = disable_popup;
+
+ FormElementsList forms;
+ // We own the FormElements* in forms.
+ FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms);
+ FormElementsList::iterator iter;
+ for (iter = forms.begin(); iter != forms.end(); ++iter) {
+ scoped_ptr<FormElements> form_elements(*iter);
+
+ // If wait_for_username is true, we don't want to initially fill the form
+ // until the user types in a valid username.
+ if (!form_data.wait_for_username)
+ FillForm(form_elements.get(), form_data.basic_data);
+
+ // Attach autocomplete listener to enable selecting alternate logins.
+ // First, get pointers to username element.
+ WebKit::WebInputElement username_element =
+ form_elements->input_elements[form_data.basic_data.fields[0].name];
+
+ // Get pointer to password element. (We currently only support single
+ // password forms).
+ WebKit::WebInputElement password_element =
+ form_elements->input_elements[form_data.basic_data.fields[1].name];
+
+ // We might have already filled this form if there are two <form> elements
+ // with identical markup.
+ if (login_to_password_info_.find(username_element) !=
+ login_to_password_info_.end())
+ continue;
+
+ PasswordInfo password_info;
+ password_info.fill_data = form_data;
+ password_info.password_field = password_element;
+ login_to_password_info_[username_element] = password_info;
+
+ FormData form;
+ FormFieldData field;
+ FindFormAndFieldForInputElement(
+ username_element, &form, &field, REQUIRE_NONE);
+ Send(new AutofillHostMsg_AddPasswordFormMapping(
+ routing_id(),
+ field,
+ form_data));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillManager, private:
+
+void PasswordAutofillManager::GetSuggestions(
+ const PasswordFormFillData& fill_data,
+ const string16& input,
+ std::vector<string16>* suggestions) {
+ if (StartsWith(fill_data.basic_data.fields[0].value, input, false))
+ suggestions->push_back(fill_data.basic_data.fields[0].value);
+
+ PasswordFormFillData::LoginCollection::const_iterator iter;
+ for (iter = fill_data.additional_logins.begin();
+ iter != fill_data.additional_logins.end(); ++iter) {
+ if (StartsWith(iter->first, input, false))
+ suggestions->push_back(iter->first);
+ }
+}
+
+bool PasswordAutofillManager::ShowSuggestionPopup(
+ const PasswordFormFillData& fill_data,
+ const WebKit::WebInputElement& user_input) {
+ WebKit::WebFrame* frame = user_input.document().frame();
+ if (!frame)
+ return false;
+
+ WebKit::WebView* webview = frame->view();
+ if (!webview)
+ return false;
+
+ std::vector<string16> suggestions;
+ GetSuggestions(fill_data, user_input.value(), &suggestions);
+
+ if (disable_popup_) {
+ FormData form;
+ FormFieldData field;
+ FindFormAndFieldForInputElement(
+ user_input, &form, &field, REQUIRE_NONE);
+
+ WebKit::WebInputElement selected_element = user_input;
+ gfx::Rect bounding_box(selected_element.boundsInViewportSpace());
+
+ float scale = web_view_->pageScaleFactor();
+ gfx::RectF bounding_box_scaled(bounding_box.x() * scale,
+ bounding_box.y() * scale,
+ bounding_box.width() * scale,
+ bounding_box.height() * scale);
+ Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(),
+ field,
+ bounding_box_scaled,
+ suggestions));
+ return !suggestions.empty();
+ }
+
+
+ if (suggestions.empty()) {
+ webview->hidePopups();
+ return false;
+ }
+
+ std::vector<string16> labels(suggestions.size());
+ std::vector<string16> icons(suggestions.size());
+ std::vector<int> ids(suggestions.size(),
+ WebKit::WebAutofillClient::MenuItemIDPasswordEntry);
+ webview->applyAutofillSuggestions(
+ user_input, suggestions, labels, icons, ids);
+ return true;
+}
+
+bool PasswordAutofillManager::FillUserNameAndPassword(
+ WebKit::WebInputElement* username_element,
+ WebKit::WebInputElement* password_element,
+ const PasswordFormFillData& fill_data,
+ bool exact_username_match,
+ bool set_selection) {
+ string16 current_username = username_element->value();
+ // username and password will contain the match found if any.
+ string16 username;
+ string16 password;
+
+ // Look for any suitable matches to current field text.
+ if (DoUsernamesMatch(fill_data.basic_data.fields[0].value, current_username,
+ exact_username_match)) {
+ username = fill_data.basic_data.fields[0].value;
+ password = fill_data.basic_data.fields[1].value;
+ } else {
+ // Scan additional logins for a match.
+ PasswordFormFillData::LoginCollection::const_iterator iter;
+ for (iter = fill_data.additional_logins.begin();
+ iter != fill_data.additional_logins.end(); ++iter) {
+ if (DoUsernamesMatch(iter->first, current_username,
+ exact_username_match)) {
+ username = iter->first;
+ password = iter->second;
+ break;
+ }
+ }
+ }
+ if (password.empty())
+ return false; // No match was found.
+
+ // Input matches the username, fill in required values.
+ username_element->setValue(username);
+
+ if (set_selection) {
+ username_element->setSelectionRange(current_username.length(),
+ username.length());
+ }
+
+ SetElementAutofilled(username_element, true);
+ if (IsElementEditable(*password_element))
+ password_element->setValue(password);
+ SetElementAutofilled(password_element, true);
+ return true;
+}
+
+void PasswordAutofillManager::PerformInlineAutocomplete(
+ const WebKit::WebInputElement& username_input,
+ const WebKit::WebInputElement& password_input,
+ const PasswordFormFillData& fill_data) {
+ DCHECK(!fill_data.wait_for_username);
+
+ // We need non-const versions of the username and password inputs.
+ WebKit::WebInputElement username = username_input;
+ WebKit::WebInputElement password = password_input;
+
+ // Don't inline autocomplete if the caret is not at the end.
+ // TODO(jcivelli): is there a better way to test the caret location?
+ if (username.selectionStart() != username.selectionEnd() ||
+ username.selectionEnd() != static_cast<int>(username.value().length())) {
+ return;
+ }
+
+ // Show the popup with the list of available usernames.
+ ShowSuggestionPopup(fill_data, username);
+
+
+#if !defined(OS_ANDROID)
+ // Fill the user and password field with the most relevant match. Android
+ // only fills in the fields after the user clicks on the suggestion popup.
+ FillUserNameAndPassword(&username, &password, fill_data, false, true);
+#endif
+}
+
+void PasswordAutofillManager::FrameClosing(const WebKit::WebFrame* frame) {
+ for (LoginToPasswordInfoMap::iterator iter = login_to_password_info_.begin();
+ iter != login_to_password_info_.end();) {
+ if (iter->first.document().frame() == frame)
+ login_to_password_info_.erase(iter++);
+ else
+ ++iter;
+ }
+}
+
+bool PasswordAutofillManager::FindLoginInfo(
+ const WebKit::WebNode& node,
+ WebKit::WebInputElement* found_input,
+ PasswordInfo* found_password) {
+ if (!node.isElementNode())
+ return false;
+
+ WebKit::WebElement element = node.toConst<WebKit::WebElement>();
+ if (!element.hasTagName("input"))
+ return false;
+
+ WebKit::WebInputElement input = element.to<WebKit::WebInputElement>();
+ LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(input);
+ if (iter == login_to_password_info_.end())
+ return false;
+
+ *found_input = input;
+ *found_password = iter->second;
+ return true;
+}
+
+} // namespace autofill
diff --git a/components/autofill/renderer/password_autofill_manager.h b/components/autofill/renderer/password_autofill_manager.h
new file mode 100644
index 0000000..52b13bf
--- /dev/null
+++ b/components/autofill/renderer/password_autofill_manager.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_RENDERER_PASSWORD_AUTOFILL_MANAGER_H_
+#define COMPONENTS_AUTOFILL_RENDERER_PASSWORD_AUTOFILL_MANAGER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/common/password_form_fill_data.h"
+#include "components/autofill/renderer/page_click_listener.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+
+namespace WebKit {
+class WebInputElement;
+class WebKeyboardEvent;
+class WebView;
+}
+
+namespace autofill {
+
+// This class is responsible for filling password forms.
+// There is one PasswordAutofillManager per RenderView.
+class PasswordAutofillManager : public content::RenderViewObserver,
+ public PageClickListener {
+ public:
+ explicit PasswordAutofillManager(content::RenderView* render_view);
+ virtual ~PasswordAutofillManager();
+
+ // WebViewClient editor related calls forwarded by the RenderView.
+ // If they return true, it indicates the event was consumed and should not
+ // be used for any other autofill activity.
+ bool TextFieldDidEndEditing(const WebKit::WebInputElement& element);
+ bool TextDidChangeInTextField(const WebKit::WebInputElement& element);
+ bool TextFieldHandlingKeyDown(const WebKit::WebInputElement& element,
+ const WebKit::WebKeyboardEvent& event);
+
+ // Fills the password associated with user name |value|. Returns true if the
+ // username and password fields were filled, false otherwise.
+ bool DidAcceptAutofillSuggestion(const WebKit::WebNode& node,
+ const WebKit::WebString& value);
+ // A no-op. No filling happens for selection. But this method returns
+ // true when |node| is fillable by password Autofill.
+ bool DidSelectAutofillSuggestion(const WebKit::WebNode& node);
+ // A no-op. Password forms are not previewed, so they do not need to be
+ // cleared when the selection changes. However, this method returns
+ // true when |node| is fillable by password Autofill.
+ bool DidClearAutofillSelection(const WebKit::WebNode& node);
+
+ private:
+ friend class PasswordAutofillManagerTest;
+
+ struct PasswordInfo {
+ WebKit::WebInputElement password_field;
+ PasswordFormFillData fill_data;
+ bool backspace_pressed_last;
+ PasswordInfo() : backspace_pressed_last(false) {}
+ };
+ typedef std::map<WebKit::WebElement, PasswordInfo> LoginToPasswordInfoMap;
+
+ // RenderViewObserver:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void DidFinishDocumentLoad(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void DidFinishLoad(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void FrameDetached(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void FrameWillClose(WebKit::WebFrame* frame) OVERRIDE;
+
+ // PageClickListener:
+ virtual bool InputElementClicked(const WebKit::WebInputElement& element,
+ bool was_focused,
+ bool is_focused) OVERRIDE;
+ virtual bool InputElementLostFocus() OVERRIDE;
+
+ // RenderView IPC handlers:
+ void OnFillPasswordForm(const PasswordFormFillData& form_data,
+ bool disable_popup);
+
+ // Scans the given frame for password forms and sends them up to the browser.
+ // If |only_visible| is true, only forms visible in the layout are sent.
+ void SendPasswordForms(WebKit::WebFrame* frame, bool only_visible);
+
+ void GetSuggestions(const PasswordFormFillData& fill_data,
+ const string16& input,
+ std::vector<string16>* suggestions);
+
+ bool ShowSuggestionPopup(const PasswordFormFillData& fill_data,
+ const WebKit::WebInputElement& user_input);
+
+ bool FillUserNameAndPassword(
+ WebKit::WebInputElement* username_element,
+ WebKit::WebInputElement* password_element,
+ const PasswordFormFillData& fill_data,
+ bool exact_username_match,
+ bool set_selection);
+
+ // Fills |login_input| and |password| with the most relevant suggestion from
+ // |fill_data| and shows a popup with other suggestions.
+ void PerformInlineAutocomplete(
+ const WebKit::WebInputElement& username,
+ const WebKit::WebInputElement& password,
+ const PasswordFormFillData& fill_data);
+
+ // Invoked when the passed frame is closing. Gives us a chance to clear any
+ // reference we may have to elements in that frame.
+ void FrameClosing(const WebKit::WebFrame* frame);
+
+ // Finds login information for a |node| that was previously filled.
+ bool FindLoginInfo(const WebKit::WebNode& node,
+ WebKit::WebInputElement* found_input,
+ PasswordInfo* found_password);
+
+ // The logins we have filled so far with their associated info.
+ LoginToPasswordInfoMap login_to_password_info_;
+
+ // Used to disable and hide the popup.
+ bool disable_popup_;
+
+ // Pointer to the WebView. Used to access page scale factor.
+ WebKit::WebView* web_view_;
+
+ base::WeakPtrFactory<PasswordAutofillManager> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordAutofillManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_PASSWORD_AUTOFILL_MANAGER_H_
diff --git a/components/autofill/renderer/password_generation_manager.cc b/components/autofill/renderer/password_generation_manager.cc
new file mode 100644
index 0000000..3b1f000
--- /dev/null
+++ b/components/autofill/renderer/password_generation_manager.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/renderer/password_generation_manager.h"
+
+#include "base/logging.h"
+#include "chrome/common/password_generation_util.h"
+#include "components/autofill/common/autofill_messages.h"
+#include "content/public/renderer/password_form_conversion_utils.h"
+#include "content/public/renderer/render_view.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebCString.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
+#include "ui/gfx/rect.h"
+
+namespace autofill {
+
+namespace {
+
+// Returns true if we think that this form is for account creation. |passwords|
+// is filled with the password field(s) in the form.
+bool GetAccountCreationPasswordFields(
+ const WebKit::WebFormElement& form,
+ std::vector<WebKit::WebInputElement>* passwords) {
+ // Grab all of the passwords for the form.
+ WebKit::WebVector<WebKit::WebFormControlElement> control_elements;
+ form.getFormControlElements(control_elements);
+
+ size_t num_input_elements = 0;
+ for (size_t i = 0; i < control_elements.size(); i++) {
+ WebKit::WebInputElement* input_element =
+ toWebInputElement(&control_elements[i]);
+ // Only pay attention to visible password fields.
+ if (input_element &&
+ input_element->isTextField() &&
+ input_element->hasNonEmptyBoundingBox()) {
+ num_input_elements++;
+ if (input_element->isPasswordField())
+ passwords->push_back(*input_element);
+ }
+ }
+
+ // This may be too lenient, but we assume that any form with at least three
+ // input elements where at least one of them is a password is an account
+ // creation form.
+ if (!passwords->empty() && num_input_elements >= 3) {
+ // We trim |passwords| because occasionally there are forms where the
+ // security question answers are put in password fields and we don't want
+ // to fill those.
+ if (passwords->size() > 2)
+ passwords->resize(2);
+
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace
+
+PasswordGenerationManager::PasswordGenerationManager(
+ content::RenderView* render_view)
+ : content::RenderViewObserver(render_view),
+ render_view_(render_view),
+ enabled_(false) {
+ render_view_->GetWebView()->addTextFieldDecoratorClient(this);
+}
+PasswordGenerationManager::~PasswordGenerationManager() {}
+
+void PasswordGenerationManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
+ // In every navigation, the IPC message sent by the password autofill manager
+ // to query whether the current form is blacklisted or not happens when the
+ // document load finishes, so we need to clear previous states here before we
+ // hear back from the browser. We only clear this state on main frame load
+ // as we don't want subframe loads to clear state that we have recieved from
+ // the main frame. Note that we assume there is only one account creation
+ // form, but there could be multiple password forms in each frame.
+ if (!frame->parent()) {
+ not_blacklisted_password_form_origins_.clear();
+ // Initialize to an empty and invalid GURL.
+ account_creation_form_origin_ = GURL();
+ passwords_.clear();
+ }
+}
+
+void PasswordGenerationManager::DidFinishLoad(WebKit::WebFrame* frame) {
+ // We don't want to generate passwords if the browser won't store or sync
+ // them.
+ if (!enabled_)
+ return;
+
+ if (!ShouldAnalyzeDocument(frame->document()))
+ return;
+
+ WebKit::WebVector<WebKit::WebFormElement> forms;
+ frame->document().forms(forms);
+ for (size_t i = 0; i < forms.size(); ++i) {
+ if (forms[i].isNull())
+ continue;
+
+ // If we can't get a valid PasswordForm, we skip this form because the
+ // the password won't get saved even if we generate it.
+ scoped_ptr<content::PasswordForm> password_form(
+ content::CreatePasswordForm(forms[i]));
+ if (!password_form.get()) {
+ DVLOG(2) << "Skipping form as it would not be saved";
+ continue;
+ }
+
+ // Do not generate password for GAIA since it is used to retrieve the
+ // generated paswords.
+ GURL realm(password_form->signon_realm);
+ if (realm == GURL(GaiaUrls::GetInstance()->gaia_login_form_realm()))
+ continue;
+
+ std::vector<WebKit::WebInputElement> passwords;
+ if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
+ DVLOG(2) << "Account creation form detected";
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::SIGN_UP_DETECTED);
+ passwords_ = passwords;
+ account_creation_form_origin_ = password_form->origin;
+ MaybeShowIcon();
+ // We assume that there is only one account creation field per URL.
+ return;
+ }
+ }
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::NO_SIGN_UP_DETECTED);
+}
+
+bool PasswordGenerationManager::ShouldAnalyzeDocument(
+ const WebKit::WebDocument& document) const {
+ // Make sure that this security origin is allowed to use password manager.
+ // Generating a password that can't be saved is a bad idea.
+ WebKit::WebSecurityOrigin origin = document.securityOrigin();
+ if (!origin.canAccessPasswordManager()) {
+ DVLOG(1) << "No PasswordManager access";
+ return false;
+ }
+
+ return true;
+}
+
+bool PasswordGenerationManager::shouldAddDecorationTo(
+ const WebKit::WebInputElement& element) {
+ return element.isPasswordField();
+}
+
+bool PasswordGenerationManager::visibleByDefault() {
+ return false;
+}
+
+WebKit::WebCString PasswordGenerationManager::imageNameForNormalState() {
+ return WebKit::WebCString("generatePassword");
+}
+
+WebKit::WebCString PasswordGenerationManager::imageNameForDisabledState() {
+ return imageNameForNormalState();
+}
+
+WebKit::WebCString PasswordGenerationManager::imageNameForReadOnlyState() {
+ return imageNameForNormalState();
+}
+
+WebKit::WebCString PasswordGenerationManager::imageNameForHoverState() {
+ return WebKit::WebCString("generatePasswordHover");
+}
+
+void PasswordGenerationManager::handleClick(WebKit::WebInputElement& element) {
+ gfx::Rect rect(element.decorationElementFor(this).boundsInViewportSpace());
+ scoped_ptr<content::PasswordForm> password_form(
+ content::CreatePasswordForm(element.form()));
+ // We should not have shown the icon we can't create a valid PasswordForm.
+ DCHECK(password_form.get());
+
+ Send(new AutofillHostMsg_ShowPasswordGenerationPopup(routing_id(),
+ rect,
+ element.maxLength(),
+ *password_form));
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::BUBBLE_SHOWN);
+}
+
+void PasswordGenerationManager::willDetach(
+ const WebKit::WebInputElement& element) {
+ // No implementation
+}
+
+bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PasswordGenerationManager, message)
+ IPC_MESSAGE_HANDLER(AutofillMsg_FormNotBlacklisted,
+ OnFormNotBlacklisted)
+ IPC_MESSAGE_HANDLER(AutofillMsg_GeneratedPasswordAccepted,
+ OnPasswordAccepted)
+ IPC_MESSAGE_HANDLER(AutofillMsg_PasswordGenerationEnabled,
+ OnPasswordGenerationEnabled)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void PasswordGenerationManager::OnFormNotBlacklisted(
+ const content::PasswordForm& form) {
+ not_blacklisted_password_form_origins_.push_back(form.origin);
+ MaybeShowIcon();
+}
+
+void PasswordGenerationManager::OnPasswordAccepted(const string16& password) {
+ for (std::vector<WebKit::WebInputElement>::iterator it = passwords_.begin();
+ it != passwords_.end(); ++it) {
+ it->setValue(password);
+ it->setAutofilled(true);
+ // Advance focus to the next input field. We assume password fields in
+ // an account creation form are always adjacent.
+ render_view_->GetWebView()->advanceFocus(false);
+ }
+}
+
+void PasswordGenerationManager::OnPasswordGenerationEnabled(bool enabled) {
+ enabled_ = enabled;
+}
+
+void PasswordGenerationManager::MaybeShowIcon() {
+ // We should show the password generation icon only when we have detected
+ // account creation form and we have confirmed from browser that this form
+ // is not blacklisted by the users.
+ if (!account_creation_form_origin_.is_valid() ||
+ passwords_.empty() ||
+ not_blacklisted_password_form_origins_.empty()) {
+ return;
+ }
+
+ for (std::vector<GURL>::iterator it =
+ not_blacklisted_password_form_origins_.begin();
+ it != not_blacklisted_password_form_origins_.end(); ++it) {
+ if (*it == account_creation_form_origin_) {
+ passwords_[0].decorationElementFor(this).setAttribute("style",
+ "display:block");
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::ICON_SHOWN);
+ return;
+ }
+ }
+}
+
+} // namespace autofill
diff --git a/components/autofill/renderer/password_generation_manager.h b/components/autofill/renderer/password_generation_manager.h
new file mode 100644
index 0000000..9ed94ca
--- /dev/null
+++ b/components/autofill/renderer/password_generation_manager.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_RENDERER_PASSWORD_GENERATION_MANAGER_H_
+#define COMPONENTS_AUTOFILL_RENDERER_PASSWORD_GENERATION_MANAGER_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "content/public/renderer/render_view_observer.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextFieldDecoratorClient.h"
+
+namespace WebKit {
+class WebCString;
+class WebDocument;
+}
+
+namespace content {
+struct PasswordForm;
+}
+
+namespace autofill {
+
+// This class is responsible for controlling communication for password
+// generation between the browser (which shows the popup and generates
+// passwords) and WebKit (shows the generation icon in the password field).
+class PasswordGenerationManager : public content::RenderViewObserver,
+ public WebKit::WebTextFieldDecoratorClient {
+ public:
+ explicit PasswordGenerationManager(content::RenderView* render_view);
+ virtual ~PasswordGenerationManager();
+
+ protected:
+ // Returns true if this document is one that we should consider analyzing.
+ // Virtual so that it can be overriden during testing.
+ virtual bool ShouldAnalyzeDocument(const WebKit::WebDocument& document) const;
+
+ // RenderViewObserver:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ // RenderViewObserver:
+ virtual void DidFinishDocumentLoad(WebKit::WebFrame* frame) OVERRIDE;
+ virtual void DidFinishLoad(WebKit::WebFrame* frame) OVERRIDE;
+
+ // WebTextFieldDecoratorClient:
+ virtual bool shouldAddDecorationTo(
+ const WebKit::WebInputElement& element) OVERRIDE;
+ virtual bool visibleByDefault() OVERRIDE;
+ virtual WebKit::WebCString imageNameForNormalState() OVERRIDE;
+ virtual WebKit::WebCString imageNameForDisabledState() OVERRIDE;
+ virtual WebKit::WebCString imageNameForReadOnlyState() OVERRIDE;
+ virtual WebKit::WebCString imageNameForHoverState() OVERRIDE;
+ virtual void handleClick(WebKit::WebInputElement& element) OVERRIDE;
+ virtual void willDetach(const WebKit::WebInputElement& element) OVERRIDE;
+
+ // Message handlers.
+ void OnFormNotBlacklisted(const content::PasswordForm& form);
+ void OnPasswordAccepted(const string16& password);
+ void OnPasswordGenerationEnabled(bool enabled);
+
+ // Helper function to decide whether we should show password generation icon.
+ void MaybeShowIcon();
+
+ content::RenderView* render_view_;
+
+ // True if password generation is enabled for the profile associated
+ // with this renderer.
+ bool enabled_;
+
+ // Stores the origin of the account creation form we detected.
+ GURL account_creation_form_origin_;
+
+ // Stores the origins of the password forms confirmed not to be blacklisted
+ // by the browser. A form can be blacklisted if a user chooses "never save
+ // passwords for this site".
+ std::vector<GURL> not_blacklisted_password_form_origins_;
+
+ std::vector<WebKit::WebInputElement> passwords_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordGenerationManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_RENDERER_PASSWORD_GENERATION_MANAGER_H_