summaryrefslogtreecommitdiff
path: root/chromium/components
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/components
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/components')
-rw-r--r--chromium/components/DEPS16
-rw-r--r--chromium/components/OWNERS46
-rw-r--r--chromium/components/README35
-rw-r--r--chromium/components/auto_login_parser.gypi23
-rw-r--r--chromium/components/auto_login_parser/DEPS3
-rw-r--r--chromium/components/auto_login_parser/OWNERS1
-rw-r--r--chromium/components/auto_login_parser/auto_login_parser.cc82
-rw-r--r--chromium/components/auto_login_parser/auto_login_parser.h50
-rw-r--r--chromium/components/auto_login_parser/auto_login_parser_unittest.cc92
-rw-r--r--chromium/components/autofill.gypi408
-rw-r--r--chromium/components/autofill/DEPS11
-rw-r--r--chromium/components/autofill/OWNERS8
-rw-r--r--chromium/components/autofill/README23
-rw-r--r--chromium/components/autofill/content/DEPS6
-rw-r--r--chromium/components/autofill/content/browser/DEPS41
-rw-r--r--chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc205
-rw-r--r--chromium/components/autofill/content/browser/autocheckout/whitelist_manager.h111
-rw-r--r--chromium/components/autofill/content/browser/autocheckout/whitelist_manager_unittest.cc309
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_manager.cc581
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_manager.h191
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_manager_unittest.cc949
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_page_meta_data.cc28
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_page_meta_data.h77
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_page_meta_data_unittest.cc54
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_request_manager.cc110
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_request_manager.h90
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_statistic.cc52
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_statistic.h40
-rw-r--r--chromium/components/autofill/content/browser/autocheckout_steps.h32
-rw-r--r--chromium/components/autofill/content/browser/autofill_driver_impl.cc235
-rw-r--r--chromium/components/autofill/content/browser/autofill_driver_impl.h108
-rw-r--r--chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc224
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.cc505
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.h68
-rw-r--r--chromium/components/autofill/content/browser/risk/proto/fingerprint.proto224
-rw-r--r--chromium/components/autofill/content/browser/wallet/OWNERS1
-rw-r--r--chromium/components/autofill/content/browser/wallet/form_field_error.cc154
-rw-r--r--chromium/components/autofill/content/browser/wallet/form_field_error.h88
-rw-r--r--chromium/components/autofill/content/browser/wallet/full_wallet.cc323
-rw-r--r--chromium/components/autofill/content/browser/wallet/full_wallet.h150
-rw-r--r--chromium/components/autofill/content/browser/wallet/full_wallet_unittest.cc530
-rw-r--r--chromium/components/autofill/content/browser/wallet/instrument.cc125
-rw-r--r--chromium/components/autofill/content/browser/wallet/instrument.h105
-rw-r--r--chromium/components/autofill/content/browser/wallet/instrument_unittest.cc66
-rw-r--r--chromium/components/autofill/content/browser/wallet/mock_wallet_client.cc17
-rw-r--r--chromium/components/autofill/content/browser/wallet/mock_wallet_client.h67
-rw-r--r--chromium/components/autofill/content/browser/wallet/required_action.cc66
-rw-r--r--chromium/components/autofill/content/browser/wallet/required_action.h44
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_address.cc332
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_address.h201
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_address_unittest.cc478
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client.cc911
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client.h270
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h88
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc1764
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_items.cc525
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_items.h300
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc570
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_service_url.cc148
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_service_url.h42
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc61
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_signin_helper.cc322
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_signin_helper.h125
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h43
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_signin_helper_unittest.cc245
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_test_util.cc237
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_test_util.h43
-rw-r--r--chromium/components/autofill/content/renderer/DEPS4
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc785
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h277
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc1059
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.h144
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.cc297
-rw-r--r--chromium/components/autofill/content/renderer/form_cache.h77
-rw-r--r--chromium/components/autofill/content/renderer/page_click_listener.h36
-rw-r--r--chromium/components/autofill/content/renderer/page_click_tracker.cc146
-rw-r--r--chromium/components/autofill/content/renderer/page_click_tracker.h71
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc680
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h133
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_manager.cc229
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_manager.h82
-rw-r--r--chromium/components/autofill/core/DEPS10
-rw-r--r--chromium/components/autofill/core/browser/DEPS53
-rw-r--r--chromium/components/autofill/core/browser/address.cc174
-rw-r--r--chromium/components/autofill/core/browser/address.h59
-rw-r--r--chromium/components/autofill/core/browser/address_field.cc341
-rw-r--r--chromium/components/autofill/core/browser/address_field.h88
-rw-r--r--chromium/components/autofill/core/browser/address_field_unittest.cc298
-rw-r--r--chromium/components/autofill/core/browser/address_unittest.cc198
-rw-r--r--chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc122
-rw-r--r--chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.h75
-rw-r--r--chromium/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc163
-rw-r--r--chromium/components/autofill/core/browser/android/auxiliary_profiles_android.cc122
-rw-r--r--chromium/components/autofill/core/browser/android/auxiliary_profiles_android.h54
-rw-r--r--chromium/components/autofill/core/browser/android/component_jni_registrar.cc23
-rw-r--r--chromium/components/autofill/core/browser/android/component_jni_registrar.h18
-rw-r--r--chromium/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java319
-rw-r--r--chromium/components/autofill/core/browser/android/personal_data_manager_android.cc25
-rw-r--r--chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc132
-rw-r--r--chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h74
-rw-r--r--chromium/components/autofill/core/browser/autocheckout_bubble_state.h22
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager.cc205
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager.h103
-rw-r--r--chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc262
-rw-r--r--chromium/components/autofill/core/browser/autofill-inl.h39
-rw-r--r--chromium/components/autofill/core/browser/autofill_common_test.cc194
-rw-r--r--chromium/components/autofill/core/browser/autofill_common_test.h84
-rw-r--r--chromium/components/autofill/core/browser/autofill_country.cc1111
-rw-r--r--chromium/components/autofill/core/browser/autofill_country.h110
-rw-r--r--chromium/components/autofill/core/browser/autofill_country_unittest.cc90
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_model.cc189
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_model.h70
-rw-r--r--chromium/components/autofill/core/browser/autofill_data_model_unittest.cc68
-rw-r--r--chromium/components/autofill/core/browser/autofill_download.cc352
-rw-r--r--chromium/components/autofill/core/browser/autofill_download.h170
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_unittest.cc491
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_url.cc47
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_url.h18
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_url_unittest.cc26
-rw-r--r--chromium/components/autofill/core/browser/autofill_driver.h67
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.cc381
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.h185
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc473
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.cc106
-rw-r--r--chromium/components/autofill/core/browser/autofill_field.h103
-rw-r--r--chromium/components/autofill/core/browser/autofill_field_unittest.cc99
-rw-r--r--chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc312
-rw-r--r--chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h24
-rw-r--r--chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc211
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.cc1219
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.h407
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_delegate.h149
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_test_delegate.h26
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_unittest.cc3271
-rw-r--r--chromium/components/autofill/core/browser/autofill_merge_unittest.cc248
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.cc598
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.h516
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics_unittest.cc1564
-rw-r--r--chromium/components/autofill/core/browser/autofill_popup_delegate.h46
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.cc906
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.h217
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile_unittest.cc931
-rw-r--r--chromium/components/autofill/core/browser/autofill_regex_constants.cc.utf8294
-rw-r--r--chromium/components/autofill/core/browser/autofill_regex_constants.h59
-rw-r--r--chromium/components/autofill/core/browser/autofill_regexes.cc84
-rw-r--r--chromium/components/autofill/core/browser/autofill_regexes.h20
-rw-r--r--chromium/components/autofill/core/browser/autofill_regexes_unittest.cc64
-rw-r--r--chromium/components/autofill/core/browser/autofill_scanner.cc58
-rw-r--r--chromium/components/autofill/core/browser/autofill_scanner.h61
-rw-r--r--chromium/components/autofill/core/browser/autofill_server_field_info.h24
-rw-r--r--chromium/components/autofill/core/browser/autofill_type.cc612
-rw-r--r--chromium/components/autofill/core/browser/autofill_type.h59
-rw-r--r--chromium/components/autofill/core/browser/autofill_type_unittest.cc91
-rw-r--r--chromium/components/autofill/core/browser/autofill_xml_parser.cc260
-rw-r--r--chromium/components/autofill/core/browser/autofill_xml_parser.h189
-rw-r--r--chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc442
-rw-r--r--chromium/components/autofill/core/browser/contact_info.cc217
-rw-r--r--chromium/components/autofill/core/browser/contact_info.h101
-rw-r--r--chromium/components/autofill/core/browser/contact_info_unittest.cc105
-rw-r--r--chromium/components/autofill/core/browser/credit_card.cc714
-rw-r--r--chromium/components/autofill/core/browser/credit_card.h174
-rw-r--r--chromium/components/autofill/core/browser/credit_card_field.cc233
-rw-r--r--chromium/components/autofill/core/browser/credit_card_field.h75
-rw-r--r--chromium/components/autofill/core/browser/credit_card_field_unittest.cc322
-rw-r--r--chromium/components/autofill/core/browser/credit_card_unittest.cc693
-rw-r--r--chromium/components/autofill/core/browser/crypto/rc4_decryptor.h110
-rw-r--r--chromium/components/autofill/core/browser/data_driven_test.cc93
-rw-r--r--chromium/components/autofill/core/browser/data_driven_test.h56
-rw-r--r--chromium/components/autofill/core/browser/email_field.cc32
-rw-r--r--chromium/components/autofill/core/browser/email_field.h32
-rw-r--r--chromium/components/autofill/core/browser/field_types.h191
-rw-r--r--chromium/components/autofill/core/browser/form_field.cc199
-rw-r--r--chromium/components/autofill/core/browser/form_field.h124
-rw-r--r--chromium/components/autofill/core/browser/form_field_unittest.cc152
-rw-r--r--chromium/components/autofill/core/browser/form_group.cc51
-rw-r--r--chromium/components/autofill/core/browser/form_group.h68
-rw-r--r--chromium/components/autofill/core/browser/form_structure.cc1259
-rw-r--r--chromium/components/autofill/core/browser/form_structure.h262
-rw-r--r--chromium/components/autofill/core/browser/form_structure_unittest.cc2466
-rw-r--r--chromium/components/autofill/core/browser/name_field.cc217
-rw-r--r--chromium/components/autofill/core/browser/name_field.h46
-rw-r--r--chromium/components/autofill/core/browser/name_field_unittest.cc311
-rw-r--r--chromium/components/autofill/core/browser/password_autofill_manager.cc96
-rw-r--r--chromium/components/autofill/core/browser/password_autofill_manager.h75
-rw-r--r--chromium/components/autofill/core/browser/password_autofill_manager_unittest.cc78
-rw-r--r--chromium/components/autofill/core/browser/password_generator.cc125
-rw-r--r--chromium/components/autofill/core/browser/password_generator.h48
-rw-r--r--chromium/components/autofill/core/browser/password_generator_unittest.cc58
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.cc1033
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.h293
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_mac.mm273
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_observer.h25
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_unittest.cc2418
-rw-r--r--chromium/components/autofill/core/browser/phone_field.cc276
-rw-r--r--chromium/components/autofill/core/browser/phone_field.h93
-rw-r--r--chromium/components/autofill/core/browser/phone_field_unittest.cc230
-rw-r--r--chromium/components/autofill/core/browser/phone_number.cc247
-rw-r--r--chromium/components/autofill/core/browser/phone_number.h99
-rw-r--r--chromium/components/autofill/core/browser/phone_number_i18n.cc299
-rw-r--r--chromium/components/autofill/core/browser/phone_number_i18n.h112
-rw-r--r--chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc389
-rw-r--r--chromium/components/autofill/core/browser/phone_number_unittest.cc212
-rw-r--r--chromium/components/autofill/core/browser/state_names.cc115
-rw-r--r--chromium/components/autofill/core/browser/state_names.h31
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_driver.cc40
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_driver.h43
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_external_delegate.cc29
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_external_delegate.h21
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc80
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_manager_delegate.h71
-rw-r--r--chromium/components/autofill/core/browser/test_personal_data_manager.cc42
-rw-r--r--chromium/components/autofill/core/browser/test_personal_data_manager.h44
-rw-r--r--chromium/components/autofill/core/browser/validation.cc220
-rw-r--r--chromium/components/autofill/core/browser/validation.h53
-rw-r--r--chromium/components/autofill/core/browser/validation_unittest.cc199
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_change.cc39
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_change.h76
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_entry.cc129
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_entry.h75
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_entry_unittest.cc82
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.cc2193
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.h388
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc1495
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata.h107
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend.h43
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc384
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h185
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc217
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h126
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h37
-rw-r--r--chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc574
-rw-r--r--chromium/components/autofill/core/common/OWNERS9
-rw-r--r--chromium/components/autofill/core/common/autocheckout_status.h22
-rw-r--r--chromium/components/autofill/core/common/autofill_constants.cc20
-rw-r--r--chromium/components/autofill/core/common/autofill_constants.h25
-rw-r--r--chromium/components/autofill/core/common/autofill_message_generator.cc33
-rw-r--r--chromium/components/autofill/core/common/autofill_message_generator.h7
-rw-r--r--chromium/components/autofill/core/common/autofill_messages.h297
-rw-r--r--chromium/components/autofill/core/common/autofill_pref_names.cc25
-rw-r--r--chromium/components/autofill/core/common/autofill_pref_names.h21
-rw-r--r--chromium/components/autofill/core/common/autofill_switches.cc49
-rw-r--r--chromium/components/autofill/core/common/autofill_switches.h27
-rw-r--r--chromium/components/autofill/core/common/form_data.cc40
-rw-r--r--chromium/components/autofill/core/common/form_data.h42
-rw-r--r--chromium/components/autofill/core/common/form_data_predictions.cc35
-rw-r--r--chromium/components/autofill/core/common/form_data_predictions.h38
-rw-r--r--chromium/components/autofill/core/common/form_field_data.cc73
-rw-r--r--chromium/components/autofill/core/common/form_field_data.h69
-rw-r--r--chromium/components/autofill/core/common/form_field_data_predictions.cc38
-rw-r--r--chromium/components/autofill/core/common/form_field_data_predictions.h34
-rw-r--r--chromium/components/autofill/core/common/forms_seen_state.h24
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.cc84
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.h87
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data_unittest.cc171
-rw-r--r--chromium/components/autofill/core/common/password_generation_util.cc42
-rw-r--r--chromium/components/autofill/core/common/password_generation_util.h77
-rw-r--r--chromium/components/autofill/core/common/web_element_descriptor.cc12
-rw-r--r--chromium/components/autofill/core/common/web_element_descriptor.h31
-rw-r--r--chromium/components/autofill_strings.grdp106
-rw-r--r--chromium/components/breakpad.gypi68
-rw-r--r--chromium/components/breakpad/OWNERS4
-rw-r--r--chromium/components/breakpad/breakpad_client.cc118
-rw-r--r--chromium/components/breakpad/breakpad_client.h131
-rw-r--r--chromium/components/browser_context_keyed_service.gypi44
-rw-r--r--chromium/components/browser_context_keyed_service/DEPS3
-rw-r--r--chromium/components/browser_context_keyed_service/OWNERS4
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc159
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h79
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc174
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc113
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.h140
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_service.h32
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_service_export.h29
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc130
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.h122
-rw-r--r--chromium/components/browser_context_keyed_service/dependency_graph.cc166
-rw-r--r--chromium/components/browser_context_keyed_service/dependency_graph.h68
-rw-r--r--chromium/components/browser_context_keyed_service/dependency_graph_unittest.cc161
-rw-r--r--chromium/components/browser_context_keyed_service/dependency_node.h16
-rw-r--r--chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc34
-rw-r--r--chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h75
-rw-r--r--chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc130
-rw-r--r--chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h100
-rw-r--r--chromium/components/component_strings.grd167
-rw-r--r--chromium/components/component_strings.gyp30
-rw-r--r--chromium/components/components.gyp28
-rw-r--r--chromium/components/components_tests.gypi156
-rw-r--r--chromium/components/components_unittests.isolate17
-rw-r--r--chromium/components/json_schema.gypi25
-rw-r--r--chromium/components/json_schema/DEPS3
-rw-r--r--chromium/components/json_schema/OWNERS5
-rw-r--r--chromium/components/json_schema/README6
-rw-r--r--chromium/components/json_schema/json_schema_constants.cc38
-rw-r--r--chromium/components/json_schema/json_schema_constants.h42
-rw-r--r--chromium/components/json_schema/json_schema_validator.cc727
-rw-r--r--chromium/components/json_schema/json_schema_validator.h235
-rw-r--r--chromium/components/json_schema/json_schema_validator_unittest.cc129
-rw-r--r--chromium/components/json_schema/json_schema_validator_unittest_base.cc730
-rw-r--r--chromium/components/json_schema/json_schema_validator_unittest_base.h63
-rw-r--r--chromium/components/nacl.gyp167
-rw-r--r--chromium/components/nacl/OWNERS7
-rw-r--r--chromium/components/nacl/broker/DEPS4
-rw-r--r--chromium/components/nacl/broker/nacl_broker_listener.cc129
-rw-r--r--chromium/components/nacl/broker/nacl_broker_listener.h50
-rw-r--r--chromium/components/nacl/common/DEPS3
-rw-r--r--chromium/components/nacl/common/OWNERS10
-rw-r--r--chromium/components/nacl/common/nacl_browser_delegate.h66
-rw-r--r--chromium/components/nacl/common/nacl_cmd_line.cc36
-rw-r--r--chromium/components/nacl/common/nacl_cmd_line.h16
-rw-r--r--chromium/components/nacl/common/nacl_debug_exception_handler_win.cc78
-rw-r--r--chromium/components/nacl/common/nacl_debug_exception_handler_win.h18
-rw-r--r--chromium/components/nacl/common/nacl_helper_linux.h42
-rw-r--r--chromium/components/nacl/common/nacl_host_messages.h110
-rw-r--r--chromium/components/nacl/common/nacl_messages.cc34
-rw-r--r--chromium/components/nacl/common/nacl_messages.h94
-rw-r--r--chromium/components/nacl/common/nacl_paths.cc53
-rw-r--r--chromium/components/nacl/common/nacl_paths.h31
-rw-r--r--chromium/components/nacl/common/nacl_process_type.h18
-rw-r--r--chromium/components/nacl/common/nacl_sandbox_type_mac.h16
-rw-r--r--chromium/components/nacl/common/nacl_switches.cc39
-rw-r--r--chromium/components/nacl/common/nacl_switches.h24
-rw-r--r--chromium/components/nacl/common/nacl_types.cc77
-rw-r--r--chromium/components/nacl/common/nacl_types.h101
-rw-r--r--chromium/components/nacl/common/pnacl_types.cc28
-rw-r--r--chromium/components/nacl/common/pnacl_types.h48
-rw-r--r--chromium/components/nacl/loader/DEPS17
-rw-r--r--chromium/components/nacl/loader/OWNERS2
-rw-r--r--chromium/components/nacl/loader/nacl_ipc_adapter.cc592
-rw-r--r--chromium/components/nacl/loader/nacl_ipc_adapter.h192
-rw-r--r--chromium/components/nacl/loader/nacl_ipc_adapter_unittest.cc357
-rw-r--r--chromium/components/nacl/loader/nacl_listener.cc336
-rw-r--r--chromium/components/nacl/loader/nacl_listener.h74
-rw-r--r--chromium/components/nacl/loader/nacl_main.cc53
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate.h26
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc31
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm26
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc31
-rw-r--r--chromium/components/nacl/loader/nacl_sandbox_linux.cc143
-rw-r--r--chromium/components/nacl/loader/nacl_sandbox_linux.h10
-rw-r--r--chromium/components/nacl/loader/nacl_validation_db.h28
-rw-r--r--chromium/components/nacl/loader/nacl_validation_query.cc172
-rw-r--r--chromium/components/nacl/loader/nacl_validation_query.h94
-rw-r--r--chromium/components/nacl/loader/nacl_validation_query_unittest.cc283
-rw-r--r--chromium/components/nacl/nacl_defines.gypi61
-rw-r--r--chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc248
-rw-r--r--chromium/components/nacl/zygote/nacl_fork_delegate_linux.h53
-rw-r--r--chromium/components/nacl_common.gyp76
-rw-r--r--chromium/components/navigation_interception.gypi80
-rw-r--r--chromium/components/navigation_interception/DEPS8
-rw-r--r--chromium/components/navigation_interception/OWNERS2
-rw-r--r--chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java21
-rw-r--r--chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java36
-rw-r--r--chromium/components/navigation_interception/component_jni_registrar.cc24
-rw-r--r--chromium/components/navigation_interception/component_jni_registrar.h18
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.cc107
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_delegate.h69
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc140
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_resource_throttle.h62
-rw-r--r--chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc475
-rw-r--r--chromium/components/navigation_interception/navigation_params.cc42
-rw-r--r--chromium/components/navigation_interception/navigation_params.h47
-rw-r--r--chromium/components/navigation_interception/navigation_params_android.cc34
-rw-r--r--chromium/components/navigation_interception/navigation_params_android.h22
-rw-r--r--chromium/components/policy.gypi41
-rw-r--r--chromium/components/policy/OWNERS6
-rw-r--r--chromium/components/policy/core/common/policy_schema.cc244
-rw-r--r--chromium/components/policy/core/common/policy_schema.h70
-rw-r--r--chromium/components/policy/core/common/policy_schema_unittest.cc193
-rw-r--r--chromium/components/policy/policy_export.h29
-rw-r--r--chromium/components/policy/stub_to_remove.cc6
-rw-r--r--chromium/components/sessions.gypi64
-rw-r--r--chromium/components/sessions/DEPS7
-rw-r--r--chromium/components/sessions/OWNERS2
-rw-r--r--chromium/components/sessions/serialized_navigation_entry.cc475
-rw-r--r--chromium/components/sessions/serialized_navigation_entry.h160
-rw-r--r--chromium/components/sessions/serialized_navigation_entry_test_helper.cc84
-rw-r--r--chromium/components/sessions/serialized_navigation_entry_test_helper.h63
-rw-r--r--chromium/components/sessions/serialized_navigation_entry_unittest.cc290
-rw-r--r--chromium/components/sessions/sessions_export.h29
-rw-r--r--chromium/components/strings/component_strings_am.xtb4
-rw-r--r--chromium/components/strings/component_strings_ar.xtb4
-rw-r--r--chromium/components/strings/component_strings_bg.xtb4
-rw-r--r--chromium/components/strings/component_strings_bn.xtb4
-rw-r--r--chromium/components/strings/component_strings_ca.xtb4
-rw-r--r--chromium/components/strings/component_strings_cs.xtb4
-rw-r--r--chromium/components/strings/component_strings_da.xtb4
-rw-r--r--chromium/components/strings/component_strings_de.xtb4
-rw-r--r--chromium/components/strings/component_strings_el.xtb4
-rw-r--r--chromium/components/strings/component_strings_en-GB.xtb4
-rw-r--r--chromium/components/strings/component_strings_es-419.xtb4
-rw-r--r--chromium/components/strings/component_strings_es.xtb4
-rw-r--r--chromium/components/strings/component_strings_et.xtb4
-rw-r--r--chromium/components/strings/component_strings_fa.xtb4
-rw-r--r--chromium/components/strings/component_strings_fi.xtb4
-rw-r--r--chromium/components/strings/component_strings_fil.xtb4
-rw-r--r--chromium/components/strings/component_strings_fr.xtb4
-rw-r--r--chromium/components/strings/component_strings_gu.xtb4
-rw-r--r--chromium/components/strings/component_strings_hi.xtb4
-rw-r--r--chromium/components/strings/component_strings_hr.xtb4
-rw-r--r--chromium/components/strings/component_strings_hu.xtb4
-rw-r--r--chromium/components/strings/component_strings_id.xtb4
-rw-r--r--chromium/components/strings/component_strings_it.xtb4
-rw-r--r--chromium/components/strings/component_strings_iw.xtb4
-rw-r--r--chromium/components/strings/component_strings_ja.xtb4
-rw-r--r--chromium/components/strings/component_strings_kn.xtb4
-rw-r--r--chromium/components/strings/component_strings_ko.xtb4
-rw-r--r--chromium/components/strings/component_strings_lt.xtb4
-rw-r--r--chromium/components/strings/component_strings_lv.xtb4
-rw-r--r--chromium/components/strings/component_strings_ml.xtb4
-rw-r--r--chromium/components/strings/component_strings_mr.xtb4
-rw-r--r--chromium/components/strings/component_strings_ms.xtb4
-rw-r--r--chromium/components/strings/component_strings_nl.xtb4
-rw-r--r--chromium/components/strings/component_strings_no.xtb4
-rw-r--r--chromium/components/strings/component_strings_pl.xtb4
-rw-r--r--chromium/components/strings/component_strings_pt-BR.xtb4
-rw-r--r--chromium/components/strings/component_strings_pt-PT.xtb4
-rw-r--r--chromium/components/strings/component_strings_ro.xtb4
-rw-r--r--chromium/components/strings/component_strings_ru.xtb4
-rw-r--r--chromium/components/strings/component_strings_sk.xtb4
-rw-r--r--chromium/components/strings/component_strings_sl.xtb4
-rw-r--r--chromium/components/strings/component_strings_sr.xtb4
-rw-r--r--chromium/components/strings/component_strings_sv.xtb4
-rw-r--r--chromium/components/strings/component_strings_sw.xtb4
-rw-r--r--chromium/components/strings/component_strings_ta.xtb4
-rw-r--r--chromium/components/strings/component_strings_te.xtb4
-rw-r--r--chromium/components/strings/component_strings_th.xtb4
-rw-r--r--chromium/components/strings/component_strings_tr.xtb4
-rw-r--r--chromium/components/strings/component_strings_uk.xtb4
-rw-r--r--chromium/components/strings/component_strings_vi.xtb4
-rw-r--r--chromium/components/strings/component_strings_zh-CN.xtb4
-rw-r--r--chromium/components/strings/component_strings_zh-TW.xtb4
-rwxr-xr-xchromium/components/tools/metrics/browser_components_metrics.py65
-rwxr-xr-xchromium/components/tools/metrics/count_ifdefs.py77
-rwxr-xr-xchromium/components/tools/metrics/count_ifdefs_unittest.py30
-rw-r--r--chromium/components/tools/metrics/testdata/foo.cc20
-rw-r--r--chromium/components/tools/metrics/testdata/foo_ignored.txt4
-rw-r--r--chromium/components/tools/metrics/testdata/subdir/foo_test.mm8
-rw-r--r--chromium/components/tracing.gyp31
-rw-r--r--chromium/components/tracing/DEPS8
-rw-r--r--chromium/components/tracing/OWNERS12
-rw-r--r--chromium/components/tracing/child_trace_message_filter.cc118
-rw-r--r--chromium/components/tracing/child_trace_message_filter.h55
-rw-r--r--chromium/components/tracing/tracing_messages.cc34
-rw-r--r--chromium/components/tracing/tracing_messages.h56
-rw-r--r--chromium/components/tracing_untrusted.gyp42
-rw-r--r--chromium/components/user_prefs.gypi32
-rw-r--r--chromium/components/user_prefs/DEPS4
-rw-r--r--chromium/components/user_prefs/OWNERS4
-rw-r--r--chromium/components/user_prefs/README8
-rw-r--r--chromium/components/user_prefs/pref_registry_syncable.cc225
-rw-r--r--chromium/components/user_prefs/pref_registry_syncable.h131
-rw-r--r--chromium/components/user_prefs/user_prefs.cc47
-rw-r--r--chromium/components/user_prefs/user_prefs.h48
-rw-r--r--chromium/components/user_prefs/user_prefs_export.h29
-rw-r--r--chromium/components/visitedlink.gypi66
-rw-r--r--chromium/components/visitedlink/OWNERS2
-rw-r--r--chromium/components/visitedlink/browser/DEPS3
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_delegate.h51
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_event_listener.cc219
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_event_listener.h70
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_master.cc989
-rw-r--r--chromium/components/visitedlink/browser/visitedlink_master.h445
-rw-r--r--chromium/components/visitedlink/common/DEPS3
-rw-r--r--chromium/components/visitedlink/common/OWNERS9
-rw-r--r--chromium/components/visitedlink/common/visitedlink_common.cc102
-rw-r--r--chromium/components/visitedlink/common/visitedlink_common.h138
-rw-r--r--chromium/components/visitedlink/common/visitedlink_message_generator.cc34
-rw-r--r--chromium/components/visitedlink/common/visitedlink_message_generator.h7
-rw-r--r--chromium/components/visitedlink/common/visitedlink_messages.h29
-rw-r--r--chromium/components/visitedlink/renderer/DEPS5
-rw-r--r--chromium/components/visitedlink/renderer/visitedlink_slave.cc92
-rw-r--r--chromium/components/visitedlink/renderer/visitedlink_slave.h42
-rw-r--r--chromium/components/web_contents_delegate_android.gypi64
-rw-r--r--chromium/components/web_contents_delegate_android/DEPS6
-rw-r--r--chromium/components/web_contents_delegate_android/OWNERS1
-rw-r--r--chromium/components/web_contents_delegate_android/android/DEPS4
-rw-r--r--chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java60
-rw-r--r--chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java167
-rw-r--r--chromium/components/web_contents_delegate_android/color_chooser_android.cc57
-rw-r--r--chromium/components/web_contents_delegate_android/color_chooser_android.h50
-rw-r--r--chromium/components/web_contents_delegate_android/component_jni_registrar.cc25
-rw-r--r--chromium/components/web_contents_delegate_android/component_jni_registrar.h19
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc361
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.h112
-rw-r--r--chromium/components/web_modal.gypi33
-rw-r--r--chromium/components/web_modal/DEPS6
-rw-r--r--chromium/components/web_modal/native_web_contents_modal_dialog.h22
-rw-r--r--chromium/components/web_modal/native_web_contents_modal_dialog_manager.h66
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_host.cc21
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_host.h54
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager.cc149
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager.h116
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.cc28
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h42
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc81
-rw-r--r--chromium/components/webdata.gypi75
-rw-r--r--chromium/components/webdata/DEPS22
-rw-r--r--chromium/components/webdata/OWNERS5
-rw-r--r--chromium/components/webdata/README5
-rw-r--r--chromium/components/webdata/common/web_data_request_manager.cc145
-rw-r--r--chromium/components/webdata/common/web_data_request_manager.h138
-rw-r--r--chromium/components/webdata/common/web_data_results.h135
-rw-r--r--chromium/components/webdata/common/web_data_service_backend.cc124
-rw-r--r--chromium/components/webdata/common/web_data_service_backend.h130
-rw-r--r--chromium/components/webdata/common/web_data_service_base.cc78
-rw-r--r--chromium/components/webdata/common/web_data_service_base.h103
-rw-r--r--chromium/components/webdata/common/web_data_service_consumer.h26
-rw-r--r--chromium/components/webdata/common/web_data_service_test_util.cc46
-rw-r--r--chromium/components/webdata/common/web_data_service_test_util.h54
-rw-r--r--chromium/components/webdata/common/web_database.cc167
-rw-r--r--chromium/components/webdata/common/web_database.h73
-rw-r--r--chromium/components/webdata/common/web_database_migration_unittest.cc2082
-rw-r--r--chromium/components/webdata/common/web_database_service.cc181
-rw-r--r--chromium/components/webdata/common/web_database_service.h155
-rw-r--r--chromium/components/webdata/common/web_database_table.cc17
-rw-r--r--chromium/components/webdata/common/web_database_table.h66
-rw-r--r--chromium/components/webdata/common/webdata_constants.cc8
-rw-r--r--chromium/components/webdata/common/webdata_constants.h13
-rw-r--r--chromium/components/webdata/common/webdata_export.h29
-rw-r--r--chromium/components/webdata/encryptor/DEPS4
-rw-r--r--chromium/components/webdata/encryptor/OWNERS1
-rw-r--r--chromium/components/webdata/encryptor/README4
-rw-r--r--chromium/components/webdata/encryptor/encryptor.h51
-rw-r--r--chromium/components/webdata/encryptor/encryptor_mac.mm150
-rw-r--r--chromium/components/webdata/encryptor/encryptor_password_mac.h35
-rw-r--r--chromium/components/webdata/encryptor/encryptor_password_mac.mm79
-rw-r--r--chromium/components/webdata/encryptor/encryptor_password_mac_unittest.cc79
-rw-r--r--chromium/components/webdata/encryptor/encryptor_posix.cc139
-rw-r--r--chromium/components/webdata/encryptor/encryptor_unittest.cc143
-rw-r--r--chromium/components/webdata/encryptor/encryptor_win.cc65
-rw-r--r--chromium/components/webdata/encryptor/ie7_password.cc144
-rw-r--r--chromium/components/webdata/encryptor/ie7_password.h46
-rw-r--r--chromium/components/webdata/encryptor/ie7_password_unittest_win.cc60
533 files changed, 83010 insertions, 0 deletions
diff --git a/chromium/components/DEPS b/chromium/components/DEPS
new file mode 100644
index 00000000000..d6fbc7c41ed
--- /dev/null
+++ b/chromium/components/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+ # Do not add chrome/ as an allowed include. Components MUST NOT
+ # depend on anything under src/chrome.
+ "-chrome",
+
+ # Components should only depend on the public Content API, and on
+ # layers below the Content Module. They must not depend on the
+ # implementation of the Content Module.
+ #
+ # Subdirectories of e.g. src/components/component_name should add
+ # the additional parts of the Content API that they need,
+ # e.g. components/component_name/browser/DEPS would add a
+ # "+content/public/browser" rule.
+ "-content",
+ "+content/public/common",
+]
diff --git a/chromium/components/OWNERS b/chromium/components/OWNERS
new file mode 100644
index 00000000000..ff02f9e3125
--- /dev/null
+++ b/chromium/components/OWNERS
@@ -0,0 +1,46 @@
+joi@chromium.org
+
+per-file autofill*=dhollowa@chromium.org
+per-file autofill*=isherman@chromium.org
+per-file autofill*=kaiwang@chromium.org
+
+per-file breakpad.gypi=jochen@chromium.org
+per-file breakpad.gypi=rsesek@chromium.org
+per-file breakpad.gypi=thestig@chromium.org
+
+per-file json_schema.gypi=asargent@chromium.org
+per-file json_schema.gypi=calamity@chromium.org
+per-file json_schema.gypi=kalman@chromium.org
+per-file json_schema.gypi=koz@chromium.org
+per-file json_schema.gypi=mpcomplete@chromium.org
+
+per-file nacl*=bradchen@chromium.org
+per-file nacl*=bradnelson@chromium.org
+per-file nacl*=jvoung@chromium.org
+per-file nacl*=mseaborn@chromium.org
+per-file nacl*=noelallen@chromium.org
+per-file nacl*=sehr@chromium.org
+
+per-file navigation_interception.gypi=joth@chromium.org
+per-file navigation_interception.gypi=mkosiba@chromium.org
+
+per-file policy.gypi=mnissler@chromium.org
+per-file policy.gypi=pastarmovj@chromium.org
+per-file policy.gypi=joaodasilva@chromium.org
+per-file policy.gypi=bartfab@chromium.org
+per-file policy.gypi=atwilson@chromium.org
+per-file policy.gypi=pneubeck@chromium.org
+
+per-file sessions.gypi=marja@chromium.org
+per-file sessions.gypi=sky@chromium.org
+
+per-file tracing*=jbauman@chromium.org
+per-file tracing*=nduca@chromium.org
+
+per-file user_prefs.gypi=battre@chromium.org
+per-file user_prefs.gypi=bauerb@chromium.org
+per-file user_prefs.gypi=mnissler@chromium.org
+per-file user_prefs.gypi=pam@chromium.org
+
+per-file *.isolate=csharp@chromium.org
+per-file *.isolate=maruel@chromium.org
diff --git a/chromium/components/README b/chromium/components/README
new file mode 100644
index 00000000000..b20ca499c36
--- /dev/null
+++ b/chromium/components/README
@@ -0,0 +1,35 @@
+This directory is for components that have the Content Module as the
+uppermost layer they depend on. They may depend only on the Content
+API (content/public) and on lower layers (e.g. base/, net/, ipc/
+etc.).
+
+Components that have bits of code that need to live in different
+processes (e.g. some code in the browser process, some in the renderer
+process, etc.) should separate the code into different subdirectories.
+Hence for a component named 'foo' you might end up with a structure
+like the following:
+
+components/foo - DEPS, OWNERS, foo.gypi
+components/foo/browser - code that needs the browser process
+components/foo/common - for e.g. IPC constants and such
+components/foo/renderer - code that needs renderer process
+
+These subdirectories should have DEPS files with the relevant
+restrictions in place, i.e. only components/*/browser should
+be allowed to #include from content/public/browser.
+
+Note that there may also be an 'android' subdir, with a Java source
+code structure underneath it where the package name is
+org.chromium.components.foo, and with subdirs after 'foo'
+to illustrate process, e.g. 'browser' or 'renderer':
+
+components/foo/android/OWNERS, DEPS
+components/foo/android/java/src/org/chromium/components/foo/browser/
+components/foo/android/javatests/src/org/chromium/components/foo/browser/
+
+Code in a component should be placed in a namespace corresponding to
+the name of the component; e.g. for a component living in
+//components/foo, code in that component should be in the foo::
+namespace. Note that it used to be the rule that all code under
+//components should be in the components:: namespace; this is being
+phased out.
diff --git a/chromium/components/auto_login_parser.gypi b/chromium/components/auto_login_parser.gypi
new file mode 100644
index 00000000000..1dae9360602
--- /dev/null
+++ b/chromium/components/auto_login_parser.gypi
@@ -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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'auto_login_parser',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../net/net.gyp:net',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'auto_login_parser/auto_login_parser.cc',
+ 'auto_login_parser/auto_login_parser.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/auto_login_parser/DEPS b/chromium/components/auto_login_parser/DEPS
new file mode 100644
index 00000000000..8fa9d48d882
--- /dev/null
+++ b/chromium/components/auto_login_parser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+net",
+]
diff --git a/chromium/components/auto_login_parser/OWNERS b/chromium/components/auto_login_parser/OWNERS
new file mode 100644
index 00000000000..a7d929a2338
--- /dev/null
+++ b/chromium/components/auto_login_parser/OWNERS
@@ -0,0 +1 @@
+bulach@chromium.org
diff --git a/chromium/components/auto_login_parser/auto_login_parser.cc b/chromium/components/auto_login_parser/auto_login_parser.cc
new file mode 100644
index 00000000000..298c9b7acf3
--- /dev/null
+++ b/chromium/components/auto_login_parser/auto_login_parser.cc
@@ -0,0 +1,82 @@
+// 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/auto_login_parser/auto_login_parser.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_split.h"
+#include "net/base/escape.h"
+#include "net/url_request/url_request.h"
+
+namespace auto_login_parser {
+
+namespace {
+
+const char kHeaderName[] = "X-Auto-Login";
+
+bool MatchRealm(const std::string& realm, RealmRestriction restriction) {
+ switch (restriction) {
+ case ONLY_GOOGLE_COM:
+ return realm == "com.google";
+ case ALLOW_ANY_REALM:
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+} // namespace
+
+HeaderData::HeaderData() {}
+HeaderData::~HeaderData() {}
+
+bool ParseHeader(const std::string& header,
+ RealmRestriction realm_restriction,
+ HeaderData* header_data) {
+ // TODO(pliard): Investigate/fix potential internationalization issue. It
+ // seems that "account" from the x-auto-login header might contain non-ASCII
+ // characters.
+ if (header.empty())
+ return false;
+
+ std::vector<std::pair<std::string, std::string> > pairs;
+ if (!base::SplitStringIntoKeyValuePairs(header, '=', '&', &pairs))
+ return false;
+
+ // Parse the information from the |header| string.
+ HeaderData local_params;
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ const std::pair<std::string, std::string>& pair = pairs[i];
+ std::string unescaped_value(net::UnescapeURLComponent(
+ pair.second, net::UnescapeRule::URL_SPECIAL_CHARS));
+ if (pair.first == "realm") {
+ if (!MatchRealm(unescaped_value, realm_restriction))
+ return false;
+ local_params.realm = unescaped_value;
+ } else if (pair.first == "account") {
+ local_params.account = unescaped_value;
+ } else if (pair.first == "args") {
+ local_params.args = unescaped_value;
+ }
+ }
+ if (local_params.realm.empty() || local_params.args.empty())
+ return false;
+
+ *header_data = local_params;
+ return true;
+}
+
+bool ParserHeaderInResponse(net::URLRequest* request,
+ RealmRestriction realm_restriction,
+ HeaderData* header_data) {
+ std::string header_string;
+ request->GetResponseHeaderByName(kHeaderName, &header_string);
+ return ParseHeader(header_string, realm_restriction, header_data);
+}
+
+} // namespace auto_login_parser
diff --git a/chromium/components/auto_login_parser/auto_login_parser.h b/chromium/components/auto_login_parser/auto_login_parser.h
new file mode 100644
index 00000000000..bd2f28f9996
--- /dev/null
+++ b/chromium/components/auto_login_parser/auto_login_parser.h
@@ -0,0 +1,50 @@
+// 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_AUTO_LOGIN_PARSER_AUTO_LOGIN_PARSER_H_
+#define COMPONENTS_AUTO_LOGIN_PARSER_AUTO_LOGIN_PARSER_H_
+
+#include <string>
+
+namespace net {
+class URLRequest;
+}
+
+namespace auto_login_parser {
+
+enum RealmRestriction {
+ ONLY_GOOGLE_COM,
+ ALLOW_ANY_REALM
+};
+
+struct HeaderData {
+ HeaderData();
+ ~HeaderData();
+
+ // "realm" string from x-auto-login (e.g. "com.google").
+ std::string realm;
+
+ // "account" string from x-auto-login.
+ std::string account;
+
+ // "args" string from x-auto-login to be passed to MergeSession. This string
+ // should be considered opaque and not be cracked open to look inside.
+ std::string args;
+};
+
+// Returns whether parsing succeeded. Parameter |header_data| will not be
+// modified if parsing fails.
+bool ParseHeader(const std::string& header,
+ RealmRestriction realm_restriction,
+ HeaderData* header_data);
+
+// Helper function that also retrieves the header from the response of the
+// given URLRequest.
+bool ParserHeaderInResponse(net::URLRequest* request,
+ RealmRestriction realm_restriction,
+ HeaderData* header_data);
+
+} // namespace auto_login_parser
+
+#endif // COMPONENTS_AUTO_LOGIN_PARSER_AUTO_LOGIN_PARSER_H_
diff --git a/chromium/components/auto_login_parser/auto_login_parser_unittest.cc b/chromium/components/auto_login_parser/auto_login_parser_unittest.cc
new file mode 100644
index 00000000000..442784c734f
--- /dev/null
+++ b/chromium/components/auto_login_parser/auto_login_parser_unittest.cc
@@ -0,0 +1,92 @@
+// 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/auto_login_parser/auto_login_parser.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace auto_login_parser {
+
+class AutoLoginParserTest : public testing::Test {
+ protected:
+ static bool IsHeaderDataEmpty(const HeaderData& header) {
+ return header.realm.empty() && header.account.empty() &&
+ header.args.empty();
+ }
+};
+
+TEST_F(AutoLoginParserTest, ParseHeader) {
+ std::string header =
+ "realm=com.google&"
+ "account=fred.example%40gmail.com&"
+ "args=kfdshfwoeriudslkfsdjfhdskjfhsdkr";
+
+ HeaderData header_data;
+ EXPECT_TRUE(ParseHeader(header, ONLY_GOOGLE_COM, &header_data));
+
+ ASSERT_EQ("com.google", header_data.realm);
+ ASSERT_EQ("fred.example@gmail.com", header_data.account);
+ ASSERT_EQ("kfdshfwoeriudslkfsdjfhdskjfhsdkr", header_data.args);
+}
+
+TEST_F(AutoLoginParserTest, ParseHeaderOnlySupportsComGoogle) {
+ std::string header =
+ "realm=com.microsoft&"
+ "account=fred.example%40gmail.com&"
+ "args=kfdshfwoeriudslkfsdjfhdskjfhsdkr";
+
+ HeaderData header_data;
+ EXPECT_FALSE(ParseHeader(header, ONLY_GOOGLE_COM, &header_data));
+ // |header| should not be touched when parsing fails.
+ EXPECT_TRUE(IsHeaderDataEmpty(header_data));
+}
+
+TEST_F(AutoLoginParserTest, ParseHeaderWithMissingRealm) {
+ std::string header =
+ "account=fred.example%40gmail.com&"
+ "args=kfdshfwoeriudslkfsdjfhdskjfhsdkr";
+
+ HeaderData header_data;
+ EXPECT_FALSE(ParseHeader(header, ONLY_GOOGLE_COM, &header_data));
+ EXPECT_TRUE(IsHeaderDataEmpty(header_data));
+}
+
+TEST_F(AutoLoginParserTest, ParseHeaderWithMissingArgs) {
+ std::string header =
+ "realm=com.google&"
+ "account=fred.example%40gmail.com&";
+
+ HeaderData header_data;
+ EXPECT_FALSE(ParseHeader(header, ONLY_GOOGLE_COM, &header_data));
+ EXPECT_TRUE(IsHeaderDataEmpty(header_data));
+}
+
+TEST_F(AutoLoginParserTest, ParseHeaderWithoutOptionalAccount) {
+ std::string header =
+ "realm=com.google&"
+ "args=kfdshfwoeriudslkfsdjfhdskjfhsdkr";
+
+ HeaderData header_data;
+ EXPECT_TRUE(ParseHeader(header, ONLY_GOOGLE_COM, &header_data));
+ ASSERT_EQ("com.google", header_data.realm);
+ ASSERT_EQ("kfdshfwoeriudslkfsdjfhdskjfhsdkr", header_data.args);
+}
+
+TEST_F(AutoLoginParserTest, ParseHeaderAllowsAnyRealmWithOption) {
+ std::string header =
+ "realm=com.microsoft&"
+ "account=fred.example%40gmail.com&"
+ "args=kfdshfwoeriudslkfsdjfhdskjfhsdkr";
+
+ HeaderData header_data;
+ EXPECT_TRUE(ParseHeader(header, ALLOW_ANY_REALM, &header_data));
+
+ ASSERT_EQ("com.microsoft", header_data.realm);
+ ASSERT_EQ("fred.example@gmail.com", header_data.account);
+ ASSERT_EQ("kfdshfwoeriudslkfsdjfhdskjfhsdkr", header_data.args);
+}
+
+} // namespace auto_login_parser
diff --git a/chromium/components/autofill.gypi b/chromium/components/autofill.gypi
new file mode 100644
index 00000000000..92eccd3a1d6
--- /dev/null
+++ b/chromium/components/autofill.gypi
@@ -0,0 +1,408 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ # Private target only used in components/autofill.
+ 'target_name': 'autofill_regexes',
+ 'type': 'none',
+ 'actions': [{
+ 'action_name': 'autofill_regexes',
+ 'inputs': [
+ '<(DEPTH)/build/escape_unicode.py',
+ 'autofill/core/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/core/browser/autofill_regex_constants.cc.utf8'],
+ }],
+ },
+ ],
+ 'conditions': [
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ 'target_name': 'autofill_core_common',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_common',
+ '../ipc/ipc.gyp:ipc',
+ '../third_party/WebKit/public/blink.gyp:blink_minimal',
+ '../ui/ui.gyp:ui',
+ '../url/url.gyp:url_lib',
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'dependencies': [
+ 'autofill_jni_headers',
+ ],
+ }],
+ ],
+ 'include_dirs': [
+ '..',
+ '<(SHARED_INTERMEDIATE_DIR)/autofill'
+ ],
+ 'sources': [
+ 'autofill/core/browser/android/auxiliary_profile_loader_android.cc',
+ 'autofill/core/browser/android/auxiliary_profile_loader_android.h',
+ 'autofill/core/browser/android/auxiliary_profiles_android.cc',
+ 'autofill/core/browser/android/auxiliary_profiles_android.h',
+ 'autofill/core/browser/android/component_jni_registrar.cc',
+ 'autofill/core/browser/android/component_jni_registrar.h',
+ 'autofill/core/browser/android/personal_data_manager_android.cc',
+ 'autofill/core/common/autocheckout_status.h',
+ 'autofill/core/common/autofill_constants.cc',
+ 'autofill/core/common/autofill_constants.h',
+ 'autofill/core/common/autofill_messages.h',
+ 'autofill/core/common/autofill_message_generator.cc',
+ 'autofill/core/common/autofill_message_generator.h',
+ 'autofill/core/common/autofill_pref_names.cc',
+ 'autofill/core/common/autofill_pref_names.h',
+ 'autofill/core/common/autofill_switches.cc',
+ 'autofill/core/common/autofill_switches.h',
+ 'autofill/core/common/form_data.cc',
+ 'autofill/core/common/form_data.h',
+ 'autofill/core/common/form_data_predictions.cc',
+ 'autofill/core/common/form_data_predictions.h',
+ 'autofill/core/common/form_field_data.cc',
+ 'autofill/core/common/form_field_data.h',
+ 'autofill/core/common/form_field_data_predictions.cc',
+ 'autofill/core/common/form_field_data_predictions.h',
+ 'autofill/core/common/password_form_fill_data.cc',
+ 'autofill/core/common/password_form_fill_data.h',
+ 'autofill/core/common/password_generation_util.cc',
+ 'autofill/core/common/password_generation_util.h',
+ 'autofill/core/common/web_element_descriptor.cc',
+ 'autofill/core/common/web_element_descriptor.h',
+ ],
+ },
+
+ {
+ 'target_name': 'autofill_core_browser',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ 'autofill_core_common',
+ 'autofill_regexes',
+ 'encryptor',
+ 'user_prefs',
+ 'webdata_common',
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../base/base.gyp:base_prefs',
+ '../content/content.gyp:content_browser',
+ '../content/content.gyp:content_common',
+ '../google_apis/google_apis.gyp:google_apis',
+ '../ipc/ipc.gyp:ipc',
+ '../skia/skia.gyp:skia',
+ '../sql/sql.gyp:sql',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../third_party/libjingle/libjingle.gyp:libjingle',
+ '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
+ '../ui/ui.gyp:ui',
+ '../url/url.gyp:url_lib',
+ '../webkit/webkit_resources.gyp:webkit_resources',
+
+ 'component_strings.gyp:component_strings',
+ ],
+ 'sources': [
+ 'autofill/core/browser/address.cc',
+ 'autofill/core/browser/address.h',
+ 'autofill/core/browser/address_field.cc',
+ 'autofill/core/browser/address_field.h',
+ 'autofill/core/browser/autocomplete_history_manager.cc',
+ 'autofill/core/browser/autocomplete_history_manager.h',
+ 'autofill/core/browser/autofill-inl.h',
+ 'autofill/core/browser/autofill_country.cc',
+ 'autofill/core/browser/autofill_country.h',
+ 'autofill/core/browser/autofill_data_model.cc',
+ 'autofill/core/browser/autofill_data_model.h',
+ 'autofill/core/browser/autofill_download.cc',
+ 'autofill/core/browser/autofill_download.h',
+ 'autofill/core/browser/autofill_download_url.cc',
+ 'autofill/core/browser/autofill_download_url.h',
+ 'autofill/core/browser/autofill_driver.h',
+ 'autofill/core/browser/autofill_external_delegate.cc',
+ 'autofill/core/browser/autofill_external_delegate.h',
+ 'autofill/core/browser/autofill_field.cc',
+ 'autofill/core/browser/autofill_field.h',
+ 'autofill/core/browser/autofill_ie_toolbar_import_win.cc',
+ 'autofill/core/browser/autofill_ie_toolbar_import_win.h',
+ 'autofill/core/browser/autofill_manager.cc',
+ 'autofill/core/browser/autofill_manager.h',
+ 'autofill/core/browser/autofill_manager_delegate.h',
+ 'autofill/core/browser/autofill_manager_test_delegate.h',
+ 'autofill/core/browser/autofill_metrics.cc',
+ 'autofill/core/browser/autofill_metrics.h',
+ 'autofill/core/browser/autofill_popup_delegate.h',
+ 'autofill/core/browser/autofill_profile.cc',
+ 'autofill/core/browser/autofill_profile.h',
+ 'autofill/core/browser/autofill_regex_constants.cc.utf8',
+ 'autofill/core/browser/autofill_regex_constants.h',
+ 'autofill/core/browser/autofill_regexes.cc',
+ 'autofill/core/browser/autofill_regexes.h',
+ 'autofill/core/browser/autofill_scanner.cc',
+ 'autofill/core/browser/autofill_scanner.h',
+ 'autofill/core/browser/autofill_server_field_info.h',
+ 'autofill/core/browser/autofill_type.cc',
+ 'autofill/core/browser/autofill_type.h',
+ 'autofill/core/browser/autofill_xml_parser.cc',
+ 'autofill/core/browser/autofill_xml_parser.h',
+ 'autofill/core/browser/contact_info.cc',
+ 'autofill/core/browser/contact_info.h',
+ 'autofill/core/browser/credit_card.cc',
+ 'autofill/core/browser/credit_card.h',
+ 'autofill/core/browser/credit_card_field.cc',
+ 'autofill/core/browser/credit_card_field.h',
+ 'autofill/core/browser/email_field.cc',
+ 'autofill/core/browser/email_field.h',
+ 'autofill/core/browser/field_types.h',
+ 'autofill/core/browser/form_field.cc',
+ 'autofill/core/browser/form_field.h',
+ 'autofill/core/browser/form_group.cc',
+ 'autofill/core/browser/form_group.h',
+ 'autofill/core/browser/form_structure.cc',
+ 'autofill/core/browser/form_structure.h',
+ 'autofill/core/browser/name_field.cc',
+ 'autofill/core/browser/name_field.h',
+ 'autofill/core/browser/password_autofill_manager.cc',
+ 'autofill/core/browser/password_autofill_manager.h',
+ 'autofill/core/browser/password_generator.cc',
+ 'autofill/core/browser/password_generator.h',
+ 'autofill/core/browser/personal_data_manager.cc',
+ 'autofill/core/browser/personal_data_manager.h',
+ 'autofill/core/browser/personal_data_manager_mac.mm',
+ 'autofill/core/browser/personal_data_manager_observer.h',
+ 'autofill/core/browser/phone_field.cc',
+ 'autofill/core/browser/phone_field.h',
+ 'autofill/core/browser/phone_number.cc',
+ 'autofill/core/browser/phone_number.h',
+ 'autofill/core/browser/phone_number_i18n.cc',
+ 'autofill/core/browser/phone_number_i18n.h',
+ 'autofill/core/browser/state_names.cc',
+ 'autofill/core/browser/state_names.h',
+ 'autofill/core/browser/validation.cc',
+ 'autofill/core/browser/validation.h',
+ 'autofill/core/browser/webdata/autofill_change.cc',
+ 'autofill/core/browser/webdata/autofill_change.h',
+ 'autofill/core/browser/webdata/autofill_entry.cc',
+ 'autofill/core/browser/webdata/autofill_entry.h',
+ 'autofill/core/browser/webdata/autofill_table.cc',
+ 'autofill/core/browser/webdata/autofill_table.h',
+ 'autofill/core/browser/webdata/autofill_webdata.h',
+ 'autofill/core/browser/webdata/autofill_webdata_backend.h',
+ 'autofill/core/browser/webdata/autofill_webdata_backend_impl.cc',
+ 'autofill/core/browser/webdata/autofill_webdata_backend_impl.h',
+ 'autofill/core/browser/webdata/autofill_webdata_service.cc',
+ 'autofill/core/browser/webdata/autofill_webdata_service.h',
+ 'autofill/core/browser/webdata/autofill_webdata_service_observer.h',
+
+ # This file is generated by the autofill_regexes action.
+ '<(SHARED_INTERMEDIATE_DIR)/autofill_regex_constants.cc',
+ ],
+
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
+ },
+
+ {
+ 'target_name': 'autofill_core_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'autofill_core_common',
+ 'autofill_core_browser',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'autofill/core/browser/android/test_auxiliary_profile_loader_android.cc',
+ 'autofill/core/browser/android/test_auxiliary_profile_loader_android.h',
+ 'autofill/core/browser/autofill_common_test.cc',
+ 'autofill/core/browser/autofill_common_test.h',
+ 'autofill/core/browser/data_driven_test.cc',
+ 'autofill/core/browser/data_driven_test.h',
+ 'autofill/core/browser/test_autofill_driver.cc',
+ 'autofill/core/browser/test_autofill_driver.h',
+ 'autofill/core/browser/test_autofill_external_delegate.cc',
+ 'autofill/core/browser/test_autofill_external_delegate.h',
+ 'autofill/core/browser/test_autofill_manager_delegate.cc',
+ 'autofill/core/browser/test_autofill_manager_delegate.h',
+ 'autofill/core/browser/test_personal_data_manager.cc',
+ 'autofill/core/browser/test_personal_data_manager.h',
+ ],
+ },
+
+ {
+ # Protobuf compiler / generate rule for Autofill's risk integration.
+ 'target_name': 'autofill_content_risk_proto',
+ 'type': 'static_library',
+ 'sources': [
+ 'autofill/content/browser/risk/proto/fingerprint.proto',
+ ],
+ 'variables': {
+ 'proto_in_dir': 'autofill/content/browser/risk/proto',
+ 'proto_out_dir': 'components/autofill/content/browser/risk/proto',
+ },
+ 'includes': [ '../build/protoc.gypi' ]
+ },
+ {
+ 'target_name': 'autofill_content_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gmock.gyp:gmock',
+ ],
+ 'sources': [
+ 'autofill/content/browser/wallet/mock_wallet_client.cc',
+ 'autofill/content/browser/wallet/mock_wallet_client.h',
+ 'autofill/content/browser/wallet/wallet_test_util.cc',
+ 'autofill/content/browser/wallet/wallet_test_util.h',
+ ],
+ 'include_dirs': [ '..' ],
+ },
+ {
+ 'target_name': 'autofill_content_browser',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ 'autofill_content_risk_proto',
+ 'autofill_core_browser',
+ 'autofill_core_common',
+ 'autofill_regexes',
+ 'encryptor',
+ 'user_prefs',
+ 'webdata_common',
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../base/base.gyp:base_prefs',
+ '../content/content.gyp:content_browser',
+ '../content/content.gyp:content_common',
+ '../google_apis/google_apis.gyp:google_apis',
+ '../ipc/ipc.gyp:ipc',
+ '../skia/skia.gyp:skia',
+ '../sql/sql.gyp:sql',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../third_party/libjingle/libjingle.gyp:libjingle',
+ '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
+ '../ui/ui.gyp:ui',
+ '../url/url.gyp:url_lib',
+ '../webkit/webkit_resources.gyp:webkit_resources',
+
+ 'component_strings.gyp:component_strings',
+ ],
+ 'sources': [
+ 'autofill/content/browser/autocheckout/whitelist_manager.cc',
+ 'autofill/content/browser/autocheckout/whitelist_manager.h',
+ 'autofill/content/browser/autocheckout_manager.cc',
+ 'autofill/content/browser/autocheckout_manager.h',
+ 'autofill/content/browser/autocheckout_page_meta_data.cc',
+ 'autofill/content/browser/autocheckout_page_meta_data.h',
+ 'autofill/content/browser/autocheckout_request_manager.cc',
+ 'autofill/content/browser/autocheckout_request_manager.h',
+ 'autofill/content/browser/autocheckout_statistic.cc',
+ 'autofill/content/browser/autocheckout_statistic.h',
+ 'autofill/content/browser/autocheckout_steps.h',
+ 'autofill/content/browser/autofill_driver_impl.cc',
+ 'autofill/content/browser/autofill_driver_impl.h',
+ 'autofill/content/browser/risk/fingerprint.cc',
+ 'autofill/content/browser/risk/fingerprint.h',
+ 'autofill/content/browser/wallet/form_field_error.cc',
+ 'autofill/content/browser/wallet/form_field_error.h',
+ 'autofill/content/browser/wallet/full_wallet.cc',
+ 'autofill/content/browser/wallet/full_wallet.h',
+ 'autofill/content/browser/wallet/instrument.cc',
+ 'autofill/content/browser/wallet/instrument.h',
+ 'autofill/content/browser/wallet/required_action.cc',
+ 'autofill/content/browser/wallet/required_action.h',
+ 'autofill/content/browser/wallet/wallet_address.cc',
+ 'autofill/content/browser/wallet/wallet_address.h',
+ 'autofill/content/browser/wallet/wallet_client.cc',
+ 'autofill/content/browser/wallet/wallet_client.h',
+ 'autofill/content/browser/wallet/wallet_client_delegate.h',
+ 'autofill/content/browser/wallet/wallet_items.cc',
+ 'autofill/content/browser/wallet/wallet_items.h',
+ 'autofill/content/browser/wallet/wallet_service_url.cc',
+ 'autofill/content/browser/wallet/wallet_service_url.h',
+ 'autofill/content/browser/wallet/wallet_signin_helper.cc',
+ 'autofill/content/browser/wallet/wallet_signin_helper.h',
+ ],
+
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
+ },
+
+ {
+ 'target_name': 'autofill_content_renderer',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ 'autofill_core_common',
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_renderer',
+ '../content/content.gyp:content_common',
+ '../ipc/ipc.gyp:ipc',
+ '../skia/skia.gyp:skia',
+
+ 'component_strings.gyp:component_strings',
+ ],
+ 'sources': [
+ 'autofill/content/renderer/autofill_agent.cc',
+ 'autofill/content/renderer/autofill_agent.h',
+ 'autofill/content/renderer/form_autofill_util.cc',
+ 'autofill/content/renderer/form_autofill_util.h',
+ 'autofill/content/renderer/form_cache.cc',
+ 'autofill/content/renderer/form_cache.h',
+ 'autofill/content/renderer/page_click_listener.h',
+ 'autofill/content/renderer/page_click_tracker.cc',
+ 'autofill/content/renderer/page_click_tracker.h',
+ 'autofill/content/renderer/password_autofill_agent.cc',
+ 'autofill/content/renderer/password_autofill_agent.h',
+ 'autofill/content/renderer/password_generation_manager.cc',
+ 'autofill/content/renderer/password_generation_manager.h',
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
+ },
+ ],
+ }],
+ ['OS == "android"', {
+ 'targets': [
+ {
+ 'target_name': 'autofill_java',
+ 'type': 'none',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_java',
+ ],
+ 'variables': {
+ 'java_in_dir': 'autofill/core/browser/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'autofill_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'autofill',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/autofill/DEPS b/chromium/components/autofill/DEPS
new file mode 100644
index 00000000000..e136eaf419b
--- /dev/null
+++ b/chromium/components/autofill/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+google_apis/gaia/gaia_urls.h",
+ "+grit", # For generated headers
+ "+jni",
+ "+net",
+ "+ui",
+ # Autofill is a layered component; subdirectories must explicitly introduce
+ # the ability to use the content layer as appropriate.
+ "-components/autofill/content",
+ "-content/public/common",
+]
diff --git a/chromium/components/autofill/OWNERS b/chromium/components/autofill/OWNERS
new file mode 100644
index 00000000000..af7b445d2a8
--- /dev/null
+++ b/chromium/components/autofill/OWNERS
@@ -0,0 +1,8 @@
+estade@chromium.org
+isherman@chromium.org
+
+# Owner for password autofill/generation only.
+gcasto@chromium.org
+
+# Temporary owner, for refactoring changes only.
+joi@chromium.org
diff --git a/chromium/components/autofill/README b/chromium/components/autofill/README
new file mode 100644
index 00000000000..44be6bfe04c
--- /dev/null
+++ b/chromium/components/autofill/README
@@ -0,0 +1,23 @@
+Autofill is in the process of becoming a layered component
+(https://sites.google.com/a/chromium.org/dev/developers/design-documents/layered-components-design)
+to enable it to be shared cleanly on iOS.
+
+When this process is complete, this component will have the following structure:
+
+- core/: shared code that does not depend on src/content/ or src/ios/
+ - browser/: Browser process code
+ - common/: Code shared by the browser and the renderer
+- content/: Driver for the shared code based on the content layer.
+ - browser/: Browser process code.
+ - renderer/: Renderer process code.
+ - common/: Code shared by the browser and the renderer.
+- ios/: Driver for the shared code based on src/ios.
+
+See
+https://sites.google.com/a/chromium.org/dev/developers/design-documents/layered-components-technical-approach/making-autofill-into-a-layered-component
+for an outline of the project.
+
+For pointers on how to continue getting your work done as the component moves
+into its new structure, see
+https://sites.google.com/a/chromium.org/dev/developers/design-documents/layered-components-technical-approach/making-autofill-into-a-layered-component#TOC-Help-How-Do-I-Get-My-Autofill-Related-Work-Done-
+
diff --git a/chromium/components/autofill/content/DEPS b/chromium/components/autofill/content/DEPS
new file mode 100644
index 00000000000..bd7656f8b4e
--- /dev/null
+++ b/chromium/components/autofill/content/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+content/public/common",
+ # Allow inclusion of WebKit API files.
+ "+third_party/WebKit/public/platform",
+ "+third_party/WebKit/public/web",
+]
diff --git a/chromium/components/autofill/content/browser/DEPS b/chromium/components/autofill/content/browser/DEPS
new file mode 100644
index 00000000000..fb768b60c28
--- /dev/null
+++ b/chromium/components/autofill/content/browser/DEPS
@@ -0,0 +1,41 @@
+include_rules = [
+ "+components/webdata/common",
+ "+content/public/browser",
+ "+crypto/random.h",
+ "+google_apis/gaia",
+ "+google_apis/google_api_keys.h",
+ "+gpu/config/gpu_info.h",
+ "+net",
+ "+sql",
+ "+third_party/libjingle",
+ "+third_party/libphonenumber", # For phone number i18n.
+]
+
+specific_include_rules = {
+ '.*_[a-z]*test\.cc': [
+ "+content/public/test",
+ ],
+
+ # TODO(joi): Removing these dependencies needs to wait until some
+ # other things (AutofillWebData::FromBrowserContext and a few other
+ # things) move out of being built in //chrome. If we break the
+ # dependency on ChromeRenderViewHostTestHarness now (by switching to
+ # content::RenderViewHostTestHarness) but leave the test running in
+ # the 'unit_tests' target, it will fail at runtime trying to cast a
+ # plain BrowserContext to a Profile. If on the other hand we move it
+ # to the 'components_unittests' target, it will at this point fail
+ # to build due to a few link-time dependencies.
+ 'autocheckout_manager_unittest.cc': [
+ "!chrome/test/base/chrome_render_view_host_test_harness.h",
+ "!chrome/test/base/testing_profile.h",
+ ],
+ 'autofill_driver_impl_unittest.cc': [
+ "!chrome/test/base/chrome_render_view_host_test_harness.h",
+ ],
+ 'wallet_client_unittest.cc': [
+ "!chrome/test/base/testing_profile.h"
+ ],
+ 'wallet_signin_helper_unittest.cc': [
+ "!chrome/test/base/testing_profile.h"
+ ],
+}
diff --git a/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc
new file mode 100644
index 00000000000..7d5423adc8d
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc
@@ -0,0 +1,205 @@
+// 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/content/browser/autocheckout/whitelist_manager.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "content/public/browser/browser_context.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"
+#include "url/gurl.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 net::BackoffEntry::Policy kBackoffPolicy = {
+ // Number of initial errors to ignore before starting to back off.
+ 0,
+
+ // Initial delay in ms: 3 seconds.
+ 3000,
+
+ // Factor by which the waiting time is multiplied.
+ 6,
+
+ // Fuzzing percentage: no fuzzing logic.
+ 0,
+
+ // Maximum delay in ms: 1 hour.
+ 1000 * 60 * 60,
+
+ // When to discard an entry: 3 hours.
+ 1000 * 60 * 60 * 3,
+
+ // |always_use_initial_delay|; false means that the initial delay is
+ // applied after the first error, and starts backing off from there.
+ false,
+};
+
+const char kDefaultWhitelistUrl[] =
+ "https://www.gstatic.com/commerce/autocheckout/whitelist.csv";
+
+const char kWhiteListKeyName[] = "autocheckout_whitelist_manager";
+
+std::string GetWhitelistUrl() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ std::string whitelist_url = command_line.GetSwitchValueASCII(
+ autofill::switches::kAutocheckoutWhitelistUrl);
+
+ return whitelist_url.empty() ? kDefaultWhitelistUrl : whitelist_url;
+}
+
+} // namespace
+
+
+namespace autofill {
+namespace autocheckout {
+
+WhitelistManager::WhitelistManager()
+ : callback_is_pending_(false),
+ experimental_form_filling_enabled_(
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalFormFilling) ||
+ base::FieldTrialList::FindFullName("Autocheckout") == "Yes"),
+ bypass_autocheckout_whitelist_(
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kBypassAutocheckoutWhitelist)),
+ retry_entry_(&kBackoffPolicy) {
+}
+
+WhitelistManager::~WhitelistManager() {}
+
+void WhitelistManager::Init(net::URLRequestContextGetter* context_getter) {
+ DCHECK(context_getter);
+ context_getter_ = context_getter;
+ ScheduleDownload(base::TimeDelta::FromSeconds(kInitialDownloadDelaySeconds));
+}
+
+void WhitelistManager::ScheduleDownload(base::TimeDelta interval) {
+ 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);
+}
+
+void WhitelistManager::StartDownloadTimer(base::TimeDelta interval) {
+ download_timer_.Start(FROM_HERE,
+ interval,
+ this,
+ &WhitelistManager::TriggerDownload);
+}
+
+const AutofillMetrics& WhitelistManager::GetMetricLogger() const {
+ return metrics_logger_;
+}
+
+void WhitelistManager::TriggerDownload() {
+ callback_is_pending_ = true;
+
+ request_started_timestamp_ = base::Time::Now();
+
+ request_.reset(net::URLFetcher::Create(
+ 0, GURL(GetWhitelistUrl()), 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());
+
+ AutofillMetrics::AutocheckoutWhitelistDownloadStatus status;
+ base::TimeDelta duration = base::Time::Now() - request_started_timestamp_;
+
+ // Refresh the whitelist after kDownloadIntervalSeconds (24 hours).
+ base::TimeDelta next_download_time =
+ base::TimeDelta::FromSeconds(kDownloadIntervalSeconds);
+
+ if (source->GetResponseCode() == net::HTTP_OK) {
+ std::string data;
+ source->GetResponseAsString(&data);
+ BuildWhitelist(data);
+ status = AutofillMetrics::AUTOCHECKOUT_WHITELIST_DOWNLOAD_SUCCEEDED;
+ retry_entry_.Reset();
+ } else {
+ status = AutofillMetrics::AUTOCHECKOUT_WHITELIST_DOWNLOAD_FAILED;
+ retry_entry_.InformOfRequest(false);
+ if (!retry_entry_.CanDiscard())
+ next_download_time = retry_entry_.GetTimeUntilRelease();
+ }
+
+ GetMetricLogger().LogAutocheckoutWhitelistDownloadDuration(duration, status);
+ ScheduleDownload(next_download_time);
+}
+
+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 bypass_autocheckout_whitelist_ ? url.spec() : 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/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.h b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.h
new file mode 100644
index 00000000000..40c16f5d697
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.h
@@ -0,0 +1,111 @@
+// 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_CONTENT_BROWSER_AUTOCHECKOUT_WHITELIST_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_WHITELIST_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/timer/timer.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "net/base/backoff_entry.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:
+ WhitelistManager();
+ virtual ~WhitelistManager();
+
+ // Schedule a fetch of the Autocheckout whitelist file if it's not already
+ // loaded. This helps ensure that the whitelist will be available by the time
+ // the user navigates to a form on which Autocheckout should be enabled.
+ void Init(net::URLRequestContextGetter* context_getter);
+
+ // 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:
+ // Schedules a future call to TriggerDownload if one isn't already pending.
+ virtual void ScheduleDownload(base::TimeDelta interval);
+
+ // Start the download timer. It is called by ScheduleDownload(), and exposed
+ // as a separate method for mocking out in tests.
+ virtual void StartDownloadTimer(base::TimeDelta interval);
+
+ // Returns the |AutofillMetrics| instance that should be used for logging
+ // Autocheckout whitelist file downloading.
+ virtual const AutofillMetrics& GetMetricLogger() const;
+
+ // 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* context_getter_; // WEAK
+
+ // State of the kEnableExperimentalFormFilling flag.
+ const bool experimental_form_filling_enabled_;
+
+ // State of the kBypassAutocheckoutWhitelist flag.
+ const bool bypass_autocheckout_whitelist_;
+
+ // Exponential back-off delay to retry a failed download.
+ net::BackoffEntry retry_entry_;
+
+ // Logger for UMA metrics.
+ AutofillMetrics metrics_logger_;
+
+ // When the whitelist download started. Used to track download latency.
+ base::Time request_started_timestamp_;
+
+ // The request object.
+ scoped_ptr<net::URLFetcher> request_;
+
+ DISALLOW_COPY_AND_ASSIGN(WhitelistManager);
+};
+
+} // namespace autocheckout
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_WHITELIST_MANAGER_H_
+
diff --git a/chromium/components/autofill/content/browser/autocheckout/whitelist_manager_unittest.cc b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager_unittest.cc
new file mode 100644
index 00000000000..304d8bf02b8
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager_unittest.cc
@@ -0,0 +1,309 @@
+// 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 <cmath>
+
+#include "base/command_line.h"
+#include "base/format_macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
+#include "components/autofill/content/browser/autocheckout/whitelist_manager.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "content/public/test/test_browser_thread_bundle.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"
+#include "url/gurl.h"
+
+namespace {
+
+const int64 kTestDownloadInterval = 3; // 3 seconds
+
+// First 4 retry delays are 3, 18, 108, 648 seconds, and following retries
+// capped at one hour.
+const int64 kBackoffDelaysInMs[] = {
+ 3000, 18000, 108000, 648000, 3600000, 3600000 };
+
+const char kDownloadWhitelistResponse[] =
+ "https://www.merchant1.com/checkout/\n"
+ "https://cart.merchant2.com/";
+
+} // namespace
+
+namespace autofill {
+namespace autocheckout {
+
+class WhitelistManagerTest;
+
+class MockAutofillMetrics : public AutofillMetrics {
+ public:
+ MockAutofillMetrics() {}
+ MOCK_CONST_METHOD2(LogAutocheckoutWhitelistDownloadDuration,
+ void(const base::TimeDelta& duration,
+ AutofillMetrics::AutocheckoutWhitelistDownloadStatus));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics);
+};
+
+class TestWhitelistManager : public WhitelistManager {
+ public:
+ TestWhitelistManager()
+ : WhitelistManager(),
+ did_start_download_timer_(false) {}
+
+ virtual void ScheduleDownload(base::TimeDelta interval) OVERRIDE {
+ did_start_download_timer_ = false;
+ download_interval_ = interval;
+ return WhitelistManager::ScheduleDownload(interval);
+ }
+
+ virtual void StartDownloadTimer(base::TimeDelta interval) OVERRIDE {
+ WhitelistManager::StartDownloadTimer(interval);
+ 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 base::TimeDelta& download_interval() const {
+ return download_interval_;
+ }
+
+ const std::vector<std::string>& url_prefixes() const {
+ return WhitelistManager::url_prefixes();
+ }
+
+ virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE {
+ return mock_metrics_logger_;
+ }
+
+ private:
+ bool did_start_download_timer_;
+ base::TimeDelta download_interval_;
+
+ MockAutofillMetrics mock_metrics_logger_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWhitelistManager);
+};
+
+class WhitelistManagerTest : public testing::Test {
+ public:
+ WhitelistManagerTest()
+ : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
+
+ protected:
+ void CreateWhitelistManager() {
+ if (!whitelist_manager_.get()) {
+ whitelist_manager_.reset(new TestWhitelistManager());
+ }
+ }
+
+ void DownloadWhitelist(int response_code, const std::string& response) {
+ // Create and register factory.
+ net::TestURLFetcherFactory factory;
+
+ CreateWhitelistManager();
+
+ AutofillMetrics::AutocheckoutWhitelistDownloadStatus status;
+ if (response_code == net::HTTP_OK)
+ status = AutofillMetrics::AUTOCHECKOUT_WHITELIST_DOWNLOAD_SUCCEEDED;
+ else
+ status = AutofillMetrics::AUTOCHECKOUT_WHITELIST_DOWNLOAD_FAILED;
+ EXPECT_CALL(
+ static_cast<const MockAutofillMetrics&>(
+ whitelist_manager_->GetMetricLogger()),
+ LogAutocheckoutWhitelistDownloadDuration(testing::_, status)).Times(1);
+
+ 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);
+ }
+
+ protected:
+ scoped_ptr<TestWhitelistManager> whitelist_manager_;
+
+ private:
+ content::TestBrowserThreadBundle thread_bundle_;
+};
+
+TEST_F(WhitelistManagerTest, DownloadWhitelist) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ ASSERT_EQ(2U, whitelist_manager_->url_prefixes().size());
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ whitelist_manager_->url_prefixes()[0]);
+ EXPECT_EQ("https://cart.merchant2.com/",
+ whitelist_manager_->url_prefixes()[1]);
+}
+
+TEST_F(WhitelistManagerTest, DoNotDownloadWhitelistWhenSwitchIsOff) {
+ CreateWhitelistManager();
+ whitelist_manager_->ScheduleDownload(
+ base::TimeDelta::FromSeconds(kTestDownloadInterval));
+ EXPECT_FALSE(whitelist_manager_->did_start_download_timer());
+}
+
+TEST_F(WhitelistManagerTest, DoNotDownloadWhitelistIfAlreadyScheduled) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ CreateWhitelistManager();
+ // First attempt should schedule a download.
+ whitelist_manager_->ScheduleDownload(
+ base::TimeDelta::FromSeconds(kTestDownloadInterval));
+ EXPECT_TRUE(whitelist_manager_->did_start_download_timer());
+ // Second attempt should NOT schedule a download while there is already one.
+ whitelist_manager_->ScheduleDownload(
+ base::TimeDelta::FromSeconds(kTestDownloadInterval));
+ EXPECT_FALSE(whitelist_manager_->did_start_download_timer());
+ // It should schedule a new download when not in backoff mode.
+ whitelist_manager_->StopDownloadTimer();
+ whitelist_manager_->ScheduleDownload(
+ base::TimeDelta::FromSeconds(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, whitelist_manager_->url_prefixes().size());
+
+ whitelist_manager_->StopDownloadTimer();
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, whitelist_manager_->url_prefixes().size());
+
+ whitelist_manager_->StopDownloadTimer();
+ DownloadWhitelist(net::HTTP_INTERNAL_SERVER_ERROR,
+ kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, whitelist_manager_->url_prefixes().size());
+}
+
+TEST_F(WhitelistManagerTest, DownloadWhitelistRetry) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+
+ for (size_t i = 0; i < arraysize(kBackoffDelaysInMs); ++i) {
+ DownloadWhitelist(net::HTTP_INTERNAL_SERVER_ERROR,
+ kDownloadWhitelistResponse);
+ SCOPED_TRACE(base::StringPrintf("Testing retry %" PRIuS
+ ", expecting delay: %" PRId64,
+ i,
+ kBackoffDelaysInMs[i]));
+ EXPECT_EQ(
+ kBackoffDelaysInMs[i],
+ whitelist_manager_->download_interval().InMillisecondsRoundedUp());
+ }
+}
+
+TEST_F(WhitelistManagerTest, GetMatchedURLPrefix) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, whitelist_manager_->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/")));
+}
+
+TEST_F(WhitelistManagerTest, BypassWhitelist) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalFormFilling);
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kBypassAutocheckoutWhitelist);
+ DownloadWhitelist(net::HTTP_OK, kDownloadWhitelistResponse);
+ EXPECT_EQ(2U, whitelist_manager_->url_prefixes().size());
+
+ // Empty url.
+ EXPECT_EQ(std::string(),
+ whitelist_manager_->GetMatchedURLPrefix(GURL(std::string())));
+ // Positive tests.
+ EXPECT_EQ("https://www.merchant1.com/checkout/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://www.merchant1.com/checkout/")));
+ EXPECT_EQ("https://cart.merchant2.com/",
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://cart.merchant2.com/ShippingInfo?a=b&c=d")));
+ // Bypass other urls.
+ EXPECT_EQ(std::string("https://bypass.me/"),
+ whitelist_manager_->GetMatchedURLPrefix(
+ GURL("https://bypass.me/")));
+}
+
+} // namespace autocheckout
+} // namespace autofill
+
diff --git a/chromium/components/autofill/content/browser/autocheckout_manager.cc b/chromium/components/autofill/content/browser/autocheckout_manager.cc
new file mode 100644
index 00000000000..be8192362e8
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_manager.cc
@@ -0,0 +1,581 @@
+// 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/content/browser/autocheckout_manager.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/browser/autocheckout_request_manager.h"
+#include "components/autofill/content/browser/autocheckout_statistic.h"
+#include "components/autofill/content/browser/autocheckout_steps.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/web_element_descriptor.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "net/cookies/cookie_options.h"
+#include "net/cookies/cookie_store.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "ui/gfx/rect.h"
+#include "url/gurl.h"
+
+using content::RenderViewHost;
+using content::WebContents;
+
+namespace autofill {
+
+namespace {
+
+const char kGoogleAccountsUrl[] = "https://accounts.google.com/";
+
+// 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 = base::string16();
+ field.value = base::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("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 address-line1"));
+ formdata.fields.push_back(BuildField("billing address-line2"));
+ 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("billing tel"));
+ formdata.fields.push_back(BuildField("shipping name"));
+ formdata.fields.push_back(BuildField("shipping address-line1"));
+ formdata.fields.push_back(BuildField("shipping address-line2"));
+ 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"));
+ formdata.fields.push_back(BuildField("shipping tel"));
+ return formdata;
+}
+
+AutofillMetrics::AutocheckoutBuyFlowMetric AutocheckoutStatusToUmaMetric(
+ AutocheckoutStatus status) {
+ switch (status) {
+ case SUCCESS:
+ return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_SUCCESS;
+ case MISSING_FIELDMAPPING:
+ return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_FIELDMAPPING;
+ case MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING:
+ return AutofillMetrics::
+ AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING;
+ case MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING:
+ return AutofillMetrics::
+ AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING;
+ case MISSING_ADVANCE:
+ return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_ADVANCE_ELEMENT;
+ case CANNOT_PROCEED:
+ return AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_CANNOT_PROCEED;
+ case AUTOCHECKOUT_STATUS_NUM_STATUS:
+ NOTREACHED();
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::NUM_AUTOCHECKOUT_BUY_FLOW_METRICS;
+}
+
+// Callback for retrieving Google Account cookies. |callback| is passed the
+// retrieved cookies and posted back to the UI thread. |cookies| is any Google
+// Account cookies.
+void GetGoogleCookiesCallback(
+ const base::Callback<void(const std::string&)>& callback,
+ const std::string& cookies) {
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback, cookies));
+}
+
+// Gets Google Account cookies. Must be called on the IO thread.
+// |request_context_getter| is a getter for the current request context.
+// |callback| is called when retrieving cookies is completed.
+void GetGoogleCookies(
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+ const base::Callback<void(const std::string&)>& callback) {
+ net::URLRequestContext* url_request_context =
+ request_context_getter->GetURLRequestContext();
+ if (!url_request_context)
+ return;
+
+ net::CookieStore* cookie_store = url_request_context->cookie_store();
+
+ base::Callback<void(const std::string&)> cookie_callback = base::Bind(
+ &GetGoogleCookiesCallback,
+ callback);
+
+ net::CookieOptions cookie_options;
+ cookie_options.set_include_httponly();
+ cookie_store->GetCookiesWithOptionsAsync(GURL(kGoogleAccountsUrl),
+ cookie_options,
+ cookie_callback);
+}
+
+bool IsBillingGroup(FieldTypeGroup group) {
+ return group == ADDRESS_BILLING ||
+ group == PHONE_BILLING ||
+ group == NAME_BILLING;
+}
+
+const char kTransactionIdNotSet[] = "transaction id not set";
+
+} // namespace
+
+AutocheckoutManager::AutocheckoutManager(AutofillManager* autofill_manager)
+ : autofill_manager_(autofill_manager),
+ metric_logger_(new AutofillMetrics),
+ should_show_bubble_(true),
+ is_autocheckout_bubble_showing_(false),
+ in_autocheckout_flow_(false),
+ should_preserve_dialog_(false),
+ google_transaction_id_(kTransactionIdNotSet),
+ 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) {
+ FormStructure* form_structure = *iter;
+ form_structure->set_filled_by_autocheckout(true);
+ 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_->click_elements_before_form_fill,
+ page_meta_data_->click_elements_after_form_fill,
+ page_meta_data_->proceed_element_descriptor));
+ // Record time taken for navigating current page.
+ RecordTimeTaken(page_meta_data_->current_page_number);
+}
+
+void AutocheckoutManager::OnAutocheckoutPageCompleted(
+ AutocheckoutStatus status) {
+ if (!in_autocheckout_flow_)
+ return;
+
+ DVLOG(2) << "OnAutocheckoutPageCompleted, page_no: "
+ << page_meta_data_->current_page_number
+ << " status: "
+ << status;
+
+ DCHECK_NE(MISSING_FIELDMAPPING, status);
+
+ SetStepProgressForPage(
+ page_meta_data_->current_page_number,
+ (status == SUCCESS) ? AUTOCHECKOUT_STEP_COMPLETED :
+ AUTOCHECKOUT_STEP_FAILED);
+
+ if (page_meta_data_->IsEndOfAutofillableFlow() || status != SUCCESS)
+ EndAutocheckout(status);
+}
+
+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();
+
+ // If there is no click element in the last page, then it's the real last page
+ // of the flow, and the dialog will be closed when the page navigates.
+ // Otherwise, the dialog should be preserved for the page loaded by the click
+ // element on the last page of the flow.
+ // Note, |should_preserve_dialog_| has to be computed at this point because
+ // |in_autocheckout_flow_| may change after |OnLoadedPageMetaData| is called.
+ should_preserve_dialog_ = in_autocheckout_flow_ ||
+ (old_meta_data.get() &&
+ old_meta_data->IsEndOfAutofillableFlow() &&
+ old_meta_data->proceed_element_descriptor.retrieval_method !=
+ WebElementDescriptor::NONE);
+
+ // Don't log that the bubble could be displayed if the user entered an
+ // Autocheckout flow and sees the first page of the flow again due to an
+ // error.
+ if (IsStartOfAutofillableFlow() && !in_autocheckout_flow_) {
+ metric_logger_->LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED);
+ }
+
+ // 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;
+
+ AutocheckoutStatus status = SUCCESS;
+
+ // Missing Autofill server results.
+ if (!page_meta_data_.get()) {
+ status = MISSING_FIELDMAPPING;
+ } else if (IsStartOfAutofillableFlow()) {
+ // Not possible unless Autocheckout failed to proceed.
+ status = CANNOT_PROCEED;
+ } else if (!page_meta_data_->IsInAutofillableFlow()) {
+ // Missing Autocheckout meta data in the Autofill server results.
+ status = MISSING_FIELDMAPPING;
+ } else if (page_meta_data_->current_page_number <=
+ old_meta_data->current_page_number) {
+ // Not possible unless Autocheckout failed to proceed.
+ status = CANNOT_PROCEED;
+ }
+
+ // Encountered an error during the Autocheckout flow, probably to
+ // do with a problem on the previous page.
+ if (status != SUCCESS) {
+ SetStepProgressForPage(old_meta_data->current_page_number,
+ AUTOCHECKOUT_STEP_FAILED);
+ EndAutocheckout(status);
+ return;
+ }
+
+ SetStepProgressForPage(page_meta_data_->current_page_number,
+ AUTOCHECKOUT_STEP_STARTED);
+
+ FillForms();
+}
+
+void AutocheckoutManager::OnFormsSeen() {
+ should_show_bubble_ = true;
+}
+
+bool AutocheckoutManager::ShouldIgnoreAjax() {
+ return in_autocheckout_flow_ && page_meta_data_->ignore_ajax;
+}
+
+void AutocheckoutManager::MaybeShowAutocheckoutBubble(
+ const GURL& frame_url,
+ const gfx::RectF& bounding_box) {
+ if (!should_show_bubble_ ||
+ is_autocheckout_bubble_showing_ ||
+ !IsStartOfAutofillableFlow())
+ return;
+
+ base::Callback<void(const std::string&)> callback = base::Bind(
+ &AutocheckoutManager::ShowAutocheckoutBubble,
+ weak_ptr_factory_.GetWeakPtr(),
+ frame_url,
+ bounding_box);
+
+ content::WebContents* web_contents = autofill_manager_->GetWebContents();
+ if (!web_contents)
+ return;
+
+ content::BrowserContext* browser_context = web_contents->GetBrowserContext();
+ if(!browser_context)
+ return;
+
+ scoped_refptr<net::URLRequestContextGetter> request_context =
+ scoped_refptr<net::URLRequestContextGetter>(
+ browser_context->GetRequestContext());
+
+ if (!request_context.get())
+ return;
+
+ base::Closure task = base::Bind(&GetGoogleCookies, request_context, callback);
+
+ content::BrowserThread::PostTask(content::BrowserThread::IO,
+ FROM_HERE,
+ task);
+}
+
+void AutocheckoutManager::ReturnAutocheckoutData(
+ const FormStructure* result,
+ const std::string& google_transaction_id) {
+ if (!result) {
+ // When user cancels the dialog, |result| is NULL.
+ // TODO(): add AutocheckoutStatus.USER_CANCELLED, and call
+ // EndAutocheckout(USER_CANCELLED) instead.
+ in_autocheckout_flow_ = false;
+ return;
+ }
+
+ latency_statistics_.clear();
+ last_step_completion_timestamp_ = base::TimeTicks().Now();
+ google_transaction_id_ = google_transaction_id;
+ in_autocheckout_flow_ = true;
+ should_preserve_dialog_ = true;
+ metric_logger_->LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_STARTED);
+
+ profile_.reset(new AutofillProfile());
+ credit_card_.reset(new CreditCard());
+ billing_address_.reset(new AutofillProfile());
+
+ for (size_t i = 0; i < result->field_count(); ++i) {
+ const AutofillType& type = result->field(i)->Type();
+ const base::string16& value = result->field(i)->value;
+ ServerFieldType server_type = type.GetStorableType();
+ if (server_type == CREDIT_CARD_VERIFICATION_CODE) {
+ cvv_ = result->field(i)->value;
+ continue;
+ }
+ FieldTypeGroup group = type.group();
+ if (group == CREDIT_CARD) {
+ credit_card_->SetRawInfo(server_type, value);
+ // TODO(dgwallinga): Find a way of cleanly deprecating CREDIT_CARD_NAME.
+ // code.google.com/p/chromium/issues/detail?id=263498
+ if (server_type == CREDIT_CARD_NAME)
+ billing_address_->SetRawInfo(NAME_BILLING_FULL, value);
+ } else if (server_type == ADDRESS_HOME_COUNTRY) {
+ if (IsBillingGroup(group))
+ billing_address_->SetInfo(type, value, autofill_manager_->app_locale());
+ else
+ profile_->SetInfo(type, value, autofill_manager_->app_locale());
+ } else if (IsBillingGroup(group)) {
+ billing_address_->SetRawInfo(server_type, value);
+ } else {
+ profile_->SetRawInfo(server_type, value);
+ }
+ }
+
+ // Page types only available in first-page meta data, so save
+ // them for use later as we navigate.
+ page_types_ = page_meta_data_->page_types;
+ SetStepProgressForPage(page_meta_data_->current_page_number,
+ AUTOCHECKOUT_STEP_STARTED);
+
+ FillForms();
+}
+
+void AutocheckoutManager::set_metric_logger(
+ scoped_ptr<AutofillMetrics> metric_logger) {
+ metric_logger_ = metric_logger.Pass();
+}
+
+void AutocheckoutManager::MaybeShowAutocheckoutDialog(
+ const GURL& frame_url,
+ AutocheckoutBubbleState state) {
+ is_autocheckout_bubble_showing_ = false;
+
+ // User has taken action on the bubble, don't offer bubble again.
+ if (state != AUTOCHECKOUT_BUBBLE_IGNORED)
+ should_show_bubble_ = false;
+
+ if (state != AUTOCHECKOUT_BUBBLE_ACCEPTED)
+ return;
+
+ base::Callback<void(const FormStructure*, const std::string&)> callback =
+ base::Bind(&AutocheckoutManager::ReturnAutocheckoutData,
+ weak_ptr_factory_.GetWeakPtr());
+ autofill_manager_->ShowRequestAutocompleteDialog(BuildAutocheckoutFormData(),
+ frame_url,
+ DIALOG_TYPE_AUTOCHECKOUT,
+ callback);
+
+ for (std::map<int, std::vector<AutocheckoutStepType> >::const_iterator
+ it = page_meta_data_->page_types.begin();
+ it != page_meta_data_->page_types.end(); ++it) {
+ for (size_t i = 0; i < it->second.size(); ++i) {
+ autofill_manager_->delegate()->AddAutocheckoutStep(it->second[i]);
+ }
+ }
+}
+
+void AutocheckoutManager::ShowAutocheckoutBubble(
+ const GURL& frame_url,
+ const gfx::RectF& bounding_box,
+ const std::string& cookies) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ base::Callback<void(AutocheckoutBubbleState)> callback = base::Bind(
+ &AutocheckoutManager::MaybeShowAutocheckoutDialog,
+ weak_ptr_factory_.GetWeakPtr(),
+ frame_url);
+ is_autocheckout_bubble_showing_ =
+ autofill_manager_->delegate()->ShowAutocheckoutBubble(
+ bounding_box,
+ cookies.find("LSID") != std::string::npos,
+ 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::SetValue(const AutofillField& field,
+ FormFieldData* field_to_fill) {
+ // No-op if Autofill server doesn't know about the field.
+ if (field.server_type() == NO_SERVER_DATA)
+ return;
+
+ const AutofillType& type = field.Type();
+
+ ServerFieldType server_type = type.GetStorableType();
+ if (server_type == FIELD_WITH_DEFAULT_VALUE) {
+ // 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.
+ //
+ // FIELD_WITH_DEFAULT_VALUE can also be used for selects, the correspondent
+ // example of the radio buttons example above is:
+ // <SELECT name="sex">
+ // <OPTION value="female">Female</OPTION>
+ // <OPTION value="male">Male</OPTION>
+ // </SELECT>
+ base::string16 default_value = UTF8ToUTF16(field.default_value());
+ if (field.is_checkable) {
+ // 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);
+ } else if (field.form_control_type == "select-one") {
+ field_to_fill->value = default_value;
+ } else {
+ // FIELD_WITH_DEFAULT_VALUE should not be used for other type of fields.
+ NOTREACHED();
+ }
+ return;
+ }
+
+ // Handle verification code directly.
+ if (server_type == CREDIT_CARD_VERIFICATION_CODE) {
+ field_to_fill->value = cvv_;
+ return;
+ }
+
+ if (type.group() == CREDIT_CARD) {
+ credit_card_->FillFormField(
+ field, 0, autofill_manager_->app_locale(), field_to_fill);
+ } else if (IsBillingGroup(type.group())) {
+ billing_address_->FillFormField(
+ field, 0, autofill_manager_->app_locale(), field_to_fill);
+ } else {
+ profile_->FillFormField(
+ field, 0, autofill_manager_->app_locale(), field_to_fill);
+ }
+}
+
+void AutocheckoutManager::SendAutocheckoutStatus(AutocheckoutStatus status) {
+ // To ensure stale data isn't being sent.
+ DCHECK_NE(kTransactionIdNotSet, google_transaction_id_);
+
+ AutocheckoutRequestManager::CreateForBrowserContext(
+ autofill_manager_->GetWebContents()->GetBrowserContext());
+ AutocheckoutRequestManager* autocheckout_request_manager =
+ AutocheckoutRequestManager::FromBrowserContext(
+ autofill_manager_->GetWebContents()->GetBrowserContext());
+ // It is assumed that the domain Autocheckout starts on does not change
+ // during the flow. If this proves to be incorrect, the |source_url| from
+ // AutofillDialogControllerImpl will need to be provided in its callback in
+ // addition to the Google transaction id.
+ autocheckout_request_manager->SendAutocheckoutStatus(
+ status,
+ autofill_manager_->GetWebContents()->GetURL(),
+ latency_statistics_,
+ google_transaction_id_);
+
+ // Log the result of this Autocheckout flow to UMA.
+ metric_logger_->LogAutocheckoutBuyFlowMetric(
+ AutocheckoutStatusToUmaMetric(status));
+
+ google_transaction_id_ = kTransactionIdNotSet;
+}
+
+void AutocheckoutManager::SetStepProgressForPage(
+ int page_number,
+ AutocheckoutStepStatus status) {
+ if (page_types_.count(page_number) == 1) {
+ for (size_t i = 0; i < page_types_[page_number].size(); ++i) {
+ autofill_manager_->delegate()->UpdateAutocheckoutStep(
+ page_types_[page_number][i], status);
+ }
+ }
+}
+
+void AutocheckoutManager::RecordTimeTaken(int page_number) {
+ AutocheckoutStatistic statistic;
+ statistic.page_number = page_number;
+ if (page_types_.count(page_number) == 1) {
+ for (size_t i = 0; i < page_types_[page_number].size(); ++i) {
+ statistic.steps.push_back(page_types_[page_number][i]);
+ }
+ }
+
+ statistic.time_taken =
+ base::TimeTicks().Now() - last_step_completion_timestamp_;
+ latency_statistics_.push_back(statistic);
+
+ // Reset timestamp.
+ last_step_completion_timestamp_ = base::TimeTicks().Now();
+}
+
+void AutocheckoutManager::EndAutocheckout(AutocheckoutStatus status) {
+ DCHECK(in_autocheckout_flow_);
+
+ DVLOG(2) << "EndAutocheckout at step: "
+ << page_meta_data_->current_page_number
+ << " with status: "
+ << status;
+
+ SendAutocheckoutStatus(status);
+ if (status == SUCCESS)
+ autofill_manager_->delegate()->OnAutocheckoutSuccess();
+ else
+ autofill_manager_->delegate()->OnAutocheckoutError();
+ in_autocheckout_flow_ = false;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/autocheckout_manager.h b/chromium/components/autofill/content/browser/autocheckout_manager.h
new file mode 100644
index 00000000000..b265d8e2ec4
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_manager.h
@@ -0,0 +1,191 @@
+// 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_CONTENT_BROWSER_AUTOCHECKOUT_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_MANAGER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/content/browser/autocheckout_statistic.h"
+#include "components/autofill/core/browser/autocheckout_bubble_state.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+
+class GURL;
+
+namespace gfx {
+class RectF;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace autofill {
+
+class AutofillField;
+class AutofillManager;
+class AutofillMetrics;
+class AutofillProfile;
+class CreditCard;
+class FormStructure;
+
+struct FormData;
+struct FormFieldData;
+
+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();
+
+ // Called to signal that the renderer has completed processing a page in the
+ // Autocheckout flow. |status| is the reason for the failure, or |SUCCESS| if
+ // there were no errors.
+ void OnAutocheckoutPageCompleted(AutocheckoutStatus status);
+
+ // 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();
+
+ // Whether ajax on the current page should be ignored during
+ // an Autocheckout flow.
+ bool ShouldIgnoreAjax();
+
+ // 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. |bounding_box| is the bounding box of the input field in
+ // focus.
+ virtual void MaybeShowAutocheckoutBubble(const GURL& frame_url,
+ const gfx::RectF& bounding_box);
+
+ // Determine whether we should keep the dialog visible.
+ bool should_preserve_dialog() const { return should_preserve_dialog_; }
+
+ void set_should_show_bubble(bool should_show_bubble) {
+ should_show_bubble_ = should_show_bubble;
+ }
+
+ bool is_autocheckout_bubble_showing() const {
+ return is_autocheckout_bubble_showing_;
+ }
+
+ protected:
+ // Exposed for testing.
+ bool in_autocheckout_flow() const { return in_autocheckout_flow_; }
+
+ // Exposed for testing.
+ bool should_show_bubble() const { return should_show_bubble_; }
+
+ // Show the requestAutocomplete dialog if |state| is
+ // AUTOCHECKOUT_BUBBLE_ACCEPTED. Also, does bookkeeping for whether or not
+ // the bubble is showing.
+ virtual void MaybeShowAutocheckoutDialog(const GURL& frame_url,
+ AutocheckoutBubbleState state);
+
+ // Callback called from AutofillDialogController on filling up the UI form.
+ void ReturnAutocheckoutData(const FormStructure* result,
+ const std::string& google_transaction_id);
+
+ const AutofillMetrics& metric_logger() const { return *metric_logger_; }
+ void set_metric_logger(scoped_ptr<AutofillMetrics> metric_logger);
+
+ private:
+ // Shows the Autocheckout bubble. Must be called on the UI thread. |frame_url|
+ // is the page where Autocheckout is being initiated. |bounding_box| is the
+ // bounding box of the input field in focus. |cookies| is any Google Account
+ // cookies.
+ void ShowAutocheckoutBubble(const GURL& frame_url,
+ const gfx::RectF& bounding_box,
+ const std::string& cookies);
+
+ // 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;
+
+ // Sends |status| to Online Wallet using AutocheckoutRequestManager.
+ void SendAutocheckoutStatus(AutocheckoutStatus status);
+
+ // 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);
+
+ // Sets the progress of all steps for the given page to the provided value.
+ void SetStepProgressForPage(int page_number, AutocheckoutStepStatus status);
+
+ // Account time spent between now and |last_step_completion_timestamp_|
+ // towards |page_number|.
+ void RecordTimeTaken(int page_number);
+
+ // Terminate the Autocheckout flow and send Autocheckout status to Wallet
+ // server.
+ void EndAutocheckout(AutocheckoutStatus status);
+
+ AutofillManager* autofill_manager_; // WEAK; owns us
+
+ // Credit card verification code.
+ base::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_;
+
+ // Billing address built using data supplied by requestAutocomplete dialog.
+ scoped_ptr<AutofillProfile> billing_address_;
+
+ // Autocheckout specific page meta data of current page.
+ scoped_ptr<AutocheckoutPageMetaData> page_meta_data_;
+
+ scoped_ptr<AutofillMetrics> metric_logger_;
+
+ // Whether or not the Autocheckout bubble should be shown to user.
+ bool should_show_bubble_;
+
+ // Whether or not the Autocheckout bubble is being displayed to the user.
+ bool is_autocheckout_bubble_showing_;
+
+ // Whether or not the user is in an Autocheckout flow.
+ bool in_autocheckout_flow_;
+
+ // Whether or not the currently visible dialog, if there is one, should be
+ // preserved.
+ bool should_preserve_dialog_;
+
+ // AutocheckoutStepTypes for the various pages of the flow.
+ std::map<int, std::vector<AutocheckoutStepType> > page_types_;
+
+ // Timestamp of last step's completion.
+ base::TimeTicks last_step_completion_timestamp_;
+
+ // Per page latency statistics.
+ std::vector<AutocheckoutStatistic> latency_statistics_;
+
+ std::string google_transaction_id_;
+
+ base::WeakPtrFactory<AutocheckoutManager> weak_ptr_factory_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_MANAGER_H_
diff --git a/chromium/components/autofill/content/browser/autocheckout_manager_unittest.cc b/chromium/components/autofill/content/browser/autocheckout_manager_unittest.cc
new file mode 100644
index 00000000000..c8e425566ee
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_manager_unittest.cc
@@ -0,0 +1,949 @@
+// 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 <map>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/tuple.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/autofill/content/browser/autocheckout_manager.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/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 "content/public/test/test_utils.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 Tuple4<std::vector<FormData>,
+ std::vector<WebElementDescriptor>,
+ std::vector<WebElementDescriptor>,
+ WebElementDescriptor> AutofillParam;
+
+enum ProceedElementPresence {
+ NO_PROCEED_ELEMENT,
+ HAS_PROCEED_ELEMENT,
+};
+
+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<ServerFieldType>& 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]);
+ // Set heuristic type to make sure that server_types are used and not
+ // heuritic type.
+ form_structure->field(i)->set_heuristic_type(CREDIT_CARD_NUMBER);
+ }
+
+ return form_structure.Pass();
+}
+
+scoped_ptr<FormStructure> CreateTestAddressFormStructure() {
+ std::vector<ServerFieldType> autofill_types;
+ autofill_types.push_back(NAME_FULL);
+ autofill_types.push_back(PHONE_HOME_WHOLE_NUMBER);
+ autofill_types.push_back(EMAIL_ADDRESS);
+ 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);
+ autofill_types.push_back(NO_SERVER_DATA);
+ return CreateTestFormStructure(autofill_types);
+}
+
+scoped_ptr<FormStructure> CreateTestCreditCardFormStructure() {
+ std::vector<ServerFieldType> 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_BILLING_LINE1);
+ autofill_types.push_back(ADDRESS_BILLING_CITY);
+ autofill_types.push_back(ADDRESS_BILLING_STATE);
+ autofill_types.push_back(ADDRESS_BILLING_COUNTRY);
+ autofill_types.push_back(ADDRESS_BILLING_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);
+ FormFieldData select = BuildField("SelectField");
+ select.form_control_type = "select-one";
+ form.fields.push_back(select);
+
+ 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");
+ form_structure->field(2)->set_server_type(FIELD_WITH_DEFAULT_VALUE);
+ form_structure->field(2)->set_default_value("Default Value");
+
+ return form_structure.Pass();
+}
+
+void PopulateClickElement(WebElementDescriptor* proceed_element,
+ const std::string& descriptor) {
+ proceed_element->descriptor = descriptor;
+ proceed_element->retrieval_method = WebElementDescriptor::ID;
+}
+
+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->page_types[0].push_back(AUTOCHECKOUT_STEP_SHIPPING);
+ start_of_flow->page_types[1].push_back(AUTOCHECKOUT_STEP_DELIVERY);
+ start_of_flow->page_types[2].push_back(AUTOCHECKOUT_STEP_BILLING);
+ PopulateClickElement(&start_of_flow->proceed_element_descriptor, "#foo");
+ 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;
+ PopulateClickElement(&in_flow->proceed_element_descriptor, "#foo");
+ return in_flow.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateNotInFlowMetaData() {
+ scoped_ptr<AutocheckoutPageMetaData> not_in_flow(
+ new AutocheckoutPageMetaData());
+ PopulateClickElement(&not_in_flow->proceed_element_descriptor, "#foo");
+ return not_in_flow.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateEndOfFlowMetaData(
+ ProceedElementPresence proceed_element_presence) {
+ scoped_ptr<AutocheckoutPageMetaData> end_of_flow(
+ new AutocheckoutPageMetaData());
+ end_of_flow->current_page_number = 2;
+ end_of_flow->total_pages = 3;
+ if (proceed_element_presence == HAS_PROCEED_ELEMENT)
+ PopulateClickElement(&end_of_flow->proceed_element_descriptor, "#foo");
+ return end_of_flow.Pass();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateOnePageFlowMetaData(
+ ProceedElementPresence proceed_element_presence) {
+ scoped_ptr<AutocheckoutPageMetaData> one_page_flow(
+ new AutocheckoutPageMetaData());
+ one_page_flow->current_page_number = 0;
+ one_page_flow->total_pages = 1;
+ one_page_flow->page_types[0].push_back(AUTOCHECKOUT_STEP_SHIPPING);
+ one_page_flow->page_types[0].push_back(AUTOCHECKOUT_STEP_DELIVERY);
+ one_page_flow->page_types[0].push_back(AUTOCHECKOUT_STEP_BILLING);
+ if (proceed_element_presence == HAS_PROCEED_ELEMENT)
+ PopulateClickElement(&one_page_flow->proceed_element_descriptor, "#foo");
+ return one_page_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();
+}
+
+scoped_ptr<AutocheckoutPageMetaData> CreateMultiClickMetaData() {
+ scoped_ptr<AutocheckoutPageMetaData> metadata(new AutocheckoutPageMetaData());
+ metadata->current_page_number = 1;
+ metadata->total_pages = 3;
+ PopulateClickElement(&metadata->proceed_element_descriptor, "#foo");
+ WebElementDescriptor element;
+ PopulateClickElement(&element, "#before_form_fill_1");
+ metadata->click_elements_before_form_fill.push_back(element);
+ PopulateClickElement(&element, "#before_form_fill_2");
+ metadata->click_elements_before_form_fill.push_back(element);
+ PopulateClickElement(&element, "#after_form_fill");
+ metadata->click_elements_after_form_fill.push_back(element);
+ return metadata.Pass();
+}
+
+struct TestField {
+ const char* const field_type;
+ const char* const field_value;
+ ServerFieldType 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},
+ {"billing-street-address", "Billing Street", ADDRESS_BILLING_LINE1},
+ {"billing-locality", "Billing City", ADDRESS_BILLING_CITY},
+ {"billing-region", "BillingState", ADDRESS_BILLING_STATE},
+ {"billing-country", "Canada", ADDRESS_BILLING_COUNTRY},
+ {"billing-postal-code", "11111", ADDRESS_BILLING_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),
+ should_autoclick_bubble_(true) {}
+
+ virtual ~MockAutofillManagerDelegate() {}
+
+ virtual void HideRequestAutocompleteDialog() OVERRIDE {
+ request_autocomplete_dialog_open_ = false;
+ }
+
+ MOCK_METHOD0(OnAutocheckoutError, void());
+ MOCK_METHOD0(OnAutocheckoutSuccess, void());
+
+ virtual bool ShowAutocheckoutBubble(
+ const gfx::RectF& bounds,
+ bool is_google_user,
+ const base::Callback<void(AutocheckoutBubbleState)>& callback) OVERRIDE {
+ autocheckout_bubble_shown_ = true;
+ if (should_autoclick_bubble_)
+ callback.Run(AUTOCHECKOUT_BUBBLE_ACCEPTED);
+ return true;
+ }
+
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback) OVERRIDE {
+ request_autocomplete_dialog_open_ = true;
+ callback.Run(user_supplied_data_.get(), "google_transaction_id");
+ }
+
+ virtual void AddAutocheckoutStep(AutocheckoutStepType step_type) OVERRIDE {
+ if (autocheckout_steps_.count(step_type) == 0)
+ autocheckout_steps_[step_type] = AUTOCHECKOUT_STEP_UNSTARTED;
+ }
+
+ virtual void UpdateAutocheckoutStep(
+ AutocheckoutStepType step_type,
+ AutocheckoutStepStatus step_status) OVERRIDE {
+ autocheckout_steps_[step_type] = step_status;
+ }
+
+ 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_;
+ }
+
+ void set_should_autoclick_bubble(bool should_autoclick_bubble) {
+ should_autoclick_bubble_ = should_autoclick_bubble;
+ }
+
+ bool AutocheckoutStepExistsWithStatus(
+ AutocheckoutStepType step_type,
+ AutocheckoutStepStatus step_status) const {
+ std::map<AutocheckoutStepType, AutocheckoutStepStatus>::const_iterator it =
+ autocheckout_steps_.find(step_type);
+ return it != autocheckout_steps_.end() && it->second == step_status;
+ }
+
+ private:
+ // Whether or not ShowRequestAutocompleteDialog method has been called.
+ bool request_autocomplete_dialog_open_;
+
+ // Whether or not Autocheckout bubble is displayed to user.
+ bool autocheckout_bubble_shown_;
+
+ // Whether or not to accept the Autocheckout bubble when offered.
+ bool should_autoclick_bubble_;
+
+ // User specified data that would be returned to AutocheckoutManager when
+ // dialog is accepted.
+ scoped_ptr<FormStructure> user_supplied_data_;
+
+ // Step status of various Autocheckout steps in this checkout flow.
+ std::map<AutocheckoutStepType, AutocheckoutStepStatus> autocheckout_steps_;
+};
+
+class TestAutofillManager : public AutofillManager {
+ public:
+ explicit TestAutofillManager(AutofillDriver* driver,
+ AutofillManagerDelegate* delegate)
+ : AutofillManager(driver, delegate, NULL) {
+ }
+ virtual ~TestAutofillManager() {}
+
+ void SetFormStructure(scoped_ptr<FormStructure> form_structure) {
+ form_structures()->clear();
+ form_structures()->push_back(form_structure.release());
+ }
+};
+
+class MockAutofillMetrics : public AutofillMetrics {
+ public:
+ MockAutofillMetrics() {}
+ MOCK_CONST_METHOD1(LogAutocheckoutBubbleMetric, void(BubbleMetric));
+ MOCK_CONST_METHOD1(LogAutocheckoutBuyFlowMetric,
+ void(AutocheckoutBuyFlowMetric));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics);
+};
+
+class TestAutocheckoutManager: public AutocheckoutManager {
+ public:
+ explicit TestAutocheckoutManager(AutofillManager* autofill_manager)
+ : AutocheckoutManager(autofill_manager) {
+ set_metric_logger(scoped_ptr<AutofillMetrics>(new MockAutofillMetrics));
+ }
+
+ const MockAutofillMetrics& metric_logger() const {
+ return static_cast<const MockAutofillMetrics&>(
+ AutocheckoutManager::metric_logger());
+ }
+
+ virtual void MaybeShowAutocheckoutBubble(
+ const GURL& frame_url,
+ const gfx::RectF& bounding_box) OVERRIDE {
+ AutocheckoutManager::MaybeShowAutocheckoutBubble(frame_url,
+ bounding_box);
+ // Needed for AutocheckoutManager to post task on IO thread.
+ content::RunAllPendingInMessageLoop(BrowserThread::IO);
+ }
+
+ using AutocheckoutManager::in_autocheckout_flow;
+ using AutocheckoutManager::should_show_bubble;
+ using AutocheckoutManager::MaybeShowAutocheckoutDialog;
+ using AutocheckoutManager::ReturnAutocheckoutData;
+};
+
+} // namespace
+
+class AutocheckoutManagerTest : public ChromeRenderViewHostTestHarness {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ SetThreadBundleOptions(content::TestBrowserThreadBundle::REAL_IO_THREAD);
+ ChromeRenderViewHostTestHarness::SetUp();
+ autofill_manager_delegate_.reset(new MockAutofillManagerDelegate());
+ autofill_driver_.reset(new TestAutofillDriver(web_contents()));
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(),
+ 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();
+ autofill_driver_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ 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 CheckFillFormsAndClickIpc(
+ ProceedElementPresence proceed_element_presence) {
+ EXPECT_EQ(1U, process()->sink().message_count());
+ uint32 kMsgID = AutofillMsg_FillFormsAndClick::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ EXPECT_TRUE(message);
+ AutofillParam autofill_param;
+ AutofillMsg_FillFormsAndClick::Read(message, &autofill_param);
+ if (proceed_element_presence == HAS_PROCEED_ELEMENT) {
+ EXPECT_EQ(WebElementDescriptor::ID, autofill_param.d.retrieval_method);
+ EXPECT_EQ("#foo", autofill_param.d.descriptor);
+ } else {
+ EXPECT_EQ(WebElementDescriptor::NONE, autofill_param.d.retrieval_method);
+ }
+ ClearIpcSink();
+ }
+
+ void ClearIpcSink() {
+ process()->sink().ClearMessages();
+ }
+
+ void OpenRequestAutocompleteDialog() {
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_FALSE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ // Simulate the user submitting some data via the requestAutocomplete UI.
+ autofill_manager_delegate_->SetUserSuppliedData(
+ FakeUserSubmittedFormStructure());
+ GURL frame_url;
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_STARTED)).Times(1);
+ autocheckout_manager_->MaybeShowAutocheckoutDialog(
+ frame_url,
+ AUTOCHECKOUT_BUBBLE_ACCEPTED);
+ CheckFillFormsAndClickIpc(HAS_PROCEED_ELEMENT);
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(autocheckout_manager_->should_preserve_dialog());
+ EXPECT_TRUE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_SHIPPING,
+ AUTOCHECKOUT_STEP_STARTED));
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_DELIVERY,
+ AUTOCHECKOUT_STEP_UNSTARTED));
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_BILLING,
+ AUTOCHECKOUT_STEP_UNSTARTED));
+ }
+
+ void HideRequestAutocompleteDialog() {
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ autofill_manager_delegate_->HideRequestAutocompleteDialog();
+ EXPECT_FALSE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ }
+
+ // Test a multi-page Autocheckout flow end to end.
+ // |proceed_element_presence_on_last_page| indicates whether the last page
+ // of the flow should have a proceed element.
+ void TestFullAutocheckoutFlow(
+ ProceedElementPresence proceed_element_presence_on_last_page) {
+ // Test for progression through last page.
+ OpenRequestAutocompleteDialog();
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_SHIPPING,
+ AUTOCHECKOUT_STEP_STARTED));
+ // Complete the first page.
+ autocheckout_manager_->OnAutocheckoutPageCompleted(SUCCESS);
+ EXPECT_TRUE(autocheckout_manager_->should_preserve_dialog());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_SHIPPING,
+ AUTOCHECKOUT_STEP_COMPLETED));
+
+ // Go to the second page.
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutSuccess()).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(autocheckout_manager_->should_preserve_dialog());
+ CheckFillFormsAndClickIpc(HAS_PROCEED_ELEMENT);
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_DELIVERY,
+ AUTOCHECKOUT_STEP_STARTED));
+ autocheckout_manager_->OnAutocheckoutPageCompleted(SUCCESS);
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_DELIVERY,
+ AUTOCHECKOUT_STEP_COMPLETED));
+
+ // Go to the third page.
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_SUCCESS)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(
+ CreateEndOfFlowMetaData(proceed_element_presence_on_last_page));
+ CheckFillFormsAndClickIpc(proceed_element_presence_on_last_page);
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(autocheckout_manager_->should_preserve_dialog());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_BILLING,
+ AUTOCHECKOUT_STEP_STARTED));
+ autocheckout_manager_->OnAutocheckoutPageCompleted(SUCCESS);
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(autocheckout_manager_->should_preserve_dialog());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_BILLING,
+ AUTOCHECKOUT_STEP_COMPLETED));
+
+ EXPECT_TRUE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+
+ // Go to the page after the flow.
+ autocheckout_manager_->OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData>());
+ EXPECT_EQ(proceed_element_presence_on_last_page == HAS_PROCEED_ELEMENT,
+ autocheckout_manager_->should_preserve_dialog());
+ // Go to another page and we should not preserve the dialog now.
+ autocheckout_manager_->OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData>());
+ EXPECT_FALSE(autocheckout_manager_->should_preserve_dialog());
+ }
+
+ // Test a signle-page Autocheckout flow. |proceed_element_presence| indicates
+ // whether the page should have a proceed element.
+ void TestSinglePageFlow(ProceedElementPresence proceed_element_presence) {
+ // Test one page flow.
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_FALSE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED)).Times(1);
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutSuccess()).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(
+ CreateOnePageFlowMetaData(proceed_element_presence));
+ // Simulate the user submitting some data via the requestAutocomplete UI.
+ autofill_manager_delegate_->SetUserSuppliedData(
+ FakeUserSubmittedFormStructure());
+ GURL frame_url;
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_STARTED)).Times(1);
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_SUCCESS)).Times(1);
+ autocheckout_manager_->MaybeShowAutocheckoutDialog(
+ frame_url,
+ AUTOCHECKOUT_BUBBLE_ACCEPTED);
+ autocheckout_manager_->OnAutocheckoutPageCompleted(SUCCESS);
+ CheckFillFormsAndClickIpc(proceed_element_presence);
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(autocheckout_manager_->should_preserve_dialog());
+ EXPECT_TRUE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_SHIPPING,
+ AUTOCHECKOUT_STEP_COMPLETED));
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_DELIVERY,
+ AUTOCHECKOUT_STEP_COMPLETED));
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_BILLING,
+ AUTOCHECKOUT_STEP_COMPLETED));
+ // Go to the page after the flow.
+ autocheckout_manager_->OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData>());
+ EXPECT_EQ(proceed_element_presence == HAS_PROCEED_ELEMENT,
+ autocheckout_manager_->should_preserve_dialog());
+ // Go to another page, and we should not preserve the dialog now.
+ autocheckout_manager_->OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData>());
+ EXPECT_FALSE(autocheckout_manager_->should_preserve_dialog());
+ }
+
+ protected:
+ scoped_ptr<TestAutofillDriver> autofill_driver_;
+ scoped_ptr<TestAutofillManager> autofill_manager_;
+ scoped_ptr<TestAutocheckoutManager> autocheckout_manager_;
+ scoped_ptr<MockAutofillManagerDelegate> autofill_manager_delegate_;
+};
+
+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(9U, 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);
+ EXPECT_EQ(ASCIIToUTF16("Fake Street"), filled_forms[0].fields[3].value);
+ EXPECT_EQ(ASCIIToUTF16("Mocked City"), filled_forms[0].fields[4].value);
+ EXPECT_EQ(ASCIIToUTF16("California"), filled_forms[0].fields[5].value);
+ EXPECT_EQ(ASCIIToUTF16("United States"), filled_forms[0].fields[6].value);
+ EXPECT_EQ(ASCIIToUTF16("49012"), filled_forms[0].fields[7].value);
+ // Last field should not be filled, because there is no server mapping
+ // available for it.
+ EXPECT_EQ(ASCIIToUTF16("SomeField"), filled_forms[0].fields[8].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("Billing Street"), filled_forms[0].fields[5].value);
+ EXPECT_EQ(ASCIIToUTF16("Billing City"), filled_forms[0].fields[6].value);
+ EXPECT_EQ(ASCIIToUTF16("BillingState"), filled_forms[0].fields[7].value);
+ EXPECT_EQ(ASCIIToUTF16("Canada"), filled_forms[0].fields[8].value);
+ EXPECT_EQ(ASCIIToUTF16("11111"), 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(3U, 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);
+ EXPECT_EQ(ASCIIToUTF16("Default Value"), filled_forms[0].fields[2].value);
+}
+
+TEST_F(AutocheckoutManagerTest, OnFormsSeenTest) {
+ GURL frame_url;
+ gfx::RectF bounding_box;
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url, bounding_box);
+
+ EXPECT_FALSE(autocheckout_manager_->should_show_bubble());
+ // OnFormsSeen resets whether or not the bubble was shown.
+ autocheckout_manager_->OnFormsSeen();
+ EXPECT_TRUE(autocheckout_manager_->should_show_bubble());
+}
+
+TEST_F(AutocheckoutManagerTest, OnClickFailedTestMissingAdvance) {
+ OpenRequestAutocompleteDialog();
+
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_ADVANCE_ELEMENT))
+ .Times(1);
+ autocheckout_manager_->OnAutocheckoutPageCompleted(MISSING_ADVANCE);
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_SHIPPING,
+ AUTOCHECKOUT_STEP_FAILED));
+}
+
+TEST_F(AutocheckoutManagerTest, OnClickFailedTestMissingClickBeforeFilling) {
+ OpenRequestAutocompleteDialog();
+
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(AutofillMetrics::
+ AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING))
+ .Times(1);
+ autocheckout_manager_->OnAutocheckoutPageCompleted(
+ MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING);
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+}
+
+TEST_F(AutocheckoutManagerTest, OnClickFailedTestMissingClickAfterFilling) {
+ OpenRequestAutocompleteDialog();
+
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(AutofillMetrics::
+ AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING))
+ .Times(1);
+ autocheckout_manager_->OnAutocheckoutPageCompleted(
+ MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING);
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+}
+
+TEST_F(AutocheckoutManagerTest, MaybeShowAutocheckoutBubbleTest) {
+ GURL frame_url;
+ gfx::RectF bounding_box;
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ // MaybeShowAutocheckoutBubble shows bubble if it has not been shown.
+ autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url, bounding_box);
+ EXPECT_FALSE(autocheckout_manager_->should_show_bubble());
+ 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.
+ autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url, bounding_box);
+ EXPECT_FALSE(autocheckout_manager_->should_show_bubble());
+ EXPECT_FALSE(autofill_manager_delegate_->autocheckout_bubble_shown());
+ EXPECT_FALSE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+}
+
+TEST_F(AutocheckoutManagerTest, OnLoadedPageMetaDataMissingMetaData) {
+ // Gettting no meta data after any autocheckout page is an error.
+ OpenRequestAutocompleteDialog();
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_FIELDMAPPING))
+ .Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(
+ scoped_ptr<AutocheckoutPageMetaData>());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+}
+
+TEST_F(AutocheckoutManagerTest, OnLoadedPageMetaDataRepeatedStartPage) {
+ // Getting start page twice in a row is an error.
+ OpenRequestAutocompleteDialog();
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_CANNOT_PROCEED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_SHIPPING,
+ AUTOCHECKOUT_STEP_FAILED));
+}
+
+TEST_F(AutocheckoutManagerTest, OnLoadedPageMetaDataRepeatedPage) {
+ // Repeating a page is an error.
+ OpenRequestAutocompleteDialog();
+ // Go to second page.
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ CheckFillFormsAndClickIpc(HAS_PROCEED_ELEMENT);
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_CANNOT_PROCEED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_DELIVERY,
+ AUTOCHECKOUT_STEP_FAILED));
+}
+
+TEST_F(AutocheckoutManagerTest, OnLoadedPageMetaDataNotInFlow) {
+ // Repeating a page is an error.
+ OpenRequestAutocompleteDialog();
+ // Go to second page.
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ CheckFillFormsAndClickIpc(HAS_PROCEED_ELEMENT);
+ EXPECT_CALL(*autofill_manager_delegate_, OnAutocheckoutError()).Times(1);
+ EXPECT_CALL(
+ autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(
+ AutofillMetrics::AUTOCHECKOUT_BUY_FLOW_MISSING_FIELDMAPPING))
+ .Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateNotInFlowMetaData());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_EQ(0U, process()->sink().message_count());
+ EXPECT_TRUE(
+ autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_TRUE(autofill_manager_delegate_
+ ->AutocheckoutStepExistsWithStatus(AUTOCHECKOUT_STEP_DELIVERY,
+ AUTOCHECKOUT_STEP_FAILED));
+}
+
+TEST_F(AutocheckoutManagerTest,
+ OnLoadedPageMetaDataShouldNotFillFormsIfNotInFlow) {
+ // If not in flow, OnLoadedPageMetaData does not fill forms.
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+ // Go to second page.
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_EQ(0U, process()->sink().message_count());
+}
+
+TEST_F(AutocheckoutManagerTest, FullAutocheckoutFlow) {
+ TestFullAutocheckoutFlow(HAS_PROCEED_ELEMENT);
+}
+
+TEST_F(AutocheckoutManagerTest, FullAutocheckoutFlowNoClickOnLastPage) {
+ TestFullAutocheckoutFlow(NO_PROCEED_ELEMENT);
+}
+
+TEST_F(AutocheckoutManagerTest, CancelledAutocheckoutFlow) {
+ // Test for progression through last page.
+ OpenRequestAutocompleteDialog();
+ // Go to second page.
+ autocheckout_manager_->OnLoadedPageMetaData(CreateInFlowMetaData());
+ EXPECT_TRUE(autocheckout_manager_->in_autocheckout_flow());
+ CheckFillFormsAndClickIpc(HAS_PROCEED_ELEMENT);
+
+ // Cancel the flow.
+ autocheckout_manager_->ReturnAutocheckoutData(NULL, std::string());
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+
+ // Go to third page.
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBuyFlowMetric(testing::_)).Times(0);
+ autocheckout_manager_->OnLoadedPageMetaData(
+ CreateEndOfFlowMetaData(HAS_PROCEED_ELEMENT));
+ EXPECT_EQ(0U, process()->sink().message_count());
+}
+
+TEST_F(AutocheckoutManagerTest, SinglePageFlow) {
+ TestSinglePageFlow(HAS_PROCEED_ELEMENT);
+}
+
+TEST_F(AutocheckoutManagerTest, SinglePageFlowNoClickElement) {
+ TestSinglePageFlow(NO_PROCEED_ELEMENT);
+}
+
+TEST_F(AutocheckoutManagerTest, CancelAutocheckoutBubble) {
+ GURL frame_url;
+ gfx::RectF bounding_box;
+ autofill_manager_delegate_->set_should_autoclick_bubble(false);
+ EXPECT_FALSE(autocheckout_manager_->in_autocheckout_flow());
+ EXPECT_FALSE(autofill_manager_delegate_->request_autocomplete_dialog_open());
+ EXPECT_FALSE(autocheckout_manager_->is_autocheckout_bubble_showing());
+ EXPECT_CALL(autocheckout_manager_->metric_logger(),
+ LogAutocheckoutBubbleMetric(
+ AutofillMetrics::BUBBLE_COULD_BE_DISPLAYED)).Times(1);
+ autocheckout_manager_->OnLoadedPageMetaData(CreateStartOfFlowMetaData());
+
+ autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url, bounding_box);
+ EXPECT_TRUE(autocheckout_manager_->is_autocheckout_bubble_showing());
+ autocheckout_manager_->MaybeShowAutocheckoutDialog(
+ frame_url,
+ AUTOCHECKOUT_BUBBLE_IGNORED);
+ EXPECT_FALSE(autocheckout_manager_->is_autocheckout_bubble_showing());
+ EXPECT_TRUE(autocheckout_manager_->should_show_bubble());
+
+ autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url, bounding_box);
+ EXPECT_TRUE(autocheckout_manager_->is_autocheckout_bubble_showing());
+ autocheckout_manager_->MaybeShowAutocheckoutDialog(
+ frame_url,
+ AUTOCHECKOUT_BUBBLE_CANCELED);
+ EXPECT_FALSE(autocheckout_manager_->is_autocheckout_bubble_showing());
+ EXPECT_FALSE(autocheckout_manager_->should_show_bubble());
+
+ autocheckout_manager_->MaybeShowAutocheckoutBubble(frame_url, bounding_box);
+ EXPECT_FALSE(autocheckout_manager_->is_autocheckout_bubble_showing());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/autocheckout_page_meta_data.cc b/chromium/components/autofill/content/browser/autocheckout_page_meta_data.cc
new file mode 100644
index 00000000000..be03d70c592
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_page_meta_data.cc
@@ -0,0 +1,28 @@
+// 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/content/browser/autocheckout_page_meta_data.h"
+
+namespace autofill {
+
+AutocheckoutPageMetaData::AutocheckoutPageMetaData()
+ : current_page_number(-1),
+ total_pages(-1),
+ ignore_ajax(true) {}
+
+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/chromium/components/autofill/content/browser/autocheckout_page_meta_data.h b/chromium/components/autofill/content/browser/autocheckout_page_meta_data.h
new file mode 100644
index 00000000000..6839d1b3ec5
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_page_meta_data.h
@@ -0,0 +1,77 @@
+// 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_CONTENT_BROWSER_AUTOCHECKOUT_PAGE_META_DATA_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_PAGE_META_DATA_H_
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "components/autofill/content/browser/autocheckout_steps.h"
+#include "components/autofill/core/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;
+
+ // Whether ajaxy form changes that occur on this page during an Autocheckout
+ // flow should be ignored.
+ bool ignore_ajax;
+
+ // A list of elements to click before filling form fields. Elements have to be
+ // clicked in order.
+ std::vector<WebElementDescriptor> click_elements_before_form_fill;
+
+ // A list of elements to click after filling form fields, and before clicking
+ // page_advance_button. Elements have to be clicked in order.
+ std::vector<WebElementDescriptor> click_elements_after_form_fill;
+
+ // The proceed element of the multipage Autofill flow. It can be empty
+ // if current page is the last page of a flow or isn't a member of a flow.
+ //
+ // We do expect page navigation when click on |proceed_element_descriptor|,
+ // and report an error if it doesn't. Oppositely, we do not expect page
+ // navigation when click elements in |click_elements_before_form_fill| and
+ // |click_elements_after_form_fill|. Because of this behavior difference and
+ // |proceed_element_descriptor| is optional, we separate it from
+ // |click_elements_after_form_fill|.
+ WebElementDescriptor proceed_element_descriptor;
+
+ // Mapping of page numbers to the types of Autocheckout actions that will be
+ // performed on the given page of a multipage Autofill flow.
+ // If this form doesn't belong to such a flow, the map will be empty.
+ std::map<int, std::vector<AutocheckoutStepType> > page_types;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutPageMetaData);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_PAGE_META_DATA_H_
diff --git a/chromium/components/autofill/content/browser/autocheckout_page_meta_data_unittest.cc b/chromium/components/autofill/content/browser/autocheckout_page_meta_data_unittest.cc
new file mode 100644
index 00000000000..0e740608319
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_page_meta_data_unittest.cc
@@ -0,0 +1,54 @@
+// 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/content/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/chromium/components/autofill/content/browser/autocheckout_request_manager.cc b/chromium/components/autofill/content/browser/autocheckout_request_manager.cc
new file mode 100644
index 00000000000..e22e9c7b040
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_request_manager.cc
@@ -0,0 +1,110 @@
+// 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/content/browser/autocheckout_request_manager.h"
+
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "content/public/browser/browser_context.h"
+
+namespace {
+
+const char kAutocheckoutRequestManagerKey[] =
+ "browser_context_autocheckout_request_manager";
+
+} // namespace
+
+namespace autofill {
+
+AutocheckoutRequestManager::~AutocheckoutRequestManager() {}
+
+// static
+void AutocheckoutRequestManager::CreateForBrowserContext(
+ content::BrowserContext* browser_context) {
+ if (FromBrowserContext(browser_context))
+ return;
+
+ browser_context->SetUserData(
+ kAutocheckoutRequestManagerKey,
+ new AutocheckoutRequestManager(browser_context->GetRequestContext()));
+}
+
+// static
+AutocheckoutRequestManager* AutocheckoutRequestManager::FromBrowserContext(
+ content::BrowserContext* browser_context) {
+ return static_cast<AutocheckoutRequestManager*>(
+ browser_context->GetUserData(kAutocheckoutRequestManagerKey));
+}
+
+void AutocheckoutRequestManager::SendAutocheckoutStatus(
+ AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::vector<AutocheckoutStatistic>& latency_statistics,
+ const std::string& google_transaction_id) {
+ wallet_client_.SendAutocheckoutStatus(status,
+ source_url,
+ latency_statistics,
+ google_transaction_id);
+}
+
+
+const AutofillMetrics& AutocheckoutRequestManager::GetMetricLogger() const {
+ return metric_logger_;
+}
+
+DialogType AutocheckoutRequestManager::GetDialogType() const {
+ return DIALOG_TYPE_AUTOCHECKOUT;
+}
+
+std::string AutocheckoutRequestManager::GetRiskData() const {
+ NOTREACHED();
+ return std::string();
+}
+
+std::string AutocheckoutRequestManager::GetWalletCookieValue() const {
+ return std::string();
+}
+
+bool AutocheckoutRequestManager::IsShippingAddressRequired() const {
+ NOTREACHED();
+ return true;
+}
+
+void AutocheckoutRequestManager::OnDidAcceptLegalDocuments() {
+ NOTREACHED();
+}
+
+void AutocheckoutRequestManager::OnDidAuthenticateInstrument(bool success) {
+ NOTREACHED();
+}
+
+void AutocheckoutRequestManager::OnDidGetFullWallet(
+ scoped_ptr<wallet::FullWallet> full_wallet) {
+ NOTREACHED();
+}
+
+void AutocheckoutRequestManager::OnDidGetWalletItems(
+ scoped_ptr<wallet::WalletItems> wallet_items) {
+ NOTREACHED();
+}
+
+
+void AutocheckoutRequestManager::OnDidSaveToWallet(
+ const std::string& instrument_id,
+ const std::string& address_id,
+ const std::vector<wallet::RequiredAction>& required_actions,
+ const std::vector<wallet::FormFieldError>& form_field_errors) {
+ NOTREACHED();
+}
+
+void AutocheckoutRequestManager::OnWalletError(
+ wallet::WalletClient::ErrorType error_type) {
+ // Nothing to be done. |error_type| is logged by |metric_logger_|.
+}
+
+AutocheckoutRequestManager::AutocheckoutRequestManager(
+ net::URLRequestContextGetter* request_context_getter)
+ : wallet_client_(request_context_getter, this) {
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/autocheckout_request_manager.h b/chromium/components/autofill/content/browser/autocheckout_request_manager.h
new file mode 100644
index 00000000000..4d20b5f727b
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_request_manager.h
@@ -0,0 +1,90 @@
+// 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_CONTENT_BROWSER_AUTOCHECKOUT_REQUEST_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_REQUEST_MANAGER_H_
+
+#include "base/supports_user_data.h"
+#include "components/autofill/content/browser/autocheckout_statistic.h"
+#include "components/autofill/content/browser/wallet/wallet_client.h"
+#include "components/autofill/content/browser/wallet/wallet_client_delegate.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+#include "url/gurl.h"
+
+namespace content {
+class BrowserContext;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace autofill {
+
+// AutocheckoutRequestManager's only responsiblity is to make sure any
+// SendAutocheckoutStatus calls succeed regardless of any actions the user may
+// make in the browser i.e. closing a tab, the requestAutocomplete dialog, etc.
+// To that end, it is a piece of user data tied to the BrowserContext.
+class AutocheckoutRequestManager : public base::SupportsUserData::Data,
+ public wallet::WalletClientDelegate {
+ public:
+ virtual ~AutocheckoutRequestManager();
+
+ // Creates a new AutocheckoutRequestManager and stores it as user data in
+ // |browser_context| if one does not already exist.
+ static void CreateForBrowserContext(
+ content::BrowserContext* browser_context);
+
+ // Retrieves the AutocheckoutRequestManager for |browser_context| if one
+ // exists.
+ static AutocheckoutRequestManager* FromBrowserContext(
+ content::BrowserContext* browser_context);
+
+ // Sends the |status| of an Autocheckout flow to Online Wallet using
+ // |wallet_client_|.
+ void SendAutocheckoutStatus(
+ AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::vector<AutocheckoutStatistic>& latency_statistics,
+ const std::string& google_transaction_id);
+
+ // wallet::WalletClientDelegate:
+ virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE;
+ virtual DialogType GetDialogType() const OVERRIDE;
+ virtual std::string GetRiskData() const OVERRIDE;
+ virtual std::string GetWalletCookieValue() const OVERRIDE;
+ virtual bool IsShippingAddressRequired() const OVERRIDE;
+ virtual void OnDidAcceptLegalDocuments() OVERRIDE;
+ virtual void OnDidAuthenticateInstrument(bool success) OVERRIDE;
+ virtual void OnDidGetFullWallet(
+ scoped_ptr<wallet::FullWallet> full_wallet) OVERRIDE;
+ virtual void OnDidGetWalletItems(
+ scoped_ptr<wallet::WalletItems> wallet_items) OVERRIDE;
+ virtual void OnDidSaveToWallet(
+ const std::string& instrument_id,
+ const std::string& address_id,
+ const std::vector<wallet::RequiredAction>& required_actions,
+ const std::vector<wallet::FormFieldError>& form_field_errors) OVERRIDE;
+ virtual void OnWalletError(
+ wallet::WalletClient::ErrorType error_type) OVERRIDE;
+
+ private:
+ // |request_context_getter| is passed in to construct |wallet_client_|.
+ AutocheckoutRequestManager(
+ net::URLRequestContextGetter* request_context_getter);
+
+ // Logs various UMA metrics.
+ AutofillMetrics metric_logger_;
+
+ // Makes requests to Online Wallet. The only request this class is configured
+ // to make is SendAutocheckoutStatus.
+ wallet::WalletClient wallet_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocheckoutRequestManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_REQUEST_MANAGER_H_
diff --git a/chromium/components/autofill/content/browser/autocheckout_statistic.cc b/chromium/components/autofill/content/browser/autocheckout_statistic.cc
new file mode 100644
index 00000000000..9df4f4e4a61
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_statistic.cc
@@ -0,0 +1,52 @@
+// 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/content/browser/autocheckout_statistic.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+
+namespace autofill {
+
+namespace {
+
+std::string AutocheckoutStepToString(AutocheckoutStepType step) {
+ switch(step) {
+ case AUTOCHECKOUT_STEP_SHIPPING:
+ return "AUTOCHECKOUT_STEP_SHIPPING";
+ case AUTOCHECKOUT_STEP_DELIVERY:
+ return "AUTOCHECKOUT_STEP_DELIVERY";
+ case AUTOCHECKOUT_STEP_BILLING:
+ return "AUTOCHECKOUT_STEP_BILLING";
+ case AUTOCHECKOUT_STEP_PROXY_CARD:
+ return "AUTOCHECKOUT_STEP_PROXY_CARD";
+ }
+ NOTREACHED();
+ return "NOT_POSSIBLE";
+}
+
+} // namespace
+
+AutocheckoutStatistic::AutocheckoutStatistic() : page_number(-1) {
+}
+
+AutocheckoutStatistic::~AutocheckoutStatistic() {
+}
+
+scoped_ptr<base::DictionaryValue> AutocheckoutStatistic::ToDictionary() const {
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+ std::string description = base::IntToString(page_number);
+ for (size_t i = 0; i < steps.size(); ++i) {
+ description = description + "_" + AutocheckoutStepToString(steps[i]);
+ }
+ dict->SetString("step_description", description);
+ dict->SetInteger("time_taken", time_taken.InMilliseconds());
+ DVLOG(1) << "Step : " << description
+ << ", time_taken: " << time_taken.InMilliseconds();
+ return dict.Pass();
+}
+
+} // namespace autofill
+
diff --git a/chromium/components/autofill/content/browser/autocheckout_statistic.h b/chromium/components/autofill/content/browser/autocheckout_statistic.h
new file mode 100644
index 00000000000..74f7df7cd9b
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_statistic.h
@@ -0,0 +1,40 @@
+// 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_CONTENT_BROWSER_AUTOCHECKOUT_STATISTIC_H__
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_STATISTIC_H__
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "components/autofill/content/browser/autocheckout_steps.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+
+// Per page statistics gathered in Autocheckout flow.
+struct AutocheckoutStatistic {
+ AutocheckoutStatistic();
+ ~AutocheckoutStatistic();
+
+ // Page number for which this statistic is being recorded.
+ int page_number;
+
+ // Autocheckout steps that are part of |page_number|.
+ std::vector<AutocheckoutStepType> steps;
+
+ // Time taken for performing |steps|, used for measuring latency.
+ base::TimeDelta time_taken;
+
+ // Helper method to convert AutocheckoutStatistic to json representation.
+ scoped_ptr<base::DictionaryValue> ToDictionary() const;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOCHECKOUT_STATISTIC_H__
diff --git a/chromium/components/autofill/content/browser/autocheckout_steps.h b/chromium/components/autofill/content/browser/autocheckout_steps.h
new file mode 100644
index 00000000000..d46abccc40f
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autocheckout_steps.h
@@ -0,0 +1,32 @@
+// 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_AUTOCHECKOUT_STEPS_H_
+#define COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_STEPS_H_
+
+namespace autofill {
+
+// Stages of a buy flow that may be encountered on an Autocheckout-supported
+// site, used primarily for display purposes.
+// Indexed for easy conversion from int values returned by Autofill server.
+enum AutocheckoutStepType {
+ AUTOCHECKOUT_STEP_MIN_VALUE = 1,
+ AUTOCHECKOUT_STEP_SHIPPING = AUTOCHECKOUT_STEP_MIN_VALUE,
+ AUTOCHECKOUT_STEP_DELIVERY = 2,
+ AUTOCHECKOUT_STEP_BILLING = 3,
+ AUTOCHECKOUT_STEP_PROXY_CARD = 4,
+ AUTOCHECKOUT_STEP_MAX_VALUE = AUTOCHECKOUT_STEP_PROXY_CARD,
+};
+
+// Possible statuses for the above step types, again used primarily for display.
+enum AutocheckoutStepStatus {
+ AUTOCHECKOUT_STEP_UNSTARTED,
+ AUTOCHECKOUT_STEP_STARTED,
+ AUTOCHECKOUT_STEP_COMPLETED,
+ AUTOCHECKOUT_STEP_FAILED,
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_AUTOCHECKOUT_STEPS_H_
diff --git a/chromium/components/autofill/content/browser/autofill_driver_impl.cc b/chromium/components/autofill/content/browser/autofill_driver_impl.cc
new file mode 100644
index 00000000000..d0cb1f62861
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autofill_driver_impl.cc
@@ -0,0 +1,235 @@
+// 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/content/browser/autofill_driver_impl.h"
+
+#include "base/command_line.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_details.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 "content/public/common/frame_navigate_params.h"
+#include "ipc/ipc_message_macros.h"
+
+namespace autofill {
+
+namespace {
+
+const char kAutofillDriverImplWebContentsUserDataKey[] =
+ "web_contents_autofill_driver_impl";
+
+} // namespace
+
+// static
+void AutofillDriverImpl::CreateForWebContentsAndDelegate(
+ content::WebContents* contents,
+ autofill::AutofillManagerDelegate* delegate,
+ const std::string& app_locale,
+ AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+ if (FromWebContents(contents))
+ return;
+
+ contents->SetUserData(kAutofillDriverImplWebContentsUserDataKey,
+ new AutofillDriverImpl(contents,
+ delegate,
+ app_locale,
+ enable_download_manager));
+ // Trigger the lazy creation of AutocheckoutWhitelistManagerService, and
+ // schedule a fetch of the Autocheckout whitelist file if it's not already
+ // loaded. This helps ensure that the whitelist will be available by the time
+ // the user navigates to a form on which Autocheckout should be enabled.
+ delegate->GetAutocheckoutWhitelistManager();
+}
+
+// static
+AutofillDriverImpl* AutofillDriverImpl::FromWebContents(
+ content::WebContents* contents) {
+ return static_cast<AutofillDriverImpl*>(
+ contents->GetUserData(kAutofillDriverImplWebContentsUserDataKey));
+}
+
+AutofillDriverImpl::AutofillDriverImpl(
+ content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate,
+ const std::string& app_locale,
+ AutofillManager::AutofillDownloadManagerState enable_download_manager)
+ : content::WebContentsObserver(web_contents),
+ autofill_manager_(new AutofillManager(
+ this, delegate, app_locale, enable_download_manager)) {
+ SetAutofillExternalDelegate(scoped_ptr<AutofillExternalDelegate>(
+ new AutofillExternalDelegate(web_contents, autofill_manager_.get(),
+ this)));
+
+ 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())));
+}
+
+AutofillDriverImpl::~AutofillDriverImpl() {}
+
+content::WebContents* AutofillDriverImpl::GetWebContents() {
+ return web_contents();
+}
+
+bool AutofillDriverImpl::RendererIsAvailable() {
+ return (web_contents()->GetRenderViewHost() != NULL);
+}
+
+void AutofillDriverImpl::SetRendererActionOnFormDataReception(
+ RendererFormDataAction action) {
+ if (!RendererIsAvailable())
+ return;
+
+ content::RenderViewHost* host = web_contents()->GetRenderViewHost();
+ switch(action) {
+ case FORM_DATA_ACTION_PREVIEW:
+ host->Send(new AutofillMsg_SetAutofillActionPreview(
+ host->GetRoutingID()));
+ return;
+ case FORM_DATA_ACTION_FILL:
+ host->Send(new AutofillMsg_SetAutofillActionFill(host->GetRoutingID()));
+ return;
+ }
+}
+
+void AutofillDriverImpl::SendFormDataToRenderer(int query_id,
+ const FormData& data) {
+ if (!RendererIsAvailable())
+ return;
+ content::RenderViewHost* host = web_contents()->GetRenderViewHost();
+ host->Send(
+ new AutofillMsg_FormDataFilled(host->GetRoutingID(), query_id, data));
+}
+
+void AutofillDriverImpl::SendAutofillTypePredictionsToRenderer(
+ const std::vector<FormStructure*>& forms) {
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kShowAutofillTypePredictions))
+ return;
+
+ content::RenderViewHost* host = GetWebContents()->GetRenderViewHost();
+ if (!host)
+ return;
+
+ std::vector<FormDataPredictions> type_predictions;
+ FormStructure::GetFieldTypePredictions(forms, &type_predictions);
+ host->Send(
+ new AutofillMsg_FieldTypePredictionsAvailable(host->GetRoutingID(),
+ type_predictions));
+}
+
+void AutofillDriverImpl::RendererShouldClearFilledForm() {
+ if (!RendererIsAvailable())
+ return;
+ content::RenderViewHost* host = web_contents()->GetRenderViewHost();
+ host->Send(new AutofillMsg_ClearForm(host->GetRoutingID()));
+}
+
+void AutofillDriverImpl::RendererShouldClearPreviewedForm() {
+ if (!RendererIsAvailable())
+ return;
+ content::RenderViewHost* host = web_contents()->GetRenderViewHost();
+ host->Send(new AutofillMsg_ClearPreviewedForm(host->GetRoutingID()));
+}
+
+bool AutofillDriverImpl::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AutofillDriverImpl, message)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_FormsSeen, autofill_manager_.get(),
+ AutofillManager::OnFormsSeen)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_FormSubmitted, autofill_manager_.get(),
+ AutofillManager::OnFormSubmitted)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_TextFieldDidChange,
+ autofill_manager_.get(),
+ AutofillManager::OnTextFieldDidChange)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_QueryFormFieldAutofill,
+ autofill_manager_.get(),
+ AutofillManager::OnQueryFormFieldAutofill)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_ShowAutofillDialog,
+ autofill_manager_.get(),
+ AutofillManager::OnShowAutofillDialog)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_FillAutofillFormData,
+ autofill_manager_.get(),
+ AutofillManager::OnFillAutofillFormData)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_DidPreviewAutofillFormData,
+ autofill_manager_.get(),
+ AutofillManager::OnDidPreviewAutofillFormData)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_DidFillAutofillFormData,
+ autofill_manager_.get(),
+ AutofillManager::OnDidFillAutofillFormData)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_DidEndTextFieldEditing,
+ autofill_manager_.get(),
+ AutofillManager::OnDidEndTextFieldEditing)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_HideAutofillUI, autofill_manager_.get(),
+ AutofillManager::OnHideAutofillUI)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_AddPasswordFormMapping,
+ autofill_manager_.get(),
+ AutofillManager::OnAddPasswordFormMapping)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_ShowPasswordSuggestions,
+ autofill_manager_.get(),
+ AutofillManager::OnShowPasswordSuggestions)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_SetDataList, autofill_manager_.get(),
+ AutofillManager::OnSetDataList)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_RequestAutocomplete,
+ autofill_manager_.get(),
+ AutofillManager::OnRequestAutocomplete)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_AutocheckoutPageCompleted,
+ autofill_manager_.get(),
+ AutofillManager::OnAutocheckoutPageCompleted)
+ IPC_MESSAGE_FORWARD(AutofillHostMsg_MaybeShowAutocheckoutBubble,
+ autofill_manager_.get(),
+ AutofillManager::OnMaybeShowAutocheckoutBubble)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AutofillDriverImpl::DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) {
+ if (details.is_navigation_to_different_page())
+ autofill_manager_->Reset();
+}
+
+void AutofillDriverImpl::SetAutofillExternalDelegate(
+ scoped_ptr<AutofillExternalDelegate> delegate) {
+ autofill_external_delegate_ = delegate.Pass();
+ autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get());
+}
+
+void AutofillDriverImpl::SetAutofillManager(
+ scoped_ptr<AutofillManager> manager) {
+ autofill_manager_ = manager.Pass();
+ autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get());
+}
+
+void AutofillDriverImpl::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();
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/autofill_driver_impl.h b/chromium/components/autofill/content/browser/autofill_driver_impl.h
new file mode 100644
index 00000000000..999822eb373
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autofill_driver_impl.h
@@ -0,0 +1,108 @@
+// 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_CONTENT_BROWSER_AUTOFILL_DRIVER_IMPL_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOFILL_DRIVER_IMPL_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/supports_user_data.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace IPC {
+class Message;
+}
+
+namespace autofill {
+
+class AutofillContext;
+class AutofillManagerDelegate;
+
+// Class that drives autofill flow in the browser process based on
+// communication from the renderer and from the external world. There is one
+// instance per WebContents.
+class AutofillDriverImpl : public AutofillDriver,
+ public content::NotificationObserver,
+ public content::WebContentsObserver,
+ public base::SupportsUserData::Data {
+ public:
+ static void CreateForWebContentsAndDelegate(
+ content::WebContents* contents,
+ autofill::AutofillManagerDelegate* delegate,
+ const std::string& app_locale,
+ AutofillManager::AutofillDownloadManagerState enable_download_manager);
+ static AutofillDriverImpl* FromWebContents(content::WebContents* contents);
+
+ // AutofillDriver:
+ virtual content::WebContents* GetWebContents() OVERRIDE;
+ virtual bool RendererIsAvailable() OVERRIDE;
+ virtual void SetRendererActionOnFormDataReception(
+ RendererFormDataAction action) OVERRIDE;
+ virtual void SendFormDataToRenderer(int query_id,
+ const FormData& data) OVERRIDE;
+ virtual void SendAutofillTypePredictionsToRenderer(
+ const std::vector<FormStructure*>& forms) OVERRIDE;
+ virtual void RendererShouldClearFilledForm() OVERRIDE;
+ virtual void RendererShouldClearPreviewedForm() OVERRIDE;
+
+ AutofillExternalDelegate* autofill_external_delegate() {
+ return autofill_external_delegate_.get();
+ }
+
+ // Sets the external delegate to |delegate| both within this class and in the
+ // shared Autofill code. Takes ownership of |delegate|.
+ void SetAutofillExternalDelegate(
+ scoped_ptr<AutofillExternalDelegate> delegate);
+
+ AutofillManager* autofill_manager() { return autofill_manager_.get(); }
+
+ protected:
+ AutofillDriverImpl(
+ content::WebContents* web_contents,
+ autofill::AutofillManagerDelegate* delegate,
+ const std::string& app_locale,
+ AutofillManager::AutofillDownloadManagerState enable_download_manager);
+ virtual ~AutofillDriverImpl();
+
+ // content::WebContentsObserver:
+ virtual void DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // Sets the manager to |manager| and sets |manager|'s external delegate
+ // to |autofill_external_delegate_|. Takes ownership of |manager|.
+ void SetAutofillManager(scoped_ptr<AutofillManager> manager);
+
+ private:
+ // content::NotificationObserver:
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // A scoped container for notification registries.
+ content::NotificationRegistrar registrar_;
+
+ // AutofillExternalDelegate instance that this object instantiates in the
+ // case where the autofill native UI is enabled.
+ scoped_ptr<AutofillExternalDelegate> autofill_external_delegate_;
+
+ // AutofillManager instance via which this object drives the shared Autofill
+ // code.
+ scoped_ptr<AutofillManager> autofill_manager_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOFILL_DRIVER_IMPL_H_
diff --git a/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc b/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc
new file mode 100644
index 00000000000..875b783a565
--- /dev/null
+++ b/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc
@@ -0,0 +1,224 @@
+// 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 <algorithm>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/autofill/content/browser/autofill_driver_impl.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/form_data_predictions.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/frame_navigate_params.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+
+const std::string kAppLocale = "en-US";
+const AutofillManager::AutofillDownloadManagerState kDownloadState =
+ AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER;
+
+} // namespace
+
+class MockAutofillManager : public AutofillManager {
+ public:
+ MockAutofillManager(AutofillDriver* driver,
+ AutofillManagerDelegate* delegate)
+ : AutofillManager(driver, delegate, kAppLocale, kDownloadState) {
+ }
+ virtual ~MockAutofillManager() {}
+
+ MOCK_METHOD0(Reset, void());
+};
+
+class TestAutofillDriverImpl : public AutofillDriverImpl {
+ public:
+ TestAutofillDriverImpl(content::WebContents* contents,
+ AutofillManagerDelegate* delegate)
+ : AutofillDriverImpl(contents, delegate, kAppLocale, kDownloadState) {
+ scoped_ptr<AutofillManager> autofill_manager(
+ new MockAutofillManager(this, delegate));
+ SetAutofillManager(autofill_manager.Pass());
+ }
+ virtual ~TestAutofillDriverImpl() {}
+
+ virtual MockAutofillManager* mock_autofill_manager() {
+ return static_cast<MockAutofillManager*>(autofill_manager());
+ }
+
+ using AutofillDriverImpl::DidNavigateMainFrame;
+};
+
+class AutofillDriverImplTest : public ChromeRenderViewHostTestHarness {
+ public:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+
+ test_manager_delegate_.reset(new TestAutofillManagerDelegate());
+ driver_.reset(new TestAutofillDriverImpl(web_contents(),
+ test_manager_delegate_.get()));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Reset the driver now to cause all pref observers to be removed and avoid
+ // crashes that otherwise occur in the destructor.
+ driver_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ protected:
+ // Searches for an |AutofillMsg_FormDataFilled| message in the queue of sent
+ // IPC messages. If none is present, returns false. Otherwise, extracts the
+ // first |AutofillMsg_FormDataFilled| message, fills the output parameters
+ // with the values of the message's parameters, and clears the queue of sent
+ // messages.
+ 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;
+ }
+
+ // Searches for an |AutofillMsg_FieldTypePredictionsAvailable| message in the
+ // queue of sent IPC messages. If none is present, returns false. Otherwise,
+ // extracts the first |AutofillMsg_FieldTypePredictionsAvailable| message,
+ // fills the output parameter with the values of the message's parameter, and
+ // clears the queue of sent messages.
+ bool GetFieldTypePredictionsAvailable(
+ std::vector<FormDataPredictions>* predictions) {
+ const uint32 kMsgID = AutofillMsg_FieldTypePredictionsAvailable::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ if (!message)
+ return false;
+ Tuple1<std::vector<FormDataPredictions> > autofill_param;
+ AutofillMsg_FieldTypePredictionsAvailable::Read(message, &autofill_param);
+ if (predictions)
+ *predictions = autofill_param.a;
+
+ process()->sink().ClearMessages();
+ return true;
+ }
+
+ // Searches for a message matching |messageID| in the queue of sent IPC
+ // messages. If none is present, returns false. Otherwise, clears the queue
+ // of sent messages and returns true.
+ bool HasMessageMatchingID(uint32 messageID) {
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(messageID);
+ if (!message)
+ return false;
+ process()->sink().ClearMessages();
+ return true;
+ }
+
+ scoped_ptr<TestAutofillManagerDelegate> test_manager_delegate_;
+ scoped_ptr<TestAutofillDriverImpl> driver_;
+};
+
+TEST_F(AutofillDriverImplTest, NavigatedToDifferentPage) {
+ EXPECT_CALL(*driver_->mock_autofill_manager(), Reset());
+ content::LoadCommittedDetails details = content::LoadCommittedDetails();
+ details.is_main_frame = true;
+ details.is_in_page = false;
+ ASSERT_TRUE(details.is_navigation_to_different_page());
+ content::FrameNavigateParams params = content::FrameNavigateParams();
+ driver_->DidNavigateMainFrame(details, params);
+}
+
+TEST_F(AutofillDriverImplTest, NavigatedWithinSamePage) {
+ EXPECT_CALL(*driver_->mock_autofill_manager(), Reset()).Times(0);
+ content::LoadCommittedDetails details = content::LoadCommittedDetails();
+ details.is_main_frame = false;
+ ASSERT_TRUE(!details.is_navigation_to_different_page());
+ content::FrameNavigateParams params = content::FrameNavigateParams();
+ driver_->DidNavigateMainFrame(details, params);
+}
+
+TEST_F(AutofillDriverImplTest, FormDataSentToRenderer) {
+ int input_page_id = 42;
+ FormData input_form_data;
+ test::CreateTestAddressFormData(&input_form_data);
+ driver_->SendFormDataToRenderer(input_page_id, input_form_data);
+
+ int output_page_id = 0;
+ FormData output_form_data;
+ EXPECT_TRUE(GetAutofillFormDataFilledMessage(&output_page_id,
+ &output_form_data));
+ EXPECT_EQ(input_page_id, output_page_id);
+ EXPECT_EQ(input_form_data, output_form_data);
+}
+
+TEST_F(AutofillDriverImplTest, TypePredictionsNotSentToRendererWhenDisabled) {
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ FormStructure form_structure(form, std::string());
+ std::vector<FormStructure*> forms(1, &form_structure);
+ driver_->SendAutofillTypePredictionsToRenderer(forms);
+ EXPECT_FALSE(GetFieldTypePredictionsAvailable(NULL));
+}
+
+TEST_F(AutofillDriverImplTest, TypePredictionsSentToRendererWhenEnabled) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kShowAutofillTypePredictions);
+
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ FormStructure form_structure(form, std::string());
+ std::vector<FormStructure*> forms(1, &form_structure);
+ std::vector<FormDataPredictions> expected_type_predictions;
+ FormStructure::GetFieldTypePredictions(forms, &expected_type_predictions);
+ driver_->SendAutofillTypePredictionsToRenderer(forms);
+
+ std::vector<FormDataPredictions> output_type_predictions;
+ EXPECT_TRUE(GetFieldTypePredictionsAvailable(&output_type_predictions));
+ EXPECT_EQ(expected_type_predictions, output_type_predictions);
+}
+
+TEST_F(AutofillDriverImplTest, PreviewActionSentToRenderer) {
+ driver_->SetRendererActionOnFormDataReception(
+ AutofillDriver::FORM_DATA_ACTION_PREVIEW);
+ EXPECT_TRUE(HasMessageMatchingID(AutofillMsg_SetAutofillActionPreview::ID));
+}
+
+TEST_F(AutofillDriverImplTest, FillActionSentToRenderer) {
+ driver_->SetRendererActionOnFormDataReception(
+ AutofillDriver::FORM_DATA_ACTION_FILL);
+ EXPECT_TRUE(HasMessageMatchingID(AutofillMsg_SetAutofillActionFill::ID));
+}
+
+TEST_F(AutofillDriverImplTest, ClearFilledFormSentToRenderer) {
+ driver_->RendererShouldClearFilledForm();
+ EXPECT_TRUE(HasMessageMatchingID(AutofillMsg_ClearForm::ID));
+}
+
+TEST_F(AutofillDriverImplTest, ClearPreviewedFormSentToRenderer) {
+ driver_->RendererShouldClearPreviewedForm();
+ EXPECT_TRUE(HasMessageMatchingID(AutofillMsg_ClearPreviewedForm::ID));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc
new file mode 100644
index 00000000000..635224d1395
--- /dev/null
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc
@@ -0,0 +1,505 @@
+// 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.
+//
+// Generating a fingerprint consists of two major steps:
+// (1) Gather all the necessary data.
+// (2) Write it into a protocol buffer.
+//
+// Step (2) is as simple as it sounds -- it's really just a matter of copying
+// data. Step (1) requires waiting on several asynchronous callbacks, which are
+// managed by the FingerprintDataLoader class.
+
+#include "components/autofill/content/browser/risk/fingerprint.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/cpu.h"
+#include "base/logging.h"
+#include "base/scoped_observer.h"
+#include "base/strings/string_split.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/font_list_async.h"
+#include "content/public/browser/geolocation_provider.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/geoposition.h"
+#include "content/public/common/webplugininfo.h"
+#include "gpu/config/gpu_info.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
+#include "third_party/WebKit/public/platform/WebScreenInfo.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/screen.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() {
+ const 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();
+}
+
+Fingerprint::MachineCharacteristics::BrowserFeature
+ DialogTypeToBrowserFeature(DialogType dialog_type) {
+ switch (dialog_type) {
+ case DIALOG_TYPE_AUTOCHECKOUT:
+ return Fingerprint::MachineCharacteristics::FEATURE_AUTOCHECKOUT;
+
+ case DIALOG_TYPE_REQUEST_AUTOCOMPLETE:
+ return Fingerprint::MachineCharacteristics::FEATURE_REQUEST_AUTOCOMPLETE;
+ }
+
+ NOTREACHED();
+ return Fingerprint::MachineCharacteristics::FEATURE_UNKNOWN;
+}
+
+// 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 = NULL;
+ 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<content::WebPluginInfo>& plugins,
+ Fingerprint::MachineCharacteristics* machine) {
+ for (std::vector<content::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<content::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);
+ }
+}
+
+// This function 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());
+
+ const 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);
+
+ const gfx::Rect screen_rect(screen_info.rect);
+ const gfx::Rect available_rect(screen_info.availableRect);
+ const 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 gpu::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(
+ uint64 obfuscated_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,
+ DialogType dialog_type,
+ const std::string& app_locale,
+ 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<content::WebPluginInfo>& plugins);
+ void OnGotGeoposition(const content::Geoposition& geoposition);
+
+ // Methods that run on the IO thread to communicate with the
+ // GeolocationProvider.
+ void LoadGeoposition();
+ void OnGotGeopositionOnIOThread(const content::Geoposition& geoposition);
+
+ // 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.
+ // Weak reference because the GpuDataManager class is a singleton.
+ content::GpuDataManager* const gpu_data_manager_;
+
+ // Ensures that any observer registratiosn for the GPU data are cleaned up by
+ // the time this object is destroyed.
+ ScopedObserver<content::GpuDataManager, FingerprintDataLoader> gpu_observer_;
+
+ // The callback used as an "observer" of the GeolocationProvider. Accessed
+ // only on the IO thread.
+ content::GeolocationProvider::LocationUpdateCallback geolocation_callback_;
+
+ // Data that will be passed on to the next loading phase. See the comment for
+ // GetFingerprint() for a description of these variables.
+ const uint64 obfuscated_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_;
+ DialogType dialog_type_;
+
+ // Data that will be loaded asynchronously.
+ scoped_ptr<base::ListValue> fonts_;
+ std::vector<content::WebPluginInfo> plugins_;
+ bool waiting_on_plugins_;
+ content::Geoposition geoposition_;
+
+ // The current application locale.
+ std::string app_locale_;
+
+ // 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(
+ uint64 obfuscated_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,
+ DialogType dialog_type,
+ const std::string& app_locale,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback)
+ : gpu_data_manager_(content::GpuDataManager::GetInstance()),
+ gpu_observer_(this),
+ obfuscated_gaia_id_(obfuscated_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),
+ dialog_type_(dialog_type),
+ waiting_on_plugins_(true),
+ callback_(callback) {
+ DCHECK(!install_time_.is_null());
+
+ // Load GPU data if needed.
+ if (!gpu_data_manager_->IsCompleteGpuInfoAvailable()) {
+ gpu_observer_.Add(gpu_data_manager_);
+ gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
+ }
+
+#if defined(ENABLE_PLUGINS)
+ // Load plugin data.
+ content::PluginService::GetInstance()->GetPlugins(
+ base::Bind(&FingerprintDataLoader::OnGotPlugins, base::Unretained(this)));
+#else
+ waiting_on_plugins_ = false;
+#endif
+
+ // Load font data.
+ content::GetFontListAsync(
+ base::Bind(&FingerprintDataLoader::OnGotFonts, base::Unretained(this)));
+
+ // Load geolocation data.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO, FROM_HERE,
+ base::Bind(&FingerprintDataLoader::LoadGeoposition,
+ base::Unretained(this)));
+}
+
+void FingerprintDataLoader::OnGpuInfoUpdate() {
+ if (!gpu_data_manager_->IsCompleteGpuInfoAvailable())
+ return;
+
+ gpu_observer_.Remove(gpu_data_manager_);
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::OnGotFonts(scoped_ptr<base::ListValue> fonts) {
+ DCHECK(!fonts_);
+ fonts_.reset(fonts.release());
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::OnGotPlugins(
+ const std::vector<content::WebPluginInfo>& plugins) {
+ DCHECK(waiting_on_plugins_);
+ waiting_on_plugins_ = false;
+ plugins_ = plugins;
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::OnGotGeoposition(
+ const content::Geoposition& geoposition) {
+ DCHECK(!geoposition_.Validate());
+
+ geoposition_ = geoposition;
+ DCHECK(geoposition_.Validate() ||
+ geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE);
+
+ MaybeFillFingerprint();
+}
+
+void FingerprintDataLoader::LoadGeoposition() {
+ geolocation_callback_ =
+ base::Bind(&FingerprintDataLoader::OnGotGeopositionOnIOThread,
+ base::Unretained(this));
+ content::GeolocationProvider::GetInstance()->AddLocationUpdateCallback(
+ geolocation_callback_, false);
+}
+
+void FingerprintDataLoader::OnGotGeopositionOnIOThread(
+ const content::Geoposition& geoposition) {
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&FingerprintDataLoader::OnGotGeoposition,
+ base::Unretained(this), geoposition));
+
+ // Unregister as an observer, since this class instance might be destroyed
+ // after this callback. Note: It's important to unregister *after* posting
+ // the task above. Unregistering as an observer can have the side-effect of
+ // modifying the value of |geoposition|.
+ bool removed =
+ content::GeolocationProvider::GetInstance()->RemoveLocationUpdateCallback(
+ geolocation_callback_);
+ DCHECK(removed);
+}
+
+void FingerprintDataLoader::MaybeFillFingerprint() {
+ // If all of the data has been loaded, fill the fingerprint and clean up.
+ if (gpu_data_manager_->IsCompleteGpuInfoAvailable() &&
+ fonts_ &&
+ !waiting_on_plugins_ &&
+ (geoposition_.Validate() ||
+ geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE)) {
+ 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(app_locale_);
+ machine->set_charset(charset_);
+ machine->set_user_agent(content::GetUserAgent(GURL()));
+ machine->set_ram(base::SysInfo::AmountOfPhysicalMemory());
+ machine->set_browser_build(version_);
+ machine->set_browser_feature(DialogTypeToBrowserFeature(dialog_type_));
+ AddFontsToFingerprint(*fonts_, machine);
+ AddPluginsToFingerprint(plugins_, machine);
+ AddAcceptLanguagesToFingerprint(accept_languages_, machine);
+ AddScreenInfoToFingerprint(screen_info_, machine);
+ AddCpuInfoToFingerprint(machine);
+ AddGpuInfoToFingerprint(machine);
+
+ // TODO(isherman): Record the user_and_device_name_hash.
+ // TODO(isherman): Record 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 more user behavior data.
+ if (geoposition_.error_code == content::Geoposition::ERROR_CODE_NONE) {
+ Fingerprint::UserCharacteristics::Location* location =
+ fingerprint->mutable_user_characteristics()->mutable_location();
+ location->set_altitude(geoposition_.altitude);
+ location->set_latitude(geoposition_.latitude);
+ location->set_longitude(geoposition_.longitude);
+ location->set_accuracy(geoposition_.accuracy);
+ location->set_time_in_ms(
+ (geoposition_.timestamp - base::Time::UnixEpoch()).InMilliseconds());
+ }
+
+ Fingerprint::Metadata* metadata = fingerprint->mutable_metadata();
+ metadata->set_timestamp_ms(
+ (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds());
+ metadata->set_obfuscated_gaia_id(obfuscated_gaia_id_);
+ metadata->set_fingerprinter_version(kFingerprinterVersion);
+
+ callback_.Run(fingerprint.Pass());
+}
+
+} // namespace
+
+namespace internal {
+
+void GetFingerprintInternal(
+ uint64 obfuscated_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,
+ DialogType dialog_type,
+ const std::string& app_locale,
+ 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(obfuscated_gaia_id, window_bounds, content_bounds,
+ screen_info, version, charset, accept_languages,
+ install_time, dialog_type, app_locale, callback);
+}
+
+} // namespace internal
+
+void GetFingerprint(
+ uint64 obfuscated_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,
+ DialogType dialog_type,
+ const std::string& app_locale,
+ 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(
+ obfuscated_gaia_id, window_bounds, content_bounds, screen_info, version,
+ charset, accept_languages, install_time, dialog_type, app_locale,
+ callback);
+}
+
+} // namespace risk
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.h b/chromium/components/autofill/content/browser/risk/fingerprint.h
new file mode 100644
index 00000000000..d66bde43b86
--- /dev/null
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.h
@@ -0,0 +1,68 @@
+// 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.
+//
+// Generates fingerprints appropriate for sending to the Google Wallet Risk
+// engine, which is the fraud-detection engine used for purchases powered by
+// Google Wallet. A fingerprint encapsulates machine and user characteristics.
+// Because much of the data is privacy-sensitive, fingerprints should only be
+// generated with explicit user consent, including consent to gather geolocation
+// data.
+
+#ifndef COMPONENTS_AUTOFILL_CONTENT_BROWSER_RISK_FINGERPRINT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_RISK_FINGERPRINT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.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.
+// |obfuscated_gaia_id| is an obfuscated 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(
+ uint64 obfuscated_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,
+ DialogType dialog_type,
+ const std::string& app_locale,
+ const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
+
+} // namespace risk
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_RISK_FINGERPRINT_H_
diff --git a/chromium/components/autofill/content/browser/risk/proto/fingerprint.proto b/chromium/components/autofill/content/browser/risk/proto/fingerprint.proto
new file mode 100644
index 00000000000..5cacd76d645
--- /dev/null
+++ b/chromium/components/autofill/content/browser/risk/proto/fingerprint.proto
@@ -0,0 +1,224 @@
+// 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.
+//
+// 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;
+ }
+
+ // Browser features that integrate with Risk.
+ enum BrowserFeature {
+ FEATURE_UNKNOWN = 0; // Should not be reachable.
+ FEATURE_AUTOCHECKOUT = 1;
+ FEATURE_REQUEST_AUTOCOMPLETE = 2;
+ }
+
+ // A hash of the concatenatation of:
+ // * The username of the user currently logged into computer / device.
+ // * The user-assigned computer or device name.
+ optional fixed64 user_and_device_name_hash = 1;
+
+ // Build version string for the current operating system.
+ optional string operating_system_build = 2;
+
+ // Browser install time (hours since epoch).
+ optional int64 browser_install_time_hours = 3;
+
+ // Fonts installed on the machine.
+ repeated string font = 4;
+
+ // Plug-ins installed on the machine.
+ repeated Plugin plugin = 5;
+
+ // Delta in ms of the device's time zone from UTC.
+ optional int64 utc_offset_ms = 6;
+
+ // IETF-formatted language tag. e.g. "en", "en-US", "es-419", etc.
+ // http://en.wikipedia.org/wiki/IETF_language_tag
+ optional string browser_language = 7;
+
+ // User-requested language code of viewed sites. Languages in
+ // accept-languages.
+ repeated string requested_language = 8;
+
+ // Default charset of the browser. (e.g. ISO-8859-1, obtained from
+ // document.defaultCharset)
+ optional string charset = 9;
+
+ // The number of physical screens.
+ optional int32 screen_count = 10;
+
+ // Information about the user's monitor's physical screen size.
+ // (e.g. 1024 x 768)
+ optional Dimension screen_size = 11;
+
+ // The color depth of the user's screen (obtained from screen.colorDepth
+ // or screen.pixelDepth)
+ optional int32 screen_color_depth = 12;
+
+ // 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 = 13;
+
+ optional string user_agent = 14;
+
+ // Total size of each hard drive partition.
+ repeated int32 partition_size = 15;
+
+ optional Cpu cpu = 16;
+
+ // Total RAM in bytes.
+ optional int64 ram = 17;
+
+ // Graphics card being used.
+ optional Graphics graphics_card = 18;
+
+ // Build version string for browser.
+ optional string browser_build = 19;
+
+ // The client-side feature corresponding to this request.
+ optional BrowserFeature browser_feature = 20;
+ }
+
+ // Contains properties relating to more transient computer / browser state.
+ message TransientState {
+ // Corresponds to window.innerWidth / innerHeight
+ 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 double accuracy = 4;
+ // Milliseconds since epoch since measurement.
+ optional int64 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;
+ // Obfuscated Gaia id associated with transaction.
+ optional uint64 obfuscated_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;
+}
diff --git a/chromium/components/autofill/content/browser/wallet/OWNERS b/chromium/components/autofill/content/browser/wallet/OWNERS
new file mode 100644
index 00000000000..cba869d6b8a
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/OWNERS
@@ -0,0 +1 @@
+dbeam@chromium.org
diff --git a/chromium/components/autofill/content/browser/wallet/form_field_error.cc b/chromium/components/autofill/content/browser/wallet/form_field_error.cc
new file mode 100644
index 00000000000..826ea37df2c
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/form_field_error.cc
@@ -0,0 +1,154 @@
+// 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/content/browser/wallet/form_field_error.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+FormFieldError::ErrorType ErrorTypeFromString(const std::string& error_type) {
+ if (LowerCaseEqualsASCII(error_type, "unknown_error"))
+ return FormFieldError::UNKNOWN_ERROR;
+ if (LowerCaseEqualsASCII(error_type, "invalid_phone_number"))
+ return FormFieldError::INVALID_PHONE_NUMBER;
+ if (LowerCaseEqualsASCII(error_type, "invalid_postal_code"))
+ return FormFieldError::INVALID_POSTAL_CODE;
+ if (LowerCaseEqualsASCII(error_type, "invalid_address"))
+ return FormFieldError::INVALID_ADDRESS;
+ if (LowerCaseEqualsASCII(error_type, "invalid_card_details"))
+ return FormFieldError::INVALID_CARD_DETAILS;
+ if (LowerCaseEqualsASCII(error_type, "invalid_city"))
+ return FormFieldError::INVALID_CITY;
+ if (LowerCaseEqualsASCII(error_type, "invalid_instrument"))
+ return FormFieldError::INVALID_INSTRUMENT;
+ if (LowerCaseEqualsASCII(error_type, "invalid_state"))
+ return FormFieldError::INVALID_STATE;
+ if (LowerCaseEqualsASCII(error_type, "required_field_not_set"))
+ return FormFieldError::REQUIRED_FIELD_NOT_SET;
+ return FormFieldError::UNKNOWN_ERROR;
+}
+
+FormFieldError::Location LocationFromString(const std::string& location) {
+ if (LowerCaseEqualsASCII(location, "unknown_location"))
+ return FormFieldError::UNKNOWN_LOCATION;
+ if (LowerCaseEqualsASCII(location, "payment_instrument"))
+ return FormFieldError::PAYMENT_INSTRUMENT;
+ if (LowerCaseEqualsASCII(location, "shipping_address"))
+ return FormFieldError::SHIPPING_ADDRESS;
+ if (LowerCaseEqualsASCII(location, "legal_address"))
+ return FormFieldError::LEGAL_ADDRESS;
+ return FormFieldError::UNKNOWN_LOCATION;
+}
+
+} // namespace
+
+FormFieldError::FormFieldError(ErrorType error_type, Location location)
+ : error_type_(error_type),
+ location_(location) {}
+
+FormFieldError::~FormFieldError() {}
+
+ServerFieldType FormFieldError::GetAutofillType() const {
+ switch (error_type_) {
+ case INVALID_PHONE_NUMBER:
+ if (location_ == LEGAL_ADDRESS || location_ == PAYMENT_INSTRUMENT)
+ return PHONE_BILLING_WHOLE_NUMBER;
+ if (location_ == SHIPPING_ADDRESS)
+ return PHONE_HOME_WHOLE_NUMBER;
+ break;
+
+ case INVALID_POSTAL_CODE:
+ case INVALID_CITY:
+ case INVALID_STATE:
+ if (location_ == LEGAL_ADDRESS || location_ == PAYMENT_INSTRUMENT)
+ return ADDRESS_BILLING_ZIP;
+ if (location_ == SHIPPING_ADDRESS)
+ return ADDRESS_HOME_ZIP;
+ break;
+
+ case INVALID_ADDRESS:
+ if (location_ == LEGAL_ADDRESS || location_ == PAYMENT_INSTRUMENT)
+ return ADDRESS_BILLING_LINE1;
+ if (location_ == SHIPPING_ADDRESS)
+ return ADDRESS_HOME_LINE1;
+ break;
+
+ case INVALID_CARD_DETAILS:
+ return CREDIT_CARD_VERIFICATION_CODE;
+
+ case INVALID_INSTRUMENT:
+ return CREDIT_CARD_NUMBER;
+
+ case REQUIRED_FIELD_NOT_SET:
+ case UNKNOWN_ERROR:
+ return MAX_VALID_FIELD_TYPE;
+ }
+
+ return MAX_VALID_FIELD_TYPE;
+}
+
+// TODO(ahutter): L10n after UX provides strings.
+base::string16 FormFieldError::GetErrorMessage() const {
+ switch (error_type_) {
+ case INVALID_PHONE_NUMBER:
+ return base::ASCIIToUTF16("Not a valid phone number");
+
+ case INVALID_POSTAL_CODE:
+ return base::ASCIIToUTF16("Not a valid zip code");
+
+ case INVALID_CITY:
+ return base::ASCIIToUTF16("Zip code is not valid for the entered city");
+
+ case INVALID_STATE:
+ return base::ASCIIToUTF16("Zip code is not valid for the entered state");
+
+ case INVALID_ADDRESS:
+ return base::ASCIIToUTF16("Not a valid street address");
+
+ case INVALID_CARD_DETAILS:
+ return base::ASCIIToUTF16("Not a valid CVN");
+
+ case INVALID_INSTRUMENT:
+ return base::ASCIIToUTF16("Not a valid CC#");
+
+ case REQUIRED_FIELD_NOT_SET:
+ return base::ASCIIToUTF16("Required field is missing");
+
+ case UNKNOWN_ERROR:
+ return base::ASCIIToUTF16("An unknown error occurred");
+ }
+
+ NOTREACHED();
+ return base::string16();
+}
+
+// static
+FormFieldError FormFieldError::CreateFormFieldError(
+ const base::DictionaryValue& dictionary) {
+ FormFieldError form_field_error(UNKNOWN_ERROR, UNKNOWN_LOCATION);
+
+ std::string error_type;
+ if (dictionary.GetString("type", &error_type))
+ form_field_error.error_type_ = ErrorTypeFromString(error_type);
+
+ std::string location;
+ if (dictionary.GetString("location", &location))
+ form_field_error.location_ = LocationFromString(location);
+
+ return form_field_error;
+}
+
+bool FormFieldError::operator==(const FormFieldError& other) const {
+ return error_type_ == other.error_type_ && location_ == other.location_;
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/form_field_error.h b/chromium/components/autofill/content/browser/wallet/form_field_error.h
new file mode 100644
index 00000000000..60df6d94148
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/form_field_error.h
@@ -0,0 +1,88 @@
+// 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_FORM_FIELD_ERROR_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_FORM_FIELD_ERROR_H_
+
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+namespace wallet {
+
+// Class for representing a single Wallet server side validation error.
+class FormFieldError {
+ public:
+ // The validation error returned from the server.
+ enum ErrorType {
+ UNKNOWN_ERROR,
+ INVALID_PHONE_NUMBER,
+ INVALID_POSTAL_CODE,
+ // Bad street address.
+ INVALID_ADDRESS,
+ // Bad CVC.
+ INVALID_CARD_DETAILS,
+ // Wallet sends this when ZIP is invalid for the given city.
+ INVALID_CITY,
+ // Catch-all for many errors. E.g., no address given, no address ID,
+ // invalid card number. Wallet should only send us this error for invalid
+ // card number.
+ INVALID_INSTRUMENT,
+ // Wallet sends this when ZIP is invalid for the given state.
+ INVALID_STATE,
+ REQUIRED_FIELD_NOT_SET,
+ // TODO(ahutter): Add INVALID_COUNTRY when user can select country in the
+ // chooser.
+ };
+
+ // The section of the "form" where the error occurred.
+ enum Location {
+ UNKNOWN_LOCATION,
+ PAYMENT_INSTRUMENT,
+ SHIPPING_ADDRESS,
+ // Currently Sugar uses the billing address as user's legal address. So any
+ // error in billing address will be accompanied by an error in legal
+ // address. The client side should map LEGAL_ADDRESS to the billing address.
+ // This will ensure compatibility in case Sugar starts having a separate
+ // legal address form.
+ LEGAL_ADDRESS,
+ };
+
+ FormFieldError(ErrorType error_type, Location location);
+ ~FormFieldError();
+
+ ErrorType error_type() const { return error_type_; }
+ Location location() const { return location_; }
+
+ // Gets the appropriate field type for |location| and |error_type|.
+ ServerFieldType GetAutofillType() const;
+
+ // Gets a user facing error message appropriate for |location| and
+ // |error_type|.
+ base::string16 GetErrorMessage() const;
+
+ // Creates an instance of FormFieldError from the input dictionary.
+ static FormFieldError CreateFormFieldError(
+ const base::DictionaryValue& dictionary);
+
+ bool operator==(const FormFieldError& other) const;
+
+ private:
+ // The type of error as defined by the Wallet server.
+ ErrorType error_type_;
+
+ // The location of the error as defined by the Wallet server.
+ Location location_;
+
+ // This class is intentionally copyable and assignable.
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_FORM_FIELD_ERROR_H_
diff --git a/chromium/components/autofill/content/browser/wallet/full_wallet.cc b/chromium/components/autofill/content/browser/wallet/full_wallet.cc
new file mode 100644
index 00000000000..f66b28a9857
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/full_wallet.cc
@@ -0,0 +1,323 @@
+// 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/content/browser/wallet/full_wallet.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+
+namespace {
+
+const size_t kPanSize = 16;
+const size_t kBinSize = 6;
+const size_t kCvnSize = 3;
+const size_t kEncryptedRestSize = 12;
+
+} // 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() {}
+
+// static
+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));
+}
+
+// static
+scoped_ptr<FullWallet>
+ FullWallet::CreateFullWalletFromClearText(
+ int expiration_month,
+ int expiration_year,
+ const std::string& pan,
+ const std::string& cvn,
+ scoped_ptr<Address> billing_address,
+ scoped_ptr<Address> shipping_address) {
+ DCHECK(billing_address);
+ DCHECK(!pan.empty());
+ DCHECK(!cvn.empty());
+
+ scoped_ptr<FullWallet> wallet(new FullWallet(
+ expiration_month,
+ expiration_year,
+ std::string(), // no iin -- clear text pan/cvn are set below.
+ std::string(), // no encrypted_rest -- clear text pan/cvn are set below.
+ billing_address.Pass(),
+ shipping_address.Pass(),
+ std::vector<RequiredAction>())); // no required actions in clear text.
+ wallet->pan_ = pan;
+ wallet->cvn_ = cvn;
+ return wallet.Pass();
+}
+
+base::string16 FullWallet::GetInfo(const AutofillType& type) {
+ switch (type.GetStorableType()) {
+ case CREDIT_CARD_NUMBER:
+ return UTF8ToUTF16(GetPan());
+
+ case CREDIT_CARD_NAME:
+ return billing_address()->recipient_name();
+
+ case CREDIT_CARD_VERIFICATION_CODE:
+ return UTF8ToUTF16(GetCvn());
+
+ case CREDIT_CARD_EXP_MONTH:
+ if (expiration_month() == 0)
+ return base::string16();
+ return base::IntToString16(expiration_month());
+
+ case CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ if (expiration_year() == 0)
+ return base::string16();
+ return base::IntToString16(expiration_year());
+
+ case CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ if (expiration_year() == 0)
+ return base::string16();
+ return base::IntToString16(expiration_year() % 100);
+
+ case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ if (expiration_month() == 0 || expiration_year() == 0)
+ return base::string16();
+ return base::IntToString16(expiration_month()) + ASCIIToUTF16("/") +
+ base::IntToString16(expiration_year() % 100);
+
+ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ if (expiration_month() == 0 || expiration_year() == 0)
+ return base::string16();
+ return base::IntToString16(expiration_month()) + ASCIIToUTF16("/") +
+ base::IntToString16(expiration_year());
+
+ case CREDIT_CARD_TYPE: {
+ std::string internal_type =
+ CreditCard::GetCreditCardType(UTF8ToUTF16(GetPan()));
+ if (internal_type == kGenericCard)
+ return base::string16();
+ return CreditCard::TypeForDisplay(internal_type);
+ }
+
+ default:
+ NOTREACHED();
+ }
+
+ return base::string16();
+}
+
+bool FullWallet::HasRequiredAction(RequiredAction action) const {
+ DCHECK(ActionAppliesToFullWallet(action));
+ return std::find(required_actions_.begin(),
+ required_actions_.end(),
+ action) != required_actions_.end();
+}
+
+base::string16 FullWallet::TypeAndLastFourDigits() {
+ CreditCard card;
+ card.SetRawInfo(CREDIT_CARD_NUMBER,
+ GetInfo(AutofillType(CREDIT_CARD_NUMBER)));
+ return card.TypeAndLastFourDigits();
+}
+
+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);
+}
+
+void FullWallet::DecryptCardInfo() {
+ // |encrypted_rest_| must be of length |kEncryptedRestSize| in order for
+ // decryption to succeed and the server will not pad it with zeros.
+ while (encrypted_rest_.size() < kEncryptedRestSize) {
+ encrypted_rest_ = '0' + encrypted_rest_;
+ }
+
+ DCHECK_EQ(kEncryptedRestSize, encrypted_rest_.size());
+
+ 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);
+}
+
+const std::string& FullWallet::GetPan() {
+ if (pan_.empty())
+ DecryptCardInfo();
+ return pan_;
+}
+
+const std::string& FullWallet::GetCvn() {
+ if (cvn_.empty())
+ DecryptCardInfo();
+ return cvn_;
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/full_wallet.h b/chromium/components/autofill/content/browser/wallet/full_wallet.h
new file mode 100644
index 00000000000..84cc82e3835
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/full_wallet.h
@@ -0,0 +1,150 @@
+// 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_CONTENT_BROWSER_WALLET_FULL_WALLET_H_
+#define COMPONENTS_AUTOFILL_CONTENT_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 "base/strings/string16.h"
+#include "components/autofill/content/browser/wallet/required_action.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+
+class AutofillType;
+
+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);
+
+ // Returns a wallet built from the provided clear-text data.
+ // Data is not validated; |pan|, |cvn| and |billing_address| must be set.
+ static scoped_ptr<FullWallet>
+ CreateFullWalletFromClearText(int expiration_month,
+ int expiration_year,
+ const std::string& pan,
+ const std::string& cvn,
+ scoped_ptr<Address> billing_address,
+ scoped_ptr<Address> shipping_address);
+
+ // Returns corresponding data for |type|.
+ base::string16 GetInfo(const AutofillType& type);
+
+ // Whether or not |action| is in |required_actions_|.
+ bool HasRequiredAction(RequiredAction action) const;
+
+ // The type of the card that this FullWallet contains and the last four digits
+ // like this "Visa - 4111".
+ base::string16 TypeAndLastFourDigits();
+
+ 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 scoped_ptr<FullWallet> GetTestFullWallet();
+ friend scoped_ptr<FullWallet> GetTestFullWalletInstrumentOnly();
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, CreateFullWallet);
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, CreateFullWalletWithRequiredActions);
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, RestLengthCorrectDecryptionTest);
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, RestLengthUnderDecryptionTest);
+ FRIEND_TEST_ALL_PREFIXES(FullWalletTest, GetCreditCardInfo);
+
+ 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);
+
+ // Decrypts both |pan_| and |cvn_|.
+ void DecryptCardInfo();
+
+ // 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();
+
+ // 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_CONTENT_BROWSER_WALLET_FULL_WALLET_H_
diff --git a/chromium/components/autofill/content/browser/wallet/full_wallet_unittest.cc b/chromium/components/autofill/content/browser/wallet/full_wallet_unittest.cc
new file mode 100644
index 00000000000..6f662131ffd
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/full_wallet_unittest.cc
@@ -0,0 +1,530 @@
+// 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 "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/full_wallet.h"
+#include "components/autofill/content/browser/wallet/required_action.h"
+#include "components/autofill/content/browser/wallet/wallet_test_util.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kFullWalletValidResponse[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":3000,"
+ " \"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\":\"admin_area_name\","
+ " \"postal_code_number\":\"postal_code_number\","
+ " \"country_name_code\":\"US\""
+ " }"
+ " },"
+ " \"shipping_address\":"
+ " {"
+ " \"id\":\"address_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\":\"US\""
+ " }"
+ " },"
+ " \"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\":\"address_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\":\"address_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\":\"address_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\":\"address_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\":\"address_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\":\"address_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,
+ std::string(),
+ std::string(),
+ 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,
+ std::string(),
+ std::string(),
+ 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);
+ std::vector<RequiredAction> required_actions;
+ FullWallet full_wallet(12,
+ 3000,
+ "iin",
+ "rest",
+ GetTestAddress(),
+ GetTestNonDefaultShippingAddress(),
+ required_actions);
+ EXPECT_EQ(full_wallet, *FullWallet::CreateFullWallet(*dict));
+}
+
+TEST_F(FullWalletTest, RestLengthCorrectDecryptionTest) {
+ std::vector<RequiredAction> required_actions;
+ FullWallet full_wallet(12,
+ 2012,
+ "528512",
+ "5ec4feecf9d6",
+ GetTestAddress(),
+ GetTestShippingAddress(),
+ required_actions);
+ std::vector<uint8> one_time_pad;
+ EXPECT_TRUE(base::HexStringToBytes("5F04A8704183", &one_time_pad));
+ full_wallet.set_one_time_pad(one_time_pad);
+ EXPECT_EQ(ASCIIToUTF16("5285121925598459"),
+ full_wallet.GetInfo(AutofillType(CREDIT_CARD_NUMBER)));
+ EXPECT_EQ(ASCIIToUTF16("989"),
+ full_wallet.GetInfo(AutofillType(CREDIT_CARD_VERIFICATION_CODE)));
+}
+
+TEST_F(FullWalletTest, RestLengthUnderDecryptionTest) {
+ std::vector<RequiredAction> required_actions;
+ FullWallet full_wallet(12,
+ 2012,
+ "528512",
+ "4c567667e6",
+ GetTestAddress(),
+ GetTestShippingAddress(),
+ required_actions);
+ std::vector<uint8> one_time_pad;
+ EXPECT_TRUE(base::HexStringToBytes("063AD35324BF", &one_time_pad));
+ full_wallet.set_one_time_pad(one_time_pad);
+ EXPECT_EQ(ASCIIToUTF16("5285127106109719"),
+ full_wallet.GetInfo(AutofillType(CREDIT_CARD_NUMBER)));
+ EXPECT_EQ(ASCIIToUTF16("385"),
+ full_wallet.GetInfo(AutofillType(CREDIT_CARD_VERIFICATION_CODE)));
+}
+
+TEST_F(FullWalletTest, GetCreditCardInfo) {
+ std::vector<RequiredAction> required_actions;
+ FullWallet full_wallet(12,
+ 2015,
+ "528512",
+ "1a068673eb0",
+ GetTestAddress(),
+ GetTestShippingAddress(),
+ required_actions);
+
+ EXPECT_EQ(ASCIIToUTF16("15"),
+ full_wallet.GetInfo(AutofillType(CREDIT_CARD_EXP_2_DIGIT_YEAR)));
+
+ EXPECT_EQ(ASCIIToUTF16("12/15"),
+ full_wallet.GetInfo(
+ AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR)));
+
+ EXPECT_EQ(ASCIIToUTF16("12/2015"),
+ full_wallet.GetInfo(
+ AutofillType(CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR)));
+
+ std::vector<uint8> one_time_pad;
+ EXPECT_TRUE(base::HexStringToBytes("075DA779F98B", &one_time_pad));
+ full_wallet.set_one_time_pad(one_time_pad);
+ EXPECT_EQ(ASCIIToUTF16("MasterCard"),
+ full_wallet.GetInfo(AutofillType(CREDIT_CARD_TYPE)));
+}
+
+TEST_F(FullWalletTest, CreateFullWalletFromClearTextData) {
+ scoped_ptr<FullWallet> full_wallet =
+ FullWallet::CreateFullWalletFromClearText(
+ 11, 2012,
+ "5555555555554444", "123",
+ GetTestAddress(), GetTestShippingAddress());
+ EXPECT_EQ(ASCIIToUTF16("5555555555554444"),
+ full_wallet->GetInfo(AutofillType(CREDIT_CARD_NUMBER)));
+ EXPECT_EQ(ASCIIToUTF16("MasterCard"),
+ full_wallet->GetInfo(AutofillType(CREDIT_CARD_TYPE)));
+ EXPECT_EQ(ASCIIToUTF16("123"),
+ full_wallet->GetInfo(AutofillType(CREDIT_CARD_VERIFICATION_CODE)));
+ EXPECT_EQ(ASCIIToUTF16("11/12"),
+ full_wallet->GetInfo(
+ AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR)));
+ EXPECT_TRUE(GetTestAddress()->EqualsIgnoreID(
+ *full_wallet->billing_address()));
+ EXPECT_TRUE(GetTestShippingAddress()->EqualsIgnoreID(
+ *full_wallet->shipping_address()));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/instrument.cc b/chromium/components/autofill/content/browser/wallet/instrument.cc
new file mode 100644
index 00000000000..d50bc453638
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/instrument.cc
@@ -0,0 +1,125 @@
+// 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/content/browser/wallet/instrument.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/validation.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+// Converts a known Autofill card type to a Instrument::FormOfPayment.
+// Used for creating new Instruments.
+Instrument::FormOfPayment FormOfPaymentFromCardType(const std::string& type) {
+ if (type == kAmericanExpressCard)
+ return Instrument::AMEX;
+ else if (type == kDiscoverCard)
+ return Instrument::DISCOVER;
+ else if (type == kMasterCard)
+ return Instrument::MASTER_CARD;
+ else if (type == kVisaCard)
+ return Instrument::VISA;
+
+ return Instrument::UNKNOWN;
+}
+
+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 CreditCard& card,
+ const base::string16& card_verification_number,
+ const AutofillProfile& profile)
+ : primary_account_number_(card.GetRawInfo(CREDIT_CARD_NUMBER)),
+ card_verification_number_(card_verification_number),
+ expiration_month_(card.expiration_month()),
+ expiration_year_(card.expiration_year()),
+ form_of_payment_(FormOfPaymentFromCardType(card.type())),
+ address_(new Address(profile)) {
+ Init();
+}
+
+Instrument::Instrument(const base::string16& primary_account_number,
+ const base::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()) {
+ 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_(instrument.address() ?
+ new Address(*instrument.address()) : NULL) {
+ 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();
+}
+
+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/chromium/components/autofill/content/browser/wallet/instrument.h b/chromium/components/autofill/content/browser/wallet/instrument.h
new file mode 100644
index 00000000000..b706a7e340d
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/instrument.h
@@ -0,0 +1,105 @@
+// 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_CONTENT_BROWSER_WALLET_INSTRUMENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_INSTRUMENT_H_
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+
+class AutofillProfile;
+class CreditCard;
+
+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,
+ };
+
+ // Convert the info in |card| to an Instrument using |profile| for address.
+ Instrument(const CreditCard& card,
+ const base::string16& card_verification_number,
+ const AutofillProfile& profile);
+
+ Instrument(const base::string16& primary_account_number,
+ const base::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;
+
+ const base::string16& primary_account_number() const {
+ return primary_account_number_;
+ }
+ const base::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_.get(); }
+ FormOfPayment form_of_payment() const { return form_of_payment_; }
+ const base::string16& last_four_digits() const { return last_four_digits_; }
+ const std::string& object_id() const { return object_id_; }
+ void set_object_id(const std::string& object_id) { object_id_ = object_id; }
+
+ private:
+ void Init();
+
+ // |primary_account_number_| is expected to be \d{12-19}.
+ base::string16 primary_account_number_;
+
+ // |card_verification_number_| is expected to be \d{3-4}.
+ base::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_|.
+ base::string16 last_four_digits_;
+
+ // Externalized Online Wallet id for this instrument.
+ std::string object_id_;
+
+ DISALLOW_ASSIGN(Instrument);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_INSTRUMENT_H_
diff --git a/chromium/components/autofill/content/browser/wallet/instrument_unittest.cc b/chromium/components/autofill/content/browser/wallet/instrument_unittest.cc
new file mode 100644
index 00000000000..b5f273f87fe
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/instrument_unittest.cc
@@ -0,0 +1,66 @@
+// 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 "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/instrument.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+#include "components/autofill/content/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());
+}
+
+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", "US");
+ 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/chromium/components/autofill/content/browser/wallet/mock_wallet_client.cc b/chromium/components/autofill/content/browser/wallet/mock_wallet_client.cc
new file mode 100644
index 00000000000..994bd3eaf41
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/mock_wallet_client.cc
@@ -0,0 +1,17 @@
+// 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/content/browser/wallet/mock_wallet_client.h"
+
+namespace autofill {
+namespace wallet {
+
+MockWalletClient::MockWalletClient(net::URLRequestContextGetter* context,
+ wallet::WalletClientDelegate* delegate)
+ : wallet::WalletClient(context, delegate) {}
+
+MockWalletClient::~MockWalletClient() {}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h b/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h
new file mode 100644
index 00000000000..c8d03b33a46
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h
@@ -0,0 +1,67 @@
+// 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_CONTENT_BROWSER_WALLET_MOCK_WALLET_CLIENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_MOCK_WALLET_CLIENT_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/content/browser/wallet/instrument.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+#include "components/autofill/content/browser/wallet/wallet_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill {
+namespace wallet {
+
+// A mock version of WalletClient that never issues real requests, just records
+// mock calls to each entry point.
+class MockWalletClient : public WalletClient {
+ public:
+ MockWalletClient(net::URLRequestContextGetter* context,
+ WalletClientDelegate* delegate);
+ virtual ~MockWalletClient();
+
+ MOCK_METHOD1(GetWalletItems, void(const GURL& source_url));
+
+ MOCK_METHOD3(AcceptLegalDocuments,
+ void(const std::vector<WalletItems::LegalDocument*>& documents,
+ const std::string& google_transaction_id,
+ const GURL& source_url));
+
+ MOCK_METHOD2(AuthenticateInstrument,
+ void(const std::string& instrument_id,
+ const std::string& card_verification_number));
+
+ MOCK_METHOD1(GetFullWallet,
+ void(const WalletClient::FullWalletRequest& request));
+
+ // Methods with scoped_ptrs can't be mocked but by using the implementation
+ // below the same effect can be achieved.
+ virtual void SaveToWallet(scoped_ptr<wallet::Instrument> instrument,
+ scoped_ptr<wallet::Address> address,
+ const GURL& source_url) OVERRIDE {
+ SaveToWalletMock(instrument.get(), address.get(), source_url);
+ }
+
+ MOCK_METHOD3(SaveToWalletMock,
+ void(Instrument* instrument,
+ Address* address,
+ const GURL& source_url));
+
+ MOCK_METHOD4(SendAutocheckoutStatus,
+ void(autofill::AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::vector<AutocheckoutStatistic>& latency_statistics,
+ const std::string& google_transaction_id));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockWalletClient);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_MOCK_WALLET_CLIENT_H_
diff --git a/chromium/components/autofill/content/browser/wallet/required_action.cc b/chromium/components/autofill/content/browser/wallet/required_action.cc
new file mode 100644
index 00000000000..28f9b20ba2f
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/required_action.cc
@@ -0,0 +1,66 @@
+// 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/content/browser/wallet/required_action.h"
+
+#include "base/logging.h"
+#include "base/strings/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 == CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS ||
+ 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/chromium/components/autofill/content/browser/wallet/required_action.h b/chromium/components/autofill/content/browser/wallet/required_action.h
new file mode 100644
index 00000000000..eb536220953
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/required_action.h
@@ -0,0 +1,44 @@
+// 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_CONTENT_BROWSER_WALLET_REQUIRED_ACTION_H_
+#define COMPONENTS_AUTOFILL_CONTENT_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_CONTENT_BROWSER_WALLET_REQUIRED_ACTION_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_address.cc b/chromium/components/autofill/content/browser/wallet/wallet_address.cc
new file mode 100644
index 00000000000..8ab59982f2b
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_address.cc
@@ -0,0 +1,332 @@
+// 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/content/browser/wallet/wallet_address.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/state_names.h"
+
+namespace autofill {
+namespace wallet {
+
+// Server specified type for address with complete details.
+const char kFullAddress[] = "FULL";
+
+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 missing 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";
+ }
+
+ Address* address = 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);
+
+ bool is_minimal_address = false;
+ if (dictionary.GetBoolean("is_minimal_address", &is_minimal_address))
+ address->set_is_complete_address(!is_minimal_address);
+ else
+ DVLOG(1) << "Response from Google Wallet missing is_minimal_address bit";
+
+ return address;
+}
+
+} // namespace
+
+Address::Address() {}
+
+Address::Address(const AutofillProfile& profile)
+ : country_name_code_(
+ UTF16ToASCII(profile.GetRawInfo(ADDRESS_HOME_COUNTRY))),
+ recipient_name_(profile.GetRawInfo(NAME_FULL)),
+ address_line_1_(profile.GetRawInfo(ADDRESS_HOME_LINE1)),
+ address_line_2_(profile.GetRawInfo(ADDRESS_HOME_LINE2)),
+ locality_name_(profile.GetRawInfo(ADDRESS_HOME_CITY)),
+ postal_code_number_(profile.GetRawInfo(ADDRESS_HOME_ZIP)),
+ phone_number_(profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER)),
+ is_complete_address_(true) {
+ state_names::GetNameAndAbbreviation(profile.GetRawInfo(ADDRESS_HOME_STATE),
+ NULL,
+ &administrative_area_name_);
+ StringToUpperASCII(&administrative_area_name_);
+}
+
+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),
+ is_complete_address_(true) {
+}
+
+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";
+
+ std::string address_state;
+ if (!dictionary.GetString("type", &address_state))
+ DVLOG(1) << "Response from Google Wallet missing type/state of address";
+
+ scoped_ptr<Address> address(
+ new Address(country_code,
+ name,
+ address1,
+ address2,
+ city,
+ state,
+ postal_code,
+ phone_number,
+ std::string()));
+ address->set_is_complete_address(address_state == kFullAddress);
+
+ return address.Pass();
+}
+
+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 {
+#if defined(OS_ANDROID)
+ // TODO(aruslan): improve this stub implementation.
+ return recipient_name();
+#else
+ // TODO(estade): improve this stub implementation + l10n.
+ return recipient_name() + ASCIIToUTF16(", ") + address_line_1();
+#endif
+}
+
+string16 Address::DisplayNameDetail() const {
+#if defined(OS_ANDROID)
+ // TODO(aruslan): improve this stub implementation.
+ return address_line_1();
+#else
+ return string16();
+#endif
+}
+
+string16 Address::GetInfo(const AutofillType& type,
+ const std::string& app_locale) const {
+ if (type.html_type() == HTML_TYPE_COUNTRY_CODE) {
+ DCHECK(IsStringASCII(country_name_code()));
+ return ASCIIToUTF16(country_name_code());
+ } else if (type.html_type() == HTML_TYPE_STREET_ADDRESS) {
+ base::string16 address = address_line_1();
+ if (!address_line_2().empty())
+ address += ASCIIToUTF16(", ") + address_line_2();
+ return address;
+ }
+
+ switch (type.GetStorableType()) {
+ 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(), app_locale);
+ return country.name();
+ }
+
+ case PHONE_HOME_WHOLE_NUMBER:
+ return phone_number();
+
+ // TODO(estade): implement more.
+ default:
+ NOTREACHED();
+ return string16();
+ }
+}
+
+bool Address::EqualsIgnoreID(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_ &&
+ is_complete_address_ == other.is_complete_address_;
+}
+
+bool Address::operator==(const Address& other) const {
+ return object_id_ == other.object_id_ && EqualsIgnoreID(other);
+}
+
+bool Address::operator!=(const Address& other) const {
+ return !(*this == other);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_address.h b/chromium/components/autofill/content/browser/wallet/wallet_address.h
new file mode 100644
index 00000000000..cead6cc5d4c
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_address.h
@@ -0,0 +1,201 @@
+// 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_CONTENT_BROWSER_WALLET_WALLET_ADDRESS_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_ADDRESS_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace autofill {
+
+class AutofillProfile;
+class AutofillType;
+
+namespace wallet {
+
+// TODO(ahutter): This address is a lot like
+// components/autofill/core/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:
+ // TODO(ahutter): Use additional fields (descriptive_name, is_post_box,
+ // is_valid, is_default) when SaveToWallet is implemented.
+ // See http://crbug.com/164284.
+
+ Address();
+
+ // Using the raw info in |profile|, create a wallet::Address.
+ explicit Address(const AutofillProfile& profile);
+
+ Address(const std::string& country_name_code,
+ const base::string16& recipient_name,
+ const base::string16& address_line_1,
+ const base::string16& address_line_2,
+ const base::string16& locality_name,
+ const base::string16& administrative_area_name,
+ const base::string16& postal_code_number,
+ const base::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);
+
+ // TODO(ahutter): Make obvious in the function name that this public method
+ // only works for shipping address and assumes existance of "postal_address".
+ // 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.
+ base::string16 DisplayName() const;
+
+ // Returns a string that could be used as a sub-label, suitable for display
+ // to the user together with DisplayName().
+ base::string16 DisplayNameDetail() const;
+
+ // Returns data appropriate for |type|.
+ base::string16 GetInfo(const AutofillType& type,
+ const std::string& app_locale) const;
+
+ const std::string& country_name_code() const { return country_name_code_; }
+ const base::string16& recipient_name() const { return recipient_name_; }
+ const base::string16& address_line_1() const { return address_line_1_; }
+ const base::string16& address_line_2() const { return address_line_2_; }
+ const base::string16& locality_name() const { return locality_name_; }
+ const base::string16& administrative_area_name() const {
+ return administrative_area_name_;
+ }
+ const base::string16& postal_code_number() const {
+ return postal_code_number_;
+ }
+ const base::string16& phone_number() const { return phone_number_; }
+ const std::string& object_id() const { return object_id_; }
+ bool is_complete_address() const {
+ return is_complete_address_;
+ }
+
+ void set_country_name_code(const std::string& country_name_code) {
+ country_name_code_ = country_name_code;
+ }
+ void set_recipient_name(const base::string16& recipient_name) {
+ recipient_name_ = recipient_name;
+ }
+ void set_address_line_1(const base::string16& address_line_1) {
+ address_line_1_ = address_line_1;
+ }
+ void set_address_line_2(const base::string16& address_line_2) {
+ address_line_2_ = address_line_2;
+ }
+ void set_locality_name(const base::string16& locality_name) {
+ locality_name_ = locality_name;
+ }
+ void set_administrative_area_name(
+ const base::string16& administrative_area_name) {
+ administrative_area_name_ = administrative_area_name;
+ }
+ void set_postal_code_number(const base::string16& postal_code_number) {
+ postal_code_number_ = postal_code_number;
+ }
+ void set_phone_number(const base::string16& phone_number) {
+ phone_number_ = phone_number;
+ }
+ void set_object_id(const std::string& object_id) {
+ object_id_ = object_id;
+ }
+ void set_is_complete_address(bool is_complete_address) {
+ is_complete_address_ = is_complete_address;
+ }
+
+ // Tests if this address exact matches |other|. |object_id| is ignored.
+ bool EqualsIgnoreID(const Address& other) const;
+
+ // Tests if this address exact matches |other| including |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".
+ base::string16 recipient_name_;
+
+ // |address_line_1| and |address_line_2| correspond to the "AddressLine"
+ // elements in xAL, which are used to hold unstructured text.
+ base::string16 address_line_1_;
+ base::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.
+ base::string16 locality_name_;
+
+ // Top-level administrative subdivision of this country.
+ // Examples: US state, IT region, UK constituent nation, JP prefecture.
+ // Note: this must be in short form, e.g. TX rather than Texas.
+ base::string16 administrative_area_name_;
+
+ // Despite the name, |postal_code_number_| values are frequently alphanumeric.
+ // Examples: "94043", "SW1W", "SW1W 9TQ".
+ base::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/.
+ base::string16 phone_number_;
+
+ // Externalized Online Wallet id for this address.
+ std::string object_id_;
+
+ // Server's understanding of this address as complete address or not.
+ bool is_complete_address_;
+
+ // This class is intentionally copyable.
+ DISALLOW_ASSIGN(Address);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_ADDRESS_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_address_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_address_unittest.cc
new file mode 100644
index 00000000000..9135f54570f
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_address_unittest.cc
@@ -0,0 +1,478 @@
+// 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 "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_profile.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\","
+ " \"is_minimal_address\":true,"
+ " \"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\","
+ " \"type\":\"FULL\""
+ "}";
+
+} // 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, AddressEqualsIgnoreID) {
+ Address address1("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"),
+ "id1");
+ // Same as address1, only id is different.
+ Address address2("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"),
+ "id2");
+ // Has same id as address1, but name is different.
+ Address address3("country_name_code",
+ ASCIIToUTF16("a_different_name"),
+ ASCIIToUTF16("address_line_1"),
+ ASCIIToUTF16("address_line_2"),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "id1");
+ // Same as address1, but no id.
+ Address address4("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"),
+ std::string());
+
+ // Compare the address has id field to itself.
+ EXPECT_EQ(address1, address1);
+ EXPECT_TRUE(address1.EqualsIgnoreID(address1));
+
+ // Compare the address has no id field to itself
+ EXPECT_EQ(address4, address4);
+ EXPECT_TRUE(address4.EqualsIgnoreID(address4));
+
+ // Compare two addresses with different id.
+ EXPECT_NE(address1, address2);
+ EXPECT_TRUE(address1.EqualsIgnoreID(address2));
+ EXPECT_TRUE(address2.EqualsIgnoreID(address1));
+
+ // Compare two different addresses.
+ EXPECT_NE(address1, address3);
+ EXPECT_FALSE(address1.EqualsIgnoreID(address3));
+ EXPECT_FALSE(address3.EqualsIgnoreID(address1));
+
+ // Compare two same addresses, one has id, the other doesn't.
+ EXPECT_NE(address1, address4);
+ EXPECT_TRUE(address1.EqualsIgnoreID(address4));
+ EXPECT_TRUE(address4.EqualsIgnoreID(address1));
+}
+
+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"),
+ std::string());
+ EXPECT_EQ(address, *Address::CreateAddress(*dict_));
+}
+
+TEST_F(WalletAddressTest, CreateAddressWithIDMissingObjectId) {
+ SetUpDictionary(kAddressMissingObjectId);
+ EXPECT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressMissingCountryNameCode) {
+ SetUpDictionary(kAddressMissingCountryNameCode);
+ EXPECT_EQ(NULL, Address::CreateAddress(*dict_).get());
+ EXPECT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressMissingRecipientName) {
+ SetUpDictionary(kAddressMissingRecipientName);
+ EXPECT_EQ(NULL, Address::CreateAddress(*dict_).get());
+ EXPECT_EQ(NULL, Address::CreateAddressWithID(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateAddressMissingPostalCodeNumber) {
+ SetUpDictionary(kAddressMissingPostalCodeNumber);
+ EXPECT_EQ(NULL, Address::CreateAddress(*dict_).get());
+ EXPECT_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");
+ address.set_is_complete_address(false);
+ EXPECT_EQ(address, *Address::CreateAddress(*dict_));
+ EXPECT_EQ(address, *Address::CreateAddressWithID(*dict_));
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddressMissingCountryNameCode) {
+ SetUpDictionary(kClientAddressMissingCountryCode);
+ EXPECT_EQ(NULL, Address::CreateDisplayAddress(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddressMissingName) {
+ SetUpDictionary(kClientAddressMissingName);
+ EXPECT_EQ(NULL, Address::CreateDisplayAddress(*dict_).get());
+}
+
+TEST_F(WalletAddressTest, CreateDisplayAddressMissingPostalCode) {
+ SetUpDictionary(kClientAddressMissingPostalCode);
+ EXPECT_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"),
+ std::string());
+ EXPECT_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"),
+ std::string());
+
+ 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()));
+}
+
+TEST_F(WalletAddressTest, FromAutofillProfile) {
+ {
+ AutofillProfile profile(test::GetFullProfile());
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("tx"));
+ Address address(profile);
+ EXPECT_EQ(ASCIIToUTF16("TX"), address.administrative_area_name());
+ }
+
+ {
+ AutofillProfile profile(test::GetFullProfile());
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("Texas"));
+ Address address(profile);
+ EXPECT_EQ(ASCIIToUTF16("TX"), address.administrative_area_name());
+ }
+
+ {
+ AutofillProfile profile(test::GetFullProfile());
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("TX"));
+ Address address(profile);
+ EXPECT_EQ(ASCIIToUTF16("TX"), address.administrative_area_name());
+ }
+
+ {
+ AutofillProfile profile(test::GetFullProfile());
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("txeas"));
+ Address address(profile);
+ EXPECT_TRUE(address.administrative_area_name().empty());
+ }
+}
+
+// Verifies that WalletAddress::GetInfo() can correctly return both country
+// codes and localized country names.
+TEST_F(WalletAddressTest, GetCountryInfo) {
+ Address address("FR",
+ 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"),
+ "id1");
+
+ AutofillType type = AutofillType(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE);
+ EXPECT_EQ(ASCIIToUTF16("FR"), address.GetInfo(type, "en-US"));
+
+ type = AutofillType(HTML_TYPE_COUNTRY_NAME, HTML_MODE_NONE);
+ EXPECT_EQ(ASCIIToUTF16("France"), address.GetInfo(type, "en-US"));
+
+ type = AutofillType(ADDRESS_HOME_COUNTRY);
+ EXPECT_EQ(ASCIIToUTF16("France"), address.GetInfo(type, "en-US"));
+}
+
+// Verifies that WalletAddress::GetInfo() can correctly return a concatenated
+// full street address.
+TEST_F(WalletAddressTest, GetStreetAddress) {
+ // Address has both lines 1 and 2.
+ Address address1("FR",
+ 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"),
+ "id1");
+ AutofillType type = AutofillType(HTML_TYPE_STREET_ADDRESS, HTML_MODE_NONE);
+ EXPECT_EQ(ASCIIToUTF16("address_line_1, address_line_2"),
+ address1.GetInfo(type, "en-US"));
+
+ // Address has only line 1.
+ Address address2("FR",
+ ASCIIToUTF16("recipient_name"),
+ ASCIIToUTF16("address_line_1"),
+ base::string16(),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "id1");
+ EXPECT_EQ(ASCIIToUTF16("address_line_1"), address2.GetInfo(type, "en-US"));
+
+ // Address has no address lines.
+ Address address3("FR",
+ ASCIIToUTF16("recipient_name"),
+ base::string16(),
+ base::string16(),
+ ASCIIToUTF16("locality_name"),
+ ASCIIToUTF16("administrative_area_name"),
+ ASCIIToUTF16("postal_code_number"),
+ ASCIIToUTF16("phone_number"),
+ "id1");
+ EXPECT_EQ(base::string16(), address3.GetInfo(type, "en-US"));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client.cc b/chromium/components/autofill/content/browser/wallet/wallet_client.cc
new file mode 100644
index 00000000000..bb5c525273d
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client.cc
@@ -0,0 +1,911 @@
+// 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/content/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/message_loop/message_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/browser/wallet/form_field_error.h"
+#include "components/autofill/content/browser/wallet/instrument.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+#include "components/autofill/content/browser/wallet/wallet_client_delegate.h"
+#include "components/autofill/content/browser/wallet/wallet_items.h"
+#include "components/autofill/content/browser/wallet/wallet_service_url.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "crypto/random.h"
+#include "google_apis/google_api_keys.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"
+
+// TODO(ahutter): Change all VLOGs to DVLOGs after dogfood.
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+const char kFormEncodedMimeType[] = "application/x-www-form-urlencoded";
+const char kJsonMimeType[] = "application/json";
+const char kEscrowNewInstrumentFormat[] =
+ "request_content_type=application/json&request=%s&cvn=%s&card_number=%s";
+const char kEscrowCardVerificationNumberFormat[] =
+ "request_content_type=application/json&request=%s&cvn=%s";
+const char kGetFullWalletRequestFormat[] =
+ "request_content_type=application/json&request=%s&otp=%s:%s";
+const size_t kOneTimePadLength = 6;
+
+// 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;
+
+std::string AutocheckoutStatusToString(AutocheckoutStatus status) {
+ switch (status) {
+ case MISSING_FIELDMAPPING:
+ return "MISSING_FIELDMAPPING";
+ case MISSING_ADVANCE:
+ return "MISSING_ADVANCE";
+ case MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING:
+ return "MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING";
+ case MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING:
+ return "MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING";
+ 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";
+ case AUTOCHECKOUT_STATUS_NUM_STATUS:
+ NOTREACHED();
+ }
+ 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";
+}
+
+std::string RiskCapabilityToString(
+ WalletClient::RiskCapability risk_capability) {
+ switch (risk_capability) {
+ case WalletClient::RELOGIN:
+ return "RELOGIN";
+ case WalletClient::VERIFY_CVC:
+ return "VERIFY_CVC";
+ }
+ NOTREACHED();
+ return "NOT_POSSIBLE";
+}
+
+WalletClient::ErrorType StringToErrorType(const std::string& error_type) {
+ std::string trimmed;
+ TrimWhitespaceASCII(error_type,
+ TRIM_ALL,
+ &trimmed);
+ if (LowerCaseEqualsASCII(trimmed, "buyer_account_error"))
+ return WalletClient::BUYER_ACCOUNT_ERROR;
+ if (LowerCaseEqualsASCII(trimmed, "unsupported_merchant"))
+ return WalletClient::UNSUPPORTED_MERCHANT;
+ if (LowerCaseEqualsASCII(trimmed, "internal_error"))
+ return WalletClient::INTERNAL_ERROR;
+ if (LowerCaseEqualsASCII(trimmed, "invalid_params"))
+ return WalletClient::INVALID_PARAMS;
+ if (LowerCaseEqualsASCII(trimmed, "service_unavailable"))
+ return WalletClient::SERVICE_UNAVAILABLE;
+ if (LowerCaseEqualsASCII(trimmed, "unsupported_api_version"))
+ return WalletClient::UNSUPPORTED_API_VERSION;
+
+ return WalletClient::UNKNOWN_ERROR;
+}
+
+// Get the more specific WalletClient::ErrorType when the error is
+// |BUYER_ACCOUNT_ERROR|.
+WalletClient::ErrorType BuyerErrorStringToErrorType(
+ const std::string& buyer_error_type) {
+ std::string trimmed;
+ TrimWhitespaceASCII(buyer_error_type,
+ TRIM_ALL,
+ &trimmed);
+ if (LowerCaseEqualsASCII(trimmed, "bla_country_not_supported"))
+ return WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED;
+ if (LowerCaseEqualsASCII(trimmed, "buyer_kyc_error"))
+ return WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS;
+
+ return WalletClient::BUYER_ACCOUNT_ERROR;
+}
+
+// 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 base::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);
+ }
+ }
+}
+
+void GetFormFieldErrors(const base::DictionaryValue& dict,
+ std::vector<FormFieldError>* form_errors) {
+ DCHECK(form_errors->empty());
+ const base::ListValue* form_errors_list;
+ if (!dict.GetList("form_field_error", &form_errors_list))
+ return;
+
+ for (size_t i = 0; i < form_errors_list->GetSize(); ++i) {
+ const base::DictionaryValue* dictionary;
+ if (form_errors_list->GetDictionary(i, &dictionary))
+ form_errors->push_back(FormFieldError::CreateFormFieldError(*dictionary));
+ }
+}
+
+// Converts the |error_type| to the corresponding value from the stable UMA
+// metric enumeration.
+AutofillMetrics::WalletErrorMetric ErrorTypeToUmaMetric(
+ WalletClient::ErrorType error_type) {
+ switch (error_type) {
+ case WalletClient::BAD_REQUEST:
+ return AutofillMetrics::WALLET_BAD_REQUEST;
+ case WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED:
+ return AutofillMetrics::WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED;
+ case WalletClient::BUYER_ACCOUNT_ERROR:
+ return AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR;
+ case WalletClient::INTERNAL_ERROR:
+ return AutofillMetrics::WALLET_INTERNAL_ERROR;
+ case WalletClient::INVALID_PARAMS:
+ return AutofillMetrics::WALLET_INVALID_PARAMS;
+ case WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS:
+ return AutofillMetrics::WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS;
+ case WalletClient::SERVICE_UNAVAILABLE:
+ return AutofillMetrics::WALLET_SERVICE_UNAVAILABLE;
+ case WalletClient::UNSUPPORTED_API_VERSION:
+ return AutofillMetrics::WALLET_UNSUPPORTED_API_VERSION;
+ case WalletClient::UNSUPPORTED_MERCHANT:
+ return AutofillMetrics::WALLET_UNSUPPORTED_MERCHANT;
+ case WalletClient::MALFORMED_RESPONSE:
+ return AutofillMetrics::WALLET_MALFORMED_RESPONSE;
+ case WalletClient::NETWORK_ERROR:
+ return AutofillMetrics::WALLET_NETWORK_ERROR;
+ case WalletClient::UNKNOWN_ERROR:
+ return AutofillMetrics::WALLET_UNKNOWN_ERROR;
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::WALLET_UNKNOWN_ERROR;
+}
+
+// Converts the |required_action| to the corresponding value from the stable UMA
+// metric enumeration.
+AutofillMetrics::WalletRequiredActionMetric RequiredActionToUmaMetric(
+ RequiredAction required_action) {
+ switch (required_action) {
+ case UNKNOWN_TYPE:
+ return AutofillMetrics::UNKNOWN_REQUIRED_ACTION;
+ case CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS:
+ return AutofillMetrics::CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS;
+ case SETUP_WALLET:
+ return AutofillMetrics::SETUP_WALLET;
+ case ACCEPT_TOS:
+ return AutofillMetrics::ACCEPT_TOS;
+ case GAIA_AUTH:
+ return AutofillMetrics::GAIA_AUTH;
+ case UPDATE_EXPIRATION_DATE:
+ return AutofillMetrics::UPDATE_EXPIRATION_DATE;
+ case UPGRADE_MIN_ADDRESS:
+ return AutofillMetrics::UPGRADE_MIN_ADDRESS;
+ case INVALID_FORM_FIELD:
+ return AutofillMetrics::INVALID_FORM_FIELD;
+ case VERIFY_CVV:
+ return AutofillMetrics::VERIFY_CVV;
+ case PASSIVE_GAIA_AUTH:
+ return AutofillMetrics::PASSIVE_GAIA_AUTH;
+ case REQUIRE_PHONE_NUMBER:
+ return AutofillMetrics::REQUIRE_PHONE_NUMBER;
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::UNKNOWN_REQUIRED_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 kBuyerErrorTypeKey[] = "wallet_error.buyer_error_type";
+const char kEncryptedOtpKey[] = "encrypted_otp";
+const char kErrorTypeKey[] = "wallet_error.error_type";
+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 kInstrumentExpMonthKey[] = "instrument.credit_card.exp_month";
+const char kInstrumentExpYearKey[] = "instrument.credit_card.exp_year";
+const char kInstrumentType[] = "instrument.type";
+const char kInstrumentPhoneNumberKey[] = "instrument_phone_number";
+const char kMerchantDomainKey[] = "merchant_domain";
+const char kPhoneNumberRequired[] = "phone_number_required";
+const char kReasonKey[] = "reason";
+const char kRiskCapabilitiesKey[] = "supported_risk_challenge";
+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 kShippingAddressRequired[] = "shipping_address_required";
+const char kAutocheckoutStepsKey[] = "steps";
+const char kSuccessKey[] = "success";
+const char kUpgradedBillingAddressKey[] = "upgraded_billing_address";
+const char kUpgradedInstrumentIdKey[] = "upgraded_instrument_id";
+const char kUseMinimalAddresses[] = "use_minimal_addresses";
+
+} // namespace
+
+WalletClient::FullWalletRequest::FullWalletRequest(
+ const std::string& instrument_id,
+ const std::string& address_id,
+ const GURL& source_url,
+ const std::string& google_transaction_id,
+ const std::vector<RiskCapability> risk_capabilities)
+ : instrument_id(instrument_id),
+ address_id(address_id),
+ source_url(source_url),
+ google_transaction_id(google_transaction_id),
+ risk_capabilities(risk_capabilities) {}
+
+WalletClient::FullWalletRequest::~FullWalletRequest() {}
+
+WalletClient::WalletClient(net::URLRequestContextGetter* context_getter,
+ WalletClientDelegate* delegate)
+ : context_getter_(context_getter),
+ delegate_(delegate),
+ request_type_(NO_PENDING_REQUEST),
+ one_time_pad_(kOneTimePadLength),
+ weak_ptr_factory_(this) {
+ DCHECK(context_getter_.get());
+ DCHECK(delegate_);
+}
+
+WalletClient::~WalletClient() {}
+
+void WalletClient::AcceptLegalDocuments(
+ const std::vector<WalletItems::LegalDocument*>& documents,
+ const std::string& google_transaction_id,
+ const GURL& source_url) {
+ if (documents.empty())
+ return;
+
+ std::vector<std::string> document_ids;
+ for (size_t i = 0; i < documents.size(); ++i) {
+ document_ids.push_back(documents[i]->id());
+ }
+ DoAcceptLegalDocuments(document_ids, google_transaction_id, source_url);
+}
+
+void WalletClient::AuthenticateInstrument(
+ const std::string& instrument_id,
+ const std::string& card_verification_number) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::AuthenticateInstrument,
+ base::Unretained(this),
+ instrument_id,
+ card_verification_number));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = AUTHENTICATE_INSTRUMENT;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData());
+ request_dict.SetString(kInstrumentIdKey, instrument_id);
+
+ std::string json_payload;
+ base::JSONWriter::Write(&request_dict, &json_payload);
+
+ std::string escaped_card_verification_number = net::EscapeUrlEncodedData(
+ card_verification_number, true);
+
+ std::string post_body = base::StringPrintf(
+ kEscrowCardVerificationNumberFormat,
+ net::EscapeUrlEncodedData(json_payload, true).c_str(),
+ escaped_card_verification_number.c_str());
+
+ MakeWalletRequest(GetAuthenticateInstrumentUrl(),
+ post_body,
+ kFormEncodedMimeType);
+}
+
+void WalletClient::GetFullWallet(const FullWalletRequest& full_wallet_request) {
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::GetFullWallet,
+ base::Unretained(this),
+ full_wallet_request));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = GET_FULL_WALLET;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData());
+ request_dict.SetBoolean(kUseMinimalAddresses, false);
+ request_dict.SetBoolean(kPhoneNumberRequired, true);
+
+ request_dict.SetString(kSelectedInstrumentIdKey,
+ full_wallet_request.instrument_id);
+ request_dict.SetString(kSelectedAddressIdKey, full_wallet_request.address_id);
+ request_dict.SetString(
+ kMerchantDomainKey,
+ full_wallet_request.source_url.GetWithEmptyPath().spec());
+ request_dict.SetString(kGoogleTransactionIdKey,
+ full_wallet_request.google_transaction_id);
+ request_dict.SetString(kFeatureKey,
+ DialogTypeToFeatureString(delegate_->GetDialogType()));
+
+ scoped_ptr<base::ListValue> risk_capabilities_list(new base::ListValue());
+ for (std::vector<RiskCapability>::const_iterator it =
+ full_wallet_request.risk_capabilities.begin();
+ it != full_wallet_request.risk_capabilities.end();
+ ++it) {
+ risk_capabilities_list->AppendString(RiskCapabilityToString(*it));
+ }
+ request_dict.Set(kRiskCapabilitiesKey, risk_capabilities_list.release());
+
+ std::string json_payload;
+ base::JSONWriter::Write(&request_dict, &json_payload);
+
+ crypto::RandBytes(&(one_time_pad_[0]), one_time_pad_.size());
+
+ size_t num_bits = one_time_pad_.size() * 8;
+ DCHECK_LE(num_bits, kMaxBits);
+ DCHECK_GE(num_bits, kMinBits);
+
+ std::string post_body = base::StringPrintf(
+ kGetFullWalletRequestFormat,
+ net::EscapeUrlEncodedData(json_payload, true).c_str(),
+ base::HexEncode(&num_bits, 1).c_str(),
+ base::HexEncode(&(one_time_pad_[0]), one_time_pad_.size()).c_str());
+
+ MakeWalletRequest(GetGetFullWalletUrl(), post_body, kFormEncodedMimeType);
+}
+
+void WalletClient::SaveToWallet(scoped_ptr<Instrument> instrument,
+ scoped_ptr<Address> address,
+ const GURL& source_url) {
+ DCHECK(instrument || address);
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::SaveToWallet,
+ base::Unretained(this),
+ base::Passed(&instrument),
+ base::Passed(&address),
+ source_url));
+ return;
+ }
+
+ DCHECK_EQ(NO_PENDING_REQUEST, request_type_);
+ request_type_ = SAVE_TO_WALLET;
+
+ base::DictionaryValue request_dict;
+ request_dict.SetString(kApiKeyKey, google_apis::GetAPIKey());
+ request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData());
+ request_dict.SetString(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+ request_dict.SetBoolean(kUseMinimalAddresses, false);
+ request_dict.SetBoolean(kPhoneNumberRequired, true);
+
+ std::string primary_account_number;
+ std::string card_verification_number;
+ if (instrument) {
+ primary_account_number = net::EscapeUrlEncodedData(
+ UTF16ToUTF8(instrument->primary_account_number()), true);
+ card_verification_number = net::EscapeUrlEncodedData(
+ UTF16ToUTF8(instrument->card_verification_number()), true);
+
+ if (instrument->object_id().empty()) {
+ request_dict.Set(kInstrumentKey, instrument->ToDictionary().release());
+ request_dict.SetString(kInstrumentPhoneNumberKey,
+ instrument->address()->phone_number());
+ } else {
+ DCHECK(instrument->address() ||
+ (instrument->expiration_month() > 0 &&
+ instrument->expiration_year() > 0));
+
+ request_dict.SetString(kUpgradedInstrumentIdKey,
+ instrument->object_id());
+
+ if (instrument->address()) {
+ request_dict.SetString(kInstrumentPhoneNumberKey,
+ instrument->address()->phone_number());
+ request_dict.Set(
+ kUpgradedBillingAddressKey,
+ instrument->address()->ToDictionaryWithoutID().release());
+ }
+
+ if (instrument->expiration_month() > 0 &&
+ instrument->expiration_year() > 0) {
+ DCHECK(!instrument->card_verification_number().empty());
+ request_dict.SetInteger(kInstrumentExpMonthKey,
+ instrument->expiration_month());
+ request_dict.SetInteger(kInstrumentExpYearKey,
+ instrument->expiration_year());
+ }
+
+ if (request_dict.HasKey(kInstrumentKey))
+ request_dict.SetString(kInstrumentType, "CREDIT_CARD");
+ }
+ }
+ if (address) {
+ request_dict.Set(kShippingAddressKey,
+ address->ToDictionaryWithID().release());
+ }
+
+ std::string json_payload;
+ base::JSONWriter::Write(&request_dict, &json_payload);
+
+ if (!card_verification_number.empty()) {
+ std::string post_body;
+ if (!primary_account_number.empty()) {
+ post_body = base::StringPrintf(
+ kEscrowNewInstrumentFormat,
+ net::EscapeUrlEncodedData(json_payload, true).c_str(),
+ card_verification_number.c_str(),
+ primary_account_number.c_str());
+ } else {
+ post_body = base::StringPrintf(
+ kEscrowCardVerificationNumberFormat,
+ net::EscapeUrlEncodedData(json_payload, true).c_str(),
+ card_verification_number.c_str());
+ }
+ MakeWalletRequest(GetSaveToWalletUrl(), post_body, kFormEncodedMimeType);
+ } else {
+ MakeWalletRequest(GetSaveToWalletNoEscrowUrl(),
+ json_payload,
+ kJsonMimeType);
+ }
+}
+
+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(kMerchantDomainKey,
+ source_url.GetWithEmptyPath().spec());
+ request_dict.SetBoolean(kShippingAddressRequired,
+ delegate_->IsShippingAddressRequired());
+ request_dict.SetBoolean(kUseMinimalAddresses, false);
+ request_dict.SetBoolean(kPhoneNumberRequired, true);
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetGetWalletItemsUrl(), post_body, kJsonMimeType);
+}
+
+void WalletClient::SendAutocheckoutStatus(
+ AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::vector<AutocheckoutStatistic>& latency_statistics,
+ const std::string& google_transaction_id) {
+ DVLOG(1) << "Sending Autocheckout Status: " << status
+ << " for: " << source_url;
+ if (HasRequestInProgress()) {
+ pending_requests_.push(base::Bind(&WalletClient::SendAutocheckoutStatus,
+ base::Unretained(this),
+ status,
+ source_url,
+ latency_statistics,
+ 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));
+ if (!latency_statistics.empty()) {
+ scoped_ptr<base::ListValue> latency_statistics_json(
+ new base::ListValue());
+ for (size_t i = 0; i < latency_statistics.size(); ++i) {
+ latency_statistics_json->Append(
+ latency_statistics[i].ToDictionary().release());
+ }
+ request_dict.Set(kAutocheckoutStepsKey,
+ latency_statistics_json.release());
+ }
+ request_dict.SetString(kGoogleTransactionIdKey, google_transaction_id);
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetSendStatusUrl(), post_body, kJsonMimeType);
+}
+
+bool WalletClient::HasRequestInProgress() const {
+ return request_;
+}
+
+void WalletClient::CancelRequests() {
+ request_.reset();
+ request_type_ = NO_PENDING_REQUEST;
+ while (!pending_requests_.empty()) {
+ pending_requests_.pop();
+ }
+}
+
+void WalletClient::DoAcceptLegalDocuments(
+ 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::DoAcceptLegalDocuments,
+ 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());
+ scoped_ptr<base::ListValue> docs_list(new base::ListValue());
+ for (std::vector<std::string>::const_iterator it = document_ids.begin();
+ it != document_ids.end(); ++it) {
+ if (!it->empty())
+ docs_list->AppendString(*it);
+ }
+ request_dict.Set(kAcceptedLegalDocumentKey, docs_list.release());
+
+ std::string post_body;
+ base::JSONWriter::Write(&request_dict, &post_body);
+
+ MakeWalletRequest(GetAcceptLegalDocumentsUrl(), post_body, kJsonMimeType);
+}
+
+void WalletClient::MakeWalletRequest(const GURL& url,
+ const std::string& post_body,
+ const std::string& mime_type) {
+ DCHECK(!HasRequestInProgress());
+
+ request_.reset(net::URLFetcher::Create(
+ 0, url, net::URLFetcher::POST, this));
+ request_->SetRequestContext(context_getter_.get());
+ VLOG(1) << "Making request to " << url << " with post_body=" << post_body;
+ request_->SetUploadData(mime_type, post_body);
+ request_->AddExtraRequestHeader("Authorization: GoogleLogin auth=" +
+ delegate_->GetWalletCookieValue());
+ DVLOG(1) << "Setting authorization header value to "
+ << delegate_->GetWalletCookieValue();
+ request_started_timestamp_ = base::Time::Now();
+ request_->Start();
+
+ delegate_->GetMetricLogger().LogWalletErrorMetric(
+ delegate_->GetDialogType(),
+ AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST);
+ delegate_->GetMetricLogger().LogWalletRequiredActionMetric(
+ delegate_->GetDialogType(),
+ AutofillMetrics::WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST);
+}
+
+// TODO(ahutter): Add manual retry logic if it's necessary.
+void WalletClient::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ delegate_->GetMetricLogger().LogWalletApiCallDuration(
+ RequestTypeToUmaMetric(request_type_),
+ base::Time::Now() - request_started_timestamp_);
+
+ DCHECK_EQ(source, request_.get());
+ VLOG(1) << "Got response from " << source->GetOriginalURL();
+
+ // |request_|, which is aliased to |source|, might continue to be used in this
+ // |method, but should be freed once control leaves the method.
+ scoped_ptr<net::URLFetcher> scoped_request(request_.Pass());
+
+ // Prepare to start the next pending request. This is queued up as an
+ // asynchronous message because |this| WalletClient instance can be destroyed
+ // before the end of the method in response to the current incoming request.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&WalletClient::StartNextPendingRequest,
+ weak_ptr_factory_.GetWeakPtr()));;
+
+ std::string data;
+ source->GetResponseAsString(&data);
+ VLOG(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;
+ HandleWalletError(BAD_REQUEST);
+ 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;
+
+ std::string error_type_string;
+ if (!response_dict->GetString(kErrorTypeKey, &error_type_string)) {
+ HandleWalletError(UNKNOWN_ERROR);
+ return;
+ }
+ WalletClient::ErrorType error_type =
+ StringToErrorType(error_type_string);
+ if (error_type == BUYER_ACCOUNT_ERROR) {
+ // If the error_type is |BUYER_ACCOUNT_ERROR|, then buyer_error_type
+ // field contains more specific information about the error.
+ std::string buyer_error_type_string;
+ if (response_dict->GetString(kBuyerErrorTypeKey,
+ &buyer_error_type_string)) {
+ error_type = BuyerErrorStringToErrorType(buyer_error_type_string);
+ }
+ }
+
+ HandleWalletError(error_type);
+ return;
+ }
+ break;
+ }
+
+ // Anything else is an error.
+ default:
+ request_type_ = NO_PENDING_REQUEST;
+ HandleWalletError(NETWORK_ERROR);
+ return;
+ }
+
+ RequestType type = request_type_;
+ request_type_ = NO_PENDING_REQUEST;
+
+ if (!(type == ACCEPT_LEGAL_DOCUMENTS || type == SEND_STATUS) &&
+ !response_dict) {
+ HandleMalformedResponse(scoped_request.get());
+ return;
+ }
+
+ switch (type) {
+ case ACCEPT_LEGAL_DOCUMENTS:
+ delegate_->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);
+ delegate_->OnDidAuthenticateInstrument(
+ LowerCaseEqualsASCII(trimmed, "success"));
+ } else {
+ HandleMalformedResponse(scoped_request.get());
+ }
+ break;
+ }
+
+ case SEND_STATUS:
+ 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_);
+ LogRequiredActions(full_wallet->required_actions());
+ delegate_->OnDidGetFullWallet(full_wallet.Pass());
+ } else {
+ HandleMalformedResponse(scoped_request.get());
+ }
+ break;
+ }
+
+ case GET_WALLET_ITEMS: {
+ scoped_ptr<WalletItems> wallet_items(
+ WalletItems::CreateWalletItems(*response_dict));
+ if (wallet_items) {
+ LogRequiredActions(wallet_items->required_actions());
+ delegate_->OnDidGetWalletItems(wallet_items.Pass());
+ } else {
+ HandleMalformedResponse(scoped_request.get());
+ }
+ break;
+ }
+
+ case SAVE_TO_WALLET: {
+ 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);
+ std::vector<FormFieldError> form_errors;
+ GetFormFieldErrors(*response_dict, &form_errors);
+ if (instrument_id.empty() && shipping_address_id.empty() &&
+ required_actions.empty()) {
+ HandleMalformedResponse(scoped_request.get());
+ } else {
+ LogRequiredActions(required_actions);
+ delegate_->OnDidSaveToWallet(instrument_id,
+ shipping_address_id,
+ required_actions,
+ form_errors);
+ }
+ break;
+ }
+
+ case NO_PENDING_REQUEST:
+ NOTREACHED();
+ }
+}
+
+void WalletClient::StartNextPendingRequest() {
+ if (pending_requests_.empty())
+ return;
+
+ base::Closure next_request = pending_requests_.front();
+ pending_requests_.pop();
+ next_request.Run();
+}
+
+void WalletClient::HandleMalformedResponse(net::URLFetcher* request) {
+ // Called to inform exponential backoff logic of the error.
+ request->ReceivedContentWasMalformed();
+ HandleWalletError(MALFORMED_RESPONSE);
+}
+
+void WalletClient::HandleWalletError(WalletClient::ErrorType error_type) {
+ std::string error_message;
+ switch (error_type) {
+ case WalletClient::BAD_REQUEST:
+ error_message = "WALLET_BAD_REQUEST";
+ break;
+ case WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED:
+ error_message = "WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED";
+ break;
+ case WalletClient::BUYER_ACCOUNT_ERROR:
+ error_message = "WALLET_BUYER_ACCOUNT_ERROR";
+ break;
+ case WalletClient::INTERNAL_ERROR:
+ error_message = "WALLET_INTERNAL_ERROR";
+ break;
+ case WalletClient::INVALID_PARAMS:
+ error_message = "WALLET_INVALID_PARAMS";
+ break;
+ case WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS:
+ error_message = "WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS";
+ break;
+ case WalletClient::SERVICE_UNAVAILABLE:
+ error_message = "WALLET_SERVICE_UNAVAILABLE";
+ break;
+ case WalletClient::UNSUPPORTED_API_VERSION:
+ error_message = "WALLET_UNSUPPORTED_API_VERSION";
+ break;
+ case WalletClient::UNSUPPORTED_MERCHANT:
+ error_message = "WALLET_UNSUPPORTED_MERCHANT";
+ break;
+ case WalletClient::MALFORMED_RESPONSE:
+ error_message = "WALLET_MALFORMED_RESPONSE";
+ break;
+ case WalletClient::NETWORK_ERROR:
+ error_message = "WALLET_NETWORK_ERROR";
+ break;
+ case WalletClient::UNKNOWN_ERROR:
+ error_message = "WALLET_UNKNOWN_ERROR";
+ break;
+ }
+
+ VLOG(1) << "Wallet encountered a " << error_message;
+
+ delegate_->OnWalletError(error_type);
+ delegate_->GetMetricLogger().LogWalletErrorMetric(
+ delegate_->GetDialogType(), ErrorTypeToUmaMetric(error_type));
+}
+
+// Logs an UMA metric for each of the |required_actions|.
+void WalletClient::LogRequiredActions(
+ const std::vector<RequiredAction>& required_actions) const {
+ for (size_t i = 0; i < required_actions.size(); ++i) {
+ delegate_->GetMetricLogger().LogWalletRequiredActionMetric(
+ delegate_->GetDialogType(),
+ RequiredActionToUmaMetric(required_actions[i]));
+ }
+}
+
+AutofillMetrics::WalletApiCallMetric WalletClient::RequestTypeToUmaMetric(
+ RequestType request_type) const {
+ switch (request_type) {
+ case ACCEPT_LEGAL_DOCUMENTS:
+ return AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS;
+ case AUTHENTICATE_INSTRUMENT:
+ return AutofillMetrics::AUTHENTICATE_INSTRUMENT;
+ case GET_FULL_WALLET:
+ return AutofillMetrics::GET_FULL_WALLET;
+ case GET_WALLET_ITEMS:
+ return AutofillMetrics::GET_WALLET_ITEMS;
+ case SAVE_TO_WALLET:
+ return AutofillMetrics::SAVE_TO_WALLET;
+ case SEND_STATUS:
+ return AutofillMetrics::SEND_STATUS;
+ case NO_PENDING_REQUEST:
+ NOTREACHED();
+ return AutofillMetrics::UNKNOWN_API_CALL;
+ }
+
+ NOTREACHED();
+ return AutofillMetrics::UNKNOWN_API_CALL;
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client.h b/chromium/components/autofill/content/browser/wallet/wallet_client.h
new file mode 100644
index 00000000000..d4f96073127
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client.h
@@ -0,0 +1,270 @@
+// 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_CONTENT_BROWSER_WALLET_WALLET_CLIENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_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/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/autocheckout_statistic.h"
+#include "components/autofill/content/browser/wallet/full_wallet.h"
+#include "components/autofill/content/browser/wallet/wallet_items.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+#include "url/gurl.h"
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace autofill {
+namespace wallet {
+
+class Address;
+class FullWallet;
+class Instrument;
+class WalletClientDelegate;
+
+// 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
+// SaveToWallet to set up their account before continuing.
+// b) If the user has not accepted 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 SaveToWallet.
+// b) The user may also choose to add a new instrument or address using
+// SaveToWallet.
+// 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:
+ // The Risk challenges supported by users of WalletClient.
+ enum RiskCapability {
+ RELOGIN,
+ VERIFY_CVC,
+ };
+
+ // The type of error returned by Online Wallet.
+ enum ErrorType {
+ // Errors to display to users.
+ BUYER_ACCOUNT_ERROR, // Risk deny, unsupported country, or
+ // account closed.
+ BUYER_LEGAL_ADDRESS_NOT_SUPPORTED, // User's Buyer Legal Address is
+ // unsupported by Online Wallet.
+ UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS, // User's "know your customer" KYC
+ // state is not verified (either
+ // KYC_REFER or KYC_FAIL).
+ UNSUPPORTED_MERCHANT, // Merchant is blacklisted due to
+ // compliance violation.
+
+ // API errors.
+ BAD_REQUEST, // Request was very malformed or sent to the
+ // wrong endpoint.
+ INVALID_PARAMS, // API call had missing or invalid parameters.
+ UNSUPPORTED_API_VERSION, // The server API version of the request is no
+ // longer supported.
+
+ // Server errors.
+ INTERNAL_ERROR, // Unknown server side error.
+ SERVICE_UNAVAILABLE, // Online Wallet is down.
+
+ // Other errors.
+ MALFORMED_RESPONSE, // The response from Wallet was malformed.
+ NETWORK_ERROR, // The response code of the server was something
+ // other than a 200 or 400.
+
+ UNKNOWN_ERROR, // Catch all error type.
+ };
+
+ struct FullWalletRequest {
+ public:
+ FullWalletRequest(const std::string& instrument_id,
+ const std::string& address_id,
+ const GURL& source_url,
+ const std::string& google_transaction_id,
+ const std::vector<RiskCapability> risk_capabilities);
+ ~FullWalletRequest();
+
+ // The ID of the backing instrument. Should have been selected by the user
+ // in some UI.
+ std::string instrument_id;
+
+ // The ID of the shipping address. Should have been selected by the user
+ // in some UI.
+ std::string address_id;
+
+ // The URL that Online Wallet usage is being initiated on.
+ GURL source_url;
+
+ // The transaction ID from GetWalletItems.
+ std::string google_transaction_id;
+
+ // The Risk challenges supported by the user of WalletClient
+ std::vector<RiskCapability> risk_capabilities;
+
+ private:
+ DISALLOW_ASSIGN(FullWalletRequest);
+ };
+
+ // |context_getter| is reference counted so it has no lifetime or ownership
+ // requirements. |delegate| must outlive |this|.
+ WalletClient(net::URLRequestContextGetter* context_getter,
+ WalletClientDelegate* delegate);
+
+ 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.
+ virtual 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 |google_transaction_id| is provided in the response to the
+ // GetWalletItems call. If |documents| are empty, |delegate_| will not receive
+ // a corresponding |OnDidAcceptLegalDocuments()| call.
+ virtual void AcceptLegalDocuments(
+ const std::vector<WalletItems::LegalDocument*>& documents,
+ 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|. |delegate_| is notified when the request is
+ // complete. Used to respond to Risk challenges.
+ virtual void AuthenticateInstrument(
+ const std::string& instrument_id,
+ const std::string& card_verification_number);
+
+ // GetFullWallet retrieves the a FullWallet for the user.
+ virtual void GetFullWallet(const FullWalletRequest& full_wallet_request);
+
+ // Saves the data in |instrument| and/or |address| to Wallet. |instrument|
+ // does not have to be complete if its being used to update an existing
+ // instrument, like in the case of expiration date or address only updates.
+ virtual void SaveToWallet(scoped_ptr<Instrument> instrument,
+ scoped_ptr<Address> address,
+ const GURL& source_url);
+
+ // SendAutocheckoutStatus is used for tracking the success of Autocheckout
+ // flows. |status| is the result of the flow, |source_url| is the domain
+ // where the purchase occured, and |google_transaction_id| is the same as the
+ // one provided by GetWalletItems. |latency_statistics| contain statistics
+ // required to measure Autocheckout process.
+ virtual void SendAutocheckoutStatus(
+ autofill::AutocheckoutStatus status,
+ const GURL& source_url,
+ const std::vector<AutocheckoutStatistic>& latency_statistics,
+ const std::string& google_transaction_id);
+
+ bool HasRequestInProgress() const;
+
+ // Cancels and clears the current |request_| and |pending_requests_| (if any).
+ void CancelRequests();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WalletClientTest, PendingRequest);
+ FRIEND_TEST_ALL_PREFIXES(WalletClientTest, CancelRequests);
+
+ enum RequestType {
+ NO_PENDING_REQUEST,
+ ACCEPT_LEGAL_DOCUMENTS,
+ AUTHENTICATE_INSTRUMENT,
+ GET_FULL_WALLET,
+ GET_WALLET_ITEMS,
+ SAVE_TO_WALLET,
+ SEND_STATUS,
+ };
+
+ // Like AcceptLegalDocuments, but takes a vector of document ids.
+ void DoAcceptLegalDocuments(
+ const std::vector<std::string>& document_ids,
+ const std::string& google_transaction_id,
+ const GURL& source_url);
+
+ // Posts |post_body| to |url| with content type |mime_type| and notifies
+ // |delegate_| when the request is complete.
+ void MakeWalletRequest(const GURL& url,
+ const std::string& post_body,
+ const std::string& mime_type);
+
+ // Performs bookkeeping tasks for any invalid requests.
+ void HandleMalformedResponse(net::URLFetcher* request);
+ void HandleNetworkError(int response_code);
+ void HandleWalletError(ErrorType error_type);
+
+ // Start the next pending request (if any).
+ void StartNextPendingRequest();
+
+ // net::URLFetcherDelegate:
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
+
+ // Logs an UMA metric for each of the |required_actions|.
+ void LogRequiredActions(
+ const std::vector<RequiredAction>& required_actions) const;
+
+ // Converts |request_type| to an UMA metric.
+ AutofillMetrics::WalletApiCallMetric RequestTypeToUmaMetric(
+ RequestType request_type) const;
+
+ // 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.
+ WalletClientDelegate* const delegate_; // 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_;
+
+ // Requests that are waiting to be run.
+ std::queue<base::Closure> pending_requests_;
+
+ // When the current request started. Used to track client side latency.
+ base::Time request_started_timestamp_;
+
+ base::WeakPtrFactory<WalletClient> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WalletClient);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_CLIENT_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h b/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h
new file mode 100644
index 00000000000..8de95046e5f
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h
@@ -0,0 +1,88 @@
+// 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_WALLET_CLIENT_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_OBSERVER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/content/browser/wallet/form_field_error.h"
+#include "components/autofill/content/browser/wallet/wallet_client.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+
+class AutofillMetrics;
+
+namespace autofill {
+namespace wallet {
+
+class FullWallet;
+class WalletItems;
+
+// WalletClientDelegate is to be implemented any classes making calls with
+// WalletClient. The appropriate callback method will be called on
+// WalletClientDelegate with the response from the Online Wallet backend.
+class WalletClientDelegate {
+ public:
+ // --------------------------------------
+ // Accessors called when making requests.
+ // --------------------------------------
+
+ // Returns the MetricLogger instance that should be used for logging Online
+ // Wallet metrics.
+ virtual const AutofillMetrics& GetMetricLogger() const = 0;
+
+ // Returns the dialog type that the delegate corresponds to.
+ virtual DialogType GetDialogType() const = 0;
+
+ // Returns the serialized fingerprint data to be sent to the Risk server.
+ virtual std::string GetRiskData() const = 0;
+
+ // Returns the cookie value used for authorization when making requests to
+ // Wallet.
+ virtual std::string GetWalletCookieValue() const = 0;
+
+ // Whether or not shipping address is required by the delegate.
+ virtual bool IsShippingAddressRequired() const = 0;
+
+ // --------------------------------------------------------------------------
+ // Callbacks called with responses from the Online Wallet backend.
+ // --------------------------------------------------------------------------
+
+ // 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 SaveToWallet 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. |form_field_errors| is
+ // populated with the actual form fields that are failing validation.
+ virtual void OnDidSaveToWallet(
+ const std::string& instrument_id,
+ const std::string& address_id,
+ const std::vector<RequiredAction>& required_actions,
+ const std::vector<FormFieldError>& form_field_errors) = 0;
+
+ // Called when a request fails.
+ virtual void OnWalletError(WalletClient::ErrorType error_type) = 0;
+
+ protected:
+ virtual ~WalletClientDelegate() {}
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_BROWSER_WALLET_WALLET_CLIENT_OBSERVER_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc
new file mode 100644
index 00000000000..031e5cd958b
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc
@@ -0,0 +1,1764 @@
+// 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 "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/content/browser/autocheckout_steps.h"
+#include "components/autofill/content/browser/wallet/full_wallet.h"
+#include "components/autofill/content/browser/wallet/instrument.h"
+#include "components/autofill/content/browser/wallet/wallet_client.h"
+#include "components/autofill/content/browser/wallet/wallet_client_delegate.h"
+#include "components/autofill/content/browser/wallet/wallet_items.h"
+#include "components/autofill/content/browser/wallet/wallet_test_util.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/escape.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_request_headers.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"
+#include "url/gurl.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+const char kGoogleTransactionId[] = "google-transaction-id";
+const char kMerchantUrl[] = "https://example.com/path?key=value";
+
+const char kGetFullWalletValidResponse[] =
+ "{"
+ " \"expiration_month\":12,"
+ " \"expiration_year\":3000,"
+ " \"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\":\"US\""
+ " }"
+ " },"
+ " \"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\":\"US\""
+ " }"
+ " },"
+ " \"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\":\"4111\","
+ " \"expiration_month\":12,"
+ " \"expiration_year\":3000,"
+ " \"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\":\"saved_address_id\""
+ "}";
+
+const char kSaveAddressWithRequiredActionsValidResponse[] =
+ "{"
+ " \"form_field_error\":"
+ " ["
+ " {"
+ " \"location\":\"SHIPPING_ADDRESS\","
+ " \"type\":\"INVALID_POSTAL_CODE\""
+ " }"
+ " ],"
+ " \"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[] =
+ "{"
+ " \"form_field_error\":"
+ " ["
+ " {"
+ " \"location\":\"SHIPPING_ADDRESS\","
+ " \"type\":\"INVALID_POSTAL_CODE\""
+ " }"
+ " ],"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kSaveInstrumentAndAddressValidResponse[] =
+ "{"
+ " \"shipping_address_id\":\"saved_address_id\","
+ " \"instrument_id\":\"saved_instrument_id\""
+ "}";
+
+const char kSaveInstrumentAndAddressWithRequiredActionsValidResponse[] =
+ "{"
+ " \"form_field_error\":"
+ " ["
+ " {"
+ " \"location\":\"SHIPPING_ADDRESS\","
+ " \"type\":\"INVALID_POSTAL_CODE\""
+ " }"
+ " ],"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kUpdateInstrumentValidResponse[] =
+ "{"
+ " \"instrument_id\":\"instrument_id\""
+ "}";
+
+const char kUpdateAddressValidResponse[] =
+ "{"
+ " \"shipping_address_id\":\"shipping_address_id\""
+ "}";
+
+const char kUpdateWithRequiredActionsValidResponse[] =
+ "{"
+ " \"form_field_error\":"
+ " ["
+ " {"
+ " \"location\":\"SHIPPING_ADDRESS\","
+ " \"type\":\"INVALID_POSTAL_CODE\""
+ " }"
+ " ],"
+ " \"required_action\":"
+ " ["
+ " \" \\treqUIRE_PhOnE_number \\n\\r\","
+ " \"INVALID_form_field\""
+ " ]"
+ "}";
+
+const char kUpdateMalformedResponse[] =
+ "{"
+ " \"cheese\":\"monkeys\""
+ "}";
+
+const char kAuthenticateInstrumentFailureResponse[] =
+ "{"
+ " \"auth_result\":\"anything else\""
+ "}";
+
+const char kAuthenticateInstrumentSuccessResponse[] =
+ "{"
+ " \"auth_result\":\"SUCCESS\""
+ "}";
+
+const char kErrorResponse[] =
+ "{"
+ " \"error_type\":\"APPLICATION_ERROR\","
+ " \"error_detail\":\"error_detail\","
+ " \"application_error\":\"application_error\","
+ " \"debug_data\":"
+ " {"
+ " \"debug_message\":\"debug_message\","
+ " \"stack_trace\":\"stack_trace\""
+ " },"
+ " \"application_error_data\":\"application_error_data\","
+ " \"wallet_error\":"
+ " {"
+ " \"error_type\":\"SERVICE_UNAVAILABLE\","
+ " \"error_detail\":\"error_detail\","
+ " \"message_for_user\":"
+ " {"
+ " \"text\":\"text\","
+ " \"subtext\":\"subtext\","
+ " \"details\":\"details\""
+ " }"
+ " }"
+ "}";
+
+const char kErrorTypeMissingInResponse[] =
+ "{"
+ " \"error_type\":\"Not APPLICATION_ERROR\","
+ " \"error_detail\":\"error_detail\","
+ " \"application_error\":\"application_error\","
+ " \"debug_data\":"
+ " {"
+ " \"debug_message\":\"debug_message\","
+ " \"stack_trace\":\"stack_trace\""
+ " },"
+ " \"application_error_data\":\"application_error_data\""
+ "}";
+
+// 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_id_1\","
+ "\"doc_id_2\""
+ "],"
+ "\"google_transaction_id\":\"google-transaction-id\","
+ "\"merchant_domain\":\"https://example.com/\""
+ "}";
+
+const char kAuthenticateInstrumentValidRequest[] =
+ "{"
+ "\"instrument_id\":\"instrument_id\","
+ "\"risk_params\":\"risky business\""
+ "}";
+
+const char kGetFullWalletValidRequest[] =
+ "{"
+ "\"feature\":\"REQUEST_AUTOCOMPLETE\","
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"selected_address_id\":\"shipping_address_id\","
+ "\"selected_instrument_id\":\"instrument_id\","
+ "\"supported_risk_challenge\":"
+ "["
+ "],"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kGetFullWalletWithRiskCapabilitesValidRequest[] =
+ "{"
+ "\"feature\":\"REQUEST_AUTOCOMPLETE\","
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"selected_address_id\":\"shipping_address_id\","
+ "\"selected_instrument_id\":\"instrument_id\","
+ "\"supported_risk_challenge\":"
+ "["
+ "\"VERIFY_CVC\""
+ "],"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kGetWalletItemsValidRequest[] =
+ "{"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"shipping_address_required\":true,"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kGetWalletItemsNoShippingRequest[] =
+ "{"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"shipping_address_required\":false,"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kSaveAddressValidRequest[] =
+ "{"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"shipping_address\":"
+ "{"
+ "\"phone_number\":\"save_phone_number\","
+ "\"postal_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"save_address_line_1\","
+ "\"save_address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"save_admin_area_name\","
+ "\"country_name_code\":\"US\","
+ "\"locality_name\":\"save_locality_name\","
+ "\"postal_code_number\":\"save_postal_code_number\","
+ "\"recipient_name\":\"save_recipient_name\""
+ "}"
+ "},"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kSaveInstrumentValidRequest[] =
+ "{"
+ "\"instrument\":"
+ "{"
+ "\"credit_card\":"
+ "{"
+ "\"address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"US\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"exp_month\":12,"
+ "\"exp_year\":3000,"
+ "\"fop_type\":\"VISA\","
+ "\"last_4_digits\":\"4448\""
+ "},"
+ "\"type\":\"CREDIT_CARD\""
+ "},"
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kSaveInstrumentAndAddressValidRequest[] =
+ "{"
+ "\"instrument\":"
+ "{"
+ "\"credit_card\":"
+ "{"
+ "\"address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"US\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"exp_month\":12,"
+ "\"exp_year\":3000,"
+ "\"fop_type\":\"VISA\","
+ "\"last_4_digits\":\"4448\""
+ "},"
+ "\"type\":\"CREDIT_CARD\""
+ "},"
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"shipping_address\":"
+ "{"
+ "\"phone_number\":\"save_phone_number\","
+ "\"postal_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"save_address_line_1\","
+ "\"save_address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"save_admin_area_name\","
+ "\"country_name_code\":\"US\","
+ "\"locality_name\":\"save_locality_name\","
+ "\"postal_code_number\":\"save_postal_code_number\","
+ "\"recipient_name\":\"save_recipient_name\""
+ "}"
+ "},"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kSendAutocheckoutStatusOfSuccessValidRequest[] =
+ "{"
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"success\":true"
+ "}";
+
+const char kSendAutocheckoutStatusWithStatisticsValidRequest[] =
+ "{"
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"steps\":[{\"step_description\":\"1_AUTOCHECKOUT_STEP_SHIPPING\""
+ ",\"time_taken\":100}],"
+ "\"success\":true"
+ "}";
+
+const char kSendAutocheckoutStatusOfFailureValidRequest[] =
+ "{"
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"reason\":\"CANNOT_PROCEED\","
+ "\"success\":false"
+ "}";
+
+const char kUpdateAddressValidRequest[] =
+ "{"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"shipping_address\":"
+ "{"
+ "\"id\":\"shipping_address_id\","
+ "\"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\":\"US\","
+ "\"locality_name\":\"ship_locality_name\","
+ "\"postal_code_number\":\"ship_postal_code_number\","
+ "\"recipient_name\":\"ship_recipient_name\""
+ "}"
+ "},"
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kUpdateInstrumentAddressValidRequest[] =
+ "{"
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"upgraded_billing_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"US\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"upgraded_instrument_id\":\"instrument_id\","
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kUpdateInstrumentAddressWithNameChangeValidRequest[] =
+ "{"
+ "\"instrument_phone_number\":\"phone_number\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"upgraded_billing_address\":"
+ "{"
+ "\"address_line\":"
+ "["
+ "\"address_line_1\","
+ "\"address_line_2\""
+ "],"
+ "\"administrative_area_name\":\"admin_area_name\","
+ "\"country_name_code\":\"US\","
+ "\"locality_name\":\"locality_name\","
+ "\"postal_code_number\":\"postal_code_number\","
+ "\"recipient_name\":\"recipient_name\""
+ "},"
+ "\"upgraded_instrument_id\":\"instrument_id\","
+ "\"use_minimal_addresses\":false"
+ "}";
+
+const char kUpdateInstrumentExpirationDateValidRequest[] =
+ "{"
+ "\"instrument\":"
+ "{"
+ "\"credit_card\":"
+ "{"
+ "\"exp_month\":12,"
+ "\"exp_year\":3000"
+ "},"
+ "\"type\":\"CREDIT_CARD\""
+ "},"
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"phone_number_required\":true,"
+ "\"risk_params\":\"risky business\","
+ "\"upgraded_instrument_id\":\"instrument_id\","
+ "\"use_minimal_addresses\":false"
+ "}";
+
+class MockAutofillMetrics : public AutofillMetrics {
+ public:
+ MockAutofillMetrics() {}
+ MOCK_CONST_METHOD2(LogWalletApiCallDuration,
+ void(WalletApiCallMetric metric,
+ const base::TimeDelta& duration));
+ MOCK_CONST_METHOD2(LogWalletErrorMetric,
+ void(DialogType dialog_type, WalletErrorMetric metric));
+ MOCK_CONST_METHOD2(LogWalletRequiredActionMetric,
+ void(DialogType dialog_type,
+ WalletRequiredActionMetric action));
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics);
+};
+
+class MockWalletClientDelegate : public WalletClientDelegate {
+ public:
+ MockWalletClientDelegate()
+ : full_wallets_received_(0),
+ wallet_items_received_(0),
+ is_shipping_required_(true) {}
+ ~MockWalletClientDelegate() {}
+
+ virtual const AutofillMetrics& GetMetricLogger() const OVERRIDE {
+ return metric_logger_;
+ }
+
+ virtual DialogType GetDialogType() const OVERRIDE {
+ return DIALOG_TYPE_REQUEST_AUTOCOMPLETE;
+ }
+
+ virtual std::string GetRiskData() const OVERRIDE {
+ return "risky business";
+ }
+
+ virtual std::string GetWalletCookieValue() const OVERRIDE {
+ return "gdToken";
+ }
+
+ virtual bool IsShippingAddressRequired() const OVERRIDE {
+ return is_shipping_required_;
+ }
+
+ void SetIsShippingAddressRequired(bool is_shipping_required) {
+ is_shipping_required_ = is_shipping_required;
+ }
+
+ void ExpectLogWalletApiCallDuration(
+ AutofillMetrics::WalletApiCallMetric metric,
+ size_t times) {
+ EXPECT_CALL(metric_logger_,
+ LogWalletApiCallDuration(metric, testing::_)).Times(times);
+ }
+
+ void ExpectWalletErrorMetric(AutofillMetrics::WalletErrorMetric metric) {
+ EXPECT_CALL(
+ metric_logger_,
+ LogWalletErrorMetric(
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric)).Times(1);
+ }
+
+ void ExpectWalletRequiredActionMetric(
+ AutofillMetrics::WalletRequiredActionMetric metric) {
+ EXPECT_CALL(
+ metric_logger_,
+ LogWalletRequiredActionMetric(
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric)).Times(1);
+ }
+
+ void ExpectBaselineMetrics() {
+ EXPECT_CALL(
+ metric_logger_,
+ LogWalletErrorMetric(
+ DIALOG_TYPE_REQUEST_AUTOCOMPLETE,
+ AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST))
+ .Times(1);
+ ExpectWalletRequiredActionMetric(
+ AutofillMetrics::WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST);
+ }
+
+ MockAutofillMetrics* metric_logger() {
+ return &metric_logger_;
+ }
+
+ MOCK_METHOD0(OnDidAcceptLegalDocuments, void());
+ MOCK_METHOD1(OnDidAuthenticateInstrument, void(bool success));
+ MOCK_METHOD4(OnDidSaveToWallet,
+ void(const std::string& instrument_id,
+ const std::string& shipping_address_id,
+ const std::vector<RequiredAction>& required_actions,
+ const std::vector<FormFieldError>& form_field_errors));
+ MOCK_METHOD1(OnWalletError, void(WalletClient::ErrorType error_type));
+
+ 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_;
+ bool is_shipping_required_;
+
+ testing::StrictMock<MockAutofillMetrics> metric_logger_;
+};
+
+} // namespace
+
+class WalletClientTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ wallet_client_.reset(
+ new WalletClient(browser_context_.GetRequestContext(), &delegate_));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ wallet_client_.reset();
+ }
+
+ void VerifyAndFinishRequest(net::HttpStatusCode response_code,
+ const std::string& request_body,
+ const std::string& response_body) {
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+
+ const std::string& upload_data = fetcher->upload_data();
+ EXPECT_EQ(request_body, GetData(upload_data));
+ net::HttpRequestHeaders request_headers;
+ fetcher->GetExtraRequestHeaders(&request_headers);
+ std::string auth_header_value;
+ EXPECT_TRUE(request_headers.GetHeader(
+ net::HttpRequestHeaders::kAuthorization,
+ &auth_header_value));
+ EXPECT_EQ("GoogleLogin auth=gdToken", auth_header_value);
+
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response_body);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+
+ // Pump the message loop to catch up to any asynchronous tasks that might
+ // have been posted from OnURLFetchComplete().
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void VerifyAndFinishFormEncodedRequest(net::HttpStatusCode response_code,
+ const std::string& json_payload,
+ const std::string& response_body,
+ size_t expected_parameter_number) {
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+
+ net::HttpRequestHeaders request_headers;
+ fetcher->GetExtraRequestHeaders(&request_headers);
+ std::string auth_header_value;
+ EXPECT_TRUE(request_headers.GetHeader(
+ net::HttpRequestHeaders::kAuthorization,
+ &auth_header_value));
+ EXPECT_EQ("GoogleLogin auth=gdToken", auth_header_value);
+
+ const std::string& upload_data = fetcher->upload_data();
+ std::vector<std::pair<std::string, std::string> > tokens;
+ base::SplitStringIntoKeyValuePairs(upload_data, '=', '&', &tokens);
+ EXPECT_EQ(tokens.size(), expected_parameter_number);
+
+ size_t num_params = 0U;
+ for (size_t i = 0; i < tokens.size(); ++i) {
+ const std::string& key = tokens[i].first;
+ const std::string& value = tokens[i].second;
+
+ if (key == "request_content_type") {
+ EXPECT_EQ("application/json", value);
+ num_params++;
+ }
+
+ if (key == "request") {
+ EXPECT_EQ(json_payload,
+ GetData(
+ net::UnescapeURLComponent(
+ value, net::UnescapeRule::URL_SPECIAL_CHARS |
+ net::UnescapeRule::REPLACE_PLUS_WITH_SPACE)));
+ num_params++;
+ }
+
+ if (key == "cvn") {
+ EXPECT_EQ("123", value);
+ num_params++;
+ }
+
+ if (key == "card_number") {
+ EXPECT_EQ("4444444444444448", value);
+ num_params++;
+ }
+
+ if (key == "otp") {
+ EXPECT_FALSE(value.empty());
+ num_params++;
+ }
+ }
+ EXPECT_EQ(expected_parameter_number, num_params);
+
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response_body);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+
+ void TestWalletErrorCode(
+ const std::string& error_type_string,
+ const std::string& buyer_error_type_string,
+ WalletClient::ErrorType expected_error_type,
+ AutofillMetrics::WalletErrorMetric expected_autofill_metric) {
+ static const char kResponseTemplate[] =
+ "{"
+ " \"error_type\":\"APPLICATION_ERROR\","
+ " \"error_detail\":\"error_detail\","
+ " \"application_error\":\"application_error\","
+ " \"debug_data\":"
+ " {"
+ " \"debug_message\":\"debug_message\","
+ " \"stack_trace\":\"stack_trace\""
+ " },"
+ " \"application_error_data\":\"application_error_data\","
+ " \"wallet_error\":"
+ " {"
+ " \"error_type\":\"%s\","
+ " %s" // Placeholder for |user_error_type|.
+ " \"error_detail\":\"error_detail\","
+ " \"message_for_user\":"
+ " {"
+ " \"text\":\"text\","
+ " \"subtext\":\"subtext\","
+ " \"details\":\"details\""
+ " }"
+ " }"
+ "}";
+ EXPECT_CALL(delegate_, OnWalletError(expected_error_type)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(expected_autofill_metric);
+
+ std::vector<AutocheckoutStatistic> statistics;
+ wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ statistics,
+ "google_transaction_id");
+ std::string buyer_error;
+ if (!buyer_error_type_string.empty()) {
+ buyer_error = base::StringPrintf("\"buyer_error_type\":\"%s\",",
+ buyer_error_type_string.c_str());
+ }
+ std::string response = base::StringPrintf(kResponseTemplate,
+ error_type_string.c_str(),
+ buyer_error.c_str());
+ VerifyAndFinishRequest(net::HTTP_INTERNAL_SERVER_ERROR,
+ kSendAutocheckoutStatusOfSuccessValidRequest,
+ response);
+ }
+
+ protected:
+ content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<WalletClient> wallet_client_;
+ TestingProfile browser_context_;
+ MockWalletClientDelegate delegate_;
+
+ private:
+ std::string GetData(const std::string& upload_data) {
+ scoped_ptr<Value> root(base::JSONReader::Read(upload_data));
+
+ // If this is not a JSON dictionary, return plain text.
+ if (!root || !root->IsType(Value::TYPE_DICTIONARY))
+ return upload_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);
+ std::string clean_upload_data;
+ base::JSONWriter::Write(dict, &clean_upload_data);
+ return clean_upload_data;
+ }
+
+ net::TestURLFetcherFactory factory_;
+};
+
+TEST_F(WalletClientTest, WalletErrorCodes) {
+ struct {
+ std::string error_type_string;
+ std::string buyer_error_type_string;
+ WalletClient::ErrorType expected_error_type;
+ AutofillMetrics::WalletErrorMetric expected_autofill_metric;
+ } test_cases[] = {
+ // General |BUYER_ACCOUNT_ERROR| with no |buyer_error_type_string|.
+ {
+ "buyer_account_error",
+ "",
+ WalletClient::BUYER_ACCOUNT_ERROR,
+ AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR
+ },
+ // |BUYER_ACCOUNT_ERROR| with "buyer_legal_address_not_supported" in
+ // buyer_error_type field.
+ {
+ "buyer_account_error",
+ "bla_country_not_supported",
+ WalletClient::BUYER_LEGAL_ADDRESS_NOT_SUPPORTED,
+ AutofillMetrics::WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED
+ },
+ // |BUYER_ACCOUNT_ERROR| with KYC error code in buyer_error_type field.
+ {
+ "buyer_account_error",
+ "buyer_kyc_error",
+ WalletClient::UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS,
+ AutofillMetrics::WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS
+ },
+ // |BUYER_ACCOUNT_ERROR| with un-recognizable |buyer_error_type|.
+ {
+ "buyer_account_error",
+ "random_string",
+ WalletClient::BUYER_ACCOUNT_ERROR,
+ AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR
+ },
+ // The following are other error types we could get from Wallet.
+ {
+ "unsupported_merchant",
+ "",
+ WalletClient::UNSUPPORTED_MERCHANT,
+ AutofillMetrics::WALLET_UNSUPPORTED_MERCHANT
+ },
+ {
+ "internal_error",
+ "",
+ WalletClient::INTERNAL_ERROR,
+ AutofillMetrics::WALLET_INTERNAL_ERROR
+ },
+ {
+ "invalid_params",
+ "",
+ WalletClient::INVALID_PARAMS,
+ AutofillMetrics::WALLET_INVALID_PARAMS
+ },
+ {
+ "service_unavailable",
+ "",
+ WalletClient::SERVICE_UNAVAILABLE,
+ AutofillMetrics::WALLET_SERVICE_UNAVAILABLE
+ },
+ {
+ "unsupported_api_version",
+ "",
+ WalletClient::UNSUPPORTED_API_VERSION,
+ AutofillMetrics::WALLET_UNSUPPORTED_API_VERSION
+ },
+ // Any un-recognizable |error_type| is a |UNKNOWN_ERROR|.
+ {
+ "random_string_1",
+ "",
+ WalletClient::UNKNOWN_ERROR,
+ AutofillMetrics::WALLET_UNKNOWN_ERROR
+ },
+ {
+ "random_string_2",
+ "",
+ WalletClient::UNKNOWN_ERROR,
+ AutofillMetrics::WALLET_UNKNOWN_ERROR
+ },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ SCOPED_TRACE(
+ base::StringPrintf("%s - %s",
+ test_cases[i].error_type_string.c_str(),
+ test_cases[i].buyer_error_type_string.c_str()));
+ TestWalletErrorCode(test_cases[i].error_type_string,
+ test_cases[i].buyer_error_type_string,
+ test_cases[i].expected_error_type,
+ test_cases[i].expected_autofill_metric);
+ }
+}
+
+TEST_F(WalletClientTest, WalletErrorResponseMissing) {
+ EXPECT_CALL(delegate_, OnWalletError(
+ WalletClient::UNKNOWN_ERROR)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_UNKNOWN_ERROR);
+
+ std::vector<AutocheckoutStatistic> statistics;
+ wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ statistics,
+ "google_transaction_id");
+ VerifyAndFinishRequest(net::HTTP_INTERNAL_SERVER_ERROR,
+ kSendAutocheckoutStatusOfSuccessValidRequest,
+ kErrorTypeMissingInResponse);
+}
+
+TEST_F(WalletClientTest, NetworkFailureOnExpectedVoidResponse) {
+ EXPECT_CALL(delegate_, OnWalletError(WalletClient::NETWORK_ERROR)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_NETWORK_ERROR);
+
+ std::vector<AutocheckoutStatistic> statistics;
+ wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ statistics,
+ "google_transaction_id");
+ VerifyAndFinishRequest(net::HTTP_UNAUTHORIZED,
+ kSendAutocheckoutStatusOfSuccessValidRequest,
+ std::string());
+}
+
+TEST_F(WalletClientTest, NetworkFailureOnExpectedResponse) {
+ EXPECT_CALL(delegate_, OnWalletError(WalletClient::NETWORK_ERROR)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_NETWORK_ERROR);
+
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_UNAUTHORIZED,
+ kGetWalletItemsValidRequest,
+ std::string());
+}
+
+TEST_F(WalletClientTest, RequestError) {
+ EXPECT_CALL(delegate_, OnWalletError(WalletClient::BAD_REQUEST)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_BAD_REQUEST);
+
+ std::vector<AutocheckoutStatistic> statistics;
+ wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ statistics,
+ "google_transaction_id");
+ VerifyAndFinishRequest(net::HTTP_BAD_REQUEST,
+ kSendAutocheckoutStatusOfSuccessValidRequest,
+ std::string());
+}
+
+TEST_F(WalletClientTest, GetFullWalletSuccess) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_FULL_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ WalletClient::FullWalletRequest full_wallet_request(
+ "instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ "google_transaction_id",
+ std::vector<WalletClient::RiskCapability>());
+ wallet_client_->GetFullWallet(full_wallet_request);
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kGetFullWalletValidRequest,
+ kGetFullWalletValidResponse,
+ 3U);
+ EXPECT_EQ(1U, delegate_.full_wallets_received());
+}
+
+TEST_F(WalletClientTest, GetFullWalletWithRiskCapabilitesSuccess) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_FULL_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ std::vector<WalletClient::RiskCapability> risk_capabilities;
+ risk_capabilities.push_back(WalletClient::VERIFY_CVC);
+ WalletClient::FullWalletRequest full_wallet_request(
+ "instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ "google_transaction_id",
+ risk_capabilities);
+ wallet_client_->GetFullWallet(full_wallet_request);
+
+ VerifyAndFinishFormEncodedRequest(
+ net::HTTP_OK,
+ kGetFullWalletWithRiskCapabilitesValidRequest,
+ kGetFullWalletValidResponse,
+ 3U);
+ EXPECT_EQ(1U, delegate_.full_wallets_received());
+}
+
+
+TEST_F(WalletClientTest, GetFullWalletMalformedResponse) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_FULL_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ WalletClient::FullWalletRequest full_wallet_request(
+ "instrument_id",
+ "shipping_address_id",
+ GURL(kMerchantUrl),
+ "google_transaction_id",
+ std::vector<WalletClient::RiskCapability>());
+ wallet_client_->GetFullWallet(full_wallet_request);
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kGetFullWalletValidRequest,
+ kGetFullWalletInvalidResponse,
+ 3U);
+ EXPECT_EQ(0U, delegate_.full_wallets_received());
+}
+
+TEST_F(WalletClientTest, AcceptLegalDocuments) {
+ EXPECT_CALL(delegate_, OnDidAcceptLegalDocuments()).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ ScopedVector<WalletItems::LegalDocument> docs;
+ base::DictionaryValue document;
+ document.SetString("legal_document_id", "doc_id_1");
+ document.SetString("display_name", "doc_1");
+ docs.push_back(
+ WalletItems::LegalDocument::CreateLegalDocument(document).release());
+ document.SetString("legal_document_id", "doc_id_2");
+ document.SetString("display_name", "doc_2");
+ docs.push_back(
+ WalletItems::LegalDocument::CreateLegalDocument(document).release());
+ docs.push_back(
+ WalletItems::LegalDocument::CreatePrivacyPolicyDocument().release());
+ wallet_client_->AcceptLegalDocuments(docs.get(),
+ kGoogleTransactionId,
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kAcceptLegalDocumentsValidRequest,
+ ")}'"); // Invalid JSON. Should be ignored.
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentSucceeded) {
+ EXPECT_CALL(delegate_, OnDidAuthenticateInstrument(true)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::AUTHENTICATE_INSTRUMENT,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->AuthenticateInstrument("instrument_id", "123");
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kAuthenticateInstrumentValidRequest,
+ kAuthenticateInstrumentSuccessResponse,
+ 3U);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentFailed) {
+ EXPECT_CALL(delegate_, OnDidAuthenticateInstrument(false)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::AUTHENTICATE_INSTRUMENT,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->AuthenticateInstrument("instrument_id", "123");
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kAuthenticateInstrumentValidRequest,
+ kAuthenticateInstrumentFailureResponse,
+ 3U);
+}
+
+TEST_F(WalletClientTest, AuthenticateInstrumentFailedMalformedResponse) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::AUTHENTICATE_INSTRUMENT,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ wallet_client_->AuthenticateInstrument("instrument_id", "123");
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kAuthenticateInstrumentValidRequest,
+ kSaveInvalidResponse,
+ 3U);
+}
+
+// TODO(ahutter): Add failure tests for GetWalletItems.
+
+TEST_F(WalletClientTest, GetWalletItems) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kGetWalletItemsValidRequest,
+ kGetWalletItemsValidResponse);
+ EXPECT_EQ(1U, delegate_.wallet_items_received());
+}
+
+TEST_F(WalletClientTest, GetWalletItemsRespectsDelegateForShippingRequired) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.SetIsShippingAddressRequired(false);
+
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kGetWalletItemsNoShippingRequest,
+ kGetWalletItemsValidResponse);
+ EXPECT_EQ(1U, delegate_.wallet_items_received());
+}
+
+TEST_F(WalletClientTest, SaveAddressSucceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet(std::string(),
+ "saved_address_id",
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveAddressValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveAddressWithRequiredActionsSucceeded) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::REQUIRE_PHONE_NUMBER);
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::INVALID_FORM_FIELD);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ std::vector<FormFieldError> form_errors;
+ form_errors.push_back(FormFieldError(FormFieldError::INVALID_POSTAL_CODE,
+ FormFieldError::SHIPPING_ADDRESS));
+
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet(std::string(),
+ std::string(),
+ required_actions,
+ form_errors)).Times(1);
+
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveAddressWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, SaveAddressFailedInvalidRequiredAction) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, SaveAddressFailedMalformedResponse) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kSaveAddressValidRequest,
+ kSaveInvalidResponse);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentSucceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet("instrument_id",
+ std::string(),
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveInstrumentValidResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentWithRequiredActionsSucceeded) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::REQUIRE_PHONE_NUMBER);
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::INVALID_FORM_FIELD);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ std::vector<FormFieldError> form_errors;
+ form_errors.push_back(FormFieldError(FormFieldError::INVALID_POSTAL_CODE,
+ FormFieldError::SHIPPING_ADDRESS));
+
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet(std::string(),
+ std::string(),
+ required_actions,
+ form_errors)).Times(1);
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(
+ net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveInstrumentWithRequiredActionsValidResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentFailedInvalidRequiredActions) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE));
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveWithInvalidRequiredActionsResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentFailedMalformedResponse) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kSaveInstrumentValidRequest,
+ kSaveInvalidResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressSucceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet("saved_instrument_id",
+ "saved_address_id",
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveInstrumentAndAddressValidResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressWithRequiredActionsSucceeded) {
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::REQUIRE_PHONE_NUMBER);
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::INVALID_FORM_FIELD);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ std::vector<FormFieldError> form_errors;
+ form_errors.push_back(FormFieldError(FormFieldError::INVALID_POSTAL_CODE,
+ FormFieldError::SHIPPING_ADDRESS));
+
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet(std::string(),
+ std::string(),
+ required_actions,
+ form_errors)).Times(1);
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(
+ net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveInstrumentAndAddressWithRequiredActionsValidResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, SaveInstrumentAndAddressFailedInvalidRequiredAction) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ scoped_ptr<Instrument> instrument = GetTestInstrument();
+ scoped_ptr<Address> address = GetTestSaveableAddress();
+ wallet_client_->SaveToWallet(instrument.Pass(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kSaveInstrumentAndAddressValidRequest,
+ kSaveWithInvalidRequiredActionsResponse,
+ 4U);
+}
+
+TEST_F(WalletClientTest, UpdateAddressSucceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet(std::string(),
+ "shipping_address_id",
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+ address->set_object_id("shipping_address_id");
+
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateAddressValidRequest,
+ kUpdateAddressValidResponse);
+}
+
+TEST_F(WalletClientTest, UpdateAddressWithRequiredActionsSucceeded) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::REQUIRE_PHONE_NUMBER);
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::INVALID_FORM_FIELD);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ std::vector<FormFieldError> form_errors;
+ form_errors.push_back(FormFieldError(FormFieldError::INVALID_POSTAL_CODE,
+ FormFieldError::SHIPPING_ADDRESS));
+
+ EXPECT_CALL(delegate_, OnDidSaveToWallet(std::string(),
+ std::string(),
+ required_actions,
+ form_errors)).Times(1);
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+ address->set_object_id("shipping_address_id");
+
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateAddressValidRequest,
+ kUpdateWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, UpdateAddressFailedInvalidRequiredAction) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+ address->set_object_id("shipping_address_id");
+
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateAddressValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, UpdateAddressMalformedResponse) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ scoped_ptr<Address> address = GetTestShippingAddress();
+ address->set_object_id("shipping_address_id");
+
+ wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
+ address.Pass(),
+ GURL(kMerchantUrl));
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateAddressValidRequest,
+ kUpdateMalformedResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentAddressSucceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet("instrument_id",
+ std::string(),
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->SaveToWallet(GetTestAddressUpgradeInstrument(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateInstrumentAddressValidRequest,
+ kUpdateInstrumentValidResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentExpirationDateSuceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet("instrument_id",
+ std::string(),
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->SaveToWallet(GetTestExpirationDateChangeInstrument(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kUpdateInstrumentExpirationDateValidRequest,
+ kUpdateInstrumentValidResponse,
+ 3U);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentAddressWithNameChangeSucceeded) {
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet("instrument_id",
+ std::string(),
+ std::vector<RequiredAction>(),
+ std::vector<FormFieldError>())).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->SaveToWallet(GetTestAddressNameChangeInstrument(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishFormEncodedRequest(
+ net::HTTP_OK,
+ kUpdateInstrumentAddressWithNameChangeValidRequest,
+ kUpdateInstrumentValidResponse,
+ 3U);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentWithRequiredActionsSucceeded) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::REQUIRE_PHONE_NUMBER);
+ delegate_.ExpectWalletRequiredActionMetric(
+ AutofillMetrics::INVALID_FORM_FIELD);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(REQUIRE_PHONE_NUMBER);
+ required_actions.push_back(INVALID_FORM_FIELD);
+
+ std::vector<FormFieldError> form_errors;
+ form_errors.push_back(FormFieldError(FormFieldError::INVALID_POSTAL_CODE,
+ FormFieldError::SHIPPING_ADDRESS));
+
+ EXPECT_CALL(delegate_,
+ OnDidSaveToWallet(std::string(),
+ std::string(),
+ required_actions,
+ form_errors)).Times(1);
+
+ wallet_client_->SaveToWallet(GetTestAddressUpgradeInstrument(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateInstrumentAddressValidRequest,
+ kUpdateWithRequiredActionsValidResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentFailedInvalidRequiredAction) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ wallet_client_->SaveToWallet(GetTestAddressUpgradeInstrument(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateInstrumentAddressValidRequest,
+ kSaveWithInvalidRequiredActionsResponse);
+}
+
+TEST_F(WalletClientTest, UpdateInstrumentMalformedResponse) {
+ EXPECT_CALL(delegate_,
+ OnWalletError(WalletClient::MALFORMED_RESPONSE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+
+ wallet_client_->SaveToWallet(GetTestAddressUpgradeInstrument(),
+ scoped_ptr<Address>(),
+ GURL(kMerchantUrl));
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kUpdateInstrumentAddressValidRequest,
+ kUpdateMalformedResponse);
+}
+
+TEST_F(WalletClientTest, SendAutocheckoutOfStatusSuccess) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ AutocheckoutStatistic statistic;
+ statistic.page_number = 1;
+ statistic.steps.push_back(AUTOCHECKOUT_STEP_SHIPPING);
+ statistic.time_taken = base::TimeDelta::FromMilliseconds(100);
+ std::vector<AutocheckoutStatistic> statistics;
+ statistics.push_back(statistic);
+ wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
+ GURL(kMerchantUrl),
+ statistics,
+ "google_transaction_id");
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kSendAutocheckoutStatusWithStatisticsValidRequest,
+ ")]}"); // Invalid JSON. Should be ignored.
+}
+
+TEST_F(WalletClientTest, SendAutocheckoutStatusOfFailure) {
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectBaselineMetrics();
+
+ std::vector<AutocheckoutStatistic> statistics;
+ wallet_client_->SendAutocheckoutStatus(autofill::CANNOT_PROCEED,
+ GURL(kMerchantUrl),
+ statistics,
+ "google_transaction_id");
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kSendAutocheckoutStatusOfFailureValidRequest,
+ ")]}"); // Invalid JSON. Should be ignored.
+}
+
+TEST_F(WalletClientTest, HasRequestInProgress) {
+ EXPECT_FALSE(wallet_client_->HasRequestInProgress());
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_TRUE(wallet_client_->HasRequestInProgress());
+
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kGetWalletItemsValidRequest,
+ kGetWalletItemsValidResponse);
+ EXPECT_FALSE(wallet_client_->HasRequestInProgress());
+}
+
+TEST_F(WalletClientTest, PendingRequest) {
+ EXPECT_EQ(0U, wallet_client_->pending_requests_.size());
+
+ // Shouldn't queue the first request.
+ delegate_.ExpectBaselineMetrics();
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_EQ(0U, wallet_client_->pending_requests_.size());
+ testing::Mock::VerifyAndClear(delegate_.metric_logger());
+
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
+ EXPECT_EQ(1U, wallet_client_->pending_requests_.size());
+
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 1);
+ delegate_.ExpectBaselineMetrics();
+ VerifyAndFinishRequest(net::HTTP_OK,
+ kGetWalletItemsValidRequest,
+ kGetWalletItemsValidResponse);
+ EXPECT_EQ(0U, wallet_client_->pending_requests_.size());
+ testing::Mock::VerifyAndClear(delegate_.metric_logger());
+
+ EXPECT_CALL(delegate_, OnWalletError(
+ WalletClient::SERVICE_UNAVAILABLE)).Times(1);
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 1);
+ delegate_.ExpectWalletErrorMetric(
+ AutofillMetrics::WALLET_SERVICE_UNAVAILABLE);
+
+ // Finish the second request.
+ VerifyAndFinishRequest(net::HTTP_INTERNAL_SERVER_ERROR,
+ kGetWalletItemsValidRequest,
+ kErrorResponse);
+}
+
+TEST_F(WalletClientTest, CancelRequests) {
+ ASSERT_EQ(0U, wallet_client_->pending_requests_.size());
+ delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_WALLET_ITEMS,
+ 0);
+ delegate_.ExpectBaselineMetrics();
+
+ 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_->CancelRequests();
+ EXPECT_EQ(0U, wallet_client_->pending_requests_.size());
+ EXPECT_FALSE(wallet_client_->HasRequestInProgress());
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_items.cc b/chromium/components/autofill/content/browser/wallet/wallet_items.cc
new file mode 100644
index 00000000000..03cf3f442c2
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_items.cc
@@ -0,0 +1,525 @@
+// 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/content/browser/wallet/wallet_items.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "grit/component_strings.h"
+#include "grit/webkit_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image.h"
+#include "url/gurl.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+const char kLegalDocumentUrl[] =
+ "https://wallet.google.com/legaldocument?docId=";
+const char kPrivacyNoticeUrl[] = "https://wallet.google.com/files/privacy.html";
+
+// TODO(estade): move to base/.
+template<class T>
+bool VectorsAreEqual(const std::vector<T*>& a, const std::vector<T*>& b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (*a[i] != *b[i])
+ return false;
+ }
+
+ return true;
+}
+
+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 == "AMEX_NOT_SUPPORTED")
+ return WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED;
+ 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 base::string16& descriptive_name,
+ const WalletItems::MaskedInstrument::Type& type,
+ const std::vector<base::string16>& supported_currencies,
+ const base::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>();
+ }
+
+ base::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<base::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) {
+ base::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";
+
+ base::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);
+}
+
+const WalletItems::MaskedInstrument* WalletItems::GetInstrumentById(
+ const std::string& object_id) const {
+ if (object_id.empty())
+ return NULL;
+
+ for (size_t i = 0; i < instruments_.size(); ++i) {
+ if (instruments_[i]->object_id() == object_id)
+ return instruments_[i];
+ }
+
+ return NULL;
+}
+
+bool WalletItems::HasRequiredAction(RequiredAction action) const {
+ DCHECK(ActionAppliesToWalletItems(action));
+ return std::find(required_actions_.begin(),
+ required_actions_.end(),
+ action) != required_actions_.end();
+}
+
+base::string16 WalletItems::MaskedInstrument::DisplayName() const {
+#if defined(OS_ANDROID)
+ // TODO(aruslan): improve this stub implementation.
+ return descriptive_name();
+#else
+ return descriptive_name();
+#endif
+}
+
+base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
+#if defined(OS_ANDROID)
+ // TODO(aruslan): improve this stub implementation.
+ return address().DisplayName();
+#else
+ return base::string16();
+#endif
+}
+
+base::string16 WalletItems::MaskedInstrument::TypeAndLastFourDigits() const {
+ base::string16 display_type;
+
+ if (type_ == AMEX)
+ display_type = CreditCard::TypeForDisplay(kAmericanExpressCard);
+ else if (type_ == DISCOVER)
+ display_type = CreditCard::TypeForDisplay(kDiscoverCard);
+ else if (type_ == MASTER_CARD)
+ display_type = CreditCard::TypeForDisplay(kMasterCard);
+ else if (type_ == VISA)
+ display_type = CreditCard::TypeForDisplay(kVisaCard);
+ else
+ display_type = CreditCard::TypeForDisplay(kGenericCard);
+
+ // TODO(dbeam): i18n.
+ return display_type + ASCIIToUTF16(" - ") + last_four_digits();
+}
+
+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 VISA:
+ idr = IDR_AUTOFILL_CC_VISA;
+ break;
+
+ case SOLO:
+ case MAESTRO:
+ case SWITCH:
+ case UNKNOWN:
+ idr = IDR_AUTOFILL_CC_GENERIC;
+ break;
+ }
+
+ return ResourceBundle::GetSharedInstance().GetImageNamed(idr);
+}
+
+base::string16 WalletItems::MaskedInstrument::GetInfo(
+ const AutofillType& type,
+ const std::string& app_locale) const {
+ if (type.group() != CREDIT_CARD)
+ return address().GetInfo(type, app_locale);
+
+ switch (type.GetStorableType()) {
+ case CREDIT_CARD_NAME:
+ return address().recipient_name();
+
+ case CREDIT_CARD_NUMBER:
+ return DisplayName();
+
+ case CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ return base::IntToString16(expiration_year());
+
+ case CREDIT_CARD_VERIFICATION_CODE:
+ break;
+
+ default:
+ NOTREACHED();
+ }
+
+ return base::string16();
+}
+
+WalletItems::LegalDocument::~LegalDocument() {}
+
+scoped_ptr<WalletItems::LegalDocument>
+ WalletItems::LegalDocument::CreateLegalDocument(
+ const base::DictionaryValue& dictionary) {
+ std::string id;
+ if (!dictionary.GetString("legal_document_id", &id)) {
+ DLOG(ERROR) << "Response from Google Wallet missing legal document id";
+ return scoped_ptr<LegalDocument>();
+ }
+
+ base::string16 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(id, display_name));
+}
+
+scoped_ptr<WalletItems::LegalDocument>
+ WalletItems::LegalDocument::CreatePrivacyPolicyDocument() {
+ return scoped_ptr<LegalDocument>(new LegalDocument(
+ GURL(kPrivacyNoticeUrl),
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK)));
+}
+
+bool WalletItems::LegalDocument::operator==(const LegalDocument& other) const {
+ return id_ == other.id_ &&
+ url_ == other.url_ &&
+ display_name_ == other.display_name_;
+}
+
+bool WalletItems::LegalDocument::operator!=(const LegalDocument& other) const {
+ return !(*this == other);
+}
+
+WalletItems::LegalDocument::LegalDocument(const std::string& id,
+ const base::string16& display_name)
+ : id_(id),
+ url_(kLegalDocumentUrl + id),
+ display_name_(display_name) {}
+
+WalletItems::LegalDocument::LegalDocument(const GURL& url,
+ const base::string16& display_name)
+ : url_(url),
+ display_name_(display_name) {}
+
+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>();
+ }
+ }
+ }
+
+ if (!legal_docs->empty()) {
+ // Always append the privacy policy link as well.
+ wallet_items->AddLegalDocument(
+ LegalDocument::CreatePrivacyPolicyDocument());
+ }
+ } 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 {
+ 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_ &&
+ VectorsAreEqual<MaskedInstrument>(instruments(),
+ other.instruments()) &&
+ VectorsAreEqual<Address>(addresses(), other.addresses()) &&
+ VectorsAreEqual<LegalDocument>(legal_documents(),
+ other.legal_documents());
+}
+
+bool WalletItems::operator!=(const WalletItems& other) const {
+ return !(*this == other);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_items.h b/chromium/components/autofill/content/browser/wallet/wallet_items.h
new file mode 100644
index 00000000000..53a49a17d05
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_items.h
@@ -0,0 +1,300 @@
+// 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_CONTENT_BROWSER_WALLET_WALLET_ITEMS_H_
+#define COMPONENTS_AUTOFILL_CONTENT_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/strings/string16.h"
+#include "components/autofill/content/browser/wallet/required_action.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+#include "url/gurl.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace gfx {
+class Image;
+}
+
+namespace autofill {
+
+class AutofillType;
+
+FORWARD_DECLARE_TEST(WalletInstrumentWrapperTest, GetInfoCreditCardExpMonth);
+FORWARD_DECLARE_TEST(WalletInstrumentWrapperTest,
+ GetDisplayTextEmptyWhenExpired);
+
+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 {
+ AMEX_NOT_SUPPORTED,
+ BILLING_INCOMPLETE,
+ DECLINED,
+ DISABLED_FOR_THIS_MERCHANT, // Deprecated.
+ 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;
+
+ // Returns a pair of strings that summarizes this CC,
+ // suitable for display to the user.
+ base::string16 DisplayName() const;
+ base::string16 DisplayNameDetail() const;
+
+ // Gets info that corresponds with |type|.
+ base::string16 GetInfo(const AutofillType& type,
+ const std::string& app_locale) const;
+
+ // Returns the display type of the and last four digits (e.g. Visa - 4444).
+ base::string16 TypeAndLastFourDigits() const;
+
+ const base::string16& descriptive_name() const { return descriptive_name_; }
+ const Type& type() const { return type_; }
+ const std::vector<base::string16>& supported_currencies() const {
+ return supported_currencies_;
+ }
+ const base::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 scoped_ptr<MaskedInstrument> GetTestMaskedInstrumentWithDetails(
+ const std::string&, scoped_ptr<Address> address,
+ Type type, Status status);
+ FRIEND_TEST_ALL_PREFIXES(::autofill::WalletInstrumentWrapperTest,
+ GetInfoCreditCardExpMonth);
+ FRIEND_TEST_ALL_PREFIXES(::autofill::WalletInstrumentWrapperTest,
+ GetDisplayTextEmptyWhenExpired);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateMaskedInstrument);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateWalletItems);
+
+ MaskedInstrument(const base::string16& descriptive_name,
+ const Type& type,
+ const std::vector<base::string16>& supported_currencies,
+ const base::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".
+ base::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<base::string16> supported_currencies_;
+
+ // The last four digits of the primary account number of the instrument.
+ base::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.
+ static scoped_ptr<LegalDocument>
+ CreateLegalDocument(const base::DictionaryValue& dictionary);
+
+ // Returns a document for the privacy policy (acceptance of which is not
+ // tracked by the server).
+ static scoped_ptr<LegalDocument> CreatePrivacyPolicyDocument();
+
+ bool operator==(const LegalDocument& other) const;
+ bool operator!=(const LegalDocument& other) const;
+
+ const std::string& id() { return id_; }
+ const GURL& url() const { return url_; }
+ const base::string16& 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, LegalDocumentUrl);
+ FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, LegalDocumentEmptyId);
+ LegalDocument(const std::string& id,
+ const base::string16& display_name);
+ LegalDocument(const GURL& url,
+ const base::string16& display_name);
+
+ // Externalized Online Wallet id for the document, or an empty string for
+ // documents not tracked by the server (such as the privacy policy).
+ std::string id_;
+ // The human-visitable URL that displays the document.
+ GURL url_;
+ // User displayable name for the document.
+ base::string16 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());
+ }
+
+ // Return the corresponding instrument for |id| or NULL if it doesn't exist.
+ const WalletItems::MaskedInstrument* GetInstrumentById(
+ const std::string& object_id) const;
+
+ // 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 scoped_ptr<WalletItems> GetTestWalletItems();
+ 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_CONTENT_BROWSER_WALLET_WALLET_ITEMS_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc
new file mode 100644
index 00000000000..83f8b1939fc
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc
@@ -0,0 +1,570 @@
+// 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 "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/required_action.h"
+#include "components/autofill/content/browser/wallet/wallet_items.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.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\","
+ " \"type\":\"FULL\""
+ " },"
+ " \"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\","
+ " \" CHOOse_ANother_INSTRUMENT_OR_ADDRESS\","
+ " \"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\":"
+ " ["
+ " \"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\""
+ " }"
+ " }"
+ " ],"
+ " \"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\","
+ " \"type\":\"FULL\""
+ " },"
+ " \"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\":"
+ " ["
+ " \"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\""
+ " }"
+ " }"
+ " ],"
+ " \"default_address_id\":\"default_address_id\","
+ " \"obfuscated_gaia_id\":\"obfuscated_gaia_id\"";
+
+const char kRequiredLegalDocument[] =
+ " ,"
+ " \"required_legal_document\":"
+ " ["
+ " {"
+ " \"legal_document_id\":\"doc_id\","
+ " \"display_name\":\"display_name\""
+ " }"
+ " ]";
+
+const char kCloseJson[] = "}";
+
+} // 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::string()));
+ std::vector<base::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", ASCIIToUTF16("display_name"));
+ EXPECT_EQ(expected,
+ *WalletItems::LegalDocument::CreateLegalDocument(*dict));
+}
+
+TEST_F(WalletItemsTest, LegalDocumentUrl) {
+ WalletItems::LegalDocument legal_doc("doc_id", ASCIIToUTF16("display_name"));
+ EXPECT_EQ("https://wallet.google.com/legaldocument?docId=doc_id",
+ legal_doc.url().spec());
+}
+
+TEST_F(WalletItemsTest, LegalDocumentEmptyId) {
+ WalletItems::LegalDocument legal_doc(GURL("http://example.com"),
+ ASCIIToUTF16("display_name"));
+ EXPECT_TRUE(legal_doc.id().empty());
+}
+
+TEST_F(WalletItemsTest, CreateWalletItemsWithRequiredActions) {
+ SetUpDictionary(kWalletItemsWithRequiredActions);
+
+ std::vector<RequiredAction> required_actions;
+ required_actions.push_back(SETUP_WALLET);
+ required_actions.push_back(CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS);
+ 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(std::string(kWalletItems) + std::string(kCloseJson));
+ 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::string()));
+ std::vector<base::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_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"));
+ expected.AddAddress(shipping_address.Pass());
+ EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict));
+
+ // Now try with a legal document as well.
+ SetUpDictionary(std::string(kWalletItems) +
+ std::string(kRequiredLegalDocument) +
+ std::string(kCloseJson));
+ scoped_ptr<WalletItems::LegalDocument> legal_document(
+ new WalletItems::LegalDocument("doc_id",
+ ASCIIToUTF16("display_name")));
+ expected.AddLegalDocument(legal_document.Pass());
+ expected.AddLegalDocument(
+ WalletItems::LegalDocument::CreatePrivacyPolicyDocument());
+
+ EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc b/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc
new file mode 100644
index 00000000000..e95a201a446
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc
@@ -0,0 +1,148 @@
+// 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/content/browser/wallet/wallet_service_url.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "net/base/url_util.h"
+#include "url/gurl.h"
+
+namespace autofill {
+namespace {
+
+const char kProdWalletServiceUrl[] = "https://wallet.google.com/";
+
+// TODO(ahutter): Remove this once production is ready.
+const char kSandboxWalletServiceUrl[] =
+ "https://payments-form-dogfood.sandbox.google.com/";
+
+// TODO(ahutter): Remove this once production is ready.
+const char kSandboxWalletSecureServiceUrl[] =
+ "https://wallet-web.sandbox.google.com/";
+
+bool IsWalletProductionEnabled() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ return command_line.HasSwitch(switches::kWalletServiceUseProd) ||
+ base::FieldTrialList::FindFullName("WalletProductionService") == "Yes" ||
+ base::FieldTrialList::FindFullName("Autocheckout") == "Yes";
+}
+
+GURL GetWalletHostUrl() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ std::string wallet_service_hostname =
+ command_line.GetSwitchValueASCII(switches::kWalletServiceUrl);
+ if (!wallet_service_hostname.empty())
+ return GURL(wallet_service_hostname);
+ if (IsWalletProductionEnabled())
+ return GURL(kProdWalletServiceUrl);
+ return GURL(kSandboxWalletServiceUrl);
+}
+
+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);
+ if (!wallet_secure_url.empty())
+ return GURL(wallet_secure_url);
+ if (IsWalletProductionEnabled())
+ return GURL(kProdWalletServiceUrl);
+ return GURL(kSandboxWalletSecureServiceUrl);
+}
+
+GURL GetBaseEncryptedFrontendUrl() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ // TODO(ahutter): Stop checking these switches once we switch over to prod.
+ GURL base_url = IsWalletProductionEnabled() ||
+ command_line.HasSwitch(switches::kWalletServiceUrl) ?
+ GetWalletHostUrl() : GetBaseSecureUrl();
+ return base_url.Resolve("online-secure/v2/autocheckout/v1/");
+}
+
+} // namespace
+
+namespace wallet {
+
+GURL GetGetWalletItemsUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("getWalletItemsJwtless");
+}
+
+GURL GetGetFullWalletUrl() {
+ return GetBaseEncryptedFrontendUrl().Resolve("getFullWalletJwtless?s7e=otp");
+}
+
+GURL GetManageInstrumentsUrl() {
+ return GetBaseSecureUrl().Resolve("manage/paymentMethods");
+}
+
+GURL GetManageAddressesUrl() {
+ return GetBaseSecureUrl().Resolve("manage/settings/addresses");
+}
+
+GURL GetAcceptLegalDocumentsUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("acceptLegalDocument");
+}
+
+GURL GetAuthenticateInstrumentUrl() {
+ return GetBaseEncryptedFrontendUrl()
+ .Resolve("authenticateInstrument?s7e=cvn");
+}
+
+GURL GetSendStatusUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("reportStatus");
+}
+
+GURL GetSaveToWalletNoEscrowUrl() {
+ return GetBaseAutocheckoutUrl().Resolve("saveToWallet");
+}
+
+GURL GetSaveToWalletUrl() {
+ return GetBaseEncryptedFrontendUrl()
+ .Resolve("saveToWallet?s7e=card_number%3Bcvn");
+}
+
+GURL GetPassiveAuthUrl() {
+ return GetBaseWalletUrl().Resolve("passiveauth?isChromePayments=true");
+}
+
+GURL GetSignInUrl() {
+ GURL url(GaiaUrls::GetInstance()->service_login_url());
+ url = net::AppendQueryParameter(url, "service", "toolbar");
+ url = net::AppendQueryParameter(url, "nui", "1");
+ 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();
+}
+
+bool IsUsingProd() {
+ return GetWalletHostUrl() == GURL(kProdWalletServiceUrl);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_service_url.h b/chromium/components/autofill/content/browser/wallet/wallet_service_url.h
new file mode 100644
index 00000000000..61bceca0c64
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_service_url.h
@@ -0,0 +1,42 @@
+// 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_CONTENT_BROWSER_WALLET_WALLET_SERVICE_URL_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_SERVICE_URL_H_
+
+class GURL;
+
+namespace autofill {
+namespace wallet {
+
+GURL GetGetWalletItemsUrl();
+GURL GetGetFullWalletUrl();
+GURL GetManageInstrumentsUrl();
+GURL GetManageAddressesUrl();
+GURL GetAcceptLegalDocumentsUrl();
+GURL GetAuthenticateInstrumentUrl();
+GURL GetSendStatusUrl();
+GURL GetSaveToWalletNoEscrowUrl();
+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);
+
+// Whether calls to Online Wallet are hitting the production server rather than
+// a sandbox or some malicious endpoint.
+bool IsUsingProd();
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_SERVICE_URL_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc
new file mode 100644
index 00000000000..3ed3600fc0a
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc
@@ -0,0 +1,61 @@
+// 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 "base/command_line.h"
+#include "components/autofill/content/browser/wallet/wallet_service_url.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace autofill {
+namespace wallet {
+
+TEST(WalletServiceUrl, CheckDefaultUrls) {
+ EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/wallet/"
+ "autocheckout/v1/getWalletItemsJwtless",
+ GetGetWalletItemsUrl().spec());
+ EXPECT_EQ("https://wallet-web.sandbox.google.com/online-secure/v2/"
+ "autocheckout/v1/getFullWalletJwtless?s7e=otp",
+ GetGetFullWalletUrl().spec());
+ EXPECT_EQ("https://wallet-web.sandbox.google.com/manage/paymentMethods",
+ GetManageInstrumentsUrl().spec());
+ EXPECT_EQ("https://wallet-web.sandbox.google.com/manage/settings/addresses",
+ GetManageAddressesUrl().spec());
+ EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/wallet/"
+ "autocheckout/v1/acceptLegalDocument",
+ GetAcceptLegalDocumentsUrl().spec());
+ EXPECT_EQ("https://wallet-web.sandbox.google.com/online-secure/v2/"
+ "autocheckout/v1/authenticateInstrument?s7e=cvn",
+ GetAuthenticateInstrumentUrl().spec());
+ EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/wallet/"
+ "autocheckout/v1/reportStatus",
+ GetSendStatusUrl().spec());
+ EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/wallet/"
+ "autocheckout/v1/saveToWallet",
+ GetSaveToWalletNoEscrowUrl().spec());
+ EXPECT_EQ("https://wallet-web.sandbox.google.com/online-secure/v2/"
+ "autocheckout/v1/saveToWallet?s7e=card_number%3Bcvn",
+ GetSaveToWalletUrl().spec());
+ EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/"
+ "passiveauth?isChromePayments=true",
+ GetPassiveAuthUrl().spec());
+}
+
+TEST(WalletServiceUrl, IsUsingProd) {
+ // The sandbox servers are the default (for now). Update if this changes.
+ EXPECT_FALSE(IsUsingProd());
+
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitch(switches::kWalletServiceUseProd);
+ EXPECT_TRUE(IsUsingProd());
+
+ const GURL prod_get_items_url = GetGetWalletItemsUrl();
+ command_line->AppendSwitchASCII(switches::kWalletServiceUrl, "http://goo.gl");
+ EXPECT_FALSE(IsUsingProd());
+
+ ASSERT_NE(prod_get_items_url, GetGetWalletItemsUrl());
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_signin_helper.cc b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper.cc
new file mode 100644
index 00000000000..beeb1e7467c
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper.cc
@@ -0,0 +1,322 @@
+// 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/content/browser/wallet/wallet_signin_helper.h"
+
+#include "base/callback_helpers.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/wallet_service_url.h"
+#include "components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h"
+#include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "net/base/escape.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/cookies/cookie_options.h"
+#include "net/cookies/cookie_store.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+// Toolbar::GetAccountInfo API URL (JSON).
+const char kGetAccountInfoUrlFormat[] =
+ "https://clients1.google.com/tbproxy/getaccountinfo?key=%d&rv=2&requestor=chrome";
+
+const char kWalletCookieName[] = "gdtoken";
+
+// Callback for retrieving Google Wallet cookies. |callback| is passed the
+// retrieved cookies and posted back to the UI thread. |cookies| is any Google
+// Wallet cookies.
+void GetGoogleCookiesCallback(
+ const base::Callback<void(const std::string&)>& callback,
+ const net::CookieList& cookies) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ std::string wallet_cookie;
+ for (size_t i = 0; i < cookies.size(); ++i) {
+ if (LowerCaseEqualsASCII(cookies[i].Name(), kWalletCookieName)) {
+ wallet_cookie = cookies[i].Value();
+ break;
+ }
+ }
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback, wallet_cookie));
+}
+
+// Gets Google Wallet cookies. Must be called on the IO thread.
+// |request_context_getter| is a getter for the current request context.
+// |callback| is called when retrieving cookies is completed.
+void GetGoogleCookies(
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter,
+ const base::Callback<void(const std::string&)>& callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ net::URLRequestContext* url_request_context =
+ request_context_getter->GetURLRequestContext();
+ if (!url_request_context) {
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback, std::string()));
+ return;
+ }
+
+ net::CookieStore* cookie_store = url_request_context->cookie_store();
+ if (!cookie_store) {
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback, std::string()));
+ return;
+ }
+
+ net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
+ if (!cookie_monster) {
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(callback, std::string()));
+ return;
+ }
+
+ net::CookieOptions cookie_options;
+ cookie_options.set_include_httponly();
+ cookie_monster->GetAllCookiesForURLWithOptionsAsync(
+ wallet::GetPassiveAuthUrl().GetWithEmptyPath(),
+ cookie_options,
+ base::Bind(&GetGoogleCookiesCallback, callback));
+}
+
+} // namespace
+
+WalletSigninHelper::WalletSigninHelper(
+ WalletSigninHelperDelegate* delegate,
+ net::URLRequestContextGetter* getter)
+ : delegate_(delegate),
+ getter_(getter),
+ state_(IDLE),
+ weak_ptr_factory_(this) {
+ DCHECK(delegate_);
+}
+
+WalletSigninHelper::~WalletSigninHelper() {
+}
+
+void WalletSigninHelper::StartPassiveSignin() {
+ DCHECK_EQ(IDLE, state_);
+ DCHECK(!url_fetcher_);
+
+ state_ = PASSIVE_EXECUTING_SIGNIN;
+ username_.clear();
+ const GURL& url = wallet::GetPassiveAuthUrl();
+ url_fetcher_.reset(net::URLFetcher::Create(
+ 0, url, net::URLFetcher::GET, this));
+ url_fetcher_->SetRequestContext(getter_);
+ url_fetcher_->Start();
+}
+
+void WalletSigninHelper::StartUserNameFetch() {
+ DCHECK_EQ(state_, IDLE);
+ DCHECK(!url_fetcher_);
+
+ state_ = USERNAME_FETCHING_USERINFO;
+ username_.clear();
+ StartFetchingUserNameFromSession();
+}
+
+void WalletSigninHelper::StartWalletCookieValueFetch() {
+ scoped_refptr<net::URLRequestContextGetter> request_context(getter_);
+ if (!request_context.get()) {
+ ReturnWalletCookieValue(std::string());
+ return;
+ }
+
+ base::Callback<void(const std::string&)> callback = base::Bind(
+ &WalletSigninHelper::ReturnWalletCookieValue,
+ weak_ptr_factory_.GetWeakPtr());
+
+ base::Closure task = base::Bind(&GetGoogleCookies, request_context, callback);
+ content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, task);
+}
+
+std::string WalletSigninHelper::GetGetAccountInfoUrlForTesting() const {
+ return base::StringPrintf(kGetAccountInfoUrlFormat, 0);
+}
+
+void WalletSigninHelper::OnServiceError(const GoogleServiceAuthError& error) {
+ const State state_with_error = state_;
+ state_ = IDLE;
+ url_fetcher_.reset();
+
+ switch(state_with_error) {
+ case IDLE:
+ NOTREACHED();
+ break;
+
+ case PASSIVE_EXECUTING_SIGNIN: /*FALLTHROUGH*/
+ case PASSIVE_FETCHING_USERINFO:
+ delegate_->OnPassiveSigninFailure(error);
+ break;
+
+ case USERNAME_FETCHING_USERINFO:
+ delegate_->OnUserNameFetchFailure(error);
+ break;
+ }
+}
+
+void WalletSigninHelper::OnOtherError() {
+ OnServiceError(GoogleServiceAuthError::AuthErrorNone());
+}
+
+void WalletSigninHelper::OnURLFetchComplete(
+ const net::URLFetcher* fetcher) {
+ DCHECK_EQ(url_fetcher_.get(), fetcher);
+ if (!fetcher->GetStatus().is_success() ||
+ fetcher->GetResponseCode() < 200 ||
+ fetcher->GetResponseCode() >= 300) {
+ LOG(ERROR) << "URLFetchFailure: state=" << state_
+ << " r=" << fetcher->GetResponseCode()
+ << " s=" << fetcher->GetStatus().status()
+ << " e=" << fetcher->GetStatus().error();
+ OnOtherError();
+ return;
+ }
+
+ switch (state_) {
+ case USERNAME_FETCHING_USERINFO: /*FALLTHROUGH*/
+ case PASSIVE_FETCHING_USERINFO:
+ ProcessGetAccountInfoResponseAndFinish();
+ break;
+
+ case PASSIVE_EXECUTING_SIGNIN:
+ if (ParseSignInResponse()) {
+ url_fetcher_.reset();
+ state_ = PASSIVE_FETCHING_USERINFO;
+ StartFetchingUserNameFromSession();
+ }
+ break;
+
+ default:
+ NOTREACHED() << "unexpected state_=" << state_;
+ }
+}
+
+void WalletSigninHelper::StartFetchingUserNameFromSession() {
+ const int random_number = static_cast<int>(base::RandUint64() % INT_MAX);
+ url_fetcher_.reset(
+ net::URLFetcher::Create(
+ 0,
+ GURL(base::StringPrintf(kGetAccountInfoUrlFormat, random_number)),
+ net::URLFetcher::GET,
+ this));
+ url_fetcher_->SetRequestContext(getter_);
+ url_fetcher_->Start(); // This will result in OnURLFetchComplete callback.
+}
+
+void WalletSigninHelper::ProcessGetAccountInfoResponseAndFinish() {
+ std::string email;
+ if (!ParseGetAccountInfoResponse(url_fetcher_.get(), &email)) {
+ LOG(ERROR) << "failed to get the user email";
+ OnOtherError();
+ return;
+ }
+
+ username_ = email;
+ const State finishing_state = state_;
+ state_ = IDLE;
+ url_fetcher_.reset();
+ switch(finishing_state) {
+ case USERNAME_FETCHING_USERINFO:
+ delegate_->OnUserNameFetchSuccess(username_);
+ break;
+
+ case PASSIVE_FETCHING_USERINFO:
+ delegate_->OnPassiveSigninSuccess(username_);
+ break;
+
+ default:
+ NOTREACHED() << "unexpected state_=" << finishing_state;
+ }
+}
+
+bool WalletSigninHelper::ParseSignInResponse() {
+ if (!url_fetcher_) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::string data;
+ if (!url_fetcher_->GetResponseAsString(&data)) {
+ DVLOG(1) << "failed to GetResponseAsString";
+ OnOtherError();
+ return false;
+ }
+
+ if (!LowerCaseEqualsASCII(data, "yes")) {
+ OnServiceError(
+ GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP));
+ return false;
+ }
+
+ return true;
+}
+
+bool WalletSigninHelper::ParseGetAccountInfoResponse(
+ const net::URLFetcher* fetcher, std::string* email) {
+ DCHECK(email);
+
+ std::string data;
+ if (!fetcher->GetResponseAsString(&data)) {
+ DVLOG(1) << "failed to GetResponseAsString";
+ return false;
+ }
+
+ scoped_ptr<base::Value> value(base::JSONReader::Read(data));
+ if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) {
+ DVLOG(1) << "failed to parse JSON response";
+ return false;
+ }
+
+ DictionaryValue* dict = static_cast<base::DictionaryValue*>(value.get());
+ base::ListValue* user_info;
+ if (!dict->GetListWithoutPathExpansion("user_info", &user_info)) {
+ DVLOG(1) << "no user_info in JSON response";
+ return false;
+ }
+
+ // |user_info| will contain each signed in user in the cookie jar.
+ // We only support the first user at the moment. http://crbug.com/259543
+ // will change that.
+ base::DictionaryValue* user_info_detail;
+ if (!user_info->GetDictionary(0, &user_info_detail)) {
+ DVLOG(1) << "empty list in JSON response";
+ return false;
+ }
+
+ if (!user_info_detail->GetStringWithoutPathExpansion("email", email)) {
+ DVLOG(1) << "no email in JSON response";
+ return false;
+ }
+
+ return !email->empty();
+}
+
+void WalletSigninHelper::ReturnWalletCookieValue(
+ const std::string& cookie_value) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ delegate_->OnDidFetchWalletCookieValue(cookie_value);
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_signin_helper.h b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper.h
new file mode 100644
index 00000000000..d7cf5ff42a9
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper.h
@@ -0,0 +1,125 @@
+// 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_CONTENT_BROWSER_WALLET_WALLET_SIGNIN_HELPER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_SIGNIN_HELPER_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+class URLRequestStatus;
+}
+
+class GoogleServiceAuthError;
+
+namespace autofill {
+namespace wallet {
+
+class WalletSigninHelperDelegate;
+
+// Authenticates the user against the Online Wallet service.
+// This class is not thread-safe. An instance may be used on any thread, but
+// should not be accessed from multiple threads.
+class WalletSigninHelper : public net::URLFetcherDelegate {
+ public:
+ // Constructs a helper that works with a given |delegate| and uses a given
+ // |getter| to obtain a context for URL. Both |delegate| and |getter| shall
+ // remain valid over the entire lifetime of the created instance.
+ WalletSigninHelper(WalletSigninHelperDelegate* delegate,
+ net::URLRequestContextGetter* getter);
+
+ virtual ~WalletSigninHelper();
+
+ // Initiates an attempt to passively sign the user into the Online Wallet.
+ // A passive sign-in is a non-interactive refresh of content area cookies,
+ // and it succeeds as long as the Online Wallet service could safely accept
+ // or refresh the existing area cookies, and the user doesn't need to be
+ // fully reauthenticated with the service.
+ // Either OnPassiveSigninSuccess or OnPassiveSigninFailure will be called
+ // on the original thread.
+ void StartPassiveSignin();
+
+ // Initiates a fetch of the user name of a signed-in user.
+ // Either OnUserNameFetchSuccess or OnUserNameFetchFailure will
+ // be called on the original thread.
+ void StartUserNameFetch();
+
+ // Initiates the fetch of the user's Google Wallet cookie.
+ void StartWalletCookieValueFetch();
+
+ protected:
+ // Sign-in helper states (for tests).
+ enum State {
+ IDLE,
+ PASSIVE_EXECUTING_SIGNIN,
+ PASSIVE_FETCHING_USERINFO,
+ USERNAME_FETCHING_USERINFO,
+ };
+
+ // (For tests) Current state of the sign-in helper.
+ State state() const { return state_; }
+
+ // (For tests) URL used to fetch the currently signed-in user info.
+ std::string GetGetAccountInfoUrlForTesting() const;
+
+ private:
+ // Called if a service authentication error occurs.
+ void OnServiceError(const GoogleServiceAuthError& error);
+
+ // Called if any other error occurs.
+ void OnOtherError();
+
+ // URLFetcherDelegate implementation.
+ virtual void OnURLFetchComplete(const net::URLFetcher* fetcher) OVERRIDE;
+
+ // Initiates fetching of the currently signed-in user information.
+ void StartFetchingUserNameFromSession();
+
+ // Processes the user information received from the server by url_fetcher_
+ // and calls the delegate callbacks on success/failure.
+ void ProcessGetAccountInfoResponseAndFinish();
+
+ // Attempts to parse a response from the Online Wallet sign-in.
+ // Returns true if the response is correct and the sign-in has succeeded.
+ // Otherwise, it calls OnServiceError() and returns false.
+ bool ParseSignInResponse();
+
+ // Attempts to parse the GetAccountInfo response from the server.
+ // Returns true on success; the obtained email address is stored into |email|.
+ bool ParseGetAccountInfoResponse(const net::URLFetcher* fetcher,
+ std::string* email);
+
+ // Callback for when the Google Wallet cookie has been retrieved.
+ void ReturnWalletCookieValue(const std::string& cookie_value);
+
+ // Should be valid throughout the lifetime of the instance.
+ WalletSigninHelperDelegate* const delegate_;
+
+ // URLRequestContextGetter to be used for URLFetchers.
+ net::URLRequestContextGetter* const getter_;
+
+ // While passive login/merge session URL fetches are going on:
+ scoped_ptr<net::URLFetcher> url_fetcher_;
+
+ // User account name (email) fetched from OnGetUserInfoSuccess().
+ std::string username_;
+
+ // Current internal state of the helper.
+ State state_;
+
+ base::WeakPtrFactory<WalletSigninHelper> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WalletSigninHelper);
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_SIGNIN_HELPER_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h
new file mode 100644
index 00000000000..d09499d8c21
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h
@@ -0,0 +1,43 @@
+// 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 CHROME_BROWSER_UI_AUTOFILL_WALLET_SIGNIN_HELPER_DELEGATE_H_
+#define CHROME_BROWSER_UI_AUTOFILL_WALLET_SIGNIN_HELPER_DELEGATE_H_
+
+#include <string>
+
+class GoogleServiceAuthError;
+
+namespace autofill {
+namespace wallet {
+
+// An interface that defines the callbacks for objects that
+// WalletSigninHelper can return data to.
+class WalletSigninHelperDelegate {
+ public:
+ virtual ~WalletSigninHelperDelegate() {}
+
+ // Called on a successful passive sign-in.
+ // |username| is the signed-in user account name (email).
+ virtual void OnPassiveSigninSuccess(const std::string& username) = 0;
+
+ // Called on a failed passive sign-in; |error| describes the error.
+ virtual void OnPassiveSigninFailure(const GoogleServiceAuthError& error) = 0;
+
+ // Called on a successful fetch of the signed-in account name.
+ // |username| is the signed-in user account name (email).
+ virtual void OnUserNameFetchSuccess(const std::string& username) = 0;
+
+ // Called on a failed fetch of the signed-in account name.
+ // |error| described the error.
+ virtual void OnUserNameFetchFailure(const GoogleServiceAuthError& error) = 0;
+
+ // Called when the Google Wallet cookie value has been retrieved.
+ virtual void OnDidFetchWalletCookieValue(const std::string& cookie_value) = 0;
+};
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // CHROME_BROWSER_UI_AUTOFILL_WALLET_SIGNIN_HELPER_DELEGATE_H_
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_signin_helper_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper_unittest.cc
new file mode 100644
index 00000000000..e9662a51ec5
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_signin_helper_unittest.cc
@@ -0,0 +1,245 @@
+// 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/content/browser/wallet/wallet_signin_helper.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/content/browser/wallet/wallet_service_url.h"
+#include "components/autofill/content/browser/wallet/wallet_signin_helper_delegate.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/gaia_urls.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/cookies/cookie_options.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+const char kGetTokenPairValidResponse[] =
+ "{"
+ " \"refresh_token\": \"rt1\","
+ " \"access_token\": \"at1\","
+ " \"expires_in\": 3600,"
+ " \"token_type\": \"Bearer\""
+ "}";
+
+const char kGetAccountInfoValidResponseFormat[] =
+ "{\"user_info\":["
+ " {"
+ " \"email\": \"%s\""
+ " }"
+ "]}";
+
+class MockWalletSigninHelperDelegate : public WalletSigninHelperDelegate {
+ public:
+ MOCK_METHOD1(OnPassiveSigninSuccess, void(const std::string& username));
+ MOCK_METHOD1(OnUserNameFetchSuccess, void(const std::string& username));
+ MOCK_METHOD1(OnPassiveSigninFailure,
+ void(const GoogleServiceAuthError& error));
+ MOCK_METHOD1(OnUserNameFetchFailure,
+ void(const GoogleServiceAuthError& error));
+ MOCK_METHOD1(OnDidFetchWalletCookieValue,
+ void(const std::string& cookie_value));
+};
+
+class WalletSigninHelperForTesting : public WalletSigninHelper {
+ public:
+ WalletSigninHelperForTesting(WalletSigninHelperDelegate* delegate,
+ net::URLRequestContextGetter* getter)
+ : WalletSigninHelper(delegate, getter) {
+ }
+
+ // Bring in the test-only getters.
+ using WalletSigninHelper::GetGetAccountInfoUrlForTesting;
+ using WalletSigninHelper::state;
+
+ // Bring in the State enum.
+ using WalletSigninHelper::State;
+ using WalletSigninHelper::IDLE;
+};
+
+} // namespace
+
+class WalletSigninHelperTest : public testing::Test {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ signin_helper_.reset(new WalletSigninHelperForTesting(
+ &mock_delegate_,
+ browser_context_.GetRequestContext()));
+ EXPECT_EQ(WalletSigninHelperForTesting::IDLE, state());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ signin_helper_.reset();
+ }
+
+ // Sets up a response for the mock URLFetcher and completes the request.
+ void SetUpFetcherResponseAndCompleteRequest(
+ const std::string& url,
+ int response_code,
+ const net::ResponseCookies& cookies,
+ const std::string& response_string) {
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ ASSERT_TRUE(fetcher->delegate());
+
+ fetcher->set_url(GURL(url));
+ fetcher->set_status(net::URLRequestStatus());
+ fetcher->set_response_code(response_code);
+ fetcher->SetResponseString(response_string);
+ fetcher->set_cookies(cookies);
+ fetcher->delegate()->OnURLFetchComplete(fetcher);
+ }
+
+ void MockSuccessfulGetAccountInfoResponse(const std::string& username) {
+ SetUpFetcherResponseAndCompleteRequest(
+ signin_helper_->GetGetAccountInfoUrlForTesting(), net::HTTP_OK,
+ net::ResponseCookies(),
+ base::StringPrintf(
+ kGetAccountInfoValidResponseFormat,
+ username.c_str()));
+ }
+
+ void MockFailedGetAccountInfoResponse404() {
+ SetUpFetcherResponseAndCompleteRequest(
+ signin_helper_->GetGetAccountInfoUrlForTesting(),
+ net::HTTP_NOT_FOUND,
+ net::ResponseCookies(),
+ std::string());
+ }
+
+ void MockSuccessfulPassiveSignInResponse() {
+ SetUpFetcherResponseAndCompleteRequest(wallet::GetPassiveAuthUrl().spec(),
+ net::HTTP_OK,
+ net::ResponseCookies(),
+ "YES");
+ }
+
+ void MockFailedPassiveSignInResponseNo() {
+ SetUpFetcherResponseAndCompleteRequest(wallet::GetPassiveAuthUrl().spec(),
+ net::HTTP_OK,
+ net::ResponseCookies(),
+ "NOOOOOOOOOOOOOOO");
+ }
+
+ void MockFailedPassiveSignInResponse404() {
+ SetUpFetcherResponseAndCompleteRequest(wallet::GetPassiveAuthUrl().spec(),
+ net::HTTP_NOT_FOUND,
+ net::ResponseCookies(),
+ std::string());
+ }
+
+ WalletSigninHelperForTesting::State state() const {
+ return signin_helper_->state();
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ scoped_ptr<WalletSigninHelperForTesting> signin_helper_;
+ MockWalletSigninHelperDelegate mock_delegate_;
+ TestingProfile browser_context_;
+
+ private:
+ net::TestURLFetcherFactory factory_;
+};
+
+TEST_F(WalletSigninHelperTest, PassiveSigninSuccessful) {
+ EXPECT_CALL(mock_delegate_, OnPassiveSigninSuccess("user@gmail.com"));
+ signin_helper_->StartPassiveSignin();
+ MockSuccessfulPassiveSignInResponse();
+ MockSuccessfulGetAccountInfoResponse("user@gmail.com");
+}
+
+TEST_F(WalletSigninHelperTest, PassiveSigninFailedSignin404) {
+ EXPECT_CALL(mock_delegate_, OnPassiveSigninFailure(_));
+ signin_helper_->StartPassiveSignin();
+ MockFailedPassiveSignInResponse404();
+}
+
+TEST_F(WalletSigninHelperTest, PassiveSigninFailedSigninNo) {
+ EXPECT_CALL(mock_delegate_, OnPassiveSigninFailure(_));
+ signin_helper_->StartPassiveSignin();
+ MockFailedPassiveSignInResponseNo();
+}
+
+TEST_F(WalletSigninHelperTest, PassiveSigninFailedUserInfo) {
+ EXPECT_CALL(mock_delegate_, OnPassiveSigninFailure(_));
+ signin_helper_->StartPassiveSignin();
+ MockSuccessfulPassiveSignInResponse();
+ MockFailedGetAccountInfoResponse404();
+}
+
+TEST_F(WalletSigninHelperTest, PassiveUserInfoSuccessful) {
+ EXPECT_CALL(mock_delegate_, OnUserNameFetchSuccess("user@gmail.com"));
+ signin_helper_->StartUserNameFetch();
+ MockSuccessfulGetAccountInfoResponse("user@gmail.com");
+}
+
+TEST_F(WalletSigninHelperTest, PassiveUserInfoFailedUserInfo) {
+ EXPECT_CALL(mock_delegate_, OnUserNameFetchFailure(_));
+ signin_helper_->StartUserNameFetch();
+ MockFailedGetAccountInfoResponse404();
+}
+
+TEST_F(WalletSigninHelperTest, GetWalletCookieValueWhenPresent) {
+ EXPECT_CALL(mock_delegate_, OnDidFetchWalletCookieValue("gdToken"));
+ net::CookieMonster* cookie_monster = new net::CookieMonster(NULL, NULL);
+ net::CookieOptions httponly_options;
+ httponly_options.set_include_httponly();
+ scoped_ptr<net::CanonicalCookie> cookie(
+ net::CanonicalCookie::Create(GetPassiveAuthUrl().GetWithEmptyPath(),
+ "gdToken=gdToken; HttpOnly",
+ base::Time::Now(),
+ httponly_options));
+
+ net::CookieList cookie_list;
+ cookie_list.push_back(*cookie);
+ cookie_monster->InitializeFrom(cookie_list);
+ browser_context_.GetRequestContext()->GetURLRequestContext()
+ ->set_cookie_store(cookie_monster);
+ signin_helper_->StartWalletCookieValueFetch();
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(WalletSigninHelperTest, GetWalletCookieValueWhenMissing) {
+ EXPECT_CALL(mock_delegate_, OnDidFetchWalletCookieValue(std::string()));
+ net::CookieMonster* cookie_monster = new net::CookieMonster(NULL, NULL);
+ net::CookieOptions httponly_options;
+ httponly_options.set_include_httponly();
+ scoped_ptr<net::CanonicalCookie> cookie(
+ net::CanonicalCookie::Create(GetPassiveAuthUrl().GetWithEmptyPath(),
+ "fake_cookie=monkeys; HttpOnly",
+ base::Time::Now(),
+ httponly_options));
+
+ net::CookieList cookie_list;
+ cookie_list.push_back(*cookie);
+ cookie_monster->InitializeFrom(cookie_list);
+ browser_context_.GetRequestContext()->GetURLRequestContext()
+ ->set_cookie_store(cookie_monster);
+ signin_helper_->StartWalletCookieValueFetch();
+ base::RunLoop().RunUntilIdle();
+}
+
+// TODO(aruslan): http://crbug.com/188317 Need more tests.
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc b/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc
new file mode 100644
index 00000000000..15f916afdc0
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc
@@ -0,0 +1,237 @@
+// 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/content/browser/wallet/wallet_test_util.h"
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/autofill/content/browser/wallet/full_wallet.h"
+#include "components/autofill/content/browser/wallet/instrument.h"
+#include "components/autofill/content/browser/wallet/required_action.h"
+#include "components/autofill/content/browser/wallet/wallet_address.h"
+
+namespace autofill {
+namespace wallet {
+
+namespace {
+
+int FutureYear() {
+ // "In the Year 3000." - Richie "LaBamba" Rosenberg
+ return 3000;
+}
+
+} // namespace
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentWithDetails(
+ const std::string& id,
+ scoped_ptr<Address> address,
+ WalletItems::MaskedInstrument::Type type,
+ WalletItems::MaskedInstrument::Status status) {
+ return scoped_ptr<WalletItems::MaskedInstrument>(
+ new WalletItems::MaskedInstrument(ASCIIToUTF16("descriptive_name"),
+ type,
+ std::vector<base::string16>(),
+ ASCIIToUTF16("1111"),
+ 12,
+ FutureYear(),
+ address.Pass(),
+ status,
+ id));
+}
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentWithId(
+ const std::string& id) {
+ return GetTestMaskedInstrumentWithDetails(
+ id,
+ GetTestAddress(),
+ WalletItems::MaskedInstrument::VISA,
+ WalletItems::MaskedInstrument::VALID);
+}
+
+scoped_ptr<WalletItems::MaskedInstrument>
+GetTestMaskedInstrumentWithIdAndAddress(
+ const std::string& id, scoped_ptr<Address> address) {
+ return GetTestMaskedInstrumentWithDetails(
+ id,
+ address.Pass(),
+ WalletItems::MaskedInstrument::VISA,
+ WalletItems::MaskedInstrument::VALID);
+}
+
+scoped_ptr<Address> GetTestAddress() {
+ return scoped_ptr<Address>(new Address("US",
+ 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()));
+}
+
+scoped_ptr<Address> GetTestMinimalAddress() {
+ scoped_ptr<Address> address = GetTestAddress();
+ address->set_is_complete_address(false);
+ return address.Pass();
+}
+
+scoped_ptr<FullWallet> GetTestFullWallet() {
+ scoped_ptr<FullWallet> wallet(new FullWallet(FutureYear(),
+ 12,
+ "528512",
+ "5ec4feecf9d6",
+ GetTestAddress(),
+ GetTestShippingAddress(),
+ std::vector<RequiredAction>()));
+ std::vector<uint8> one_time_pad;
+ base::HexStringToBytes("5F04A8704183", &one_time_pad);
+ wallet->set_one_time_pad(one_time_pad);
+ return wallet.Pass();
+}
+
+scoped_ptr<FullWallet> GetTestFullWalletInstrumentOnly() {
+ scoped_ptr<FullWallet> wallet(new FullWallet(FutureYear(),
+ 12,
+ "528512",
+ "5ec4feecf9d6",
+ GetTestAddress(),
+ scoped_ptr<Address>(),
+ std::vector<RequiredAction>()));
+ std::vector<uint8> one_time_pad;
+ base::HexStringToBytes("5F04A8704183", &one_time_pad);
+ wallet->set_one_time_pad(one_time_pad);
+ return wallet.Pass();
+}
+
+scoped_ptr<Instrument> GetTestInstrument() {
+ return scoped_ptr<Instrument>(new Instrument(ASCIIToUTF16("4444444444444448"),
+ ASCIIToUTF16("123"),
+ 12,
+ FutureYear(),
+ Instrument::VISA,
+ GetTestAddress()));
+}
+
+scoped_ptr<Instrument> GetTestAddressUpgradeInstrument() {
+ scoped_ptr<Instrument> instrument(new Instrument(base::string16(),
+ base::string16(),
+ 0,
+ 0,
+ Instrument::UNKNOWN,
+ GetTestAddress()));
+ instrument->set_object_id("instrument_id");
+ return instrument.Pass();
+}
+
+scoped_ptr<Instrument> GetTestExpirationDateChangeInstrument() {
+ scoped_ptr<Instrument> instrument(new Instrument(base::string16(),
+ ASCIIToUTF16("123"),
+ 12,
+ FutureYear(),
+ Instrument::UNKNOWN,
+ scoped_ptr<Address>()));
+ instrument->set_object_id("instrument_id");
+ return instrument.Pass();
+}
+
+scoped_ptr<Instrument> GetTestAddressNameChangeInstrument() {
+ scoped_ptr<Instrument> instrument(new Instrument(base::string16(),
+ ASCIIToUTF16("123"),
+ 0,
+ 0,
+ Instrument::UNKNOWN,
+ GetTestAddress()));
+ instrument->set_object_id("instrument_id");
+ return instrument.Pass();
+}
+
+scoped_ptr<WalletItems::LegalDocument> GetTestLegalDocument() {
+ base::DictionaryValue dict;
+ dict.SetString("legal_document_id", "document_id");
+ dict.SetString("display_name", "display_name");
+ return wallet::WalletItems::LegalDocument::CreateLegalDocument(dict);
+}
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrument() {
+ return GetTestMaskedInstrumentWithId("default_instrument_id");
+}
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentExpired() {
+ return GetTestMaskedInstrumentWithDetails(
+ "default_instrument_id",
+ GetTestAddress(),
+ WalletItems::MaskedInstrument::VISA,
+ WalletItems::MaskedInstrument::EXPIRED);
+}
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid() {
+ return GetTestMaskedInstrumentWithDetails(
+ "default_instrument_id",
+ GetTestAddress(),
+ WalletItems::MaskedInstrument::VISA,
+ WalletItems::MaskedInstrument::DECLINED);
+}
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentAmex() {
+ return GetTestMaskedInstrumentWithDetails(
+ "default_instrument_id",
+ GetTestAddress(),
+ WalletItems::MaskedInstrument::AMEX,
+ // Amex cards are marked with status AMEX_NOT_SUPPORTED by the server.
+ WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED);
+}
+
+scoped_ptr<WalletItems::MaskedInstrument> GetTestNonDefaultMaskedInstrument() {
+ return GetTestMaskedInstrumentWithId("instrument_id");
+}
+
+scoped_ptr<Address> GetTestSaveableAddress() {
+ return scoped_ptr<Address>(new Address(
+ "US",
+ ASCIIToUTF16("save_recipient_name"),
+ ASCIIToUTF16("save_address_line_1"),
+ ASCIIToUTF16("save_address_line_2"),
+ ASCIIToUTF16("save_locality_name"),
+ ASCIIToUTF16("save_admin_area_name"),
+ ASCIIToUTF16("save_postal_code_number"),
+ ASCIIToUTF16("save_phone_number"),
+ std::string()));
+}
+
+scoped_ptr<Address> GetTestShippingAddress() {
+ return scoped_ptr<Address>(new Address(
+ "US",
+ 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"),
+ "default_address_id"));
+}
+
+scoped_ptr<Address> GetTestNonDefaultShippingAddress() {
+ scoped_ptr<Address> address = GetTestShippingAddress();
+ address->set_object_id("address_id");
+ return address.Pass();
+}
+
+scoped_ptr<WalletItems> GetTestWalletItems() {
+ return scoped_ptr<WalletItems>(
+ new wallet::WalletItems(std::vector<RequiredAction>(),
+ "google_transaction_id",
+ "default_instrument_id",
+ "default_address_id",
+ "obfuscated_gaia_id"));
+}
+
+} // namespace wallet
+} // namespace autofill
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_test_util.h b/chromium/components/autofill/content/browser/wallet/wallet_test_util.h
new file mode 100644
index 00000000000..fd78b05b865
--- /dev/null
+++ b/chromium/components/autofill/content/browser/wallet/wallet_test_util.h
@@ -0,0 +1,43 @@
+// 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_CONTENT_BROWSER_WALLET_WALLET_TEST_UTIL_H_
+#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_TEST_UTIL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/content/browser/wallet/wallet_items.h"
+
+namespace autofill {
+namespace wallet {
+
+class Address;
+class FullWallet;
+class Instrument;
+
+scoped_ptr<Address> GetTestAddress();
+scoped_ptr<Address> GetTestMinimalAddress();
+scoped_ptr<FullWallet> GetTestFullWallet();
+scoped_ptr<FullWallet> GetTestFullWalletInstrumentOnly();
+scoped_ptr<Instrument> GetTestInstrument();
+scoped_ptr<Instrument> GetTestAddressUpgradeInstrument();
+scoped_ptr<Instrument> GetTestExpirationDateChangeInstrument();
+scoped_ptr<Instrument> GetTestAddressNameChangeInstrument();
+scoped_ptr<WalletItems::LegalDocument> GetTestLegalDocument();
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrument();
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentExpired();
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid();
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentAmex();
+scoped_ptr<WalletItems::MaskedInstrument> GetTestNonDefaultMaskedInstrument();
+scoped_ptr<WalletItems::MaskedInstrument>
+ GetTestMaskedInstrumentWithIdAndAddress(
+ const std::string& id, scoped_ptr<Address> address);
+scoped_ptr<Address> GetTestSaveableAddress();
+scoped_ptr<Address> GetTestShippingAddress();
+scoped_ptr<Address> GetTestNonDefaultShippingAddress();
+scoped_ptr<WalletItems> GetTestWalletItems();
+
+} // namespace wallet
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_WALLET_WALLET_TEST_UTIL_H_
diff --git a/chromium/components/autofill/content/renderer/DEPS b/chromium/components/autofill/content/renderer/DEPS
new file mode 100644
index 00000000000..dbd28cf1ed0
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/common",
+ "+content/public/renderer",
+]
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
new file mode 100644
index 00000000000..e8509c74750
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -0,0 +1,785 @@
+// 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/content/renderer/autofill_agent.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/content/renderer/page_click_tracker.h"
+#include "components/autofill/content/renderer/password_autofill_agent.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_data_predictions.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/web_element_descriptor.h"
+#include "content/public/common/password_form.h"
+#include "content/public/common/ssl_status.h"
+#include "content/public/common/url_constants.h"
+#include "content/public/renderer/render_view.h"
+#include "grit/component_strings.h"
+#include "net/cert/cert_status_flags.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/web/WebDataSource.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFormControlElement.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "third_party/WebKit/public/web/WebNode.h"
+#include "third_party/WebKit/public/web/WebNodeCollection.h"
+#include "third_party/WebKit/public/web/WebOptionElement.h"
+#include "third_party/WebKit/public/web/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;
+
+
+// Gets all the data list values (with corresponding label) for the given
+// element.
+void GetDataListSuggestions(const WebKit::WebInputElement& element,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels) {
+ WebNodeCollection options = element.dataListOptions();
+ if (options.isNull())
+ return;
+
+ base::string16 prefix = element.editingValue();
+ if (element.isMultiple() &&
+ element.formControlType() == WebString::fromUTF8("email")) {
+ std::vector<base::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(base::string16());
+ }
+}
+
+// Trim the vector before sending it to the browser process to ensure we
+// don't send too much data through the IPC.
+void TrimStringVectorForIPC(std::vector<base::string16>* strings) {
+ // Limit the size of the vector.
+ if (strings->size() > kMaximumDataListSizeForAutofill)
+ strings->resize(kMaximumDataListSizeForAutofill);
+
+ // Limit the size of the strings in the vector.
+ for (size_t i = 0; i < strings->size(); ++i) {
+ if ((*strings)[i].length() > kMaximumTextSizeForAutofill)
+ (*strings)[i].resize(kMaximumTextSizeForAutofill);
+ }
+}
+
+gfx::RectF GetScaledBoundingBox(float scale, WebInputElement* element) {
+ gfx::Rect bounding_box(element->boundsInViewportSpace());
+ return gfx::RectF(bounding_box.x() * scale,
+ bounding_box.y() * scale,
+ bounding_box.width() * scale,
+ bounding_box.height() * scale);
+}
+
+} // namespace
+
+namespace autofill {
+
+AutofillAgent::AutofillAgent(content::RenderView* render_view,
+ PasswordAutofillAgent* password_autofill_agent)
+ : content::RenderViewObserver(render_view),
+ password_autofill_agent_(password_autofill_agent),
+ 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),
+ is_autocheckout_supported_(false),
+ has_new_forms_for_browser_(false),
+ ignore_text_changes_(false),
+ weak_ptr_factory_(this) {
+ render_view->GetWebView()->setAutofillClient(this);
+
+ // The PageClickTracker is a RenderViewObserver, and hence will be freed when
+ // the RenderView is destroyed.
+ new PageClickTracker(render_view, this);
+}
+
+AutofillAgent::~AutofillAgent() {}
+
+bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AutofillAgent, message)
+ IPC_MESSAGE_HANDLER(AutofillMsg_GetAllForms, OnGetAllForms)
+ 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_HANDLER(AutofillMsg_AutocheckoutSupported,
+ OnAutocheckoutSupported)
+ IPC_MESSAGE_HANDLER(AutofillMsg_PageShown,
+ OnPageShown)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) {
+ // Record timestamp on document load. This is used to record overhead of
+ // Autofill feature.
+ forms_seen_timestamp_ = base::TimeTicks::Now();
+
+ // The document has now been fully loaded. Scan for forms to be sent up to
+ // the browser.
+ std::vector<FormData> forms;
+ bool has_more_forms = false;
+ if (!frame->parent()) {
+ topmost_frame_ = frame;
+ form_elements_.clear();
+ has_more_forms = form_cache_.ExtractFormsAndFormElements(
+ *frame, kRequiredAutofillFields, &forms, &form_elements_);
+ } else {
+ form_cache_.ExtractForms(*frame, &forms);
+ }
+
+ autofill::FormsSeenState state = has_more_forms ?
+ autofill::PARTIAL_FORMS_SEEN : autofill::NO_SPECIAL_FORMS_SEEN;
+
+ // Always communicate to browser process for topmost frame.
+ if (!forms.empty() || !frame->parent()) {
+ Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
+ forms_seen_timestamp_,
+ state));
+ }
+}
+
+void AutofillAgent::DidStartProvisionalLoad(WebFrame* frame) {
+ if (!frame->parent()) {
+ is_autocheckout_supported_ = false;
+ topmost_frame_ = NULL;
+ if (click_timer_.IsRunning()) {
+ click_timer_.Stop();
+ autocheckout_click_in_progress_ = true;
+ }
+ }
+}
+
+void AutofillAgent::DidFailProvisionalLoad(WebFrame* frame,
+ const WebKit::WebURLError& error) {
+ if (!frame->parent() && autocheckout_click_in_progress_) {
+ autocheckout_click_in_progress_ = false;
+ ClickFailed();
+ }
+}
+
+void AutofillAgent::DidCommitProvisionalLoad(WebFrame* frame,
+ bool is_new_navigation) {
+ in_flight_request_form_.reset();
+ if (!frame->parent() && autocheckout_click_in_progress_) {
+ autocheckout_click_in_progress_ = false;
+ CompleteAutocheckoutPage(SUCCESS);
+ }
+}
+
+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.
+ HideAutofillUI();
+}
+
+void AutofillAgent::FocusedNodeChanged(const WebKit::WebNode& node) {
+ if (node.isNull() || !node.isElementNode())
+ return;
+
+ WebKit::WebElement web_element = node.toConst<WebKit::WebElement>();
+
+ if (!web_element.document().frame())
+ return;
+
+ const WebInputElement* element = toWebInputElement(&web_element);
+
+ if (!element || !element->isEnabled() || element->isReadOnly() ||
+ !element->isTextField() || element->isPasswordField())
+ return;
+
+ element_ = *element;
+
+ MaybeShowAutocheckoutBubble();
+}
+
+void AutofillAgent::MaybeShowAutocheckoutBubble() {
+ if (element_.isNull() || !element_.focused())
+ return;
+
+ FormData form;
+ FormFieldData field;
+ // This must be called to short circuit this method if it fails.
+ if (!FindFormAndFieldForInputElement(element_, &form, &field, REQUIRE_NONE))
+ return;
+
+ Send(new AutofillHostMsg_MaybeShowAutocheckoutBubble(
+ routing_id(),
+ form,
+ GetScaledBoundingBox(web_view_->pageScaleFactor(), &element_)));
+}
+
+void AutofillAgent::DidChangeScrollOffset(WebKit::WebFrame*) {
+ HideAutofillUI();
+}
+
+void AutofillAgent::didRequestAutocomplete(WebKit::WebFrame* frame,
+ const WebFormElement& form) {
+ GURL url(frame->document().url());
+ content::SSLStatus ssl_status = render_view()->GetSSLStatusOfFrame(frame);
+ FormData form_data;
+ if (!in_flight_request_form_.isNull() ||
+ (url.SchemeIs(chrome::kHttpsScheme) &&
+ (net::IsCertStatusError(ssl_status.cert_status) ||
+ net::IsCertStatusMinorError(ssl_status.cert_status))) ||
+ !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_;
+ HideAutofillUI();
+
+ in_flight_request_form_ = form;
+ Send(new AutofillHostMsg_RequestAutocomplete(routing_id(), form_data, url));
+}
+
+void AutofillAgent::setIgnoreTextChanges(bool ignore) {
+ ignore_text_changes_ = ignore;
+}
+
+void AutofillAgent::InputElementClicked(const WebInputElement& element,
+ bool was_focused,
+ bool is_focused) {
+ if (was_focused)
+ ShowSuggestions(element, true, false, true);
+}
+
+void AutofillAgent::InputElementLostFocus() {
+ HideAutofillUI();
+}
+
+void AutofillAgent::didClearAutofillSelection(const WebNode& node) {
+ if (password_autofill_agent_->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::textFieldDidEndEditing(const WebInputElement& element) {
+ password_autofill_agent_->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();
+ base::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_agent_->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_agent_->TextFieldHandlingKeyDown(element, event)) {
+ element_ = element;
+ return;
+ }
+
+ if (event.windowsKeyCode == ui::VKEY_DOWN ||
+ event.windowsKeyCode == ui::VKEY_UP)
+ ShowSuggestions(element, true, true, true);
+}
+
+void AutofillAgent::AcceptDataListSuggestion(
+ const base::string16& suggested_value) {
+ base::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<base::string16> parts;
+
+ base::SplitStringDontTrim(element_.editingValue(), ',', &parts);
+ if (parts.size() == 0)
+ parts.push_back(base::string16());
+
+ base::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 base::string16& value) {
+ SetNodeText(value, &element_);
+}
+
+void AutofillAgent::OnAcceptDataListSuggestion(const base::string16& value) {
+ AcceptDataListSuggestion(value);
+}
+
+void AutofillAgent::OnAcceptPasswordAutofillSuggestion(
+ const base::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_agent_->DidAcceptAutofillSuggestion(
+ element_,
+ value);
+ DCHECK(handled);
+}
+
+void AutofillAgent::OnGetAllForms() {
+ form_elements_.clear();
+
+ // Force fetch all non empty forms.
+ std::vector<FormData> forms;
+ form_cache_.ExtractFormsAndFormElements(
+ *topmost_frame_, 0, &forms, &form_elements_);
+
+ // OnGetAllForms should only be called if AutofillAgent reported to
+ // AutofillManager that there are more forms
+ DCHECK(!forms.empty());
+
+ // Report to AutofillManager that all forms are being sent.
+ Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
+ forms_seen_timestamp_,
+ NO_SPECIAL_FORMS_SEEN));
+}
+
+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_);
+ if (!in_flight_request_form_.checkValidityWithoutDispatchingEvents())
+ result = WebFormElement::AutocompleteResultErrorInvalid;
+ }
+
+ in_flight_request_form_.finishRequestAutocomplete(result);
+ in_flight_request_form_.reset();
+}
+
+void AutofillAgent::OnFillFormsAndClick(
+ const std::vector<FormData>& forms,
+ const std::vector<WebElementDescriptor>& click_elements_before_form_fill,
+ const std::vector<WebElementDescriptor>& click_elements_after_form_fill,
+ const WebElementDescriptor& click_element_descriptor) {
+ DCHECK_EQ(forms.size(), form_elements_.size());
+
+ // Click elements in click_elements_before_form_fill.
+ for (size_t i = 0; i < click_elements_before_form_fill.size(); ++i) {
+ if (!ClickElement(topmost_frame_->document(),
+ click_elements_before_form_fill[i])) {
+ CompleteAutocheckoutPage(MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING);
+ return;
+ }
+ }
+
+ // Fill the form.
+ for (size_t i = 0; i < forms.size(); ++i)
+ FillFormForAllElements(forms[i], form_elements_[i]);
+
+ // Click elements in click_elements_after_form_fill.
+ for (size_t i = 0; i < click_elements_after_form_fill.size(); ++i) {
+ if (!ClickElement(topmost_frame_->document(),
+ click_elements_after_form_fill[i])) {
+ CompleteAutocheckoutPage(MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING);
+ return;
+ }
+ }
+
+ // Exit early if there is nothing to click.
+ if (click_element_descriptor.retrieval_method == WebElementDescriptor::NONE) {
+ CompleteAutocheckoutPage(SUCCESS);
+ return;
+ }
+
+ // 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)) {
+ CompleteAutocheckoutPage(MISSING_ADVANCE);
+ }
+}
+
+void AutofillAgent::OnAutocheckoutSupported() {
+ is_autocheckout_supported_ = true;
+ if (has_new_forms_for_browser_)
+ MaybeSendDynamicFormsSeen();
+ MaybeShowAutocheckoutBubble();
+}
+
+void AutofillAgent::OnPageShown() {
+ if (is_autocheckout_supported_)
+ MaybeShowAutocheckoutBubble();
+}
+
+void AutofillAgent::CompleteAutocheckoutPage(
+ autofill::AutocheckoutStatus status) {
+ click_timer_.Stop();
+ Send(new AutofillHostMsg_AutocheckoutPageCompleted(routing_id(), status));
+}
+
+void AutofillAgent::ClickFailed() {
+ CompleteAutocheckoutPage(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.
+ HideAutofillUI();
+ return;
+ }
+
+ element_ = element;
+ if (password_autofill_agent_->ShowSuggestions(element))
+ return;
+
+ // If autocomplete is disabled at the field level, ensure that the native
+ // UI won't try to show a warning, since that may conflict with a custom
+ // popup. 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.
+ const base::string16 autocomplete_attribute =
+ element.getAttribute("autocomplete");
+ if (LowerCaseEqualsASCII(autocomplete_attribute, "off"))
+ display_warning_if_disabled = false;
+
+ 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
+ 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::RectF bounding_box_scaled =
+ GetScaledBoundingBox(web_view_->pageScaleFactor(), &element_);
+
+ // Find the datalist values and send them to the browser process.
+ std::vector<base::string16> data_list_values;
+ std::vector<base::string16> data_list_labels;
+ GetDataListSuggestions(element_, &data_list_values, &data_list_labels);
+ TrimStringVectorForIPC(&data_list_values);
+ TrimStringVectorForIPC(&data_list_labels);
+
+ Send(new AutofillHostMsg_SetDataList(routing_id(),
+ data_list_values,
+ data_list_labels));
+
+ 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 base::string16& value,
+ WebKit::WebInputElement* node) {
+ did_set_node_text_ = true;
+ base::string16 substring = value;
+ substring = substring.substr(0, node->maxLength());
+
+ node->setEditingValue(substring);
+}
+
+void AutofillAgent::HideAutofillUI() {
+ Send(new AutofillHostMsg_HideAutofillUI(routing_id()));
+}
+
+void AutofillAgent::didAssociateFormControls(
+ const WebKit::WebVector<WebKit::WebNode>& nodes) {
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ WebKit::WebNode node = nodes[i];
+ if (node.document().frame() == topmost_frame_) {
+ forms_seen_timestamp_ = base::TimeTicks::Now();
+ has_new_forms_for_browser_ = true;
+ break;
+ }
+ }
+
+ if (has_new_forms_for_browser_ && is_autocheckout_supported_)
+ MaybeSendDynamicFormsSeen();
+}
+
+void AutofillAgent::MaybeSendDynamicFormsSeen() {
+ has_new_forms_for_browser_ = false;
+ form_elements_.clear();
+ std::vector<FormData> forms;
+ // This will only be called for Autocheckout flows, so send all forms to
+ // save an IPC.
+ form_cache_.ExtractFormsAndFormElements(
+ *topmost_frame_, 0, &forms, &form_elements_);
+ autofill::FormsSeenState state = autofill::DYNAMIC_FORMS_SEEN;
+
+ if (!forms.empty()) {
+ if (click_timer_.IsRunning())
+ click_timer_.Stop();
+ Send(new AutofillHostMsg_FormsSeen(routing_id(), forms,
+ forms_seen_timestamp_,
+ state));
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.h b/chromium/components/autofill/content/renderer/autofill_agent.h
new file mode 100644
index 00000000000..9236246c18d
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -0,0 +1,277 @@
+// 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_CONTENT_RENDERER_AUTOFILL_AGENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_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/time/time.h"
+#include "base/timer/timer.h"
+#include "components/autofill/content/renderer/form_cache.h"
+#include "components/autofill/content/renderer/page_click_listener.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+#include "components/autofill/core/common/forms_seen_state.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/public/web/WebAutofillClient.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+
+namespace WebKit {
+class WebNode;
+class WebView;
+}
+
+namespace autofill {
+
+struct FormData;
+struct FormFieldData;
+struct WebElementDescriptor;
+class PasswordAutofillAgent;
+
+// 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:
+ // PasswordAutofillAgent is guaranteed to outlive AutofillAgent.
+ AutofillAgent(content::RenderView* render_view,
+ PasswordAutofillAgent* 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;
+ virtual void FocusedNodeChanged(const WebKit::WebNode& node) OVERRIDE;
+
+ // PageClickListener:
+ virtual void InputElementClicked(const WebKit::WebInputElement& element,
+ bool was_focused,
+ bool is_focused) OVERRIDE;
+ virtual void InputElementLostFocus() OVERRIDE;
+
+ // WebKit::WebAutofillClient:
+ virtual void didClearAutofillSelection(const WebKit::WebNode& node) 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;
+ virtual void didAssociateFormControls(
+ const WebKit::WebVector<WebKit::WebNode>& nodes) OVERRIDE;
+
+ 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 base::string16& value);
+ void OnAcceptDataListSuggestion(const base::string16& value);
+ void OnAcceptPasswordAutofillSuggestion(const base::string16& value);
+ void OnGetAllForms();
+
+ // 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 std::vector<WebElementDescriptor>& click_elements_before_form_fill,
+ const std::vector<WebElementDescriptor>& click_elements_after_form_fill,
+ const WebElementDescriptor& element_descriptor);
+
+ // Called when |topmost_frame_| is supported for Autocheckout.
+ void OnAutocheckoutSupported();
+
+ // Called when the page is actually shown in the browser, as opposed to simply
+ // being preloaded.
+ void OnPageShown();
+
+ // Called when an Autocheckout page is completed by the renderer.
+ void CompleteAutocheckoutPage(autofill::AutocheckoutStatus status);
+
+ // 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);
+
+ // Sets the element value to reflect the selected |suggested_value|.
+ void AcceptDataListSuggestion(const base::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 base::string16& value, WebKit::WebInputElement* node);
+
+ // Hides any currently showing Autofill UI.
+ void HideAutofillUI();
+
+ void MaybeSendDynamicFormsSeen();
+
+ // Send |AutofillHostMsg_MaybeShowAutocheckoutBubble| to browser if needed.
+ void MaybeShowAutocheckoutBubble();
+
+ FormCache form_cache_;
+
+ PasswordAutofillAgent* password_autofill_agent_; // 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 |topmost_frame_| is whitelisted for Autocheckout.
+ bool is_autocheckout_supported_;
+
+ // Whether or not new forms/fields have been dynamically added
+ // since the last loaded forms were sent to the browser process.
+ bool has_new_forms_for_browser_;
+
+ // 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_;
+
+ // Timestamp of first time forms are seen.
+ base::TimeTicks forms_seen_timestamp_;
+
+ base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_;
+
+ friend class PasswordAutofillAgentTest;
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, FillFormElement);
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, SendForms);
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, SendDynamicForms);
+ FRIEND_TEST_ALL_PREFIXES(ChromeRenderViewTest, ShowAutofillWarning);
+ FRIEND_TEST_ALL_PREFIXES(PasswordAutofillAgentTest, WaitUsername);
+ FRIEND_TEST_ALL_PREFIXES(PasswordAutofillAgentTest, SuggestionAccept);
+ FRIEND_TEST_ALL_PREFIXES(PasswordAutofillAgentTest, SuggestionSelect);
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillAgent);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_AUTOFILL_AGENT_H_
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
new file mode 100644
index 00000000000..55f3a03e7c9
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -0,0 +1,1059 @@
+// 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/content/renderer/form_autofill_util.h"
+
+#include <map>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/web_element_descriptor.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebExceptionCode.h"
+#include "third_party/WebKit/public/web/WebFormControlElement.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebLabelElement.h"
+#include "third_party/WebKit/public/web/WebNode.h"
+#include "third_party/WebKit/public/web/WebNodeList.h"
+#include "third_party/WebKit/public/web/WebOptionElement.h"
+#include "third_party/WebKit/public/web/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 autofill {
+namespace {
+
+// The maximum length allowed for form data.
+const size_t kMaxDataLength = 1024;
+
+// A bit field mask for FillForm functions to not fill some fields.
+enum FieldFilterMask {
+ FILTER_NONE = 0,
+ FILTER_DISABLED_ELEMENTS = 1 << 0,
+ FILTER_READONLY_ELEMENTS = 1 << 1,
+ FILTER_NON_FOCUSABLE_ELEMENTS = 1 << 2,
+ FILTER_ALL_NON_EDITIABLE_ELEMENTS = FILTER_DISABLED_ELEMENTS |
+ FILTER_READONLY_ELEMENTS |
+ FILTER_NON_FOCUSABLE_ELEMENTS,
+};
+
+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);
+}
+
+bool IsAutocheckoutEnabled() {
+ return base::FieldTrialList::FindFullName("Autocheckout") == "Yes" ||
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalFormFilling);
+}
+
+// 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() || IsAutocheckoutEnabled();
+}
+
+// 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 base::string16 CombineAndCollapseWhitespace(
+ const base::string16& prefix,
+ const base::string16& suffix,
+ bool force_whitespace) {
+ base::string16 prefix_trimmed;
+ TrimPositions prefix_trailing_whitespace =
+ TrimWhitespace(prefix, TRIM_TRAILING, &prefix_trimmed);
+
+ // Recursively compute the children's text.
+ base::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.
+base::string16 FindChildTextInner(const WebNode& node, int depth) {
+ if (depth <= 0 || node.isNull())
+ return base::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 base::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 base::string16();
+ }
+ }
+
+ // Extract the text exactly at this node.
+ base::string16 node_text = node.nodeValue();
+
+ // Recursively compute the children's text.
+ // Preserve inter-element whitespace separation.
+ base::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.
+ base::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.
+base::string16 FindChildText(const WebNode& node) {
+ if (node.isTextNode())
+ return node.nodeValue();
+
+ WebNode child = node.firstChild();
+
+ const int kChildSearchDepth = 10;
+ base::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 ...>.
+base::string16 InferLabelFromPrevious(const WebFormControlElement& element) {
+ base::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)) {
+ base::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.
+ base::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>
+base::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 base::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>
+base::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 base::string16();
+
+ // Check all previous siblings, skipping non-element nodes, until we find a
+ // non-empty text block.
+ base::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>
+base::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 base::string16();
+
+ // Check all previous siblings, skipping non-element nodes, until we find a
+ // non-empty text block.
+ base::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>
+base::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.
+ base::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>
+base::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 base::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 base::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.
+base::string16 InferLabelForElement(const WebFormControlElement& element) {
+ base::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<base::string16>* option_values,
+ std::vector<base::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,
+ FieldFilterMask filters,
+ bool force_override,
+ Callback callback) {
+ std::vector<WebFormControlElement> control_elements;
+ ExtractAutofillableElements(form_element, 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 (base::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 (((filters & FILTER_DISABLED_ELEMENTS) && !element->isEnabled()) ||
+ ((filters & FILTER_READONLY_ELEMENTS) && element->isReadOnly()) ||
+ ((filters & FILTER_NON_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 WebElementDescriptor::RetrievalMethod& method) {
+ switch (method) {
+ case WebElementDescriptor::CSS_SELECTOR:
+ return "CSS_SELECTOR";
+ case WebElementDescriptor::ID:
+ return "ID";
+ case WebElementDescriptor::NONE:
+ return "NONE";
+ }
+ NOTREACHED();
+ return "UNKNOWN";
+}
+
+} // namespace
+
+const size_t kMaxParseableFields = 100;
+
+// All text fields, including password fields, should be extracted.
+bool IsTextInput(const WebInputElement* element) {
+ return element && element->isTextField();
+}
+
+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 base::string16 GetFormIdentifier(const WebFormElement& form) {
+ base::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->is_checkable = IsCheckableElement(input_element);
+ field->is_checked = input_element->isChecked();
+ field->should_autocomplete = input_element->autoComplete();
+ field->text_direction = input_element->directionForFormData() == "rtl" ?
+ base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
+ } 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;
+
+ base::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<base::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>();
+
+ base::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<base::string16, FormFieldData*>::iterator iter =
+ name_map.find(element_name);
+ if (iter != name_map.end()) {
+ base::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,
+ FILTER_ALL_NON_EDITIABLE_ELEMENTS,
+ false, /* dont force override */
+ &FillFormField);
+}
+
+void FillFormIncludingNonFocusableElements(const FormData& form_data,
+ const WebFormElement& form_element) {
+ if (form_element.isNull())
+ return;
+
+ FieldFilterMask filter_mask = static_cast<FieldFilterMask>(
+ FILTER_DISABLED_ELEMENTS | FILTER_READONLY_ELEMENTS);
+ ForEachMatchingFormField(form_element,
+ WebInputElement(),
+ form_data,
+ filter_mask,
+ true, /* force override */
+ &FillFormField);
+}
+
+void FillFormForAllElements(const FormData& form_data,
+ const WebFormElement& form_element) {
+ if (form_element.isNull())
+ return;
+
+ ForEachMatchingFormField(form_element,
+ WebInputElement(),
+ form_data,
+ FILTER_NONE,
+ 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,
+ FILTER_ALL_NON_EDITIABLE_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/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h
new file mode 100644
index 00000000000..5a1241897f8
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.h
@@ -0,0 +1,144 @@
+// 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_CONTENT_RENDERER_FORM_AUTOFILL_UTIL_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_FORM_AUTOFILL_UTIL_H_
+
+#include <vector>
+
+#include "base/strings/string16.h"
+
+namespace WebKit {
+class WebDocument;
+class WebFormElement;
+class WebFormControlElement;
+class WebInputElement;
+}
+
+namespace autofill {
+
+struct FormData;
+struct FormFieldData;
+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 base::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);
+
+// Fills all (including disabled, read-only and non-focusable) form control
+// elements within |form_element| with field data from |form_data|.
+void FillFormForAllElements(
+ 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_CONTENT_RENDERER_FORM_AUTOFILL_UTIL_H_
diff --git a/chromium/components/autofill/content/renderer/form_cache.cc b/chromium/components/autofill/content/renderer/form_cache.cc
new file mode 100644
index 00000000000..ba4baebf2fe
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/form_cache.cc
@@ -0,0 +1,297 @@
+// 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/content/renderer/form_cache.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_data_predictions.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/form_field_data_predictions.h"
+#include "grit/component_strings.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFormControlElement.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/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 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, kRequiredAutofillFields, forms, NULL);
+}
+
+bool FormCache::ExtractFormsAndFormElements(
+ const WebFrame& frame,
+ size_t minimum_required_fields,
+ 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 false;
+
+ web_documents_.insert(document);
+
+ WebVector<WebFormElement> web_forms;
+ document.forms(web_forms);
+
+ size_t num_fields_seen = 0;
+ bool has_skipped_forms = false;
+ 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 < minimum_required_fields &&
+ control_elements.size() > 0) {
+ has_skipped_forms = true;
+ 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() >= minimum_required_fields) {
+ forms->push_back(form);
+ if (web_form_elements)
+ web_form_elements->push_back(form_element);
+ } else {
+ has_skipped_forms = true;
+ }
+ }
+
+ // Return true if there are any WebFormElements skipped, else false.
+ return has_skipped_forms;
+}
+
+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(base::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, base::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.
+ base::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 (base::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;
+ base::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/chromium/components/autofill/content/renderer/form_cache.h b/chromium/components/autofill/content/renderer/form_cache.h
new file mode 100644
index 00000000000..9e70a3f992e
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/form_cache.h
@@ -0,0 +1,77 @@
+// 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_CONTENT_RENDERER_FORM_CACHE_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_FORM_CACHE_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/strings/string16.h"
+
+namespace WebKit {
+class WebDocument;
+class WebFormElement;
+class WebFrame;
+class WebInputElement;
+class WebSelectElement;
+}
+
+namespace autofill {
+
+struct FormData;
+struct FormDataPredictions;
+
+// Manages the forms in a RenderView.
+class FormCache {
+ public:
+ FormCache();
+ ~FormCache();
+
+ // Scans the DOM in |frame| extracting and storing forms.
+ // Fills |forms| with extracted forms.
+ void ExtractForms(const WebKit::WebFrame& frame,
+ std::vector<FormData>* forms);
+
+ // Scans the DOM in |frame| extracting and storing forms.
+ // Fills |forms| with extracted forms and |web_form_elements| with associated
+ // web form elements. Returns true if there are unextracted forms due to
+ // |minimum_required_fields| limit, else false.
+ bool ExtractFormsAndFormElements(
+ const WebKit::WebFrame& frame,
+ size_t minimum_required_fields,
+ 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, base::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_CONTENT_RENDERER_FORM_CACHE_H_
diff --git a/chromium/components/autofill/content/renderer/page_click_listener.h b/chromium/components/autofill/content/renderer/page_click_listener.h
new file mode 100644
index 00000000000..a7490a00dab
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/page_click_listener.h
@@ -0,0 +1,36 @@
+// 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_CONTENT_RENDERER_PAGE_CLICK_LISTENER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_CLICK_LISTENER_H_
+
+namespace WebKit {
+class WebInputElement;
+}
+
+namespace autofill {
+
+// 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.
+ virtual void 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.
+ virtual void InputElementLostFocus() = 0;
+
+ protected:
+ virtual ~PageClickListener() {}
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_CLICK_LISTENER_H_
diff --git a/chromium/components/autofill/content/renderer/page_click_tracker.cc b/chromium/components/autofill/content/renderer/page_click_tracker.cc
new file mode 100644
index 00000000000..ff7cde31a89
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/page_click_tracker.cc
@@ -0,0 +1,146 @@
+// 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/content/renderer/page_click_tracker.h"
+
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/content/renderer/page_click_listener.h"
+#include "content/public/renderer/render_view.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+#include "third_party/WebKit/public/web/WebDOMMouseEvent.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "third_party/WebKit/public/web/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
+
+namespace autofill {
+
+PageClickTracker::PageClickTracker(content::RenderView* render_view,
+ PageClickListener* listener)
+ : content::RenderViewObserver(render_view),
+ was_focused_(false),
+ listener_(listener) {
+}
+
+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());
+ listener_->InputElementClicked(input_element, was_focused_, is_focused);
+}
+
+void PageClickTracker::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
+ tracked_frames_.push_back(frame);
+ frame->document().addEventListener("mousedown", this, false);
+}
+
+void PageClickTracker::FrameDetached(WebKit::WebFrame* frame) {
+ std::vector<WebKit::WebFrame*>::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))
+ listener_->InputElementLostFocus();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/page_click_tracker.h b/chromium/components/autofill/content/renderer/page_click_tracker.h
new file mode 100644
index 00000000000..8328bd1ca39
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/page_click_tracker.h
@@ -0,0 +1,71 @@
+// 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_CONTENT_RENDERER_PAGE_CLICK_TRACKER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_CLICK_TRACKER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/public/web/WebDOMEventListener.h"
+#include "third_party/WebKit/public/web/WebNode.h"
+
+namespace autofill {
+
+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:
+ // The |listener| will be notified when an element is clicked. It must
+ // outlive this class.
+ PageClickTracker(content::RenderView* render_view,
+ PageClickListener* listener);
+ virtual ~PageClickTracker();
+
+ 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.
+ std::vector<WebKit::WebFrame*> tracked_frames_;
+
+ // The listener getting the actual notifications.
+ PageClickListener* listener_;
+
+ DISALLOW_COPY_AND_ASSIGN(PageClickTracker);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PAGE_CLICK_TRACKER_H_
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
new file mode 100644
index 00000000000..b6294abf59e
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -0,0 +1,680 @@
+// 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/content/renderer/password_autofill_agent.h"
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form_fill_data.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/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebAutofillClient.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+namespace autofill {
+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<base::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;
+ }
+
+ // Only fill saved passwords into password fields and usernames into
+ // text fields.
+ WebKit::WebInputElement input_element =
+ temp_elements[i].to<WebKit::WebInputElement>();
+ if (input_element.isPasswordField() !=
+ (data.fields[j].form_control_type == "password"))
+ continue;
+
+ // 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] = input_element;
+ 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<base::string16, base::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;
+
+ // Don't fill forms with uneditable fields or fields with autocomplete
+ // disabled.
+ if (!IsElementEditable(element) || !element.autoComplete())
+ return;
+ }
+
+ for (FormInputElementMap::iterator it = fe->input_elements.begin();
+ it != fe->input_elements.end(); ++it) {
+ WebKit::WebInputElement element = it->second;
+
+ // 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 base::string16& username1,
+ const base::string16& username2,
+ bool exact_match) {
+ if (exact_match)
+ return username1 == username2;
+ return StartsWith(username1, username2, true);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillAgent, public:
+
+PasswordAutofillAgent::PasswordAutofillAgent(content::RenderView* render_view)
+ : content::RenderViewObserver(render_view),
+ usernames_usage_(NOTHING_TO_AUTOFILL),
+ web_view_(render_view->GetWebView()),
+ weak_ptr_factory_(this) {
+}
+
+PasswordAutofillAgent::~PasswordAutofillAgent() {
+}
+
+bool PasswordAutofillAgent::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 PasswordAutofillAgent::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(base::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 PasswordAutofillAgent::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 PasswordAutofillAgent::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 PasswordAutofillAgent::DidClearAutofillSelection(
+ const WebKit::WebNode& node) {
+ WebKit::WebInputElement input;
+ PasswordInfo password;
+ return FindLoginInfo(node, &input, &password);
+}
+
+bool PasswordAutofillAgent::ShowSuggestions(
+ const WebKit::WebInputElement& element) {
+ LoginToPasswordInfoMap::const_iterator iter =
+ login_to_password_info_.find(element);
+ if (iter == login_to_password_info_.end())
+ return false;
+
+ return ShowSuggestionPopup(iter->second.fill_data, element);
+}
+
+void PasswordAutofillAgent::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 PasswordAutofillAgent::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PasswordAutofillAgent, message)
+ IPC_MESSAGE_HANDLER(AutofillMsg_FillPasswordForm, OnFillPasswordForm)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void PasswordAutofillAgent::DidStartLoading() {
+ if (usernames_usage_ != NOTHING_TO_AUTOFILL) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordManager.OtherPossibleUsernamesUsage",
+ usernames_usage_, OTHER_POSSIBLE_USERNAMES_MAX);
+ usernames_usage_ = NOTHING_TO_AUTOFILL;
+ }
+}
+
+void PasswordAutofillAgent::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 PasswordAutofillAgent::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 PasswordAutofillAgent::FrameDetached(WebKit::WebFrame* frame) {
+ FrameClosing(frame);
+}
+
+void PasswordAutofillAgent::FrameWillClose(WebKit::WebFrame* frame) {
+ FrameClosing(frame);
+}
+
+void PasswordAutofillAgent::OnFillPasswordForm(
+ const PasswordFormFillData& form_data) {
+ if (usernames_usage_ == NOTHING_TO_AUTOFILL) {
+ if (form_data.other_possible_usernames.size())
+ usernames_usage_ = OTHER_POSSIBLE_USERNAMES_PRESENT;
+ else if (usernames_usage_ == NOTHING_TO_AUTOFILL)
+ usernames_usage_ = OTHER_POSSIBLE_USERNAMES_ABSENT;
+ }
+
+ 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));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillAgent, private:
+
+void PasswordAutofillAgent::GetSuggestions(
+ const PasswordFormFillData& fill_data,
+ const base::string16& input,
+ std::vector<base::string16>* suggestions,
+ std::vector<base::string16>* realms) {
+ if (StartsWith(fill_data.basic_data.fields[0].value, input, false)) {
+ suggestions->push_back(fill_data.basic_data.fields[0].value);
+ realms->push_back(UTF8ToUTF16(fill_data.preferred_realm));
+ }
+
+ for (PasswordFormFillData::LoginCollection::const_iterator iter =
+ fill_data.additional_logins.begin();
+ iter != fill_data.additional_logins.end(); ++iter) {
+ if (StartsWith(iter->first, input, false)) {
+ suggestions->push_back(iter->first);
+ realms->push_back(UTF8ToUTF16(iter->second.realm));
+ }
+ }
+
+ for (PasswordFormFillData::UsernamesCollection::const_iterator iter =
+ fill_data.other_possible_usernames.begin();
+ iter != fill_data.other_possible_usernames.end(); ++iter) {
+ for (size_t i = 0; i < iter->second.size(); ++i) {
+ if (StartsWith(iter->second[i], input, false)) {
+ usernames_usage_ = OTHER_POSSIBLE_USERNAME_SHOWN;
+ suggestions->push_back(iter->second[i]);
+ realms->push_back(UTF8ToUTF16(iter->first.realm));
+ }
+ }
+ }
+}
+
+bool PasswordAutofillAgent::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<base::string16> suggestions;
+ std::vector<base::string16> realms;
+ GetSuggestions(fill_data, user_input.value(), &suggestions, &realms);
+ DCHECK_EQ(suggestions.size(), realms.size());
+
+ 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,
+ realms));
+ return !suggestions.empty();
+}
+
+bool PasswordAutofillAgent::FillUserNameAndPassword(
+ WebKit::WebInputElement* username_element,
+ WebKit::WebInputElement* password_element,
+ const PasswordFormFillData& fill_data,
+ bool exact_username_match,
+ bool set_selection) {
+ base::string16 current_username = username_element->value();
+ // username and password will contain the match found if any.
+ base::string16 username;
+ base::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.password;
+ break;
+ }
+ }
+
+ // Check possible usernames.
+ if (username.empty() && password.empty()) {
+ for (PasswordFormFillData::UsernamesCollection::const_iterator iter =
+ fill_data.other_possible_usernames.begin();
+ iter != fill_data.other_possible_usernames.end(); ++iter) {
+ for (size_t i = 0; i < iter->second.size(); ++i) {
+ if (DoUsernamesMatch(iter->second[i], current_username,
+ exact_username_match)) {
+ usernames_usage_ = OTHER_POSSIBLE_USERNAME_SELECTED;
+ username = iter->second[i];
+ password = iter->first.password;
+ break;
+ }
+ }
+ if (!username.empty() && !password.empty())
+ 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 PasswordAutofillAgent::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 PasswordAutofillAgent::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 PasswordAutofillAgent::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/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
new file mode 100644
index 00000000000..30fef628c15
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -0,0 +1,133 @@
+// 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_CONTENT_RENDERER_PASSWORD_AUTOFILL_AGENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_AUTOFILL_AGENT_H_
+
+#include <map>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "components/autofill/core/common/password_form_fill_data.h"
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+
+namespace WebKit {
+class WebInputElement;
+class WebKeyboardEvent;
+class WebView;
+}
+
+namespace autofill {
+
+// This class is responsible for filling password forms.
+// There is one PasswordAutofillAgent per RenderView.
+class PasswordAutofillAgent : public content::RenderViewObserver {
+ public:
+ explicit PasswordAutofillAgent(content::RenderView* render_view);
+ virtual ~PasswordAutofillAgent();
+
+ // 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. 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);
+ // Shows an Autofill popup with username suggestions for |element|.
+ // Returns true if any suggestions were shown, false otherwise.
+ bool ShowSuggestions(const WebKit::WebInputElement& element);
+
+ private:
+ friend class PasswordAutofillAgentTest;
+
+ enum OtherPossibleUsernamesUsage {
+ NOTHING_TO_AUTOFILL,
+ OTHER_POSSIBLE_USERNAMES_ABSENT,
+ OTHER_POSSIBLE_USERNAMES_PRESENT,
+ OTHER_POSSIBLE_USERNAME_SHOWN,
+ OTHER_POSSIBLE_USERNAME_SELECTED,
+ OTHER_POSSIBLE_USERNAMES_MAX
+ };
+
+ 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 DidStartLoading() 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;
+
+ // RenderView IPC handlers:
+ void OnFillPasswordForm(const PasswordFormFillData& form_data);
+
+ // 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 base::string16& input,
+ std::vector<base::string16>* suggestions,
+ std::vector<base::string16>* realms);
+
+ 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 for UMA stats.
+ OtherPossibleUsernamesUsage usernames_usage_;
+
+ // Pointer to the WebView. Used to access page scale factor.
+ WebKit::WebView* web_view_;
+
+ base::WeakPtrFactory<PasswordAutofillAgent> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgent);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_AUTOFILL_AGENT_H_
diff --git a/chromium/components/autofill/content/renderer/password_generation_manager.cc b/chromium/components/autofill/content/renderer/password_generation_manager.cc
new file mode 100644
index 00000000000..aa50cdc0b4d
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_generation_manager.cc
@@ -0,0 +1,229 @@
+// 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/content/renderer/password_generation_manager.h"
+
+#include "base/logging.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/password_generation_util.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/public/platform/WebCString.h"
+#include "third_party/WebKit/public/platform/WebRect.h"
+#include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/web/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()->setPasswordGeneratorClient(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;
+}
+
+void PasswordGenerationManager::openPasswordGenerator(
+ WebKit::WebInputElement& element) {
+ WebKit::WebElement button(element.passwordGeneratorButtonElement());
+ gfx::Rect rect(button.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);
+}
+
+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 base::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].passwordGeneratorButtonElement().setAttribute("style",
+ "display:block");
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::ICON_SHOWN);
+ return;
+ }
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_generation_manager.h b/chromium/components/autofill/content/renderer/password_generation_manager.h
new file mode 100644
index 00000000000..a6154286167
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_generation_manager.h
@@ -0,0 +1,82 @@
+// 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_CONTENT_RENDERER_PASSWORD_GENERATION_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_GENERATION_MANAGER_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "content/public/renderer/render_view_observer.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebPasswordGeneratorClient.h"
+#include "url/gurl.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::WebPasswordGeneratorClient {
+ 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;
+
+ // WebPasswordGeneratorClient:
+ virtual void openPasswordGenerator(WebKit::WebInputElement& element) OVERRIDE;
+
+ // Message handlers.
+ void OnFormNotBlacklisted(const content::PasswordForm& form);
+ void OnPasswordAccepted(const base::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_CONTENT_RENDERER_PASSWORD_GENERATION_MANAGER_H_
diff --git a/chromium/components/autofill/core/DEPS b/chromium/components/autofill/core/DEPS
new file mode 100644
index 00000000000..caaebde2f13
--- /dev/null
+++ b/chromium/components/autofill/core/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ # TODO(blundell): 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.
+ "!content/public/common/common_param_traits.h",
+ "!content/public/common/common_param_traits_macros.h",
+ "!content/public/common/password_form.h",
+ "!third_party/WebKit/public/web/WebFormElement.h",
+]
diff --git a/chromium/components/autofill/core/browser/DEPS b/chromium/components/autofill/core/browser/DEPS
new file mode 100644
index 00000000000..c59b4e28000
--- /dev/null
+++ b/chromium/components/autofill/core/browser/DEPS
@@ -0,0 +1,53 @@
+include_rules = [
+ "+components/webdata/common",
+ "+crypto/random.h",
+ "+google_apis/gaia",
+ "+google_apis/google_api_keys.h",
+ "+net",
+ "+sql",
+ "+third_party/libjingle",
+ "+third_party/libphonenumber", # For phone number i18n.
+
+ # TODO(blundell): 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.
+ "!components/autofill/content/browser/autocheckout/whitelist_manager.h",
+ "!components/autofill/content/browser/autocheckout_manager.h",
+ "!components/autofill/content/browser/autocheckout_page_meta_data.h",
+ "!components/autofill/content/browser/autocheckout_steps.h",
+ "!content/public/browser/android/content_view_core.h",
+ "!content/public/browser/browser_context.h",
+ "!content/public/browser/browser_thread.h",
+ "!content/public/browser/render_view_host.h",
+ "!content/public/browser/web_contents.h",
+ "!content/public/browser/web_contents_observer.h",
+ "!content/public/browser/web_contents_view.h",
+ "!content/public/common/url_constants.h",
+ "!third_party/WebKit/public/web/WebAutofillClient.h",
+ "!third_party/WebKit/public/web/WebInputElement.h",
+]
+
+specific_include_rules = {
+ '.*_[a-z]*test\.cc': [
+ "+content/public/test",
+
+ # TODO(joi): 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/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/ui/autofill/tab_autofill_manager_delegate.h",
+ "!chrome/browser/webdata/web_data_service.h",
+ "!chrome/browser/webdata/web_data_service_factory.h",
+ "!chrome/common/chrome_paths.h",
+ "!chrome/test/base/chrome_render_view_host_test_harness.h",
+ "!chrome/test/base/testing_profile.h",
+ ],
+}
diff --git a/chromium/components/autofill/core/browser/address.cc b/chromium/components/autofill/core/browser/address.cc
new file mode 100644
index 00000000000..6f7f2a93ac7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/address.cc
@@ -0,0 +1,174 @@
+// 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/core/browser/address.h"
+
+#include <stddef.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_type.h"
+
+namespace {
+
+const char16 kAddressSplitChars[] = {'-', ',', '#', '.', ' ', 0};
+
+} // namespace
+
+namespace autofill {
+
+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;
+}
+
+base::string16 Address::GetRawInfo(ServerFieldType type) const {
+ // TODO(isherman): Is GetStorableType even necessary?
+ switch (AutofillType(type).GetStorableType()) {
+ case ADDRESS_HOME_LINE1:
+ return line1_;
+
+ case ADDRESS_HOME_LINE2:
+ return line2_;
+
+ case ADDRESS_HOME_CITY:
+ return city_;
+
+ case ADDRESS_HOME_STATE:
+ return state_;
+
+ case ADDRESS_HOME_ZIP:
+ return zip_code_;
+
+ case ADDRESS_HOME_COUNTRY:
+ return ASCIIToUTF16(country_code_);
+
+ default:
+ return base::string16();
+ }
+}
+
+void Address::SetRawInfo(ServerFieldType type, const base::string16& value) {
+ // TODO(isherman): Is GetStorableType even necessary?
+ switch (AutofillType(type).GetStorableType()) {
+ case ADDRESS_HOME_LINE1:
+ line1_ = value;
+ break;
+
+ case ADDRESS_HOME_LINE2:
+ line2_ = value;
+ break;
+
+ case ADDRESS_HOME_CITY:
+ city_ = value;
+ break;
+
+ case ADDRESS_HOME_STATE:
+ state_ = value;
+ break;
+
+ case ADDRESS_HOME_COUNTRY:
+ DCHECK(value.empty() ||
+ (value.length() == 2u && IsStringASCII(value)));
+ country_code_ = UTF16ToASCII(value);
+ break;
+
+ case ADDRESS_HOME_ZIP:
+ zip_code_ = value;
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+base::string16 Address::GetInfo(const AutofillType& type,
+ const std::string& app_locale) const {
+ if (type.html_type() == HTML_TYPE_COUNTRY_CODE) {
+ return ASCIIToUTF16(country_code_);
+ } else if (type.html_type() == HTML_TYPE_STREET_ADDRESS) {
+ base::string16 address = line1_;
+ if (!line2_.empty())
+ address += ASCIIToUTF16(", ") + line2_;
+ return address;
+ }
+
+ ServerFieldType storable_type = type.GetStorableType();
+ if (storable_type == ADDRESS_HOME_COUNTRY && !country_code_.empty())
+ return AutofillCountry(country_code_, app_locale).name();
+
+ return GetRawInfo(storable_type);
+}
+
+bool Address::SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) {
+ if (type.html_type() == HTML_TYPE_COUNTRY_CODE) {
+ if (!value.empty() && (value.size() != 2u || !IsStringASCII(value))) {
+ country_code_ = std::string();
+ return false;
+ }
+
+ country_code_ = StringToUpperASCII(UTF16ToASCII(value));
+ return true;
+ } else if (type.html_type() == HTML_TYPE_STREET_ADDRESS) {
+ // Don't attempt to parse the address into lines, since this is potentially
+ // a user-entered address in the user's own format, so the code would have
+ // to rely on iffy heuristics at best. Instead, just give up when importing
+ // addresses like this.
+ line1_ = line2_ = base::string16();
+ return false;
+ }
+
+ ServerFieldType storable_type = type.GetStorableType();
+ if (storable_type == ADDRESS_HOME_COUNTRY && !value.empty()) {
+ country_code_ = AutofillCountry::GetCountryCode(value, app_locale);
+ return !country_code_.empty();
+ }
+
+ SetRawInfo(storable_type, value);
+ return true;
+}
+
+void Address::GetMatchingTypes(const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* 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);
+}
+
+void Address::GetSupportedTypes(ServerFieldTypeSet* 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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/address.h b/chromium/components/autofill/core/browser/address.h
new file mode 100644
index 00000000000..f62c32ea092
--- /dev/null
+++ b/chromium/components/autofill/core/browser/address.h
@@ -0,0 +1,59 @@
+// 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_CORE_BROWSER_ADDRESS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/form_group.h"
+
+namespace autofill {
+
+// 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 base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+ virtual base::string16 GetInfo(const AutofillType& type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) OVERRIDE;
+ virtual void GetMatchingTypes(
+ const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const OVERRIDE;
+
+ // The address, sans country info.
+ base::string16 line1_;
+ base::string16 line2_;
+ base::string16 city_;
+ base::string16 state_;
+ base::string16 zip_code_;
+
+ // The ISO 3166 2-letter country code, or an empty string if there is no
+ // country data specified for this address.
+ std::string country_code_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_H_
diff --git a/chromium/components/autofill/core/browser/address_field.cc b/chromium/components/autofill/core/browser/address_field.cc
new file mode 100644
index 00000000000..ba7c09e23d1
--- /dev/null
+++ b/chromium/components/autofill/core/browser/address_field.cc
@@ -0,0 +1,341 @@
+// 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/core/browser/address_field.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+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();
+
+ base::string16 attention_ignored = UTF8ToUTF16(autofill::kAttentionIgnoredRe);
+ base::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_) {
+ base::string16 name = StringToLowerASCII(company_->name);
+ return AddressTypeFromText(name);
+ }
+ if (address1_) {
+ base::string16 name = StringToLowerASCII(address1_->name);
+ return AddressTypeFromText(name);
+ }
+ if (address2_) {
+ base::string16 name = StringToLowerASCII(address2_->name);
+ return AddressTypeFromText(name);
+ }
+ if (city_) {
+ base::string16 name = StringToLowerASCII(city_->name);
+ return AddressTypeFromText(name);
+ }
+ if (zip_) {
+ base::string16 name = StringToLowerASCII(zip_->name);
+ return AddressTypeFromText(name);
+ }
+ if (state_) {
+ base::string16 name = StringToLowerASCII(state_->name);
+ return AddressTypeFromText(name);
+ }
+ if (country_) {
+ base::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(ServerFieldTypeMap* map) const {
+ ServerFieldType address_company;
+ ServerFieldType address_line1;
+ ServerFieldType address_line2;
+ ServerFieldType address_city;
+ ServerFieldType address_state;
+ ServerFieldType address_zip;
+ ServerFieldType 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;
+
+ base::string16 pattern = UTF8ToUTF16(autofill::kAddressLine1Re);
+ base::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 surplus lines, which we will promptly discard.
+ if (address_field->address2_ != NULL) {
+ pattern = UTF8ToUTF16(autofill::kAddressLinesExtraRe);
+ while (ParseField(scanner, pattern, NULL)) {
+ // Consumed a surplus line, try for another.
+ }
+ }
+
+ 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;
+
+ base::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 base::string16 &text) {
+ size_t same_as = text.find(UTF8ToUTF16(autofill::kAddressTypeSameAsRe));
+ size_t use_shipping = text.find(UTF8ToUTF16(autofill::kAddressTypeUseMyRe));
+ if (same_as != base::string16::npos || use_shipping != base::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 == base::string16::npos && ship == base::string16::npos)
+ return kGenericAddress;
+
+ if (bill != base::string16::npos && ship == base::string16::npos)
+ return kBillingAddress;
+
+ if (bill == base::string16::npos && ship != base::string16::npos)
+ return kShippingAddress;
+
+ if (bill > ship)
+ return kBillingAddress;
+
+ return kShippingAddress;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/address_field.h b/chromium/components/autofill/core/browser/address_field.h
new file mode 100644
index 00000000000..a12d5981608
--- /dev/null
+++ b/chromium/components/autofill/core/browser/address_field.h
@@ -0,0 +1,88 @@
+// 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_CORE_BROWSER_ADDRESS_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_FIELD_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/form_field.h"
+
+namespace autofill {
+
+class AutofillField;
+class AutofillScanner;
+
+class AddressField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(ServerFieldTypeMap* 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 base::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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ADDRESS_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/address_field_unittest.cc b/chromium/components/autofill/core/browser/address_field_unittest.cc
new file mode 100644
index 00000000000..6949ef75230
--- /dev/null
+++ b/chromium/components/autofill/core/browser/address_field_unittest.cc
@@ -0,0 +1,298 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address_field.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class AddressFieldTest : public testing::Test {
+ public:
+ AddressFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<AddressField> field_;
+ ServerFieldTypeMap 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 = base::string16();
+ field.name = base::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 = base::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")]);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/address_unittest.cc b/chromium/components/autofill/core/browser/address_unittest.cc
new file mode 100644
index 00000000000..b5bcfafaf53
--- /dev/null
+++ b/chromium/components/autofill/core/browser/address_unittest.cc
@@ -0,0 +1,198 @@
+// 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 <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+// Test that country data can be properly returned as either a country code or a
+// localized country name.
+TEST(AddressTest, GetCountry) {
+ Address address;
+ EXPECT_EQ(base::string16(), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+
+ // Make sure that nothing breaks when the country code is missing.
+ base::string16 country =
+ address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(base::string16(), country);
+
+ address.SetInfo(
+ AutofillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("US"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+ country = address.GetInfo(
+ AutofillType(HTML_TYPE_COUNTRY_NAME, HTML_MODE_NONE), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+ country = address.GetInfo(
+ AutofillType(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("US"), country);
+
+ address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("CA"));
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("Canada"), country);
+ country = address.GetInfo(
+ AutofillType(HTML_TYPE_COUNTRY_NAME, HTML_MODE_NONE), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("Canada"), country);
+ country = address.GetInfo(
+ AutofillType(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("CA"), country);
+}
+
+// Test that we properly detect country codes appropriate for each country.
+TEST(AddressTest, SetCountry) {
+ Address address;
+ EXPECT_EQ(base::string16(), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+
+ // Test basic conversion.
+ address.SetInfo(
+ AutofillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("United States"),
+ "en-US");
+ base::string16 country =
+ address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("US"), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+
+ // Test basic synonym detection.
+ address.SetInfo(
+ AutofillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("USA"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("US"), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+
+ // Test case-insensitivity.
+ address.SetInfo(
+ AutofillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("canADA"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("CA"), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(ASCIIToUTF16("Canada"), country);
+
+ // Test country code detection.
+ address.SetInfo(
+ AutofillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("JP"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("JP"), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(ASCIIToUTF16("Japan"), country);
+
+ // Test that we ignore unknown countries.
+ address.SetInfo(
+ AutofillType(ADDRESS_HOME_COUNTRY), ASCIIToUTF16("Unknown"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(base::string16(), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(base::string16(), country);
+
+ // Test setting the country based on an HTML field type.
+ AutofillType html_type_country_code =
+ AutofillType(HTML_TYPE_COUNTRY_CODE, HTML_MODE_NONE);
+ address.SetInfo(html_type_country_code, ASCIIToUTF16("US"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("US"), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(ASCIIToUTF16("United States"), country);
+
+ // Test case-insensitivity when setting the country based on an HTML field
+ // type.
+ address.SetInfo(html_type_country_code, ASCIIToUTF16("cA"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("CA"), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(ASCIIToUTF16("Canada"), country);
+
+ // Test setting the country based on invalid data with an HTML field type.
+ address.SetInfo(html_type_country_code, ASCIIToUTF16("unknown"), "en-US");
+ country = address.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), "en-US");
+ EXPECT_EQ(base::string16(), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(base::string16(), country);
+}
+
+// Test that we properly match typed values to stored country data.
+TEST(AddressTest, IsCountry) {
+ Address address;
+ address.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("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]);
+ ServerFieldTypeSet 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) {
+ ServerFieldTypeSet 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.SetRawInfo(ADDRESS_HOME_COUNTRY, base::string16());
+ EXPECT_EQ(base::string16(), address.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ ServerFieldTypeSet matching_types;
+ address.GetMatchingTypes(ASCIIToUTF16("Garbage"), "US", &matching_types);
+ EXPECT_EQ(0U, matching_types.size());
+}
+
+// Verifies that Address::GetInfo() can correctly return a concatenated full
+// street address.
+TEST(AddressTest, GetStreetAddress) {
+ // Address has no address lines.
+ Address address;
+ EXPECT_TRUE(address.GetRawInfo(ADDRESS_HOME_LINE1).empty());
+ EXPECT_TRUE(address.GetRawInfo(ADDRESS_HOME_LINE2).empty());
+
+ AutofillType type = AutofillType(HTML_TYPE_STREET_ADDRESS, HTML_MODE_NONE);
+ EXPECT_EQ(base::string16(), address.GetInfo(type, "en-US"));
+
+ // Address has only line 1.
+ address.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("123 Example Ave."));
+ EXPECT_FALSE(address.GetRawInfo(ADDRESS_HOME_LINE1).empty());
+ EXPECT_TRUE(address.GetRawInfo(ADDRESS_HOME_LINE2).empty());
+
+ EXPECT_EQ(ASCIIToUTF16("123 Example Ave."),
+ address.GetInfo(type, "en-US"));
+
+ // Address has lines 1 and 2.
+ address.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Apt. 42"));
+ EXPECT_FALSE(address.GetRawInfo(ADDRESS_HOME_LINE1).empty());
+ EXPECT_FALSE(address.GetRawInfo(ADDRESS_HOME_LINE2).empty());
+
+ EXPECT_EQ(ASCIIToUTF16("123 Example Ave., Apt. 42"),
+ address.GetInfo(type, "en-US"));
+}
+
+// Verifies that Address::SetInfo() rejects setting data for
+// HTML_TYPE_STREET_ADDRESS, as there is no good general way to parse that data
+// into the consituent address lines.
+TEST(AddressTest, SetStreetAddress) {
+ // Address has no address lines.
+ Address address;
+ address.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("123 Example Ave."));
+ address.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Apt. 42"));
+ EXPECT_FALSE(address.GetRawInfo(ADDRESS_HOME_LINE1).empty());
+ EXPECT_FALSE(address.GetRawInfo(ADDRESS_HOME_LINE2).empty());
+
+ AutofillType type = AutofillType(HTML_TYPE_STREET_ADDRESS, HTML_MODE_NONE);
+ base::string16 street_address = ASCIIToUTF16("456 New St., Apt. 17");
+ EXPECT_FALSE(address.SetInfo(type, street_address, "en-US"));
+ EXPECT_TRUE(address.GetRawInfo(ADDRESS_HOME_LINE1).empty());
+ EXPECT_TRUE(address.GetRawInfo(ADDRESS_HOME_LINE2).empty());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc b/chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc
new file mode 100644
index 00000000000..4361791f66c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc
@@ -0,0 +1,122 @@
+// 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/core/browser/android/auxiliary_profile_loader_android.h"
+
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "jni/PersonalAutofillPopulator_jni.h"
+
+#define JAVA_METHOD(__jmethod__) Java_PersonalAutofillPopulator_##__jmethod__( \
+ env_, \
+ populator_.obj())
+
+namespace {
+
+base::string16 SafeJavaStringToUTF16(
+ const ScopedJavaLocalRef<jstring>& jstring) {
+ if (jstring.is_null())
+ return base::string16();
+
+ return ConvertJavaStringToUTF16(jstring);
+}
+
+void SafeJavaStringArrayToStringVector(
+ const ScopedJavaLocalRef<jobjectArray>& jarray,
+ JNIEnv* env,
+ std::vector<base::string16>* string_vector) {
+ if (!jarray.is_null()) {
+ base::android::AppendJavaStringArrayToStringVector(env,
+ jarray.obj(),
+ string_vector);
+ }
+}
+
+} // namespace
+
+namespace autofill {
+
+bool RegisterAuxiliaryProfileLoader(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+AuxiliaryProfileLoaderAndroid::AuxiliaryProfileLoaderAndroid() {}
+
+AuxiliaryProfileLoaderAndroid::~AuxiliaryProfileLoaderAndroid() {}
+
+void AuxiliaryProfileLoaderAndroid::Init(JNIEnv* env, const jobject& context) {
+ env_ = env;
+ populator_ = Java_PersonalAutofillPopulator_create(env_, context);
+}
+
+bool AuxiliaryProfileLoaderAndroid::GetHasPermissions() const {
+ return (bool)JAVA_METHOD(getHasPermissions);
+}
+
+// Address info
+base::string16 AuxiliaryProfileLoaderAndroid::GetStreet() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getStreet));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetPostOfficeBox() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getPobox));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetNeighborhood() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getNeighborhood));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetRegion() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getRegion));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetCity() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getCity));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetPostalCode() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getPostalCode));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetCountry() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getCountry));
+}
+
+// Name info
+base::string16 AuxiliaryProfileLoaderAndroid::GetFirstName() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getFirstName));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetMiddleName() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getMiddleName));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetLastName() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getLastName));
+}
+
+base::string16 AuxiliaryProfileLoaderAndroid::GetSuffix() const {
+ return SafeJavaStringToUTF16(JAVA_METHOD(getSuffix));
+}
+
+// Email info
+void AuxiliaryProfileLoaderAndroid::GetEmailAddresses(
+ std::vector<base::string16>* email_addresses) const {
+ SafeJavaStringArrayToStringVector(JAVA_METHOD(getEmailAddresses),
+ env_,
+ email_addresses);
+}
+
+// Phone info
+void AuxiliaryProfileLoaderAndroid::GetPhoneNumbers(
+ std::vector<base::string16>* phone_numbers) const {
+ SafeJavaStringArrayToStringVector(JAVA_METHOD(getPhoneNumbers),
+ env_,
+ phone_numbers);
+}
+
+} // namespace
diff --git a/chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.h b/chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.h
new file mode 100644
index 00000000000..2185e83e377
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/auxiliary_profile_loader_android.h
@@ -0,0 +1,75 @@
+// 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_CORE_BROWSER_ANDROID_AUXILIARY_PROFILE_LOADER_ANDROID_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILE_LOADER_ANDROID_H_
+
+#include <vector>
+
+#include "base/android/jni_android.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+bool RegisterAuxiliaryProfileLoader(JNIEnv* env);
+
+// This class loads user's contact information from their device.
+// The populated data corresponds to the user's 'Me' profile in their
+// contact list.
+class AuxiliaryProfileLoaderAndroid {
+ public:
+ AuxiliaryProfileLoaderAndroid();
+ virtual ~AuxiliaryProfileLoaderAndroid();
+
+ // Init method should be called after object initialization.
+ // context parameter is an Android context.
+ void Init(JNIEnv* env, const jobject& context);
+
+ // Returns true IFF the application has priviledges to access the user's
+ // contact information.
+ virtual bool GetHasPermissions() const;
+ // Returns address street.
+ virtual base::string16 GetStreet() const;
+ // Returns address post office box.
+ virtual base::string16 GetPostOfficeBox() const;
+ // Returns address neighborhood (e.g. Noe Valley, Nob Hill, Twin Peaks, ...).
+ virtual base::string16 GetNeighborhood() const;
+ // Returns address region such as state or province information
+ // (e.g. Ontario, California, Hubei).
+ virtual base::string16 GetRegion() const;
+ // Returns address city.
+ virtual base::string16 GetCity() const;
+ // Returns address postal code or zip code.
+ virtual base::string16 GetPostalCode() const;
+ // Returns address country.
+ virtual base::string16 GetCountry() const;
+
+ // Returns contact's first name.
+ virtual base::string16 GetFirstName() const;
+ // Returns contact's middle name.
+ virtual base::string16 GetMiddleName() const;
+ // Returns contact's last name.
+ virtual base::string16 GetLastName() const;
+ // Returns contact's suffix (e.g. Ph.D, M.D., ...).
+ virtual base::string16 GetSuffix() const;
+
+ // Populates string vector parameter with contact's email addresses.
+ virtual void GetEmailAddresses(
+ std::vector<base::string16>* email_addresses) const;
+
+ // Populates string vector parameter with contact's phones numbers.
+ virtual void GetPhoneNumbers(
+ std::vector<base::string16>* phone_numbers) const;
+
+ private:
+ JNIEnv* env_;
+ // The reference to java |PersonalAutofillPopulator| which
+ // actually extracts users contact information from the physical device
+ base::android::ScopedJavaLocalRef<jobject> populator_;
+ DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfileLoaderAndroid);
+};
+
+} // namespace
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILE_LOADER_ANDROID_H_
diff --git a/chromium/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc b/chromium/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc
new file mode 100644
index 00000000000..b8f8dce1306
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc
@@ -0,0 +1,163 @@
+// 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 "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h"
+#include "components/autofill/core/browser/android/auxiliary_profiles_android.h"
+#include "components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class AuxiliaryProfileAndroidTest : public testing::Test {
+ public:
+ AuxiliaryProfileAndroidTest() {}
+
+ AutofillProfile* GetAndLoadProfile() {
+ autofill::AuxiliaryProfilesAndroid impl(profile_loader_, "en-US");
+ profile_ = impl.LoadContactsProfile();
+ return profile_.get();
+ }
+
+ TestAuxiliaryProfileLoader& profile_loader() {
+ return profile_loader_;
+ }
+
+ private:
+ TestAuxiliaryProfileLoader profile_loader_;
+ scoped_ptr<AutofillProfile> profile_;
+};
+
+TEST_F(AuxiliaryProfileAndroidTest, SetNameInfo) {
+ base::string16 first_name = ASCIIToUTF16("John");
+ base::string16 middle_name = ASCIIToUTF16("H.");
+ base::string16 last_name = ASCIIToUTF16("Waston");
+
+ profile_loader().SetFirstName(first_name);
+ profile_loader().SetMiddleName(middle_name);
+ profile_loader().SetLastName(last_name);
+
+ AutofillProfile* profile = GetAndLoadProfile();
+
+ EXPECT_EQ(profile->GetRawInfo(NAME_FIRST), first_name);
+ EXPECT_EQ(profile->GetRawInfo(NAME_MIDDLE), middle_name);
+ EXPECT_EQ(profile->GetRawInfo(NAME_LAST), last_name);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetNameInfoEmpty) {
+ AutofillProfile* profile = GetAndLoadProfile();
+
+ EXPECT_EQ(profile->GetRawInfo(NAME_FIRST), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(NAME_MIDDLE), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(NAME_LAST), base::string16());
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetEmailInfo) {
+ std::vector<base::string16> email_addresses;
+ email_addresses.push_back(ASCIIToUTF16("sherlock@holmes.com"));
+ email_addresses.push_back(ASCIIToUTF16("watson@holmes.com"));
+ profile_loader().SetEmailAddresses(email_addresses);
+
+ AutofillProfile* profile = GetAndLoadProfile();
+ std::vector<base::string16> loaded_email_addresses;
+ profile->GetRawMultiInfo(EMAIL_ADDRESS, &loaded_email_addresses);
+ EXPECT_EQ(loaded_email_addresses, email_addresses);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetEmailInfoEmpty) {
+ std::vector<base::string16> expected_email_addresses;
+ expected_email_addresses.push_back(base::string16());
+ std::vector<base::string16> loaded_email_addresses;
+ AutofillProfile* profile = GetAndLoadProfile();
+ profile->GetRawMultiInfo(EMAIL_ADDRESS, &loaded_email_addresses);
+ EXPECT_EQ(loaded_email_addresses, expected_email_addresses);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetPhoneInfo) {
+ std::vector<base::string16> phone_numbers;
+ phone_numbers.push_back(ASCIIToUTF16("6502530000"));
+ profile_loader().SetPhoneNumbers(phone_numbers);
+
+ std::vector<base::string16> loaded_phone_numbers;
+ AutofillProfile* profile = GetAndLoadProfile();
+ profile->GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &loaded_phone_numbers);
+ EXPECT_EQ(loaded_phone_numbers, phone_numbers);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetPhoneInfoEmpty) {
+ std::vector<base::string16> expected_phone_numbers;
+ expected_phone_numbers.push_back(base::string16());
+
+ std::vector<base::string16> loaded_phone_numbers;
+ AutofillProfile* profile = GetAndLoadProfile();
+ profile->GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &loaded_phone_numbers);
+ EXPECT_EQ(loaded_phone_numbers, expected_phone_numbers);
+}
+
+//
+// Android user's profile contact does not parse its address
+// into constituent parts. Instead we just Get a long string blob.
+// Disable address population tests until we implement a way to parse the
+// data.
+//
+
+#if 0
+TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfo) {
+ base::string16 street = ASCIIToUTF16("221 B Baker Street");
+ base::string16 city = ASCIIToUTF16("London");
+ base::string16 postal_code = ASCIIToUTF16("123456");
+ base::string16 region = ASCIIToUTF16("Georgian Terrace");
+ base::string16 country = ASCIIToUTF16("England");
+
+ profile_loader().SetStreet(street);
+ profile_loader().SetCity(city);
+ profile_loader().SetPostalCode(postal_code);
+ profile_loader().SetRegion(region);
+ profile_loader().SetCountry(country);
+
+ AutofillProfile* profile = GetAndLoadProfile();
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE1), street);
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_CITY), city);
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_ZIP), postal_code);
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_STATE), region);
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_COUNTRY), country);
+}
+
+base::string16 post_office_box= ASCIIToUTF16("222");
+base::string16 neighborhood = ASCIIToUTF16("Doyle");
+TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoCompoundFields1) {
+ profile_loader().SetPostOfficeBox(post_office_box);
+ profile_loader().SetNeighborhood(neighborhood);
+ base::string16 expectedLine2= ASCIIToUTF16("222, Doyle");
+ AutofillProfile* profile = GetAndLoadProfile();
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), expectedLine2);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoCompoundFields2) {
+ profile_loader().SetPostOfficeBox(post_office_box);
+ AutofillProfile* profile = GetAndLoadProfile();
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), post_office_box);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoCompoundFields3) {
+ profile_loader().SetNeighborhood(neighborhood);
+ AutofillProfile* profile = GetAndLoadProfile();
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), neighborhood);
+}
+
+TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoEmpty) {
+ AutofillProfile* profile = GetAndLoadProfile();
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE1), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_CITY), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_ZIP), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_STATE), base::string16());
+ EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_COUNTRY), base::string16());
+}
+#endif
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/android/auxiliary_profiles_android.cc b/chromium/components/autofill/core/browser/android/auxiliary_profiles_android.cc
new file mode 100644
index 00000000000..6d0b65b74d8
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/auxiliary_profiles_android.cc
@@ -0,0 +1,122 @@
+// 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.
+
+// Populates default autofill profile from user's own Android contact.
+#include "components/autofill/core/browser/android/auxiliary_profiles_android.h"
+
+#include <vector>
+
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/phone_number.h"
+
+// Generates the autofill profile by accessing the Android
+// ContactsContract.Profile API through PersonalAutofillPopulator via JNI.
+// The generated profile corresponds to the user's "ME" contact in the
+// "People" app. The caller passes a vector of profiles into the constructor
+// then initiates a fetch using |GetContactsProfile()| method. This clears
+// any existing addresses.
+
+// Randomly generated guid. The Autofillprofile class requires a consistent
+// unique guid or else things break.
+const char kAndroidMeContactA[] = "9A9E1C06-7A3B-48FA-AA4F-135CA6FC25D9";
+
+// The origin string for all profiles loaded from the Android contacts list.
+const char kAndroidContactsOrigin[] = "Android Contacts";
+
+namespace {
+
+// Takes misc. address information strings from Android API and collapses
+// into single string for "address line 2"
+base::string16 CollapseAddress(const base::string16& post_office_box,
+ const base::string16& neighborhood) {
+ std::vector<base::string16> accumulator;
+ if (!post_office_box.empty())
+ accumulator.push_back(post_office_box);
+ if (!neighborhood.empty())
+ accumulator.push_back(neighborhood);
+
+ return JoinString(accumulator, ASCIIToUTF16(", "));
+}
+
+} // namespace
+
+namespace autofill {
+
+AuxiliaryProfilesAndroid::AuxiliaryProfilesAndroid(
+ const AuxiliaryProfileLoaderAndroid& profileLoader,
+ const std::string& app_locale)
+ : profile_loader_(profileLoader),
+ app_locale_(app_locale) {}
+
+AuxiliaryProfilesAndroid::~AuxiliaryProfilesAndroid() {
+}
+
+scoped_ptr<AutofillProfile> AuxiliaryProfilesAndroid::LoadContactsProfile() {
+ scoped_ptr<AutofillProfile> profile(
+ new AutofillProfile(kAndroidMeContactA, kAndroidContactsOrigin));
+ LoadName(profile.get());
+ LoadEmailAddress(profile.get());
+ LoadPhoneNumbers(profile.get());
+
+ // Android user's profile contact does not parse its address
+ // into constituent parts. Instead we just get a long string blob.
+ // Disable address population until we implement a way to parse the
+ // data.
+ // http://crbug.com/178838
+ // LoadAddress(profile.get());
+
+ return profile.Pass();
+}
+
+void AuxiliaryProfilesAndroid::LoadAddress(AutofillProfile* profile) {
+ base::string16 street = profile_loader_.GetStreet();
+ base::string16 post_office_box = profile_loader_.GetPostOfficeBox();
+ base::string16 neighborhood = profile_loader_.GetNeighborhood();
+ base::string16 city = profile_loader_.GetCity();
+ base::string16 postal_code = profile_loader_.GetPostalCode();
+ base::string16 region = profile_loader_.GetRegion();
+ base::string16 country = profile_loader_.GetCountry();
+
+ base::string16 street2 = CollapseAddress(post_office_box, neighborhood);
+
+ profile->SetRawInfo(ADDRESS_HOME_LINE1, street);
+ profile->SetRawInfo(ADDRESS_HOME_LINE2, street2);
+ profile->SetRawInfo(ADDRESS_HOME_CITY, city);
+ profile->SetRawInfo(ADDRESS_HOME_STATE, region);
+ profile->SetRawInfo(ADDRESS_HOME_ZIP, postal_code);
+ profile->SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), country, app_locale_);
+}
+
+void AuxiliaryProfilesAndroid::LoadName(AutofillProfile* profile) {
+ base::string16 first_name = profile_loader_.GetFirstName();
+ base::string16 middle_name = profile_loader_.GetMiddleName();
+ base::string16 last_name = profile_loader_.GetLastName();
+
+ profile->SetRawInfo(NAME_FIRST, first_name);
+ profile->SetRawInfo(NAME_MIDDLE, middle_name);
+ profile->SetRawInfo(NAME_LAST, last_name);
+}
+
+void AuxiliaryProfilesAndroid::LoadEmailAddress(AutofillProfile* profile) {
+ std::vector<base::string16> emails;
+ profile_loader_.GetEmailAddresses(&emails);
+ profile->SetRawMultiInfo(EMAIL_ADDRESS, emails);
+}
+
+void AuxiliaryProfilesAndroid::LoadPhoneNumbers(AutofillProfile* profile) {
+ std::vector<base::string16> phone_numbers;
+ profile_loader_.GetPhoneNumbers(&phone_numbers);
+ profile->SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, phone_numbers);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/android/auxiliary_profiles_android.h b/chromium/components/autofill/core/browser/android/auxiliary_profiles_android.h
new file mode 100644
index 00000000000..5f81fd1f6df
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/auxiliary_profiles_android.h
@@ -0,0 +1,54 @@
+// 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_CORE_BROWSER_ANDROID_AUXILIARY_PROFILES_ANDROID_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILES_ANDROID_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h"
+
+namespace autofill {
+
+class AutofillProfile;
+class AuxiliaryProfileLoaderAndroid;
+
+ // This class is used to populate an AutofillProfile vector with
+ // a 'default' auxiliary profile. It depends on an
+ // AuxiliaryProfileLoaderAndroid object to provide contact information
+ // that is re-organized into an Autofill profile.
+class AuxiliaryProfilesAndroid {
+ public:
+ // Takes in an AuxiliaryProfileLoader object which provides contact
+ // information methods.
+ AuxiliaryProfilesAndroid(
+ const autofill::AuxiliaryProfileLoaderAndroid& profile_loader,
+ const std::string& app_locale);
+ ~AuxiliaryProfilesAndroid();
+
+ // Returns autofill profile constructed from profile_loader_.
+ scoped_ptr<AutofillProfile> LoadContactsProfile();
+
+ private:
+ // Inserts contact's address data into profile.
+ void LoadAddress(AutofillProfile* profile);
+ // Inserts contact's name data into profile.
+ void LoadName(AutofillProfile* profile);
+ // Inserts contact's email address data into profile.
+ void LoadEmailAddress(AutofillProfile* profile);
+ // Inserts contact's phone number data into profile.
+ void LoadPhoneNumbers(AutofillProfile* profile);
+
+ const AuxiliaryProfileLoaderAndroid& profile_loader_;
+ std::string app_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesAndroid);
+};
+
+} // namespace
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILES_ANDROID_H_
diff --git a/chromium/components/autofill/core/browser/android/component_jni_registrar.cc b/chromium/components/autofill/core/browser/android/component_jni_registrar.cc
new file mode 100644
index 00000000000..f3aac1f02ad
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/component_jni_registrar.cc
@@ -0,0 +1,23 @@
+// 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/core/browser/android/component_jni_registrar.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h"
+
+namespace autofill {
+
+static base::android::RegistrationMethod kComponentRegisteredMethods[] = {
+ { "RegisterAuxiliaryProfileLoader",
+ autofill::RegisterAuxiliaryProfileLoader },
+};
+
+bool RegisterAutofillAndroidJni(JNIEnv* env) {
+ return RegisterNativeMethods(env,
+ kComponentRegisteredMethods, arraysize(kComponentRegisteredMethods));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/android/component_jni_registrar.h b/chromium/components/autofill/core/browser/android/component_jni_registrar.h
new file mode 100644
index 00000000000..6cc0900cfdd
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/component_jni_registrar.h
@@ -0,0 +1,18 @@
+// 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_CORE_BROWSER_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+namespace autofill {
+
+// Register all JNI bindings necessary for the autofill
+// component.
+bool RegisterAutofillAndroidJni(JNIEnv* env);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_COMPONENT_JNI_REGISTRAR_H_
diff --git a/chromium/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java b/chromium/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java
new file mode 100644
index 00000000000..b00ed61df29
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java
@@ -0,0 +1,319 @@
+// 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.
+
+// Populates data fields from Android contacts profile API (i.e. "me" contact).
+
+package org.chromium.components.browser.autofill;
+
+import android.app.Activity;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Profile;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.util.ArrayList;
+
+/**
+ * Loads user profile information stored under the "Me" contact.
+ * Requires permissions: READ_CONTACTS and READ_PROFILE.
+ */
+@JNINamespace("autofill")
+public class PersonalAutofillPopulator {
+ /**
+ * SQL query definitions for obtaining specific profile information.
+ */
+ private abstract static class ProfileQuery {
+ Uri profileDataUri = Uri.withAppendedPath(
+ ContactsContract.Profile.CONTENT_URI,
+ ContactsContract.Contacts.Data.CONTENT_DIRECTORY
+ );
+ public abstract String[] projection();
+ public abstract String mimeType();
+ }
+
+ private static class EmailProfileQuery extends ProfileQuery {
+ private static final int EMAIL_ADDRESS = 0;
+
+ @Override
+ public String[] projection() {
+ return new String[] {
+ ContactsContract.CommonDataKinds.Email.ADDRESS,
+ };
+ }
+
+ @Override
+ public String mimeType() {
+ return ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
+ }
+ }
+
+ private static class PhoneProfileQuery extends ProfileQuery {
+ private static final int NUMBER = 0;
+
+ @Override
+ public String[] projection() {
+ return new String[] {
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ };
+ }
+
+ @Override
+ public String mimeType() {
+ return ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
+ }
+ }
+
+ private static class AddressProfileQuery extends ProfileQuery {
+ private static final int STREET = 0;
+ private static final int POBOX = 1;
+ private static final int NEIGHBORHOOD = 2;
+ private static final int CITY = 3;
+ private static final int REGION = 4;
+ private static final int POSTALCODE = 5;
+ private static final int COUNTRY = 6;
+
+ @Override
+ public String[] projection() {
+ return new String[] {
+ ContactsContract.CommonDataKinds.StructuredPostal.STREET,
+ ContactsContract.CommonDataKinds.StructuredPostal.POBOX,
+ ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD,
+ ContactsContract.CommonDataKinds.StructuredPostal.CITY,
+ ContactsContract.CommonDataKinds.StructuredPostal.REGION,
+ ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE,
+ ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY,
+ };
+ }
+
+ @Override
+ public String mimeType() {
+ return ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE;
+ }
+ }
+
+ private static class NameProfileQuery extends ProfileQuery {
+ private static final int GIVEN_NAME = 0;
+ private static final int MIDDLE_NAME = 1;
+ private static final int FAMILY_NAME = 2;
+ private static final int SUFFIX = 3;
+
+ @Override
+ public String[] projection() {
+ return new String[] {
+ ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.SUFFIX
+ };
+ }
+
+ @Override
+ public String mimeType() {
+ return ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;
+ }
+ }
+
+ /**
+ * Takes a query object, transforms into actual query and returns cursor.
+ * Primary contact values will be first.
+ */
+ private Cursor cursorFromProfileQuery(ProfileQuery query, ContentResolver contentResolver) {
+ String sortDescriptor = ContactsContract.Contacts.Data.IS_PRIMARY + " DESC";
+ return contentResolver.query(
+ query.profileDataUri,
+ query.projection(),
+ ContactsContract.Contacts.Data.MIMETYPE + " = ?",
+ new String[]{query.mimeType()},
+ sortDescriptor
+ );
+ }
+ // Extracted data variables.
+ private String[] mEmailAddresses;
+ private String mGivenName;
+ private String mMiddleName;
+ private String mFamilyName;
+ private String mSuffix;
+ private String mPobox;
+ private String mStreet;
+ private String mNeighborhood;
+ private String mCity;
+ private String mRegion;
+ private String mCountry;
+ private String mPostalCode;
+ private String[] mPhoneNumbers;
+ private boolean mHasPermissions;
+
+ /**
+ * Constructor
+ * @param context a valid android context reference
+ */
+ PersonalAutofillPopulator(Context context) {
+ mHasPermissions = hasPermissions(context);
+ if (mHasPermissions) {
+ ContentResolver contentResolver = context.getContentResolver();
+ populateName(contentResolver);
+ populateEmail(contentResolver);
+ populateAddress(contentResolver);
+ populatePhone(contentResolver);
+ }
+ }
+
+ // Check if the user has granted permissions.
+ private boolean hasPermissions(Context context) {
+ String [] permissions = {
+ "android.permission.READ_CONTACTS",
+ "android.permission.READ_PROFILE"
+ };
+ for (String permission : permissions) {
+ int res = context.checkCallingOrSelfPermission(permission);
+ if (res != PackageManager.PERMISSION_GRANTED) return false;
+ }
+ return true;
+ }
+
+ // Populating data fields.
+ private void populateName(ContentResolver contentResolver) {
+ NameProfileQuery nameProfileQuery = new NameProfileQuery();
+ Cursor nameCursor = cursorFromProfileQuery(nameProfileQuery, contentResolver);
+ if (nameCursor.moveToNext()) {
+ mGivenName = nameCursor.getString(nameProfileQuery.GIVEN_NAME);
+ mMiddleName = nameCursor.getString(nameProfileQuery.MIDDLE_NAME);
+ mFamilyName = nameCursor.getString(nameProfileQuery.FAMILY_NAME);
+ mSuffix = nameCursor.getString(nameProfileQuery.SUFFIX);
+ }
+ nameCursor.close();
+ }
+
+ private void populateEmail(ContentResolver contentResolver) {
+ EmailProfileQuery emailProfileQuery = new EmailProfileQuery();
+ Cursor emailCursor = cursorFromProfileQuery(emailProfileQuery, contentResolver);
+ mEmailAddresses = new String[emailCursor.getCount()];
+ for (int i = 0; emailCursor.moveToNext(); i++) {
+ mEmailAddresses[i] = emailCursor.getString(emailProfileQuery.EMAIL_ADDRESS);
+ }
+ emailCursor.close();
+ }
+
+ private void populateAddress(ContentResolver contentResolver) {
+ AddressProfileQuery addressProfileQuery = new AddressProfileQuery();
+ Cursor addressCursor = cursorFromProfileQuery(addressProfileQuery, contentResolver);
+ if(addressCursor.moveToNext()) {
+ mPobox = addressCursor.getString(addressProfileQuery.POBOX);
+ mStreet = addressCursor.getString(addressProfileQuery.STREET);
+ mNeighborhood = addressCursor.getString(addressProfileQuery.NEIGHBORHOOD);
+ mCity = addressCursor.getString(addressProfileQuery.CITY);
+ mRegion = addressCursor.getString(addressProfileQuery.REGION);
+ mPostalCode = addressCursor.getString(addressProfileQuery.POSTALCODE);
+ mCountry = addressCursor.getString(addressProfileQuery.COUNTRY);
+ }
+ addressCursor.close();
+ }
+
+ private void populatePhone(ContentResolver contentResolver) {
+ PhoneProfileQuery phoneProfileQuery = new PhoneProfileQuery();
+ Cursor phoneCursor = cursorFromProfileQuery(phoneProfileQuery, contentResolver);
+ mPhoneNumbers = new String[phoneCursor.getCount()];
+ for (int i = 0; phoneCursor.moveToNext(); i++) {
+ mPhoneNumbers[i] = phoneCursor.getString(phoneProfileQuery.NUMBER);
+ }
+ phoneCursor.close();
+ }
+
+ /**
+ * Static factory method for instance creation.
+ * @param context valid Android context.
+ * @return PersonalAutofillPopulator new instance of PersonalAutofillPopulator.
+ */
+ @CalledByNative
+ static PersonalAutofillPopulator create(Context context) {
+ return new PersonalAutofillPopulator(context);
+ }
+
+ @CalledByNative
+ private String getFirstName() {
+ return mGivenName;
+ }
+
+ @CalledByNative
+ private String getLastName() {
+ return mFamilyName;
+ }
+
+ @CalledByNative
+ private String getMiddleName() {
+ return mMiddleName;
+ }
+
+ @CalledByNative
+ private String getSuffix() {
+ return mSuffix;
+ }
+
+ @CalledByNative
+ private String[] getEmailAddresses() {
+ return mEmailAddresses;
+ }
+
+ @CalledByNative
+ private String getStreet() {
+ return mStreet;
+ }
+
+ @CalledByNative
+ private String getPobox() {
+ return mPobox;
+ }
+
+ @CalledByNative
+ private String getNeighborhood() {
+ return mNeighborhood;
+ }
+
+ @CalledByNative
+ private String getCity() {
+ return mCity;
+ }
+
+ @CalledByNative
+ private String getRegion() {
+ return mRegion;
+ }
+
+ @CalledByNative
+ private String getPostalCode() {
+ return mPostalCode;
+ }
+
+ @CalledByNative
+ private String getCountry() {
+ return mCountry;
+ }
+
+ @CalledByNative
+ private String[] getPhoneNumbers() {
+ return mPhoneNumbers;
+ }
+
+ @CalledByNative
+ private boolean getHasPermissions() {
+ return mHasPermissions;
+ }
+}
diff --git a/chromium/components/autofill/core/browser/android/personal_data_manager_android.cc b/chromium/components/autofill/core/browser/android/personal_data_manager_android.cc
new file mode 100644
index 00000000000..9a6e1c647a3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/personal_data_manager_android.cc
@@ -0,0 +1,25 @@
+// 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.
+
+// Populates default autofill profile from user's own Android contact.
+
+#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h"
+#include "components/autofill/core/browser/android/auxiliary_profiles_android.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+
+namespace autofill {
+
+void PersonalDataManager::LoadAuxiliaryProfiles() {
+ auxiliary_profiles_.clear();
+ autofill::AuxiliaryProfileLoaderAndroid profile_loader;
+ profile_loader.Init(
+ base::android::AttachCurrentThread(),
+ base::android::GetApplicationContext());
+ if (profile_loader.GetHasPermissions()) {
+ autofill::AuxiliaryProfilesAndroid impl(profile_loader, app_locale_);
+ auxiliary_profiles_.push_back(impl.LoadContactsProfile().release());
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc b/chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc
new file mode 100644
index 00000000000..ced72c8a603
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc
@@ -0,0 +1,132 @@
+// 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/core/browser/android/test_auxiliary_profile_loader_android.h"
+
+namespace autofill {
+
+TestAuxiliaryProfileLoader::TestAuxiliaryProfileLoader() {
+}
+
+TestAuxiliaryProfileLoader::~TestAuxiliaryProfileLoader() {
+}
+
+bool TestAuxiliaryProfileLoader::GetHasPermissions() const {
+ return true;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetFirstName() const {
+ return first_name_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetMiddleName() const {
+ return middle_name_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetLastName() const {
+ return last_name_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetSuffix() const {
+ return suffix_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetStreet() const {
+ return street_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetPostOfficeBox() const {
+ return post_office_box_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetCity() const {
+ return city_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetNeighborhood() const {
+ return neighborhood_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetRegion() const {
+ return region_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetPostalCode() const {
+ return postal_code_;
+}
+
+base::string16 TestAuxiliaryProfileLoader::GetCountry() const {
+ return country_;
+}
+
+void TestAuxiliaryProfileLoader::GetEmailAddresses(
+ std::vector<base::string16>* email_addresses) const {
+ *email_addresses = email_addresses_;
+}
+
+void TestAuxiliaryProfileLoader::GetPhoneNumbers(
+ std::vector<base::string16>* phone_numbers) const {
+ *phone_numbers = phone_numbers_;
+}
+
+void TestAuxiliaryProfileLoader::SetFirstName(
+ const base::string16& first_name) {
+ first_name_ = first_name;
+}
+
+void TestAuxiliaryProfileLoader::SetMiddleName(
+ const base::string16& middle_name) {
+ middle_name_ = middle_name;
+}
+
+void TestAuxiliaryProfileLoader::SetLastName(const base::string16& last_name) {
+ last_name_ = last_name;
+}
+
+void TestAuxiliaryProfileLoader::SetSuffix(const base::string16& suffix) {
+ suffix_ = suffix;
+}
+
+void TestAuxiliaryProfileLoader::SetStreet(const base::string16& street) {
+ street_ = street;
+}
+
+void TestAuxiliaryProfileLoader::SetPostOfficeBox(
+ const base::string16& post_office_box) {
+ post_office_box_ = post_office_box;
+}
+
+void TestAuxiliaryProfileLoader::SetNeighborhood(
+ const base::string16& neighborhood) {
+ neighborhood_ = neighborhood;
+}
+
+void TestAuxiliaryProfileLoader::SetRegion(const base::string16& region) {
+ region_ = region;
+}
+
+void TestAuxiliaryProfileLoader::SetCity(const base::string16& city) {
+ city_ = city;
+}
+
+void TestAuxiliaryProfileLoader::SetPostalCode(
+ const base::string16& postal_code) {
+ postal_code_ = postal_code;
+}
+
+void TestAuxiliaryProfileLoader::SetCountry(const base::string16& country) {
+ country_ = country;
+}
+
+void TestAuxiliaryProfileLoader::SetEmailAddresses(
+ const std::vector<base::string16>& addresses) {
+ email_addresses_ = addresses;
+}
+
+void TestAuxiliaryProfileLoader::SetPhoneNumbers(
+ const std::vector<base::string16>& phone_numbers) {
+ phone_numbers_ = phone_numbers;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h b/chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h
new file mode 100644
index 00000000000..34d424c9adf
--- /dev/null
+++ b/chromium/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h
@@ -0,0 +1,74 @@
+// 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_CORE_BROWSER_ANDROID_TEST_AUXILIARY_PROFILE_LOADER_ANDROID_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_TEST_AUXILIARY_PROFILE_LOADER_ANDROID_H_
+
+#include "base/compiler_specific.h"
+#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h"
+
+namespace autofill {
+
+class TestAuxiliaryProfileLoader
+ : public autofill::AuxiliaryProfileLoaderAndroid {
+ // Mock object for unit testing |AuxiliaryProfilesAndroid|
+ public:
+ TestAuxiliaryProfileLoader();
+ virtual ~TestAuxiliaryProfileLoader();
+
+ virtual bool GetHasPermissions() const OVERRIDE;
+
+ virtual base::string16 GetFirstName() const OVERRIDE;
+ virtual base::string16 GetMiddleName() const OVERRIDE;
+ virtual base::string16 GetLastName() const OVERRIDE;
+ virtual base::string16 GetSuffix() const OVERRIDE;
+
+ virtual base::string16 GetStreet() const OVERRIDE;
+ virtual base::string16 GetCity() const OVERRIDE;
+ virtual base::string16 GetNeighborhood() const OVERRIDE;
+ virtual base::string16 GetPostalCode() const OVERRIDE;
+ virtual base::string16 GetRegion() const OVERRIDE;
+ virtual base::string16 GetPostOfficeBox() const OVERRIDE;
+ virtual base::string16 GetCountry() const OVERRIDE;
+
+ virtual void GetEmailAddresses(
+ std::vector<base::string16>* email_addresses) const OVERRIDE;
+ virtual void GetPhoneNumbers(
+ std::vector<base::string16>* phone_numbers) const OVERRIDE;
+
+ void SetFirstName(const base::string16& first_name);
+ void SetMiddleName(const base::string16& middle_name);
+ void SetLastName(const base::string16& last_name);
+ void SetSuffix(const base::string16& suffix);
+
+ void SetStreet(const base::string16& street);
+ void SetPostOfficeBox(const base::string16& post_office_box);
+ void SetNeighborhood(const base::string16& neighborhood);
+ void SetRegion(const base::string16& region);
+ void SetCity(const base::string16& city);
+ void SetPostalCode(const base::string16& postal_code);
+ void SetCountry(const base::string16& country);
+
+ void SetEmailAddresses(const std::vector<base::string16>& email_addresses);
+ void SetPhoneNumbers(const std::vector<base::string16>& phone_numbers);
+
+ private:
+ base::string16 street_;
+ base::string16 post_office_box_;
+ base::string16 neighborhood_;
+ base::string16 region_;
+ base::string16 city_;
+ base::string16 postal_code_;
+ base::string16 country_;
+ base::string16 first_name_;
+ base::string16 middle_name_;
+ base::string16 last_name_;
+ base::string16 suffix_;
+ std::vector<base::string16> email_addresses_;
+ std::vector<base::string16> phone_numbers_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_TEST_AUXILIARY_PROFILE_LOADER_ANDROID_H_
diff --git a/chromium/components/autofill/core/browser/autocheckout_bubble_state.h b/chromium/components/autofill/core/browser/autocheckout_bubble_state.h
new file mode 100644
index 00000000000..b015cb01d30
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autocheckout_bubble_state.h
@@ -0,0 +1,22 @@
+// 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_CORE_BROWSER_AUTOCHECKOUT_BUBBLE_STATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCHECKOUT_BUBBLE_STATE_H_
+
+namespace autofill {
+
+// Specifies the state of Autocheckout bubble.
+enum AutocheckoutBubbleState {
+ // User clicked on continue.
+ AUTOCHECKOUT_BUBBLE_ACCEPTED = 0,
+ // User clicked on "No Thanks".
+ AUTOCHECKOUT_BUBBLE_CANCELED = 1,
+ // Bubble is ignored.
+ AUTOCHECKOUT_BUBBLE_IGNORED = 2,
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCHECKOUT_BUBBLE_STATE_H_
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
new file mode 100644
index 00000000000..171c24596ed
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.cc
@@ -0,0 +1,205 @@
+// 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/core/browser/autocomplete_history_manager.h"
+
+#include <vector>
+
+#include "base/prefs/pref_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/autofill/core/common/form_data.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+
+using content::BrowserContext;
+using content::WebContents;
+
+namespace autofill {
+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;
+
+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";
+}
+
+} // namespace
+
+AutocompleteHistoryManager::AutocompleteHistoryManager(
+ AutofillDriver* driver,
+ AutofillManagerDelegate* manager_delegate)
+ : browser_context_(driver->GetWebContents()->GetBrowserContext()),
+ driver_(driver),
+ autofill_data_(
+ AutofillWebDataService::FromBrowserContext(browser_context_)),
+ pending_query_handle_(0),
+ query_id_(0),
+ external_delegate_(NULL),
+ manager_delegate_(manager_delegate),
+ send_ipc_(true) {
+ DCHECK(manager_delegate_);
+}
+
+AutocompleteHistoryManager::~AutocompleteHistoryManager() {
+ CancelPendingQuery();
+}
+
+void AutocompleteHistoryManager::OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle h,
+ const WDTypedResult* result) {
+ DCHECK(pending_query_handle_);
+ pending_query_handle_ = 0;
+
+ if (!manager_delegate_->IsAutocompleteEnabled()) {
+ 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<base::string16> >* autofill_result =
+ static_cast<const WDResult<std::vector<base::string16> >*>(result);
+ std::vector<base::string16> suggestions = autofill_result->GetValue();
+ SendSuggestions(&suggestions);
+}
+
+void AutocompleteHistoryManager::OnGetAutocompleteSuggestions(
+ int query_id,
+ const base::string16& name,
+ const base::string16& prefix,
+ const std::vector<base::string16>& autofill_values,
+ const std::vector<base::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 (!manager_delegate_->IsAutocompleteEnabled()) {
+ SendSuggestions(NULL);
+ return;
+ }
+
+ if (autofill_data_.get()) {
+ pending_query_handle_ = autofill_data_->GetFormValuesForElementName(
+ name, prefix, kMaxAutocompleteMenuItems, this);
+ }
+}
+
+void AutocompleteHistoryManager::OnFormSubmitted(const FormData& form) {
+ if (!manager_delegate_->IsAutocompleteEnabled())
+ 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) &&
+ !autofill::IsSSN(iter->value)) {
+ values.push_back(*iter);
+ }
+ }
+
+ if (!values.empty() && autofill_data_.get())
+ autofill_data_->AddFormFields(values);
+}
+
+void AutocompleteHistoryManager::OnRemoveAutocompleteEntry(
+ const base::string16& name, const base::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_.get())
+ autofill_data_->CancelRequest(pending_query_handle_);
+ pending_query_handle_ = 0;
+ }
+}
+
+void AutocompleteHistoryManager::SendSuggestions(
+ const std::vector<base::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(base::string16());
+ autofill_icons_.push_back(base::string16());
+ autofill_unique_ids_.push_back(0); // 0 means no profile.
+ }
+ }
+ }
+
+ external_delegate_->OnSuggestionsReturned(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();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager.h b/chromium/components/autofill/core/browser/autocomplete_history_manager.h
new file mode 100644
index 00000000000..15c2ac625a5
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager.h
@@ -0,0 +1,103 @@
+// 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_CORE_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
+
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/prefs/pref_member.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+
+namespace content {
+class BrowserContext;
+class WebContents;
+}
+
+namespace autofill {
+
+class AutofillDriver;
+class AutofillExternalDelegate;
+class AutofillManagerDelegate;
+struct FormData;
+
+// 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 WebDataServiceConsumer {
+ public:
+ AutocompleteHistoryManager(AutofillDriver* driver,
+ AutofillManagerDelegate* delegate);
+ virtual ~AutocompleteHistoryManager();
+
+ // 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 base::string16& name,
+ const base::string16& prefix,
+ const std::vector<base::string16>& autofill_values,
+ const std::vector<base::string16>& autofill_labels,
+ const std::vector<base::string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids);
+ virtual void OnFormSubmitted(const FormData& form);
+
+ // Must be public for the external delegate to use.
+ void OnRemoveAutocompleteEntry(const base::string16& name,
+ const base::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<base::string16>* suggestions);
+
+ // Used by tests to disable sending IPC.
+ void set_send_ipc(bool send_ipc) { send_ipc_ = send_ipc; }
+
+ private:
+ // Cancels the currently pending WebDataService query, if there is one.
+ void CancelPendingQuery();
+
+ content::BrowserContext* browser_context_;
+ // Provides driver-level context. Must outlive this object.
+ AutofillDriver* driver_;
+ scoped_refptr<AutofillWebDataService> autofill_data_;
+
+ // 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<base::string16> autofill_values_;
+ std::vector<base::string16> autofill_labels_;
+ std::vector<base::string16> autofill_icons_;
+ std::vector<int> autofill_unique_ids_;
+
+ // Delegate to perform external processing (display, selection) on
+ // our behalf. Weak.
+ AutofillExternalDelegate* external_delegate_;
+
+ // Delegate to provide whether or not autocomplete functionality is enabled.
+ AutofillManagerDelegate* const manager_delegate_;
+
+ // Whether IPC is sent.
+ bool send_ipc_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutocompleteHistoryManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
new file mode 100644
index 00000000000..f2cf35788ab
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
@@ -0,0 +1,262 @@
+// 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 <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/prefs/testing_pref_service.h"
+#include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.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_profile.h"
+#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/webdata/common/web_data_service_test_util.h"
+#include "content/public/test/test_browser_thread.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+
+using content::WebContents;
+using testing::_;
+
+namespace autofill {
+
+namespace {
+
+class MockWebDataService : public AutofillWebDataService {
+ public:
+ MockWebDataService()
+ : AutofillWebDataService() {
+ current_mock_web_data_service_ = this;
+ }
+
+ MOCK_METHOD1(AddFormFields, void(const std::vector<FormFieldData>&));
+
+ static scoped_refptr<MockWebDataService> GetCurrent() {
+ if (!current_mock_web_data_service_) {
+ return new MockWebDataService();
+ }
+ return current_mock_web_data_service_;
+ }
+
+ 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 MockWebDataServiceWrapperCurrent : public MockWebDataServiceWrapperBase {
+ public:
+ static BrowserContextKeyedService* Build(content::BrowserContext* profile) {
+ return new MockWebDataServiceWrapperCurrent();
+ }
+
+ MockWebDataServiceWrapperCurrent() {}
+
+ virtual scoped_refptr<AutofillWebDataService> GetAutofillWebData() OVERRIDE {
+ return MockWebDataService::GetCurrent();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapperCurrent);
+};
+
+class MockAutofillManagerDelegate
+ : public autofill::TestAutofillManagerDelegate {
+ public:
+ MockAutofillManagerDelegate() {}
+ virtual ~MockAutofillManagerDelegate() {}
+ virtual PrefService* GetPrefs() OVERRIDE { return &prefs_; }
+
+ private:
+ TestingPrefServiceSimple prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
+};
+
+} // namespace
+
+class AutocompleteHistoryManagerTest : public ChromeRenderViewHostTestHarness {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+ web_data_service_ = new MockWebDataService();
+ WebDataServiceFactory::GetInstance()->SetTestingFactory(
+ profile(), MockWebDataServiceWrapperCurrent::Build);
+ autofill_driver_.reset(new TestAutofillDriver(web_contents()));
+ autocomplete_manager_.reset(
+ new AutocompleteHistoryManager(autofill_driver_.get(),
+ &manager_delegate));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ autocomplete_manager_.reset();
+ web_data_service_ = NULL;
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ scoped_refptr<MockWebDataService> web_data_service_;
+ scoped_ptr<AutocompleteHistoryManager> autocomplete_manager_;
+ scoped_ptr<AutofillDriver> autofill_driver_;
+ 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_.get(), 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_.get(), 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:
+ MockAutofillExternalDelegate(content::WebContents* web_contents,
+ AutofillManager* autofill_manager,
+ AutofillDriver* autofill_driver)
+ : AutofillExternalDelegate(web_contents, autofill_manager,
+ autofill_driver) {}
+ virtual ~MockAutofillExternalDelegate() {}
+
+ MOCK_METHOD5(OnSuggestionsReturned,
+ void(int query_id,
+ const std::vector<base::string16>& autofill_values,
+ const std::vector<base::string16>& autofill_labels,
+ const std::vector<base::string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillExternalDelegate);
+};
+
+class AutocompleteHistoryManagerNoIPC : public AutocompleteHistoryManager {
+ public:
+ AutocompleteHistoryManagerNoIPC(AutofillDriver* driver,
+ AutofillManagerDelegate* delegate)
+ : AutocompleteHistoryManager(driver, delegate) {
+ // Ensure that IPC is not sent during the test.
+ set_send_ipc(false);
+ }
+
+ using AutocompleteHistoryManager::SendSuggestions;
+};
+
+} // namespace
+
+// Make sure our external delegate is called at the right time.
+TEST_F(AutocompleteHistoryManagerTest, ExternalDelegate) {
+ AutocompleteHistoryManagerNoIPC autocomplete_history_manager(
+ autofill_driver_.get(), &manager_delegate);
+
+ scoped_ptr<AutofillManager> autofill_manager(new AutofillManager(
+ autofill_driver_.get(),
+ &manager_delegate,
+ "en-US",
+ AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER));
+
+ MockAutofillExternalDelegate external_delegate(web_contents(),
+ autofill_manager.get(),
+ autofill_driver_.get());
+ 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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill-inl.h b/chromium/components/autofill/core/browser/autofill-inl.h
new file mode 100644
index 00000000000..33e6a880d08
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill-inl.h
@@ -0,0 +1,39 @@
+// 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_CORE_BROWSER_AUTOFILL_INL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_INL_H_
+
+namespace autofill {
+
+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();
+}
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_INL_H_
diff --git a/chromium/components/autofill/core/browser/autofill_common_test.cc b/chromium/components/autofill/core/browser/autofill_common_test.cc
new file mode 100644
index 00000000000..f477b25c2e6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_common_test.cc
@@ -0,0 +1,194 @@
+// 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/core/browser/autofill_common_test.h"
+
+#include "base/guid.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/user_prefs/user_prefs.h"
+#include "components/webdata/encryptor/encryptor.h"
+#include "content/public/browser/browser_context.h"
+
+namespace autofill {
+namespace test {
+
+namespace {
+
+const char kSettingsOrigin[] = "Chrome settings";
+
+} // namespace
+
+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;
+}
+
+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;
+ test::CreateTestFormField("First Name", "firstname", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Middle Name", "middlename", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Address Line 1", "addr1", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Address Line 2", "addr2", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("City", "city", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("State", "state", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Postal Code", "zipcode", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Country", "country", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Phone Number", "phonenumber", "", "tel", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Email", "email", "", "email", &field);
+ form->fields.push_back(field);
+}
+
+inline void check_and_set(
+ FormGroup* profile, ServerFieldType type, const char* value) {
+ if (value)
+ profile->SetRawInfo(type, UTF8ToUTF16(value));
+}
+
+AutofillProfile GetFullProfile() {
+ AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
+ SetProfileInfo(&profile,
+ "John",
+ "H.",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "Apt 8",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ return profile;
+}
+
+AutofillProfile GetFullProfile2() {
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
+ SetProfileInfo(&profile,
+ "Jane",
+ "A.",
+ "Smith",
+ "jsmith@example.com",
+ "ACME",
+ "123 Main Street",
+ "Unit 1",
+ "Greensdale", "MI",
+ "48838",
+ "US",
+ "13105557889");
+ return profile;
+}
+
+AutofillProfile GetVerifiedProfile() {
+ AutofillProfile profile(GetFullProfile());
+ profile.set_origin(kSettingsOrigin);
+ return profile;
+}
+
+AutofillProfile GetVerifiedProfile2() {
+ AutofillProfile profile(GetFullProfile2());
+ profile.set_origin(kSettingsOrigin);
+ return profile;
+}
+
+CreditCard GetCreditCard() {
+ CreditCard credit_card(base::GenerateGUID(), "http://www.example.com");
+ SetCreditCardInfo(
+ &credit_card, "Test User", "4111111111111111" /* Visa */, "11", "2017");
+ return credit_card;
+}
+
+CreditCard GetVerifiedCreditCard() {
+ CreditCard credit_card(GetCreditCard());
+ credit_card.set_origin(kSettingsOrigin);
+ return credit_card;
+}
+
+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(content::BrowserContext* browser_context) {
+ // 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 (browser_context) {
+ user_prefs::UserPrefs::Get(browser_context)->SetBoolean(
+ prefs::kAutofillAuxiliaryProfilesEnabled, false);
+ }
+}
+
+} // namespace test
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_common_test.h b/chromium/components/autofill/core/browser/autofill_common_test.h
new file mode 100644
index 00000000000..b0818f70df7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_common_test.h
@@ -0,0 +1,84 @@
+// 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_CORE_BROWSER_AUTOFILL_COMMON_TEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_COMMON_TEST_H_
+
+namespace content {
+class BrowserContext;
+}
+
+namespace autofill {
+
+class AutofillProfile;
+class CreditCard;
+struct FormData;
+struct FormFieldData;
+
+// Common utilities shared amongst Autofill tests.
+namespace 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);
+
+// 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);
+
+// Returns a profile full of dummy info.
+AutofillProfile GetFullProfile();
+
+// Returns a profile full of dummy info, different to the above.
+AutofillProfile GetFullProfile2();
+
+// Returns a verified profile full of dummy info.
+AutofillProfile GetVerifiedProfile();
+
+// Returns a verified profile full of dummy info, different to the above.
+AutofillProfile GetVerifiedProfile2();
+
+// Returns a credit card full of dummy info.
+CreditCard GetCreditCard();
+
+// Returns a verified credit card full of dummy info.
+CreditCard GetVerifiedCreditCard();
+
+// 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(content::BrowserContext* browser_context);
+
+} // namespace test
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_COMMON_TEST_H_
diff --git a/chromium/components/autofill/core/browser/autofill_country.cc b/chromium/components/autofill/core/browser/autofill_country.cc
new file mode 100644
index 00000000000..45c89c7b711
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_country.cc
@@ -0,0 +1,1111 @@
+// 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/core/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/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "grit/component_strings.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#include "third_party/icu/source/common/unicode/unistr.h"
+#include "third_party/icu/source/common/unicode/urename.h"
+#include "third_party/icu/source/common/unicode/utypes.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
+#include "third_party/icu/source/i18n/unicode/ucol.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+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;
+ AddressRequiredFields address_required_fields;
+};
+
+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,
+ ADDRESS_REQUIRES_STATE } },
+ { "AE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_EMIRATE,
+ ADDRESS_REQUIRES_STATE } },
+ { "AF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_ADDRESS_LINE_1_ONLY } },
+ { "AI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AS", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "AT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "AU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "AW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "AX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "AZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PARISH,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "BF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "BM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "BS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "BZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "CC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "CI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "CO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "CZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "DE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "DJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "DK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "DM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "DO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "DZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "EC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "EE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "EG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "EH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "ER", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "ES", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "ET", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "FI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "FJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "FK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "FM", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "FO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "FR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_COUNTY,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_ADDRESS_LINE_1_ONLY } },
+ { "GL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "GT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GU", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "GW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "GY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "HK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_AREA,
+ ADDRESS_REQUIRES_STATE } },
+ { "HM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "HN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "HR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "HT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "HU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "ID", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "IE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_COUNTY,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "IL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "IM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "IN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "IO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "IQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "IS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "IT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "JE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "JM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PARISH,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "JO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "JP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PREFECTURE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "KE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "KP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "KW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "KY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIRES_STATE } },
+ { "KZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "LK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "LV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "LY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "ME", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "MG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MH", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "MK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "ML", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_ADDRESS_LINE_1_ONLY } },
+ { "MP", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "MQ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "MR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "MX", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "MY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "MZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "NE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_DEPARTMENT,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "NO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "NP", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_DISTRICT,
+ ADDRESS_REQUIRES_STATE } },
+ { "NU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "NZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "OM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "PA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "PE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "PF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "PG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "PH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY } },
+ { "PK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "PL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "PM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "PN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "PR", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "PS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "PT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "PW", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "PY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "QA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "RE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "RO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "RS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "RU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "RW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SB", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "SG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_ZIP } },
+ { "SH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "SI", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "SK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_ZIP } },
+ { "SN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "SR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "ST", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "SV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "SZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "TD", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TH", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TJ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TK", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TL", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TO", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TR", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "TT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TV", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_ISLAND,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "TW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_COUNTY,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "TZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "UA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "UG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "UM", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "US", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "UY", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "UZ", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "VA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "VC", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "VE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_STATE } },
+ { "VG", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_ADDRESS_LINE_1_ONLY } },
+ { "VI", { IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_STATE,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP } },
+ { "VN", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY } },
+ { "VU", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "WF", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "WS", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+ { "YE", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY } },
+ { "YT", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "ZA", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY_ZIP } },
+ { "ZM", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIRES_CITY } },
+ { "ZW", { IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE,
+ IDS_AUTOFILL_FIELD_LABEL_PROVINCE,
+ ADDRESS_REQUIREMENTS_UNKNOWN } },
+};
+
+// 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 country code corresponding to |country|, which should be a
+ // country code or country name localized to |locale|.
+ const std::string GetCountryCode(const base::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 base::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 base::string16& str,
+ scoped_ptr<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_;
+
+ DISALLOW_COPY_AND_ASSIGN(CountryNames);
+};
+
+// static
+CountryNames* CountryNames::GetInstance() {
+ return Singleton<CountryNames>::get();
+}
+
+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 base::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_ptr<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;
+ base::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 base::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_ptr<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 base::string16& str,
+ scoped_ptr<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);
+ address_required_fields_ = data.address_required_fields;
+}
+
+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 base::string16& country,
+ const std::string& locale) {
+ return CountryNames::GetInstance()->GetCountryCode(country, locale);
+}
+
+AutofillCountry::AutofillCountry(const std::string& country_code,
+ const base::string16& name,
+ const base::string16& postal_code_label,
+ const base::string16& state_label)
+ : country_code_(country_code),
+ name_(name),
+ postal_code_label_(postal_code_label),
+ state_label_(state_label) {
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_country.h b/chromium/components/autofill/core/browser/autofill_country.h
new file mode 100644
index 00000000000..17bb74c49f6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_country.h
@@ -0,0 +1,110 @@
+// 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_CORE_BROWSER_AUTOFILL_COUNTRY_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_COUNTRY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+// The minimal required fields for an address to be complete for a given
+// country.
+enum AddressRequiredFields {
+ ADDRESS_REQUIRES_CITY = 1 << 0,
+ ADDRESS_REQUIRES_STATE = 1 << 1,
+ ADDRESS_REQUIRES_ZIP = 1 << 2,
+
+ // Composite versions (for data).
+ ADDRESS_REQUIRES_CITY_STATE =
+ ADDRESS_REQUIRES_CITY | ADDRESS_REQUIRES_STATE,
+ ADDRESS_REQUIRES_STATE_ZIP =
+ ADDRESS_REQUIRES_STATE | ADDRESS_REQUIRES_ZIP,
+ ADDRESS_REQUIRES_CITY_ZIP =
+ ADDRESS_REQUIRES_CITY |ADDRESS_REQUIRES_ZIP,
+ ADDRESS_REQUIRES_CITY_STATE_ZIP =
+ ADDRESS_REQUIRES_CITY | ADDRESS_REQUIRES_STATE | ADDRESS_REQUIRES_ZIP,
+
+ // Policy for countries that don't have city, state or zip requirements.
+ ADDRESS_REQUIRES_ADDRESS_LINE_1_ONLY = 0,
+
+ // Policy for countries for which we do not have information about valid
+ // address format.
+ ADDRESS_REQUIREMENTS_UNKNOWN = ADDRESS_REQUIRES_CITY_STATE_ZIP,
+};
+
+// 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 base::string16& country,
+ const std::string& locale);
+
+ const std::string country_code() const { return country_code_; }
+ const base::string16 name() const { return name_; }
+ const base::string16 postal_code_label() const { return postal_code_label_; }
+ const base::string16 state_label() const { return state_label_; }
+
+ // City is expected in a complete address for this country.
+ bool requires_city() const {
+ return (address_required_fields_ & ADDRESS_REQUIRES_CITY) != 0;
+ }
+
+ // State is expected in a complete address for this country.
+ bool requires_state() const {
+ return (address_required_fields_ & ADDRESS_REQUIRES_STATE) != 0;
+ }
+
+ // Zip is expected in a complete address for this country.
+ bool requires_zip() const {
+ return (address_required_fields_ & ADDRESS_REQUIRES_ZIP) != 0;
+ }
+
+ private:
+ AutofillCountry(const std::string& country_code,
+ const base::string16& name,
+ const base::string16& postal_code_label,
+ const base::string16& state_label);
+
+ // The two-letter ISO-3166 country code.
+ std::string country_code_;
+
+ // The country's name, localized to the app locale.
+ base::string16 name_;
+
+ // The localized label for the postal code (or zip code) field.
+ base::string16 postal_code_label_;
+
+ // The localized label for the state (or province, district, etc.) field.
+ base::string16 state_label_;
+
+ // Address requirement field codes for the country.
+ AddressRequiredFields address_required_fields_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillCountry);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_COUNTRY_H_
diff --git a/chromium/components/autofill/core/browser/autofill_country_unittest.cc b/chromium/components/autofill/core/browser/autofill_country_unittest.cc
new file mode 100644
index 00000000000..dc43a52a1aa
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_country_unittest.cc
@@ -0,0 +1,90 @@
+// 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 <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+// 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"));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_data_model.cc b/chromium/components/autofill/core/browser/autofill_data_model.cc
new file mode 100644
index 00000000000..2d1edcf221d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_data_model.cc
@@ -0,0 +1,189 @@
+// 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/core/browser/autofill_data_model.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/state_names.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "grit/component_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+namespace autofill {
+namespace {
+
+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 base::string16& value,
+ FormFieldData* field) {
+ base::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 base::string16& value,
+ FormFieldData* field) {
+ base::string16 full, abbreviation;
+ state_names::GetNameAndAbbreviation(value, &full, &abbreviation);
+
+ // Try the abbreviation first.
+ if (!abbreviation.empty() && SetSelectControlValue(abbreviation, field))
+ return true;
+
+ return !full.empty() && SetSelectControlValue(full, field);
+}
+
+bool FillExpirationMonthSelectControl(const base::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 base::string16& value,
+ FormFieldData* field) {
+ // Try stripping off spaces.
+ base::string16 value_stripped;
+ RemoveChars(StringToLowerASCII(value), kWhitespaceUTF16, &value_stripped);
+
+ for (size_t i = 0; i < field->option_values.size(); ++i) {
+ base::string16 option_value_lowercase;
+ RemoveChars(StringToLowerASCII(field->option_values[i]), kWhitespaceUTF16,
+ &option_value_lowercase);
+ base::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
+
+AutofillDataModel::AutofillDataModel(const std::string& guid,
+ const std::string& origin)
+ : guid_(guid),
+ origin_(origin) {}
+AutofillDataModel::~AutofillDataModel() {}
+
+void AutofillDataModel::FillSelectControl(const AutofillType& type,
+ const std::string& app_locale,
+ FormFieldData* field) const {
+ DCHECK(field);
+ DCHECK_EQ("select-one", field->form_control_type);
+ DCHECK_EQ(field->option_values.size(), field->option_contents.size());
+
+ base::string16 field_text = GetInfo(type, app_locale);
+ base::string16 field_text_lower = StringToLowerASCII(field_text);
+ if (field_text.empty())
+ return;
+
+ base::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;
+ }
+
+ ServerFieldType storable_type = type.GetStorableType();
+ if (storable_type == ADDRESS_HOME_STATE) {
+ FillStateSelectControl(field_text, field);
+ } else if (storable_type == ADDRESS_HOME_COUNTRY) {
+ FillCountrySelectControl(app_locale, field);
+ } else if (storable_type == CREDIT_CARD_EXP_MONTH) {
+ FillExpirationMonthSelectControl(field_text, field);
+ } else if (storable_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(AutofillType(CREDIT_CARD_EXP_2_DIGIT_YEAR), app_locale,
+ field);
+ } else if (storable_type == CREDIT_CARD_TYPE) {
+ FillCreditCardTypeSelectControl(field_text, field);
+ }
+}
+
+bool AutofillDataModel::FillCountrySelectControl(
+ const std::string& app_locale,
+ FormFieldData* field_data) const {
+ return false;
+}
+
+bool AutofillDataModel::IsVerified() const {
+ return !origin_.empty() && !GURL(origin_).is_valid();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_data_model.h b/chromium/components/autofill/core/browser/autofill_data_model.h
new file mode 100644
index 00000000000..0f2cd33f0ff
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_data_model.h
@@ -0,0 +1,70 @@
+// 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_CORE_BROWSER_AUTOFILL_DATA_MODEL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DATA_MODEL_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_group.h"
+
+namespace autofill {
+
+class AutofillField;
+struct FormFieldData;
+
+// This class is an interface for the primary data models that back Autofill.
+// The information in objects of this class is managed by the
+// PersonalDataManager.
+class AutofillDataModel : public FormGroup {
+ public:
+ AutofillDataModel(const std::string& guid, const std::string& origin);
+ virtual ~AutofillDataModel();
+
+ // Set |field_data|'s value based on |field| and contents of |this| (using
+ // data variant |variant|).
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ const std::string& app_locale,
+ FormFieldData* field_data) const = 0;
+
+ // Fills in select control with data matching |type| from |this|.
+ // Public for testing purposes.
+ void FillSelectControl(const AutofillType& type,
+ const std::string& app_locale,
+ FormFieldData* field_data) const;
+
+ // Returns true if the data in this model was entered directly by the user,
+ // rather than automatically aggregated.
+ bool IsVerified() const;
+
+ std::string guid() const { return guid_; }
+ void set_guid(const std::string& guid) { guid_ = guid; }
+
+ std::string origin() const { return origin_; }
+ void set_origin(const std::string& origin) { origin_ = origin; }
+
+ protected:
+ // Fills in a select control for a country from data in |this|. Returns true
+ // for success.
+ virtual bool FillCountrySelectControl(const std::string& app_locale,
+ FormFieldData* field_data) const;
+
+ private:
+ // A globally unique ID for this object.
+ std::string guid_;
+
+ // The origin of this data. This should be
+ // (a) a web URL for the domain of the form from which the data was
+ // automatically aggregated, e.g. https://www.example.com/register,
+ // (b) some other non-empty string, which cannot be interpreted as a web
+ // URL, identifying the origin for non-aggregated data, or
+ // (c) an empty string, indicating that the origin for this data is unknown.
+ std::string origin_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DATA_MODEL_H_
diff --git a/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc b/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc
new file mode 100644
index 00000000000..8004df46425
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_data_model_unittest.cc
@@ -0,0 +1,68 @@
+// 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/core/browser/autofill_data_model.h"
+
+#include "base/compiler_specific.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+namespace {
+
+// Provides concrete implementations for pure virtual methods.
+class TestAutofillDataModel : public AutofillDataModel {
+ public:
+ TestAutofillDataModel(const std::string& guid, const std::string& origin)
+ : AutofillDataModel(guid, origin) {}
+ virtual ~TestAutofillDataModel() {}
+
+ private:
+ virtual base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE {
+ return base::string16();
+ }
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE {}
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const OVERRIDE {}
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ const std::string& app_locale,
+ FormFieldData* field_data) const OVERRIDE {}
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillDataModel);
+};
+
+} // namespace
+
+TEST(AutofillDataModelTest, IsVerified) {
+ TestAutofillDataModel model("guid", std::string());
+ EXPECT_FALSE(model.IsVerified());
+
+ model.set_origin("http://www.example.com");
+ EXPECT_FALSE(model.IsVerified());
+
+ model.set_origin("https://www.example.com");
+ EXPECT_FALSE(model.IsVerified());
+
+ model.set_origin("file:///tmp/example.txt");
+ EXPECT_FALSE(model.IsVerified());
+
+ model.set_origin("data:text/plain;charset=utf-8;base64,ZXhhbXBsZQ==");
+ EXPECT_FALSE(model.IsVerified());
+
+ model.set_origin("chrome://settings/autofill");
+ EXPECT_FALSE(model.IsVerified());
+
+ model.set_origin("Chrome Settings");
+ EXPECT_TRUE(model.IsVerified());
+
+ model.set_origin("Some gibberish string");
+ EXPECT_TRUE(model.IsVerified());
+
+ model.set_origin(std::string());
+ EXPECT_FALSE(model.IsVerified());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_download.cc b/chromium/components/autofill/core/browser/autofill_download.cc
new file mode 100644
index 00000000000..9bf81e2c72b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_download.cc
@@ -0,0 +1,352 @@
+// 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/core/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/strings/string_util.h"
+#include "components/autofill/core/browser/autofill_download_url.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_xml_parser.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.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"
+#include "url/gurl.h"
+
+using content::BrowserContext;
+
+namespace autofill {
+
+namespace {
+const char kAutofillQueryServerNameStartInHeader[] = "GFE/";
+
+const size_t kMaxFormCacheSize = 16;
+
+// Generate field assignments xml that can be manually changed and then fed back
+// into the Autofill server as experiment data.
+static void LogFieldAssignments(
+ const FormStructure& form,
+ const ServerFieldTypeSet& available_field_types) {
+ std::string form_xml;
+ if (!form.EncodeFieldAssignments(available_field_types, &form_xml))
+ return;
+
+ VLOG(1) << "AutofillDownloadManager FieldAssignments for "
+ << form.source_url()
+ << " :\n"
+ << form_xml;
+}
+
+} // namespace
+
+// 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 = user_prefs::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, form signatures: "
+ << GetCombinedSignature(request_data.form_signatures);
+ observer_->OnLoadedServerPredictions(query_data);
+ return true;
+ }
+
+ return StartRequest(form_xml, request_data);
+}
+
+bool AutofillDownloadManager::StartUploadRequest(
+ const FormStructure& form,
+ bool form_was_autofilled,
+ const ServerFieldTypeSet& available_field_types) {
+ std::string form_xml;
+ if (!form.EncodeUploadRequest(available_field_types, form_was_autofilled,
+ &form_xml))
+ return false;
+
+ LogFieldAssignments(form, available_field_types);
+
+ 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 = user_prefs::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 = user_prefs::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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_download.h b/chromium/components/autofill/core/browser/autofill_download.h
new file mode 100644
index 00000000000..bba1d719762
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_download.h
@@ -0,0 +1,170 @@
+// 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_CORE_BROWSER_AUTOFILL_DOWNLOAD_H_
+#define COMPONENTS_AUTOFILL_CORE_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/time.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "net/url_request/url_fetcher_delegate.h"
+
+namespace content {
+class BrowserContext;
+} // namespace content
+
+namespace net {
+class URLFetcher;
+} // namespace net
+
+namespace autofill {
+
+class AutofillMetrics;
+class FormStructure;
+
+// 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 ServerFieldTypeSet& 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_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DOWNLOAD_H_
diff --git a/chromium/components/autofill/core/browser/autofill_download_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_unittest.cc
new file mode 100644
index 00000000000..9035c9cdafa
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_download_unittest.cc
@@ -0,0 +1,491 @@
+// 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 <list>
+
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/test_timeouts.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/browser/autofill_download.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/form_data.h"
+#include "content/public/test/test_browser_thread_bundle.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/public/web/WebInputElement.h"
+
+using WebKit::WebInputElement;
+
+namespace autofill {
+
+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) {
+ }
+
+ 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_;
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ TestingProfile profile_;
+ AutofillDownloadManager download_manager_;
+};
+
+TEST_F(AutofillDownloadTest, QueryAndUploadTest) {
+ // 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 = base::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 = base::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, ServerFieldTypeSet()));
+ // Request with id 2.
+ EXPECT_TRUE(download_manager_.StartUploadRequest(
+ *(form_structures[1]), false, ServerFieldTypeSet()));
+
+ 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, ServerFieldTypeSet()));
+ EXPECT_FALSE(download_manager_.StartUploadRequest(
+ *(form_structures[1]), false, ServerFieldTypeSet()));
+ 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, ServerFieldTypeSet()));
+ 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, ServerFieldTypeSet()));
+ fetcher = factory.GetFetcherByID(5);
+ EXPECT_EQ(NULL, fetcher);
+}
+
+TEST_F(AutofillDownloadTest, CacheQueryTest) {
+ // 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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_download_url.cc b/chromium/components/autofill/core/browser/autofill_download_url.cc
new file mode 100644
index 00000000000..657e69e9ee4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_download_url.cc
@@ -0,0 +1,47 @@
+// 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/core/browser/autofill_download_url.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "url/gurl.h"
+
+namespace autofill {
+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;
+}
+
+} // namespace
+
+GURL GetAutofillQueryUrl() {
+ std::string baseAutofillServiceUrl = GetBaseAutofillUrl();
+ return GURL(baseAutofillServiceUrl + "query?client=" + kClientName);
+}
+
+GURL GetAutofillUploadUrl() {
+ std::string baseAutofillServiceUrl = GetBaseAutofillUrl();
+ return GURL(baseAutofillServiceUrl + "upload?client=" + kClientName);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_download_url.h b/chromium/components/autofill/core/browser/autofill_download_url.h
new file mode 100644
index 00000000000..894a060dd98
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_download_url.h
@@ -0,0 +1,18 @@
+// 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_CORE_BROWSER_AUTOFILL_DOWNLOAD_URL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DOWNLOAD_URL_H_
+
+class GURL;
+
+namespace autofill {
+
+GURL GetAutofillQueryUrl();
+GURL GetAutofillUploadUrl();
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DOWNLOAD_URL_H_
+
diff --git a/chromium/components/autofill/core/browser/autofill_download_url_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_url_unittest.cc
new file mode 100644
index 00000000000..123ec6cf3e7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_download_url_unittest.cc
@@ -0,0 +1,26 @@
+// 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/core/browser/autofill_download_url.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using testing::StartsWith;
+
+namespace autofill {
+
+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="));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_driver.h b/chromium/components/autofill/core/browser/autofill_driver.h
new file mode 100644
index 00000000000..2c1e175ee35
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_driver.h
@@ -0,0 +1,67 @@
+// 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_CORE_BROWSER_AUTOFILL_DRIVER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DRIVER_H_
+
+#include <vector>
+
+#include "components/autofill/core/common/form_data.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace autofill {
+
+class FormStructure;
+
+// Interface that allows Autofill core code to interact with its driver (i.e.,
+// obtain information from it and give information to it). A concrete
+// implementation must be provided by the driver.
+class AutofillDriver {
+ public:
+ // The possible actions that the renderer can take on receiving form data.
+ enum RendererFormDataAction {
+ // The renderer should fill the form data.
+ FORM_DATA_ACTION_FILL,
+ // The renderer should preview the form data.
+ FORM_DATA_ACTION_PREVIEW
+ };
+
+ virtual ~AutofillDriver() {}
+
+ // TODO(blundell): Remove this method once shared code no longer needs to
+ // know about WebContents.
+ virtual content::WebContents* GetWebContents() = 0;
+
+ // Returns true iff the renderer is available for communication.
+ virtual bool RendererIsAvailable() = 0;
+
+ // Informs the renderer what action to take with the next form data that it
+ // receives. Must be called before each call to |SendFormDataToRenderer|.
+ virtual void SetRendererActionOnFormDataReception(
+ RendererFormDataAction action) = 0;
+
+ // Forwards |data| to the renderer. |query_id| is the id of the renderer's
+ // original request for the data. This method is a no-op if the renderer is
+ // not currently available.
+ virtual void SendFormDataToRenderer(int query_id, const FormData& data) = 0;
+
+ // Sends the field type predictions specified in |forms| to the renderer. This
+ // method is a no-op if the renderer is not available or the appropriate
+ // command-line flag is not set.
+ virtual void SendAutofillTypePredictionsToRenderer(
+ const std::vector<FormStructure*>& forms) = 0;
+
+ // Tells the renderer to clear the currently filled Autofill results.
+ virtual void RendererShouldClearFilledForm() = 0;
+
+ // Tells the renderer to clear the currently previewed Autofill results.
+ virtual void RendererShouldClearPreviewedForm() = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_DRIVER_H_
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
new file mode 100644
index 00000000000..95119bfad99
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
@@ -0,0 +1,381 @@
+// 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/core/browser/autofill_external_delegate.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "grit/component_strings.h"
+#include "third_party/WebKit/public/web/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;
+
+namespace autofill {
+
+AutofillExternalDelegate::AutofillExternalDelegate(
+ content::WebContents* web_contents,
+ AutofillManager* autofill_manager,
+ AutofillDriver* autofill_driver)
+ : web_contents_(web_contents),
+ autofill_manager_(autofill_manager),
+ autofill_driver_(autofill_driver),
+ 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),
+ weak_ptr_factory_(this) {
+ DCHECK(autofill_manager);
+}
+
+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<base::string16>& autofill_values,
+ const std::vector<base::string16>& autofill_labels,
+ const std::vector<base::string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids) {
+ if (query_id != autofill_query_id_)
+ return;
+
+ std::vector<base::string16> values(autofill_values);
+ std::vector<base::string16> labels(autofill_labels);
+ std::vector<base::string16> icons(autofill_icons);
+ std::vector<int> ids(autofill_unique_ids);
+
+ // Add or hide warnings as appropriate.
+ ApplyAutofillWarnings(&values, &labels, &icons, &ids);
+
+ // Add a separator to go between the values and menu items.
+ values.push_back(base::string16());
+ labels.push_back(base::string16());
+ icons.push_back(base::string16());
+ ids.push_back(WebAutofillClient::MenuItemIDSeparator);
+
+ // 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.
+ DCHECK_GT(ids.size(), 0U);
+ if (ids.back() == WebAutofillClient::MenuItemIDSeparator) {
+ values.pop_back();
+ labels.pop_back();
+ icons.pop_back();
+ ids.pop_back();
+ }
+
+ // If anything else is added to modify the values after inserting the data
+ // list, AutofillPopupControllerImpl::UpdateDataListValues will need to be
+ // updated to match.
+ 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_,
+ autofill_query_field_.text_direction,
+ values,
+ labels,
+ icons,
+ ids,
+ GetWeakPtr());
+ }
+}
+
+void AutofillExternalDelegate::OnShowPasswordSuggestions(
+ const std::vector<base::string16>& suggestions,
+ const std::vector<base::string16>& realms,
+ 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<base::string16> empty(suggestions.size());
+ std::vector<int> password_ids(suggestions.size(),
+ WebAutofillClient::MenuItemIDPasswordEntry);
+ autofill_manager_->delegate()->ShowAutofillPopup(
+ element_bounds_,
+ autofill_query_field_.text_direction,
+ suggestions,
+ realms,
+ empty,
+ password_ids,
+ GetWeakPtr());
+}
+
+void AutofillExternalDelegate::SetCurrentDataListValues(
+ const std::vector<base::string16>& data_list_values,
+ const std::vector<base::string16>& data_list_labels) {
+ data_list_values_ = data_list_values;
+ data_list_labels_ = data_list_labels;
+
+ autofill_manager_->delegate()->UpdateAutofillPopupDataListValues(
+ data_list_values,
+ data_list_labels);
+}
+
+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 ((!web_contents_->IsBeingDestroyed()) &&
+ (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 base::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'.
+ autofill_driver_->RendererShouldClearFilledForm();
+ } else if (identifier == WebAutofillClient::MenuItemIDPasswordEntry) {
+ bool success = password_autofill_manager_.DidAcceptAutofillSuggestion(
+ autofill_query_field_, value);
+ DCHECK(success);
+ } 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 base::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() {
+ autofill_driver_->RendererShouldClearPreviewedForm();
+}
+
+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);
+}
+
+base::WeakPtr<AutofillExternalDelegate> AutofillExternalDelegate::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+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;
+
+ AutofillDriver::RendererFormDataAction renderer_action = is_preview ?
+ AutofillDriver::FORM_DATA_ACTION_PREVIEW :
+ AutofillDriver::FORM_DATA_ACTION_FILL;
+
+ DCHECK(autofill_driver_->RendererIsAvailable());
+ autofill_driver_->SetRendererActionOnFormDataReception(renderer_action);
+ // 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<base::string16>* autofill_values,
+ std::vector<base::string16>* autofill_labels,
+ std::vector<base::string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids) {
+ if (!autofill_query_field_.should_autocomplete) {
+ // Autofill is disabled. If there were some profile or credit card
+ // suggestions to show, show a warning instead. Otherwise, clear out the
+ // list of suggestions.
+ if (!autofill_unique_ids->empty() && (*autofill_unique_ids)[0] > 0) {
+ // 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, base::string16());
+ autofill_icons->assign(1, base::string16());
+ autofill_unique_ids->assign(1,
+ WebAutofillClient::MenuItemIDWarningMessage);
+ } else {
+ autofill_values->clear();
+ autofill_labels->clear();
+ autofill_icons->clear();
+ autofill_unique_ids->clear();
+ }
+ } 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<base::string16>* autofill_values,
+ std::vector<base::string16>* autofill_labels,
+ std::vector<base::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(base::string16());
+ autofill_icons->push_back(base::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(base::string16());
+ autofill_icons->push_back(base::string16());
+ autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDAutofillOptions);
+}
+
+void AutofillExternalDelegate::InsertDataListValues(
+ std::vector<base::string16>* autofill_values,
+ std::vector<base::string16>* autofill_labels,
+ std::vector<base::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(), base::string16());
+ autofill_labels->insert(autofill_labels->begin(), base::string16());
+ autofill_icons->insert(autofill_icons->begin(), base::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());
+
+ // Set the values that all datalist elements share.
+ autofill_icons->insert(autofill_icons->begin(),
+ data_list_values_.size(),
+ base::string16());
+ autofill_unique_ids->insert(autofill_unique_ids->begin(),
+ data_list_values_.size(),
+ WebAutofillClient::MenuItemIDDataListEntry);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h
new file mode 100644
index 00000000000..3362ff8e19b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h
@@ -0,0 +1,185 @@
+// 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_CORE_BROWSER_AUTOFILL_EXTERNAL_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXTERNAL_DELEGATE_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_popup_delegate.h"
+#include "components/autofill/core/browser/password_autofill_manager.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form_fill_data.h"
+#include "ui/gfx/rect.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace content {
+class RenderViewHost;
+class WebContents;
+}
+
+namespace autofill {
+
+class AutofillDriver;
+class AutofillManager;
+
+// 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 AutofillPopupDelegate {
+ public:
+ // Creates an AutofillExternalDelegate for the specified contents,
+ // AutofillManager, and AutofillDriver.
+ AutofillExternalDelegate(content::WebContents* web_contents,
+ AutofillManager* autofill_manager,
+ AutofillDriver* autofill_driver);
+ virtual ~AutofillExternalDelegate();
+
+ // 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 base::string16& value,
+ int identifier) OVERRIDE;
+ virtual void RemoveSuggestion(const base::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<base::string16>& autofill_values,
+ const std::vector<base::string16>& autofill_labels,
+ const std::vector<base::string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids);
+
+ // Show password suggestions in the popup.
+ void OnShowPasswordSuggestions(const std::vector<base::string16>& suggestions,
+ const std::vector<base::string16>& realms,
+ const FormFieldData& field,
+ const gfx::RectF& bounds);
+
+ // Set the data list value associated with the current field.
+ void SetCurrentDataListValues(
+ const std::vector<base::string16>& data_list_values,
+ const std::vector<base::string16>& data_list_labels);
+
+ // 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:
+ content::WebContents* web_contents() { return web_contents_; }
+
+ base::WeakPtr<AutofillExternalDelegate> GetWeakPtr();
+
+ 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<base::string16>* autofill_values,
+ std::vector<base::string16>* autofill_labels,
+ std::vector<base::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<base::string16>* autofill_values,
+ std::vector<base::string16>* autofill_labels,
+ std::vector<base::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<base::string16>* autofill_values,
+ std::vector<base::string16>* autofill_labels,
+ std::vector<base::string16>* autofill_icons,
+ std::vector<int>* autofill_unique_ids);
+
+ // The web_contents associated with this delegate.
+ content::WebContents* web_contents_; // weak; owns me.
+ AutofillManager* autofill_manager_; // weak.
+
+ // Provides driver-level context to the shared code of the component. Must
+ // outlive this object.
+ AutofillDriver* autofill_driver_; // 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_;
+
+ // 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<base::string16> data_list_values_;
+ std::vector<base::string16> data_list_labels_;
+
+ base::WeakPtrFactory<AutofillExternalDelegate> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillExternalDelegate);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXTERNAL_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
new file mode 100644
index 00000000000..ae863ca6a38
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -0,0 +1,473 @@
+// 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 <vector>
+
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_autofill_external_delegate.h"
+#include "components/autofill/core/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form_fill_data.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/web/WebAutofillClient.h"
+#include "ui/gfx/rect.h"
+
+using testing::_;
+using WebKit::WebAutofillClient;
+
+namespace autofill {
+
+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 MockAutofillDriver : public TestAutofillDriver {
+ public:
+ explicit MockAutofillDriver(content::WebContents* web_contents)
+ : TestAutofillDriver(web_contents) {}
+
+ // Mock methods to enable testability.
+ MOCK_METHOD1(SetRendererActionOnFormDataReception,
+ void(RendererFormDataAction action));
+ MOCK_METHOD0(RendererShouldClearFilledForm, void());
+ MOCK_METHOD0(RendererShouldClearPreviewedForm, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver);
+};
+
+class MockAutofillManagerDelegate
+ : public autofill::TestAutofillManagerDelegate {
+ public:
+ MockAutofillManagerDelegate() {}
+
+ MOCK_METHOD7(ShowAutofillPopup,
+ void(const gfx::RectF& element_bounds,
+ base::i18n::TextDirection text_direction,
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels,
+ const std::vector<base::string16>& icons,
+ const std::vector<int>& identifiers,
+ base::WeakPtr<AutofillPopupDelegate> delegate));
+
+ MOCK_METHOD2(UpdateAutofillPopupDataListValues,
+ void(const std::vector<base::string16>& values,
+ const std::vector<base::string16>& lables));
+
+ MOCK_METHOD0(HideAutofillPopup, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
+};
+
+class MockAutofillManager : public AutofillManager {
+ public:
+ MockAutofillManager(AutofillDriver* driver,
+ 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(driver, 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 {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+ autofill_driver_.reset(new MockAutofillDriver(web_contents()));
+ autofill_manager_.reset(
+ new MockAutofillManager(autofill_driver_.get(),
+ &manager_delegate_));
+ external_delegate_.reset(
+ new AutofillExternalDelegate(
+ web_contents(),
+ autofill_manager_.get(), autofill_driver_.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();
+ autofill_driver_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+ }
+
+ // 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, true);
+ }
+
+ MockAutofillManagerDelegate manager_delegate_;
+ scoped_ptr<MockAutofillDriver> autofill_driver_;
+ scoped_ptr<MockAutofillManager> autofill_manager_;
+ scoped_ptr<AutofillExternalDelegate> external_delegate_;
+};
+
+// 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)),
+ _));
+
+ // This should call ShowAutofillPopup.
+ std::vector<base::string16> autofill_item;
+ autofill_item.push_back(base::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(*autofill_driver_, SetRendererActionOnFormDataReception(
+ AutofillDriver::FORM_DATA_ACTION_FILL));
+
+ 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<base::string16> data_list_items;
+ data_list_items.push_back(base::string16());
+
+ external_delegate_->SetCurrentDataListValues(data_list_items,
+ data_list_items);
+
+ // 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)),
+ _));
+
+ // This should call ShowAutofillPopup.
+ std::vector<base::string16> autofill_item;
+ autofill_item.push_back(base::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)),
+ _));
+
+ autofill_item = std::vector<base::string16>();
+ autofill_ids = std::vector<int>();
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_item,
+ autofill_item,
+ autofill_item,
+ autofill_ids);
+}
+
+// Test that datalist values can get updated while a popup is showing.
+TEST_F(AutofillExternalDelegateUnitTest, UpdateDataListWhileShowingPopup) {
+ IssueOnQuery(kQueryId);
+
+ EXPECT_CALL(manager_delegate_,
+ ShowAutofillPopup(_, _, _, _, _, _, _)).Times(0);
+
+ // Make sure just setting the data list values doesn't cause the popup to
+ // appear.
+ std::vector<base::string16> data_list_items;
+ data_list_items.push_back(base::string16());
+
+ external_delegate_->SetCurrentDataListValues(data_list_items,
+ data_list_items);
+
+ // 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)),
+ _));
+
+ // Ensure the popup is displayed.
+ std::vector<base::string16> autofill_item;
+ autofill_item.push_back(base::string16());
+ std::vector<int> autofill_ids;
+ autofill_ids.push_back(kAutofillProfileId);
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_item,
+ autofill_item,
+ autofill_item,
+ autofill_ids);
+
+ // This would normally get called from ShowAutofillPopup, but it is mocked
+ // we need to call OnPopupShown ourselves.
+ external_delegate_->OnPopupShown(NULL);
+
+ // Update the current data list and ensure the popup is updated.
+ data_list_items.push_back(base::string16());
+
+ // The enums must be cast to ints to prevent compile errors on linux_rel.
+ EXPECT_CALL(manager_delegate_,
+ UpdateAutofillPopupDataListValues(data_list_items,
+ data_list_items));
+
+ external_delegate_->SetCurrentDataListValues(data_list_items,
+ data_list_items);
+}
+
+// Test that the Autofill popup is able to display warnings explaining why
+// Autofill is disabled for a website.
+// Regression test for http://crbug.com/247880
+TEST_F(AutofillExternalDelegateUnitTest, AutofillWarnings) {
+ IssueOnQuery(kQueryId);
+
+ // 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::MenuItemIDWarningMessage)),
+ _));
+
+ // This should call ShowAutofillPopup.
+ std::vector<base::string16> autofill_item;
+ autofill_item.push_back(base::string16());
+ std::vector<int> autofill_ids;
+ autofill_ids.push_back(WebAutofillClient::MenuItemIDWarningMessage);
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_item,
+ autofill_item,
+ autofill_item,
+ autofill_ids);
+}
+
+// Test that the Autofill popup doesn't display a warning explaining why
+// Autofill is disabled for a website when there are no Autofill suggestions.
+// Regression test for http://crbug.com/105636
+TEST_F(AutofillExternalDelegateUnitTest, NoAutofillWarningsWithoutSuggestions) {
+ const FormData form;
+ FormFieldData field;
+ field.is_focusable = true;
+ field.should_autocomplete = false;
+ const gfx::RectF element_bounds;
+
+ external_delegate_->OnQuery(kQueryId, form, field, element_bounds, true);
+
+ EXPECT_CALL(manager_delegate_,
+ ShowAutofillPopup(_, _, _, _, _, _, _)).Times(0);
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup()).Times(1);
+
+ // This should not call ShowAutofillPopup.
+ std::vector<base::string16> autofill_item;
+ autofill_item.push_back(base::string16());
+ std::vector<int> autofill_ids;
+ autofill_ids.push_back(WebAutofillClient::MenuItemIDAutocompleteEntry);
+ 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(*autofill_driver_,
+ SetRendererActionOnFormDataReception(_)).Times(0);
+ EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).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);
+ EXPECT_CALL(*autofill_driver_,
+ SetRendererActionOnFormDataReception(_)).Times(0);
+ external_delegate_->DidAcceptSuggestion(base::string16(), -1);
+}
+
+// Test that the ClearPreview call is only sent if 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(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
+ external_delegate_->DidSelectSuggestion(
+ WebAutofillClient::MenuItemIDPasswordEntry);
+
+ EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
+ EXPECT_CALL(*autofill_driver_, SetRendererActionOnFormDataReception(
+ AutofillDriver::FORM_DATA_ACTION_PREVIEW));
+ 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) {
+ static const base::string16 kUsername = ASCIIToUTF16("username");
+ static const base::string16 kSignonRealm = ASCIIToUTF16("http://foo.com/");
+ std::vector<base::string16> suggestions;
+ suggestions.push_back(kUsername);
+ std::vector<base::string16> realms;
+ realms.push_back(kSignonRealm);
+
+ FormFieldData field;
+ field.is_focusable = true;
+ field.should_autocomplete = true;
+ const gfx::RectF element_bounds;
+
+ FormFieldData username_field_data;
+ username_field_data.value = kUsername;
+ PasswordFormFillData password_form_fill_data;
+ password_form_fill_data.basic_data.fields.push_back(username_field_data);
+ external_delegate_->AddPasswordFormMapping(field, password_form_fill_data);
+
+ // 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_->OnShowPasswordSuggestions(suggestions,
+ realms,
+ field,
+ element_bounds);
+
+ 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);
+}
+
+// Test that the driver is directed to clear the form after being notified that
+// the user accepted the suggestion to clear the form.
+TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateClearForm) {
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup());
+ EXPECT_CALL(*autofill_driver_, RendererShouldClearFilledForm());
+
+ external_delegate_->DidAcceptSuggestion(
+ base::string16(),
+ WebAutofillClient::MenuItemIDClearForm);
+}
+
+TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateHideWarning) {
+ // Set up a field that shouldn't get autocompleted or display warnings.
+ const FormData form;
+ FormFieldData field;
+ field.is_focusable = true;
+ field.should_autocomplete = false;
+ const gfx::RectF element_bounds;
+
+ external_delegate_->OnQuery(kQueryId, form, field, element_bounds, false);
+
+ std::vector<base::string16> autofill_items;
+ autofill_items.push_back(base::string16());
+ std::vector<int> autofill_ids;
+ autofill_ids.push_back(WebAutofillClient::MenuItemIDAutocompleteEntry);
+
+ // Ensure the popup tries to hide itself, since it is not allowed to show
+ // anything.
+ EXPECT_CALL(manager_delegate_, HideAutofillPopup());
+
+ external_delegate_->OnSuggestionsReturned(kQueryId,
+ autofill_items,
+ autofill_items,
+ autofill_items,
+ autofill_ids);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_field.cc b/chromium/components/autofill/core/browser/autofill_field.cc
new file mode 100644
index 00000000000..4bcb1185366
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_field.cc
@@ -0,0 +1,106 @@
+// 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/core/browser/autofill_field.h"
+
+#include "base/logging.h"
+#include "base/sha1.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_type.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
+
+namespace autofill {
+
+AutofillField::AutofillField()
+ : server_type_(NO_SERVER_DATA),
+ heuristic_type_(UNKNOWN_TYPE),
+ html_type_(HTML_TYPE_UNKNOWN),
+ html_mode_(HTML_MODE_NONE),
+ phone_part_(IGNORED) {
+}
+
+AutofillField::AutofillField(const FormFieldData& field,
+ const base::string16& unique_name)
+ : FormFieldData(field),
+ unique_name_(unique_name),
+ server_type_(NO_SERVER_DATA),
+ heuristic_type_(UNKNOWN_TYPE),
+ html_type_(HTML_TYPE_UNKNOWN),
+ html_mode_(HTML_MODE_NONE),
+ phone_part_(IGNORED) {
+}
+
+AutofillField::~AutofillField() {}
+
+void AutofillField::set_heuristic_type(ServerFieldType 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(ServerFieldType 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;
+}
+
+void AutofillField::SetHtmlType(HtmlFieldType type, HtmlFieldMode mode) {
+ html_type_ = type;
+ html_mode_ = mode;
+
+ if (type == HTML_TYPE_TEL_LOCAL_PREFIX)
+ phone_part_ = AutofillField::PHONE_PREFIX;
+ else if (type == HTML_TYPE_TEL_LOCAL_SUFFIX)
+ phone_part_ = AutofillField::PHONE_SUFFIX;
+}
+
+AutofillType AutofillField::Type() const {
+ if (html_type_ != HTML_TYPE_UNKNOWN)
+ return AutofillType(html_type_, html_mode_);
+
+ if (server_type_ != NO_SERVER_DATA)
+ return AutofillType(server_type_);
+
+ return AutofillType(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().IsUnknown();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_field.h b/chromium/components/autofill/core/browser/autofill_field.h
new file mode 100644
index 00000000000..6f57484d535
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_field.h
@@ -0,0 +1,103 @@
+// 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_CORE_BROWSER_AUTOFILL_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_FIELD_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/form_field_data.h"
+
+namespace autofill {
+
+class AutofillType;
+
+class AutofillField : public FormFieldData {
+ public:
+ enum PhonePart {
+ IGNORED = 0,
+ PHONE_PREFIX = 1,
+ PHONE_SUFFIX = 2,
+ };
+
+ AutofillField();
+ AutofillField(const FormFieldData& field, const base::string16& unique_name);
+ virtual ~AutofillField();
+
+ const base::string16& unique_name() const { return unique_name_; }
+
+ const std::string& section() const { return section_; }
+ ServerFieldType heuristic_type() const { return heuristic_type_; }
+ ServerFieldType server_type() const { return server_type_; }
+ HtmlFieldType html_type() const { return html_type_; }
+ HtmlFieldMode html_mode() const { return html_mode_; }
+ const ServerFieldTypeSet& possible_types() const { return possible_types_; }
+ PhonePart phone_part() const { return phone_part_; }
+
+ // Setters for the detected type and section for this field.
+ void set_section(const std::string& section) { section_ = section; }
+ void set_heuristic_type(ServerFieldType type);
+ void set_server_type(ServerFieldType type);
+ void set_possible_types(const ServerFieldTypeSet& possible_types) {
+ possible_types_ = possible_types;
+ }
+ void SetHtmlType(HtmlFieldType type, HtmlFieldMode mode);
+
+ // This function automatically chooses between server and heuristic autofill
+ // type, depending on the data available.
+ AutofillType 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.
+ base::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.
+ ServerFieldType server_type_;
+
+ // The type of the field, as determined by the local heuristics.
+ ServerFieldType heuristic_type_;
+
+ // The type of the field, as specified by the site author in HTML.
+ HtmlFieldType html_type_;
+
+ // The "mode" of the field, as specified by the site author in HTML.
+ // Currently this is used to distinguish between billing and shipping fields.
+ HtmlFieldMode html_mode_;
+
+ // The set of possible types for this field.
+ ServerFieldTypeSet 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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/autofill_field_unittest.cc b/chromium/components/autofill/core/browser/autofill_field_unittest.cc
new file mode 100644
index 00000000000..3c263023783
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_field_unittest.cc
@@ -0,0 +1,99 @@
+// 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 "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+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().GetStorableType());
+
+ // Set the heuristic type and check it.
+ field.set_heuristic_type(NAME_FIRST);
+ EXPECT_EQ(NAME_FIRST, field.Type().GetStorableType());
+ EXPECT_EQ(NAME, field.Type().group());
+
+ // Set the server type and check it.
+ field.set_server_type(ADDRESS_BILLING_LINE1);
+ EXPECT_EQ(ADDRESS_HOME_LINE1, field.Type().GetStorableType());
+ EXPECT_EQ(ADDRESS_BILLING, field.Type().group());
+
+ // 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().GetStorableType());
+ EXPECT_EQ(NAME, field.Type().group());
+}
+
+TEST(AutofillFieldTest, IsEmpty) {
+ AutofillField field;
+ ASSERT_EQ(base::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(base::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().GetStorableType());
+
+ // 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
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc
new file mode 100644
index 00000000000..388e079f3ef
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.cc
@@ -0,0 +1,312 @@
+// 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/core/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/strings/string16.h"
+#include "base/win/registry.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/crypto/rc4_decryptor.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_group.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
+#include "components/autofill/core/browser/phone_number.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/webdata/encryptor/encryptor.h"
+
+using base::win::RegKey;
+
+namespace autofill {
+
+// Forward declaration. This function is not in unnamed namespace as it
+// is referenced in the unittest.
+bool ImportCurrentUserProfiles(const std::string& app_locale,
+ 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 string is stored along with saved addresses and credit cards in the
+// WebDB, and hence should not be modified, so that it remains consistent over
+// time.
+const char kIEToolbarImportOrigin[] = "Imported from Internet Explorer";
+
+// 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;
+}
+
+base::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 base::string16();
+ std::string 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 (Encryptor::DecryptString(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 base::string16(
+ reinterpret_cast<const wchar_t *>(out_data.c_str()));
+ }
+ }
+ }
+ return base::string16();
+}
+
+struct {
+ ServerFieldType 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, ServerFieldType> 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,
+ const std::string& app_locale,
+ FormGroup* form_group,
+ PhoneNumber::PhoneCombineHelper* phone) {
+ if (!key.Valid())
+ return false;
+
+ bool has_non_empty_fields = false;
+
+ 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.
+
+ base::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(AutofillType(it->second), field_value)) {
+ has_non_empty_fields = true;
+ form_group->SetInfo(AutofillType(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 std::string& app_locale,
+ const RegKey& key,
+ const RegToFieldMap& reg_to_field,
+ AutofillProfile* profile) {
+ PhoneNumber::PhoneCombineHelper phone;
+ bool has_non_empty_fields =
+ ImportSingleFormGroup(key, reg_to_field, app_locale, profile, &phone);
+
+ // Now re-construct the phones if needed.
+ base::string16 constructed_number;
+ 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(personal_data_manager_->app_locale(),
+ &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(const std::string& app_locale,
+ 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;
+ profile.set_origin(kIEToolbarImportOrigin);
+ if (ImportSingleProfile(app_locale, key, reg_to_field, &profile)) {
+ // Combine phones into whole phone #.
+ profiles->push_back(profile);
+ }
+ }
+ base::string16 password_hash;
+ base::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;
+ credit_card.set_origin(kIEToolbarImportOrigin);
+ if (ImportSingleFormGroup(
+ key, reg_to_field, app_locale, &credit_card, NULL)) {
+ base::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();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h
new file mode 100644
index 00000000000..830aac2555f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win.h
@@ -0,0 +1,24 @@
+// 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_CORE_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_
+
+namespace autofill {
+
+// 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);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_IE_TOOLBAR_IMPORT_WIN_H_
diff --git a/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
new file mode 100644
index 00000000000..61917bcaf37
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_ie_toolbar_import_win_unittest.cc
@@ -0,0 +1,211 @@
+// 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/core/browser/autofill_ie_toolbar_import_win.h"
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "base/win/registry.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/webdata/encryptor/encryptor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::win::RegKey;
+
+namespace autofill {
+
+// Defined in autofill_ie_toolbar_import_win.cc. Not exposed in the header file.
+bool ImportCurrentUserProfiles(const std::string& app_locale,
+ 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::string encrypted_data;
+ Encryptor::EncryptString(data, &encrypted_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("en-US", &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(AutofillType(PHONE_HOME_COUNTRY_CODE), "US"));
+ EXPECT_EQ(profile1[6].value,
+ profiles[1].GetInfo(AutofillType(PHONE_HOME_CITY_CODE), "US"));
+ EXPECT_EQ(L"5555555",
+ profiles[1].GetInfo(AutofillType(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("en-US", &profiles, &credit_cards));
+ // Profiles are not protected.
+ EXPECT_EQ(2U, profiles.size());
+ // Credit cards are.
+ EXPECT_EQ(0U, credit_cards.size());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc
new file mode 100644
index 00000000000..4c66699b819
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_manager.cc
@@ -0,0 +1,1219 @@
+// 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/core/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/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "components/autofill/content/browser/autocheckout/whitelist_manager.h"
+#include "components/autofill/content/browser/autocheckout_manager.h"
+#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/autofill_data_model.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/browser/autofill_manager_test_delegate.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/phone_number.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_data_predictions.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/password_form_fill_data.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.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 "grit/component_strings.h"
+#include "third_party/WebKit/public/web/WebAutofillClient.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/rect.h"
+#include "url/gurl.h"
+
+namespace autofill {
+
+typedef PersonalDataManager::GUIDPair GUIDPair;
+
+using base::TimeTicks;
+using content::BrowserThread;
+using content::RenderViewHost;
+using WebKit::WebFormElement;
+
+namespace {
+
+// 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<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::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<base::string16, base::string16> > seen_suggestions;
+ std::vector<base::string16> values_copy;
+ std::vector<base::string16> labels_copy;
+ std::vector<base::string16> icons_copy;
+ std::vector<int> unique_ids_copy;
+
+ for (size_t i = 0; i < values->size(); ++i) {
+ const std::pair<base::string16, base::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);
+ base::string16 value = CollapseWhitespace(field->value, false);
+
+ ServerFieldTypeSet 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);
+ }
+}
+
+// Returns true if server returned known field types to one or more fields in
+// this form.
+bool HasServerSpecifiedFieldTypes(const FormStructure& form_structure) {
+ for (size_t i = 0; i < form_structure.field_count(); ++i) {
+ if (form_structure.field(i)->server_type() != NO_SERVER_DATA)
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+AutofillManager::AutofillManager(
+ AutofillDriver* driver,
+ autofill::AutofillManagerDelegate* delegate,
+ const std::string& app_locale,
+ AutofillDownloadManagerState enable_download_manager)
+ : driver_(driver),
+ manager_delegate_(delegate),
+ app_locale_(app_locale),
+ personal_data_(delegate->GetPersonalDataManager()),
+ autocomplete_history_manager_(
+ new AutocompleteHistoryManager(driver, delegate)),
+ 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),
+ external_delegate_(NULL),
+ test_delegate_(NULL),
+ weak_ptr_factory_(this) {
+ if (enable_download_manager == ENABLE_AUTOFILL_DOWNLOAD_MANAGER) {
+ download_manager_.reset(
+ new AutofillDownloadManager(
+ driver->GetWebContents()->GetBrowserContext(), this));
+ }
+}
+
+AutofillManager::~AutofillManager() {}
+
+// static
+void AutofillManager::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(
+ prefs::kAutofillEnabled,
+ true,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
+ registry->RegisterBooleanPref(
+ prefs::kAutofillAuxiliaryProfilesEnabled,
+ true,
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#else
+ registry->RegisterBooleanPref(
+ prefs::kAutofillAuxiliaryProfilesEnabled,
+ false,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+#endif
+ registry->RegisterDoublePref(
+ prefs::kAutofillPositiveUploadRate,
+ kAutofillPositiveUploadRateDefaultValue,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+ registry->RegisterDoublePref(
+ prefs::kAutofillNegativeUploadRate,
+ kAutofillNegativeUploadRateDefaultValue,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+}
+
+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::OnFormSubmitted(const FormData& form,
+ const TimeTicks& timestamp) {
+ // Let Autocomplete know as well.
+ autocomplete_history_manager_->OnFormSubmitted(form);
+
+ if (!IsAutofillEnabled())
+ return false;
+
+ if (driver_->GetWebContents()->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);
+ // Don't prompt the user to save data entered by Autocheckout.
+ if (submitted_form->IsAutofillable(true) &&
+ !submitted_form->filled_by_autocheckout())
+ 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_->GetCreditCards();
+ 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,
+ app_locale_,
+ 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,
+ autofill::FormsSeenState state) {
+ bool is_post_document_load = state == autofill::DYNAMIC_FORMS_SEEN;
+ bool has_more_forms = state == autofill::PARTIAL_FORMS_SEEN;
+ // If new forms were added dynamically, and the autocheckout manager
+ // doesn't tell us to ignore ajax on this page, treat as a new page.
+ if (is_post_document_load) {
+ if (autocheckout_manager_.ShouldIgnoreAjax())
+ return;
+
+ Reset();
+ }
+
+ RenderViewHost* host = driver_->GetWebContents()->GetRenderViewHost();
+ if (!host)
+ return;
+
+ if (!GetAutocheckoutURLPrefix().empty()) {
+ // If whitelisted URL, fetch all the forms.
+ if (has_more_forms)
+ host->Send(new AutofillMsg_GetAllForms(host->GetRoutingID()));
+ if (!is_post_document_load) {
+ host->Send(
+ new AutofillMsg_AutocheckoutSupported(host->GetRoutingID()));
+ }
+ // Now return early, as OnFormsSeen will get called again with all forms.
+ if (has_more_forms)
+ return;
+ }
+
+ 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_) {
+ autocheckout_manager_.set_should_show_bubble(false);
+ 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) {
+ if (autocheckout_manager_.is_autocheckout_bubble_showing())
+ return;
+
+ std::vector<base::string16> values;
+ std::vector<base::string16> labels;
+ std::vector<base::string16> icons;
+ std::vector<int> unique_ids;
+
+ 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)) {
+ AutofillType type = autofill_field->Type();
+ bool is_filling_credit_card = (type.group() == 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, base::string16());
+ icons.assign(1, base::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(), base::string16());
+ icons.assign(icons.size(), base::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;
+ }
+ }
+ }
+ }
+
+ // 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 AutofillDataModel* data_model = NULL;
+ size_t variant = 0;
+ FormStructure* form_structure = NULL;
+ AutofillField* autofill_field = NULL;
+ // NOTE: GetHost may invalidate |data_model| because it causes the
+ // PersonalDataManager to reload Mac address book entries. Thus it must
+ // come before GetProfileOrCreditCard.
+ if (!GetHost(&host) ||
+ !GetProfileOrCreditCard(unique_id, &data_model, &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) {
+ data_model->FillFormField(
+ *autofill_field, variant, app_locale_, &(*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;
+ }
+ }
+
+ driver_->SendFormDataToRenderer(query_id, result);
+ return;
+ }
+
+ // Cache the field type for the field from which the user initiated autofill.
+ FieldTypeGroup initiating_group_type = 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 = cached_field->Type().group();
+ if (field_group_type != 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;
+ }
+ data_model->FillFormField(*cached_field,
+ use_variant,
+ app_locale_,
+ &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();
+
+ driver_->SendFormDataToRenderer(query_id, result);
+}
+
+void AutofillManager::OnShowAutofillDialog() {
+ manager_delegate_->ShowAutofillSettings();
+}
+
+void AutofillManager::OnDidPreviewAutofillFormData() {
+ if (test_delegate_)
+ test_delegate_->DidPreviewFormData();
+}
+
+void AutofillManager::OnDidFillAutofillFormData(const TimeTicks& timestamp) {
+ if (test_delegate_)
+ test_delegate_->DidFillFormData();
+
+ 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) {
+ if (test_delegate_)
+ test_delegate_->DidShowSuggestions();
+
+ 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::OnHideAutofillUI() {
+ if (!IsAutofillEnabled())
+ return;
+
+ manager_delegate_->HideAutofillPopup();
+ manager_delegate_->HideAutocheckoutBubble();
+}
+
+void AutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
+ const AutofillDataModel* data_model = NULL;
+ size_t variant = 0;
+ if (!GetProfileOrCreditCard(unique_id, &data_model, &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(data_model->guid());
+}
+
+void AutofillManager::RemoveAutocompleteEntry(const base::string16& name,
+ const base::string16& value) {
+ autocomplete_history_manager_->OnRemoveAutocompleteEntry(name, value);
+}
+
+content::WebContents* AutofillManager::GetWebContents() const {
+ return driver_->GetWebContents();
+}
+
+const std::vector<FormStructure*>& AutofillManager::GetFormStructures() {
+ return form_structures_.get();
+}
+
+void AutofillManager::ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ autofill::DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback) {
+ manager_delegate_->ShowRequestAutocompleteDialog(
+ form, source_url, dialog_type, callback);
+}
+
+void AutofillManager::SetTestDelegate(
+ autofill::AutofillManagerTestDelegate* delegate) {
+ test_delegate_ = delegate;
+}
+
+void AutofillManager::OnAddPasswordFormMapping(
+ const FormFieldData& form,
+ const PasswordFormFillData& fill_data) {
+ external_delegate_->AddPasswordFormMapping(form, fill_data);
+}
+
+void AutofillManager::OnShowPasswordSuggestions(
+ const FormFieldData& field,
+ const gfx::RectF& bounds,
+ const std::vector<base::string16>& suggestions,
+ const std::vector<base::string16>& realms) {
+ external_delegate_->OnShowPasswordSuggestions(suggestions,
+ realms,
+ field,
+ bounds);
+}
+
+void AutofillManager::OnSetDataList(const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) {
+ if (values.size() != labels.size())
+ return;
+
+ external_delegate_->SetCurrentDataListValues(values, labels);
+}
+
+void AutofillManager::OnRequestAutocomplete(
+ const FormData& form,
+ const GURL& frame_url) {
+ if (!IsAutofillEnabled()) {
+ ReturnAutocompleteResult(WebFormElement::AutocompleteResultErrorDisabled,
+ FormData());
+ return;
+ }
+
+ base::Callback<void(const FormStructure*, const std::string&)> callback =
+ base::Bind(&AutofillManager::ReturnAutocompleteData,
+ weak_ptr_factory_.GetWeakPtr());
+ ShowRequestAutocompleteDialog(
+ form, frame_url, autofill::DIALOG_TYPE_REQUEST_AUTOCOMPLETE, callback);
+}
+
+void AutofillManager::ReturnAutocompleteResult(
+ WebFormElement::AutocompleteResult result, const FormData& form_data) {
+ // driver_->GetWebContents() will be NULL when the interactive autocomplete
+ // is closed due to a tab or browser window closing.
+ if (!driver_->GetWebContents())
+ return;
+
+ RenderViewHost* host = driver_->GetWebContents()->GetRenderViewHost();
+ if (!host)
+ return;
+
+ host->Send(new AutofillMsg_RequestAutocompleteResult(host->GetRoutingID(),
+ result,
+ form_data));
+}
+
+void AutofillManager::ReturnAutocompleteData(
+ const FormStructure* result,
+ const std::string& unused_transaction_id) {
+ 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_);
+
+ if (page_meta_data->IsInAutofillableFlow()) {
+ RenderViewHost* host = driver_->GetWebContents()->GetRenderViewHost();
+ if (host)
+ host->Send(new AutofillMsg_AutocheckoutSupported(host->GetRoutingID()));
+ }
+
+ // TODO(ahutter): Remove this once Autocheckout is implemented on other
+ // platforms. See http://crbug.com/173416.
+#if defined(TOOLKIT_VIEWS)
+ if (!GetAutocheckoutURLPrefix().empty())
+ autocheckout_manager_.OnLoadedPageMetaData(page_meta_data.Pass());
+#endif // #if defined(TOOLKIT_VIEWS)
+
+ // If the corresponding flag is set, annotate forms with the predicted types.
+ driver_->SendAutofillTypePredictionsToRenderer(form_structures_.get());
+}
+
+void AutofillManager::OnDidEndTextFieldEditing() {
+ external_delegate_->DidEndTextFieldEditing();
+}
+
+void AutofillManager::OnAutocheckoutPageCompleted(
+ autofill::AutocheckoutStatus status) {
+ autocheckout_manager_.OnAutocheckoutPageCompleted(status);
+}
+
+std::string AutofillManager::GetAutocheckoutURLPrefix() const {
+ if (!driver_->GetWebContents())
+ return std::string();
+
+ autofill::autocheckout::WhitelistManager* whitelist_manager =
+ manager_delegate_->GetAutocheckoutWhitelistManager();
+
+ return whitelist_manager ? whitelist_manager->GetMatchedURLPrefix(
+ driver_->GetWebContents()->GetURL()) : std::string();
+}
+
+bool AutofillManager::IsAutofillEnabled() const {
+ return manager_delegate_->GetPrefs()->GetBoolean(prefs::kAutofillEnabled);
+}
+
+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::OnMaybeShowAutocheckoutBubble(
+ const FormData& form,
+ const gfx::RectF& bounding_box) {
+ if (!IsAutofillEnabled())
+ return;
+
+ // Don't show bubble if corresponding FormStructure doesn't have anything to
+ // autofill.
+ FormStructure* cached_form;
+ if (!FindCachedForm(form, &cached_form))
+ return;
+
+ // Don't offer Autocheckout bubble if Autofill server is not aware of this
+ // form in the context of Autocheckout experiment.
+ if (!HasServerSpecifiedFieldTypes(*cached_form))
+ return;
+
+ autocheckout_manager_.MaybeShowAutocheckoutBubble(form.origin, bounding_box);
+}
+
+void AutofillManager::UploadFormData(const FormStructure& submitted_form) {
+ if (!download_manager_)
+ 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;
+ }
+
+ ServerFieldTypeSet 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();
+ external_delegate_->Reset();
+}
+
+AutofillManager::AutofillManager(AutofillDriver* driver,
+ autofill::AutofillManagerDelegate* delegate,
+ PersonalDataManager* personal_data)
+ : driver_(driver),
+ manager_delegate_(delegate),
+ app_locale_("en-US"),
+ personal_data_(personal_data),
+ autocomplete_history_manager_(
+ new AutocompleteHistoryManager(driver, delegate)),
+ 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),
+ external_delegate_(NULL),
+ test_delegate_(NULL),
+ weak_ptr_factory_(this) {
+ DCHECK(driver_);
+ DCHECK(driver_->GetWebContents());
+ DCHECK(manager_delegate_);
+}
+
+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_->GetCreditCards().empty()) {
+ return false;
+ }
+
+ if (!driver_->RendererIsAvailable())
+ return false;
+
+ *host = driver_->GetWebContents()->GetRenderViewHost();
+ return true;
+}
+
+bool AutofillManager::GetProfileOrCreditCard(
+ int unique_id,
+ const AutofillDataModel** data_model,
+ 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)) {
+ *data_model = personal_data_->GetProfileByGUID(profile_guid.first);
+ *variant = profile_guid.second;
+ } else if (base::IsValidGUID(credit_card_guid.first)) {
+ *data_model = personal_data_->GetCreditCardByGUID(credit_card_guid.first);
+ *variant = credit_card_guid.second;
+ }
+
+ return !!*data_model;
+}
+
+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<base::string16, const AutofillField*> cached_fields;
+ for (size_t i = 0; i < cached_form->field_count(); ++i) {
+ const AutofillField* field = cached_form->field(i);
+ cached_fields[field->unique_name()] = field;
+ }
+
+ for (size_t i = 0; i < (*updated_form)->field_count(); ++i) {
+ AutofillField* field = (*updated_form)->field(i);
+ std::map<base::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);
+ driver_->SendAutofillTypePredictionsToRenderer(forms);
+
+ return true;
+}
+
+void AutofillManager::GetProfileSuggestions(
+ FormStructure* form,
+ const FormFieldData& field,
+ const AutofillType& type,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::string16>* icons,
+ std::vector<int>* unique_ids) const {
+ std::vector<ServerFieldType> field_types(form->field_count());
+ for (size_t i = 0; i < form->field_count(); ++i) {
+ field_types.push_back(form->field(i)->Type().GetStorableType());
+ }
+ 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,
+ const AutofillType& type,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::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 (form_structures_.empty()) {
+ // Call OnLoadedPageMetaData with no page metadata immediately if there is
+ // no form in the page. This give |autocheckout_manager| a chance to
+ // terminate Autocheckout and send Autocheckout status.
+ autocheckout_manager_.OnLoadedPageMetaData(
+ scoped_ptr<autofill::AutocheckoutPageMetaData>());
+ } else if (download_manager_) {
+ // Query the server if we have at least one of the forms were parsed.
+ 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.
+ driver_->SendAutofillTypePredictionsToRenderer(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;
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h
new file mode 100644
index 00000000000..b9e4d311522
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_manager.h
@@ -0,0 +1,407 @@
+// 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_CORE_BROWSER_AUTOFILL_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_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/strings/string16.h"
+#include "base/time/time.h"
+#include "components/autofill/content/browser/autocheckout_manager.h"
+#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/autofill_download.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/forms_seen_state.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+
+class GURL;
+
+namespace content {
+class RenderViewHost;
+class WebContents;
+}
+
+namespace gfx {
+class Rect;
+class RectF;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace autofill {
+
+class AutofillDriver;
+class AutofillDataModel;
+class AutofillDownloadManager;
+class AutofillExternalDelegate;
+class AutofillField;
+class AutofillManagerDelegate;
+class AutofillManagerTestDelegate;
+class AutofillMetrics;
+class AutofillProfile;
+class AutofillType;
+class CreditCard;
+class FormStructureBrowserTest;
+
+struct FormData;
+struct FormFieldData;
+struct PasswordFormFillData;
+
+// Manages saving and restoring the user's personal information entered into web
+// forms.
+class AutofillManager : public AutofillDownloadManager::Observer {
+ public:
+ enum AutofillDownloadManagerState {
+ ENABLE_AUTOFILL_DOWNLOAD_MANAGER,
+ DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
+ };
+
+ // Registers our Enable/Disable Autofill pref.
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ AutofillManager(AutofillDriver* driver,
+ autofill::AutofillManagerDelegate* delegate,
+ const std::string& app_locale,
+ AutofillDownloadManagerState enable_download_manager);
+ virtual ~AutofillManager();
+
+ // Sets an external delegate.
+ void SetExternalDelegate(AutofillExternalDelegate* delegate);
+
+ // 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();
+
+ // 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 base::string16& name,
+ const base::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,
+ autofill::DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback);
+
+ // Happens when the autocomplete dialog runs its callback when being closed.
+ void RequestAutocompleteDialogClosed();
+
+ autofill::AutofillManagerDelegate* delegate() const {
+ return manager_delegate_;
+ }
+
+ const std::string& app_locale() const { return app_locale_; }
+
+ // Only for testing.
+ void SetTestDelegate(autofill::AutofillManagerTestDelegate* delegate);
+
+ void OnFormsSeen(const std::vector<FormData>& forms,
+ const base::TimeTicks& timestamp,
+ autofill::FormsSeenState state);
+
+ // Processes the submitted |form|, saving any new Autofill data and uploading
+ // the possible field types for the submitted fields to the crowdsourcing
+ // server. Returns false if this form is not relevant for Autofill.
+ bool OnFormSubmitted(const FormData& form,
+ 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 OnHideAutofillUI();
+ void OnAddPasswordFormMapping(
+ const FormFieldData& form,
+ const PasswordFormFillData& fill_data);
+ void OnShowPasswordSuggestions(
+ const FormFieldData& field,
+ const gfx::RectF& bounds,
+ const std::vector<base::string16>& suggestions,
+ const std::vector<base::string16>& realms);
+ void OnSetDataList(const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels);
+
+ // Requests an interactive autocomplete UI be shown.
+ void OnRequestAutocomplete(const FormData& form,
+ const GURL& frame_url);
+
+ // Called to signal a page is completed in renderer in the Autocheckout flow.
+ void OnAutocheckoutPageCompleted(autofill::AutocheckoutStatus status);
+
+ // Shows the Autocheckout bubble if conditions are right. See comments for
+ // AutocheckoutManager::MaybeShowAutocheckoutBubble. Input element requesting
+ // bubble belongs to |form|. |bounding_box| is the bounding box of the input
+ // field in focus.
+ virtual void OnMaybeShowAutocheckoutBubble(const FormData& form,
+ const gfx::RectF& bounding_box);
+
+ // Resets cache.
+ virtual void Reset();
+
+ autofill::AutocheckoutManager* autocheckout_manager() {
+ return &autocheckout_manager_;
+ }
+
+ protected:
+ // Test code should prefer to use this constructor.
+ AutofillManager(AutofillDriver* driver,
+ 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);
+
+ // 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_;
+ }
+
+ // Tell the renderer the current interactive autocomplete finished.
+ virtual void ReturnAutocompleteResult(
+ WebKit::WebFormElement::AutocompleteResult result,
+ const FormData& form_data);
+
+ private:
+
+ // AutofillDownloadManager::Observer:
+ virtual void OnLoadedServerPredictions(
+ const std::string& response_xml) OVERRIDE;
+
+ // Passes return data for an OnRequestAutocomplete call back to the page.
+ void ReturnAutocompleteData(const FormStructure* result,
+ const std::string& unused_transaction_id);
+
+ // Returns the matched whitelist URL prefix for the current tab's url.
+ virtual 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 AutofillDataModel** data_model,
+ 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,
+ const AutofillType& type,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::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,
+ const AutofillType& type,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::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);
+
+ // Provides driver-level context to the shared code of the component. Must
+ // outlive this object.
+ AutofillDriver* driver_;
+
+ autofill::AutofillManagerDelegate* const manager_delegate_;
+
+ std::string app_locale_;
+
+ // 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. Will be NULL if
+ // the download manager functionality is disabled.
+ scoped_ptr<AutofillDownloadManager> download_manager_;
+
+ // Handles single-field autocomplete form data.
+ scoped_ptr<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_;
+
+ // 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_;
+
+ // Delegate used in test to get notifications on certain events.
+ autofill::AutofillManagerTestDelegate* test_delegate_;
+
+ base::WeakPtrFactory<AutofillManager> weak_ptr_factory_;
+
+ friend class AutofillManagerTest;
+ friend class autofill::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);
+ FRIEND_TEST_ALL_PREFIXES(AutofillManagerTest,
+ FormSubmittedAutocompleteEnabled);
+ DISALLOW_COPY_AND_ASSIGN(AutofillManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/autofill_manager_delegate.h b/chromium/components/autofill/core/browser/autofill_manager_delegate.h
new file mode 100644
index 00000000000..374c105fd5d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_manager_delegate.h
@@ -0,0 +1,149 @@
+// 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_CORE_BROWSER_AUTOFILL_MANAGER_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_MANAGER_DELEGATE_H_
+
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/i18n/rtl.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "components/autofill/content/browser/autocheckout_steps.h"
+#include "components/autofill/core/browser/autocheckout_bubble_state.h"
+
+namespace content {
+struct PasswordForm;
+}
+
+namespace gfx {
+class Rect;
+class RectF;
+}
+
+class GURL;
+class InfoBarService;
+class PrefService;
+
+namespace autofill {
+
+class AutofillMetrics;
+class AutofillPopupDelegate;
+class CreditCard;
+class FormStructure;
+class PasswordGenerator;
+class PersonalDataManager;
+struct FormData;
+
+namespace autocheckout {
+class WhitelistManager;
+}
+
+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;
+
+ // Gets the autocheckout::WhitelistManager instance associated with the
+ // delegate.
+ virtual autocheckout::WhitelistManager*
+ GetAutocheckoutWhitelistManager() const = 0;
+
+ // Hides the associated request autocomplete dialog (if it exists).
+ virtual void HideRequestAutocompleteDialog() = 0;
+
+ // Causes an error explaining that Autocheckout has failed to be displayed to
+ // the user.
+ virtual void OnAutocheckoutError() = 0;
+
+ // Called when an Autocheckout flow has succeeded. Causes a notification
+ // explaining that they must confirm their purchase to be displayed to the
+ // user.
+ virtual void OnAutocheckoutSuccess() = 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 Autocheckout bubble UI to be displayed. |bounding_box| is the
+ // anchor for the bubble. |is_google_user| is whether or not the user is
+ // logged into or has been logged into accounts.google.com. |callback| is run
+ // if the bubble is accepted. The returned boolean informs the caller whether
+ // or not the bubble is successfully shown.
+ virtual bool ShowAutocheckoutBubble(
+ const gfx::RectF& bounding_box,
+ bool is_google_user,
+ const base::Callback<void(AutocheckoutBubbleState)>& callback) = 0;
+
+ // Causes the dialog for request autocomplete feature to be shown.
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback) = 0;
+
+ // Hide the Autocheckout bubble if one is currently showing.
+ virtual void HideAutocheckoutBubble() = 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,
+ base::i18n::TextDirection text_direction,
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels,
+ const std::vector<base::string16>& icons,
+ const std::vector<int>& identifiers,
+ base::WeakPtr<AutofillPopupDelegate> delegate) = 0;
+
+ // Update the data list values shown by the Autofill popup, if visible.
+ virtual void UpdateAutofillPopupDataListValues(
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) = 0;
+
+ // Hide the Autofill popup if one is currently showing.
+ virtual void HideAutofillPopup() = 0;
+
+ // Whether the Autocomplete feature of Autofill should be enabled.
+ virtual bool IsAutocompleteEnabled() = 0;
+
+ // Update progress of the Autocheckout flow as displayed to the user.
+ virtual void AddAutocheckoutStep(AutocheckoutStepType step_type) = 0;
+ virtual void UpdateAutocheckoutStep(
+ AutocheckoutStepType step_type,
+ AutocheckoutStepStatus step_status) = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_MANAGER_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/autofill_manager_test_delegate.h b/chromium/components/autofill/core/browser/autofill_manager_test_delegate.h
new file mode 100644
index 00000000000..222a8df2925
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_manager_test_delegate.h
@@ -0,0 +1,26 @@
+// 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_CORE_BROWSER_AUTOFILL_MANAGER_TEST_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_MANAGER_TEST_DELEGATE_H_
+
+namespace autofill {
+
+class AutofillManagerTestDelegate {
+ public:
+ virtual ~AutofillManagerTestDelegate() {}
+
+ // Called when a form is previewed with Autofill suggestions.
+ virtual void DidPreviewFormData() = 0;
+
+ // Called when a form is filled with Autofill suggestions.
+ virtual void DidFillFormData() = 0;
+
+ // Called when a popup with Autofill suggestions is shown.
+ virtual void DidShowSuggestions() = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_MANAGER_TEST_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
new file mode 100644
index 00000000000..7aeb81ec6d5
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -0,0 +1,3271 @@
+// 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 <algorithm>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/tuple.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/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/browser/test_autofill_external_delegate.h"
+#include "components/autofill/core/browser/test_autofill_manager_delegate.h"
+#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/forms_seen_state.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_utils.h"
+#include "grit/component_strings.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/public/web/WebAutofillClient.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/rect.h"
+#include "url/gurl.h"
+
+using content::WebContents;
+using testing::_;
+using WebKit::WebFormElement;
+
+namespace autofill {
+
+typedef PersonalDataManager::GUIDPair GUIDPair;
+
+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;
+
+class TestPersonalDataManager : public PersonalDataManager {
+ public:
+ TestPersonalDataManager() : PersonalDataManager("en-US") {
+ 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 BrowserContextKeyedService* Build(content::BrowserContext* 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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 3 fields and a field with autocomplete attribute.
+void CreateTestFormWithAutocompleteAttribute(FormData* form) {
+ form->name = ASCIIToUTF16("UserSpecified");
+ form->method = ASCIIToUTF16("POST");
+ form->origin = GURL("http://myform.com/userspecified.html");
+ form->action = GURL("http://myform.com/submit.html");
+ form->user_submitted = true;
+
+ FormFieldData field;
+ test::CreateTestFormField("First Name", "firstname", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Middle Name", "middlename", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Last Name", "lastname", "", "text", &field);
+ form->fields.push_back(field);
+ field.autocomplete_attribute="cc-type";
+ test::CreateTestFormField("cc-type", "cc-type", "", "text", &field);
+ form->fields.push_back(field);
+}
+
+// Populates |form| with data corresponding to a simple shipping options form.
+void CreateTestShippingOptionsFormData(FormData* form) {
+ form->name = ASCIIToUTF16("Shipping Options");
+ form->method = ASCIIToUTF16("POST");
+ form->origin = GURL("http://myform.com/shipping.html");
+ form->action = GURL("http://myform.com/submit.html");
+ form->user_submitted = true;
+
+ FormFieldData field;
+ test::CreateTestFormField("Shipping1", "option", "option1", "radio", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Shipping2", "option", "option2", "radio", &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;
+ test::CreateTestFormField("Name on Card", "nameoncard", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
+ form->fields.push_back(field);
+ if (use_month_type) {
+ test::CreateTestFormField(
+ "Expiration Date", "ccmonth", "", "month", &field);
+ form->fields.push_back(field);
+ } else {
+ test::CreateTestFormField("Expiration Date", "ccmonth", "", "text", &field);
+ form->fields.push_back(field);
+ test::CreateTestFormField("", "ccyear", "", "text", &field);
+ form->fields.push_back(field);
+ }
+}
+
+void ExpectSuggestions(int page_id,
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels,
+ const std::vector<base::string16>& icons,
+ const std::vector<int>& unique_ids,
+ int expected_page_id,
+ size_t expected_num_suggestions,
+ const base::string16 expected_values[],
+ const base::string16 expected_labels[],
+ const base::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(base::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 MockAutocompleteHistoryManager : public AutocompleteHistoryManager {
+ public:
+ MockAutocompleteHistoryManager(AutofillDriver* driver,
+ AutofillManagerDelegate* delegate)
+ : AutocompleteHistoryManager(driver, delegate) {}
+
+ MOCK_METHOD1(OnFormSubmitted, void(const FormData& form));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutocompleteHistoryManager);
+};
+
+class MockAutofillDriver : public TestAutofillDriver {
+ public:
+ explicit MockAutofillDriver(content::WebContents* web_contents)
+ : TestAutofillDriver(web_contents) {}
+
+ // Mock methods to enable testability.
+ MOCK_METHOD2(SendFormDataToRenderer, void(int query_id,
+ const FormData& data));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillDriver);
+};
+
+class TestAutofillManager : public AutofillManager {
+ public:
+ TestAutofillManager(AutofillDriver* driver,
+ autofill::AutofillManagerDelegate* delegate,
+ TestPersonalDataManager* personal_data)
+ : AutofillManager(driver, delegate, personal_data),
+ personal_data_(personal_data),
+ autofill_enabled_(true) {}
+ virtual ~TestAutofillManager() {}
+
+ virtual bool IsAutofillEnabled() const OVERRIDE { return autofill_enabled_; }
+
+ void set_autofill_enabled(bool autofill_enabled) {
+ autofill_enabled_ = autofill_enabled;
+ }
+
+ void set_autocheckout_url_prefix(const std::string& autocheckout_url_prefix) {
+ autocheckout_url_prefix_ = autocheckout_url_prefix;
+ }
+
+ virtual std::string GetAutocheckoutURLPrefix() const OVERRIDE {
+ return autocheckout_url_prefix_;
+ }
+
+ 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<ServerFieldTypeSet>& 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 {
+ message_loop_runner_->Quit();
+
+ // 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(
+ base::StringPrintf(
+ "Field %d with value %s", static_cast<int>(i),
+ UTF16ToUTF8(submitted_form->field(i)->value).c_str()));
+ const ServerFieldTypeSet& possible_types =
+ submitted_form->field(i)->possible_types();
+ EXPECT_EQ(expected_submitted_field_types_[i].size(),
+ possible_types.size());
+ for (ServerFieldTypeSet::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(*it).ToString();
+ }
+ }
+ }
+
+ AutofillManager::UploadFormDataAsyncCallback(submitted_form,
+ load_time,
+ interaction_time,
+ submission_time);
+ }
+
+ virtual void OnMaybeShowAutocheckoutBubble(
+ const FormData& form,
+ const gfx::RectF& bounding_box) OVERRIDE {
+ AutofillManager::OnMaybeShowAutocheckoutBubble(form, bounding_box);
+ // Needed for AutocheckoutManager to post task on IO thread.
+ content::RunAllPendingInMessageLoop(content::BrowserThread::IO);
+ }
+
+ // Resets the MessageLoopRunner so that it can wait for an asynchronous form
+ // submission to complete.
+ void ResetMessageLoopRunner() {
+ message_loop_runner_ = new content::MessageLoopRunner();
+ }
+
+ // Wait for the asynchronous OnFormSubmitted() call to complete.
+ void WaitForAsyncFormSubmit() {
+ message_loop_runner_->Run();
+ }
+
+ virtual void UploadFormData(const FormStructure& submitted_form) OVERRIDE {
+ submitted_form_signature_ = submitted_form.FormSignature();
+ }
+
+ 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);
+ }
+
+ void ClearFormStructures() {
+ form_structures()->clear();
+ }
+
+ virtual void ReturnAutocompleteResult(
+ WebFormElement::AutocompleteResult result,
+ const FormData& form_data) OVERRIDE {
+ request_autocomplete_results_.push_back(std::make_pair(result, form_data));
+ }
+
+ // Set autocheckout manager's page meta data to first page on Autocheckout
+ // flow.
+ void MarkAsFirstPageInAutocheckoutFlow() {
+ scoped_ptr<AutocheckoutPageMetaData> start_of_flow(
+ new AutocheckoutPageMetaData());
+ start_of_flow->current_page_number = 0;
+ start_of_flow->total_pages = 3;
+ WebElementDescriptor* proceed_element =
+ &start_of_flow->proceed_element_descriptor;
+ proceed_element->descriptor = "#foo";
+ proceed_element->retrieval_method = WebElementDescriptor::ID;
+ autocheckout_manager()->OnLoadedPageMetaData(start_of_flow.Pass());
+ }
+
+ // Set autocheckout manager's page meta data to first page on Autocheckout
+ // flow.
+ void MarkAsFirstPageInAutocheckoutFlowIgnoringAjax() {
+ scoped_ptr<AutocheckoutPageMetaData> start_of_flow(
+ new AutocheckoutPageMetaData());
+ start_of_flow->current_page_number = 0;
+ start_of_flow->total_pages = 3;
+ start_of_flow->ignore_ajax = true;
+ WebElementDescriptor* proceed_element =
+ &start_of_flow->proceed_element_descriptor;
+ proceed_element->descriptor = "#foo";
+ proceed_element->retrieval_method = WebElementDescriptor::ID;
+ autocheckout_manager()->OnLoadedPageMetaData(start_of_flow.Pass());
+ }
+
+ private:
+ // Weak reference.
+ TestPersonalDataManager* personal_data_;
+
+ bool autofill_enabled_;
+ std::vector<std::pair<WebFormElement::AutocompleteResult, FormData> >
+ request_autocomplete_results_;
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+
+ std::string autocheckout_url_prefix_;
+ std::string submitted_form_signature_;
+ std::vector<ServerFieldTypeSet> expected_submitted_field_types_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillManager);
+};
+
+class TestAutofillExternalDelegate : public AutofillExternalDelegate {
+ public:
+ explicit TestAutofillExternalDelegate(content::WebContents* web_contents,
+ AutofillManager* autofill_manager,
+ AutofillDriver* autofill_driver)
+ : AutofillExternalDelegate(web_contents, autofill_manager,
+ autofill_driver),
+ on_query_seen_(false),
+ on_suggestions_returned_seen_(false) {}
+ virtual ~TestAutofillExternalDelegate() {}
+
+ virtual void OnQuery(int query_id,
+ const FormData& form,
+ const FormFieldData& field,
+ const gfx::RectF& bounds,
+ bool display_warning) OVERRIDE {
+ on_query_seen_ = true;
+ on_suggestions_returned_seen_ = false;
+ }
+
+ virtual void OnSuggestionsReturned(
+ int query_id,
+ const std::vector<base::string16>& autofill_values,
+ const std::vector<base::string16>& autofill_labels,
+ const std::vector<base::string16>& autofill_icons,
+ const std::vector<int>& autofill_unique_ids) OVERRIDE {
+ on_suggestions_returned_seen_ = true;
+
+ query_id_ = query_id;
+ autofill_values_ = autofill_values;
+ autofill_labels_ = autofill_labels;
+ autofill_icons_ = autofill_icons;
+ autofill_unique_ids_ = autofill_unique_ids;
+ }
+
+ void CheckSuggestions(int expected_page_id,
+ size_t expected_num_suggestions,
+ const base::string16 expected_values[],
+ const base::string16 expected_labels[],
+ const base::string16 expected_icons[],
+ const int expected_unique_ids[]) {
+ // Ensure that these results are from the most recent query.
+ EXPECT_TRUE(on_suggestions_returned_seen_);
+
+ EXPECT_EQ(expected_page_id, query_id_);
+ ASSERT_EQ(expected_num_suggestions, autofill_values_.size());
+ ASSERT_EQ(expected_num_suggestions, autofill_labels_.size());
+ ASSERT_EQ(expected_num_suggestions, autofill_icons_.size());
+ ASSERT_EQ(expected_num_suggestions, autofill_unique_ids_.size());
+ for (size_t i = 0; i < expected_num_suggestions; ++i) {
+ SCOPED_TRACE(base::StringPrintf("i: %" PRIuS, i));
+ EXPECT_EQ(expected_values[i], autofill_values_[i]);
+ EXPECT_EQ(expected_labels[i], autofill_labels_[i]);
+ EXPECT_EQ(expected_icons[i], autofill_icons_[i]);
+ EXPECT_EQ(expected_unique_ids[i], autofill_unique_ids_[i]);
+ }
+ }
+
+ bool on_query_seen() const {
+ return on_query_seen_;
+ }
+
+ bool on_suggestions_returned_seen() const {
+ return on_suggestions_returned_seen_;
+ }
+
+ private:
+ // Records if OnQuery has been called yet.
+ bool on_query_seen_;
+
+ // Records if OnSuggestionsReturned has been called after the most recent
+ // call to OnQuery.
+ bool on_suggestions_returned_seen_;
+
+ // The query id of the most recent Autofill query.
+ int query_id_;
+
+ // The results returned by the most recent Autofill query.
+ std::vector<base::string16> autofill_values_;
+ std::vector<base::string16> autofill_labels_;
+ std::vector<base::string16> autofill_icons_;
+ std::vector<int> autofill_unique_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate);
+};
+
+} // namespace
+
+class AutofillManagerTest : public ChromeRenderViewHostTestHarness {
+ public:
+ virtual void SetUp() OVERRIDE {
+ ChromeRenderViewHostTestHarness::SetUp();
+
+ autofill::PersonalDataManagerFactory::GetInstance()->SetTestingFactory(
+ profile(), TestPersonalDataManager::Build);
+
+
+ autofill::TabAutofillManagerDelegate::CreateForWebContents(web_contents());
+
+ personal_data_.SetBrowserContext(profile());
+ autofill_driver_.reset(new MockAutofillDriver(web_contents()));
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(),
+ autofill::TabAutofillManagerDelegate::FromWebContents(web_contents()),
+ &personal_data_));
+
+ external_delegate_.reset(new TestAutofillExternalDelegate(
+ web_contents(),
+ autofill_manager_.get(),
+ autofill_driver_.get()));
+ autofill_manager_->SetExternalDelegate(external_delegate_.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();
+ autofill_driver_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+
+ // Remove the BrowserContext so TestPersonalDataManager does not need to
+ // care about removing self as an observer in destruction.
+ personal_data_.SetBrowserContext(NULL);
+ }
+
+ 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<base::string16>& result) {
+ autofill_manager_->autocomplete_history_manager_->SendSuggestions(&result);
+ }
+
+ void FormsSeen(const std::vector<FormData>& forms) {
+ autofill_manager_->OnFormsSeen(forms, base::TimeTicks(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+ }
+
+ void PartialFormsSeen(const std::vector<FormData>& forms) {
+ autofill_manager_->OnFormsSeen(forms, base::TimeTicks(),
+ autofill::PARTIAL_FORMS_SEEN);
+ }
+
+ void DynamicFormsSeen(const std::vector<FormData>& forms) {
+ autofill_manager_->OnFormsSeen(forms, base::TimeTicks(),
+ autofill::DYNAMIC_FORMS_SEEN);
+ }
+
+ void FormSubmitted(const FormData& form) {
+ autofill_manager_->ResetMessageLoopRunner();
+ 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);
+ }
+
+ // Calls |autofill_manager_->OnFillAutofillFormData()| with the specified
+ // input parameters after setting up the expectation that the mock driver's
+ // |SendFormDataToRenderer()| method will be called and saving the parameters
+ // of that call into the |response_query_id| and |response_data| output
+ // parameters.
+ void FillAutofillFormDataAndSaveResults(int input_query_id,
+ const FormData& input_form,
+ const FormFieldData& input_field,
+ int unique_id,
+ int* response_query_id,
+ FormData* response_data) {
+ EXPECT_CALL(*autofill_driver_, SendFormDataToRenderer(_, _)).
+ WillOnce((DoAll(testing::SaveArg<0>(response_query_id),
+ testing::SaveArg<1>(response_data))));
+ FillAutofillFormData(input_query_id, input_form, input_field, unique_id);
+ }
+
+ int PackGUIDs(const GUIDPair& cc_guid, const GUIDPair& profile_guid) const {
+ return autofill_manager_->PackGUIDs(cc_guid, profile_guid);
+ }
+
+
+ bool HasSeenAutofillGetAllFormsMessage() {
+ const uint32 kMsgID = AutofillMsg_GetAllForms::ID;
+ const IPC::Message* message =
+ process()->sink().GetFirstMessageMatching(kMsgID);
+ return message != NULL;
+ }
+
+ protected:
+ scoped_ptr<MockAutofillDriver> autofill_driver_;
+ scoped_ptr<TestAutofillManager> autofill_manager_;
+ scoped_ptr<TestAutofillExternalDelegate> external_delegate_;
+ 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_;
+};
+
+class TestFormStructure : public FormStructure {
+ public:
+ explicit TestFormStructure(const FormData& form)
+ : FormStructure(form, std::string()) {}
+ virtual ~TestFormStructure() {}
+
+ void SetFieldTypes(const std::vector<ServerFieldType>& heuristic_types,
+ const std::vector<ServerFieldType>& 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 browser asks for all forms when Autocheckout is enabled.
+TEST_F(AutofillManagerTest, GetAllForms) {
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ // Enable autocheckout.
+ autofill_manager_->set_autocheckout_url_prefix("test-prefix");
+
+ PartialFormsSeen(forms);
+
+ ASSERT_TRUE(HasSeenAutofillGetAllFormsMessage());
+}
+
+// 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;
+ test::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ // Inferred labels include full first relevant field, which in this case is
+ // the address line 1.
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ base::string16 expected_icons[] = {base::string16(), base::string16()};
+ int expected_unique_ids[] = {1, 2};
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// Test that in the case of Autocheckout, forms seen are in order supplied.
+TEST_F(AutofillManagerTest, AutocheckoutFormsSeen) {
+ FormData shipping_options;
+ CreateTestShippingOptionsFormData(&shipping_options);
+ FormData user_supplied;
+ CreateTestFormWithAutocompleteAttribute(&user_supplied);
+ FormData address;
+ test::CreateTestAddressFormData(&address);
+
+ // Push user_supplied before address and observe order changing when
+ // Autocheckout is not enabled..
+ std::vector<FormData> forms;
+ forms.push_back(shipping_options);
+ forms.push_back(user_supplied);
+ forms.push_back(address);
+
+ // Test without enabling Autocheckout. FormStructure should only contain
+ // form1. Shipping Options form will not qualify as parsable form.
+ FormsSeen(forms);
+ std::vector<FormStructure*> form_structures;
+ form_structures = autofill_manager_->GetFormStructures();
+ ASSERT_EQ(2U, form_structures.size());
+ EXPECT_EQ("/form.html", form_structures[0]->source_url().path());
+ EXPECT_EQ("/userspecified.html", form_structures[1]->source_url().path());
+ autofill_manager_->ClearFormStructures();
+
+ // Test after enabling Autocheckout. Order should be shipping_options,
+ // userspecified and then address form.
+ autofill_manager_->set_autocheckout_url_prefix("yes-autocheckout");
+ FormsSeen(forms);
+ form_structures = autofill_manager_->GetFormStructures();
+ ASSERT_EQ(3U, form_structures.size());
+ EXPECT_EQ("/shipping.html", form_structures[0]->source_url().path());
+ EXPECT_EQ("/userspecified.html", form_structures[1]->source_url().path());
+ EXPECT_EQ("/form.html", form_structures[2]->source_url().path());
+}
+
+// Test that in the case of Autocheckout, forms seen are in order supplied.
+TEST_F(AutofillManagerTest, DynamicFormsSeen) {
+ FormData shipping_options;
+ CreateTestShippingOptionsFormData(&shipping_options);
+ FormData user_supplied;
+ CreateTestFormWithAutocompleteAttribute(&user_supplied);
+ FormData address;
+ test::CreateTestAddressFormData(&address);
+
+ autofill_manager_->set_autocheckout_url_prefix("test-prefix");
+ // Push user_supplied only
+ std::vector<FormData> forms;
+ forms.push_back(user_supplied);
+
+ // Make sure normal form is handled correctly.
+ FormsSeen(forms);
+ std::vector<FormStructure*> form_structures;
+ form_structures = autofill_manager_->GetFormStructures();
+ ASSERT_EQ(1U, form_structures.size());
+ EXPECT_EQ("/userspecified.html", form_structures[0]->source_url().path());
+
+ // Push other forms
+ forms.push_back(shipping_options);
+ forms.push_back(address);
+
+ // FormStructure should contain three and only three forms. Otherwise, it
+ // would indicate that the manager didn't reset upon being notified of
+ // the new forms;
+ DynamicFormsSeen(forms);
+ form_structures = autofill_manager_->GetFormStructures();
+ ASSERT_EQ(3U, form_structures.size());
+ EXPECT_EQ("/userspecified.html", form_structures[0]->source_url().path());
+ EXPECT_EQ("/shipping.html", form_structures[1]->source_url().path());
+ EXPECT_EQ("/form.html", form_structures[2]->source_url().path());
+}
+
+// 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;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ FormFieldData field;
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {ASCIIToUTF16("Elvis")};
+ base::string16 expected_labels[] = {ASCIIToUTF16("3734 Elvis Presley Blvd.")};
+ base::string16 expected_icons[] = {base::string16()};
+ int expected_unique_ids[] = {1};
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::CreateTestFormField("Username", "username", "", "text",&field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Password", "password", "", "password",&field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Quest", "quest", "", "quest", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Color", "color", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GetAutofillSuggestions(form, field);
+ EXPECT_FALSE(external_delegate_->on_suggestions_returned_seen());
+}
+
+// Test that we cull duplicate profile suggestions.
+TEST_F(AutofillManagerTest, GetProfileSuggestionsWithDuplicates) {
+ // Set up our form data.
+ FormData form;
+ test::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ base::string16 expected_icons[] = {base::string16(), base::string16()};
+ int expected_unique_ids[] = {1, 2};
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::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(external_delegate_->on_suggestions_returned_seen());
+}
+
+// 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;
+ test::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)
+ };
+ base::string16 expected_labels[] = {base::string16()};
+ base::string16 expected_icons[] = {base::string16()};
+ int expected_unique_ids[] =
+ {WebKit::WebAutofillClient::MenuItemIDWarningMessage};
+ external_delegate_->CheckSuggestions(
+ 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<base::string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("Jay"));
+ suggestions.push_back(ASCIIToUTF16("Jason"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ base::string16 expected_values2[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED),
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ base::string16 expected_labels2[] = { base::string16(), base::string16(),
+ base::string16()};
+ base::string16 expected_icons2[] = { base::string16(), base::string16(),
+ base::string16()};
+ int expected_unique_ids2[] = {-1, 0, 0};
+ external_delegate_->CheckSuggestions(
+ 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(external_delegate_->on_suggestions_returned_seen());
+}
+
+// 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("************3456"),
+ ASCIIToUTF16("************8765")
+ };
+ base::string16 expected_labels[] = { ASCIIToUTF16("*3456"),
+ ASCIIToUTF16("*8765")};
+ base::string16 expected_icons[] = {
+ ASCIIToUTF16(kVisaCard),
+ ASCIIToUTF16(kMasterCard)
+ };
+ int expected_unique_ids[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5)
+ };
+ external_delegate_->CheckSuggestions(
+ 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;
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {ASCIIToUTF16("************3456")};
+ base::string16 expected_labels[] = {ASCIIToUTF16("*3456")};
+ base::string16 expected_icons[] = {ASCIIToUTF16(kVisaCard)};
+ int expected_unique_ids[] = {autofill_manager_->GetPackedCreditCardID(4)};
+ external_delegate_->CheckSuggestions(
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis Presley"),
+ ASCIIToUTF16("Buddy Holly")
+ };
+ base::string16 expected_labels[] = { ASCIIToUTF16("*3456"),
+ ASCIIToUTF16("*8765") };
+ base::string16 expected_icons[] = {
+ ASCIIToUTF16(kVisaCard),
+ ASCIIToUTF16(kMasterCard)
+ };
+ int expected_unique_ids[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5)
+ };
+ external_delegate_->CheckSuggestions(
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION)
+ };
+ base::string16 expected_labels[] = {base::string16()};
+ base::string16 expected_icons[] = {base::string16()};
+ int expected_unique_ids[] = {-1};
+ external_delegate_->CheckSuggestions(
+ 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<base::string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("Jay"));
+ suggestions.push_back(ASCIIToUTF16("Jason"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ base::string16 expected_values2[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION),
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ base::string16 expected_labels2[] = { base::string16(), base::string16(),
+ base::string16() };
+ base::string16 expected_icons2[] = { base::string16(), base::string16(),
+ base::string16() };
+ int expected_unique_ids2[] = {-1, 0, 0};
+ external_delegate_->CheckSuggestions(
+ 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(external_delegate_->on_suggestions_returned_seen());
+}
+
+// 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;
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("************3456"),
+ ASCIIToUTF16("************8765"),
+ ASCIIToUTF16("************3456")
+ };
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("*3456"),
+ ASCIIToUTF16("*8765"),
+ ASCIIToUTF16("*3456"),
+ };
+ base::string16 expected_icons[] = {
+ ASCIIToUTF16(kVisaCard),
+ ASCIIToUTF16(kMasterCard),
+ ASCIIToUTF16(kMasterCard)
+ };
+ int expected_unique_ids[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5),
+ autofill_manager_->GetPackedCreditCardID(7)
+ };
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::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<base::string16>());
+
+ // Test that we sent the right address suggestions to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ base::string16 expected_icons[] = {base::string16(), base::string16()};
+ int expected_unique_ids[] = {1, 2};
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+
+ const int kPageID2 = 2;
+ 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<base::string16>());
+
+ // Test that we sent the credit card suggestions to the external delegate.
+ base::string16 expected_values2[] = {
+ ASCIIToUTF16("************3456"),
+ ASCIIToUTF16("************8765")
+ };
+ base::string16 expected_labels2[] = { ASCIIToUTF16("*3456"),
+ ASCIIToUTF16("*8765")};
+ base::string16 expected_icons2[] = {
+ ASCIIToUTF16(kVisaCard),
+ ASCIIToUTF16(kMasterCard)
+ };
+ int expected_unique_ids2[] = {
+ autofill_manager_->GetPackedCreditCardID(4),
+ autofill_manager_->GetPackedCreditCardID(5)
+ };
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::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<base::string16>());
+
+ // Test that we sent the right suggestions to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St.")
+ };
+ base::string16 expected_icons[] = {base::string16(), base::string16()};
+ int expected_unique_ids[] = {1, 2};
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values2[] = {
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION)
+ };
+ base::string16 expected_labels2[] = {base::string16()};
+ base::string16 expected_icons2[] = {base::string16()};
+ int expected_unique_ids2[] = {-1};
+ external_delegate_->CheckSuggestions(
+ 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(external_delegate_->on_suggestions_returned_seen());
+}
+
+// Test that we correctly combine autofill and autocomplete suggestions.
+TEST_F(AutofillManagerTest, GetCombinedAutofillAndAutocompleteSuggestions) {
+ // Set up our form data.
+ FormData form;
+ test::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<base::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 values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles"),
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("3734 Elvis Presley Blvd."),
+ ASCIIToUTF16("123 Apple St."),
+ base::string16(),
+ base::string16()
+ };
+ base::string16 expected_icons[] = { base::string16(), base::string16(),
+ base::string16(), base::string16()};
+ int expected_unique_ids[] = {1, 2, 0, 0};
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Charles")
+ };
+ base::string16 expected_labels[] = {base::string16(), base::string16()};
+ base::string16 expected_icons[] = {base::string16(), base::string16()};
+ int expected_unique_ids[] = {1, 2};
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::CreateTestAddressFormData(&form);
+ FormFieldData field;
+ 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<base::string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("one"));
+ suggestions.push_back(ASCIIToUTF16("two"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("one"),
+ ASCIIToUTF16("two")
+ };
+ base::string16 expected_labels[] = {base::string16(), base::string16()};
+ base::string16 expected_icons[] = {base::string16(), base::string16()};
+ int expected_unique_ids[] = {0, 0};
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // |profile| will be owned by the mock PersonalDataManager.
+ AutofillProfile* profile = new AutofillProfile;
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = { ASCIIToUTF16("Elvis") };
+ base::string16 expected_labels[] = { base::string16() };
+ base::string16 expected_icons[] = { base::string16() };
+ int expected_unique_ids[] = { 1 };
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ // |profile| will be owned by the mock PersonalDataManager.
+ AutofillProfile* profile = new AutofillProfile;
+ test::SetProfileInfo(profile, "Elvis", "", "Presley", "me@x.com", "",
+ "", "", "", "", "", "", "");
+ profile->set_guid("00000000-0000-0000-0000-000000000101");
+ std::vector<base::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Elvis"),
+ ASCIIToUTF16("Elena")
+ };
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("me@x.com"),
+ ASCIIToUTF16("me@x.com")
+ };
+ base::string16 expected_icons[] = { base::string16(), base::string16() };
+ int expected_unique_ids[] = { 1, 2 };
+ external_delegate_->CheckSuggestions(
+ 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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = { ASCIIToUTF16("Elena") };
+ base::string16 expected_labels[] = { ASCIIToUTF16("me@x.com") };
+ base::string16 expected_icons[] = { base::string16() };
+ int expected_unique_ids[] = { 2 };
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::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<base::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Travis"),
+ ASCIIToUTF16("Cynthia"),
+ ASCIIToUTF16("Zac")
+ };
+ base::string16 expected_labels[] = { base::string16(), base::string16(),
+ base::string16() };
+ base::string16 expected_icons[] = { base::string16(), base::string16(),
+ base::string16() };
+ int expected_unique_ids[] = { 1, 2, 3 };
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+TEST_F(AutofillManagerTest, GetProfileSuggestionsFancyPhone) {
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ AutofillProfile* profile = new AutofillProfile;
+ profile->set_guid("00000000-0000-0000-0000-000000000103");
+ std::vector<base::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<base::string16>());
+
+ // Test that we sent the right values to the external delegate.
+ base::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.
+ base::string16 expected_labels[] = {
+ ASCIIToUTF16("Elvis Aaron Presley"),
+ ASCIIToUTF16("Charles Hardin Holley"),
+ ASCIIToUTF16("Natty Bumppo"),
+ };
+ base::string16 expected_icons[] = { base::string16(), base::string16(),
+ base::string16()};
+ int expected_unique_ids[] = {1, 2, 3};
+ external_delegate_->CheckSuggestions(
+ 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;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, kDefaultPageID, false);
+}
+
+// Test that we correctly fill an address form from an auxiliary profile.
+TEST_F(AutofillManagerTest, FillAddressFormFromAuxiliaryProfile) {
+ personal_data_.ClearAutofillProfiles();
+ PrefService* prefs = user_prefs::UserPrefs::Get(profile());
+ prefs->SetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, true);
+ personal_data_.CreateTestAuxiliaryProfiles();
+
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+
+ GUIDPair guid("00000000-0000-0000-0000-000000000001", 0);
+ GUIDPair empty(std::string(), 0);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty), &response_page_id, &response_data);
+ ExpectFilledCreditCardFormElvis(
+ response_page_id, response_data, 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty), &response_page_id, &response_data);
+ ExpectFilledCreditCardYearMonthWithYearMonth(response_page_id, response_data,
+ 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty), &response_page_id, &response_data);
+ ExpectFilledCreditCardYearMonthWithYearMonth(response_page_id, response_data,
+ 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty), &response_page_id, &response_data);
+ ExpectFilledCreditCardYearMonthWithYearMonth(response_page_id, response_data,
+ 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(guid, empty), &response_page_id, &response_data);
+ ExpectFilledCreditCardYearMonthWithYearMonth(response_page_id, response_data,
+ 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;
+ test::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);
+ int response_page_id = 0;
+ FormData response_data;
+ {
+ SCOPED_TRACE("Address");
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, kDefaultPageID, true);
+ }
+
+ // Now fill the credit card data.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000004", 0);
+ response_page_id = 0;
+ {
+ FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields.back(),
+ PackGUIDs(guid2, empty), &response_page_id, &response_data);
+ SCOPED_TRACE("Credit card");
+ ExpectFilledCreditCardFormElvis(
+ response_page_id, response_data, 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;
+ test::CreateTestAddressFormData(&form);
+ const size_t kAddressFormSize = form.fields.size();
+ test::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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Address 1");
+ // The second address section should be empty.
+ ASSERT_EQ(response_data.fields.size(), 2*kAddressFormSize);
+ for (size_t i = kAddressFormSize; i < form.fields.size(); ++i) {
+ EXPECT_EQ(base::string16(), response_data.fields[i].value);
+ }
+
+ // The first address section should be filled with Elvis's data.
+ response_data.fields.resize(kAddressFormSize);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, 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);
+ response_page_id = 0;
+ FillAutofillFormDataAndSaveResults(
+ kPageID2, form, form.fields[kAddressFormSize + 9],
+ PackGUIDs(empty, guid2), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Address 2");
+ ASSERT_EQ(response_data.fields.size(), form.fields.size());
+
+ // The first address section should be empty.
+ ASSERT_EQ(response_data.fields.size(), 2*kAddressFormSize);
+ for (size_t i = 0; i < kAddressFormSize; ++i) {
+ EXPECT_EQ(base::string16(), response_data.fields[i].value);
+ }
+
+ // The second address section should be filled with Elvis's data.
+ FormData secondSection = response_data;
+ secondSection.fields.erase(secondSection.fields.begin(),
+ secondSection.fields.begin() + kAddressFormSize);
+ for (size_t i = 0; i < kAddressFormSize; ++i) {
+ // Restore the expected field names.
+ base::string16 name = secondSection.fields[i].name;
+ base::string16 original_name = name.substr(0, name.size() - 1);
+ secondSection.fields[i].name = original_name;
+ }
+ ExpectFilledAddressFormElvis(
+ response_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;
+
+ test::CreateTestFormField("", "country", "", "text", &field);
+ field.autocomplete_attribute = "section-billing country";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "firstname", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "lastname", "", "text", &field);
+ field.autocomplete_attribute = "family-name";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "address", "", "text", &field);
+ field.autocomplete_attribute = "section-billing address-line1";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "city", "", "text", &field);
+ field.autocomplete_attribute = "section-billing locality";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "state", "", "text", &field);
+ field.autocomplete_attribute = "section-billing region";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "zip", "", "text", &field);
+ field.autocomplete_attribute = "section-billing postal-code";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "ccname", "", "text", &field);
+ field.autocomplete_attribute = "section-billing cc-name";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "ccnumber", "", "text", &field);
+ field.autocomplete_attribute = "section-billing cc-number";
+ form.fields.push_back(field);
+
+ test::CreateTestFormField("", "ccexp", "", "text", &field);
+ field.autocomplete_attribute = "section-billing cc-exp";
+ form.fields.push_back(field);
+
+ 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[1],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Unnamed section");
+ EXPECT_EQ(kDefaultPageID, response_page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), response_data.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), response_data.method);
+ EXPECT_EQ(GURL("https://myform.com/form.html"), response_data.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), response_data.action);
+ EXPECT_TRUE(response_data.user_submitted);
+ ASSERT_EQ(11U, response_data.fields.size());
+
+ ExpectFilledField("", "country", "", "text", response_data.fields[0]);
+ ExpectFilledField("", "firstname", "Elvis", "text",
+ response_data.fields[1]);
+ ExpectFilledField("", "lastname", "Presley", "text",
+ response_data.fields[2]);
+ ExpectFilledField("", "address", "", "text", response_data.fields[3]);
+ ExpectFilledField("", "city", "", "text", response_data.fields[4]);
+ ExpectFilledField("", "state", "", "text", response_data.fields[5]);
+ ExpectFilledField("", "zip", "", "text", response_data.fields[6]);
+ ExpectFilledField("", "ccname", "", "text", response_data.fields[7]);
+ ExpectFilledField("", "ccnumber", "", "text", response_data.fields[8]);
+ ExpectFilledField("", "ccexp", "", "text", response_data.fields[9]);
+ ExpectFilledField("", "email", "theking@gmail.com", "text",
+ response_data.fields[10]);
+ }
+
+ // Fill the address portion of the billing section.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000001", 0);
+ response_page_id = 0;
+ FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields[0],
+ PackGUIDs(empty, guid2), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Billing address");
+ EXPECT_EQ(kPageID2, response_page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), response_data.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), response_data.method);
+ EXPECT_EQ(GURL("https://myform.com/form.html"), response_data.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), response_data.action);
+ EXPECT_TRUE(response_data.user_submitted);
+ ASSERT_EQ(11U, response_data.fields.size());
+
+ ExpectFilledField("", "country", "US", "text",
+ response_data.fields[0]);
+ ExpectFilledField("", "firstname", "", "text", response_data.fields[1]);
+ ExpectFilledField("", "lastname", "", "text", response_data.fields[2]);
+ ExpectFilledField("", "address", "3734 Elvis Presley Blvd.", "text",
+ response_data.fields[3]);
+ ExpectFilledField("", "city", "Memphis", "text", response_data.fields[4]);
+ ExpectFilledField("", "state", "Tennessee", "text",
+ response_data.fields[5]);
+ ExpectFilledField("", "zip", "38116", "text", response_data.fields[6]);
+ ExpectFilledField("", "ccname", "", "text", response_data.fields[7]);
+ ExpectFilledField("", "ccnumber", "", "text", response_data.fields[8]);
+ ExpectFilledField("", "ccexp", "", "text", response_data.fields[9]);
+ ExpectFilledField("", "email", "", "text", response_data.fields[10]);
+ }
+
+ // Fill the credit card portion of the billing section.
+ const int kPageID3 = 3;
+ GUIDPair guid3("00000000-0000-0000-0000-000000000004", 0);
+ response_page_id = 0;
+ FillAutofillFormDataAndSaveResults(
+ kPageID3, form, form.fields[form.fields.size() - 2],
+ PackGUIDs(guid3, empty), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Credit card");
+ EXPECT_EQ(kPageID3, response_page_id);
+ EXPECT_EQ(ASCIIToUTF16("MyForm"), response_data.name);
+ EXPECT_EQ(ASCIIToUTF16("POST"), response_data.method);
+ EXPECT_EQ(GURL("https://myform.com/form.html"), response_data.origin);
+ EXPECT_EQ(GURL("https://myform.com/submit.html"), response_data.action);
+ EXPECT_TRUE(response_data.user_submitted);
+ ASSERT_EQ(11U, response_data.fields.size());
+
+ ExpectFilledField("", "country", "", "text", response_data.fields[0]);
+ ExpectFilledField("", "firstname", "", "text", response_data.fields[1]);
+ ExpectFilledField("", "lastname", "", "text", response_data.fields[2]);
+ ExpectFilledField("", "address", "", "text", response_data.fields[3]);
+ ExpectFilledField("", "city", "", "text", response_data.fields[4]);
+ ExpectFilledField("", "state", "", "text", response_data.fields[5]);
+ ExpectFilledField("", "zip", "", "text", response_data.fields[6]);
+ ExpectFilledField("", "ccname", "Elvis Presley", "text",
+ response_data.fields[7]);
+ ExpectFilledField("", "ccnumber", "4234567890123456", "text",
+ response_data.fields[8]);
+ ExpectFilledField("", "ccexp", "04/2012", "text", response_data.fields[9]);
+ ExpectFilledField("", "email", "", "text", response_data.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;
+ test::CreateTestAddressFormData(&form);
+ FormFieldData field;
+ 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+
+ // The second email address should be filled.
+ EXPECT_EQ(ASCIIToUTF16("theking@gmail.com"),
+ response_data.fields.back().value);
+
+ // The remainder of the form should be filled as usual.
+ response_data.fields.pop_back();
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, kDefaultPageID, false);
+}
+
+// Test that we correctly fill a previously auto-filled form.
+TEST_F(AutofillManagerTest, FillAutofilledForm) {
+ // Set up our form data.
+ FormData form;
+ test::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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, *form.fields.begin(),
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Address");
+ ExpectFilledForm(response_page_id, response_data, kDefaultPageID,
+ "Elvis", "", "", "", "", "", "", "", "", "", "", "", "",
+ "", "", true, true, false);
+ }
+
+ // Now fill the credit card data.
+ const int kPageID2 = 2;
+ GUIDPair guid2("00000000-0000-0000-0000-000000000004", 0);
+ response_page_id = 0;
+ FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields.back(),
+ PackGUIDs(guid2, empty), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Credit card 1");
+ ExpectFilledCreditCardFormElvis(
+ response_page_id, response_data, 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;
+ response_page_id = 0;
+ FillAutofillFormDataAndSaveResults(kPageID3, form, *form.fields.rbegin(),
+ PackGUIDs(guid2, empty), &response_page_id, &response_data);
+ {
+ SCOPED_TRACE("Credit card 2");
+ ExpectFilledForm(response_page_id, response_data, 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;
+ test::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 base::string16 elvis_name = profile->GetRawInfo(NAME_FULL);
+
+ std::vector<base::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);
+ int response_page_id = 0;
+ FormData response_data1;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data1);
+ {
+ SCOPED_TRACE("Valid variant");
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data1, 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);
+ response_page_id = 0;
+ FormData response_data2;
+ FillAutofillFormDataAndSaveResults(kPageID2, form, form.fields[0],
+ PackGUIDs(empty, guid2), &response_page_id, &response_data2);
+ {
+ SCOPED_TRACE("Invalid variant");
+ ExpectFilledForm(response_page_id, response_data2, 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) {
+ 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;
+ int response_page_id = 0;
+ FormData response_data1;
+ FillAutofillFormDataAndSaveResults(page_id, form_with_maxlength,
+ *form_with_maxlength.fields.begin(),
+ PackGUIDs(empty, guid), &response_page_id, &response_data1);
+ EXPECT_EQ(1, response_page_id);
+
+ ASSERT_EQ(5U, response_data1.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("1"), response_data1.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("650"), response_data1.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("555"), response_data1.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("4567"), response_data1.fields[3].value);
+ EXPECT_EQ(base::string16(), response_data1.fields[4].value);
+
+ page_id = 2;
+ response_page_id = 0;
+ FormData response_data2;
+ FillAutofillFormDataAndSaveResults(page_id, form_with_autocompletetype,
+ *form_with_autocompletetype.fields.begin(),
+ PackGUIDs(empty, guid), &response_page_id, &response_data2);
+ EXPECT_EQ(2, response_page_id);
+
+ ASSERT_EQ(5U, response_data2.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("1"), response_data2.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("650"), response_data2.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("555"), response_data2.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("4567"), response_data2.fields[3].value);
+ EXPECT_EQ(base::string16(), response_data2.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;
+ response_page_id = 0;
+ FormData response_data3;
+ FillAutofillFormDataAndSaveResults(page_id, form_with_maxlength,
+ *form_with_maxlength.fields.begin(),
+ PackGUIDs(empty, guid), &response_page_id, &response_data3);
+ EXPECT_EQ(3, response_page_id);
+
+ ASSERT_EQ(5U, response_data3.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("44"), response_data3.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("7700"), response_data3.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), response_data3.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), response_data3.fields[3].value);
+ EXPECT_EQ(base::string16(), response_data3.fields[4].value);
+
+ page_id = 4;
+ response_page_id = 0;
+ FormData response_data4;
+ FillAutofillFormDataAndSaveResults(page_id, form_with_autocompletetype,
+ *form_with_autocompletetype.fields.begin(),
+ PackGUIDs(empty, guid), &response_page_id, &response_data4);
+ EXPECT_EQ(4, response_page_id);
+
+ ASSERT_EQ(5U, response_data4.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("44"), response_data4.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("7700"), response_data4.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), response_data4.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("954321"), response_data4.fields[3].value);
+ EXPECT_EQ(base::string16(), response_data4.fields[4].value);
+
+ // We should fill all phone fields with the same phone number variant.
+ std::vector<base::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;
+ response_page_id = 0;
+ FormData response_data5;
+ GUIDPair variant_guid(work_profile->guid(), 1);
+ FillAutofillFormDataAndSaveResults(page_id, form_with_maxlength,
+ *form_with_maxlength.fields.begin(),
+ PackGUIDs(empty, variant_guid), &response_page_id, &response_data5);
+ EXPECT_EQ(5, response_page_id);
+
+ ASSERT_EQ(5U, response_data5.fields.size());
+ EXPECT_EQ(ASCIIToUTF16("1"), response_data5.fields[0].value);
+ EXPECT_EQ(ASCIIToUTF16("888"), response_data5.fields[1].value);
+ EXPECT_EQ(ASCIIToUTF16("777"), response_data5.fields[2].value);
+ EXPECT_EQ(ASCIIToUTF16("1234"), response_data5.fields[3].value);
+ EXPECT_EQ(base::string16(), response_data5.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;
+ test::CreateTestAddressFormData(&form);
+
+ // Add a field -- we'll remove it again later.
+ FormFieldData field;
+ 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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, 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;
+ test::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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, 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;
+ test::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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, 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(response_data);
+}
+
+// Test that when Autocomplete is enabled and Autofill is disabled,
+// form submissions are still received by AutocompleteHistoryManager.
+TEST_F(AutofillManagerTest, FormSubmittedAutocompleteEnabled) {
+ TestAutofillManagerDelegate delegate;
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(),
+ &delegate,
+ NULL));
+ autofill_manager_->set_autofill_enabled(false);
+ scoped_ptr<MockAutocompleteHistoryManager> autocomplete_history_manager;
+ autocomplete_history_manager.reset(
+ new MockAutocompleteHistoryManager(autofill_driver_.get(), &delegate));
+ autofill_manager_->autocomplete_history_manager_ =
+ autocomplete_history_manager.Pass();
+
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ form.method = ASCIIToUTF16("GET");
+ MockAutocompleteHistoryManager* m = static_cast<
+ MockAutocompleteHistoryManager*>(
+ autofill_manager_->autocomplete_history_manager_.get());
+ EXPECT_CALL(*m,
+ OnFormSubmitted(_)).Times(1);
+ FormSubmitted(form);
+}
+
+// Test that when Autocomplete is enabled and Autofill is disabled,
+// Autocomplete suggestions are still received.
+TEST_F(AutofillManagerTest, AutocompleteSuggestionsWhenAutofillDisabled) {
+ TestAutofillManagerDelegate delegate;
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(),
+ &delegate,
+ NULL));
+ autofill_manager_->set_autofill_enabled(false);
+ autofill_manager_->SetExternalDelegate(external_delegate_.get());
+
+ // Set up our form data.
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+ form.method = ASCIIToUTF16("GET");
+ std::vector<FormData> forms(1, form);
+ FormsSeen(forms);
+ const FormFieldData& field = form.fields[0];
+ GetAutofillSuggestions(form, field);
+
+ // Add some Autocomplete suggestions. We should return the autocomplete
+ // suggestions, these will be culled by the renderer.
+ std::vector<base::string16> suggestions;
+ suggestions.push_back(ASCIIToUTF16("Jay"));
+ suggestions.push_back(ASCIIToUTF16("Jason"));
+ AutocompleteSuggestionsReturned(suggestions);
+
+ base::string16 expected_values[] = {
+ ASCIIToUTF16("Jay"),
+ ASCIIToUTF16("Jason")
+ };
+ base::string16 expected_labels[] = { base::string16(), base::string16()};
+ base::string16 expected_icons[] = { base::string16(), base::string16()};
+ int expected_unique_ids[] = {0, 0};
+ external_delegate_->CheckSuggestions(
+ kDefaultPageID, arraysize(expected_values), expected_values,
+ expected_labels, expected_icons, expected_unique_ids);
+}
+
+// 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;
+ test::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<ServerFieldType> 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)->heuristic_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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[0],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+ ExpectFilledAddressFormElvis(
+ response_page_id, response_data, 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(response_data);
+}
+
+// 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;
+ test::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;
+ test::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);
+ int response_page_id = 0;
+ FormData response_data;
+ FillAutofillFormDataAndSaveResults(kDefaultPageID, form, form.fields[3],
+ PackGUIDs(empty, guid), &response_page_id, &response_data);
+
+ // 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(response_data);
+
+ // Set the address field's value back to the default value.
+ response_data.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(response_data);
+}
+
+// Checks that resetting the auxiliary profile enabled preference does the right
+// thing on all platforms.
+TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) {
+ PrefService* prefs = user_prefs::UserPrefs::Get(profile());
+#if defined(OS_MACOSX) || defined(OS_ANDROID)
+ // Auxiliary profiles is implemented on Mac and Android only.
+ // OSX: enables Mac Address Book integration.
+ // Android: enables integration with user's contact profile.
+ ASSERT_TRUE(
+ prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled));
+ prefs->SetBoolean(
+ ::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, false);
+ prefs->ClearPref(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled);
+ ASSERT_TRUE(
+ prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled));
+#else
+ ASSERT_FALSE(
+ prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled));
+ prefs->SetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, true);
+ prefs->ClearPref(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled);
+ ASSERT_FALSE(
+ prefs->GetBoolean(::autofill::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<ServerFieldTypeSet> expected_types;
+
+ // These fields should all match.
+ FormFieldData field;
+ ServerFieldTypeSet types;
+ test::CreateTestFormField("", "1", "Elvis", "text", &field);
+ types.clear();
+ types.insert(NAME_FIRST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "2", "Aaron", "text", &field);
+ types.clear();
+ types.insert(NAME_MIDDLE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "3", "A", "text", &field);
+ types.clear();
+ types.insert(NAME_MIDDLE_INITIAL);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "4", "Presley", "text", &field);
+ types.clear();
+ types.insert(NAME_LAST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "5", "Elvis Presley", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_NAME);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "6", "Elvis Aaron Presley", "text",
+ &field);
+ types.clear();
+ types.insert(NAME_FULL);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "7", "theking@gmail.com", "email",
+ &field);
+ types.clear();
+ types.insert(EMAIL_ADDRESS);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "8", "RCA", "text", &field);
+ types.clear();
+ types.insert(COMPANY_NAME);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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);
+
+ test::CreateTestFormField("", "10", "Apt. 10", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_LINE2);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "11", "Memphis", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_CITY);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "12", "Tennessee", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_STATE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "13", "38116", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_ZIP);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "14", "USA", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_COUNTRY);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "15", "United States", "text", &field);
+ types.clear();
+ types.insert(ADDRESS_HOME_COUNTRY);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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);
+
+ 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);
+
+ test::CreateTestFormField("", "18", "1", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_COUNTRY_CODE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "19", "234", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_CITY_CODE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "20", "5678901", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "21", "567", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "22", "8901", "text", &field);
+ types.clear();
+ types.insert(PHONE_HOME_NUMBER);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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);
+
+ test::CreateTestFormField("", "24", "04", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_MONTH);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "25", "April", "text", &field);
+ types.clear();
+ types.insert(CREDIT_CARD_EXP_MONTH);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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);
+
+ 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);
+
+ 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.
+ test::CreateTestFormField("", "29", "", "text", &field);
+ types.clear();
+ types.insert(EMPTY_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "30", " ", "text", &field);
+ types.clear();
+ types.insert(EMPTY_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "31", " Elvis", "text", &field);
+ types.clear();
+ types.insert(NAME_FIRST);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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.
+ test::CreateTestFormField("", "33", "elvis", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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.
+ test::CreateTestFormField("", "35", "Elvis Aaron", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "36", "Mr. Presley", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "37", "3734 Elvis Presley", "text",
+ &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "38", "TN", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "39", "38116-1023", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "20", "5", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ test::CreateTestFormField("", "20", "56", "text", &field);
+ types.clear();
+ types.insert(UNKNOWN_TYPE);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
+ 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, 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());
+
+ EXPECT_EQ(1U, autofill_manager_->request_autocomplete_results().size());
+ EXPECT_EQ(WebFormElement::AutocompleteResultErrorDisabled,
+ autofill_manager_->request_autocomplete_results()[0].first);
+}
+
+namespace {
+
+class MockAutofillManagerDelegate : public TestAutofillManagerDelegate {
+ public:
+ MockAutofillManagerDelegate()
+ : autocheckout_bubble_shown_(false) {}
+
+ virtual ~MockAutofillManagerDelegate() {}
+
+ virtual bool ShowAutocheckoutBubble(
+ const gfx::RectF& bounds,
+ bool is_google_user,
+ const base::Callback<void(AutocheckoutBubbleState)>& callback) OVERRIDE {
+ autocheckout_bubble_shown_ = true;
+ callback.Run(AUTOCHECKOUT_BUBBLE_ACCEPTED);
+ return true;
+ }
+
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback) OVERRIDE {
+ callback.Run(user_supplied_data_.get(), "google_transaction_id");
+ }
+
+ 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_;
+ }
+
+ private:
+ bool autocheckout_bubble_shown_;
+ scoped_ptr<FormStructure> user_supplied_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAutofillManagerDelegate);
+};
+
+} // namespace
+
+// Test that Autocheckout bubble is offered when server specifies field types.
+TEST_F(AutofillManagerTest, TestBubbleShown) {
+ MockAutofillManagerDelegate delegate;
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(), &delegate, &personal_data_));
+ autofill_manager_->set_autofill_enabled(true);
+ autofill_manager_->MarkAsFirstPageInAutocheckoutFlow();
+
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+
+ TestFormStructure* form_structure = new TestFormStructure(form);
+ AutofillMetrics metrics_logger; // ignored
+ form_structure->DetermineHeuristicTypes(metrics_logger);
+
+ // Build and add form structure with server data.
+ std::vector<ServerFieldType> 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)->heuristic_type());
+ }
+ form_structure->SetFieldTypes(heuristic_types, server_types);
+ autofill_manager_->AddSeenForm(form_structure);
+
+ autofill_manager_->OnMaybeShowAutocheckoutBubble(form, gfx::RectF());
+
+ EXPECT_TRUE(delegate.autocheckout_bubble_shown());
+}
+
+// Test that Autocheckout bubble is not offered when server doesn't have data
+// for the form.
+TEST_F(AutofillManagerTest, TestAutocheckoutBubbleNotShown) {
+ MockAutofillManagerDelegate delegate;
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(), &delegate, &personal_data_));
+ autofill_manager_->set_autofill_enabled(true);
+ autofill_manager_->MarkAsFirstPageInAutocheckoutFlow();
+
+ FormData form;
+ test::CreateTestAddressFormData(&form);
+
+ TestFormStructure* form_structure = new TestFormStructure(form);
+ AutofillMetrics metrics_logger; // ignored
+ form_structure->DetermineHeuristicTypes(metrics_logger);
+
+ // Build form structure without server data.
+ std::vector<ServerFieldType> heuristic_types, server_types;
+ for (size_t i = 0; i < form.fields.size(); ++i) {
+ heuristic_types.push_back(form_structure->field(i)->heuristic_type());
+ server_types.push_back(NO_SERVER_DATA);
+ }
+ form_structure->SetFieldTypes(heuristic_types, server_types);
+ autofill_manager_->AddSeenForm(form_structure);
+
+ autofill_manager_->OnMaybeShowAutocheckoutBubble(form, gfx::RectF());
+
+ EXPECT_FALSE(delegate.autocheckout_bubble_shown());
+}
+
+// Test our external delegate is called at the right time.
+TEST_F(AutofillManagerTest, TestExternalDelegate) {
+ FormData form;
+ test::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()
+
+ EXPECT_TRUE(external_delegate_->on_query_seen());
+}
+
+// Test that in the case of Autocheckout, forms seen are in order supplied.
+TEST_F(AutofillManagerTest, DynamicFormsSeenAndIgnored) {
+ MockAutofillManagerDelegate delegate;
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(), &delegate, &personal_data_));
+ FormData shipping_options;
+ CreateTestShippingOptionsFormData(&shipping_options);
+ FormData user_supplied;
+ CreateTestFormWithAutocompleteAttribute(&user_supplied);
+ FormData address;
+ test::CreateTestAddressFormData(&address);
+
+ autofill_manager_->set_autocheckout_url_prefix("test-prefix");
+ // Push address only
+ std::vector<FormData> forms;
+ forms.push_back(address);
+
+ // Build and add form structure with server data.
+ scoped_ptr<TestFormStructure> form_structure(new TestFormStructure(address));
+ std::vector<ServerFieldType> heuristic_types, server_types;
+ for (size_t i = 0; i < address.fields.size(); ++i) {
+ heuristic_types.push_back(UNKNOWN_TYPE);
+ server_types.push_back(form_structure->field(i)->heuristic_type());
+ }
+ form_structure->SetFieldTypes(heuristic_types, server_types);
+ autofill_manager_->AddSeenForm(form_structure.release());
+
+ // Make sure normal form is handled correctly.
+ autofill_manager_->MarkAsFirstPageInAutocheckoutFlowIgnoringAjax();
+ std::vector<FormStructure*> form_structures;
+ form_structures = autofill_manager_->GetFormStructures();
+ ASSERT_EQ(1U, form_structures.size());
+ EXPECT_EQ("/form.html", form_structures[0]->source_url().path());
+
+ scoped_ptr<FormStructure> filled_form(new TestFormStructure(address));
+ delegate.SetUserSuppliedData(filled_form.Pass());
+ autofill_manager_->OnMaybeShowAutocheckoutBubble(address, gfx::RectF());
+
+ // Push other forms
+ forms.push_back(shipping_options);
+ forms.push_back(user_supplied);
+
+ // FormStructure should contain the same forms as before.
+ DynamicFormsSeen(forms);
+ form_structures = autofill_manager_->GetFormStructures();
+ ASSERT_EQ(1U, form_structures.size());
+ EXPECT_EQ("/form.html", form_structures[0]->source_url().path());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
new file mode 100644
index 00000000000..e007a59d905
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -0,0 +1,248 @@
+// 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 <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/data_driven_test.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/common/form_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "url/gurl.h"
+
+namespace autofill {
+
+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 ServerFieldType 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
+};
+
+const base::FilePath& GetTestDataDir() {
+ CR_DEFINE_STATIC_LOCAL(base::FilePath, dir, ());
+ if (dir.empty()) {
+ PathService::Get(base::DIR_SOURCE_ROOT, &dir);
+ dir = dir.AppendASCII("components");
+ dir = dir.AppendASCII("test");
+ dir = dir.AppendASCII("data");
+ }
+ return dir;
+}
+
+// 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) {
+ ServerFieldType type = kProfileFieldTypes[j];
+ std::vector<base::string16> values;
+ profiles[i]->GetRawMultiInfo(type, &values);
+ for (size_t k = 0; k < values.size(); ++k) {
+ result += AutofillType(type).ToString();
+ 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("en-US") {
+}
+
+PersonalDataManagerMock::~PersonalDataManagerMock() {
+}
+
+void PersonalDataManagerMock::Reset() {
+ profiles_.clear();
+}
+
+void PersonalDataManagerMock::SaveImportedProfile(
+ const AutofillProfile& profile) {
+ std::vector<AutofillProfile> profiles;
+ if (!MergeProfile(profile, profiles_.get(), "en-US", &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);
+
+ // Deserializes |str| into a field type.
+ ServerFieldType StringToFieldType(const std::string& str);
+
+ PersonalDataManagerMock personal_data_;
+
+ private:
+ std::map<std::string, ServerFieldType> string_to_field_type_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillMergeTest);
+};
+
+AutofillMergeTest::AutofillMergeTest() : DataDrivenTest(GetTestDataDir()) {
+ for (size_t i = NO_SERVER_DATA; i < MAX_VALID_FIELD_TYPE; ++i) {
+ ServerFieldType field_type = static_cast<ServerFieldType>(i);
+ string_to_field_type_map_[AutofillType(field_type).ToString()] = field_type;
+ }
+}
+
+AutofillMergeTest::~AutofillMergeTest() {
+}
+
+void AutofillMergeTest::SetUp() {
+ 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);
+ base::string16 field_type = UTF8ToUTF16(line.substr(0, separator_pos));
+ base::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));
+ ServerFieldType type = 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());
+}
+
+ServerFieldType AutofillMergeTest::StringToFieldType(const std::string& str) {
+ return string_to_field_type_map_[str];
+}
+
+TEST_F(AutofillMergeTest, DataDrivenMergeProfiles) {
+ RunDataDrivenTest(GetInputDirectory(kTestName), GetOutputDirectory(kTestName),
+ kFileNamePattern);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc
new file mode 100644
index 00000000000..57743c3b7fd
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_metrics.cc
@@ -0,0 +1,598 @@
+// 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/core/browser/autofill_metrics.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/common/form_data.h"
+
+namespace autofill {
+
+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 ServerFieldType field_type,
+ const int metric,
+ const int num_possible_metrics) {
+ DCHECK_LT(metric, num_possible_metrics);
+
+ FieldTypeGroupForMetrics group;
+ switch (AutofillType(field_type).group()) {
+ case ::autofill::NO_GROUP:
+ group = AMBIGUOUS;
+ break;
+
+ case ::autofill::NAME:
+ group = NAME;
+ break;
+
+ case ::autofill::COMPANY:
+ group = COMPANY;
+ break;
+
+ case ::autofill::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 ::autofill::EMAIL:
+ group = EMAIL;
+ break;
+
+ case ::autofill::PHONE_HOME:
+ group = PHONE;
+ break;
+
+ case ::autofill::CREDIT_CARD:
+ switch (field_type) {
+ case ::autofill::CREDIT_CARD_NAME:
+ group = CREDIT_CARD_NAME;
+ break;
+ case ::autofill::CREDIT_CARD_NUMBER:
+ group = CREDIT_CARD_NUMBER;
+ break;
+ case ::autofill::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;
+}
+
+// Returns the histogram prefix to use for reporting metrics for |dialog_type|.
+std::string GetPrefixForDialogType(autofill::DialogType dialog_type) {
+ switch (dialog_type) {
+ case autofill::DIALOG_TYPE_AUTOCHECKOUT:
+ return "Autocheckout";
+
+ case autofill::DIALOG_TYPE_REQUEST_AUTOCOMPLETE:
+ return "RequestAutocomplete";
+ }
+
+ NOTREACHED();
+ return "UnknownDialogType";
+}
+
+std::string WalletApiMetricToString(
+ AutofillMetrics::WalletApiCallMetric metric) {
+ switch (metric) {
+ case AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS:
+ return "AcceptLegalDocuments";
+ case AutofillMetrics::AUTHENTICATE_INSTRUMENT:
+ return "AuthenticateInstrument";
+ case AutofillMetrics::GET_FULL_WALLET:
+ return "GetFullWallet";
+ case AutofillMetrics::GET_WALLET_ITEMS:
+ return "GetWalletItems";
+ case AutofillMetrics::SAVE_TO_WALLET:
+ return "SaveToWallet";
+ case AutofillMetrics::SEND_STATUS:
+ return "SendStatus";
+ case AutofillMetrics::UNKNOWN_API_CALL:
+ NOTREACHED();
+ return "UnknownApiCall";
+ }
+
+ NOTREACHED();
+ return "UnknownApiCall";
+}
+
+// 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) {
+ DCHECK_LT(sample, 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_TIMES macro that allows the |name|
+// to vary over the program's runtime.
+void LogUMAHistogramTimes(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::FromSeconds(10),
+ 50,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->AddTime(duration);
+}
+
+// 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 ServerFieldType field_type,
+ const std::string& experiment_id) {
+ DCHECK_LT(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_LT(metric, NUM_SERVER_EXPERIMENTS);
+ LogUMAHistogramEnumeration(histogram_name, metric, NUM_SERVER_EXPERIMENTS);
+}
+
+} // namespace
+
+AutofillMetrics::AutofillMetrics() {
+}
+
+AutofillMetrics::~AutofillMetrics() {
+}
+
+void AutofillMetrics::LogAutocheckoutBubbleMetric(BubbleMetric metric) const {
+ DCHECK_LT(metric, NUM_BUBBLE_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autocheckout.Bubble", metric, NUM_BUBBLE_METRICS);
+}
+
+void AutofillMetrics::LogAutocheckoutBuyFlowMetric(
+ AutocheckoutBuyFlowMetric metric) const {
+ DCHECK_LT(metric, NUM_AUTOCHECKOUT_BUY_FLOW_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autocheckout.BuyFlow", metric,
+ NUM_AUTOCHECKOUT_BUY_FLOW_METRICS);
+}
+
+void AutofillMetrics::LogCreditCardInfoBarMetric(InfoBarMetric metric) const {
+ DCHECK_LT(metric, NUM_INFO_BAR_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardInfoBar", metric,
+ NUM_INFO_BAR_METRICS);
+}
+
+void AutofillMetrics::LogDialogDismissalState(
+ autofill::DialogType dialog_type,
+ DialogDismissalState state) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".DismissalState";
+ LogUMAHistogramEnumeration(name, state, NUM_DIALOG_DISMISSAL_STATES);
+}
+
+void AutofillMetrics::LogDialogInitialUserState(
+ autofill::DialogType dialog_type,
+ DialogInitialUserStateMetric user_type) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".InitialUserState";
+ LogUMAHistogramEnumeration(
+ name, user_type, NUM_DIALOG_INITIAL_USER_STATE_METRICS);
+}
+
+void AutofillMetrics::LogDialogLatencyToShow(
+ autofill::DialogType dialog_type,
+ const base::TimeDelta& duration) const {
+ std::string name =
+ GetPrefixForDialogType(dialog_type) + ".UiLatencyToShow";
+ LogUMAHistogramTimes(name, duration);
+}
+
+void AutofillMetrics::LogDialogPopupEvent(autofill::DialogType dialog_type,
+ DialogPopupEvent event) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".PopupInDialog";
+ LogUMAHistogramEnumeration(name, event, NUM_DIALOG_POPUP_EVENTS);
+}
+
+void AutofillMetrics::LogDialogSecurityMetric(
+ autofill::DialogType dialog_type,
+ DialogSecurityMetric metric) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".Security";
+ LogUMAHistogramEnumeration(name, metric, NUM_DIALOG_SECURITY_METRICS);
+}
+
+void AutofillMetrics::LogDialogUiDuration(
+ const base::TimeDelta& duration,
+ autofill::DialogType dialog_type,
+ DialogDismissalAction dismissal_action) const {
+ std::string prefix = GetPrefixForDialogType(dialog_type);
+
+ 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::LogDialogUiEvent(autofill::DialogType dialog_type,
+ DialogUiEvent event) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".UiEvents";
+ LogUMAHistogramEnumeration(name, event, NUM_DIALOG_UI_EVENTS);
+}
+
+void AutofillMetrics::LogWalletErrorMetric(autofill::DialogType dialog_type,
+ WalletErrorMetric metric) const {
+ std::string name = GetPrefixForDialogType(dialog_type) + ".WalletErrors";
+ LogUMAHistogramEnumeration(name, metric, NUM_WALLET_ERROR_METRICS);
+}
+
+void AutofillMetrics::LogWalletApiCallDuration(
+ WalletApiCallMetric metric,
+ const base::TimeDelta& duration) const {
+ LogUMAHistogramTimes("Wallet.ApiCallDuration." +
+ WalletApiMetricToString(metric), duration);
+}
+
+void AutofillMetrics::LogWalletRequiredActionMetric(
+ autofill::DialogType dialog_type,
+ WalletRequiredActionMetric required_action) const {
+ std::string name =
+ GetPrefixForDialogType(dialog_type) + ".WalletRequiredActions";
+ LogUMAHistogramEnumeration(
+ name, required_action, NUM_WALLET_REQUIRED_ACTIONS);
+}
+
+void AutofillMetrics::LogAutocheckoutDuration(
+ const base::TimeDelta& duration,
+ AutocheckoutCompletionStatus status) const {
+ std::string suffix;
+ switch (status) {
+ case AUTOCHECKOUT_CANCELLED:
+ suffix = "Cancelled";
+ break;
+
+ case AUTOCHECKOUT_FAILED:
+ suffix = "Failed";
+ break;
+
+ case AUTOCHECKOUT_SUCCEEDED:
+ suffix = "Succeeded";
+ break;
+ }
+
+ LogUMAHistogramLongTimes("Autocheckout.FlowDuration", duration);
+ LogUMAHistogramLongTimes("Autocheckout.FlowDuration." + suffix, duration);
+}
+
+void AutofillMetrics::LogAutocheckoutWhitelistDownloadDuration(
+ const base::TimeDelta& duration,
+ AutocheckoutWhitelistDownloadStatus status) const {
+ std::string suffix;
+ switch (status) {
+ case AUTOCHECKOUT_WHITELIST_DOWNLOAD_FAILED:
+ suffix = "Failed";
+ break;
+
+ case AUTOCHECKOUT_WHITELIST_DOWNLOAD_SUCCEEDED:
+ suffix = "Succeeded";
+ break;
+ }
+
+ LogUMAHistogramTimes("Autocheckout.WhitelistDownloadDuration", duration);
+ LogUMAHistogramTimes(
+ "Autocheckout.WhitelistDownloadDuration." + suffix, duration);
+}
+
+void AutofillMetrics::LogDeveloperEngagementMetric(
+ DeveloperEngagementMetric metric) const {
+ DCHECK_LT(metric, NUM_DEVELOPER_ENGAGEMENT_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.DeveloperEngagement", metric,
+ NUM_DEVELOPER_ENGAGEMENT_METRICS);
+}
+
+void AutofillMetrics::LogHeuristicTypePrediction(
+ FieldTypeQualityMetric metric,
+ ServerFieldType 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,
+ ServerFieldType 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,
+ ServerFieldType 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_LT(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_LT(metric, NUM_SERVER_QUERY_METRICS);
+
+ UMA_HISTOGRAM_ENUMERATION("Autofill.ServerQueryResponse", metric,
+ NUM_SERVER_QUERY_METRICS);
+}
+
+void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric) const {
+ DCHECK_LT(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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h
new file mode 100644
index 00000000000..d6ae790b392
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_metrics.h
@@ -0,0 +1,516 @@
+// 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_CORE_BROWSER_AUTOFILL_METRICS_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_METRICS_H_
+
+#include <stddef.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace base {
+class TimeDelta;
+}
+
+namespace autofill {
+
+class AutofillMetrics {
+ public:
+ // The possible results of an Autocheckout flow.
+ enum AutocheckoutBuyFlowMetric {
+ // The user has initated Autocheckout. The baseline metric.
+ AUTOCHECKOUT_BUY_FLOW_STARTED,
+ // Autocheckout completed successfully.
+ AUTOCHECKOUT_BUY_FLOW_SUCCESS,
+ // Autocheckout failed due to missing server side data.
+ AUTOCHECKOUT_BUY_FLOW_MISSING_FIELDMAPPING,
+ // Autocheckout failed due to a missing proceed element.
+ AUTOCHECKOUT_BUY_FLOW_MISSING_ADVANCE_ELEMENT,
+ // Autocheckout failed for any number of other reasons, e.g, the proceed
+ // element click failed, the page numbers were not increasing, etc.
+ AUTOCHECKOUT_BUY_FLOW_CANNOT_PROCEED,
+ // Autocheckout failed due to a missing click element before form filling.
+ AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING,
+ // Autocheckout failed due to a missing click element after form filling.
+ AUTOCHECKOUT_BUY_FLOW_MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING,
+ NUM_AUTOCHECKOUT_BUY_FLOW_METRICS
+ };
+
+ // The success or failure of Autocheckout.
+ enum AutocheckoutCompletionStatus {
+ AUTOCHECKOUT_CANCELLED, // The user canceled Autocheckout while it was in
+ // progress.
+ 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.
+ BUBBLE_COULD_BE_DISPLAYED, // The bubble could be displayed.
+ 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. submitted, the dialog.
+ DIALOG_CANCELED, // The user canceled out of the dialog.
+ };
+
+ // The state of the Autofill dialog when it was dismissed.
+ enum DialogDismissalState {
+ // The user submitted with no data available to save.
+ DIALOG_ACCEPTED_EXISTING_DATA,
+ // The saved details to Online Wallet on submit.
+ DIALOG_ACCEPTED_SAVE_TO_WALLET,
+ // The saved details to the local Autofill database on submit.
+ DIALOG_ACCEPTED_SAVE_TO_AUTOFILL,
+ // The user submitted without saving any edited sections.
+ DIALOG_ACCEPTED_NO_SAVE,
+ // The user canceled with no edit UI showing.
+ DIALOG_CANCELED_NO_EDITS,
+ // The user canceled with edit UI showing, but no invalid fields.
+ DIALOG_CANCELED_NO_INVALID_FIELDS,
+ // The user canceled with at least one invalid field.
+ DIALOG_CANCELED_WITH_INVALID_FIELDS,
+ // The user canceled while the sign-in form was showing.
+ DIALOG_CANCELED_DURING_SIGNIN,
+ NUM_DIALOG_DISMISSAL_STATES
+ };
+
+ // The initial state of user that's interacting with a freshly shown Autofill
+ // dialog.
+ enum DialogInitialUserStateMetric {
+ // Could not determine the user's state due to failure to communicate with
+ // the Wallet server.
+ DIALOG_USER_STATE_UNKNOWN = 0,
+ // Not signed in, no verified Autofill profiles.
+ DIALOG_USER_NOT_SIGNED_IN_NO_AUTOFILL,
+ // Not signed in, has verified Autofill profiles.
+ DIALOG_USER_NOT_SIGNED_IN_HAS_AUTOFILL,
+ // Signed in, no Wallet items, no verified Autofill profiles.
+ DIALOG_USER_SIGNED_IN_NO_WALLET_NO_AUTOFILL,
+ // Signed in, no Wallet items, has verified Autofill profiles.
+ DIALOG_USER_SIGNED_IN_NO_WALLET_HAS_AUTOFILL,
+ // Signed in, has Wallet items, no verified Autofill profiles.
+ DIALOG_USER_SIGNED_IN_HAS_WALLET_NO_AUTOFILL,
+ // Signed in, has Wallet items, has verified Autofill profiles.
+ DIALOG_USER_SIGNED_IN_HAS_WALLET_HAS_AUTOFILL,
+ NUM_DIALOG_INITIAL_USER_STATE_METRICS
+ };
+
+ // Events related to the Autofill popup shown in a requestAutocomplete or
+ // Autocheckout dialog.
+ enum DialogPopupEvent {
+ // An Autofill popup was shown.
+ DIALOG_POPUP_SHOWN = 0,
+ // The user chose to fill the form with a suggestion from the popup.
+ DIALOG_POPUP_FORM_FILLED,
+ NUM_DIALOG_POPUP_EVENTS
+ };
+
+ // For measuring the frequency of security warnings or errors that can come
+ // up as part of the requestAutocomplete flow.
+ enum DialogSecurityMetric {
+ // Baseline metric: The dialog was shown.
+ SECURITY_METRIC_DIALOG_SHOWN = 0,
+ // Credit card requested over non-secure protocol.
+ SECURITY_METRIC_CREDIT_CARD_OVER_HTTP,
+ // Autocomplete data requested from a frame hosted on an origin not matching
+ // the main frame's origin.
+ SECURITY_METRIC_CROSS_ORIGIN_FRAME,
+ NUM_DIALOG_SECURITY_METRICS
+ };
+
+ // For measuring how users are interacting with the Autofill dialog UI.
+ enum DialogUiEvent {
+ // Baseline metric: The dialog was shown.
+ DIALOG_UI_SHOWN = 0,
+
+ // Dialog dismissal actions:
+ DIALOG_UI_ACCEPTED,
+ DIALOG_UI_CANCELED,
+
+ // Selections within the account switcher:
+ // Switched from a Wallet account to local Autofill data.
+ DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_TO_AUTOFILL,
+ // Switched from local Autofill data to a Wallet account.
+ DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_TO_WALLET,
+ // Switched from one Wallet account to another one.
+ DIALOG_UI_ACCOUNT_CHOOSER_SWITCHED_WALLET_ACCOUNT,
+
+ // The sign-in UI was shown.
+ DIALOG_UI_SIGNIN_SHOWN,
+
+ // Selecting a different item from a suggestion menu dropdown:
+ DIALOG_UI_EMAIL_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_BILLING_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_CC_BILLING_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_SHIPPING_SELECTED_SUGGESTION_CHANGED,
+ DIALOG_UI_CC_SELECTED_SUGGESTION_CHANGED,
+
+ // Showing the editing UI for a section of the dialog:
+ DIALOG_UI_EMAIL_EDIT_UI_SHOWN,
+ DIALOG_UI_BILLING_EDIT_UI_SHOWN,
+ DIALOG_UI_CC_BILLING_EDIT_UI_SHOWN,
+ DIALOG_UI_SHIPPING_EDIT_UI_SHOWN,
+ DIALOG_UI_CC_EDIT_UI_SHOWN,
+
+ // Adding a new item in a section of the dialog:
+ DIALOG_UI_EMAIL_ITEM_ADDED,
+ DIALOG_UI_BILLING_ITEM_ADDED,
+ DIALOG_UI_CC_BILLING_ITEM_ADDED,
+ DIALOG_UI_SHIPPING_ITEM_ADDED,
+ DIALOG_UI_CC_ITEM_ADDED,
+
+ NUM_DIALOG_UI_EVENTS
+ };
+
+ 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,
+ };
+
+ // For measuring the network request time of various Wallet API calls. See
+ // WalletClient::RequestType.
+ enum WalletApiCallMetric {
+ UNKNOWN_API_CALL, // Catch all. Should never be used.
+ ACCEPT_LEGAL_DOCUMENTS,
+ AUTHENTICATE_INSTRUMENT,
+ GET_FULL_WALLET,
+ GET_WALLET_ITEMS,
+ SEND_STATUS,
+ SAVE_TO_WALLET,
+ };
+
+ // For measuring the frequency of errors while communicating with the Wallet
+ // server.
+ enum WalletErrorMetric {
+ // Baseline metric: Issued a request to the Wallet server.
+ WALLET_ERROR_BASELINE_ISSUED_REQUEST = 0,
+ // A fatal error occured while communicating with the Wallet server. This
+ // value has been deprecated.
+ WALLET_FATAL_ERROR_DEPRECATED,
+ // Received a malformed response from the Wallet server.
+ WALLET_MALFORMED_RESPONSE,
+ // A network error occured while communicating with the Wallet server.
+ WALLET_NETWORK_ERROR,
+ // The request was malformed.
+ WALLET_BAD_REQUEST,
+ // Risk deny, unsupported country, or account closed.
+ WALLET_BUYER_ACCOUNT_ERROR,
+ // Unknown server side error.
+ WALLET_INTERNAL_ERROR,
+ // API call had missing or invalid parameters.
+ WALLET_INVALID_PARAMS,
+ // Online Wallet is down.
+ WALLET_SERVICE_UNAVAILABLE,
+ // User needs make a cheaper transaction or not use Online Wallet. This
+ // value has been deprecated.
+ WALLET_SPENDING_LIMIT_EXCEEDED_DEPRECATED,
+ // The server API version of the request is no longer supported.
+ WALLET_UNSUPPORTED_API_VERSION,
+ // Catch all error type.
+ WALLET_UNKNOWN_ERROR,
+ // The merchant has been blacklisted for Online Wallet due to some manner
+ // of compliance violation.
+ WALLET_UNSUPPORTED_MERCHANT,
+ // Buyer Legal Address has a country which is unsupported by Wallet.
+ WALLET_BUYER_LEGAL_ADDRESS_NOT_SUPPORTED,
+ // Wallet's Know Your Customer(KYC) action is pending/failed for this user.
+ WALLET_UNVERIFIED_KNOW_YOUR_CUSTOMER_STATUS,
+ NUM_WALLET_ERROR_METRICS
+ };
+
+ // For measuring the frequency of "required actions" returned by the Wallet
+ // server. This is similar to the autofill::wallet::RequiredAction enum;
+ // but unlike that enum, the values in this one must remain constant over
+ // time, so that the metrics can be consistently interpreted on the
+ // server-side.
+ enum WalletRequiredActionMetric {
+ // Baseline metric: Issued a request to the Wallet server.
+ WALLET_REQUIRED_ACTION_BASELINE_ISSUED_REQUEST = 0,
+ // Values from the autofill::wallet::RequiredAction enum:
+ UNKNOWN_REQUIRED_ACTION, // Catch all type.
+ GAIA_AUTH,
+ PASSIVE_GAIA_AUTH,
+ SETUP_WALLET,
+ ACCEPT_TOS,
+ UPDATE_EXPIRATION_DATE,
+ UPGRADE_MIN_ADDRESS,
+ CHOOSE_ANOTHER_INSTRUMENT_OR_ADDRESS,
+ VERIFY_CVV,
+ INVALID_FORM_FIELD,
+ REQUIRE_PHONE_NUMBER,
+ NUM_WALLET_REQUIRED_ACTIONS
+ };
+
+ // The success or failure of downloading Autocheckout whitelist file.
+ enum AutocheckoutWhitelistDownloadStatus {
+ AUTOCHECKOUT_WHITELIST_DOWNLOAD_FAILED,
+ AUTOCHECKOUT_WHITELIST_DOWNLOAD_SUCCEEDED,
+ };
+
+ AutofillMetrics();
+ virtual ~AutofillMetrics();
+
+ // Logs how the user interacted with the Autocheckout bubble.
+ virtual void LogAutocheckoutBubbleMetric(BubbleMetric metric) const;
+
+ // Logs the result of an Autocheckout buy flow.
+ virtual void LogAutocheckoutBuyFlowMetric(
+ AutocheckoutBuyFlowMetric metric) const;
+
+ virtual void LogCreditCardInfoBarMetric(InfoBarMetric metric) const;
+
+ virtual void LogDeveloperEngagementMetric(
+ DeveloperEngagementMetric metric) const;
+
+ virtual void LogHeuristicTypePrediction(
+ FieldTypeQualityMetric metric,
+ ServerFieldType field_type,
+ const std::string& experiment_id) const;
+ virtual void LogOverallTypePrediction(
+ FieldTypeQualityMetric metric,
+ ServerFieldType field_type,
+ const std::string& experiment_id) const;
+ virtual void LogServerTypePrediction(FieldTypeQualityMetric metric,
+ ServerFieldType 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;
+
+ // Logs |state| to the dismissal states histogram for |dialog_type|.
+ virtual void LogDialogDismissalState(autofill::DialogType dialog_type,
+ DialogDismissalState state) const;
+
+ // This should be called as soon as the user's signed-in status and Wallet
+ // item count is known. Records that a user starting out in |user_state| is
+ // interacting with a dialog of |dialog_type|.
+ virtual void LogDialogInitialUserState(
+ autofill::DialogType dialog_type,
+ DialogInitialUserStateMetric user_type) const;
+
+ // Logs the time elapsed between the dialog being shown for |dialog_type| and
+ // when it is ready for user interaction.
+ virtual void LogDialogLatencyToShow(autofill::DialogType dialog_type,
+ const base::TimeDelta& duration) const;
+
+ // Logs |event| to the popup events histogram for |dialog_type|.
+ virtual void LogDialogPopupEvent(autofill::DialogType dialog_type,
+ DialogPopupEvent event) const;
+
+ // Logs |metric| to the security metrics histogram for |dialog_type|.
+ virtual void LogDialogSecurityMetric(autofill::DialogType dialog_type,
+ DialogSecurityMetric metric) const;
+
+ // This should be called when the Autofill dialog, invoked by a dialog of type
+ // |dialog_type|, 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 canceling.
+ virtual void LogDialogUiDuration(
+ const base::TimeDelta& duration,
+ autofill::DialogType dialog_type,
+ DialogDismissalAction dismissal_action) const;
+
+ // Logs |event| to the UI events histogram for |dialog_type|.
+ virtual void LogDialogUiEvent(autofill::DialogType dialog_type,
+ DialogUiEvent event) const;
+
+ // Logs |metric| to the Wallet errors histogram for |dialog_type|.
+ virtual void LogWalletErrorMetric(autofill::DialogType dialog_type,
+ WalletErrorMetric metric) const;
+
+ // Logs the network request time of Wallet API calls.
+ virtual void LogWalletApiCallDuration(
+ WalletApiCallMetric metric,
+ const base::TimeDelta& duration) const;
+
+ // Logs |required_action| to the required actions histogram for |dialog_type|.
+ virtual void LogWalletRequiredActionMetric(
+ autofill::DialogType dialog_type,
+ WalletRequiredActionMetric required_action) const;
+
+ virtual void LogAutocheckoutDuration(
+ const base::TimeDelta& duration,
+ AutocheckoutCompletionStatus status) const;
+
+ // Logs the time taken to download Autocheckout whitelist file.
+ virtual void LogAutocheckoutWhitelistDownloadDuration(
+ const base::TimeDelta& duration,
+ AutocheckoutWhitelistDownloadStatus 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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_METRICS_H_
diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
new file mode 100644
index 00000000000..ece18d8dfd3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -0,0 +1,1564 @@
+// 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/core/browser/autofill_metrics.h"
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.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/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+#include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/autofill_manager_delegate.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/test_autofill_driver.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/forms_seen_state.h"
+#include "components/webdata/common/web_data_results.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+#include "url/gurl.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+using testing::_;
+using testing::AnyNumber;
+using testing::Mock;
+
+namespace autofill {
+
+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,
+ ServerFieldType field_type,
+ const std::string& experiment_id));
+ MOCK_CONST_METHOD3(LogOverallTypePrediction,
+ void(FieldTypeQualityMetric metric,
+ ServerFieldType field_type,
+ const std::string& experiment_id));
+ MOCK_CONST_METHOD3(LogServerTypePrediction,
+ void(FieldTypeQualityMetric metric,
+ ServerFieldType 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()
+ : PersonalDataManager("en-US"),
+ autofill_enabled_(true) {
+ set_metric_logger(new testing::NiceMock<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;
+ 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;
+ 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<ServerFieldType>& heuristic_types,
+ const std::vector<ServerFieldType>& 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(AutofillDriver* driver,
+ AutofillManagerDelegate* manager_delegate,
+ TestPersonalDataManager* personal_manager)
+ : AutofillManager(driver, manager_delegate, personal_manager),
+ autofill_enabled_(true) {
+ set_metric_logger(new testing::NiceMock<MockAutofillMetrics>);
+ }
+ virtual ~TestAutofillManager() {}
+
+ virtual std::string GetAutocheckoutURLPrefix() const OVERRIDE {
+ return std::string();
+ }
+
+ 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<ServerFieldType>& heuristic_types,
+ const std::vector<ServerFieldType>& 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 = base::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) {
+ message_loop_runner_ = new content::MessageLoopRunner();
+ if (!OnFormSubmitted(form, timestamp))
+ return;
+
+ // Wait for the asynchronous FormSubmitted() call to complete.
+ message_loop_runner_->Run();
+ }
+
+ virtual void UploadFormDataAsyncCallback(
+ const FormStructure* submitted_form,
+ const base::TimeTicks& load_time,
+ const base::TimeTicks& interaction_time,
+ const base::TimeTicks& submission_time) OVERRIDE {
+ message_loop_runner_->Quit();
+
+ AutofillManager::UploadFormDataAsyncCallback(submitted_form,
+ load_time,
+ interaction_time,
+ submission_time);
+ }
+
+ private:
+ bool autofill_enabled_;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillManager);
+};
+
+} // namespace
+
+class AutofillMetricsTest : public ChromeRenderViewHostTestHarness {
+ public:
+ virtual ~AutofillMetricsTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ protected:
+ scoped_ptr<ConfirmInfoBarDelegate> CreateDelegate(
+ MockAutofillMetrics* metric_logger);
+
+ scoped_ptr<TestAutofillDriver> autofill_driver_;
+ scoped_ptr<TestAutofillManager> autofill_manager_;
+ scoped_ptr<TestPersonalDataManager> personal_data_;
+ scoped_ptr<AutofillExternalDelegate> external_delegate_;
+};
+
+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() {
+ ChromeRenderViewHostTestHarness::SetUp();
+
+ // Ensure Mac OS X does not pop up a modal dialog for the Address Book.
+ autofill::test::DisableSystemServices(profile());
+
+ PersonalDataManagerFactory::GetInstance()->SetTestingFactory(profile(), NULL);
+
+ TabAutofillManagerDelegate::CreateForWebContents(web_contents());
+
+ personal_data_.reset(new TestPersonalDataManager());
+ personal_data_->SetBrowserContext(profile());
+ autofill_driver_.reset(new TestAutofillDriver(web_contents()));
+ autofill_manager_.reset(new TestAutofillManager(
+ autofill_driver_.get(),
+ TabAutofillManagerDelegate::FromWebContents(web_contents()),
+ personal_data_.get()));
+
+ external_delegate_.reset(new AutofillExternalDelegate(
+ web_contents(),
+ autofill_manager_.get(),
+ autofill_driver_.get()));
+ autofill_manager_->SetExternalDelegate(external_delegate_.get());
+}
+
+void AutofillMetricsTest::TearDown() {
+ // 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();
+ autofill_driver_.reset();
+ personal_data_.reset();
+ ChromeRenderViewHostTestHarness::TearDown();
+}
+
+scoped_ptr<ConfirmInfoBarDelegate> AutofillMetricsTest::CreateDelegate(
+ MockAutofillMetrics* metric_logger) {
+ EXPECT_CALL(*metric_logger,
+ LogCreditCardInfoBarMetric(AutofillMetrics::INFOBAR_SHOWN));
+
+ CreditCard credit_card;
+ return AutofillCCInfoBarDelegate::Create(
+ metric_logger,
+ base::Bind(&TestPersonalDataManager::SaveImportedCreditCard,
+ base::Unretained(personal_data_.get()), 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<ServerFieldType> heuristic_types, server_types;
+ FormFieldData field;
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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;
+ ServerFieldType heuristic_type;
+ ServerFieldType 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<ServerFieldType> heuristic_types, server_types;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(failure_cases); ++i) {
+ FormFieldData field;
+ 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<ServerFieldType> heuristic_types, server_types;
+
+ FormFieldData field;
+ 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);
+ 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);
+ 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);
+ 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();
+ 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;
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+
+ // Ensure no metrics are logged when loading a non-fillable form.
+ {
+ EXPECT_CALL(*autofill_manager_->metric_logger(),
+ LogDeveloperEngagementMetric(_)).Times(0);
+ autofill_manager_->OnFormsSeen(forms, TimeTicks(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+ autofill_manager_->Reset();
+ Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger());
+ }
+
+ // Add another field to the form, so that it becomes fillable.
+ test::CreateTestFormField("Phone", "phone", "", "text", &field);
+ forms.back().fields.push_back(field);
+
+ // Expect only the "form parsed" metric to be logged; no metrics about
+ // author-specified field type hints.
+ {
+ EXPECT_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::NO_SPECIAL_FORMS_SEEN);
+ 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.
+ test::CreateTestFormField("", "", "", "text", &field);
+ field.autocomplete_attribute = "given-name";
+ forms.back().fields.push_back(field);
+ test::CreateTestFormField("", "", "", "text", &field);
+ field.autocomplete_attribute = "email";
+ forms.back().fields.push_back(field);
+ test::CreateTestFormField("", "", "", "text", &field);
+ field.autocomplete_attribute = "address-line1";
+ forms.back().fields.push_back(field);
+
+ // Expect 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::NO_SPECIAL_FORMS_SEEN);
+ 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;
+ test::CreateTestFormField(
+ "Autofilled", "autofilled", "Elvis Presley", "text", &field);
+ field.is_autofilled = true;
+ form.fields.push_back(field);
+ 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");
+ 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<ServerFieldType> heuristic_types, server_types;
+ FormFieldData field;
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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<ServerFieldType> field_types;
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ field_types.push_back(NAME_FULL);
+ test::CreateTestFormField("Email", "email", "", "email", &field);
+ form.fields.push_back(field);
+ field_types.push_back(EMAIL_ADDRESS);
+ 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.
+ 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(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+
+ // 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(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+}
+
+// Test that credit card infobar metrics are logged correctly.
+TEST_F(AutofillMetricsTest, CreditCardInfoBar) {
+ testing::NiceMock<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) {
+ testing::NiceMock<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));
+ 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;
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+
+ std::vector<FormData> forms(1, form);
+
+ // 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(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+ }
+
+
+ // 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.
+ test::CreateTestFormField("Phone", "phone", "", "text", &field);
+ form.fields.push_back(field);
+ 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(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+ }
+
+ // 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 = base::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;
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+ 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(),
+ autofill::NO_SPECIAL_FORMS_SEEN);
+ }
+
+ // 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;
+ test::CreateTestFormField("Name", "name", "", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email", "email", "", "text", &field);
+ form.fields.push_back(field);
+ 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::NO_SPECIAL_FORMS_SEEN);
+ 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::NO_SPECIAL_FORMS_SEEN);
+ 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::NO_SPECIAL_FORMS_SEEN);
+ 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::NO_SPECIAL_FORMS_SEEN);
+ 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());
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_popup_delegate.h b/chromium/components/autofill/core/browser/autofill_popup_delegate.h
new file mode 100644
index 00000000000..ec9a2207bc6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_popup_delegate.h
@@ -0,0 +1,46 @@
+// 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_CORE_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
+
+#include "base/strings/string16.h"
+
+namespace content {
+class KeyboardListener;
+}
+
+namespace autofill {
+
+// 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 base::string16& value,
+ int identifier) = 0;
+
+ // Delete the described suggestion.
+ virtual void RemoveSuggestion(const base::string16& value,
+ int identifier) = 0;
+
+ // Informs the delegate that the Autofill previewed form should be cleared.
+ virtual void ClearPreviewedForm() = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc
new file mode 100644
index 00000000000..6e18011e5c4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_profile.cc
@@ -0,0 +1,906 @@
+// 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/core/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/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/contact_info.h"
+#include "components/autofill/core/browser/phone_number.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "grit/component_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+namespace {
+
+// Like |AutofillType::GetStorableType()|, but also returns |NAME_FULL| for
+// first, middle, and last name field types.
+ServerFieldType GetStorableTypeCollapsingNames(ServerFieldType type) {
+ ServerFieldType storable_type = AutofillType(type).GetStorableType();
+ if (AutofillType(storable_type).group() == NAME)
+ return NAME_FULL;
+
+ return storable_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<ServerFieldType>* suggested_fields,
+ ServerFieldType excluded_field,
+ std::vector<ServerFieldType>* distinguishing_fields) {
+ static const ServerFieldType 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<ServerFieldType> seen_fields;
+ seen_fields.insert(UNKNOWN_TYPE);
+ seen_fields.insert(GetStorableTypeCollapsingNames(excluded_field));
+
+ distinguishing_fields->clear();
+ for (std::vector<ServerFieldType>::const_iterator it =
+ suggested_fields->begin();
+ it != suggested_fields->end(); ++it) {
+ ServerFieldType suggested_type = GetStorableTypeCollapsingNames(*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 &&
+ GetStorableTypeCollapsingNames(excluded_field) == NAME_FULL) {
+ for (std::vector<ServerFieldType>::const_iterator it =
+ suggested_fields->begin();
+ it != suggested_fields->end(); ++it) {
+ if (*it != excluded_field &&
+ GetStorableTypeCollapsingNames(*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 base::string16 MultiString(const AutofillProfile& p,
+ ServerFieldType type) {
+ std::vector<base::string16> values;
+ p.GetRawMultiInfo(type, &values);
+ base::string16 accumulate;
+ for (size_t i = 0; i < values.size(); ++i) {
+ if (i > 0)
+ accumulate += ASCIIToUTF16(" ");
+ accumulate += values[i];
+ }
+ return accumulate;
+}
+
+base::string16 GetFormGroupInfo(const FormGroup& form_group,
+ const AutofillType& type,
+ const std::string& app_locale) {
+ return app_locale.empty() ?
+ form_group.GetRawInfo(type.GetStorableType()) :
+ form_group.GetInfo(type, app_locale);
+}
+
+template <class T>
+void CopyValuesToItems(ServerFieldType type,
+ const std::vector<base::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(const AutofillType& type,
+ const std::vector<T>& form_group_items,
+ const std::string& app_locale,
+ std::vector<base::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(ServerFieldTypeSet* type_set) {
+ ServerFieldTypeSet collapsed_set;
+ for (ServerFieldTypeSet::iterator it = type_set->begin();
+ it != type_set->end(); ++it) {
+ switch (*it) {
+ 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(*it);
+ }
+ }
+ std::swap(*type_set, collapsed_set);
+}
+
+class FindByPhone {
+ public:
+ FindByPhone(const base::string16& phone,
+ const std::string& country_code,
+ const std::string& app_locale)
+ : phone_(phone),
+ country_code_(country_code),
+ app_locale_(app_locale) {
+ }
+
+ bool operator()(const base::string16& phone) {
+ return i18n::PhoneNumbersMatch(phone, phone_, country_code_, app_locale_);
+ }
+
+ bool operator()(const base::string16* phone) {
+ return i18n::PhoneNumbersMatch(*phone, phone_, country_code_, app_locale_);
+ }
+
+ private:
+ base::string16 phone_;
+ std::string country_code_;
+ std::string app_locale_;
+};
+
+// Functor used to check for case-insensitive equality of two strings.
+struct CaseInsensitiveStringEquals
+ : public std::binary_function<base::string16, base::string16, bool>
+{
+ bool operator()(const base::string16& x, const base::string16& y) const {
+ return
+ x.size() == y.size() && StringToLowerASCII(x) == StringToLowerASCII(y);
+ }
+};
+
+} // namespace
+
+AutofillProfile::AutofillProfile(const std::string& guid,
+ const std::string& origin)
+ : AutofillDataModel(guid, origin),
+ name_(1),
+ email_(1),
+ phone_number_(1, PhoneNumber(this)) {
+}
+
+AutofillProfile::AutofillProfile()
+ : AutofillDataModel(base::GenerateGUID(), std::string()),
+ name_(1),
+ email_(1),
+ phone_number_(1, PhoneNumber(this)) {
+}
+
+AutofillProfile::AutofillProfile(const AutofillProfile& profile)
+ : AutofillDataModel(std::string(), std::string()) {
+ operator=(profile);
+}
+
+AutofillProfile::~AutofillProfile() {
+}
+
+AutofillProfile& AutofillProfile::operator=(const AutofillProfile& profile) {
+ if (this == &profile)
+ return *this;
+
+ set_guid(profile.guid());
+ set_origin(profile.origin());
+
+ label_ = profile.label_;
+ name_ = profile.name_;
+ email_ = profile.email_;
+ company_ = profile.company_;
+ phone_number_ = profile.phone_number_;
+
+ for (size_t i = 0; i < phone_number_.size(); ++i)
+ phone_number_[i].set_profile(this);
+
+ address_ = profile.address_;
+
+ return *this;
+}
+
+void AutofillProfile::GetMatchingTypes(
+ const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const {
+ FormGroupList info = FormGroups();
+ for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it)
+ (*it)->GetMatchingTypes(text, app_locale, matching_types);
+}
+
+base::string16 AutofillProfile::GetRawInfo(ServerFieldType type) const {
+ const FormGroup* form_group = FormGroupForType(AutofillType(type));
+ if (!form_group)
+ return base::string16();
+
+ return form_group->GetRawInfo(type);
+}
+
+void AutofillProfile::SetRawInfo(ServerFieldType type,
+ const base::string16& value) {
+ FormGroup* form_group = MutableFormGroupForType(AutofillType(type));
+ if (form_group)
+ form_group->SetRawInfo(type, CollapseWhitespace(value, false));
+}
+
+base::string16 AutofillProfile::GetInfo(const AutofillType& type,
+ const std::string& app_locale) const {
+ const FormGroup* form_group = FormGroupForType(type);
+ if (!form_group)
+ return base::string16();
+
+ return form_group->GetInfo(type, app_locale);
+}
+
+bool AutofillProfile::SetInfo(const AutofillType& type,
+ const base::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(
+ ServerFieldType type,
+ const std::vector<base::string16>& values) {
+ switch (AutofillType(type).group()) {
+ case NAME:
+ case NAME_BILLING:
+ CopyValuesToItems(type, values, &name_, NameInfo());
+ break;
+ case EMAIL:
+ CopyValuesToItems(type, values, &email_, EmailInfo());
+ break;
+ case PHONE_HOME:
+ case PHONE_BILLING:
+ CopyValuesToItems(type,
+ values,
+ &phone_number_,
+ PhoneNumber(this));
+ break;
+ default:
+ if (values.size() == 1) {
+ SetRawInfo(type, values[0]);
+ } else if (values.size() == 0) {
+ SetRawInfo(type, base::string16());
+ } else {
+ // Shouldn't attempt to set multiple values on single-valued field.
+ NOTREACHED();
+ }
+ break;
+ }
+}
+
+void AutofillProfile::GetRawMultiInfo(
+ ServerFieldType type,
+ std::vector<base::string16>* values) const {
+ GetMultiInfoImpl(AutofillType(type), std::string(), values);
+}
+
+void AutofillProfile::GetMultiInfo(const AutofillType& type,
+ const std::string& app_locale,
+ std::vector<base::string16>* values) const {
+ GetMultiInfoImpl(type, app_locale, values);
+}
+
+void AutofillProfile::FillFormField(const AutofillField& field,
+ size_t variant,
+ const std::string& app_locale,
+ FormFieldData* field_data) const {
+ AutofillType type = field.Type();
+ DCHECK_NE(CREDIT_CARD, type.group());
+ DCHECK(field_data);
+
+ if (type.GetStorableType() == PHONE_HOME_NUMBER) {
+ FillPhoneNumberField(field, variant, app_locale, field_data);
+ } else if (field_data->form_control_type == "select-one") {
+ FillSelectControl(type, app_locale, field_data);
+ } else {
+ std::vector<base::string16> values;
+ GetMultiInfo(type, app_locale, &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,
+ const std::string& app_locale,
+ FormFieldData* field_data) const {
+ std::vector<base::string16> values;
+ GetMultiInfo(field.Type(), app_locale, &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.
+ base::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 base::string16 AutofillProfile::Label() const {
+ return label_;
+}
+
+bool AutofillProfile::IsEmpty(const std::string& app_locale) const {
+ ServerFieldTypeSet types;
+ GetNonEmptyTypes(app_locale, &types);
+ return types.empty();
+}
+
+bool AutofillProfile::IsPresentButInvalid(ServerFieldType type) const {
+ std::string country = UTF16ToUTF8(GetRawInfo(ADDRESS_HOME_COUNTRY));
+ base::string16 data = GetRawInfo(type);
+ switch (type) {
+ case ADDRESS_HOME_STATE:
+ if (!data.empty() && country == "US" && !autofill::IsValidState(data))
+ return true;
+ break;
+
+ case ADDRESS_HOME_ZIP:
+ if (!data.empty() && country == "US" && !autofill::IsValidZip(data))
+ return true;
+ break;
+
+ case PHONE_HOME_WHOLE_NUMBER: {
+ if (!data.empty() && !i18n::PhoneObject(data, country).IsValidNumber())
+ return true;
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return false;
+}
+
+
+int AutofillProfile::Compare(const AutofillProfile& profile) const {
+ const ServerFieldType 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 ServerFieldType 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<base::string16> values_a;
+ std::vector<base::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() &&
+ origin() == profile.origin() &&
+ Compare(profile) == 0;
+}
+
+bool AutofillProfile::operator!=(const AutofillProfile& profile) const {
+ return !operator==(profile);
+}
+
+const base::string16 AutofillProfile::PrimaryValue() const {
+ return GetRawInfo(ADDRESS_HOME_LINE1) + GetRawInfo(ADDRESS_HOME_CITY);
+}
+
+bool AutofillProfile::IsSubsetOf(const AutofillProfile& profile,
+ const std::string& app_locale) const {
+ ServerFieldTypeSet types;
+ GetNonEmptyTypes(app_locale, &types);
+
+ for (ServerFieldTypeSet::const_iterator it = types.begin(); it != types.end();
+ ++it) {
+ if (*it == 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(*it).group() == PHONE_HOME) {
+ // Phone numbers should be canonicalized prior to being compared.
+ if (*it != PHONE_HOME_WHOLE_NUMBER) {
+ continue;
+ } else if (!i18n::PhoneNumbersMatch(
+ GetRawInfo(*it),
+ profile.GetRawInfo(*it),
+ UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY)),
+ app_locale)) {
+ return false;
+ }
+ } else if (StringToLowerASCII(GetRawInfo(*it)) !=
+ StringToLowerASCII(profile.GetRawInfo(*it))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void AutofillProfile::OverwriteWithOrAddTo(const AutofillProfile& profile,
+ const std::string& app_locale) {
+ // Verified profiles should never be overwritten with unverified data.
+ DCHECK(!IsVerified() || profile.IsVerified());
+ set_origin(profile.origin());
+
+ ServerFieldTypeSet field_types;
+ profile.GetNonEmptyTypes(app_locale, &field_types);
+
+ // Only transfer "full" types (e.g. full name) and not fragments (e.g.
+ // first name, last name).
+ CollapseCompoundFieldTypes(&field_types);
+
+ for (ServerFieldTypeSet::const_iterator iter = field_types.begin();
+ iter != field_types.end(); ++iter) {
+ if (AutofillProfile::SupportsMultiValue(*iter)) {
+ std::vector<base::string16> new_values;
+ profile.GetRawMultiInfo(*iter, &new_values);
+ std::vector<base::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<base::string16>::iterator value_iter =
+ new_values.begin();
+ value_iter != new_values.end(); ++value_iter) {
+ // Don't add duplicates.
+ if (group == PHONE_HOME) {
+ AddPhoneIfUnique(*value_iter, app_locale, &existing_values);
+ } else {
+ std::vector<base::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 {
+ base::string16 new_value = profile.GetRawInfo(*iter);
+ if (StringToLowerASCII(GetRawInfo(*iter)) !=
+ StringToLowerASCII(new_value)) {
+ SetRawInfo(*iter, new_value);
+ }
+ }
+ }
+}
+
+// static
+bool AutofillProfile::SupportsMultiValue(ServerFieldType type) {
+ FieldTypeGroup group = AutofillType(type).group();
+ return group == NAME ||
+ group == NAME_BILLING ||
+ group == EMAIL ||
+ group == PHONE_HOME ||
+ group == PHONE_BILLING;
+}
+
+// static
+bool AutofillProfile::AdjustInferredLabels(
+ std::vector<AutofillProfile*>* profiles) {
+ const size_t kMinimalFieldsShown = 2;
+
+ std::vector<base::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<ServerFieldType>* suggested_fields,
+ ServerFieldType excluded_field,
+ size_t minimal_fields_shown,
+ std::vector<base::string16>* created_labels) {
+ DCHECK(profiles);
+ DCHECK(created_labels);
+
+ std::vector<ServerFieldType> 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<base::string16, std::list<size_t> > labels;
+ for (size_t i = 0; i < profiles->size(); ++i) {
+ base::string16 label =
+ (*profiles)[i]->ConstructInferredLabel(fields_to_use,
+ minimal_fields_shown);
+ labels[label].push_back(i);
+ }
+
+ created_labels->resize(profiles->size());
+ for (std::map<base::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.
+ base::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(
+ ServerFieldTypeSet* supported_types) const {
+ FormGroupList info = FormGroups();
+ for (FormGroupList::const_iterator it = info.begin(); it != info.end(); ++it)
+ (*it)->GetSupportedTypes(supported_types);
+}
+
+bool AutofillProfile::FillCountrySelectControl(
+ const std::string& app_locale,
+ FormFieldData* field_data) const {
+ std::string country_code = UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY));
+
+ 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.
+ base::string16 value = field_data->option_values[i];
+ base::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(
+ const AutofillType& type,
+ const std::string& app_locale,
+ std::vector<base::string16>* values) const {
+ switch (type.group()) {
+ case NAME:
+ case NAME_BILLING:
+ CopyItemsToValues(type, name_, app_locale, values);
+ break;
+ case EMAIL:
+ CopyItemsToValues(type, email_, app_locale, values);
+ break;
+ case PHONE_HOME:
+ case PHONE_BILLING:
+ CopyItemsToValues(type, phone_number_, app_locale, values);
+ break;
+ default:
+ values->resize(1);
+ (*values)[0] = GetFormGroupInfo(*this, type, app_locale);
+ }
+}
+
+void AutofillProfile::AddPhoneIfUnique(
+ const base::string16& phone,
+ const std::string& app_locale,
+ std::vector<base::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.
+ std::string country_code = UTF16ToASCII(GetRawInfo(ADDRESS_HOME_COUNTRY));
+ if (std::find_if(existing_phones->begin(), existing_phones->end(),
+ FindByPhone(phone, country_code, app_locale)) ==
+ existing_phones->end()) {
+ existing_phones->push_back(phone);
+ }
+}
+
+base::string16 AutofillProfile::ConstructInferredLabel(
+ const std::vector<ServerFieldType>& included_fields,
+ size_t num_fields_to_use) const {
+ const base::string16 separator =
+ l10n_util::GetStringUTF16(IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR);
+
+ base::string16 label;
+ size_t num_fields_used = 0;
+ for (std::vector<ServerFieldType>::const_iterator it =
+ included_fields.begin();
+ it != included_fields.end() && num_fields_used < num_fields_to_use;
+ ++it) {
+ base::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<ServerFieldType>& fields,
+ size_t num_fields_to_include,
+ std::vector<base::string16>* created_labels) {
+ // For efficiency, we first construct a map of fields to their text values and
+ // each value's frequency.
+ std::map<ServerFieldType,
+ std::map<base::string16, size_t> > field_text_frequencies_by_field;
+ for (std::vector<ServerFieldType>::const_iterator field = fields.begin();
+ field != fields.end(); ++field) {
+ std::map<base::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];
+ base::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<ServerFieldType> label_fields;
+ bool found_differentiating_field = false;
+ for (std::vector<ServerFieldType>::const_iterator field = fields.begin();
+ field != fields.end(); ++field) {
+ // Skip over empty fields.
+ base::string16 field_text = profile->GetRawInfo(*field);
+ if (field_text.empty())
+ continue;
+
+ std::map<base::string16, size_t>& field_text_frequencies =
+ field_text_frequencies_by_field[*field];
+ found_differentiating_field |=
+ !field_text_frequencies.count(base::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] = &phone_number_[0];
+ v[4] = &address_;
+ return v;
+}
+
+const FormGroup* AutofillProfile::FormGroupForType(
+ const AutofillType& type) const {
+ return const_cast<AutofillProfile*>(this)->MutableFormGroupForType(type);
+}
+
+FormGroup* AutofillProfile::MutableFormGroupForType(const AutofillType& type) {
+ switch (type.group()) {
+ case NAME:
+ case NAME_BILLING:
+ return &name_[0];
+
+ case EMAIL:
+ return &email_[0];
+
+ case COMPANY:
+ return &company_;
+
+ case PHONE_HOME:
+ case PHONE_BILLING:
+ return &phone_number_[0];
+
+ case ADDRESS_HOME:
+ case ADDRESS_BILLING:
+ return &address_;
+
+ case NO_GROUP:
+ case CREDIT_CARD:
+ return NULL;
+ }
+
+ NOTREACHED();
+ return NULL;
+}
+
+// So we can compare AutofillProfiles with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile) {
+ return os
+ << UTF16ToUTF8(profile.Label())
+ << " "
+ << profile.guid()
+ << " "
+ << profile.origin()
+ << " "
+ << 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));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_profile.h b/chromium/components/autofill/core/browser/autofill_profile.h
new file mode 100644
index 00000000000..5a3f65c756a
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_profile.h
@@ -0,0 +1,217 @@
+// 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_CORE_BROWSER_AUTOFILL_PROFILE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <list>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/address.h"
+#include "components/autofill/core/browser/autofill_data_model.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/contact_info.h"
+#include "components/autofill/core/browser/phone_number.h"
+
+namespace autofill {
+
+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 AutofillDataModel {
+ public:
+ AutofillProfile(const std::string& guid, const std::string& origin);
+
+ // For use in STL containers.
+ AutofillProfile();
+ AutofillProfile(const AutofillProfile& profile);
+ virtual ~AutofillProfile();
+
+ AutofillProfile& operator=(const AutofillProfile& profile);
+
+ // FormGroup:
+ virtual void GetMatchingTypes(
+ const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const OVERRIDE;
+ virtual base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+ virtual base::string16 GetInfo(const AutofillType& type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) OVERRIDE;
+
+ // AutofillDataModel:
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ const std::string& app_locale,
+ FormFieldData* field_data) const OVERRIDE;
+
+ // Multi-value equivalents to |GetInfo| and |SetInfo|.
+ void SetRawMultiInfo(ServerFieldType type,
+ const std::vector<base::string16>& values);
+ void GetRawMultiInfo(ServerFieldType type,
+ std::vector<base::string16>* values) const;
+ void GetMultiInfo(const AutofillType& type,
+ const std::string& app_locale,
+ std::vector<base::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,
+ const std::string& app_locale,
+ 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 base::string16 Label() const;
+
+ // Returns true if there are no values (field types) set.
+ bool IsEmpty(const std::string& app_locale) const;
+
+ // Returns true if the |type| of data in this profile is present, but invalid.
+ // Otherwise returns false.
+ bool IsPresentButInvalid(ServerFieldType type) 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 and origins 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, origins, 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 base::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 std::string& app_locale) 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,
+ const std::string& app_locale);
+
+ // Returns |true| if |type| accepts multi-values.
+ static bool SupportsMultiValue(ServerFieldType 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<ServerFieldType>* suggested_fields,
+ ServerFieldType excluded_field,
+ size_t minimal_fields_shown,
+ std::vector<base::string16>* created_labels);
+
+ private:
+ typedef std::vector<const FormGroup*> FormGroupList;
+
+ // FormGroup:
+ virtual bool FillCountrySelectControl(const std::string& app_locale,
+ FormFieldData* field) const OVERRIDE;
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* 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(const AutofillType& type,
+ const std::string& app_locale,
+ std::vector<base::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 base::string16& phone,
+ const std::string& app_locale,
+ std::vector<base::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.
+ base::string16 ConstructInferredLabel(
+ const std::vector<ServerFieldType>& 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<ServerFieldType>& fields,
+ size_t num_fields_to_include,
+ std::vector<base::string16>* created_labels);
+
+ // Utilities for listing and lookup of the data members that constitute
+ // user-visible profile information.
+ FormGroupList FormGroups() const;
+ const FormGroup* FormGroupForType(const AutofillType& type) const;
+ FormGroup* MutableFormGroupForType(const AutofillType& type);
+
+ // The label presented to the user when selecting a profile.
+ base::string16 label_;
+
+ // Personal information for this profile.
+ std::vector<NameInfo> name_;
+ std::vector<EmailInfo> email_;
+ CompanyInfo company_;
+ std::vector<PhoneNumber> phone_number_;
+ Address address_;
+};
+
+// So we can compare AutofillProfiles with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillProfile& profile);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_PROFILE_H_
diff --git a/chromium/components/autofill/core/browser/autofill_profile_unittest.cc b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc
new file mode 100644
index 00000000000..f67c536324d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_profile_unittest.cc
@@ -0,0 +1,931 @@
+// 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 "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/strings/string16.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "grit/component_strings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+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(base::GenerateGUID(), "https://www.example.com/");
+ // Empty profile - nothing to update.
+ EXPECT_FALSE(UpdateProfileLabel(&profile0));
+ base::string16 summary0 = profile0.Label();
+ EXPECT_EQ(base::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(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile00, "", "", "",
+ "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA", "91601", "US",
+ "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile00));
+ base::string16 summary00 = profile00.Label();
+ EXPECT_EQ(ASCIIToUTF16("Hollywood, CA"), summary00);
+
+ // Case 1: "<address>" without line 2.
+ AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile1, "", "", "",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile1));
+ base::string16 summary1 = profile1.Label();
+ EXPECT_EQ(ASCIIToUTF16("123 Zoo St., Hollywood"), summary1);
+
+ // Case 1a: "<address>" with line 2.
+ AutofillProfile profile1a(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile1a, "", "", "",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile1a));
+ base::string16 summary1a = profile1a.Label();
+ EXPECT_EQ(ASCIIToUTF16("123 Zoo St., unit 5"), summary1a);
+
+ // Case 2: "<lastname>"
+ AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile2, "", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile2));
+ base::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(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile3, "", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "",
+ "Hollywood", "CA", "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile3));
+ base::string16 summary3 = profile3.Label();
+ EXPECT_EQ(ASCIIToUTF16("Mitchell Morrison, 123 Zoo St."), summary3);
+
+ // Case 4: "<firstname>"
+ AutofillProfile profile4(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile4, "Marion", "Mitchell", "",
+ "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA", "91601", "US",
+ "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile4));
+ base::string16 summary4 = profile4.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell, Hollywood"), summary4);
+
+ // Case 5: "<firstname>, <address>"
+ AutofillProfile profile5(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile5, "Marion", "Mitchell", "",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile5));
+ base::string16 summary5 = profile5.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell, 123 Zoo St."), summary5);
+
+ // Case 6: "<firstname> <lastname>"
+ AutofillProfile profile6(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile6, "Marion", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "", "", "Hollywood", "CA",
+ "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile6));
+ base::string16 summary6 = profile6.Label();
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell Morrison, Hollywood"),
+ summary6);
+
+ // Case 7: "<firstname> <lastname>, <address>"
+ AutofillProfile profile7(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile7, "Marion", "Mitchell",
+ "Morrison", "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US", "16505678910");
+ EXPECT_TRUE(UpdateProfileLabel(&profile7));
+ base::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(base::GenerateGUID(), "https://www.example.com/");
+ 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();
+ base::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) {
+ ScopedVector<AutofillProfile> profiles;
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(
+ profiles[0],
+ "John",
+ "",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "http://www.example.com/"));
+ 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.get()));
+ // No need to adjust them anymore.
+ EXPECT_FALSE(AutofillProfile::AdjustInferredLabels(&profiles.get()));
+ 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(base::GenerateGUID(), "Chrome settings"));
+ test::SetProfileInfo(
+ profiles[2],
+ "John",
+ "",
+ "Doe",
+ "johndoe@tertium.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ EXPECT_TRUE(AutofillProfile::AdjustInferredLabels(&profiles.get()));
+
+ // 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());
+
+ profiles.resize(2);
+
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), std::string()));
+ 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.get()));
+
+ // 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(base::GenerateGUID(), "https://www.example.com/"));
+ 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.get()));
+
+ 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(base::GenerateGUID(), "https://www.example.com/"));
+ 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.get()));
+
+ 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.get()));
+}
+
+TEST(AutofillProfileTest, CreateInferredLabels) {
+ ScopedVector<AutofillProfile> profiles;
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0],
+ "John",
+ "",
+ "Doe",
+ "johndoe@hades.com",
+ "Underworld",
+ "666 Erebus St.",
+ "",
+ "Elysium", "CA",
+ "91111",
+ "US",
+ "16502111111");
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[1],
+ "Jane",
+ "",
+ "Doe",
+ "janedoe@tertium.com",
+ "Pluto Inc.",
+ "123 Letha Shore.",
+ "",
+ "Dis", "CA",
+ "91222",
+ "US",
+ "12345678910");
+ std::vector<base::string16> labels;
+ // Two fields at least - no filter.
+ AutofillProfile::CreateInferredLabels(&profiles.get(), 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.get(), 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<ServerFieldType> 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.get(), &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.get(), &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.get(), &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.get(), &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.get(), &suggested_fields,
+ NAME_MIDDLE, 1, &labels);
+ EXPECT_EQ(base::string16(), labels[0]);
+ EXPECT_EQ(base::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.get(), &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.get(), &suggested_fields,
+ NAME_FULL, 1, &labels);
+ EXPECT_EQ(base::string16(ASCIIToUTF16("666 Erebus St.")), labels[0]);
+ EXPECT_EQ(base::string16(ASCIIToUTF16("123 Letha Shore.")), labels[1]);
+}
+
+// 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(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0],
+ "John", "", "Doe", "doe@example.com", "",
+ "88 Nowhere Ave.", "", "", "", "", "", "");
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ 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<ServerFieldType> suggested_fields;
+ suggested_fields.push_back(NAME_LAST);
+ suggested_fields.push_back(ADDRESS_HOME_LINE1);
+ suggested_fields.push_back(EMAIL_ADDRESS);
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0],
+ "John", "", "Doe", "doe@example.com", "",
+ "88 Nowhere Ave.", "", "", "", "", "", "");
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ 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<ServerFieldType> suggested_fields;
+ suggested_fields.push_back(ADDRESS_HOME_LINE1);
+ suggested_fields.push_back(ADDRESS_BILLING_LINE1);
+ suggested_fields.push_back(EMAIL_ADDRESS);
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[0],
+ "John", "", "Doe", "doe@example.com",
+ "Gogole", "", "", "", "", "", "", "");
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[1],
+ "John", "", "Doe", "doe@example.com",
+ "Ggoole", "", "", "", "", "", "", "");
+ profiles.push_back(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(profiles[2],
+ "John", "", "Doe", "john.doe@example.com",
+ "Goolge", "", "", "", "", "", "", "");
+
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/"));
+ b.reset(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(a.get(), "Thomas", NULL, "Jefferson",
+ "declaration_guy@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ 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, "en-US"));
+
+ // |b| is not a subset of |a|.
+ EXPECT_FALSE(b->IsSubsetOf(*a, "en-US"));
+
+ // |a| is a subset of |a|.
+ EXPECT_TRUE(a->IsSubsetOf(*a, "en-US"));
+
+ // One field in |b| is different.
+ a.reset(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ b.reset(
+ new AutofillProfile(base::GenerateGUID(), "https://www.example.com/"));
+ test::SetProfileInfo(a.get(), "Thomas", NULL, "Jefferson",
+ "declaration_guy@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ test::SetProfileInfo(a.get(), "Thomas", NULL, "Adams",
+ "declaration_guy@gmail.com", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
+ EXPECT_FALSE(a->IsSubsetOf(*b, "en-US"));
+}
+
+TEST(AutofillProfileTest, OverwriteWithOrAddTo) {
+ AutofillProfile a(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&a, "Marion", "Mitchell", "Morrison",
+ "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US",
+ "12345678910");
+ std::vector<base::string16> names;
+ a.GetRawMultiInfo(NAME_FULL, &names);
+ names.push_back(ASCIIToUTF16("Marion Morrison"));
+ a.SetRawMultiInfo(NAME_FULL, names);
+
+ // Create an identical profile except that the new profile:
+ // (1) Has a different origin,
+ // (2) Has a different address line 2,
+ // (3) Lacks a company name, and
+ // (4) Has a different full name variant.
+ AutofillProfile b = a;
+ b.set_guid(base::GenerateGUID());
+ b.set_origin("Chrome settings");
+ b.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("area 51"));
+ b.SetRawInfo(COMPANY_NAME, base::string16());
+ b.GetRawMultiInfo(NAME_FULL, &names);
+ names.push_back(ASCIIToUTF16("Marion M. Morrison"));
+ b.SetRawMultiInfo(NAME_FULL, names);
+
+ a.OverwriteWithOrAddTo(b, "en-US");
+ EXPECT_EQ("Chrome settings", a.origin());
+ EXPECT_EQ(ASCIIToUTF16("area 51"), a.GetRawInfo(ADDRESS_HOME_LINE2));
+ EXPECT_EQ(ASCIIToUTF16("Fox"), a.GetRawInfo(COMPANY_NAME));
+ a.GetRawMultiInfo(NAME_FULL, &names);
+ ASSERT_EQ(3U, names.size());
+ EXPECT_EQ(ASCIIToUTF16("Marion Mitchell Morrison"), names[0]);
+ EXPECT_EQ(ASCIIToUTF16("Marion Morrison"), names[1]);
+ EXPECT_EQ(ASCIIToUTF16("Marion M. Morrison"), names[2]);
+}
+
+TEST(AutofillProfileTest, AssignmentOperator) {
+ AutofillProfile a(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&a, "Marion", "Mitchell", "Morrison",
+ "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US",
+ "12345678910");
+
+ // Result of assignment should be logically equal to the original profile.
+ AutofillProfile b(base::GenerateGUID(), "http://www.example.com/");
+ 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(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&a, "Marion", "Mitchell", "Morrison",
+ "marion@me.xyz", "Fox", "123 Zoo St.", "unit 5",
+ "Hollywood", "CA", "91601", "US",
+ "12345678910");
+
+ // Clone should be logically equal to the original.
+ AutofillProfile b(a);
+ EXPECT_TRUE(a == b);
+}
+
+TEST(AutofillProfileTest, Compare) {
+ AutofillProfile a(base::GenerateGUID(), std::string());
+ AutofillProfile b(base::GenerateGUID(), std::string());
+
+ // 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));
+
+ // Origins don't count.
+ a.set_origin("apple");
+ b.set_origin("banana");
+ EXPECT_EQ(0, a.Compare(b));
+
+ // Different values produce non-zero results.
+ test::SetProfileInfo(&a, "Jimmy", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ 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
+ test::SetProfileInfo(&a, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "650.555.4321");
+ 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, MultiValueNames) {
+ AutofillProfile p(base::GenerateGUID(), "https://www.example.com/");
+ const base::string16 kJohnDoe(ASCIIToUTF16("John Doe"));
+ const base::string16 kJohnPDoe(ASCIIToUTF16("John P. Doe"));
+ std::vector<base::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<base::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 base::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(base::string16(), get_values[0]);
+
+ // Expect regular |GetInfo| returns empty value.
+ EXPECT_EQ(base::string16(), p.GetRawInfo(NAME_FULL));
+}
+
+TEST(AutofillProfileTest, MultiValueEmails) {
+ AutofillProfile p(base::GenerateGUID(), "https://www.example.com/");
+ const base::string16 kJohnDoe(ASCIIToUTF16("john@doe.com"));
+ const base::string16 kJohnPDoe(ASCIIToUTF16("john_p@doe.com"));
+ std::vector<base::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<base::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 base::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(base::string16(), get_values[0]);
+
+ // Expect regular |GetInfo| returns empty value.
+ EXPECT_EQ(base::string16(), p.GetRawInfo(EMAIL_ADDRESS));
+}
+
+TEST(AutofillProfileTest, MultiValuePhone) {
+ AutofillProfile p(base::GenerateGUID(), "https://www.example.com/");
+ const base::string16 kJohnDoe(ASCIIToUTF16("4151112222"));
+ const base::string16 kJohnPDoe(ASCIIToUTF16("4151113333"));
+ std::vector<base::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<base::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 base::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(base::string16(), get_values[0]);
+
+ // Expect regular |GetInfo| returns empty value.
+ EXPECT_EQ(base::string16(), p.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+}
+
+TEST(AutofillProfileTest, AddressCountryFull) {
+ const char* const kCountries[] = {
+ "Albania", "Canada"
+ };
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("CA"));
+ profile.FillSelectControl(
+ AutofillType(ADDRESS_HOME_COUNTRY), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("Canada"), field.value);
+}
+
+TEST(AutofillProfileTest, AddressCountryAbbrev) {
+ const char* const kCountries[] = {
+ "AL", "CA"
+ };
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("CA"));
+ profile.FillSelectControl(
+ AutofillType(ADDRESS_HOME_COUNTRY), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("CA"), field.value);
+}
+
+TEST(AutofillProfileTest, AddressStateFull) {
+ const char* const kStates[] = {
+ "Alabama", "California"
+ };
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.FillSelectControl(AutofillType(ADDRESS_HOME_STATE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("California"), field.value);
+}
+
+TEST(AutofillProfileTest, AddressStateAbbrev) {
+ const char* const kStates[] = {
+ "AL", "CA"
+ };
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.FillSelectControl(AutofillType(ADDRESS_HOME_STATE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("CA"), field.value);
+}
+
+TEST(AutofillProfileTest, FillByValue) {
+ const char* const kStates[] = {
+ "Alabama", "California"
+ };
+ std::vector<base::string16> values(arraysize(kStates));
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.FillSelectControl(AutofillType(ADDRESS_HOME_STATE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("California"), field.value);
+}
+
+TEST(AutofillProfileTest, FillByContents) {
+ const char* const kStates[] = {
+ "Alabama", "California"
+ };
+ std::vector<base::string16> values(arraysize(kStates));
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("California"));
+ profile.FillSelectControl(AutofillType(ADDRESS_HOME_STATE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("2"), field.value);
+}
+
+TEST(AutofillProfileTest, IsPresentButInvalid) {
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
+ EXPECT_FALSE(profile.IsPresentButInvalid(ADDRESS_HOME_STATE));
+ EXPECT_FALSE(profile.IsPresentButInvalid(ADDRESS_HOME_ZIP));
+ EXPECT_FALSE(profile.IsPresentButInvalid(PHONE_HOME_WHOLE_NUMBER));
+
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ EXPECT_FALSE(profile.IsPresentButInvalid(ADDRESS_HOME_STATE));
+ EXPECT_FALSE(profile.IsPresentButInvalid(ADDRESS_HOME_ZIP));
+ EXPECT_FALSE(profile.IsPresentButInvalid(PHONE_HOME_WHOLE_NUMBER));
+
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("C"));
+ EXPECT_TRUE(profile.IsPresentButInvalid(ADDRESS_HOME_STATE));
+
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ EXPECT_FALSE(profile.IsPresentButInvalid(ADDRESS_HOME_STATE));
+
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90"));
+ EXPECT_TRUE(profile.IsPresentButInvalid(ADDRESS_HOME_ZIP));
+
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90210"));
+ EXPECT_FALSE(profile.IsPresentButInvalid(ADDRESS_HOME_ZIP));
+
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("310"));
+ EXPECT_TRUE(profile.IsPresentButInvalid(PHONE_HOME_WHOLE_NUMBER));
+
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("(310) 310-6000"));
+ EXPECT_FALSE(profile.IsPresentButInvalid(PHONE_HOME_WHOLE_NUMBER));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_regex_constants.cc.utf8 b/chromium/components/autofill/core/browser/autofill_regex_constants.cc.utf8
new file mode 100644
index 00000000000..960f43269db
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_regex_constants.cc.utf8
@@ -0,0 +1,294 @@
+// 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.
+
+// 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/core/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 kAddressLinesExtraRe[] =
+ "address.*line[3-9]|address[3-9]|addr[3-9]|street|line[3-9]"
+ "|municipio" // es
+ "|batiment|residence" // fr-FR
+ "|indirizzo[3-9]"; // 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"
+ "|courriel" // fr
+ "|メールアドレス" // 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/chromium/components/autofill/core/browser/autofill_regex_constants.h b/chromium/components/autofill/core/browser/autofill_regex_constants.h
new file mode 100644
index 00000000000..1c97dec282e
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_regex_constants.h
@@ -0,0 +1,59 @@
+// 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_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_
+#define COMPONENTS_AUTOFILL_CORE_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 kAddressLinesExtraRe[];
+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_CORE_BROWSER_AUTOFILL_REGEX_CONSTANTS_H_
diff --git a/chromium/components/autofill/core/browser/autofill_regexes.cc b/chromium/components/autofill/core/browser/autofill_regexes.cc
new file mode 100644
index 00000000000..c8b2ba487a8
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_regexes.cc
@@ -0,0 +1,84 @@
+// 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/core/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/strings/string16.h"
+#include "third_party/icu/source/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 base::string16& pattern);
+
+ private:
+ AutofillRegexes();
+ ~AutofillRegexes();
+ friend struct DefaultSingletonTraits<AutofillRegexes>;
+
+ // Maps patterns to their corresponding regex matchers.
+ std::map<base::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 base::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 base::string16& input,
+ const base::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/chromium/components/autofill/core/browser/autofill_regexes.h b/chromium/components/autofill/core/browser/autofill_regexes.h
new file mode 100644
index 00000000000..f4b57759e03
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_regexes.h
@@ -0,0 +1,20 @@
+// 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_CORE_BROWSER_AUTOFILL_REGEXES_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_
+
+#include "base/strings/string16.h"
+
+// Parsing utilities.
+namespace autofill {
+
+// Case-insensitive regular expression matching.
+// Returns true if |pattern| is found in |input|.
+bool MatchesPattern(const base::string16& input,
+ const base::string16& pattern);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_REGEXES_H_
diff --git a/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc b/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc
new file mode 100644
index 00000000000..453941567ff
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_regexes_unittest.cc
@@ -0,0 +1,64 @@
+// 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/core/browser/autofill_regexes.h"
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+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)));
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_scanner.cc b/chromium/components/autofill/core/browser/autofill_scanner.cc
new file mode 100644
index 00000000000..418362e4c8c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_scanner.cc
@@ -0,0 +1,58 @@
+// 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/core/browser/autofill_scanner.h"
+
+#include "base/logging.h"
+#include "components/autofill/core/browser/autofill_field.h"
+
+namespace autofill {
+
+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_);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_scanner.h b/chromium/components/autofill/core/browser/autofill_scanner.h
new file mode 100644
index 00000000000..2a634e6d8aa
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_scanner.h
@@ -0,0 +1,61 @@
+// 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_CORE_BROWSER_AUTOFILL_SCANNER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SCANNER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SCANNER_H_
diff --git a/chromium/components/autofill/core/browser/autofill_server_field_info.h b/chromium/components/autofill/core/browser/autofill_server_field_info.h
new file mode 100644
index 00000000000..e62cba7d095
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_server_field_info.h
@@ -0,0 +1,24 @@
+// 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_CORE_BROWSER_AUTOFILL_SERVER_FIELD_INFO_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SERVER_FIELD_INFO_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+
+struct AutofillServerFieldInfo {
+ // The predicted type returned by the Autofill server for this field.
+ ServerFieldType field_type;
+ // Default value to be used for the field (only applies to
+ // FIELD_WITH_DEFAULT_TYPE field type)
+ std::string default_value;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_SERVER_FIELD_INFO_H_
diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc
new file mode 100644
index 00000000000..f1e280ba9d8
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_type.cc
@@ -0,0 +1,612 @@
+// 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/core/browser/autofill_type.h"
+
+#include "base/logging.h"
+
+namespace autofill {
+
+AutofillType::AutofillType(ServerFieldType field_type)
+ : html_type_(HTML_TYPE_UNKNOWN),
+ html_mode_(HTML_MODE_NONE) {
+ 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)) {
+ server_type_ = UNKNOWN_TYPE;
+ } else {
+ server_type_ = field_type;
+ }
+}
+
+AutofillType::AutofillType(HtmlFieldType field_type, HtmlFieldMode mode)
+ : server_type_(UNKNOWN_TYPE),
+ html_type_(field_type),
+ html_mode_(mode) {}
+
+
+AutofillType::AutofillType(const AutofillType& autofill_type) {
+ *this = autofill_type;
+}
+
+AutofillType& AutofillType::operator=(const AutofillType& autofill_type) {
+ if (this != &autofill_type) {
+ this->server_type_ = autofill_type.server_type_;
+ this->html_type_ = autofill_type.html_type_;
+ this->html_mode_ = autofill_type.html_mode_;
+ }
+
+ return *this;
+}
+
+FieldTypeGroup AutofillType::group() const {
+ switch (server_type_) {
+ case NAME_FIRST:
+ case NAME_MIDDLE:
+ case NAME_LAST:
+ case NAME_MIDDLE_INITIAL:
+ case NAME_FULL:
+ case NAME_SUFFIX:
+ return NAME;
+
+ case NAME_BILLING_FIRST:
+ case NAME_BILLING_MIDDLE:
+ case NAME_BILLING_LAST:
+ case NAME_BILLING_MIDDLE_INITIAL:
+ case NAME_BILLING_FULL:
+ case NAME_BILLING_SUFFIX:
+ return NAME_BILLING;
+
+ 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_HOME;
+
+ case PHONE_BILLING_NUMBER:
+ case PHONE_BILLING_CITY_CODE:
+ case PHONE_BILLING_COUNTRY_CODE:
+ case PHONE_BILLING_CITY_AND_NUMBER:
+ case PHONE_BILLING_WHOLE_NUMBER:
+ return PHONE_BILLING;
+
+ 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;
+
+ case NO_SERVER_DATA:
+ case EMPTY_TYPE:
+ case PHONE_FAX_NUMBER:
+ case PHONE_FAX_CITY_CODE:
+ case PHONE_FAX_COUNTRY_CODE:
+ case PHONE_FAX_CITY_AND_NUMBER:
+ case PHONE_FAX_WHOLE_NUMBER:
+ case FIELD_WITH_DEFAULT_VALUE:
+ return NO_GROUP;
+
+ case MAX_VALID_FIELD_TYPE:
+ NOTREACHED();
+ return NO_GROUP;
+
+ case UNKNOWN_TYPE:
+ break;
+ }
+
+ switch (html_type_) {
+ case HTML_TYPE_NAME:
+ case HTML_TYPE_GIVEN_NAME:
+ case HTML_TYPE_ADDITIONAL_NAME:
+ case HTML_TYPE_ADDITIONAL_NAME_INITIAL:
+ case HTML_TYPE_FAMILY_NAME:
+ return html_mode_ == HTML_MODE_BILLING ? NAME_BILLING : NAME;
+
+ case HTML_TYPE_ORGANIZATION:
+ return COMPANY;
+
+ case HTML_TYPE_STREET_ADDRESS:
+ case HTML_TYPE_ADDRESS_LINE1:
+ case HTML_TYPE_ADDRESS_LINE2:
+ case HTML_TYPE_LOCALITY:
+ case HTML_TYPE_REGION:
+ case HTML_TYPE_COUNTRY_CODE:
+ case HTML_TYPE_COUNTRY_NAME:
+ case HTML_TYPE_POSTAL_CODE:
+ return html_mode_ == HTML_MODE_BILLING ? ADDRESS_BILLING : ADDRESS_HOME;
+
+ case HTML_TYPE_CREDIT_CARD_NAME:
+ case HTML_TYPE_CREDIT_CARD_NUMBER:
+ case HTML_TYPE_CREDIT_CARD_EXP:
+ case HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ case HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ case HTML_TYPE_CREDIT_CARD_EXP_MONTH:
+ case HTML_TYPE_CREDIT_CARD_EXP_YEAR:
+ case HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ case HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ case HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE:
+ case HTML_TYPE_CREDIT_CARD_TYPE:
+ return CREDIT_CARD;
+
+ case HTML_TYPE_TEL:
+ case HTML_TYPE_TEL_COUNTRY_CODE:
+ case HTML_TYPE_TEL_NATIONAL:
+ case HTML_TYPE_TEL_AREA_CODE:
+ case HTML_TYPE_TEL_LOCAL:
+ case HTML_TYPE_TEL_LOCAL_PREFIX:
+ case HTML_TYPE_TEL_LOCAL_SUFFIX:
+ return html_mode_ == HTML_MODE_BILLING ? PHONE_BILLING : PHONE_HOME;
+
+ case HTML_TYPE_EMAIL:
+ return EMAIL;
+
+ case HTML_TYPE_UNKNOWN:
+ break;
+ }
+
+ return NO_GROUP;
+}
+
+bool AutofillType::IsUnknown() const {
+ return server_type_ == UNKNOWN_TYPE && html_type_ == HTML_TYPE_UNKNOWN;
+}
+
+ServerFieldType AutofillType::GetStorableType() const {
+ // Map billing types to the equivalent non-billing types.
+ switch (server_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;
+
+ case PHONE_BILLING_WHOLE_NUMBER:
+ return PHONE_HOME_WHOLE_NUMBER;
+
+ case PHONE_BILLING_NUMBER:
+ return PHONE_HOME_NUMBER;
+
+ case PHONE_BILLING_CITY_CODE:
+ return PHONE_HOME_CITY_CODE;
+
+ case PHONE_BILLING_COUNTRY_CODE:
+ return PHONE_HOME_COUNTRY_CODE;
+
+ case PHONE_BILLING_CITY_AND_NUMBER:
+ return PHONE_HOME_CITY_AND_NUMBER;
+
+ case NAME_BILLING_FIRST:
+ return NAME_FIRST;
+
+ case NAME_BILLING_MIDDLE:
+ return NAME_MIDDLE;
+
+ case NAME_BILLING_LAST:
+ return NAME_LAST;
+
+ case NAME_BILLING_MIDDLE_INITIAL:
+ return NAME_MIDDLE_INITIAL;
+
+ case NAME_BILLING_FULL:
+ return NAME_FULL;
+
+ case NAME_BILLING_SUFFIX:
+ return NAME_SUFFIX;
+
+ case UNKNOWN_TYPE:
+ break; // Try to parse HTML types instead.
+
+ default:
+ return server_type_;
+ }
+
+ switch (html_type_) {
+ case HTML_TYPE_UNKNOWN:
+ return UNKNOWN_TYPE;
+
+ case HTML_TYPE_NAME:
+ return NAME_FULL;
+
+ case HTML_TYPE_GIVEN_NAME:
+ return NAME_FIRST;
+
+ case HTML_TYPE_ADDITIONAL_NAME:
+ return NAME_MIDDLE;
+
+ case HTML_TYPE_FAMILY_NAME:
+ return NAME_LAST;
+
+ case HTML_TYPE_ORGANIZATION:
+ return COMPANY_NAME;
+
+ case HTML_TYPE_STREET_ADDRESS:
+ return ADDRESS_HOME_LINE1;
+
+ case HTML_TYPE_ADDRESS_LINE1:
+ return ADDRESS_HOME_LINE1;
+
+ case HTML_TYPE_ADDRESS_LINE2:
+ return ADDRESS_HOME_LINE2;
+
+ case HTML_TYPE_LOCALITY:
+ return ADDRESS_HOME_CITY;
+
+ case HTML_TYPE_REGION:
+ return ADDRESS_HOME_STATE;
+
+ case HTML_TYPE_COUNTRY_CODE:
+ case HTML_TYPE_COUNTRY_NAME:
+ return ADDRESS_HOME_COUNTRY;
+
+ case HTML_TYPE_POSTAL_CODE:
+ return ADDRESS_HOME_ZIP;
+
+ case HTML_TYPE_CREDIT_CARD_NAME:
+ return CREDIT_CARD_NAME;
+
+ case HTML_TYPE_CREDIT_CARD_NUMBER:
+ return CREDIT_CARD_NUMBER;
+
+ case HTML_TYPE_CREDIT_CARD_EXP:
+ return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
+
+ case HTML_TYPE_CREDIT_CARD_EXP_MONTH:
+ return CREDIT_CARD_EXP_MONTH;
+
+ case HTML_TYPE_CREDIT_CARD_EXP_YEAR:
+ return CREDIT_CARD_EXP_4_DIGIT_YEAR;
+
+ case HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE:
+ return CREDIT_CARD_VERIFICATION_CODE;
+
+ case HTML_TYPE_CREDIT_CARD_TYPE:
+ return CREDIT_CARD_TYPE;
+
+ case HTML_TYPE_TEL:
+ return PHONE_HOME_WHOLE_NUMBER;
+
+ case HTML_TYPE_TEL_COUNTRY_CODE:
+ return PHONE_HOME_COUNTRY_CODE;
+
+ case HTML_TYPE_TEL_NATIONAL:
+ return PHONE_HOME_CITY_AND_NUMBER;
+
+ case HTML_TYPE_TEL_AREA_CODE:
+ return PHONE_HOME_CITY_CODE;
+
+ case HTML_TYPE_TEL_LOCAL:
+ case HTML_TYPE_TEL_LOCAL_PREFIX:
+ case HTML_TYPE_TEL_LOCAL_SUFFIX:
+ return PHONE_HOME_NUMBER;
+
+ case HTML_TYPE_EMAIL:
+ return EMAIL_ADDRESS;
+
+ case HTML_TYPE_ADDITIONAL_NAME_INITIAL:
+ return NAME_MIDDLE_INITIAL;
+
+ case HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ return CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
+
+ case HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ return CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
+
+ case HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ return CREDIT_CARD_EXP_2_DIGIT_YEAR;
+
+ case HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ return CREDIT_CARD_EXP_4_DIGIT_YEAR;
+ }
+
+ NOTREACHED();
+ return UNKNOWN_TYPE;
+}
+
+// static
+ServerFieldType AutofillType::GetEquivalentBillingFieldType(
+ ServerFieldType field_type) {
+ switch (field_type) {
+ case ADDRESS_HOME_LINE1:
+ return ADDRESS_BILLING_LINE1;
+
+ case ADDRESS_HOME_LINE2:
+ return ADDRESS_BILLING_LINE2;
+
+ case ADDRESS_HOME_APT_NUM:
+ return ADDRESS_BILLING_APT_NUM;
+
+ case ADDRESS_HOME_CITY:
+ return ADDRESS_BILLING_CITY;
+
+ case ADDRESS_HOME_STATE:
+ return ADDRESS_BILLING_STATE;
+
+ case ADDRESS_HOME_ZIP:
+ return ADDRESS_BILLING_ZIP;
+
+ case ADDRESS_HOME_COUNTRY:
+ return ADDRESS_BILLING_COUNTRY;
+
+ case PHONE_HOME_WHOLE_NUMBER:
+ return PHONE_BILLING_WHOLE_NUMBER;
+
+ case PHONE_HOME_NUMBER:
+ return PHONE_BILLING_NUMBER;
+
+ case PHONE_HOME_CITY_CODE:
+ return PHONE_BILLING_CITY_CODE;
+
+ case PHONE_HOME_COUNTRY_CODE:
+ return PHONE_BILLING_COUNTRY_CODE;
+
+ case PHONE_HOME_CITY_AND_NUMBER:
+ return PHONE_BILLING_CITY_AND_NUMBER;
+
+ case NAME_FIRST:
+ return NAME_BILLING_FIRST;
+
+ case NAME_MIDDLE:
+ return NAME_BILLING_MIDDLE;
+
+ case NAME_LAST:
+ return NAME_BILLING_LAST;
+
+ case NAME_MIDDLE_INITIAL:
+ return NAME_BILLING_MIDDLE_INITIAL;
+
+ case NAME_FULL:
+ return NAME_BILLING_FULL;
+
+ case NAME_SUFFIX:
+ return NAME_BILLING_SUFFIX;
+
+ default:
+ return field_type;
+ }
+}
+
+std::string AutofillType::ToString() const {
+ if (IsUnknown())
+ return "UNKNOWN_TYPE";
+
+ switch (server_type_) {
+ case NO_SERVER_DATA:
+ return "NO_SERVER_DATA";
+ case UNKNOWN_TYPE:
+ break; // Should be handled in the HTML type handling code below.
+ 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 NAME_BILLING_FIRST:
+ return "NAME_BILLING_FIRST";
+ case NAME_BILLING_MIDDLE:
+ return "NAME_BILLING_MIDDLE";
+ case NAME_BILLING_LAST:
+ return "NAME_BILLING_LAST";
+ case NAME_BILLING_MIDDLE_INITIAL:
+ return "NAME_BILLING_MIDDLE_INITIAL";
+ case NAME_BILLING_FULL:
+ return "NAME_BILLING_FULL";
+ case NAME_BILLING_SUFFIX:
+ return "NAME_BILLING_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 PHONE_FAX_NUMBER:
+ return "PHONE_FAX_NUMBER";
+ case PHONE_FAX_CITY_CODE:
+ return "PHONE_FAX_CITY_CODE";
+ case PHONE_FAX_COUNTRY_CODE:
+ return "PHONE_FAX_COUNTRY_CODE";
+ case PHONE_FAX_CITY_AND_NUMBER:
+ return "PHONE_FAX_CITY_AND_NUMBER";
+ case PHONE_FAX_WHOLE_NUMBER:
+ return "PHONE_FAX_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";
+ case FIELD_WITH_DEFAULT_VALUE:
+ return "FIELD_WITH_DEFAULT_VALUE";
+ case PHONE_BILLING_NUMBER:
+ return "PHONE_BILLING_NUMBER";
+ case PHONE_BILLING_CITY_CODE:
+ return "PHONE_BILLING_CITY_CODE";
+ case PHONE_BILLING_COUNTRY_CODE:
+ return "PHONE_BILLING_COUNTRY_CODE";
+ case PHONE_BILLING_CITY_AND_NUMBER:
+ return "PHONE_BILLING_CITY_AND_NUMBER";
+ case PHONE_BILLING_WHOLE_NUMBER:
+ return "PHONE_BILLING_WHOLE_NUMBER";
+ case MAX_VALID_FIELD_TYPE:
+ return std::string();
+ }
+
+ switch (html_type_) {
+ case HTML_TYPE_UNKNOWN:
+ NOTREACHED();
+ break;
+ case HTML_TYPE_NAME:
+ return "HTML_TYPE_NAME";
+ case HTML_TYPE_GIVEN_NAME:
+ return "HTML_TYPE_GIVEN_NAME";
+ case HTML_TYPE_ADDITIONAL_NAME:
+ return "HTML_TYPE_ADDITIONAL_NAME";
+ case HTML_TYPE_FAMILY_NAME:
+ return "HTML_TYPE_FAMILY_NAME";
+ case HTML_TYPE_ORGANIZATION:
+ return "HTML_TYPE_ORGANIZATION";
+ case HTML_TYPE_STREET_ADDRESS:
+ return "HTML_TYPE_STREET_ADDRESS";
+ case HTML_TYPE_ADDRESS_LINE1:
+ return "HTML_TYPE_ADDRESS_LINE1";
+ case HTML_TYPE_ADDRESS_LINE2:
+ return "HTML_TYPE_ADDRESS_LINE2";
+ case HTML_TYPE_LOCALITY:
+ return "HTML_TYPE_LOCALITY";
+ case HTML_TYPE_REGION:
+ return "HTML_TYPE_REGION";
+ case HTML_TYPE_COUNTRY_CODE:
+ return "HTML_TYPE_COUNTRY_CODE";
+ case HTML_TYPE_COUNTRY_NAME:
+ return "HTML_TYPE_COUNTRY_NAME";
+ case HTML_TYPE_POSTAL_CODE:
+ return "HTML_TYPE_POSTAL_CODE";
+ case HTML_TYPE_CREDIT_CARD_NAME:
+ return "HTML_TYPE_CREDIT_CARD_NAME";
+ case HTML_TYPE_CREDIT_CARD_NUMBER:
+ return "HTML_TYPE_CREDIT_CARD_NUMBER";
+ case HTML_TYPE_CREDIT_CARD_EXP:
+ return "HTML_TYPE_CREDIT_CARD_EXP";
+ case HTML_TYPE_CREDIT_CARD_EXP_MONTH:
+ return "HTML_TYPE_CREDIT_CARD_EXP_MONTH";
+ case HTML_TYPE_CREDIT_CARD_EXP_YEAR:
+ return "HTML_TYPE_CREDIT_CARD_EXP_YEAR";
+ case HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE:
+ return "HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE";
+ case HTML_TYPE_CREDIT_CARD_TYPE:
+ return "HTML_TYPE_CREDIT_CARD_TYPE";
+ case HTML_TYPE_TEL:
+ return "HTML_TYPE_TEL";
+ case HTML_TYPE_TEL_COUNTRY_CODE:
+ return "HTML_TYPE_TEL_COUNTRY_CODE";
+ case HTML_TYPE_TEL_NATIONAL:
+ return "HTML_TYPE_TEL_NATIONAL";
+ case HTML_TYPE_TEL_AREA_CODE:
+ return "HTML_TYPE_TEL_AREA_CODE";
+ case HTML_TYPE_TEL_LOCAL:
+ return "HTML_TYPE_TEL_LOCAL";
+ case HTML_TYPE_TEL_LOCAL_PREFIX:
+ return "HTML_TYPE_TEL_LOCAL_PREFIX";
+ case HTML_TYPE_TEL_LOCAL_SUFFIX:
+ return "HTML_TYPE_TEL_LOCAL_SUFFIX";
+ case HTML_TYPE_EMAIL:
+ return "HTML_TYPE_EMAIL";
+ case HTML_TYPE_ADDITIONAL_NAME_INITIAL:
+ return "HTML_TYPE_ADDITIONAL_NAME_INITIAL";
+ case HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ return "HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR";
+ case HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
+ return "HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR";
+ case HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ return "HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR";
+ case HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ return "HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR";
+ }
+
+ NOTREACHED();
+ return std::string();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_type.h b/chromium/components/autofill/core/browser/autofill_type.h
new file mode 100644
index 00000000000..41fa613b767
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_type.h
@@ -0,0 +1,59 @@
+// 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_CORE_BROWSER_AUTOFILL_TYPE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_TYPE_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+
+// 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:
+ explicit AutofillType(ServerFieldType field_type);
+ AutofillType(HtmlFieldType field_type, HtmlFieldMode mode);
+ AutofillType(const AutofillType& autofill_type);
+ AutofillType& operator=(const AutofillType& autofill_type);
+
+ HtmlFieldType html_type() const { return html_type_; }
+
+ FieldTypeGroup group() const;
+
+ // Returns true if both the |server_type_| and the |html_type_| are set to
+ // their respective enum's unknown value.
+ bool IsUnknown() const;
+
+ // Maps |this| type to a field type that can be directly stored in an Autofill
+ // data model (in the sense that it makes sense to call
+ // |AutofillDataModel::SetRawInfo()| with the returned field type as the first
+ // parameter). Note that the returned type might not be exactly equivalent to
+ // |this| type. For example, there is no exact analogue to the
+ // 'street-address' HTML type hint among the storable field types.
+ ServerFieldType GetStorableType() const;
+
+ // Serializes |this| type to a string.
+ std::string ToString() const;
+
+ // Maps |field_type| to the corresponding billing field type if the field type
+ // is an address, name, or phone number type.
+ static ServerFieldType GetEquivalentBillingFieldType(
+ ServerFieldType field_type);
+
+ private:
+ // The server-native field type, or UNKNOWN_TYPE if unset.
+ ServerFieldType server_type_;
+
+ // The HTML autocomplete field type and mode hints, or HTML_TYPE_UNKNOWN and
+ // HTML_MODE_NONE if unset.
+ HtmlFieldType html_type_;
+ HtmlFieldMode html_mode_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_TYPE_H_
diff --git a/chromium/components/autofill/core/browser/autofill_type_unittest.cc b/chromium/components/autofill/core/browser/autofill_type_unittest.cc
new file mode 100644
index 00000000000..71e98b4996d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_type_unittest.cc
@@ -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.
+
+#include "components/autofill/core/browser/autofill_type.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace {
+
+TEST(AutofillTypeTest, ServerFieldTypes) {
+ // No server data.
+ AutofillType none(NO_SERVER_DATA);
+ EXPECT_EQ(NO_SERVER_DATA, none.GetStorableType());
+ EXPECT_EQ(NO_GROUP, none.group());
+
+ // Unknown type.
+ AutofillType unknown(UNKNOWN_TYPE);
+ EXPECT_EQ(UNKNOWN_TYPE, unknown.GetStorableType());
+ EXPECT_EQ(NO_GROUP, unknown.group());
+
+ // Type with group but no subgroup.
+ AutofillType first(NAME_FIRST);
+ EXPECT_EQ(NAME_FIRST, first.GetStorableType());
+ EXPECT_EQ(NAME, first.group());
+
+ // Type with group and subgroup.
+ AutofillType phone(PHONE_HOME_NUMBER);
+ EXPECT_EQ(PHONE_HOME_NUMBER, phone.GetStorableType());
+ EXPECT_EQ(PHONE_HOME, phone.group());
+
+ // Billing type.
+ AutofillType billing_address(ADDRESS_BILLING_LINE1);
+ EXPECT_EQ(ADDRESS_HOME_LINE1, billing_address.GetStorableType());
+ EXPECT_EQ(ADDRESS_BILLING, billing_address.group());
+
+ // Last value, to check any offset errors.
+ AutofillType last(NAME_BILLING_SUFFIX);
+ EXPECT_EQ(NAME_SUFFIX, last.GetStorableType());
+ EXPECT_EQ(NAME_BILLING, last.group());
+
+ // Boundary (error) condition.
+ AutofillType boundary(MAX_VALID_FIELD_TYPE);
+ EXPECT_EQ(UNKNOWN_TYPE, boundary.GetStorableType());
+ EXPECT_EQ(NO_GROUP, boundary.group());
+
+ // Beyond the boundary (error) condition.
+ AutofillType beyond(static_cast<ServerFieldType>(MAX_VALID_FIELD_TYPE + 10));
+ EXPECT_EQ(UNKNOWN_TYPE, beyond.GetStorableType());
+ EXPECT_EQ(NO_GROUP, beyond.group());
+
+ // In-between value. Missing from enum but within range. Error condition.
+ AutofillType between(static_cast<ServerFieldType>(16));
+ EXPECT_EQ(UNKNOWN_TYPE, between.GetStorableType());
+ EXPECT_EQ(NO_GROUP, between.group());
+}
+
+TEST(AutofillTypeTest, HtmlFieldTypes) {
+ // Unknown type.
+ AutofillType unknown(HTML_TYPE_UNKNOWN, HTML_MODE_NONE);
+ EXPECT_EQ(UNKNOWN_TYPE, unknown.GetStorableType());
+ EXPECT_EQ(NO_GROUP, unknown.group());
+
+ // Type with group but no subgroup.
+ AutofillType first(HTML_TYPE_GIVEN_NAME, HTML_MODE_NONE);
+ EXPECT_EQ(NAME_FIRST, first.GetStorableType());
+ EXPECT_EQ(NAME, first.group());
+
+ // Type with group and subgroup.
+ AutofillType phone(HTML_TYPE_TEL, HTML_MODE_NONE);
+ EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER, phone.GetStorableType());
+ EXPECT_EQ(PHONE_HOME, phone.group());
+
+ // Last value, to check any offset errors.
+ AutofillType last(HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, HTML_MODE_NONE);
+ EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR, last.GetStorableType());
+ EXPECT_EQ(CREDIT_CARD, last.group());
+
+ // Shipping mode.
+ AutofillType shipping_first(HTML_TYPE_GIVEN_NAME, HTML_MODE_SHIPPING);
+ EXPECT_EQ(NAME_FIRST, shipping_first.GetStorableType());
+ EXPECT_EQ(NAME, shipping_first.group());
+
+ // Billing mode.
+ AutofillType billing_first(HTML_TYPE_GIVEN_NAME, HTML_MODE_BILLING);
+ EXPECT_EQ(NAME_FIRST, billing_first.GetStorableType());
+ EXPECT_EQ(NAME_BILLING, billing_first.group());
+}
+
+} // namespace
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_xml_parser.cc b/chromium/components/autofill/core/browser/autofill_xml_parser.cc
new file mode 100644
index 00000000000..c1acbe90f7f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_xml_parser.cc
@@ -0,0 +1,260 @@
+// 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/core/browser/autofill_xml_parser.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/core/browser/autofill_server_field_info.h"
+#include "third_party/libjingle/source/talk/xmllite/qname.h"
+
+namespace autofill {
+
+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,
+ AutocheckoutPageMetaData* page_meta_data)
+ : field_infos_(field_infos),
+ upload_required_(upload_required),
+ experiment_id_(experiment_id),
+ page_meta_data_(page_meta_data),
+ current_click_element_(NULL),
+ current_page_number_for_page_types_(0),
+ is_in_type_section_(false) {
+ DCHECK(upload_required_);
+ DCHECK(experiment_id_);
+ DCHECK(page_meta_data_);
+}
+
+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<ServerFieldType>(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)
+ page_meta_data_->current_page_number = GetIntValue(context, *attrs);
+ else if (attribute_name.compare("total_pages") == 0)
+ page_meta_data_->total_pages = GetIntValue(context, *attrs);
+ else if (attribute_name.compare("ignore_ajax") == 0)
+ page_meta_data_->ignore_ajax = strcmp(*attrs, "false") != 0;
+ ++attrs;
+ }
+ } else if (element.compare("page_advance_button") == 0) {
+ page_meta_data_->proceed_element_descriptor = WebElementDescriptor();
+ ParseElementDescriptor(context,
+ attrs,
+ &page_meta_data_->proceed_element_descriptor);
+ } else if (element.compare("click_elements_before_formfill") == 0) {
+ page_meta_data_->click_elements_before_form_fill.push_back(
+ WebElementDescriptor());
+ current_click_element_ = &page_meta_data_->click_elements_before_form_fill.
+ back();
+ } else if (element.compare("click_elements_after_formfill") == 0) {
+ page_meta_data_->click_elements_after_form_fill.push_back(
+ WebElementDescriptor());
+ current_click_element_ = &page_meta_data_->click_elements_after_form_fill.
+ back();
+ } else if (element.compare("web_element") == 0) {
+ ParseElementDescriptor(context, attrs, current_click_element_);
+ } else if (element.compare("flow_page") == 0) {
+ 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_for_page_types_ = GetIntValue(context, *attrs);
+ ++attrs;
+ }
+ } else if (element.compare("type") == 0) {
+ is_in_type_section_ = true;
+ }
+}
+
+void AutofillQueryXmlParser::ParseElementDescriptor(
+ buzz::XmlParseContext* context,
+ const char* const* attrs,
+ WebElementDescriptor* element_descriptor) {
+ // If both id and css_selector are set, the first one to appear will take
+ // precedence.
+ // |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();
+ 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()) {
+ element_descriptor->retrieval_method = autofill::WebElementDescriptor::ID;
+ element_descriptor->descriptor = attribute_value;
+ break;
+ } else if (attribute_name.compare("css_selector") == 0 &&
+ !attribute_value.empty()) {
+ element_descriptor->retrieval_method =
+ autofill::WebElementDescriptor::CSS_SELECTOR;
+ element_descriptor->descriptor = attribute_value;
+ break;
+ }
+ }
+}
+
+void AutofillQueryXmlParser::EndElement(buzz::XmlParseContext* context,
+ const char* name) {
+ is_in_type_section_ = false;
+}
+
+void AutofillQueryXmlParser::CharacterData(
+ buzz::XmlParseContext* context, const char* text, int len) {
+ if (!is_in_type_section_)
+ return;
+
+ int type = -1;
+ base::StringToInt(std::string(text, len), &type);
+ if (type >= AUTOCHECKOUT_STEP_MIN_VALUE &&
+ type <= AUTOCHECKOUT_STEP_MAX_VALUE) {
+ AutocheckoutStepType step_type =
+ static_cast<AutocheckoutStepType>(type);
+ page_meta_data_->page_types[current_page_number_for_page_types_]
+ .push_back(step_type);
+ }
+}
+
+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;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_xml_parser.h b/chromium/components/autofill/core/browser/autofill_xml_parser.h
new file mode 100644
index 00000000000..c01f13759c9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_xml_parser.h
@@ -0,0 +1,189 @@
+// 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_CORE_BROWSER_AUTOFILL_XML_PARSER_H_
+#define COMPONENTS_AUTOFILL_CORE_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/core/browser/autofill_server_field_info.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
+
+namespace autofill {
+
+struct AutocheckoutPageMetaData;
+
+// 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/core/browser/field_types.h.
+class AutofillQueryXmlParser : public AutofillXmlParser {
+ public:
+ AutofillQueryXmlParser(std::vector<AutofillServerFieldInfo>* field_infos,
+ UploadRequired* upload_required,
+ std::string* experiment_id,
+ AutocheckoutPageMetaData* page_meta_data);
+ virtual ~AutofillQueryXmlParser();
+
+ 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 parse a |WebElementDescriptor|.
+ // |context| is the current parsing context.
+ // |attrs| is the list of attributes (names and values) for the element.
+ // |element_descriptor| will be populated by this function.
+ void ParseElementDescriptor(buzz::XmlParseContext* context,
+ const char* const* attrs,
+ WebElementDescriptor* element_descriptor);
+
+ // 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;
+
+ // 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_;
+
+ // The server experiment to which this query response belongs.
+ // For the default server implementation, this is empty.
+ std::string* experiment_id_;
+
+ // Page metadata for multipage autofill flow.
+ AutocheckoutPageMetaData* page_meta_data_;
+
+ // The click element the parser is currently processing.
+ WebElementDescriptor* current_click_element_;
+
+ // Number of page whose type is currently being parsed.
+ int current_page_number_for_page_types_;
+
+ // Whether the instance is currently parsing inside 'type' tags.
+ bool is_in_type_section_;
+
+ 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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_XML_PARSER_H_
diff --git a/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc b/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc
new file mode 100644
index 00000000000..0c6b0f2154b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc
@@ -0,0 +1,442 @@
+// 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 <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/core/browser/autofill_xml_parser.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlparser.h"
+
+namespace autofill {
+namespace {
+
+class AutofillQueryXmlParserTest : public testing::Test {
+ public:
+ AutofillQueryXmlParserTest(): upload_required_(USE_UPLOAD_RATES) {};
+ virtual ~AutofillQueryXmlParserTest() {};
+
+ protected:
+ void ParseQueryXML(const std::string& xml, bool should_succeed) {
+ // Create a parser.
+ AutofillQueryXmlParser parse_handler(&field_infos_,
+ &upload_required_,
+ &experiment_id_,
+ &page_meta_data_);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(xml.c_str(), xml.length(), true);
+ EXPECT_EQ(should_succeed, parse_handler.succeeded());
+ }
+
+ std::vector<AutofillServerFieldInfo> field_infos_;
+ UploadRequired upload_required_;
+ std::string experiment_id_;
+ autofill::AutocheckoutPageMetaData page_meta_data_;
+};
+
+class AutofillUploadXmlParserTest : public testing::Test {
+ public:
+ AutofillUploadXmlParserTest(): positive_(0), negative_(0) {};
+ virtual ~AutofillUploadXmlParserTest() {};
+
+ protected:
+ void ParseUploadXML(const std::string& xml, bool should_succeed) {
+ // Create a parser.
+ AutofillUploadXmlParser parse_handler(&positive_, &negative_);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(xml.c_str(), xml.length(), true);
+
+ EXPECT_EQ(should_succeed, parse_handler.succeeded());
+ }
+
+ double positive_;
+ double negative_;
+};
+
+TEST_F(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>";
+ ParseQueryXML(xml, true);
+
+ 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_TRUE(field_infos_[3].default_value.empty());
+ EXPECT_EQ(FIELD_WITH_DEFAULT_VALUE, field_infos_[4].field_type);
+ EXPECT_EQ("default", field_infos_[4].default_value);
+ EXPECT_TRUE(experiment_id_.empty());
+}
+
+// Test parsing the upload required attribute.
+TEST_F(AutofillQueryXmlParserTest, TestUploadRequired) {
+ std::string xml = "<autofillqueryresponse uploadrequired=\"true\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(upload_required_, upload_required_);
+ ASSERT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos_[0].field_type);
+ EXPECT_TRUE(experiment_id_.empty());
+
+ field_infos_.clear();
+ xml = "<autofillqueryresponse uploadrequired=\"false\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(UPLOAD_NOT_REQUIRED, upload_required_);
+ ASSERT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos_[0].field_type);
+ EXPECT_TRUE(experiment_id_.empty());
+
+ field_infos_.clear();
+ xml = "<autofillqueryresponse uploadrequired=\"bad_value\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required_);
+ ASSERT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos_[0].field_type);
+ EXPECT_TRUE(experiment_id_.empty());
+}
+
+// Test parsing the experiment id attribute
+TEST_F(AutofillQueryXmlParserTest, ParseExperimentId) {
+ // When the attribute is missing, we should get back the default value -- the
+ // empty string.
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required_);
+ ASSERT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos_[0].field_type);
+ EXPECT_TRUE(experiment_id_.empty());
+
+ field_infos_.clear();
+
+ // When the attribute is present, make sure we parse it.
+ xml = "<autofillqueryresponse experimentid=\"FancyNewAlgorithm\">"
+ "<field autofilltype=\"0\" />"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ 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>";
+
+ ParseQueryXML(xml, true);
+
+ 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("ServerSmartyPants", experiment_id_);
+}
+
+// Fails on ASAN bot. http://crbug.com/253797
+#if defined(ADDRESS_SANITIZER)
+#define MAYBE_ParseAutofillFlow DISABLED_ParseAutofillFlow
+#else
+#define MAYBE_ParseAutofillFlow ParseAutofillFlow
+#endif
+
+// Test XML response with autofill_flow information.
+TEST_F(AutofillQueryXmlParserTest, MAYBE_ParseAutofillFlow) {
+ std::string xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+ "<page_advance_button id=\"foo\"/>"
+ "<flow_page page_no=\"0\">"
+ "<type>1</type>"
+ "<type>2</type>"
+ "</flow_page>"
+ "<flow_page page_no=\"1\">"
+ "<type>3</type>"
+ "</flow_page>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ EXPECT_TRUE(page_meta_data_.ignore_ajax);
+ EXPECT_EQ("foo", page_meta_data_.proceed_element_descriptor.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::ID,
+ page_meta_data_.proceed_element_descriptor.retrieval_method);
+ EXPECT_EQ(2U, page_meta_data_.page_types.size());
+ EXPECT_EQ(2U, page_meta_data_.page_types[0].size());
+ EXPECT_EQ(1U, page_meta_data_.page_types[1].size());
+ EXPECT_EQ(AUTOCHECKOUT_STEP_SHIPPING, page_meta_data_.page_types[0][0]);
+ EXPECT_EQ(AUTOCHECKOUT_STEP_DELIVERY, page_meta_data_.page_types[0][1]);
+ EXPECT_EQ(AUTOCHECKOUT_STEP_BILLING, page_meta_data_.page_types[1][0]);
+
+ // 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>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ EXPECT_EQ("[name=\"foo\"]",
+ page_meta_data_.proceed_element_descriptor.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ page_meta_data_.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>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ EXPECT_EQ("[name=\"foo\"]",
+ page_meta_data_.proceed_element_descriptor.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ page_meta_data_.proceed_element_descriptor.retrieval_method);
+
+ // Clear |field_infos_| for the next test;
+ field_infos_.clear();
+
+ // Test parsing click_elements_before_formfill correctly.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+ "<click_elements_before_formfill>"
+ "<web_element id=\"btn1\" /></click_elements_before_formfill>"
+ "<click_elements_before_formfill>"
+ "<web_element css_selector=\"[name=&quot;btn2&quot;]\"/>"
+ "</click_elements_before_formfill>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ ASSERT_EQ(2U, page_meta_data_.click_elements_before_form_fill.size());
+ autofill::WebElementDescriptor& click_elment =
+ page_meta_data_.click_elements_before_form_fill[0];
+ EXPECT_EQ("btn1", click_elment.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::ID, click_elment.retrieval_method);
+ click_elment = page_meta_data_.click_elements_before_form_fill[1];
+ EXPECT_EQ("[name=\"btn2\"]", click_elment.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ click_elment.retrieval_method);
+
+ // Clear |field_infos_| for the next test;
+ field_infos_.clear();
+
+ // Test parsing click_elements_after_formfill correctly.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\">"
+ "<click_elements_after_formfill>"
+ "<web_element id=\"btn1\" /></click_elements_after_formfill>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ ASSERT_EQ(1U, page_meta_data_.click_elements_after_form_fill.size());
+ click_elment = page_meta_data_.click_elements_after_form_fill[0];
+ EXPECT_EQ("btn1", click_elment.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::ID, click_elment.retrieval_method);
+
+ // Clear |field_infos_| for the next test.
+ field_infos_.clear();
+
+ // Test setting of ignore_ajax attribute.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\" ignore_ajax=\"true\">"
+ "<page_advance_button css_selector=\"[name=&quot;foo&quot;]\""
+ " id=\"foo\"/>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ EXPECT_TRUE(page_meta_data_.ignore_ajax);
+ EXPECT_EQ("[name=\"foo\"]",
+ page_meta_data_.proceed_element_descriptor.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ page_meta_data_.proceed_element_descriptor.retrieval_method);
+
+ // Clear |field_infos_| for the next test.
+ field_infos_.clear();
+
+ // Test redundant setting to false of ignore_ajax attribute.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"55\"/>"
+ "<autofill_flow page_no=\"1\" total_pages=\"10\" ignore_ajax=\"false\">"
+ "<page_advance_button css_selector=\"[name=&quot;foo&quot;]\""
+ " id=\"foo\"/>"
+ "</autofill_flow>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ EXPECT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(1, page_meta_data_.current_page_number);
+ EXPECT_EQ(10, page_meta_data_.total_pages);
+ EXPECT_FALSE(page_meta_data_.ignore_ajax);
+ EXPECT_EQ("[name=\"foo\"]",
+ page_meta_data_.proceed_element_descriptor.descriptor);
+ EXPECT_EQ(autofill::WebElementDescriptor::CSS_SELECTOR,
+ page_meta_data_.proceed_element_descriptor.retrieval_method);
+}
+
+// Test badly formed XML queries.
+TEST_F(AutofillQueryXmlParserTest, ParseErrors) {
+ // Test no Autofill type.
+ std::string xml = "<autofillqueryresponse>"
+ "<field/>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, false);
+
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required_);
+ EXPECT_EQ(0U, field_infos_.size());
+ EXPECT_TRUE(experiment_id_.empty());
+
+ // Test an incorrect Autofill type.
+ xml = "<autofillqueryresponse>"
+ "<field autofilltype=\"-1\"/>"
+ "</autofillqueryresponse>";
+
+ ParseQueryXML(xml, true);
+
+ 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_TRUE(experiment_id_.empty());
+
+ // 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>";
+
+ ParseQueryXML(xml, true);
+
+ 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_TRUE(experiment_id_.empty());
+
+ // 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_.
+ ParseQueryXML(xml, false);
+
+ EXPECT_EQ(USE_UPLOAD_RATES, upload_required_);
+ ASSERT_EQ(1U, field_infos_.size());
+ EXPECT_EQ(NO_SERVER_DATA, field_infos_[0].field_type);
+ EXPECT_TRUE(experiment_id_.empty());
+}
+
+// Test successfull upload response.
+TEST_F(AutofillUploadXmlParserTest, TestSuccessfulResponse) {
+ ParseUploadXML("<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"/>",
+ true);
+
+ EXPECT_DOUBLE_EQ(0.5, positive_);
+ EXPECT_DOUBLE_EQ(0.3, negative_);
+}
+
+// Test failed upload response.
+TEST_F(AutofillUploadXmlParserTest, TestFailedResponse) {
+ ParseUploadXML("<autofilluploadresponse positiveuploadrate=\"\" "
+ "negativeuploadrate=\"0.3\"/>",
+ false);
+
+ EXPECT_DOUBLE_EQ(0, positive_);
+ EXPECT_DOUBLE_EQ(0.3, negative_); // Partially parsed.
+ negative_ = 0;
+
+ ParseUploadXML("<autofilluploadresponse positiveuploadrate=\"0.5\" "
+ "negativeuploadrate=\"0.3\"",
+ false);
+
+ EXPECT_DOUBLE_EQ(0, positive_);
+ EXPECT_DOUBLE_EQ(0, negative_);
+
+ ParseUploadXML("bad data", false);
+
+ EXPECT_DOUBLE_EQ(0, positive_);
+ EXPECT_DOUBLE_EQ(0, negative_);
+
+ ParseUploadXML(std::string(), false);
+
+ EXPECT_DOUBLE_EQ(0, positive_);
+ EXPECT_DOUBLE_EQ(0, negative_);
+}
+
+} // namespace
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/contact_info.cc b/chromium/components/autofill/core/browser/contact_info.cc
new file mode 100644
index 00000000000..603c4fa3b54
--- /dev/null
+++ b/chromium/components/autofill/core/browser/contact_info.cc
@@ -0,0 +1,217 @@
+// 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/core/browser/contact_info.h"
+
+#include <stddef.h>
+#include <ostream>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_type.h"
+
+namespace autofill {
+
+static const ServerFieldType 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(ServerFieldTypeSet* 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);
+}
+
+base::string16 NameInfo::GetRawInfo(ServerFieldType type) const {
+ // TODO(isherman): Is GetStorableType even necessary?
+ switch (AutofillType(type).GetStorableType()) {
+ case NAME_FIRST:
+ return first();
+
+ case NAME_MIDDLE:
+ return middle();
+
+ case NAME_LAST:
+ return last();
+
+ case NAME_MIDDLE_INITIAL:
+ return MiddleInitial();
+
+ case NAME_FULL:
+ return FullName();
+
+ default:
+ return base::string16();
+ }
+}
+
+void NameInfo::SetRawInfo(ServerFieldType type, const base::string16& value) {
+ // TODO(isherman): Is GetStorableType even necessary?
+ ServerFieldType storable_type = AutofillType(type).GetStorableType();
+ DCHECK_EQ(NAME, AutofillType(storable_type).group());
+ switch (storable_type) {
+ case NAME_FIRST:
+ first_ = value;
+ break;
+
+ case NAME_MIDDLE:
+ case NAME_MIDDLE_INITIAL:
+ middle_ = value;
+ break;
+
+ case NAME_LAST:
+ last_ = value;
+ break;
+
+ case NAME_FULL:
+ SetFullName(value);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+base::string16 NameInfo::FullName() const {
+ std::vector<base::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, ' ');
+}
+
+base::string16 NameInfo::MiddleInitial() const {
+ if (middle_.empty())
+ return base::string16();
+
+ base::string16 middle_name(middle());
+ base::string16 initial;
+ initial.push_back(middle_name[0]);
+ return initial;
+}
+
+void NameInfo::SetFullName(const base::string16& full) {
+ // Clear the names.
+ first_ = base::string16();
+ middle_ = base::string16();
+ last_ = base::string16();
+
+ std::vector<base::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(ServerFieldTypeSet* supported_types) const {
+ supported_types->insert(EMAIL_ADDRESS);
+}
+
+base::string16 EmailInfo::GetRawInfo(ServerFieldType type) const {
+ if (type == EMAIL_ADDRESS)
+ return email_;
+
+ return base::string16();
+}
+
+void EmailInfo::SetRawInfo(ServerFieldType type, const base::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(ServerFieldTypeSet* supported_types) const {
+ supported_types->insert(COMPANY_NAME);
+}
+
+base::string16 CompanyInfo::GetRawInfo(ServerFieldType type) const {
+ if (type == COMPANY_NAME)
+ return company_name_;
+
+ return base::string16();
+}
+
+void CompanyInfo::SetRawInfo(ServerFieldType type,
+ const base::string16& value) {
+ DCHECK_EQ(COMPANY_NAME, type);
+ company_name_ = value;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/contact_info.h b/chromium/components/autofill/core/browser/contact_info.h
new file mode 100644
index 00000000000..007be1be81f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/contact_info.h
@@ -0,0 +1,101 @@
+// 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_CORE_BROWSER_CONTACT_INFO_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_CONTACT_INFO_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/form_group.h"
+
+namespace autofill {
+
+// 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 base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const OVERRIDE;
+
+ // Returns the full name, which can include up to the first, middle, and last
+ // name.
+ base::string16 FullName() const;
+
+ // Returns the middle initial if |middle_| is non-empty. Returns an empty
+ // string otherwise.
+ base::string16 MiddleInitial() const;
+
+ const base::string16& first() const { return first_; }
+ const base::string16& middle() const { return middle_; }
+ const base::string16& last() const { return last_; }
+
+ // Sets |first_|, |middle_|, and |last_| to the tokenized |full|.
+ // It is tokenized on a space only.
+ void SetFullName(const base::string16& full);
+
+ base::string16 first_;
+ base::string16 middle_;
+ base::string16 last_;
+};
+
+class EmailInfo : public FormGroup {
+ public:
+ EmailInfo();
+ EmailInfo(const EmailInfo& info);
+ virtual ~EmailInfo();
+
+ EmailInfo& operator=(const EmailInfo& info);
+
+ // FormGroup:
+ virtual base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const OVERRIDE;
+
+ base::string16 email_;
+};
+
+class CompanyInfo : public FormGroup {
+ public:
+ CompanyInfo();
+ CompanyInfo(const CompanyInfo& info);
+ virtual ~CompanyInfo();
+
+ CompanyInfo& operator=(const CompanyInfo& info);
+
+ // FormGroup:
+ virtual base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* supported_types) const OVERRIDE;
+
+ base::string16 company_name_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_CONTACT_INFO_H_
diff --git a/chromium/components/autofill/core/browser/contact_info_unittest.cc b/chromium/components/autofill/core/browser/contact_info_unittest.cc
new file mode 100644
index 00000000000..87e96ada779
--- /dev/null
+++ b/chromium/components/autofill/core/browser/contact_info_unittest.cc
@@ -0,0 +1,105 @@
+// 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/core/browser/contact_info.h"
+
+#include "base/basictypes.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+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), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), base::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), base::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, base::string16());
+ name.SetRawInfo(NAME_LAST, base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First"));
+
+ name.SetRawInfo(NAME_FIRST, base::string16());
+ name.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Middle"));
+ name.SetRawInfo(NAME_LAST, base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Middle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("Middle"));
+
+ name.SetRawInfo(NAME_FIRST, base::string16());
+ name.SetRawInfo(NAME_MIDDLE, base::string16());
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), base::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, base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), ASCIIToUTF16("Middle"));
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First Middle"));
+
+ name.SetRawInfo(NAME_FIRST, ASCIIToUTF16("First"));
+ name.SetRawInfo(NAME_MIDDLE, base::string16());
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), ASCIIToUTF16("First"));
+ EXPECT_EQ(name.GetRawInfo(NAME_MIDDLE), base::string16());
+ EXPECT_EQ(name.GetRawInfo(NAME_LAST), ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FULL), ASCIIToUTF16("First Last"));
+
+ name.SetRawInfo(NAME_FIRST, base::string16());
+ name.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Middle"));
+ name.SetRawInfo(NAME_LAST, ASCIIToUTF16("Last"));
+ EXPECT_EQ(name.GetRawInfo(NAME_FIRST), base::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"));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc
new file mode 100644
index 00000000000..f3ac6b112ab
--- /dev/null
+++ b/chromium/components/autofill/core/browser/credit_card.cc
@@ -0,0 +1,714 @@
+// 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/core/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/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regexes.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "grit/component_strings.h"
+#include "grit/webkit_resources.h"
+#include "third_party/icu/source/common/unicode/uloc.h"
+#include "third_party/icu/source/i18n/unicode/dtfmtsym.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+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;
+
+bool ConvertYear(const base::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 base::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".
+ base::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 base::string16 icu_month = base::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 base::string16 icu_month = base::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, const std::string& origin)
+ : AutofillDataModel(guid, origin),
+ type_(kGenericCard),
+ expiration_month_(0),
+ expiration_year_(0) {
+}
+
+CreditCard::CreditCard()
+ : AutofillDataModel(base::GenerateGUID(), std::string()),
+ type_(kGenericCard),
+ expiration_month_(0),
+ expiration_year_(0) {
+}
+
+CreditCard::CreditCard(const CreditCard& credit_card)
+ : AutofillDataModel(std::string(), std::string()) {
+ operator=(credit_card);
+}
+
+CreditCard::~CreditCard() {}
+
+// static
+const base::string16 CreditCard::StripSeparators(const base::string16& number) {
+ const char16 kSeparators[] = {'-', ' ', '\0'};
+ base::string16 stripped;
+ RemoveChars(number, kSeparators, &stripped);
+ return stripped;
+}
+
+// static
+base::string16 CreditCard::TypeForDisplay(const std::string& type) {
+ 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 == kUnionPay)
+ return l10n_util::GetStringUTF16(IDS_AUTOFILL_CC_UNION_PAY);
+ 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 base::string16();
+}
+
+// static
+int CreditCard::IconResourceId(const std::string& type) {
+ 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 == kUnionPay)
+ return IDR_AUTOFILL_CC_GENERIC; // Needs resource: http://crbug.com/259211
+ 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;
+}
+
+// static
+std::string CreditCard::GetCreditCardType(const base::string16& number) {
+ // Credit card number specifications taken from:
+ // http://en.wikipedia.org/wiki/Credit_card_numbers,
+ // http://en.wikipedia.org/wiki/List_of_Issuer_Identification_Numbers,
+ // http://www.discovernetwork.com/merchants/images/Merchant_Marketing_PDF.pdf,
+ // http://www.regular-expressions.info/creditcard.html,
+ // http://developer.ean.com/general_info/Valid_Credit_Card_Types,
+ // http://www.bincodes.com/,
+ // http://www.fraudpractice.com/FL-binCC.html, and
+ // http://www.beachnet.com/~hstiles/cardtype.html
+ //
+ // The last site is currently unavailable, but a cached version remains at
+ // http://web.archive.org/web/20120923111349/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,3095,36,38-39 14
+ // Discover Card 6011,644-649,65 16
+ // JCB 3528-3589 16
+ // MasterCard 51-55 16
+ // UnionPay 62 16-19
+
+ // Check for prefixes of length 1.
+ if (number.empty())
+ return kGenericCard;
+
+ if (number[0] == '4')
+ return kVisaCard;
+
+ // Check for prefixes of length 2.
+ if (number.size() < 2)
+ return kGenericCard;
+
+ int first_two_digits = 0;
+ if (!base::StringToInt(number.substr(0, 2), &first_two_digits))
+ return kGenericCard;
+
+ if (first_two_digits == 34 || first_two_digits == 37)
+ return kAmericanExpressCard;
+
+ if (first_two_digits == 36 ||
+ first_two_digits == 38 ||
+ first_two_digits == 39)
+ return kDinersCard;
+
+ if (first_two_digits >= 51 && first_two_digits <= 55)
+ return kMasterCard;
+
+ if (first_two_digits == 62)
+ return kUnionPay;
+
+ if (first_two_digits == 65)
+ return kDiscoverCard;
+
+ // Check for prefixes of length 3.
+ if (number.size() < 3)
+ return kGenericCard;
+
+ int first_three_digits = 0;
+ if (!base::StringToInt(number.substr(0, 3), &first_three_digits))
+ return kGenericCard;
+
+ if (first_three_digits >= 300 && first_three_digits <= 305)
+ return kDinersCard;
+
+ if (first_three_digits >= 644 && first_three_digits <= 649)
+ return kDiscoverCard;
+
+ // Check for prefixes of length 4.
+ if (number.size() < 4)
+ return kGenericCard;
+
+ int first_four_digits = 0;
+ if (!base::StringToInt(number.substr(0, 4), &first_four_digits))
+ return kGenericCard;
+
+ if (first_four_digits == 3095)
+ return kDinersCard;
+
+ if (first_four_digits >= 3528 && first_four_digits <= 3589)
+ return kJCBCard;
+
+ if (first_four_digits == 6011)
+ return kDiscoverCard;
+
+ return kGenericCard;
+}
+
+base::string16 CreditCard::GetRawInfo(ServerFieldType 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: {
+ base::string16 month = ExpirationMonthAsString();
+ base::string16 year = Expiration2DigitYearAsString();
+ if (!month.empty() && !year.empty())
+ return month + ASCIIToUTF16("/") + year;
+ return base::string16();
+ }
+
+ case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
+ base::string16 month = ExpirationMonthAsString();
+ base::string16 year = Expiration4DigitYearAsString();
+ if (!month.empty() && !year.empty())
+ return month + ASCIIToUTF16("/") + year;
+ return base::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 base::string16();
+
+ default:
+ // ComputeDataPresentForArray will hit this repeatedly.
+ return base::string16();
+ }
+}
+
+void CreditCard::SetRawInfo(ServerFieldType type,
+ const base::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;
+ }
+}
+
+base::string16 CreditCard::GetInfo(const AutofillType& type,
+ const std::string& app_locale) const {
+ ServerFieldType storable_type = type.GetStorableType();
+ if (storable_type == CREDIT_CARD_NUMBER)
+ return StripSeparators(number_);
+
+ return GetRawInfo(storable_type);
+}
+
+bool CreditCard::SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) {
+ ServerFieldType storable_type = type.GetStorableType();
+ if (storable_type == CREDIT_CARD_NUMBER)
+ SetRawInfo(storable_type, StripSeparators(value));
+ else if (storable_type == CREDIT_CARD_EXP_MONTH)
+ SetExpirationMonthFromString(value, app_locale);
+ else
+ SetRawInfo(storable_type, value);
+
+ return true;
+}
+
+void CreditCard::GetMatchingTypes(const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const {
+ FormGroup::GetMatchingTypes(text, app_locale, matching_types);
+
+ base::string16 card_number =
+ GetInfo(AutofillType(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 base::string16 CreditCard::Label() const {
+ base::string16 label;
+ if (number().empty())
+ return name_on_card_; // No CC number, return name only.
+
+ base::string16 obfuscated_cc_number = ObfuscatedNumber();
+ if (!expiration_month_ || !expiration_year_)
+ return obfuscated_cc_number; // No expiration date set.
+
+ // TODO(georgey): Internationalize date.
+ base::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 base::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<base::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);
+}
+
+base::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_;
+
+ base::string16 number = StripSeparators(number_);
+
+ // Avoid making very long obfuscated numbers.
+ size_t obfuscated_digits = std::min(kMaxObfuscationSize, number.size() - 4);
+ base::string16 result(obfuscated_digits, kCreditCardObfuscationSymbol);
+ return result.append(LastFourDigits());
+}
+
+base::string16 CreditCard::LastFourDigits() const {
+ static const size_t kNumLastDigits = 4;
+
+ base::string16 number = StripSeparators(number_);
+ if (number.size() < kNumLastDigits)
+ return base::string16();
+
+ return number.substr(number.size() - kNumLastDigits, kNumLastDigits);
+}
+
+base::string16 CreditCard::TypeForDisplay() const {
+ return CreditCard::TypeForDisplay(type_);
+}
+
+base::string16 CreditCard::TypeAndLastFourDigits() const {
+ base::string16 type = TypeForDisplay();
+ // TODO(estade): type may be empty, we probably want to return
+ // "Card - 1234" or something in that case.
+
+ base::string16 digits = LastFourDigits();
+ if (digits.empty())
+ return type;
+
+ // TODO(estade): i18n.
+ return type + ASCIIToUTF16(" - ") + digits;
+}
+
+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_;
+
+ set_guid(credit_card.guid());
+ set_origin(credit_card.origin());
+}
+
+bool CreditCard::UpdateFromImportedCard(const CreditCard& imported_card,
+ const std::string& app_locale) {
+ if (this->GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale) !=
+ imported_card.GetInfo(AutofillType(CREDIT_CARD_NUMBER), app_locale)) {
+ return false;
+ }
+
+ // Heuristically aggregated data should never overwrite verified data.
+ // Instead, discard any heuristically aggregated credit cards that disagree
+ // with explicitly entered data, so that the UI is not cluttered with
+ // duplicate cards.
+ if (this->IsVerified() && !imported_card.IsVerified())
+ return true;
+
+ set_origin(imported_card.origin());
+
+ // 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*/,
+ const std::string& app_locale,
+ FormFieldData* field_data) const {
+ DCHECK_EQ(CREDIT_CARD, field.Type().group());
+ DCHECK(field_data);
+
+ if (field_data->form_control_type == "select-one") {
+ FillSelectControl(field.Type(), app_locale, field_data);
+ } else if (field_data->form_control_type == "month") {
+ // HTML5 input="month" consists of year-month.
+ base::string16 year =
+ GetInfo(AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), app_locale);
+ base::string16 month =
+ GetInfo(AutofillType(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 ServerFieldType types[] = { CREDIT_CARD_NAME,
+ CREDIT_CARD_NUMBER,
+ CREDIT_CARD_EXP_MONTH,
+ CREDIT_CARD_EXP_4_DIGIT_YEAR };
+ for (size_t i = 0; i < arraysize(types); ++i) {
+ int comparison =
+ GetRawInfo(types[i]).compare(credit_card.GetRawInfo(types[i]));
+ if (comparison != 0)
+ return comparison;
+ }
+
+ return 0;
+}
+
+bool CreditCard::operator==(const CreditCard& credit_card) const {
+ return guid() == credit_card.guid() &&
+ origin() == credit_card.origin() &&
+ Compare(credit_card) == 0;
+}
+
+bool CreditCard::operator!=(const CreditCard& credit_card) const {
+ return !operator==(credit_card);
+}
+
+bool CreditCard::IsEmpty(const std::string& app_locale) const {
+ ServerFieldTypeSet types;
+ GetNonEmptyTypes(app_locale, &types);
+ return types.empty();
+}
+
+bool CreditCard::IsComplete() const {
+ return
+ autofill::IsValidCreditCardNumber(number_) &&
+ expiration_month_ != 0 &&
+ expiration_year_ != 0;
+}
+
+bool CreditCard::IsValid() const {
+ return autofill::IsValidCreditCardNumber(number_) &&
+ autofill::IsValidCreditCardExpirationDate(
+ expiration_year_, expiration_month_, base::Time::Now());
+}
+
+void CreditCard::GetSupportedTypes(ServerFieldTypeSet* 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);
+}
+
+base::string16 CreditCard::ExpirationMonthAsString() const {
+ if (expiration_month_ == 0)
+ return base::string16();
+
+ base::string16 month = base::IntToString16(expiration_month_);
+ if (expiration_month_ >= 10)
+ return month;
+
+ base::string16 zero = ASCIIToUTF16("0");
+ zero.append(month);
+ return zero;
+}
+
+base::string16 CreditCard::Expiration4DigitYearAsString() const {
+ if (expiration_year_ == 0)
+ return base::string16();
+
+ return base::IntToString16(Expiration4DigitYear());
+}
+
+base::string16 CreditCard::Expiration2DigitYearAsString() const {
+ if (expiration_year_ == 0)
+ return base::string16();
+
+ return base::IntToString16(Expiration2DigitYear());
+}
+
+void CreditCard::SetExpirationMonthFromString(const base::string16& text,
+ const std::string& app_locale) {
+ int month;
+ if (!ConvertMonth(text, app_locale, &month))
+ return;
+
+ SetExpirationMonth(month);
+}
+
+void CreditCard::SetExpirationYearFromString(const base::string16& text) {
+ int year;
+ if (!ConvertYear(text, &year))
+ return;
+
+ SetExpirationYear(year);
+}
+
+void CreditCard::SetNumber(const base::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()
+ << " "
+ << credit_card.origin()
+ << " "
+ << 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 kUnionPay = "unionPayCC";
+const char* const kVisaCard = "visaCC";
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/credit_card.h b/chromium/components/autofill/core/browser/credit_card.h
new file mode 100644
index 00000000000..d5bf1a0f7d7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/credit_card.h
@@ -0,0 +1,174 @@
+// 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_CORE_BROWSER_CREDIT_CARD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_H_
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_data_model.h"
+
+namespace autofill {
+
+struct FormFieldData;
+
+// A form group that stores credit card information.
+class CreditCard : public AutofillDataModel {
+ public:
+ CreditCard(const std::string& guid, const std::string& origin);
+
+ // 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 base::string16 StripSeparators(const base::string16& number);
+
+ // The user-visible type of the card, e.g. 'Mastercard'.
+ static base::string16 TypeForDisplay(const std::string& type);
+
+ // The ResourceBundle ID for the appropriate credit card image.
+ static int IconResourceId(const std::string& type);
+
+ // Returns the internal representation of credit card type corresponding to
+ // the given |number|. The credit card type is determined purely according to
+ // the Issuer Identification Number (IIN), a.k.a. the "Bank Identification
+ // Number (BIN)", which is parsed from the relevant prefix of the |number|.
+ // This function performs no additional validation checks on the |number|.
+ // Hence, the returned type for both the valid card "4111-1111-1111-1111" and
+ // the invalid card "4garbage" will be Visa, which has an IIN of 4.
+ static std::string GetCreditCardType(const base::string16& number);
+
+ // FormGroup:
+ virtual void GetMatchingTypes(
+ const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const OVERRIDE;
+ virtual base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+ virtual base::string16 GetInfo(const AutofillType& type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) OVERRIDE;
+
+ // AutofillDataModel:
+ virtual void FillFormField(const AutofillField& field,
+ size_t variant,
+ const std::string& app_locale,
+ FormFieldData* field_data) const OVERRIDE;
+
+ // Credit card preview summary, for example: ******1234, Exp: 01/2020
+ const base::string16 Label() const;
+
+ // Special method to set value for HTML5 month input type.
+ void SetInfoForMonthInputType(const base::string16& value);
+
+ // The number altered for display, for example: ******1234
+ base::string16 ObfuscatedNumber() const;
+ // The last four digits of the credit card number.
+ base::string16 LastFourDigits() const;
+ // The user-visible type of the card, e.g. 'Mastercard'.
+ base::string16 TypeForDisplay() const;
+ // A label for this credit card formatted as 'Cardname - 2345'.
+ base::string16 TypeAndLastFourDigits() const;
+
+ const std::string& type() const { return type_; }
+
+ int expiration_month() const { return expiration_month_; }
+ int expiration_year() const { return expiration_year_; }
+
+ // For use in STL containers.
+ void operator=(const CreditCard& credit_card);
+
+ // If the card numbers for |this| and |imported_card| match, and merging the
+ // two wouldn't result in unverified data overwriting verified data,
+ // overwrites |this| card's data with the data in |credit_card|.
+ // Returns true if the card numbers match, false otherwise.
+ 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, origins, 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 std::string& app_locale) const;
+
+ // Returns true if all field types have valid values set.
+ bool IsComplete() const;
+
+ // Returns true if all field types have valid values set and the card is not
+ // expired.
+ bool IsValid() const;
+
+ // Returns the credit card number.
+ const base::string16& number() const { return number_; }
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* 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; }
+ base::string16 ExpirationMonthAsString() const;
+ base::string16 Expiration4DigitYearAsString() const;
+ base::string16 Expiration2DigitYearAsString() const;
+
+ // Sets |expiration_month_| to the integer conversion of |text|.
+ void SetExpirationMonthFromString(const base::string16& text,
+ const std::string& app_locale);
+
+ // Sets |expiration_year_| to the integer conversion of |text|.
+ void SetExpirationYearFromString(const base::string16& text);
+
+ // Sets |number_| to |number| and computes the appropriate card |type_|.
+ void SetNumber(const base::string16& number);
+
+ // These setters verify that the month and year are within appropriate
+ // ranges.
+ void SetExpirationMonth(int expiration_month);
+ void SetExpirationYear(int expiration_year);
+
+ base::string16 number_; // The credit card number.
+ base::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_;
+};
+
+// 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 kUnionPay;
+extern const char* const kVisaCard;
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_H_
diff --git a/chromium/components/autofill/core/browser/credit_card_field.cc b/chromium/components/autofill/core/browser/credit_card_field.cc
new file mode 100644
index 00000000000..bba804c9586
--- /dev/null
+++ b/chromium/components/autofill/core/browser/credit_card_field.cc
@@ -0,0 +1,233 @@
+// 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/core/browser/credit_card_field.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+// 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) {
+ base::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.
+ base::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.
+ base::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(ServerFieldTypeMap* 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;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/credit_card_field.h b/chromium/components/autofill/core/browser/credit_card_field.h
new file mode 100644
index 00000000000..119e1ab79dd
--- /dev/null
+++ b/chromium/components/autofill/core/browser/credit_card_field.h
@@ -0,0 +1,75 @@
+// 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_CORE_BROWSER_CREDIT_CARD_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_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/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/form_field.h"
+
+namespace autofill {
+
+class AutofillField;
+class AutofillScanner;
+
+class CreditCardField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(ServerFieldTypeMap* 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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_CREDIT_CARD_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/credit_card_field_unittest.cc b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc
new file mode 100644
index 00000000000..9f60a1d6bda
--- /dev/null
+++ b/chromium/components/autofill/core/browser/credit_card_field_unittest.cc
@@ -0,0 +1,322 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/credit_card_field.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class CreditCardFieldTest : public testing::Test {
+ public:
+ CreditCardFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<CreditCardField> field_;
+ ServerFieldTypeMap 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")]);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/credit_card_unittest.cc b/chromium/components/autofill/core/browser/credit_card_unittest.cc
new file mode 100644
index 00000000000..837d729f979
--- /dev/null
+++ b/chromium/components/autofill/core/browser/credit_card_unittest.cc
@@ -0,0 +1,693 @@
+// 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 "base/basictypes.h"
+#include "base/guid.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "grit/webkit_resources.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+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",
+
+ // A UnionPay card that doesn't pass the Luhn checksum
+ "6200000000000000",
+};
+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(base::GenerateGUID(), "https://www.example.com/");
+ base::string16 summary0 = credit_card0.Label();
+ EXPECT_EQ(base::string16(), summary0);
+ base::string16 obfuscated0 = credit_card0.ObfuscatedNumber();
+ EXPECT_EQ(base::string16(), obfuscated0);
+
+ // Case 00: Empty credit card with empty strings.
+ CreditCard credit_card00(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(&credit_card00,"John Dillinger", "", "", "");
+ base::string16 summary00 = credit_card00.Label();
+ EXPECT_EQ(base::string16(ASCIIToUTF16("John Dillinger")), summary00);
+ base::string16 obfuscated00 = credit_card00.ObfuscatedNumber();
+ EXPECT_EQ(base::string16(), obfuscated00);
+
+ // Case 1: No credit card number.
+ CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(&credit_card1,"John Dillinger", "", "01", "2010");
+ base::string16 summary1 = credit_card1.Label();
+ EXPECT_EQ(base::string16(ASCIIToUTF16("John Dillinger")), summary1);
+ base::string16 obfuscated1 = credit_card1.ObfuscatedNumber();
+ EXPECT_EQ(base::string16(), obfuscated1);
+
+ // Case 2: No month.
+ CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(
+ &credit_card2, "John Dillinger", "5105 1051 0510 5100", "", "2010");
+ base::string16 summary2 = credit_card2.Label();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), summary2);
+ base::string16 obfuscated2 = credit_card2.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), obfuscated2);
+
+ // Case 3: No year.
+ CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(
+ &credit_card3, "John Dillinger", "5105 1051 0510 5100", "01", "");
+ base::string16 summary3 = credit_card3.Label();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), summary3);
+ base::string16 obfuscated3 = credit_card3.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), obfuscated3);
+
+ // Case 4: Have everything.
+ CreditCard credit_card4(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(
+ &credit_card4, "John Dillinger", "5105 1051 0510 5100", "01", "2010");
+ base::string16 summary4 = credit_card4.Label();
+ EXPECT_EQ(ASCIIToUTF16("************5100, Exp: 01/2010"), summary4);
+ base::string16 obfuscated4 = credit_card4.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("************5100"), obfuscated4);
+
+ // Case 5: Very long credit card
+ CreditCard credit_card5(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(
+ &credit_card5,
+ "John Dillinger",
+ "0123456789 0123456789 0123456789 5105 1051 0510 5100", "01", "2010");
+ base::string16 summary5 = credit_card5.Label();
+ EXPECT_EQ(ASCIIToUTF16("********************5100, Exp: 01/2010"), summary5);
+ base::string16 obfuscated5 = credit_card5.ObfuscatedNumber();
+ EXPECT_EQ(ASCIIToUTF16("********************5100"), obfuscated5);
+}
+
+TEST(CreditCardTest, AssignmentOperator) {
+ CreditCard a(base::GenerateGUID(), "some origin");
+ test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010");
+
+ // Result of assignment should be logically equal to the original profile.
+ CreditCard b(base::GenerateGUID(), "some other origin");
+ b = a;
+ EXPECT_TRUE(a == b);
+
+ // Assignment to self should not change the profile value.
+ a = a;
+ EXPECT_TRUE(a == b);
+}
+
+TEST(CreditCardTest, Copy) {
+ CreditCard a(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&a, "John Dillinger", "123456789012", "01", "2010");
+
+ // Clone should be logically equal to the original.
+ CreditCard b(a);
+ EXPECT_TRUE(a == b);
+}
+
+TEST(CreditCardTest, Compare) {
+ CreditCard a(base::GenerateGUID(), std::string());
+ CreditCard b(base::GenerateGUID(), std::string());
+
+ // Empty cards 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));
+
+ // Origins don't count.
+ a.set_origin("apple");
+ b.set_origin("banana");
+ EXPECT_EQ(0, a.Compare(b));
+
+ // Different values produce non-zero results.
+ test::SetCreditCardInfo(&a, "Jimmy", NULL, NULL, NULL);
+ test::SetCreditCardInfo(&b, "Ringo", NULL, NULL, NULL);
+ EXPECT_GT(0, a.Compare(b));
+ EXPECT_LT(0, b.Compare(a));
+}
+
+// Test we get the correct icon for each card type.
+TEST(CreditCardTest, IconResourceId) {
+ EXPECT_EQ(IDR_AUTOFILL_CC_AMEX,
+ CreditCard::IconResourceId(kAmericanExpressCard));
+ EXPECT_EQ(IDR_AUTOFILL_CC_DINERS,
+ CreditCard::IconResourceId(kDinersCard));
+ EXPECT_EQ(IDR_AUTOFILL_CC_DISCOVER,
+ CreditCard::IconResourceId(kDiscoverCard));
+ EXPECT_EQ(IDR_AUTOFILL_CC_JCB,
+ CreditCard::IconResourceId(kJCBCard));
+ EXPECT_EQ(IDR_AUTOFILL_CC_MASTERCARD,
+ CreditCard::IconResourceId(kMasterCard));
+ EXPECT_EQ(IDR_AUTOFILL_CC_VISA,
+ CreditCard::IconResourceId(kVisaCard));
+}
+
+TEST(CreditCardTest, UpdateFromImportedCard) {
+ CreditCard original_card(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(
+ &original_card, "John Dillinger", "123456789012", "09", "2017");
+
+ CreditCard a = original_card;
+
+ // The new card has a different name, expiration date, and origin.
+ CreditCard b = a;
+ b.set_guid(base::GenerateGUID());
+ b.set_origin("https://www.example.org");
+ b.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("J. Dillinger"));
+ b.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("08"));
+ b.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2019"));
+
+ EXPECT_TRUE(a.UpdateFromImportedCard(b, "en-US"));
+ EXPECT_EQ("https://www.example.org", a.origin());
+ EXPECT_EQ(ASCIIToUTF16("J. Dillinger"), a.GetRawInfo(CREDIT_CARD_NAME));
+ EXPECT_EQ(ASCIIToUTF16("08"), a.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(ASCIIToUTF16("2019"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ // Try again, but with no name set for |b|.
+ a = original_card;
+ b.SetRawInfo(CREDIT_CARD_NAME, base::string16());
+
+ EXPECT_TRUE(a.UpdateFromImportedCard(b, "en-US"));
+ EXPECT_EQ("https://www.example.org", a.origin());
+ EXPECT_EQ(ASCIIToUTF16("John Dillinger"), a.GetRawInfo(CREDIT_CARD_NAME));
+ EXPECT_EQ(ASCIIToUTF16("08"), a.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(ASCIIToUTF16("2019"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ // Try again, but with only the original card having a verified origin.
+ // |a| should be unchanged.
+ a = original_card;
+ a.set_origin("Chrome settings");
+ b.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("J. Dillinger"));
+
+ EXPECT_TRUE(a.UpdateFromImportedCard(b, "en-US"));
+ EXPECT_EQ("Chrome settings", a.origin());
+ EXPECT_EQ(ASCIIToUTF16("John Dillinger"), a.GetRawInfo(CREDIT_CARD_NAME));
+ EXPECT_EQ(ASCIIToUTF16("09"), a.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(ASCIIToUTF16("2017"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ // Try again, but with only the new card having a verified origin.
+ a = original_card;
+ b.set_origin("Chrome settings");
+
+ EXPECT_TRUE(a.UpdateFromImportedCard(b, "en-US"));
+ EXPECT_EQ("Chrome settings", a.origin());
+ EXPECT_EQ(ASCIIToUTF16("J. Dillinger"), a.GetRawInfo(CREDIT_CARD_NAME));
+ EXPECT_EQ(ASCIIToUTF16("08"), a.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(ASCIIToUTF16("2019"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ // Try again, with both cards having a verified origin.
+ a = original_card;
+ a.set_origin("Chrome Autofill dialog");
+ b.set_origin("Chrome settings");
+
+ EXPECT_TRUE(a.UpdateFromImportedCard(b, "en-US"));
+ EXPECT_EQ("Chrome settings", a.origin());
+ EXPECT_EQ(ASCIIToUTF16("J. Dillinger"), a.GetRawInfo(CREDIT_CARD_NAME));
+ EXPECT_EQ(ASCIIToUTF16("08"), a.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(ASCIIToUTF16("2019"), a.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
+
+ // Try again, but with |b| having a different card number.
+ // |a| should be unchanged.
+ a = original_card;
+ b.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("4111111111111111"));
+
+ EXPECT_FALSE(a.UpdateFromImportedCard(b, "en-US"));
+ EXPECT_EQ(original_card, a);
+}
+
+TEST(CreditCardTest, IsComplete) {
+ CreditCard card(base::GenerateGUID(), "https://www.example.com/");
+ 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, IsValid) {
+ CreditCard card;
+ // Invalid because expired
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("1"));
+ card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2010"));
+ card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("4111111111111111"));
+ EXPECT_FALSE(card.IsValid());
+
+ // Invalid because card number is not complete
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("12"));
+ card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("9999"));
+ card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("41111"));
+ EXPECT_FALSE(card.IsValid());
+
+ // Valid
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("12"));
+ card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("9999"));
+ card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("4111111111111111"));
+ EXPECT_TRUE(card.IsValid());
+}
+
+TEST(CreditCardTest, InvalidMastercardNumber) {
+ CreditCard card(base::GenerateGUID(), "https://www.example.com/");
+
+ test::SetCreditCardInfo(&card, "Baby Face Nelson",
+ "5200000000000004", "01", "2010");
+ EXPECT_EQ(kMasterCard, card.type());
+ EXPECT_FALSE(card.IsComplete());
+}
+
+// Verify that we preserve exactly what the user typed for credit card numbers.
+TEST(CreditCardTest, SetRawInfoCreditCardNumber) {
+ CreditCard card(base::GenerateGUID(), "https://www.example.com/");
+
+ 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(base::GenerateGUID(), "https://www.example.com/");
+
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("05"));
+ EXPECT_EQ(ASCIIToUTF16("05"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(5, card.expiration_month());
+
+ card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("7"));
+ EXPECT_EQ(ASCIIToUTF16("07"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(7, card.expiration_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));
+ EXPECT_EQ(7, card.expiration_month());
+
+ card.SetInfo(
+ AutofillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("January"), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("01"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(1, card.expiration_month());
+
+ card.SetInfo(
+ AutofillType(CREDIT_CARD_EXP_MONTH), ASCIIToUTF16("Apr"), "en-US");
+ EXPECT_EQ(ASCIIToUTF16("04"), card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
+ EXPECT_EQ(4, card.expiration_month());
+}
+
+TEST(CreditCardTest, CreditCardType) {
+ CreditCard card(base::GenerateGUID(), "https://www.example.com/");
+
+ // The card type cannot be set directly.
+ card.SetRawInfo(CREDIT_CARD_TYPE, ASCIIToUTF16("Visa"));
+ EXPECT_EQ(base::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(base::GenerateGUID(), "https://www.example.com/");
+
+ // The verification code cannot be set, as Chrome does not store this data.
+ card.SetRawInfo(CREDIT_CARD_VERIFICATION_CODE, ASCIIToUTF16("999"));
+ EXPECT_EQ(base::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<base::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(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_EXP_MONTH), "en-US", &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<base::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(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_EXP_MONTH), "en-US", &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<base::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(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_EXP_MONTH), "en-US", &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<base::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(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_EXP_MONTH), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("1"), field.value);
+}
+
+TEST(CreditCardTest, CreditCardTwoDigitYear) {
+ const char* const kYears[] = {
+ "12", "13", "14", "15", "16", "17", "18", "19"
+ };
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2017"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("17"), field.value);
+ EXPECT_EQ(2017, credit_card.expiration_year());
+}
+
+TEST(CreditCardTest, CreditCardTypeSelectControl) {
+ const char* const kCreditCardTypes[] = {
+ "Visa", "Master Card", "AmEx", "discover"
+ };
+ std::vector<base::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(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("4111111111111111"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_TYPE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("Visa"), field.value);
+ }
+
+ {
+ // Filling should be able to handle intervening whitespace:
+ CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("5105105105105100"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_TYPE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("Master Card"), field.value);
+ }
+
+ {
+ // American Express is sometimes abbreviated as AmEx:
+ CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("371449635398431"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_TYPE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("AmEx"), field.value);
+ }
+
+ {
+ // Case insensitivity:
+ CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/");
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("6011111111111117"));
+ credit_card.FillSelectControl(
+ AutofillType(CREDIT_CARD_TYPE), "en-US", &field);
+ EXPECT_EQ(ASCIIToUTF16("discover"), field.value);
+ }
+}
+
+TEST(CreditCardTest, GetCreditCardType) {
+ struct {
+ std::string card_number;
+ std::string type;
+ bool is_valid;
+ } test_cases[] = {
+ // The relevant sample numbers from
+ // http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
+ { "378282246310005", kAmericanExpressCard, true },
+ { "371449635398431", kAmericanExpressCard, true },
+ { "378734493671000", kAmericanExpressCard, true },
+ { "30569309025904", kDinersCard, true },
+ { "38520000023237", kDinersCard, true },
+ { "6011111111111117", kDiscoverCard, true },
+ { "6011000990139424", kDiscoverCard, true },
+ { "3530111333300000", kJCBCard, true },
+ { "3566002020360505", kJCBCard, true },
+ { "5555555555554444", kMasterCard, true },
+ { "5105105105105100", kMasterCard, true },
+ { "4111111111111111", kVisaCard, true },
+ { "4012888888881881", kVisaCard, true },
+ { "4222222222222", kVisaCard, true },
+
+ // The relevant sample numbers from
+ // http://auricsystems.com/support-center/sample-credit-card-numbers/
+ { "343434343434343", kAmericanExpressCard, true },
+ { "371144371144376", kAmericanExpressCard, true },
+ { "341134113411347", kAmericanExpressCard, true },
+ { "36438936438936", kDinersCard, true },
+ { "36110361103612", kDinersCard, true },
+ { "36111111111111", kDinersCard, true },
+ { "6011016011016011", kDiscoverCard, true },
+ { "6011000990139424", kDiscoverCard, true },
+ { "6011000000000004", kDiscoverCard, true },
+ { "6011000995500000", kDiscoverCard, true },
+ { "6500000000000002", kDiscoverCard, true },
+ { "3566002020360505", kJCBCard, true },
+ { "3528000000000007", kJCBCard, true },
+ { "5500005555555559", kMasterCard, true },
+ { "5555555555555557", kMasterCard, true },
+ { "5454545454545454", kMasterCard, true },
+ { "5555515555555551", kMasterCard, true },
+ { "5405222222222226", kMasterCard, true },
+ { "5478050000000007", kMasterCard, true },
+ { "5111005111051128", kMasterCard, true },
+ { "5112345112345114", kMasterCard, true },
+ { "5115915115915118", kMasterCard, true },
+
+ // A UnionPay card that doesn't pass the Luhn checksum
+ { "6200000000000000", kUnionPay, true },
+
+ // Empty string
+ { std::string(), kGenericCard, false },
+
+ // Non-numeric
+ { "garbage", kGenericCard, false },
+ { "4garbage", kVisaCard, false },
+
+ // Fails Luhn check.
+ { "4111111111111112", kVisaCard, false },
+
+ // Invalid length.
+ { "3434343434343434", kAmericanExpressCard, false },
+ { "411111111111116", kVisaCard, false },
+
+ // Issuer Identification Numbers (IINs) that Chrome recognizes.
+ { "4", kVisaCard, false },
+ { "34", kAmericanExpressCard, false },
+ { "37", kAmericanExpressCard, false },
+ { "300", kDinersCard, false },
+ { "301", kDinersCard, false },
+ { "302", kDinersCard, false },
+ { "303", kDinersCard, false },
+ { "304", kDinersCard, false },
+ { "305", kDinersCard, false },
+ { "3095", kDinersCard, false },
+ { "36", kDinersCard, false },
+ { "38", kDinersCard, false },
+ { "39", kDinersCard, false },
+ { "6011", kDiscoverCard, false },
+ { "644", kDiscoverCard, false },
+ { "645", kDiscoverCard, false },
+ { "646", kDiscoverCard, false },
+ { "647", kDiscoverCard, false },
+ { "648", kDiscoverCard, false },
+ { "649", kDiscoverCard, false },
+ { "65", kDiscoverCard, false },
+ { "3528", kJCBCard, false },
+ { "3531", kJCBCard, false },
+ { "3589", kJCBCard, false },
+ { "51", kMasterCard, false },
+ { "52", kMasterCard, false },
+ { "53", kMasterCard, false },
+ { "54", kMasterCard, false },
+ { "55", kMasterCard, false },
+ { "62", kUnionPay, false },
+
+ // Not enough data to determine an IIN uniquely.
+ { "3", kGenericCard, false },
+ { "30", kGenericCard, false },
+ { "309", kGenericCard, false },
+ { "35", kGenericCard, false },
+ { "5", kGenericCard, false },
+ { "6", kGenericCard, false },
+ { "60", kGenericCard, false },
+ { "601", kGenericCard, false },
+ { "64", kGenericCard, false },
+
+ // Unknown IINs.
+ { "0", kGenericCard, false },
+ { "1", kGenericCard, false },
+ { "2", kGenericCard, false },
+ { "306", kGenericCard, false },
+ { "307", kGenericCard, false },
+ { "308", kGenericCard, false },
+ { "3091", kGenericCard, false },
+ { "3094", kGenericCard, false },
+ { "3096", kGenericCard, false },
+ { "31", kGenericCard, false },
+ { "32", kGenericCard, false },
+ { "33", kGenericCard, false },
+ { "351", kGenericCard, false },
+ { "3527", kGenericCard, false },
+ { "359", kGenericCard, false },
+ { "50", kGenericCard, false },
+ { "56", kGenericCard, false },
+ { "57", kGenericCard, false },
+ { "58", kGenericCard, false },
+ { "59", kGenericCard, false },
+ { "600", kGenericCard, false },
+ { "602", kGenericCard, false },
+ { "603", kGenericCard, false },
+ { "604", kGenericCard, false },
+ { "605", kGenericCard, false },
+ { "606", kGenericCard, false },
+ { "607", kGenericCard, false },
+ { "608", kGenericCard, false },
+ { "609", kGenericCard, false },
+ { "61", kGenericCard, false },
+ { "63", kGenericCard, false },
+ { "640", kGenericCard, false },
+ { "641", kGenericCard, false },
+ { "642", kGenericCard, false },
+ { "643", kGenericCard, false },
+ { "66", kGenericCard, false },
+ { "67", kGenericCard, false },
+ { "68", kGenericCard, false },
+ { "69", kGenericCard, false },
+ { "7", kGenericCard, false },
+ { "8", kGenericCard, false },
+ { "9", kGenericCard, false },
+
+ // Oddball case: Unknown issuer, but valid Luhn check and plausible length.
+ { "7000700070007000", kGenericCard, true },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ base::string16 card_number = ASCIIToUTF16(test_cases[i].card_number);
+ SCOPED_TRACE(card_number);
+ EXPECT_EQ(test_cases[i].type, CreditCard::GetCreditCardType(card_number));
+ EXPECT_EQ(test_cases[i].is_valid, IsValidCreditCardNumber(card_number));
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/crypto/rc4_decryptor.h b/chromium/components/autofill/core/browser/crypto/rc4_decryptor.h
new file mode 100644
index 00000000000..095413a6b3a
--- /dev/null
+++ b/chromium/components/autofill/core/browser/crypto/rc4_decryptor.h
@@ -0,0 +1,110 @@
+// 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_CORE_BROWSER_CRYPTO_RC4_DECRYPTOR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_CRYPTO_RC4_DECRYPTOR_H_
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace autofill {
+
+// 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/core/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_ptr<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_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_CRYPTO_RC4_DECRYPTOR_H_
diff --git a/chromium/components/autofill/core/browser/data_driven_test.cc b/chromium/components/autofill/core/browser/data_driven_test.cc
new file mode 100644
index 00000000000..18f461ece0e
--- /dev/null
+++ b/chromium/components/autofill/core/browser/data_driven_test.cc
@@ -0,0 +1,93 @@
+// 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/core/browser/data_driven_test.h"
+
+#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+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(),
+ static_cast<int>(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) {
+ ASSERT_TRUE(base::DirectoryExists(input_directory));
+ ASSERT_TRUE(base::DirectoryExists(output_directory));
+ base::FileEnumerator input_files(input_directory,
+ false,
+ base::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 dir;
+ dir = test_data_directory_.AppendASCII("autofill")
+ .Append(test_name)
+ .AppendASCII("input");
+ return dir;
+}
+
+base::FilePath DataDrivenTest::GetOutputDirectory(
+ const base::FilePath::StringType& test_name) {
+ base::FilePath dir;
+ dir = test_data_directory_.AppendASCII("autofill")
+ .Append(test_name)
+ .AppendASCII("output");
+ return dir;
+}
+
+DataDrivenTest::DataDrivenTest(const base::FilePath& test_data_directory)
+ : test_data_directory_(test_data_directory) {
+}
+
+DataDrivenTest::~DataDrivenTest() {
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/data_driven_test.h b/chromium/components/autofill/core/browser/data_driven_test.h
new file mode 100644
index 00000000000..aff2f319cd8
--- /dev/null
+++ b/chromium/components/autofill/core/browser/data_driven_test.h
@@ -0,0 +1,56 @@
+// 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_CORE_BROWSER_DATA_DRIVEN_TEST_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_DRIVEN_TEST_H_
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+// 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(const base::FilePath& test_data_directory);
+ virtual ~DataDrivenTest();
+
+ private:
+ base::FilePath test_data_directory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataDrivenTest);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_DATA_DRIVEN_TEST_H_
diff --git a/chromium/components/autofill/core/browser/email_field.cc b/chromium/components/autofill/core/browser/email_field.cc
new file mode 100644
index 00000000000..053ecec3c83
--- /dev/null
+++ b/chromium/components/autofill/core/browser/email_field.cc
@@ -0,0 +1,32 @@
+// 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/core/browser/email_field.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+// 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(ServerFieldTypeMap* map) const {
+ return AddClassification(field_, EMAIL_ADDRESS, map);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/email_field.h b/chromium/components/autofill/core/browser/email_field.h
new file mode 100644
index 00000000000..4e270a18bc6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/email_field.h
@@ -0,0 +1,32 @@
+// 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_CORE_BROWSER_EMAIL_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_EMAIL_FIELD_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "components/autofill/core/browser/form_field.h"
+
+namespace autofill {
+
+class EmailField : public FormField {
+ public:
+ static FormField* Parse(AutofillScanner* scanner);
+
+ protected:
+ // FormField:
+ virtual bool ClassifyField(ServerFieldTypeMap* map) const OVERRIDE;
+
+ private:
+ explicit EmailField(const AutofillField* field);
+
+ const AutofillField* field_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmailField);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_EMAIL_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h
new file mode 100644
index 00000000000..081a7a75c42
--- /dev/null
+++ b/chromium/components/autofill/core/browser/field_types.h
@@ -0,0 +1,191 @@
+// 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_CORE_BROWSER_FIELD_TYPES_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_FIELD_TYPES_H_
+
+#include <map>
+#include <set>
+
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+// 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.
+//
+// The list of all field types natively understood by the Autofill server. A
+// subset of these types is used to store Autofill data in the user's profile.
+enum ServerFieldType {
+ // 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,
+
+ PHONE_BILLING_NUMBER = 62,
+ PHONE_BILLING_CITY_CODE = 63,
+ PHONE_BILLING_COUNTRY_CODE = 64,
+ PHONE_BILLING_CITY_AND_NUMBER = 65,
+ PHONE_BILLING_WHOLE_NUMBER = 66,
+
+ NAME_BILLING_FIRST = 67,
+ NAME_BILLING_MIDDLE = 68,
+ NAME_BILLING_LAST = 69,
+ NAME_BILLING_MIDDLE_INITIAL = 70,
+ NAME_BILLING_FULL = 71,
+ NAME_BILLING_SUFFIX = 72,
+
+ // No new types can be added without a corresponding change to the Autofill
+ // server.
+
+ MAX_VALID_FIELD_TYPE = 73,
+};
+
+// The list of all HTML autocomplete field type hints supported by Chrome.
+// See [ http://is.gd/whatwg_autocomplete ] for the full list of specced hints.
+enum HtmlFieldType {
+ // Default type.
+ HTML_TYPE_UNKNOWN,
+
+ // Name types.
+ HTML_TYPE_NAME,
+ HTML_TYPE_GIVEN_NAME,
+ HTML_TYPE_ADDITIONAL_NAME,
+ HTML_TYPE_FAMILY_NAME,
+
+ // Business types.
+ HTML_TYPE_ORGANIZATION,
+
+ // Address types.
+ HTML_TYPE_STREET_ADDRESS,
+ HTML_TYPE_ADDRESS_LINE1,
+ HTML_TYPE_ADDRESS_LINE2,
+ HTML_TYPE_LOCALITY, // For U.S. addresses, corresponds to the city.
+ HTML_TYPE_REGION, // For U.S. addresses, corresponds to the state.
+ HTML_TYPE_COUNTRY_CODE, // The ISO 3166-1-alpha-2 country code.
+ HTML_TYPE_COUNTRY_NAME, // The localized country name.
+ HTML_TYPE_POSTAL_CODE,
+
+ // Credit card types.
+ HTML_TYPE_CREDIT_CARD_NAME,
+ HTML_TYPE_CREDIT_CARD_NUMBER,
+ HTML_TYPE_CREDIT_CARD_EXP,
+ HTML_TYPE_CREDIT_CARD_EXP_MONTH,
+ HTML_TYPE_CREDIT_CARD_EXP_YEAR,
+ HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE,
+ HTML_TYPE_CREDIT_CARD_TYPE,
+
+ // Phone number types.
+ HTML_TYPE_TEL,
+ HTML_TYPE_TEL_COUNTRY_CODE,
+ HTML_TYPE_TEL_NATIONAL,
+ HTML_TYPE_TEL_AREA_CODE,
+ HTML_TYPE_TEL_LOCAL,
+ HTML_TYPE_TEL_LOCAL_PREFIX,
+ HTML_TYPE_TEL_LOCAL_SUFFIX,
+
+ // Email.
+ HTML_TYPE_EMAIL,
+
+ // Variants of type hints specified in the HTML specification that are
+ // inferred based on a field's 'maxlength' attribute.
+ // TODO(isherman): Remove these types, in favor of understanding maxlength
+ // when filling fields. See also: AutofillField::phone_part_.
+ HTML_TYPE_ADDITIONAL_NAME_INITIAL,
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR,
+ HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR,
+ HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR,
+ HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR,
+};
+
+// The list of all HTML autocomplete field mode hints supported by Chrome.
+// See [ http://is.gd/whatwg_autocomplete ] for the full list of specced hints.
+enum HtmlFieldMode {
+ HTML_MODE_NONE,
+ HTML_MODE_BILLING,
+ HTML_MODE_SHIPPING,
+};
+
+enum FieldTypeGroup {
+ NO_GROUP,
+ NAME,
+ NAME_BILLING,
+ EMAIL,
+ COMPANY,
+ ADDRESS_HOME,
+ ADDRESS_BILLING,
+ PHONE_HOME,
+ PHONE_BILLING,
+ CREDIT_CARD,
+};
+
+typedef std::set<ServerFieldType> ServerFieldTypeSet;
+typedef std::map<base::string16, ServerFieldType> ServerFieldTypeMap;
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FIELD_TYPES_H_
diff --git a/chromium/components/autofill/core/browser/form_field.cc b/chromium/components/autofill/core/browser/form_field.cc
new file mode 100644
index 00000000000..2e001127ada
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_field.cc
@@ -0,0 +1,199 @@
+// 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/core/browser/form_field.h"
+
+#include <stddef.h>
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address_field.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regexes.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/credit_card_field.h"
+#include "components/autofill/core/browser/email_field.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/name_field.h"
+#include "components/autofill/core/browser/phone_field.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+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,
+ ServerFieldTypeMap* 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 base::string16& pattern,
+ const AutofillField** match) {
+ return ParseFieldSpecifics(scanner, pattern, MATCH_DEFAULT, match);
+}
+
+// static
+bool FormField::ParseFieldSpecifics(AutofillScanner* scanner,
+ const base::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,
+ ServerFieldType type,
+ ServerFieldTypeMap* 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 base::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 base::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,
+ ServerFieldTypeMap* 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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_field.h b/chromium/components/autofill/core/browser/form_field.h
new file mode 100644
index 00000000000..dbb937f7b61
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_field.h
@@ -0,0 +1,124 @@
+// 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_CORE_BROWSER_FORM_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_FIELD_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+
+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,
+ ServerFieldTypeMap* 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 base::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 base::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,
+ ServerFieldType type,
+ ServerFieldTypeMap* 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(ServerFieldTypeMap* 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 base::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 base::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,
+ ServerFieldTypeMap* map);
+
+ DISALLOW_COPY_AND_ASSIGN(FormField);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/form_field_unittest.cc b/chromium/components/autofill/core/browser/form_field_unittest.cc
new file mode 100644
index 00000000000..5014b713648
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_field_unittest.cc
@@ -0,0 +1,152 @@
+// 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 "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/form_field.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(FormFieldTest, Match) {
+ AutofillField field;
+
+ // Empty strings match.
+ EXPECT_TRUE(FormField::Match(&field, base::string16(),
+ FormField::MATCH_LABEL));
+
+ // Empty pattern matches non-empty string.
+ field.label = ASCIIToUTF16("a");
+ EXPECT_TRUE(FormField::Match(&field, base::string16(),
+ FormField::MATCH_LABEL));
+
+ // Strictly empty pattern matches empty string.
+ field.label = base::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 = base::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));
+
+ ServerFieldTypeMap 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);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_group.cc b/chromium/components/autofill/core/browser/form_group.cc
new file mode 100644
index 00000000000..17eb036aadc
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_group.cc
@@ -0,0 +1,51 @@
+// 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/core/browser/form_group.h"
+
+#include "components/autofill/core/browser/autofill_type.h"
+
+namespace autofill {
+
+void FormGroup::GetMatchingTypes(const base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const {
+ if (text.empty()) {
+ matching_types->insert(EMPTY_TYPE);
+ return;
+ }
+
+ ServerFieldTypeSet types;
+ GetSupportedTypes(&types);
+ for (ServerFieldTypeSet::const_iterator type = types.begin();
+ type != types.end(); ++type) {
+ if (GetInfo(AutofillType(*type), app_locale) == text)
+ matching_types->insert(*type);
+ }
+}
+
+void FormGroup::GetNonEmptyTypes(const std::string& app_locale,
+ ServerFieldTypeSet* non_empty_types) const {
+ ServerFieldTypeSet types;
+ GetSupportedTypes(&types);
+ for (ServerFieldTypeSet::const_iterator type = types.begin();
+ type != types.end(); ++type) {
+ if (!GetInfo(AutofillType(*type), app_locale).empty())
+ non_empty_types->insert(*type);
+ }
+}
+
+base::string16 FormGroup::GetInfo(const AutofillType& type,
+ const std::string& app_locale) const {
+ return GetRawInfo(type.GetStorableType());
+}
+
+bool FormGroup::SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) {
+ SetRawInfo(type.GetStorableType(), value);
+ return true;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_group.h b/chromium/components/autofill/core/browser/form_group.h
new file mode 100644
index 00000000000..86bd4531bb1
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_group.h
@@ -0,0 +1,68 @@
+// 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_CORE_BROWSER_FORM_GROUP_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_GROUP_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace autofill {
+
+class AutofillType;
+
+// This class is an interface for collections of form fields, grouped by type.
+class FormGroup {
+ public:
+ virtual ~FormGroup() {}
+
+ // 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 base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const;
+
+ // Returns a set of server field types for which this FormGroup has non-empty
+ // data. This method is additive on |non_empty_types|.
+ virtual void GetNonEmptyTypes(const std::string& app_locale,
+ ServerFieldTypeSet* non_empty_types) const;
+
+ // Returns the string associated with |type|, without canonicalizing the
+ // returned value. For user-visible strings, use GetInfo() instead.
+ virtual base::string16 GetRawInfo(ServerFieldType 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(ServerFieldType type,
+ const base::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 base::string16 GetInfo(const AutofillType& 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(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale);
+
+ 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 server field types for which this FormGroup can store
+ // data. This method is additive on |supported_types|.
+ virtual void GetSupportedTypes(ServerFieldTypeSet* supported_types) const = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_GROUP_H_
diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc
new file mode 100644
index 00000000000..0a534f8d847
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_structure.cc
@@ -0,0 +1,1259 @@
+// 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/core/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/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/autofill_xml_parser.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_field.h"
+#include "components/autofill/core/common/autofill_constants.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_data_predictions.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/form_field_data_predictions.h"
+#include "third_party/icu/source/i18n/unicode/regex.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+namespace autofill {
+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 kAttributeFieldID[] = "fieldid";
+const char kAttributeFieldType[] = "fieldtype";
+const char kAttributeFormSignature[] = "formsignature";
+const char kAttributeName[] = "name";
+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 kXMLElementFieldAssignments[] = "fieldassignments";
+const char kXMLElementField[] = "field";
+const char kXMLElementFields[] = "fields";
+const char kXMLElementForm[] = "form";
+const char kBillingMode[] = "billing";
+const char kShippingMode[] = "shipping";
+
+// Stip away >= 5 consecutive digits.
+const char kIgnorePatternInFieldName[] = "\\d{5,}+";
+
+// 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 ServerFieldTypeSet& 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 (ServerFieldTypeSet::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;
+}
+
+// Helper for |EncodeFormRequest()| that creates XmlElements for the given field
+// in upload xml, and also add them to the parent XmlElement.
+void EncodeFieldForUpload(const AutofillField& field,
+ buzz::XmlElement* parent) {
+ // Don't upload checkable fields.
+ if (field.is_checkable)
+ return;
+
+ ServerFieldTypeSet types = field.possible_types();
+ // |types| could be empty in unit-tests only.
+ for (ServerFieldTypeSet::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));
+ parent->AddElement(field_element);
+ }
+}
+
+// Helper for |EncodeFormRequest()| that creates XmlElement for the given field
+// in query xml, and also add it to the parent XmlElement.
+void EncodeFieldForQuery(const AutofillField& field,
+ buzz::XmlElement* parent) {
+ buzz::XmlElement *field_element = new buzz::XmlElement(
+ buzz::QName(kXMLElementField));
+ field_element->SetAttr(buzz::QName(kAttributeSignature),
+ field.FieldSignature());
+ parent->AddElement(field_element);
+}
+
+// Helper for |EncodeFormRequest()| that creates XmlElements for the given field
+// in field assignments xml, and also add them to the parent XmlElement.
+void EncodeFieldForFieldAssignments(const AutofillField& field,
+ buzz::XmlElement* parent) {
+ ServerFieldTypeSet types = field.possible_types();
+ for (ServerFieldTypeSet::iterator field_type = types.begin();
+ field_type != types.end(); ++field_type) {
+ buzz::XmlElement *field_element = new buzz::XmlElement(
+ buzz::QName(kXMLElementFields));
+
+ field_element->SetAttr(buzz::QName(kAttributeFieldID),
+ field.FieldSignature());
+ field_element->SetAttr(buzz::QName(kAttributeFieldType),
+ base::IntToString(*field_type));
+ field_element->SetAttr(buzz::QName(kAttributeName),
+ UTF16ToUTF8(field.name));
+ parent->AddElement(field_element);
+ }
+}
+
+// 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,
+ HtmlFieldType 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 == HTML_TYPE_EMAIL ||
+ (field_type >= HTML_TYPE_TEL &&
+ field_type <= HTML_TYPE_TEL_LOCAL_SUFFIX);
+ }
+
+ // 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 >= HTML_TYPE_TEL &&
+ field_type <= HTML_TYPE_TEL_LOCAL_SUFFIX;
+ }
+
+ return false;
+}
+
+// Returns the Chrome Autofill-supported field type corresponding to the given
+// |autocomplete_attribute_value|, 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
+HtmlFieldType FieldTypeFromAutocompleteAttributeValue(
+ const std::string& autocomplete_attribute_value,
+ const AutofillField& field) {
+ if (autocomplete_attribute_value == "name")
+ return HTML_TYPE_NAME;
+
+ if (autocomplete_attribute_value == "given-name")
+ return HTML_TYPE_GIVEN_NAME;
+
+ if (autocomplete_attribute_value == "additional-name") {
+ if (field.max_length == 1)
+ return HTML_TYPE_ADDITIONAL_NAME_INITIAL;
+ else
+ return HTML_TYPE_ADDITIONAL_NAME;
+ }
+
+ if (autocomplete_attribute_value == "family-name")
+ return HTML_TYPE_FAMILY_NAME;
+
+ if (autocomplete_attribute_value == "organization")
+ return HTML_TYPE_ORGANIZATION;
+
+ if (autocomplete_attribute_value == "street-address")
+ return HTML_TYPE_STREET_ADDRESS;
+
+ if (autocomplete_attribute_value == "address-line1")
+ return HTML_TYPE_ADDRESS_LINE1;
+
+ if (autocomplete_attribute_value == "address-line2")
+ return HTML_TYPE_ADDRESS_LINE2;
+
+ if (autocomplete_attribute_value == "locality")
+ return HTML_TYPE_LOCALITY;
+
+ if (autocomplete_attribute_value == "region")
+ return HTML_TYPE_REGION;
+
+ if (autocomplete_attribute_value == "country")
+ return HTML_TYPE_COUNTRY_CODE;
+
+ if (autocomplete_attribute_value == "country-name")
+ return HTML_TYPE_COUNTRY_NAME;
+
+ if (autocomplete_attribute_value == "postal-code")
+ return HTML_TYPE_POSTAL_CODE;
+
+ if (autocomplete_attribute_value == "cc-name")
+ return HTML_TYPE_CREDIT_CARD_NAME;
+
+ if (autocomplete_attribute_value == "cc-number")
+ return HTML_TYPE_CREDIT_CARD_NUMBER;
+
+ if (autocomplete_attribute_value == "cc-exp") {
+ if (field.max_length == 5)
+ return HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR;
+ else if (field.max_length == 7)
+ return HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR;
+ else
+ return HTML_TYPE_CREDIT_CARD_EXP;
+ }
+
+ if (autocomplete_attribute_value == "cc-exp-month")
+ return HTML_TYPE_CREDIT_CARD_EXP_MONTH;
+
+ if (autocomplete_attribute_value == "cc-exp-year") {
+ if (field.max_length == 2)
+ return HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR;
+ else if (field.max_length == 4)
+ return HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR;
+ else
+ return HTML_TYPE_CREDIT_CARD_EXP_YEAR;
+ }
+
+ if (autocomplete_attribute_value == "cc-csc")
+ return HTML_TYPE_CREDIT_CARD_VERIFICATION_CODE;
+
+ if (autocomplete_attribute_value == "cc-type")
+ return HTML_TYPE_CREDIT_CARD_TYPE;
+
+ if (autocomplete_attribute_value == "tel")
+ return HTML_TYPE_TEL;
+
+ if (autocomplete_attribute_value == "tel-country-code")
+ return HTML_TYPE_TEL_COUNTRY_CODE;
+
+ if (autocomplete_attribute_value == "tel-national")
+ return HTML_TYPE_TEL_NATIONAL;
+
+ if (autocomplete_attribute_value == "tel-area-code")
+ return HTML_TYPE_TEL_AREA_CODE;
+
+ if (autocomplete_attribute_value == "tel-local")
+ return HTML_TYPE_TEL_LOCAL;
+
+ if (autocomplete_attribute_value == "tel-local-prefix")
+ return HTML_TYPE_TEL_LOCAL_PREFIX;
+
+ if (autocomplete_attribute_value == "tel-local-suffix")
+ return HTML_TYPE_TEL_LOCAL_SUFFIX;
+
+ if (autocomplete_attribute_value == "email")
+ return HTML_TYPE_EMAIL;
+
+ return HTML_TYPE_UNKNOWN;
+}
+
+std::string StripDigitsIfRequired(const base::string16& input) {
+ UErrorCode status = U_ZERO_ERROR;
+ CR_DEFINE_STATIC_LOCAL(icu::UnicodeString, icu_pattern,
+ (kIgnorePatternInFieldName));
+ CR_DEFINE_STATIC_LOCAL(icu::RegexMatcher, matcher,
+ (icu_pattern, UREGEX_CASE_INSENSITIVE, status));
+ DCHECK_EQ(status, U_ZERO_ERROR);
+
+ icu::UnicodeString icu_input(input.data(), input.length());
+ matcher.reset(icu_input);
+
+ icu::UnicodeString replaced_string = matcher.replaceAll("", status);
+
+ std::string return_string;
+ status = U_ZERO_ERROR;
+ UTF16ToUTF8(replaced_string.getBuffer(),
+ static_cast<size_t>(replaced_string.length()),
+ &return_string);
+ if (status != U_ZERO_ERROR) {
+ DVLOG(1) << "Couldn't strip digits in " << UTF16ToUTF8(input);
+ return UTF16ToUTF8(input);
+ }
+
+ return return_string;
+}
+
+} // 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),
+ active_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),
+ filled_by_autocheckout_(false) {
+ // Copy the form fields.
+ std::map<base::string16, size_t> unique_names;
+ for (std::vector<FormFieldData>::const_iterator field =
+ form.fields.begin();
+ field != form.fields.end(); field++) {
+
+ if (!ShouldSkipField(*field)) {
+ // 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(StripDigitsIfRequired(field->name));
+
+ ++active_field_count_;
+ }
+
+ // 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];
+ base::string16 unique_name = field->name + ASCIIToUTF16("_") +
+ base::IntToString16(unique_names[field->name]);
+ fields_.push_back(new AutofillField(*field, unique_name));
+ }
+
+ 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_) {
+ ServerFieldTypeMap field_type_map;
+ FormField::ParseFormFields(fields_.get(), &field_type_map);
+ for (size_t i = 0; i < field_count(); ++i) {
+ AutofillField* field = fields_[i];
+ ServerFieldTypeMap::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 ServerFieldTypeSet& available_field_types,
+ bool form_was_autofilled,
+ std::string* encoded_xml) const {
+ DCHECK(ShouldBeCrowdsourced());
+
+ // 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 (ServerFieldTypeSet::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 = 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;
+}
+
+bool FormStructure::EncodeFieldAssignments(
+ const ServerFieldTypeSet& available_field_types,
+ std::string* encoded_xml) const {
+ DCHECK(ShouldBeCrowdsourced());
+
+ // Set up the <fieldassignments> element and its attributes.
+ buzz::XmlElement autofill_request_xml(
+ (buzz::QName(kXMLElementFieldAssignments)));
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeFormSignature),
+ FormSignature());
+
+ if (!EncodeFormRequest(FormStructure::FIELD_ASSIGNMENTS,
+ &autofill_request_xml))
+ return false; // Malformed form, skip it.
+
+ // Obtain the XML structure as a string.
+ *encoded_xml = kXMLDeclaration;
+ *encoded_xml += autofill_request_xml.Str().c_str();
+
+ 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,
+ page_meta_data);
+ buzz::XmlParser parser(&parse_handler);
+ parser.Parse(response_xml.c_str(), response_xml.length(), true);
+ if (!parse_handler.succeeded())
+ return;
+
+ 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) {
+ if (form->ShouldSkipField(**field))
+ continue;
+
+ // 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);
+
+ ServerFieldType heuristic_type = (*field)->heuristic_type();
+ if (heuristic_type != UNKNOWN_TYPE)
+ heuristics_detected_fillable_field = true;
+
+ (*field)->set_server_type(current_info->field_type);
+ if (heuristic_type != (*field)->Type().GetStorableType())
+ 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);
+
+ ++current_info;
+ }
+
+ 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((*field)->heuristic_type()).ToString();
+ annotated_field.server_type =
+ AutofillType((*field)->server_type()).ToString();
+ annotated_field.overall_type = (*field)->Type().ToString();
+ 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();
+}
+
+bool FormStructure::ShouldSkipField(const FormFieldData& field) const {
+ return (field.is_checkable || field.form_control_type == "password") &&
+ !IsAutocheckoutEnabled();
+}
+
+size_t FormStructure::RequiredFillableFields() const {
+ return IsAutocheckoutEnabled() ? 0 : kRequiredAutofillFields;
+}
+
+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 {
+ if (active_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 {
+ // Allow all forms in Autocheckout flow to be crowdsourced.
+ return (!has_author_specified_types_ && ShouldBeParsed(true)) ||
+ IsAutocheckoutEnabled();
+}
+
+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 = base::string16();
+ }
+
+ field->set_heuristic_type(cached_field->second->heuristic_type());
+ field->set_server_type(cached_field->second->server_type());
+ }
+ }
+
+ UpdateAutofillCount();
+
+ filled_by_autocheckout_ = cached_form.filled_by_autocheckout();
+ 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 ServerFieldTypeSet& 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.
+ ServerFieldTypeSet collapsed_field_types;
+ for (ServerFieldTypeSet::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(*it).GetStorableType());
+ }
+
+ // Capture the field's type, if it is unambiguous.
+ ServerFieldType field_type = UNKNOWN_TYPE;
+ if (collapsed_field_types.size() == 1)
+ field_type = *collapsed_field_types.begin();
+
+ ServerFieldType heuristic_type =
+ AutofillType(field->heuristic_type()).GetStorableType();
+ ServerFieldType server_type =
+ AutofillType(field->server_type()).GetStorableType();
+ ServerFieldType predicted_type = field->Type().GetStorableType();
+
+ // Log heuristic, server, and overall type quality metrics, independently of
+ // whether the field was autofilled.
+ 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::active_field_count() const {
+ return active_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];
+ switch (request_type) {
+ case FormStructure::UPLOAD:
+ EncodeFieldForUpload(*field, encompassing_xml_element);
+ break;
+ case FormStructure::QUERY:
+ if (ShouldSkipField(*field))
+ continue;
+ EncodeFieldForQuery(*field, encompassing_xml_element);
+ break;
+ case FormStructure::FIELD_ASSIGNMENTS:
+ EncodeFieldForFieldAssignments(*field, encompassing_xml_element);
+ break;
+ }
+ }
+ 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();
+ HtmlFieldType field_type =
+ FieldTypeFromAutocompleteAttributeValue(field_type_token, *field);
+ if (field_type == HTML_TYPE_UNKNOWN)
+ 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();
+ HtmlFieldMode mode = HTML_MODE_NONE;
+ if (!tokens.empty()) {
+ if (tokens.back() == kShippingMode)
+ mode = HTML_MODE_SHIPPING;
+ else if (tokens.back() == kBillingMode)
+ mode = HTML_MODE_BILLING;
+ }
+
+ if (mode != HTML_MODE_NONE) {
+ 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->SetHtmlType(field_type, mode);
+ }
+}
+
+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.
+ base::string16 current_section = fields_.front()->unique_name();
+
+ // Keep track of the types we've seen in this section.
+ std::set<ServerFieldType> seen_types;
+ ServerFieldType previous_type = UNKNOWN_TYPE;
+
+ for (std::vector<AutofillField*>::iterator field = fields_.begin();
+ field != fields_.end(); ++field) {
+ const ServerFieldType current_type = (*field)->Type().GetStorableType();
+
+ 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() == PHONE_HOME)
+ 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) {
+ FieldTypeGroup field_type_group = (*field)->Type().group();
+ if (field_type_group == CREDIT_CARD)
+ (*field)->set_section((*field)->section() + "-cc");
+ else
+ (*field)->set_section((*field)->section() + "-default");
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h
new file mode 100644
index 00000000000..1af765057c2
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_structure.h
@@ -0,0 +1,262 @@
+// 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_CORE_BROWSER_FORM_STRUCTURE_H_
+#define COMPONENTS_AUTOFILL_CORE_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/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/web_element_descriptor.h"
+#include "url/gurl.h"
+
+enum RequestMethod {
+ GET,
+ POST
+};
+
+enum UploadRequired {
+ UPLOAD_NOT_REQUIRED,
+ UPLOAD_REQUIRED,
+ USE_UPLOAD_RATES
+};
+
+namespace base {
+class TimeTicks;
+}
+
+namespace buzz {
+class XmlElement;
+}
+
+namespace autofill {
+
+class AutofillMetrics;
+
+struct AutocheckoutPageMetaData;
+struct FormData;
+struct FormDataPredictions;
+
+// 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 ServerFieldTypeSet& available_field_types,
+ bool form_was_autofilled,
+ std::string* encoded_xml) const;
+
+ // Encodes a XML block contains autofill field type from this FormStructure.
+ // This XML will be written VLOG only, never be sent to server. It will
+ // help make FieldAssignments and feed back to autofill server as
+ // experiment data.
+ bool EncodeFieldAssignments(const ServerFieldTypeSet& available_field_types,
+ 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;
+
+ // 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 filled_by_autocheckout() const { return filled_by_autocheckout_; }
+ void set_filled_by_autocheckout(bool filled_by_autocheckout) {
+ filled_by_autocheckout_ = filled_by_autocheckout;
+ }
+
+ 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,
+ FIELD_ASSIGNMENTS,
+ };
+
+ // 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 true if field should be skipped when talking to Autofill server.
+ bool ShouldSkipField(const FormFieldData& field) const;
+
+ // Returns the minimal number of fillable fields required to start autofill.
+ size_t RequiredFillableFields() const;
+ size_t active_field_count() const;
+
+ // The name of the form.
+ base::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 counted towards form signature and request to Autofill
+ // server.
+ size_t active_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_;
+
+ // Whether or not this form was filled by Autocheckout.
+ bool filled_by_autocheckout_;
+
+ DISALLOW_COPY_AND_ASSIGN(FormStructure);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_FORM_STRUCTURE_H_
diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc
new file mode 100644
index 00000000000..7d38165b4d1
--- /dev/null
+++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc
@@ -0,0 +1,2466 @@
+// 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/core/browser/form_structure.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/browser/autocheckout_page_meta_data.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "url/gurl.h"
+
+using WebKit::WebInputElement;
+
+namespace autofill {
+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 = base::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 = base::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 = base::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 = base::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 = base::string16();
+ field.name = ASCIIToUTF16("field1");
+ field.autocomplete_attribute = "given-name";
+ form.fields.push_back(field);
+
+ field.label = base::string16();
+ field.name = ASCIIToUTF16("field2");
+ field.autocomplete_attribute = "family-name";
+ form.fields.push_back(field);
+
+ field.label = base::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(HTML_TYPE_GIVEN_NAME, form_structure->field(0)->html_type());
+ EXPECT_EQ(HTML_TYPE_FAMILY_NAME, form_structure->field(1)->html_type());
+ EXPECT_EQ(HTML_TYPE_EMAIL, form_structure->field(2)->html_type());
+ EXPECT_EQ(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 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 = base::string16();
+ field.name = ASCIIToUTF16("field1");
+ field.autocomplete_attribute = "tel-local";
+ form.fields.push_back(field);
+
+ field.label = base::string16();
+ field.name = ASCIIToUTF16("field2");
+ field.autocomplete_attribute = "tel-local-prefix";
+ form.fields.push_back(field);
+
+ field.label = base::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(HTML_TYPE_TEL_LOCAL, form_structure->field(0)->html_type());
+ EXPECT_EQ(AutofillField::IGNORED, form_structure->field(0)->phone_part());
+ EXPECT_EQ(HTML_TYPE_TEL_LOCAL_PREFIX, form_structure->field(1)->html_type());
+ EXPECT_EQ(AutofillField::PHONE_PREFIX,
+ form_structure->field(1)->phone_part());
+ EXPECT_EQ(HTML_TYPE_TEL_LOCAL_SUFFIX, form_structure->field(2)->html_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());
+
+ // When Autocheckout is enabled, we should ignore 'autocomplete' attribute
+ // when deciding to crowdsource.
+ form_structure.reset(new FormStructure(form, "http://fake.url"));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ EXPECT_TRUE(form_structure->IsAutofillable(true));
+ EXPECT_TRUE(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 address-line1";
+ 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 = "address-line1";
+ form.fields.push_back(field);
+ field.name = base::string16();
+ field.autocomplete_attribute = "section-foo email";
+ form.fields.push_back(field);
+ field.name = base::string16();
+ field.autocomplete_attribute = "name";
+ form.fields.push_back(field);
+ field.name = ASCIIToUTF16("two");
+ field.autocomplete_attribute = "address-line1";
+ 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 = base::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 = base::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 = base::string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Last Name");
+ field.name = base::string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Email");
+ field.name = base::string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Phone");
+ field.name = base::string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = base::string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address");
+ field.name = base::string16();
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Zip code");
+ field.name = base::string16();
+ form.fields.push_back(field);
+
+ field.label = base::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 = base::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 = base::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());
+}
+
+// Numbered address lines after line two are ignored.
+TEST(FormStructureTest, SurplusAddressLinesIgnored) {
+ 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 Line3");
+ field.name = ASCIIToUTF16("billing.address.addressLine3");
+ form.fields.push_back(field);
+
+ field.label = ASCIIToUTF16("Address Line4");
+ field.name = ASCIIToUTF16("billing.address.addressLine4");
+ form.fields.push_back(field);
+
+ form_structure.reset(new FormStructure(form, std::string()));
+ form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
+ ASSERT_EQ(4U, form_structure->field_count());
+ ASSERT_EQ(2U, 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 (ignored).
+ EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type());
+ // Address Line 4 (ignored).
+ EXPECT_EQ(UNKNOWN_TYPE, 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 = base::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<ServerFieldTypeSet> 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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]);
+
+ ServerFieldTypeSet 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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));
+}
+
+TEST(FormStructureTest, EncodeFieldAssignments) {
+ scoped_ptr<FormStructure> form_structure;
+ std::vector<ServerFieldTypeSet> 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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]);
+
+ ServerFieldTypeSet 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->EncodeFieldAssignments(
+ available_field_types, &encoded_xml));
+ EXPECT_EQ(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<fieldassignments formsignature=\"8736493185895608956\">"
+ "<fields fieldid=\"3763331450\" fieldtype=\"3\" name=\"firstname\"/>"
+ "<fields fieldid=\"3494530716\" fieldtype=\"5\" name=\"lastname\"/>"
+ "<fields fieldid=\"1029417091\" fieldtype=\"9\" name=\"email\"/>"
+ "<fields fieldid=\"466116101\" fieldtype=\"14\" name=\"phone\"/>"
+ "<fields fieldid=\"2799270304\" fieldtype=\"36\" name=\"country\"/>"
+ "<fields fieldid=\"3410250678\" fieldtype=\"36\" name=\"Checkable1\"/>"
+ "</fieldassignments>",
+ 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(ServerFieldTypeSet());
+ 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->EncodeFieldAssignments(
+ available_field_types, &encoded_xml));
+ EXPECT_EQ(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<fieldassignments formsignature=\"7816485729218079147\">"
+ "<fields fieldid=\"3763331450\" fieldtype=\"3\" name=\"firstname\"/>"
+ "<fields fieldid=\"3494530716\" fieldtype=\"5\" name=\"lastname\"/>"
+ "<fields fieldid=\"1029417091\" fieldtype=\"9\" name=\"email\"/>"
+ "<fields fieldid=\"466116101\" fieldtype=\"14\" name=\"phone\"/>"
+ "<fields fieldid=\"2799270304\" fieldtype=\"36\" name=\"country\"/>"
+ "<fields fieldid=\"3410250678\" fieldtype=\"36\" name=\"Checkable1\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"30\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"31\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"37\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"38\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"30\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"31\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"37\" name=\"address\"/>"
+ "<fields fieldid=\"509334676\" fieldtype=\"38\" name=\"address\"/>"
+ "</fieldassignments>",
+ 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());
+
+ ServerFieldTypeSet 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
+ ServerFieldTypeSet 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
+ ServerFieldTypeSet 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<ServerFieldTypeSet> 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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(ServerFieldTypeSet());
+ 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);
+
+ // Password fields shouldn't affect the signature.
+ field.label = ASCIIToUTF16("Password");
+ field.name = ASCIIToUTF16("password");
+ field.form_control_type = "password";
+ 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());
+
+ field.label = ASCIIToUTF16("Random Field label");
+ field.name = ASCIIToUTF16("random1234");
+ field.form_control_type = "text";
+ form.fields.push_back(field);
+ field.label = ASCIIToUTF16("Random Field label2");
+ field.name = ASCIIToUTF16("random12345");
+ form.fields.push_back(field);
+ field.label = ASCIIToUTF16("Random Field label3");
+ field.name = ASCIIToUTF16("1random12345678");
+ form.fields.push_back(field);
+ field.label = ASCIIToUTF16("Random Field label3");
+ field.name = ASCIIToUTF16("12345random");
+ form.fields.push_back(field);
+ form_structure.reset(new FormStructure(form, std::string()));
+ EXPECT_EQ(FormStructureTest::Hash64Bit(
+ std::string("https://login.facebook.com&login_form&email&first&"
+ "random1234&random&1random&random")),
+ 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 = base::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());
+}
+
+TEST(FormStructureTest, SkipFieldTest) {
+ 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 = base::string16();
+ field.name = ASCIIToUTF16("email");
+ field.form_control_type = "text";
+ form.fields.push_back(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 kSignature = "18006745212084723782";
+ const char * const kResponse =
+ "<\?xml version=\"1.0\" encoding=\"UTF-8\"?><autofillquery "
+ "clientversion=\"6.1.1715.1442/en (GGLL)\" accepts=\"e\"><form "
+ "signature=\"18006745212084723782\"><field signature=\"239111655\"/>"
+ "<field signature=\"420638584\"/></form></autofillquery>";
+ ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
+ &encoded_signatures,
+ &encoded_xml));
+ ASSERT_EQ(1U, encoded_signatures.size());
+ EXPECT_EQ(kSignature, encoded_signatures[0]);
+ EXPECT_EQ(kResponse, encoded_xml);
+
+ AutocheckoutPageMetaData page_meta_data;
+ const char * const kServerResponse =
+ "<autofillqueryresponse><field autofilltype=\"3\" />"
+ "<field autofilltype=\"9\" /></autofillqueryresponse>";
+ FormStructure::ParseQueryResponse(kServerResponse, forms.get(),
+ &page_meta_data, TestAutofillMetrics());
+ ASSERT_EQ(NAME_FIRST, forms[0]->field(0)->server_type());
+ ASSERT_EQ(NO_SERVER_DATA, forms[0]->field(1)->server_type());
+ ASSERT_EQ(EMAIL_ADDRESS, forms[0]->field(2)->server_type());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/name_field.cc b/chromium/components/autofill/core/browser/name_field.cc
new file mode 100644
index 00000000000..de74ba1a2d6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/name_field.cc
@@ -0,0 +1,217 @@
+// 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/core/browser/name_field.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+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(ServerFieldTypeMap* 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(ServerFieldTypeMap* 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(ServerFieldTypeMap* 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(ServerFieldTypeMap* 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(ServerFieldTypeMap* map) const {
+ bool ok = AddClassification(first_name_, NAME_FIRST, map);
+ ok = ok && AddClassification(last_name_, NAME_LAST, map);
+ ServerFieldType type = middle_initial_ ? NAME_MIDDLE_INITIAL : NAME_MIDDLE;
+ ok = ok && AddClassification(middle_name_, type, map);
+ return ok;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/name_field.h b/chromium/components/autofill/core/browser/name_field.h
new file mode 100644
index 00000000000..e15e7e3d5f7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/name_field.h
@@ -0,0 +1,46 @@
+// 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_CORE_BROWSER_NAME_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_NAME_FIELD_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/form_field.h"
+
+namespace autofill {
+
+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(ServerFieldTypeMap* 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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_NAME_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/name_field_unittest.cc b/chromium/components/autofill/core/browser/name_field_unittest.cc
new file mode 100644
index 00000000000..df6a29bf70b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/name_field_unittest.cc
@@ -0,0 +1,311 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/name_field.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class NameFieldTest : public testing::Test {
+ public:
+ NameFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<NameField> field_;
+ ServerFieldTypeMap 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 = base::string16();
+ field.name = ASCIIToUTF16("firstName");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = base::string16();
+ field.name = ASCIIToUTF16("middleName");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = base::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 = base::string16();
+ field.name = ASCIIToUTF16("first_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = base::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 = base::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 = base::string16();
+ field.name = ASCIIToUTF16("middle_name");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = base::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 = base::string16();
+ field.name = ASCIIToUTF16("XXXnameXXXfirst");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name1")));
+
+ field.label = base::string16();
+ field.name = ASCIIToUTF16("XXXnameXXXmi");
+ list_.push_back(new AutofillField(field, ASCIIToUTF16("name2")));
+
+ field.label = base::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")]);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_autofill_manager.cc b/chromium/components/autofill/core/browser/password_autofill_manager.cc
new file mode 100644
index 00000000000..b568f8d36ed
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_autofill_manager.cc
@@ -0,0 +1,96 @@
+// 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/core/browser/password_autofill_manager.h"
+#include "components/autofill/core/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"
+
+namespace autofill {
+
+////////////////////////////////////////////////////////////////////////////////
+// PasswordAutofillManager, public:
+
+PasswordAutofillManager::PasswordAutofillManager(
+ content::WebContents* web_contents) : web_contents_(web_contents) {
+}
+
+PasswordAutofillManager::~PasswordAutofillManager() {
+}
+
+bool PasswordAutofillManager::DidAcceptAutofillSuggestion(
+ const FormFieldData& field,
+ const base::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 base::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;
+
+ // Scan additional logins for a match.
+ for (PasswordFormFillData::LoginCollection::const_iterator iter =
+ fill_data.additional_logins.begin();
+ iter != fill_data.additional_logins.end(); ++iter) {
+ if (iter->first == current_username)
+ return true;
+ }
+
+ for (PasswordFormFillData::UsernamesCollection::const_iterator usernames_iter
+ = fill_data.other_possible_usernames.begin();
+ usernames_iter != fill_data.other_possible_usernames.end();
+ ++usernames_iter) {
+ for (size_t i = 0; i < usernames_iter->second.size(); ++i) {
+ if (usernames_iter->second[i] == 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;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_autofill_manager.h b/chromium/components/autofill/core/browser/password_autofill_manager.h
new file mode 100644
index 00000000000..97af87f865e
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_autofill_manager.h
@@ -0,0 +1,75 @@
+// 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_CORE_BROWSER_PASSWORD_AUTOFILL_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_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/core/common/password_form_fill_data.h"
+
+namespace content {
+class WebContents;
+} // namespace content
+
+namespace autofill {
+
+// 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 base::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 base::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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_AUTOFILL_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/password_autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/password_autofill_manager_unittest.cc
new file mode 100644
index 00000000000..d7c5cfdd679
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_autofill_manager_unittest.cc
@@ -0,0 +1,78 @@
+// 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 "base/compiler_specific.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/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
+
+namespace autofill {
+
+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.
+ base::string16 username1 = ASCIIToUTF16(kAliceUsername);
+ base::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)));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/password_generator.cc b/chromium/components/autofill/core/browser/password_generator.cc
new file mode 100644
index 00000000000..6f09b6c7029
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator.cc
@@ -0,0 +1,125 @@
+// 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/core/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/chromium/components/autofill/core/browser/password_generator.h b/chromium/components/autofill/core/browser/password_generator.h
new file mode 100644
index 00000000000..a57695c6d4f
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator.h
@@ -0,0 +1,48 @@
+// 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_CORE_BROWSER_PASSWORD_GENERATOR_H_
+#define COMPONENTS_AUTOFILL_CORE_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_CORE_BROWSER_PASSWORD_GENERATOR_H_
diff --git a/chromium/components/autofill/core/browser/password_generator_unittest.cc b/chromium/components/autofill/core/browser/password_generator_unittest.cc
new file mode 100644
index 00000000000..6d9118943c9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/password_generator_unittest.cc
@@ -0,0 +1,58 @@
+// 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 <locale>
+
+#include "components/autofill/core/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/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc
new file mode 100644
index 00000000000..9ddff255d5d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/personal_data_manager.cc
@@ -0,0 +1,1033 @@
+// 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/core/browser/personal_data_manager.h"
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill-inl.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
+#include "components/autofill/core/browser/phone_number.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+using content::BrowserContext;
+
+namespace autofill {
+namespace {
+
+const base::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 the fields
+// required as determined by its country code.
+// No verification of validity of the contents is preformed. This is an
+// existence check only.
+bool IsMinimumAddress(const AutofillProfile& profile,
+ const std::string& app_locale) {
+ // All countries require at least one address line.
+ if (profile.GetRawInfo(ADDRESS_HOME_LINE1).empty())
+ return false;
+ std::string country_code =
+ UTF16ToASCII(profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
+
+ if (country_code.empty())
+ country_code = AutofillCountry::CountryCodeForLocale(app_locale);
+
+ AutofillCountry country(country_code, app_locale);
+
+ if (country.requires_city() && profile.GetRawInfo(ADDRESS_HOME_CITY).empty())
+ return false;
+
+ if (country.requires_state() &&
+ profile.GetRawInfo(ADDRESS_HOME_STATE).empty())
+ return false;
+
+ if (country.requires_zip() && profile.GetRawInfo(ADDRESS_HOME_ZIP).empty())
+ return false;
+
+ return true;
+}
+
+// Return true if the |field_type| and |value| are valid within the context
+// of importing a form.
+bool IsValidFieldTypeAndValue(const std::set<ServerFieldType>& types_seen,
+ ServerFieldType field_type,
+ const base::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, and for EMAIL_ADDRESS because it is
+ // common to see second 'confirm email address' fields on forms.
+ if (types_seen.count(field_type) &&
+ field_type != PHONE_HOME_NUMBER &&
+ field_type != EMAIL_ADDRESS)
+ 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 && IsValidEmailAddress(value))
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+PersonalDataManager::PersonalDataManager(const std::string& app_locale)
+ : browser_context_(NULL),
+ is_data_loaded_(false),
+ pending_profiles_query_(0),
+ pending_creditcards_query_(0),
+ app_locale_(app_locale),
+ metric_logger_(new AutofillMetrics),
+ has_logged_profile_count_(false) {}
+
+void PersonalDataManager::Init(BrowserContext* browser_context) {
+ browser_context_ = browser_context;
+
+ if (!browser_context_->IsOffTheRecord())
+ metric_logger_->LogIsAutofillEnabledAtStartup(IsAutofillEnabled());
+
+ scoped_refptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+
+ // WebDataService may not be available in tests.
+ if (!autofill_data.get())
+ return;
+
+ LoadProfiles();
+ LoadCreditCards();
+
+ autofill_data->AddObserver(this);
+}
+
+PersonalDataManager::~PersonalDataManager() {
+ CancelPendingQuery(&pending_profiles_query_);
+ CancelPendingQuery(&pending_creditcards_query_);
+
+ if (!browser_context_)
+ return;
+
+ scoped_refptr<AutofillWebDataService> autofill_data(
+ AutofillWebDataService::FromBrowserContext(browser_context_));
+ if (autofill_data.get())
+ autofill_data->RemoveObserver(this);
+}
+
+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::AutofillMultipleChanged() {
+ Refresh();
+}
+
+void PersonalDataManager::AddObserver(PersonalDataManagerObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void PersonalDataManager::RemoveObserver(
+ PersonalDataManagerObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+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);
+
+ const std::string origin = form.source_url().spec();
+ imported_profile->set_origin(origin);
+ local_imported_credit_card->set_origin(origin);
+
+ // 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.
+ // TODO(isherman): Some types are overlapping but not equal, e.g. phone number
+ // parts, address parts.
+ std::set<ServerFieldType> types_seen;
+
+ // We only set complete phone, so aggregate phone parts in these vars and set
+ // complete at the end.
+ PhoneNumber::PhoneCombineHelper home;
+
+ for (size_t i = 0; i < form.field_count(); ++i) {
+ const AutofillField* field = form.field(i);
+ base::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;
+
+ AutofillType field_type = field->Type();
+ ServerFieldType server_field_type = field_type.GetStorableType();
+ FieldTypeGroup group(field_type.group());
+
+ // There can be multiple email fields (e.g. in the case of 'confirm email'
+ // fields) but they must all contain the same value, else the profile is
+ // invalid.
+ if (server_field_type == EMAIL_ADDRESS) {
+ if (types_seen.count(server_field_type) &&
+ imported_profile->GetRawInfo(EMAIL_ADDRESS) != value) {
+ imported_profile.reset();
+ break;
+ }
+ }
+
+ // If the |field_type| and |value| don't pass basic validity checks then
+ // abandon the import.
+ if (!IsValidFieldTypeAndValue(types_seen, server_field_type, value)) {
+ imported_profile.reset();
+ local_imported_credit_card.reset();
+ break;
+ }
+
+ types_seen.insert(server_field_type);
+
+ if (group == CREDIT_CARD) {
+ if (LowerCaseEqualsASCII(field->form_control_type, "month")) {
+ DCHECK_EQ(CREDIT_CARD_EXP_MONTH, server_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 (server_field_type == ADDRESS_HOME_COUNTRY &&
+ !value.empty() &&
+ imported_profile->GetRawInfo(ADDRESS_HOME_COUNTRY).empty()) {
+ imported_profile.reset();
+ break;
+ }
+ }
+ }
+
+ // Construct the phone number. Reject the profile if the number is invalid.
+ if (imported_profile.get() && !home.IsEmpty()) {
+ base::string16 constructed_number;
+ if (!home.ParseNumber(*imported_profile, app_locale_,
+ &constructed_number) ||
+ !imported_profile->SetInfo(AutofillType(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, app_locale_))
+ 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) {
+ // Make a local copy so that the data in |credit_cards_| isn't modified
+ // directly by the UpdateFromImportedCard() call.
+ CreditCard card = **iter;
+ if (card.UpdateFromImportedCard(*local_imported_credit_card.get(),
+ app_locale_)) {
+ merged_credit_card = true;
+ UpdateCreditCard(card);
+ 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(app_locale_))
+ return;
+
+ // Don't add an existing profile.
+ if (FindByGUID<AutofillProfile>(web_profiles_, profile.guid()))
+ return;
+
+ scoped_refptr<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;
+
+ AutofillProfile* existing_profile = GetProfileByGUID(profile.guid());
+ if (!existing_profile)
+ return;
+
+ // Don't overwrite the origin for a profile that is already stored.
+ if (existing_profile->Compare(profile) == 0)
+ return;
+
+ if (profile.IsEmpty(app_locale_)) {
+ RemoveByGUID(profile.guid());
+ return;
+ }
+
+ scoped_refptr<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(app_locale_))
+ return;
+
+ if (FindByGUID<CreditCard>(credit_cards_, credit_card.guid()))
+ return;
+
+ scoped_refptr<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;
+
+ CreditCard* existing_credit_card = GetCreditCardByGUID(credit_card.guid());
+ if (!existing_credit_card)
+ return;
+
+ // Don't overwrite the origin for a credit card that is already stored.
+ if (existing_credit_card->Compare(credit_card) == 0)
+ return;
+
+ if (credit_card.IsEmpty(app_locale_)) {
+ RemoveByGUID(credit_card.guid());
+ return;
+ }
+
+ scoped_refptr<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_refptr<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) {
+ const std::vector<CreditCard*>& credit_cards = GetCreditCards();
+ for (std::vector<CreditCard*>::const_iterator iter = credit_cards.begin();
+ iter != credit_cards.end(); ++iter) {
+ if ((*iter)->guid() == guid)
+ return *iter;
+ }
+ return NULL;
+}
+
+void PersonalDataManager::GetNonEmptyTypes(
+ ServerFieldTypeSet* non_empty_types) {
+ 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 (!user_prefs::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::GetCreditCards() const {
+ return credit_cards_.get();
+}
+
+void PersonalDataManager::Refresh() {
+ LoadProfiles();
+ LoadCreditCards();
+}
+
+void PersonalDataManager::GetProfileSuggestions(
+ const AutofillType& type,
+ const base::string16& field_contents,
+ bool field_is_autofilled,
+ std::vector<ServerFieldType> other_field_types,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::string16>* icons,
+ std::vector<GUIDPair>* guid_pairs) {
+ values->clear();
+ labels->clear();
+ icons->clear();
+ guid_pairs->clear();
+
+ const std::vector<AutofillProfile*>& profiles = GetProfiles();
+ 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<base::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;
+
+ base::string16 profile_value_lower_case(
+ StringToLowerASCII(multi_values[i]));
+ base::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.GetStorableType() == PHONE_HOME_NUMBER &&
+ !field_value_lower_case.empty() &&
+ profile_value_lower_case.find(field_value_lower_case) !=
+ base::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.GetStorableType(), 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(
+ const AutofillType& type,
+ const base::string16& field_contents,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::string16>* icons,
+ std::vector<GUIDPair>* guid_pairs) {
+ values->clear();
+ labels->clear();
+ icons->clear();
+ guid_pairs->clear();
+
+ const std::vector<CreditCard*>& credit_cards = GetCreditCards();
+ 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|.
+ base::string16 creditcard_field_value =
+ credit_card->GetInfo(type, app_locale_);
+ if (!creditcard_field_value.empty() &&
+ StartsWith(creditcard_field_value, field_contents, false)) {
+ if (type.GetStorableType() == CREDIT_CARD_NUMBER)
+ creditcard_field_value = credit_card->ObfuscatedNumber();
+
+ base::string16 label;
+ if (credit_card->number().empty()) {
+ // If there is no CC number, return name to show something.
+ label =
+ credit_card->GetInfo(AutofillType(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 user_prefs::UserPrefs::Get(browser_context_)->GetBoolean(
+ prefs::kAutofillEnabled);
+}
+
+// static
+bool PersonalDataManager::IsValidLearnableProfile(
+ const AutofillProfile& profile,
+ const std::string& app_locale) {
+ if (!IsMinimumAddress(profile, app_locale))
+ return false;
+
+ base::string16 email = profile.GetRawInfo(EMAIL_ADDRESS);
+ if (!email.empty() && !IsValidEmailAddress(email))
+ return false;
+
+ // Reject profiles with invalid US state information.
+ if (profile.IsPresentButInvalid(ADDRESS_HOME_STATE))
+ return false;
+
+ // Reject profiles with invalid US zip information.
+ if (profile.IsPresentButInvalid(ADDRESS_HOME_ZIP))
+ return false;
+
+ return true;
+}
+
+// static
+bool PersonalDataManager::MergeProfile(
+ const AutofillProfile& new_profile,
+ const std::vector<AutofillProfile*>& existing_profiles,
+ const std::string& app_locale,
+ std::vector<AutofillProfile>* merged_profiles) {
+ merged_profiles->clear();
+
+ // Set to true if |existing_profiles| already contains an equivalent profile.
+ bool matching_profile_found = 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) {
+ AutofillProfile* existing_profile = *iter;
+ if (!matching_profile_found &&
+ !new_profile.PrimaryValue().empty() &&
+ StringToLowerASCII(existing_profile->PrimaryValue()) ==
+ StringToLowerASCII(new_profile.PrimaryValue())) {
+ // Unverified profiles should always be updated with the newer data,
+ // whereas verified profiles should only ever be overwritten by verified
+ // data. If an automatically aggregated profile would overwrite a
+ // verified profile, just drop it.
+ matching_profile_found = true;
+ if (!existing_profile->IsVerified() || new_profile.IsVerified())
+ existing_profile->OverwriteWithOrAddTo(new_profile, app_locale);
+ }
+ merged_profiles->push_back(*existing_profile);
+ }
+
+ // If the new profile was not merged with an existing one, add it to the list.
+ if (!matching_profile_found)
+ merged_profiles->push_back(new_profile);
+
+ return matching_profile_found;
+}
+
+void PersonalDataManager::SetProfiles(std::vector<AutofillProfile>* profiles) {
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ // Remove empty profiles from input.
+ for (std::vector<AutofillProfile>::iterator it = profiles->begin();
+ it != profiles->end();) {
+ if (it->IsEmpty(app_locale_))
+ profiles->erase(it);
+ else
+ it++;
+ }
+
+ // 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_refptr<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.
+ for (std::vector<CreditCard>::iterator it = credit_cards->begin();
+ it != credit_cards->end();) {
+ if (it->IsEmpty(app_locale_))
+ credit_cards->erase(it);
+ else
+ it++;
+ }
+
+ scoped_refptr<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_refptr<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 and Android implementations
+// fill in the contents of |auxiliary_profiles_|.
+#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
+void PersonalDataManager::LoadAuxiliaryProfiles() {
+}
+#endif
+
+void PersonalDataManager::LoadCreditCards() {
+ scoped_refptr<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_refptr<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())
+ 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, app_locale_))
+ return;
+ }
+
+ std::vector<AutofillProfile> profiles;
+ MergeProfile(imported_profile, web_profiles_.get(), app_locale_, &profiles);
+ SetProfiles(&profiles);
+}
+
+
+void PersonalDataManager::SaveImportedCreditCard(
+ const CreditCard& imported_card) {
+ DCHECK(!imported_card.number().empty());
+ if (browser_context_->IsOffTheRecord())
+ return;
+
+ // Set to true if |imported_card| is merged into the credit card list.
+ bool merged = false;
+
+ 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;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h
new file mode 100644
index 00000000000..9b71aa55c58
--- /dev/null
+++ b/chromium/components/autofill/core/browser/personal_data_manager.h
@@ -0,0 +1,293 @@
+// 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_CORE_BROWSER_PERSONAL_DATA_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_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/strings/string16.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+
+class RemoveAutofillTester;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace autofill {
+class AutofillInteractiveTest;
+class AutofillMetrics;
+class AutofillTest;
+class FormStructure;
+class PersonalDataManagerObserver;
+class PersonalDataManagerFactory;
+} // namespace autofill
+
+namespace autofill_helper {
+void SetProfiles(int, std::vector<autofill::AutofillProfile>*);
+void SetCreditCards(int, std::vector<autofill::CreditCard>*);
+} // namespace autofill_helper
+
+namespace autofill {
+
+// 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 AutofillWebDataServiceObserverOnUIThread {
+ 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;
+
+ explicit PersonalDataManager(const std::string& app_locale);
+ 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;
+
+ // AutofillWebDataServiceObserverOnUIThread:
+ virtual void AutofillMultipleChanged() 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);
+
+ // 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(ServerFieldTypeSet* 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. |GetProfiles()| 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*>& GetCreditCards() 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(
+ const AutofillType& type,
+ const base::string16& field_contents,
+ bool field_is_autofilled,
+ std::vector<ServerFieldType> other_field_types,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::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(
+ const AutofillType& type,
+ const base::string16& field_contents,
+ std::vector<base::string16>* values,
+ std::vector<base::string16>* labels,
+ std::vector<base::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();
+
+ const std::string& app_locale() const { return app_locale_; }
+
+ // Checks suitability of |profile| for adding to the user's set of profiles.
+ static bool IsValidLearnableProfile(const AutofillProfile& profile,
+ const std::string& app_locale);
+
+ // Merges |new_profile| into one of the |existing_profiles| if possible;
+ // otherwise appends |new_profile| to the end of that list. Fills
+ // |merged_profiles| with the result.
+ static bool MergeProfile(
+ const AutofillProfile& new_profile,
+ const std::vector<AutofillProfile*>& existing_profiles,
+ const std::string& app_locale,
+ 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 autofill::AutofillInteractiveTest;
+ friend class autofill::AutofillTest;
+ friend class autofill::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<autofill::AutofillProfile>*);
+ friend void autofill_helper::SetCreditCards(
+ int, std::vector<autofill::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 and Android 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:
+ std::string app_locale_;
+
+ // 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(PersonalDataManager);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_mac.mm b/chromium/components/autofill/core/browser/personal_data_manager_mac.mm
new file mode 100644
index 00000000000..409fdc11da3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/personal_data_manager_mac.mm
@@ -0,0 +1,273 @@
+// 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/core/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/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/phone_number.h"
+#include "grit/component_strings.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+namespace autofill {
+namespace {
+
+const char kAddressBookOrigin[] = "OS X Address Book";
+
+// 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(const std::string& app_locale);
+
+ private:
+ void GetAddressBookNames(ABPerson* me,
+ NSString* addressLabelRaw,
+ AutofillProfile* profile);
+ void GetAddressBookAddress(const std::string& app_locale,
+ 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(
+ const std::string& app_locale) {
+ 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, kAddressBookOrigin));
+ DCHECK(base::IsValidGUID(profile->guid()));
+
+ // Fill in name and company information.
+ GetAddressBookNames(me, addressLabelRaw, profile.get());
+
+ // Fill in address information.
+ GetAddressBookAddress(app_locale, 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(const std::string& app_locale,
+ 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(AutofillType(ADDRESS_HOME_COUNTRY),
+ base::SysNSStringToUTF16(country),
+ app_locale);
+ }
+}
+
+// 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]) {
+ base::string16 homePhone = base::SysNSStringToUTF16(
+ [phoneNumbers valueAtIndex:reverseK]);
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, homePhone);
+ } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] &&
+ [phoneLabelRaw isEqualToString:kABPhoneWorkLabel]) {
+ base::string16 workPhone = base::SysNSStringToUTF16(
+ [phoneNumbers valueAtIndex:reverseK]);
+ profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, workPhone);
+ } else if ([phoneLabelRaw isEqualToString:kABPhoneMobileLabel] ||
+ [phoneLabelRaw isEqualToString:kABPhoneMainLabel]) {
+ base::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(app_locale_);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_observer.h b/chromium/components/autofill/core/browser/personal_data_manager_observer.h
new file mode 100644
index 00000000000..d4a24cabb1c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/personal_data_manager_observer.h
@@ -0,0 +1,25 @@
+// 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_CORE_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
+
+namespace autofill {
+
+// 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() {}
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PERSONAL_DATA_MANAGER_OBSERVER_H_
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
new file mode 100644
index 00000000000..7c08e5f8983
--- /dev/null
+++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -0,0 +1,2418 @@
+// 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 <string>
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/autofill/core/browser/autofill_common_test.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/webdata/encryptor/encryptor.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 autofill {
+namespace {
+
+ACTION(QuitUIMessageLoop) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ base::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_->CreateWebDataService();
+
+ test::DisableSystemServices(profile_.get());
+ ResetPersonalDataManager();
+ }
+
+ virtual void TearDown() {
+ // Destruction order is imposed explicitly here.
+ personal_data_.reset(NULL);
+ profile_.reset(NULL);
+
+ // Schedule another task on the DB thread to notify us that it's safe to
+ // stop the thread.
+ base::WaitableEvent done(false, false);
+ BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
+ done.Wait();
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::MessageLoop::QuitClosure());
+ base::MessageLoop::current()->Run();
+ db_thread_.Stop();
+ }
+
+ void ResetPersonalDataManager() {
+ personal_data_.reset(new PersonalDataManager("en-US"));
+ 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());
+ base::MessageLoop::current()->Run();
+ }
+
+ void MakeProfileIncognito() {
+ profile_->set_incognito(true);
+ }
+
+ base::MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ scoped_ptr<TestingProfile> profile_;
+ scoped_ptr<PersonalDataManager> personal_data_;
+ PersonalDataLoadedObserverMock personal_data_observer_;
+};
+
+TEST_F(PersonalDataManagerTest, AddProfile) {
+ // Add profile0 to the database.
+ AutofillProfile profile0(autofill::test::GetFullProfile());
+ profile0.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("j@s.com"));
+ 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(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile0,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+
+ AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "19482937549");
+
+ AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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());
+ base::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(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card0,
+ "John Dillinger", "423456789012" /* Visa */, "01", "2010");
+
+ CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card1,
+ "Bonnie Parker", "518765432109" /* Mastercard */, "12", "2012");
+
+ CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results1 = personal_data_->GetCreditCards();
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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_->GetCreditCards();
+ ASSERT_EQ(2U, results3.size());
+ EXPECT_EQ(credit_card0, *results3[0]);
+ EXPECT_EQ(credit_card2, *results3[1]);
+}
+
+TEST_F(PersonalDataManagerTest, UpdateUnverifiedProfilesAndCreditCards) {
+ // Start with unverified data.
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
+ test::SetProfileInfo(&profile,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+ EXPECT_FALSE(profile.IsVerified());
+
+ CreditCard credit_card(base::GenerateGUID(), "https://www.example.com/");
+ test::SetCreditCardInfo(&credit_card,
+ "John Dillinger", "423456789012" /* Visa */, "01", "2010");
+ EXPECT_FALSE(credit_card.IsVerified());
+
+ // Add the data to the database.
+ personal_data_->AddProfile(profile);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& profiles1 =
+ personal_data_->GetProfiles();
+ const std::vector<CreditCard*>& cards1 = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, profiles1.size());
+ ASSERT_EQ(1U, cards1.size());
+ EXPECT_EQ(0, profile.Compare(*profiles1[0]));
+ EXPECT_EQ(0, credit_card.Compare(*cards1[0]));
+
+ // Try to update with just the origin changed.
+ AutofillProfile original_profile(profile);
+ CreditCard original_credit_card(credit_card);
+ profile.set_origin("Chrome settings");
+ credit_card.set_origin("Chrome settings");
+
+ EXPECT_TRUE(profile.IsVerified());
+ EXPECT_TRUE(credit_card.IsVerified());
+
+ personal_data_->UpdateProfile(profile);
+ personal_data_->UpdateCreditCard(credit_card);
+
+ // Note: No refresh, as no update is expected.
+
+ const std::vector<AutofillProfile*>& profiles2 =
+ personal_data_->GetProfiles();
+ const std::vector<CreditCard*>& cards2 = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, profiles2.size());
+ ASSERT_EQ(1U, cards2.size());
+ EXPECT_NE(profile.origin(), profiles2[0]->origin());
+ EXPECT_NE(credit_card.origin(), cards2[0]->origin());
+ EXPECT_EQ(original_profile.origin(), profiles2[0]->origin());
+ EXPECT_EQ(original_credit_card.origin(), cards2[0]->origin());
+
+ // Try to update with data changed as well.
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ credit_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Joe"));
+
+ personal_data_->UpdateProfile(profile);
+ personal_data_->UpdateCreditCard(credit_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& profiles3 =
+ personal_data_->GetProfiles();
+ const std::vector<CreditCard*>& cards3 = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, profiles3.size());
+ ASSERT_EQ(1U, cards3.size());
+ EXPECT_EQ(0, profile.Compare(*profiles3[0]));
+ EXPECT_EQ(0, credit_card.Compare(*cards3[0]));
+ EXPECT_EQ(profile.origin(), profiles3[0]->origin());
+ EXPECT_EQ(credit_card.origin(), cards3[0]->origin());
+}
+
+TEST_F(PersonalDataManagerTest, AddProfilesAndCreditCards) {
+ AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile0,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+
+ AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "19482937549");
+
+ CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&credit_card0,
+ "John Dillinger", "423456789012" /* Visa */, "01", "2010");
+
+ CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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(base::GenerateGUID(), "https://www.example.com");
+ 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(base::GenerateGUID(), "https://www.example.com");
+ 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_->GetCreditCards();
+ ASSERT_EQ(0U, results2.size());
+}
+
+TEST_F(PersonalDataManagerTest, Refresh) {
+ AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile0,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+
+ AutofillProfile profile1(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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(base::GenerateGUID(), "https://www.example.com");
+ 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<AutofillWebDataService> wds =
+ AutofillWebDataService::FromBrowserContext(profile_.get());
+ 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());
+ base::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 deleted
+ // 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).
+ profile0.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Mar"));
+ 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());
+ base::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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Email:", "email", "bogus", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ 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());
+}
+
+// Tests that a 'confirm email' field does not block profile import.
+TEST_F(PersonalDataManagerTest, ImportFormDataTwoEmails) {
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "Name:", "name", "George Washington", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "example@example.com", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Confirm email:", "confirm_email", "example@example.com", "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));
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results.size());
+}
+
+// Tests two email fields containing different values blocks provile import.
+TEST_F(PersonalDataManagerTest, ImportFormDataTwoDifferentEmails) {
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "Name:", "name", "George Washington", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "example@example.com", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email2", "example2@example.com", "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));
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(0U, results.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormDataNotEnoughFilledFields) {
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ 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*>& cards = personal_data_->GetCreditCards();
+ ASSERT_EQ(0U, cards.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormMinimumAddressUSA) {
+ // United States addresses must specifiy one address line, a city, state and
+ // zip code.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("Name:", "name", "Barack Obama", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address", "1600 Pennsylvania Avenue", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "Washington", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "DC", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "20500", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Country:", "country", "USA", "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));
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, profiles.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormMinimumAddressGB) {
+ // British addresses do not require a state/province as the county is usually
+ // not requested on forms.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField("Name:", "name", "David Cameron", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address", "10 Downing Street", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "London", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Postcode:", "postcode", "SW1A 2AA", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Country:", "country", "United Kingdom", "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));
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, profiles.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportFormMinimumAddressGI) {
+ // Gibraltar has the most minimal set of requirements for a valid address.
+ // There are no cities or provinces and no postal/zip code system.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "Name:", "name", "Sir Adrian Johns", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address", "The Convent, Main Street", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Country:", "country", "Gibraltar", "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));
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, profiles.size());
+}
+
+TEST_F(PersonalDataManagerTest, ImportPhoneNumberSplitAcrossMultipleFields) {
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Phone #:", "home_phone_area_code", "650", "text", &field);
+ field.max_length = 3;
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Phone #:", "home_phone_prefix", "555", "text", &field);
+ field.max_length = 3;
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Phone #:", "home_phone_suffix", "0000", "text", &field);
+ field.max_length = 4;
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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(base::GenerateGUID(), "https://www.example.com");
+ credit_card0.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("John"));
+ CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com");
+ credit_card1.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Paul"));
+ CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com");
+ credit_card2.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Ringo"));
+ CreditCard credit_card3(base::GenerateGUID(), "https://www.example.com");
+ credit_card3.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Other"));
+ CreditCard credit_card4(base::GenerateGUID(), "https://www.example.com");
+ credit_card4.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Ozzy"));
+ CreditCard credit_card5(base::GenerateGUID(), "https://www.example.com");
+ 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_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "John", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Adams", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "second@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "22 Laussat St", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ AutofillProfile expected2(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "John", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Last name:", "last_name", "Adams", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "second@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Modify expected to include multi-valued fields.
+ std::vector<base::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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address", "1600 Pennsylvania Avenue", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 2:", "address2", "Suite A", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address", "1600 Pennsylvania Avenue", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 2:", "address2", "Suite A", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ // Country gets added.
+ test::CreateTestFormField("Country:", "country", "USA", "text", &field);
+ form2.fields.push_back(field);
+ // Phone gets updated.
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Add multi-valued phone number to expectation. Also, country gets added.
+ std::vector<base::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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "Philadelphia", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "Pennsylvania", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "Philadelphia", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "Pennsylvania", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ AutofillProfile expected2(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Company:", "company", "Government", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "Philadelphia", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "Pennsylvania", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ // Note missing Company field.
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "Philadelphia", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "Pennsylvania", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Company:", "company", "Government", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address Line 1:", "address", "190 High Street", "text", &field);
+ form1.fields.push_back(field);
+ 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);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ const std::vector<AutofillProfile*>& profiles = personal_data_->GetProfiles();
+ ASSERT_EQ(0U, profiles.size());
+ const std::vector<CreditCard*>& cards = personal_data_->GetCreditCards();
+ ASSERT_EQ(0U, 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(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "Tester", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "McAddressBookTesterson", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "tester@example.com", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Address:", "address1", "1 Main", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "CA", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form.fields.push_back(field);
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard expected(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second different valid credit card.
+ FormData form2;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "5500 0000 0000 0004", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "02", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected2,"", "5500000000000004", "02", "2012");
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard expected(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second different invalid credit card.
+ FormData form2;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Jim Johansen", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "1000000000000000", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "02", "text", &field);
+ form2.fields.push_back(field);
+ 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);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard expected(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111 1111 1111 1111", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ // Expect that the newer information is saved. In this case the year is
+ // updated to "2012".
+ CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2012");
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard expected(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, expected.Compare(*results[0]));
+
+ // Add a second credit card with no number.
+ FormData form2;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form2.fields.push_back(field);
+ 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);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ // No change is expected.
+ CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard expected(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ 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.
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111111111111111", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form2.fields.push_back(field);
+ 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);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ // No change is expected.
+ CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Johnny McEnroe", "text", &field);
+ form3.fields.push_back(field);
+ 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);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ // No change is expected.
+ CreditCard expected3(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected3,
+ "Biggie Smalls", "4111111111111111", "01", "2011");
+ const std::vector<CreditCard*>& results3 = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results3.size());
+ EXPECT_EQ(0, expected3.Compare(*results3[0]));
+}
+
+TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInOld) {
+ // Start with a single valid credit card stored via the preferences.
+ // Note the empty name.
+ CreditCard saved_credit_card(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results1 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ // Expect that the newer information is saved. In this case the year is
+ // added to the existing credit card.
+ CreditCard expected2(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(&expected2,
+ "Biggie Smalls", "4111111111111111", "01", "2012");
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ 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(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<CreditCard*>& results1 = personal_data_->GetCreditCards();
+ 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;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111-1111-1111-1111", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form.fields.push_back(field);
+ 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);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ // Expect that no new card is saved.
+ const std::vector<CreditCard*>& results2 = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results2.size());
+ EXPECT_EQ(0, saved_credit_card.Compare(*results2[0]));
+}
+
+// Ensure that if a verified profile already exists, aggregated profiles cannot
+// modify it in any way.
+TEST_F(PersonalDataManagerTest, AggregateExistingVerifiedProfileWithConflict) {
+ // Start with a verified profile.
+ AutofillProfile profile(base::GenerateGUID(), "Chrome settings");
+ test::SetProfileInfo(&profile,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+ EXPECT_TRUE(profile.IsVerified());
+
+ // Add the profile to the database.
+ personal_data_->AddProfile(profile);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ // Simulate a form submission with conflicting info.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "First name:", "first_name", "Marion", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Morrison", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "other.email@example.com", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "123 Zoo St.", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "Hollywood", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "CA", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "91601", "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());
+ base::MessageLoop::current()->Run();
+
+ // Expect that no new profile is saved.
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, profile.Compare(*results[0]));
+}
+
+// Ensure that if a verified credit card already exists, aggregated credit cards
+// cannot modify it in any way.
+TEST_F(PersonalDataManagerTest,
+ AggregateExistingVerifiedCreditCardWithConflict) {
+ // Start with a verified credit card.
+ CreditCard credit_card(base::GenerateGUID(), "Chrome settings");
+ test::SetCreditCardInfo(&credit_card,
+ "Biggie Smalls", "4111 1111 1111 1111" /* Visa */, "01", "2011");
+ EXPECT_TRUE(credit_card.IsVerified());
+
+ // Add the credit card to the database.
+ 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());
+ base::MessageLoop::current()->Run();
+
+ // Simulate a form submission with conflicting expiration year.
+ FormData form;
+ FormFieldData field;
+ test::CreateTestFormField(
+ "Name on card:", "name_on_card", "Biggie Smalls", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField(
+ "Card Number:", "card_number", "4111 1111 1111 1111", "text", &field);
+ form.fields.push_back(field);
+ test::CreateTestFormField("Exp Month:", "exp_month", "01", "text", &field);
+ form.fields.push_back(field);
+ 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));
+ ASSERT_FALSE(imported_credit_card);
+
+ // Since no refresh is expected, reload the data from the database to make
+ // sure no changes were written out.
+ ResetPersonalDataManager();
+
+ // Expect that the saved credit card is not modified.
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, credit_card.Compare(*results[0]));
+}
+
+// Ensure that verified profiles can be saved via SaveImportedProfile,
+// overwriting existing unverified profiles.
+TEST_F(PersonalDataManagerTest, SaveImportedProfileWithVerifiedData) {
+ // Start with an unverified profile.
+ AutofillProfile profile(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+ EXPECT_FALSE(profile.IsVerified());
+
+ // Add the profile to the database.
+ personal_data_->AddProfile(profile);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile new_verified_profile = profile;
+ new_verified_profile.set_guid(base::GenerateGUID());
+ new_verified_profile.set_origin("Chrome settings");
+ new_verified_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Fizzbang, Inc."));
+ EXPECT_TRUE(new_verified_profile.IsVerified());
+
+ personal_data_->SaveImportedProfile(new_verified_profile);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ // Expect that the existing profile is not modified, and instead the new
+ // profile is added.
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(0, new_verified_profile.Compare(*results[0]));
+}
+
+// Ensure that verified profiles can be saved via SaveImportedProfile,
+// overwriting existing verified profiles as well.
+TEST_F(PersonalDataManagerTest, SaveImportedProfileWithExistingVerifiedData) {
+ // Start with a verified profile.
+ AutofillProfile profile(base::GenerateGUID(), "Chrome settings");
+ test::SetProfileInfo(&profile,
+ "Marion", "Mitchell", "Morrison",
+ "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
+ "91601", "US", "12345678910");
+ EXPECT_TRUE(profile.IsVerified());
+
+ // Add the profile to the database.
+ personal_data_->AddProfile(profile);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile new_verified_profile = profile;
+ new_verified_profile.set_guid(base::GenerateGUID());
+ new_verified_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Fizzbang, Inc."));
+ new_verified_profile.SetRawInfo(NAME_MIDDLE, base::string16());
+ EXPECT_TRUE(new_verified_profile.IsVerified());
+
+ personal_data_->SaveImportedProfile(new_verified_profile);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ // The new profile should be merged into the existing one.
+ AutofillProfile expected_profile = new_verified_profile;
+ expected_profile.set_guid(profile.guid());
+ std::vector<base::string16> names;
+ expected_profile.GetRawMultiInfo(NAME_FULL, &names);
+ names.insert(names.begin(), ASCIIToUTF16("Marion Mitchell Morrison"));
+ expected_profile.SetRawMultiInfo(NAME_FULL, names);
+
+ const std::vector<AutofillProfile*>& results = personal_data_->GetProfiles();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(expected_profile, *results[0]);
+}
+
+// Ensure that verified credit cards can be saved via SaveImportedCreditCard.
+TEST_F(PersonalDataManagerTest, SaveImportedCreditCardWithVerifiedData) {
+ // Start with a verified credit card.
+ CreditCard credit_card(base::GenerateGUID(), "Chrome settings");
+ test::SetCreditCardInfo(&credit_card,
+ "Biggie Smalls", "4111 1111 1111 1111" /* Visa */, "01", "2011");
+ EXPECT_TRUE(credit_card.IsVerified());
+
+ // Add the credit card to the database.
+ 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());
+ base::MessageLoop::current()->Run();
+
+ CreditCard new_verified_card = credit_card;
+ new_verified_card.set_guid(base::GenerateGUID());
+ new_verified_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("B. Small"));
+ EXPECT_TRUE(new_verified_card.IsVerified());
+
+ personal_data_->SaveImportedCreditCard(new_verified_card);
+
+ // Verify that the web database has been updated and the notification sent.
+ EXPECT_CALL(personal_data_observer_,
+ OnPersonalDataChanged()).WillOnce(QuitUIMessageLoop());
+ base::MessageLoop::current()->Run();
+
+ // Expect that the saved credit card is updated.
+ const std::vector<CreditCard*>& results = personal_data_->GetCreditCards();
+ ASSERT_EQ(1U, results.size());
+ EXPECT_EQ(ASCIIToUTF16("B. Small"), results[0]->GetRawInfo(CREDIT_CARD_NAME));
+}
+
+TEST_F(PersonalDataManagerTest, GetNonEmptyTypes) {
+ // Check that there are no available types with no profiles stored.
+ ServerFieldTypeSet non_empty_types;
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+ EXPECT_EQ(0U, non_empty_types.size());
+
+ // Test with one profile stored.
+ AutofillProfile profile0(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&profile1,
+ "Josephine", "Alicia", "Saenz",
+ "joewayne@me.xyz", "Fox", "903 Apple Ct.", NULL, "Orlando", "FL", "32801",
+ "US", "16502937549");
+
+ AutofillProfile profile2(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::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(base::GenerateGUID(), "https://www.example.com");
+ 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());
+ base::MessageLoop::current()->Run();
+
+ personal_data_->GetNonEmptyTypes(&non_empty_types);
+ EXPECT_EQ(26U, 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_TYPE));
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "George", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "City:", "city", "San Francisco", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form1.fields.push_back(field);
+ test::CreateTestFormField(
+ "Zip:", "zip", "94102", "text", &field);
+ form1.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ AutofillProfile expected(base::GenerateGUID(), "https://www.example.com");
+ 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;
+ test::CreateTestFormField(
+ "First name:", "first_name", "GEORGE", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Last name:", "last_name", "Washington", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Email:", "email", "theprez@gmail.com", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField(
+ "Address:", "address1", "21 Laussat St", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("City:", "city", "San Francisco", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("State:", "state", "California", "text", &field);
+ form2.fields.push_back(field);
+ test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
+ form2.fields.push_back(field);
+ 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());
+ base::MessageLoop::current()->Run();
+
+ const std::vector<AutofillProfile*>& results2 = personal_data_->GetProfiles();
+
+ // Modify expected to include multi-valued fields.
+ std::vector<base::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]));
+}
+
+TEST_F(PersonalDataManagerTest, IncognitoReadOnly) {
+ ASSERT_TRUE(personal_data_->GetProfiles().empty());
+ ASSERT_TRUE(personal_data_->GetCreditCards().empty());
+
+ AutofillProfile steve_jobs(base::GenerateGUID(), "https://www.example.com");
+ test::SetProfileInfo(&steve_jobs, "Steven", "Paul", "Jobs", "sjobs@apple.com",
+ "Apple Computer, Inc.", "1 Infinite Loop", "", "Cupertino", "CA", "95014",
+ "US", "(800) 275-2273");
+ personal_data_->AddProfile(steve_jobs);
+
+ CreditCard bill_gates(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(
+ &bill_gates, "William H. Gates", "5555555555554444", "1", "2020");
+ personal_data_->AddCreditCard(bill_gates);
+
+ ResetPersonalDataManager();
+ ASSERT_EQ(1U, personal_data_->GetProfiles().size());
+ ASSERT_EQ(1U, personal_data_->GetCreditCards().size());
+
+ // After this point no adds, saves, or updates should take effect.
+ MakeProfileIncognito();
+ EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()).Times(0);
+
+ // Add profiles or credit card shouldn't work.
+ personal_data_->AddProfile(test::GetFullProfile());
+
+ CreditCard larry_page(base::GenerateGUID(), "https://www.example.com");
+ test::SetCreditCardInfo(
+ &larry_page, "Lawrence Page", "4111111111111111", "10", "2025");
+ personal_data_->AddCreditCard(larry_page);
+
+ ResetPersonalDataManager();
+ EXPECT_EQ(1U, personal_data_->GetProfiles().size());
+ EXPECT_EQ(1U, personal_data_->GetCreditCards().size());
+
+ // Saving or creating profiles from imported profiles shouldn't work.
+ steve_jobs.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Steve"));
+ personal_data_->SaveImportedProfile(steve_jobs);
+
+ bill_gates.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Bill Gates"));
+ personal_data_->SaveImportedCreditCard(bill_gates);
+
+ ResetPersonalDataManager();
+ EXPECT_EQ(ASCIIToUTF16("Steven"),
+ personal_data_->GetProfiles()[0]->GetRawInfo(NAME_FIRST));
+ EXPECT_EQ(ASCIIToUTF16("William H. Gates"),
+ personal_data_->GetCreditCards()[0]->GetRawInfo(CREDIT_CARD_NAME));
+
+ // Updating existing profiles shouldn't work.
+ steve_jobs.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Steve"));
+ personal_data_->UpdateProfile(steve_jobs);
+
+ bill_gates.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Bill Gates"));
+ personal_data_->UpdateCreditCard(bill_gates);
+
+ ResetPersonalDataManager();
+ EXPECT_EQ(ASCIIToUTF16("Steven"),
+ personal_data_->GetProfiles()[0]->GetRawInfo(NAME_FIRST));
+ EXPECT_EQ(ASCIIToUTF16("William H. Gates"),
+ personal_data_->GetCreditCards()[0]->GetRawInfo(CREDIT_CARD_NAME));
+
+ // Removing shouldn't work.
+ personal_data_->RemoveByGUID(steve_jobs.guid());
+ personal_data_->RemoveByGUID(bill_gates.guid());
+
+ ResetPersonalDataManager();
+ EXPECT_EQ(1U, personal_data_->GetProfiles().size());
+ EXPECT_EQ(1U, personal_data_->GetCreditCards().size());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/phone_field.cc b/chromium/components/autofill/core/browser/phone_field.cc
new file mode 100644
index 00000000000..6d156e909c3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_field.cc
@@ -0,0 +1,276 @@
+// 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/core/browser/phone_field.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_regex_constants.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+namespace {
+
+// This string includes all area code separators, including NoText.
+base::string16 GetAreaRegex() {
+ base::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(ServerFieldTypeMap* 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);
+ }
+
+ ServerFieldType 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
+base::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 base::string16();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/phone_field.h b/chromium/components/autofill/core/browser/phone_field.h
new file mode 100644
index 00000000000..f163b9c89ae
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_field.h
@@ -0,0 +1,93 @@
+// 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_CORE_BROWSER_PHONE_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PHONE_FIELD_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/form_field.h"
+#include "components/autofill/core/browser/phone_number.h"
+
+namespace autofill {
+
+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(ServerFieldTypeMap* 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 base::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);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PHONE_FIELD_H_
diff --git a/chromium/components/autofill/core/browser/phone_field_unittest.cc b/chromium/components/autofill/core/browser/phone_field_unittest.cc
new file mode 100644
index 00000000000..44c3df66b8c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_field_unittest.cc
@@ -0,0 +1,230 @@
+// 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 "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/phone_field.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+class PhoneFieldTest : public testing::Test {
+ public:
+ PhoneFieldTest() {}
+
+ protected:
+ ScopedVector<const AutofillField> list_;
+ scoped_ptr<PhoneField> field_;
+ ServerFieldTypeMap 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")]);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/phone_number.cc b/chromium/components/autofill/core/browser/phone_number.cc
new file mode 100644
index 00000000000..2ee67f98b72
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_number.cc
@@ -0,0 +1,247 @@
+// 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/core/browser/phone_number.h"
+
+#include "base/basictypes.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+
+namespace autofill {
+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(base::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) {
+ base::string16 country_code = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
+ if (!country_code.empty())
+ return UTF16ToASCII(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(ServerFieldTypeSet* 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);
+}
+
+base::string16 PhoneNumber::GetRawInfo(ServerFieldType type) const {
+ // TODO(isherman): Is GetStorableType even necessary?
+ if (AutofillType(type).GetStorableType() == 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 base::string16();
+}
+
+void PhoneNumber::SetRawInfo(ServerFieldType type,
+ const base::string16& value) {
+ // TODO(isherman): Is GetStorableType even necessary?
+ type = AutofillType(type).GetStorableType();
+ 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_ = 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.
+base::string16 PhoneNumber::GetInfo(const AutofillType& type,
+ const std::string& app_locale) const {
+ ServerFieldType storable_type = type.GetStorableType();
+ 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 (storable_type != PHONE_HOME_WHOLE_NUMBER &&
+ !cached_parsed_phone_.IsValidNumber())
+ return base::string16();
+
+ switch (storable_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 base::string16();
+ }
+}
+
+bool PhoneNumber::SetInfo(const AutofillType& type,
+ const base::string16& value,
+ const std::string& app_locale) {
+ SetRawInfo(type.GetStorableType(), 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 base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const {
+ base::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.
+ base::string16 number = GetInfo(AutofillType(PHONE_HOME_NUMBER), app_locale);
+ if (GetRegion(*profile_, app_locale) == "US" &&
+ number.size() == (kPrefixLength + kSuffixLength)) {
+ base::string16 prefix = number.substr(kPrefixOffset, kPrefixLength);
+ base::string16 suffix = number.substr(kSuffixOffset, kSuffixLength);
+ if (text == prefix || text == suffix)
+ matching_types->insert(PHONE_HOME_NUMBER);
+ }
+
+ base::string16 whole_number =
+ GetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), app_locale);
+ if (!whole_number.empty()) {
+ base::string16 normalized_number =
+ 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_ = i18n::PhoneObject(number_, region);
+}
+
+PhoneNumber::PhoneCombineHelper::PhoneCombineHelper() {
+}
+
+PhoneNumber::PhoneCombineHelper::~PhoneCombineHelper() {
+}
+
+bool PhoneNumber::PhoneCombineHelper::SetInfo(const AutofillType& type,
+ const base::string16& value) {
+ ServerFieldType storable_type = type.GetStorableType();
+ if (storable_type == PHONE_HOME_COUNTRY_CODE) {
+ country_ = value;
+ return true;
+ }
+
+ if (storable_type == PHONE_HOME_CITY_CODE) {
+ city_ = value;
+ return true;
+ }
+
+ if (storable_type == PHONE_HOME_CITY_AND_NUMBER) {
+ phone_ = value;
+ return true;
+ }
+
+ if (storable_type == PHONE_HOME_WHOLE_NUMBER) {
+ whole_number_ = value;
+ return true;
+ }
+
+ if (storable_type == PHONE_HOME_NUMBER) {
+ phone_.append(value);
+ return true;
+ }
+
+ return false;
+}
+
+bool PhoneNumber::PhoneCombineHelper::ParseNumber(
+ const AutofillProfile& profile,
+ const std::string& app_locale,
+ base::string16* value) {
+ if (IsEmpty())
+ return false;
+
+ if (!whole_number_.empty()) {
+ *value = whole_number_;
+ return true;
+ }
+
+ return i18n::ConstructPhoneNumber(
+ country_, city_, phone_, GetRegion(profile, app_locale), value);
+}
+
+bool PhoneNumber::PhoneCombineHelper::IsEmpty() const {
+ return phone_.empty() && whole_number_.empty();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/phone_number.h b/chromium/components/autofill/core/browser/phone_number.h
new file mode 100644
index 00000000000..19f9f091da4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_number.h
@@ -0,0 +1,99 @@
+// 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_CORE_BROWSER_PHONE_NUMBER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PHONE_NUMBER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/form_group.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+
+namespace autofill {
+
+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 base::string16& text,
+ const std::string& app_locale,
+ ServerFieldTypeSet* matching_types) const OVERRIDE;
+ virtual base::string16 GetRawInfo(ServerFieldType type) const OVERRIDE;
+ virtual void SetRawInfo(ServerFieldType type,
+ const base::string16& value) OVERRIDE;
+ virtual base::string16 GetInfo(const AutofillType& type,
+ const std::string& app_locale) const OVERRIDE;
+ virtual bool SetInfo(const AutofillType& type,
+ const base::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(const AutofillType& type, const base::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,
+ base::string16* value);
+
+ // Returns true if both |phone_| and |whole_number_| are empty.
+ bool IsEmpty() const;
+
+ private:
+ base::string16 country_;
+ base::string16 city_;
+ base::string16 phone_;
+ base::string16 whole_number_;
+ };
+
+ private:
+ // FormGroup:
+ virtual void GetSupportedTypes(
+ ServerFieldTypeSet* 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.
+ base::string16 number_;
+ // Profile which stores the region used as hint when normalizing the number.
+ const AutofillProfile* profile_; // WEAK
+
+ // Cached number.
+ mutable i18n::PhoneObject cached_parsed_phone_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PHONE_NUMBER_H_
diff --git a/chromium/components/autofill/core/browser/phone_number_i18n.cc b/chromium/components/autofill/core/browser/phone_number_i18n.cc
new file mode 100644
index 00000000000..1cb49ca20b3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_number_i18n.cc
@@ -0,0 +1,299 @@
+// 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/core/browser/phone_number_i18n.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "third_party/libphonenumber/src/phonenumber_api.h"
+
+using i18n::phonenumbers::PhoneNumber;
+using i18n::phonenumbers::PhoneNumberUtil;
+
+namespace autofill {
+
+namespace {
+
+std::string SanitizeRegion(const std::string& region,
+ const std::string& app_locale) {
+ if (region.length() == 2)
+ return region;
+
+ return AutofillCountry::CountryCodeForLocale(app_locale);
+}
+
+// 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 base::string16& country_code,
+ base::string16* formatted_number,
+ base::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 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 base::string16& value,
+ const std::string& region,
+ base::string16* country_code,
+ base::string16* city_code,
+ base::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 = base::string16();
+
+ phone_util->NormalizeDigitsOnly(&number_text);
+ base::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;
+}
+
+base::string16 NormalizePhoneNumber(const base::string16& value,
+ const std::string& region) {
+ DCHECK_EQ(2u, region.size());
+ base::string16 country_code;
+ base::string16 unused_city_code;
+ base::string16 unused_number;
+ PhoneNumber phone_number;
+ if (!ParsePhoneNumber(value, region, &country_code, &unused_city_code,
+ &unused_number, &phone_number)) {
+ return base::string16(); // Parsing failed - do not store phone.
+ }
+
+ base::string16 normalized_number;
+ FormatValidatedNumber(phone_number, country_code, NULL, &normalized_number);
+ return normalized_number;
+}
+
+bool ConstructPhoneNumber(const base::string16& country_code,
+ const base::string16& city_code,
+ const base::string16& number,
+ const std::string& region,
+ base::string16* whole_number) {
+ DCHECK_EQ(2u, region.size());
+ whole_number->clear();
+
+ base::string16 unused_country_code;
+ base::string16 unused_city_code;
+ base::string16 unused_number;
+ PhoneNumber phone_number;
+ if (!ParsePhoneNumber(country_code + city_code + number, 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 base::string16& number_a,
+ const base::string16& number_b,
+ const std::string& raw_region,
+ const std::string& app_locale) {
+ // Sanitize the provided |raw_region| before trying to use it for parsing.
+ const std::string region = SanitizeRegion(raw_region, app_locale);
+
+ 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 base::string16& number,
+ const std::string& region)
+ : region_(region) {
+ DCHECK_EQ(2u, region.size());
+ // 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) { *this = other; }
+
+PhoneObject::PhoneObject() {}
+
+PhoneObject::~PhoneObject() {
+}
+
+base::string16 PhoneObject::GetFormattedNumber() const {
+ if (i18n_number_ && formatted_number_.empty()) {
+ FormatValidatedNumber(*i18n_number_, country_code_, &formatted_number_,
+ &whole_number_);
+ }
+
+ return formatted_number_;
+}
+
+base::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 i18n
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/phone_number_i18n.h b/chromium/components/autofill/core/browser/phone_number_i18n.h
new file mode 100644
index 00000000000..3fcc824280a
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_number_i18n.h
@@ -0,0 +1,112 @@
+// 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_CORE_BROWSER_PHONE_NUMBER_I18N_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PHONE_NUMBER_I18N_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+
+namespace i18n {
+namespace phonenumbers {
+class PhoneNumber;
+}
+}
+
+namespace autofill {
+
+// Utilities to process, normalize and compare international phone numbers.
+namespace 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 base::string16& value,
+ const std::string& region,
+ base::string16* country_code,
+ base::string16* city_code,
+ base::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.
+base::string16 NormalizePhoneNumber(const base::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 base::string16& country_code,
+ const base::string16& city_code,
+ const base::string16& number,
+ const std::string& region,
+ base::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 base::string16& number_a,
+ const base::string16& number_b,
+ const std::string& region,
+ const std::string& app_locale);
+
+// The cached phone number, does parsing only once, improves performance.
+class PhoneObject {
+ public:
+ PhoneObject(const base::string16& number,
+ const std::string& region);
+ PhoneObject(const PhoneObject&);
+ PhoneObject();
+ ~PhoneObject();
+
+ std::string region() const { return region_; }
+
+ base::string16 country_code() const { return country_code_; }
+ base::string16 city_code() const { return city_code_; }
+ base::string16 number() const { return number_; }
+
+ base::string16 GetFormattedNumber() const;
+ base::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_;
+ base::string16 city_code_;
+ base::string16 country_code_;
+ base::string16 number_;
+
+ // Pretty printed version of the whole number, or empty if parsing failed.
+ // Set on first request.
+ mutable base::string16 formatted_number_;
+
+ // The whole number, normalized to contain only digits if possible.
+ // Set on first request.
+ mutable base::string16 whole_number_;
+};
+
+} // namespace i18n
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_PHONE_NUMBER_I18N_H_
diff --git a/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc b/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc
new file mode 100644
index 00000000000..b10345b2166
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_number_i18n_unittest.cc
@@ -0,0 +1,389 @@
+// 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 "base/message_loop/message_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/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 content::BrowserThread;
+
+namespace autofill {
+
+using i18n::NormalizePhoneNumber;
+using i18n::ParsePhoneNumber;
+using i18n::ConstructPhoneNumber;
+using i18n::PhoneNumbersMatch;
+
+TEST(PhoneNumberI18NTest, NormalizePhoneNumber) {
+ // "Large" digits.
+ base::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.
+ base::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"));
+
+ base::string16 phone3(UTF8ToUTF16("16503334\xef\xbc\x92\x35\xd9\xa5"));
+ EXPECT_EQ(NormalizePhoneNumber(phone3, "US"), ASCIIToUTF16("16503334255"));
+
+ base::string16 phone4(UTF8ToUTF16("+1(650)2346789"));
+ EXPECT_EQ(NormalizePhoneNumber(phone4, "US"), ASCIIToUTF16("16502346789"));
+
+ base::string16 phone5(UTF8ToUTF16("6502346789"));
+ EXPECT_EQ(NormalizePhoneNumber(phone5, "US"), ASCIIToUTF16("6502346789"));
+}
+
+TEST(PhoneNumberI18NTest, ParsePhoneNumber) {
+ base::string16 number;
+ base::string16 city_code;
+ base::string16 country_code;
+ ::i18n::phonenumbers::PhoneNumber unused_i18n_number;
+
+ // Test for empty string. Should give back empty strings.
+ base::string16 phone0;
+ EXPECT_FALSE(ParsePhoneNumber(phone0, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ // Test for string with less than 7 digits. Should give back empty strings.
+ base::string16 phone1(ASCIIToUTF16("1234"));
+ EXPECT_FALSE(ParsePhoneNumber(phone1, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ // Test for string with exactly 7 digits.
+ // Not a valid number - starts with 1
+ base::string16 phone2(ASCIIToUTF16("1234567"));
+ EXPECT_FALSE(ParsePhoneNumber(phone2, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ // Not a valid number - does not have area code.
+ base::string16 phone3(ASCIIToUTF16("2234567"));
+ EXPECT_FALSE(ParsePhoneNumber(phone3, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ // Test for string with greater than 7 digits but less than 10 digits.
+ // Should fail parsing in US.
+ base::string16 phone4(ASCIIToUTF16("123456789"));
+ EXPECT_FALSE(ParsePhoneNumber(phone4, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ // Test for string with greater than 7 digits but less than 10 digits and
+ // separators.
+ // Should fail parsing in US.
+ base::string16 phone_separator4(ASCIIToUTF16("12.345-6789"));
+ EXPECT_FALSE(ParsePhoneNumber(phone_separator4, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::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.
+ base::string16 phone5(ASCIIToUTF16("1234567890"));
+ EXPECT_FALSE(ParsePhoneNumber(phone5, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ base::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(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ base::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(base::string16(), country_code);
+
+ // Test for string with exactly 10 digits and separators.
+ // Should give back phone number and city code.
+ base::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(base::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.
+ base::string16 phone8(ASCIIToUTF16("0116504567890"));
+ EXPECT_FALSE(ParsePhoneNumber(phone8, "US",
+ &country_code,
+ &city_code,
+ &number,
+ &unused_i18n_number));
+ EXPECT_EQ(base::string16(), number);
+ EXPECT_EQ(base::string16(), city_code);
+ EXPECT_EQ(base::string16(), country_code);
+
+ // 011 is a correct "dial out" prefix in the USA - the parsing should succeed.
+ base::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.
+ base::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.
+ base::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.
+ base::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);
+
+ base::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);
+
+ base::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.
+ base::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) {
+ base::string16 number;
+ EXPECT_TRUE(ConstructPhoneNumber(ASCIIToUTF16("1"),
+ ASCIIToUTF16("650"),
+ ASCIIToUTF16("2345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("+1 650-234-5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(base::string16(),
+ ASCIIToUTF16("650"),
+ ASCIIToUTF16("2345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("(650) 234-5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(ASCIIToUTF16("1"),
+ base::string16(),
+ ASCIIToUTF16("6502345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("+1 650-234-5678"));
+ EXPECT_TRUE(ConstructPhoneNumber(base::string16(),
+ base::string16(),
+ ASCIIToUTF16("6502345678"),
+ "US",
+ &number));
+ EXPECT_EQ(number, ASCIIToUTF16("(650) 234-5678"));
+
+ EXPECT_FALSE(ConstructPhoneNumber(base::string16(),
+ ASCIIToUTF16("650"),
+ ASCIIToUTF16("234567890"),
+ "US",
+ &number));
+ EXPECT_EQ(number, base::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(base::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(base::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",
+ "en-US"));
+ // Same numbers, undefined country code.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("4158889999"),
+ std::string(),
+ "en-US"));
+
+ // Numbers differ by country code only.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("14158889999"),
+ ASCIIToUTF16("4158889999"),
+ "US",
+ "en-US"));
+
+ // Same numbers, different formats.
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("415-888-9999"),
+ "US",
+ "en-US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("(415)888-9999"),
+ "US",
+ "en-US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("415 888 9999"),
+ "US",
+ "en-US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("4158889999"),
+ ASCIIToUTF16("415 TUV WXYZ"),
+ "US",
+ "en-US"));
+ EXPECT_TRUE(PhoneNumbersMatch(ASCIIToUTF16("1(415)888-99-99"),
+ ASCIIToUTF16("+14158889999"),
+ "US",
+ "en-US"));
+
+ // Partial matches don't count.
+ EXPECT_FALSE(PhoneNumbersMatch(ASCIIToUTF16("14158889999"),
+ ASCIIToUTF16("8889999"),
+ "US",
+ "en-US"));
+
+ // Different numbers don't match.
+ EXPECT_FALSE(PhoneNumbersMatch(ASCIIToUTF16("14158889999"),
+ ASCIIToUTF16("1415888"),
+ "US",
+ "en-US"));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/phone_number_unittest.cc b/chromium/components/autofill/core/browser/phone_number_unittest.cc
new file mode 100644
index 00000000000..fd654d06fa7
--- /dev/null
+++ b/chromium/components/autofill/core/browser/phone_number_unittest.cc
@@ -0,0 +1,212 @@
+// 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 "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/phone_number.h"
+#include "components/autofill/core/browser/phone_number_i18n.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(PhoneNumberTest, Matcher) {
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ // Set phone number so country_code == 1, city_code = 650, number = 2345678.
+ base::string16 phone(ASCIIToUTF16("1 [650] 234-5678"));
+ PhoneNumber phone_number(&profile);
+ phone_number.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), phone, "US");
+
+ ServerFieldTypeSet matching_types;
+ phone_number.GetMatchingTypes(base::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.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+
+ PhoneNumber phone(&profile);
+ EXPECT_EQ(base::string16(), phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+
+ // Set the formatted info directly.
+ EXPECT_TRUE(phone.SetInfo(AutofillType(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(AutofillType(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(AutofillType(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(AutofillType(PHONE_HOME_WHOLE_NUMBER),
+ ASCIIToUTF16("650111111"), "US"));
+ EXPECT_EQ(base::string16(), phone.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
+}
+
+// Test that cached phone numbers are correctly invalidated and updated.
+TEST(PhoneNumberTest, UpdateCachedPhoneNumber) {
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+
+ PhoneNumber phone(&profile);
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("6502345678"));
+ EXPECT_EQ(ASCIIToUTF16("650"),
+ phone.GetInfo(AutofillType(PHONE_HOME_CITY_CODE), "US"));
+
+ // Update the area code.
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("8322345678"));
+ EXPECT_EQ(ASCIIToUTF16("832"),
+ phone.GetInfo(AutofillType(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(base::string16(),
+ phone.GetInfo(AutofillType(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.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("GB"));
+ phone.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("07023456789"));
+ EXPECT_EQ(ASCIIToUTF16("70"),
+ phone.GetInfo(AutofillType(PHONE_HOME_CITY_CODE), "US"));
+}
+
+TEST(PhoneNumberTest, PhoneCombineHelper) {
+ AutofillProfile profile;
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+
+ PhoneNumber::PhoneCombineHelper number1;
+ EXPECT_FALSE(number1.SetInfo(AutofillType(ADDRESS_BILLING_CITY),
+ ASCIIToUTF16("1")));
+ EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_COUNTRY_CODE),
+ ASCIIToUTF16("1")));
+ EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_CITY_CODE),
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number1.SetInfo(AutofillType(PHONE_HOME_NUMBER),
+ ASCIIToUTF16("2345678")));
+ base::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(AutofillType(PHONE_HOME_CITY_CODE),
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number3.SetInfo(AutofillType(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(AutofillType(PHONE_HOME_CITY_CODE),
+ ASCIIToUTF16("123"))); // Incorrect city code.
+ EXPECT_TRUE(number4.SetInfo(AutofillType(PHONE_HOME_NUMBER),
+ ASCIIToUTF16("2345680")));
+ EXPECT_FALSE(number4.ParseNumber(profile, "en-US", &parsed_phone));
+ EXPECT_EQ(base::string16(), parsed_phone);
+
+ PhoneNumber::PhoneCombineHelper number5;
+ EXPECT_TRUE(number5.SetInfo(AutofillType(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(AutofillType(PHONE_HOME_CITY_CODE),
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number6.SetInfo(AutofillType(PHONE_HOME_NUMBER),
+ ASCIIToUTF16("234")));
+ EXPECT_TRUE(number6.SetInfo(AutofillType(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(AutofillType(PHONE_HOME_CITY_CODE),
+ ASCIIToUTF16("650")));
+ EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_NUMBER),
+ ASCIIToUTF16("234")));
+ EXPECT_TRUE(number7.SetInfo(AutofillType(PHONE_HOME_NUMBER),
+ ASCIIToUTF16("5682")));
+ EXPECT_TRUE(number7.ParseNumber(AutofillProfile(), "en-US", &parsed_phone));
+ EXPECT_EQ(ASCIIToUTF16("(650) 234-5682"), parsed_phone);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/state_names.cc b/chromium/components/autofill/core/browser/state_names.cc
new file mode 100644
index 00000000000..0ad35250cf5
--- /dev/null
+++ b/chromium/components/autofill/core/browser/state_names.cc
@@ -0,0 +1,115 @@
+// 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/core/browser/state_names.h"
+
+#include "base/basictypes.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace autofill {
+namespace state_names {
+
+namespace {
+
+// TODO(jhawkins): Add more states/provinces. See http://crbug.com/45039.
+
+struct StateData {
+ const char* const name;
+ const char abbreviation[3];
+};
+
+StateData kStateData[] = {
+ { "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", "mn" },
+ { "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" },
+};
+
+} // namespace
+
+base::string16 GetAbbreviationForName(const base::string16& name) {
+ for (size_t i = 0; i < arraysize(kStateData); ++i) {
+ const StateData& state = kStateData[i];
+ if (LowerCaseEqualsASCII(name, state.name))
+ return ASCIIToUTF16(state.abbreviation);
+ }
+ return base::string16();
+}
+
+base::string16 GetNameForAbbreviation(const base::string16& abbreviation) {
+ for (size_t i = 0; i < arraysize(kStateData); ++i) {
+ const StateData& state = kStateData[i];
+ if (LowerCaseEqualsASCII(abbreviation, state.abbreviation))
+ return ASCIIToUTF16(state.name);
+ }
+ return base::string16();
+}
+
+void GetNameAndAbbreviation(const base::string16& value,
+ base::string16* name,
+ base::string16* abbreviation) {
+ base::string16 full = GetNameForAbbreviation(value);
+ base::string16 abbr = value;
+ if (full.empty()) {
+ abbr = GetAbbreviationForName(value);
+ full = value;
+ }
+
+ if (name)
+ name->swap(full);
+ if (abbreviation)
+ abbreviation->swap(abbr);
+}
+
+} // namespace state_names
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/state_names.h b/chromium/components/autofill/core/browser/state_names.h
new file mode 100644
index 00000000000..cd71539de6a
--- /dev/null
+++ b/chromium/components/autofill/core/browser/state_names.h
@@ -0,0 +1,31 @@
+// 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 "base/strings/string16.h"
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_STATE_NAMES_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_STATE_NAMES_H_
+
+namespace autofill {
+namespace state_names {
+
+// Returns the abbrevation corresponding to the state named |name|, or the empty
+// string if there is no such state.
+base::string16 GetAbbreviationForName(const base::string16& name);
+
+// Returns the full state name corresponding to the |abbrevation|, or the empty
+// string if there is no such state.
+base::string16 GetNameForAbbreviation(const base::string16& abbreviation);
+
+// |value| is either a state name or abbreviation. Detects which it is, and
+// outputs both |name| and |abbreviation|. If it's neither, then |name| is
+// set to |value| and |abbreviation| will be empty.
+void GetNameAndAbbreviation(const base::string16& value,
+ base::string16* name,
+ base::string16* abbreviation);
+
+} // namespace state_names
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_STATE_NAMES_H_
diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.cc b/chromium/components/autofill/core/browser/test_autofill_driver.cc
new file mode 100644
index 00000000000..2b20c3d743c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_autofill_driver.cc
@@ -0,0 +1,40 @@
+// 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/core/browser/test_autofill_driver.h"
+
+namespace autofill {
+
+TestAutofillDriver::TestAutofillDriver(content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents) {}
+
+TestAutofillDriver::~TestAutofillDriver() {}
+
+content::WebContents* TestAutofillDriver::GetWebContents() {
+ return web_contents();
+}
+
+bool TestAutofillDriver::RendererIsAvailable() {
+ return true;
+}
+
+void TestAutofillDriver::SetRendererActionOnFormDataReception(
+ RendererFormDataAction action) {
+}
+
+void TestAutofillDriver::SendFormDataToRenderer(int query_id,
+ const FormData& form_data) {
+}
+
+void TestAutofillDriver::SendAutofillTypePredictionsToRenderer(
+ const std::vector<FormStructure*>& forms) {
+}
+
+void TestAutofillDriver::RendererShouldClearFilledForm() {
+}
+
+void TestAutofillDriver::RendererShouldClearPreviewedForm() {
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_driver.h b/chromium/components/autofill/core/browser/test_autofill_driver.h
new file mode 100644
index 00000000000..dbfe4ef43d4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_autofill_driver.h
@@ -0,0 +1,43 @@
+// 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_CORE_BROWSER_TEST_AUTOFILL_DRIVER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_DRIVER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "components/autofill/core/browser/autofill_driver.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace autofill {
+
+// This class is only for easier writing of tests. All pure virtual functions
+// have been given empty methods.
+// TODO(blundell): Eliminate this class being a WebContentsObserver once
+// autofill shared code no longer needs knowledge of WebContents.
+class TestAutofillDriver : public AutofillDriver,
+ public content::WebContentsObserver {
+ public:
+ explicit TestAutofillDriver(content::WebContents* web_contents);
+ virtual ~TestAutofillDriver();
+
+ // AutofillDriver implementation.
+ virtual content::WebContents* GetWebContents() OVERRIDE;
+ virtual bool RendererIsAvailable() OVERRIDE;
+ virtual void SetRendererActionOnFormDataReception(
+ RendererFormDataAction action) OVERRIDE;
+ virtual void SendFormDataToRenderer(int query_id,
+ const FormData& data) OVERRIDE;
+ virtual void SendAutofillTypePredictionsToRenderer(
+ const std::vector<FormStructure*>& forms) OVERRIDE;
+ virtual void RendererShouldClearFilledForm() OVERRIDE;
+ virtual void RendererShouldClearPreviewedForm() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillDriver);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_DRIVER_H_
diff --git a/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
new file mode 100644
index 00000000000..94e6b9ec8bc
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.cc
@@ -0,0 +1,29 @@
+// 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/core/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<base::string16> autofill_item;
+ autofill_item.push_back(base::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/chromium/components/autofill/core/browser/test_autofill_external_delegate.h b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
new file mode 100644
index 00000000000..cc7bf73ee44
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_autofill_external_delegate.h
@@ -0,0 +1,21 @@
+// 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_CORE_BROWSER_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_
+
+#include "components/autofill/core/browser/autofill_external_delegate.h"
+
+namespace autofill {
+
+class AutofillManager;
+
+// 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_CORE_BROWSER_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc
new file mode 100644
index 00000000000..dc9781d11e0
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc
@@ -0,0 +1,80 @@
+// 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/core/browser/test_autofill_manager_delegate.h"
+
+namespace autofill {
+
+TestAutofillManagerDelegate::TestAutofillManagerDelegate() {}
+TestAutofillManagerDelegate::~TestAutofillManagerDelegate() {}
+
+PersonalDataManager* TestAutofillManagerDelegate::GetPersonalDataManager() {
+ return NULL;
+}
+
+autocheckout::WhitelistManager*
+TestAutofillManagerDelegate::GetAutocheckoutWhitelistManager() const {
+ return NULL;
+}
+
+PrefService* TestAutofillManagerDelegate::GetPrefs() {
+ return NULL;
+}
+
+void TestAutofillManagerDelegate::HideRequestAutocompleteDialog() {}
+
+void TestAutofillManagerDelegate::OnAutocheckoutError() {}
+
+void TestAutofillManagerDelegate::OnAutocheckoutSuccess() {}
+
+void TestAutofillManagerDelegate::ShowAutofillSettings() {}
+
+void TestAutofillManagerDelegate::ConfirmSaveCreditCard(
+ const AutofillMetrics& metric_logger,
+ const CreditCard& credit_card,
+ const base::Closure& save_card_callback) {}
+
+bool TestAutofillManagerDelegate::ShowAutocheckoutBubble(
+ const gfx::RectF& bounding_box,
+ bool is_google_user,
+ const base::Callback<void(AutocheckoutBubbleState)>& callback) {
+ return true;
+}
+
+void TestAutofillManagerDelegate::HideAutocheckoutBubble() {}
+
+void TestAutofillManagerDelegate::ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback) {}
+
+void TestAutofillManagerDelegate::ShowAutofillPopup(
+ const gfx::RectF& element_bounds,
+ base::i18n::TextDirection text_direction,
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels,
+ const std::vector<base::string16>& icons,
+ const std::vector<int>& identifiers,
+ base::WeakPtr<AutofillPopupDelegate> delegate) {}
+
+void TestAutofillManagerDelegate::UpdateAutofillPopupDataListValues(
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) {}
+
+void TestAutofillManagerDelegate::HideAutofillPopup() {}
+
+void TestAutofillManagerDelegate::AddAutocheckoutStep(
+ AutocheckoutStepType step_type) {}
+
+void TestAutofillManagerDelegate::UpdateAutocheckoutStep(
+ AutocheckoutStepType step_type,
+ AutocheckoutStepStatus step_status) {}
+
+bool TestAutofillManagerDelegate::IsAutocompleteEnabled() {
+ return true;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_autofill_manager_delegate.h b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.h
new file mode 100644
index 00000000000..1fee0ba24a9
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.h
@@ -0,0 +1,71 @@
+// 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_CORE_BROWSER_TEST_AUTOFILL_MANAGER_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_MANAGER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "base/i18n/rtl.h"
+#include "components/autofill/core/browser/autocheckout_bubble_state.h"
+#include "components/autofill/core/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 autocheckout::WhitelistManager*
+ GetAutocheckoutWhitelistManager() const OVERRIDE;
+ virtual void HideRequestAutocompleteDialog() OVERRIDE;
+ virtual void OnAutocheckoutError() OVERRIDE;
+ virtual void OnAutocheckoutSuccess() OVERRIDE;
+ virtual void ShowAutofillSettings() OVERRIDE;
+ virtual void ConfirmSaveCreditCard(
+ const AutofillMetrics& metric_logger,
+ const CreditCard& credit_card,
+ const base::Closure& save_card_callback) OVERRIDE;
+ virtual bool ShowAutocheckoutBubble(
+ const gfx::RectF& bounding_box,
+ bool is_google_user,
+ const base::Callback<void(AutocheckoutBubbleState)>& callback) OVERRIDE;
+ virtual void HideAutocheckoutBubble() OVERRIDE;
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ DialogType dialog_type,
+ const base::Callback<void(const FormStructure*,
+ const std::string&)>& callback) OVERRIDE;
+ virtual void ShowAutofillPopup(
+ const gfx::RectF& element_bounds,
+ base::i18n::TextDirection text_direction,
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels,
+ const std::vector<base::string16>& icons,
+ const std::vector<int>& identifiers,
+ base::WeakPtr<AutofillPopupDelegate> delegate) OVERRIDE;
+ virtual void UpdateAutofillPopupDataListValues(
+ const std::vector<base::string16>& values,
+ const std::vector<base::string16>& labels) OVERRIDE;
+ virtual void HideAutofillPopup() OVERRIDE;
+ virtual bool IsAutocompleteEnabled() OVERRIDE;
+
+ virtual void AddAutocheckoutStep(AutocheckoutStepType step_type) OVERRIDE;
+ virtual void UpdateAutocheckoutStep(
+ AutocheckoutStepType step_type,
+ AutocheckoutStepStatus step_status) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAutofillManagerDelegate);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_AUTOFILL_MANAGER_DELEGATE_H_
diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc
new file mode 100644
index 00000000000..0a517d7655b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc
@@ -0,0 +1,42 @@
+// 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/core/browser/test_personal_data_manager.h"
+
+#include "components/autofill/core/browser/personal_data_manager_observer.h"
+
+namespace autofill {
+
+TestPersonalDataManager::TestPersonalDataManager()
+ : PersonalDataManager("en-US") {}
+
+TestPersonalDataManager::~TestPersonalDataManager() {}
+
+void TestPersonalDataManager::AddTestingProfile(AutofillProfile* profile) {
+ profiles_.push_back(profile);
+ FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_,
+ OnPersonalDataChanged());
+}
+
+void TestPersonalDataManager::AddTestingCreditCard(CreditCard* credit_card) {
+ credit_cards_.push_back(credit_card);
+ FOR_EACH_OBSERVER(PersonalDataManagerObserver, observers_,
+ OnPersonalDataChanged());
+}
+
+const std::vector<AutofillProfile*>& TestPersonalDataManager::GetProfiles() {
+ return profiles_;
+}
+
+const std::vector<CreditCard*>& TestPersonalDataManager::
+ GetCreditCards() const {
+ return credit_cards_;
+}
+
+void TestPersonalDataManager::SaveImportedProfile(
+ const AutofillProfile& imported_profile) {
+ imported_profile_ = imported_profile;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.h b/chromium/components/autofill/core/browser/test_personal_data_manager.h
new file mode 100644
index 00000000000..c9f9a43d4a4
--- /dev/null
+++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h
@@ -0,0 +1,44 @@
+// 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_BROWSER_AUTOFILL_TEST_PERSONAL_DATA_MANAGER_H_
+#define COMPONENTS_BROWSER_AUTOFILL_TEST_PERSONAL_DATA_MANAGER_H_
+
+#include <vector>
+
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+
+namespace autofill {
+
+// A simplistic PersonalDataManager used for testing.
+class TestPersonalDataManager : public PersonalDataManager {
+ public:
+ TestPersonalDataManager();
+ virtual ~TestPersonalDataManager();
+
+ // Adds |profile| to |profiles_|. This does not take ownership of |profile|.
+ void AddTestingProfile(AutofillProfile* profile);
+
+ // Adds |credit_card| to |credit_cards_|. This does not take ownership of
+ // |credit_card|.
+ void AddTestingCreditCard(CreditCard* credit_card);
+
+ virtual const std::vector<AutofillProfile*>& GetProfiles() OVERRIDE;
+ virtual void SaveImportedProfile(const AutofillProfile& imported_profile)
+ OVERRIDE;
+
+ const AutofillProfile& imported_profile() { return imported_profile_; }
+ virtual const std::vector<CreditCard*>& GetCreditCards() const OVERRIDE;
+
+ private:
+ std::vector<AutofillProfile*> profiles_;
+ std::vector<CreditCard*> credit_cards_;
+ AutofillProfile imported_profile_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_BROWSER_AUTOFILL_TEST_PERSONAL_DATA_MANAGER_H_
diff --git a/chromium/components/autofill/core/browser/validation.cc b/chromium/components/autofill/core/browser/validation.cc
new file mode 100644
index 00000000000..a8aef35f7ca
--- /dev/null
+++ b/chromium/components/autofill/core/browser/validation.cc
@@ -0,0 +1,220 @@
+// 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/core/browser/validation.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_regexes.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/state_names.h"
+
+using base::StringPiece16;
+
+namespace {
+
+// The separator characters for SSNs.
+const char16 kSSNSeparators[] = {' ', '-', 0};
+
+} // namespace
+
+namespace autofill {
+
+bool IsValidCreditCardExpirationDate(const base::string16& year,
+ const base::string16& month,
+ const base::Time& now) {
+ base::string16 year_cleaned, month_cleaned;
+ TrimWhitespace(year, TRIM_ALL, &year_cleaned);
+ TrimWhitespace(month, TRIM_ALL, &month_cleaned);
+ if (year_cleaned.length() != 4)
+ return false;
+
+ int cc_year;
+ if (!base::StringToInt(year_cleaned, &cc_year))
+ return false;
+
+ int cc_month;
+ if (!base::StringToInt(month_cleaned, &cc_month))
+ return false;
+
+ return IsValidCreditCardExpirationDate(cc_year, cc_month, now);
+}
+
+bool IsValidCreditCardExpirationDate(int year,
+ int month,
+ const base::Time& now) {
+ base::Time::Exploded now_exploded;
+ now.LocalExplode(&now_exploded);
+
+ if (year < now_exploded.year)
+ return false;
+
+ if (year == now_exploded.year && month < now_exploded.month)
+ return false;
+
+ return true;
+}
+
+bool IsValidCreditCardNumber(const base::string16& text) {
+ base::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]. Specific card issuers have more rigidly
+ // defined sizes.
+ // [1] http://www.merriampark.com/anatomycc.htm
+ // [2] http://en.wikipedia.org/wiki/Bank_card_number
+ const std::string type = CreditCard::GetCreditCardType(text);
+ if (type == kAmericanExpressCard && number.size() != 15)
+ return false;
+ if (type == kDinersCard && number.size() != 14)
+ return false;
+ if (type == kDiscoverCard && number.size() != 16)
+ return false;
+ if (type == kJCBCard && number.size() != 16)
+ return false;
+ if (type == kMasterCard && number.size() != 16)
+ return false;
+ if (type == kUnionPay && (number.size() < 16 || number.size() > 19))
+ return false;
+ if (type == kVisaCard && number.size() != 13 && number.size() != 16)
+ return false;
+ if (type == kGenericCard && (number.size() < 12 || number.size() > 19))
+ return false;
+
+ // Unlike all the other supported types, UnionPay cards lack Luhn checksum
+ // validation.
+ if (type == kUnionPay)
+ return true;
+
+ // Use the Luhn formula [3] to validate the number.
+ // [3] http://en.wikipedia.org/wiki/Luhn_algorithm
+ int sum = 0;
+ bool odd = false;
+ for (base::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 base::string16& text) {
+ if (text.size() < 3U || text.size() > 4U)
+ return false;
+
+ for (base::string16::const_iterator iter = text.begin();
+ iter != text.end();
+ ++iter) {
+ if (!IsAsciiDigit(*iter))
+ return false;
+ }
+ return true;
+}
+
+bool IsValidCreditCardSecurityCode(const base::string16& code,
+ const base::string16& number) {
+ CreditCard card;
+ card.SetRawInfo(CREDIT_CARD_NUMBER, number);
+ size_t required_length = 3;
+ if (card.type() == kAmericanExpressCard)
+ required_length = 4;
+
+ return code.length() == required_length;
+}
+
+bool IsValidEmailAddress(const base::string16& text) {
+ // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state)
+ const base::string16 kEmailPattern = ASCIIToUTF16(
+ "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@"
+ "[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$");
+ return MatchesPattern(text, kEmailPattern);
+}
+
+bool IsValidState(const base::string16& text) {
+ return !state_names::GetAbbreviationForName(text).empty() ||
+ !state_names::GetNameForAbbreviation(text).empty();
+}
+
+bool IsValidZip(const base::string16& text) {
+ const base::string16 kZipPattern = ASCIIToUTF16("^\\d{5}(-\\d{4})?$");
+ return MatchesPattern(text, kZipPattern);
+}
+
+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;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/validation.h b/chromium/components/autofill/core/browser/validation.h
new file mode 100644
index 00000000000..bdb6574d4ce
--- /dev/null
+++ b/chromium/components/autofill/core/browser/validation.h
@@ -0,0 +1,53 @@
+// 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_CORE_BROWSER_VALIDATION_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_
+
+#include "base/strings/string16.h"
+
+namespace base {
+class Time;
+} // namespace base;
+
+namespace autofill {
+
+// Returns true if |year| and |month| describe a date later than |now|.
+// |year| must have 4 digits.
+bool IsValidCreditCardExpirationDate(const base::string16& year,
+ const base::string16& month,
+ const base::Time& now);
+bool IsValidCreditCardExpirationDate(int year,
+ int month,
+ const base::Time& now);
+
+// Returns true if |text| looks like a valid credit card number.
+// Uses the Luhn formula to validate the number.
+bool IsValidCreditCardNumber(const base::string16& text);
+
+// Returns true if |text| looks like a valid credit card security code.
+bool IsValidCreditCardSecurityCode(const base::string16& text);
+
+// Returns true if |code| looks like a valid credit card security code
+// for the type of credit card designated by |number|.
+bool IsValidCreditCardSecurityCode(const base::string16& code,
+ const base::string16& number);
+
+// Returns true if |text| looks like a valid e-mail address.
+bool IsValidEmailAddress(const base::string16& text);
+
+// Returns true if |text| is a valid US state name or abbreviation. It is
+// case insensitive. Valid for US states only.
+bool IsValidState(const base::string16& text);
+
+// Returns true if |text| looks like a valid zip code.
+// Valid for US zip codes only.
+bool IsValidZip(const base::string16& text);
+
+// Returns true if |text| looks like an SSN, with or without separators.
+bool IsSSN(const string16& text);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_VALIDATION_H_
diff --git a/chromium/components/autofill/core/browser/validation_unittest.cc b/chromium/components/autofill/core/browser/validation_unittest.cc
new file mode 100644
index 00000000000..171dc5fd087
--- /dev/null
+++ b/chromium/components/autofill/core/browser/validation_unittest.cc
@@ -0,0 +1,199 @@
+// 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 "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/validation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+namespace {
+
+struct ExpirationDate {
+ const char* year;
+ const char* month;
+};
+
+struct IntExpirationDate {
+ const int year;
+ const int month;
+};
+
+// 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",
+
+ // A UnionPay card that doesn't pass the Luhn checksum
+ "6200000000000000",
+};
+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 kCurrentDate[]="1 May 2013";
+const ExpirationDate kValidCreditCardExpirationDate[] = {
+ { "2013", "5" }, // Valid month in current year.
+ { "2014", "1" }, // Any month in next year.
+ { "2014", " 1" }, // Whitespace in month.
+ { " 2014", "1" }, // Whitespace in year.
+};
+const IntExpirationDate kValidCreditCardIntExpirationDate[] = {
+ { 2013, 5 }, // Valid month in current year.
+ { 2014, 1 }, // Any month in next year.
+};
+const ExpirationDate kInvalidCreditCardExpirationDate[] = {
+ { "2013", "04" }, // Previous month in current year.
+ { "2012", "12" }, // Any month in previous year.
+};
+const IntExpirationDate kInvalidCreditCardIntExpirationDate[] = {
+ { 2013, 4 }, // Previous month in current year.
+ { 2012, 12 }, // Any month in previous year.
+};
+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"
+};
+const char kAmericanExpressCard[] = "341111111111111";
+const char kVisaCard[] = "4111111111111111";
+const char kAmericanExpressCVC[] = "1234";
+const char kVisaCVC[] = "123";
+} // 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, IsValidCreditCardExpirationDate)
+{
+ base::Time now;
+ ASSERT_TRUE(base::Time::FromString(kCurrentDate, &now));
+
+ for (size_t i = 0; i < arraysize(kValidCreditCardExpirationDate); ++i) {
+ const ExpirationDate& data = kValidCreditCardExpirationDate[i];
+ SCOPED_TRACE(data.year);
+ SCOPED_TRACE(data.month);
+ EXPECT_TRUE(
+ autofill::IsValidCreditCardExpirationDate(ASCIIToUTF16(data.year),
+ ASCIIToUTF16(data.month),
+ now));
+ }
+ for (size_t i = 0; i < arraysize(kInvalidCreditCardExpirationDate); ++i) {
+ const ExpirationDate& data = kInvalidCreditCardExpirationDate[i];
+ SCOPED_TRACE(data.year);
+ SCOPED_TRACE(data.month);
+ EXPECT_TRUE(
+ !autofill::IsValidCreditCardExpirationDate(ASCIIToUTF16(data.year),
+ ASCIIToUTF16(data.month),
+ now));
+ }
+}
+
+TEST(AutofillValidation, IsValidCreditCardIntExpirationDate)
+{
+ base::Time now;
+ ASSERT_TRUE(base::Time::FromString(kCurrentDate, &now));
+
+ for (size_t i = 0; i < arraysize(kValidCreditCardIntExpirationDate); ++i) {
+ const IntExpirationDate& data = kValidCreditCardIntExpirationDate[i];
+ SCOPED_TRACE(data.year);
+ SCOPED_TRACE(data.month);
+ EXPECT_TRUE(
+ autofill::IsValidCreditCardExpirationDate(data.year, data.month, now));
+ }
+ for (size_t i = 0; i < arraysize(kInvalidCreditCardIntExpirationDate); ++i) {
+ const IntExpirationDate& data = kInvalidCreditCardIntExpirationDate[i];
+ SCOPED_TRACE(data.year);
+ SCOPED_TRACE(data.month);
+ EXPECT_TRUE(
+ !autofill::IsValidCreditCardExpirationDate(data.year, data.month, now));
+ }
+}
+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])));
+ }
+}
+
+TEST(AutofillValidation, IsValidCreditCardSecurityCodeWithNumber) {
+ EXPECT_TRUE(autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kAmericanExpressCard)));
+ EXPECT_TRUE(autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kVisaCard)));
+ EXPECT_FALSE(autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kAmericanExpressCard)));
+ EXPECT_FALSE(autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kVisaCard)));
+ EXPECT_TRUE(autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kVisaCVC), ASCIIToUTF16(kInvalidNumbers[0])));
+ EXPECT_FALSE(autofill::IsValidCreditCardSecurityCode(
+ ASCIIToUTF16(kAmericanExpressCVC), ASCIIToUTF16(kInvalidNumbers[0])));
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_change.cc b/chromium/components/autofill/core/browser/webdata/autofill_change.cc
new file mode 100644
index 00000000000..c02bdd8464d
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_change.cc
@@ -0,0 +1,39 @@
+// 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/core/browser/webdata/autofill_change.h"
+
+#include "base/logging.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+
+namespace autofill {
+
+AutofillChange::AutofillChange(Type type, const AutofillKey& key)
+ : GenericAutofillChange<AutofillKey>(type, key) {
+}
+
+AutofillChange::~AutofillChange() {
+}
+
+AutofillProfileChange::AutofillProfileChange(
+ Type type, const std::string& key, const AutofillProfile* profile)
+ : GenericAutofillChange<std::string>(type, key),
+ profile_(profile) {
+ DCHECK(type == ADD ? (profile && profile->guid() == key) : true);
+ DCHECK(type == UPDATE ? (profile && profile->guid() == key) : true);
+ DCHECK(type == REMOVE ? !profile : true);
+}
+
+AutofillProfileChange::~AutofillProfileChange() {
+}
+
+bool AutofillProfileChange::operator==(
+ const AutofillProfileChange& change) const {
+ return type() == change.type() &&
+ key() == change.key() &&
+ (type() != REMOVE) ? *profile() == *change.profile() : true;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_change.h b/chromium/components/autofill/core/browser/webdata/autofill_change.h
new file mode 100644
index 00000000000..458ac6d2e42
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_change.h
@@ -0,0 +1,76 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
+
+#include <vector>
+
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+
+namespace autofill {
+
+class AutofillProfile;
+class CreditCard;
+
+// For classic Autofill form fields, the KeyType is AutofillKey.
+// Autofill++ types such as AutofillProfile and CreditCard simply use an int.
+template <typename KeyType>
+class GenericAutofillChange {
+ public:
+ typedef enum {
+ ADD,
+ UPDATE,
+ REMOVE
+ } Type;
+
+ virtual ~GenericAutofillChange() {}
+
+ Type type() const { return type_; }
+ const KeyType& key() const { return key_; }
+
+ protected:
+ GenericAutofillChange(Type type, const KeyType& key)
+ : type_(type),
+ key_(key) {}
+ private:
+ Type type_;
+ KeyType key_;
+};
+
+class AutofillChange : public GenericAutofillChange<AutofillKey> {
+ public:
+ AutofillChange(Type type, const AutofillKey& key);
+ virtual ~AutofillChange();
+ bool operator==(const AutofillChange& change) const {
+ return type() == change.type() && key() == change.key();
+ }
+};
+
+typedef std::vector<AutofillChange> AutofillChangeList;
+
+// Change notification details for Autofill profile changes.
+class AutofillProfileChange : public GenericAutofillChange<std::string> {
+ public:
+ // The |type| input specifies the change type. The |key| input is the key,
+ // which is expected to be the GUID identifying the |profile|.
+ // When |type| == ADD, |profile| should be non-NULL.
+ // When |type| == UPDATE, |profile| should be non-NULL.
+ // When |type| == REMOVE, |profile| should be NULL.
+ AutofillProfileChange(Type type,
+ const std::string& key,
+ const AutofillProfile* profile);
+ virtual ~AutofillProfileChange();
+
+ const AutofillProfile* profile() const { return profile_; }
+ bool operator==(const AutofillProfileChange& change) const;
+
+ private:
+ // Weak reference, can be NULL.
+ const AutofillProfile* profile_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_CHANGE_H__
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.cc b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc
new file mode 100644
index 00000000000..5cdc237d88c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.cc
@@ -0,0 +1,129 @@
+// 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/core/browser/webdata/autofill_entry.h"
+
+#include <algorithm>
+#include <set>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace autofill {
+namespace {
+
+// The period after which Autofill entries should expire in days.
+const int64 kExpirationPeriodInDays = 60;
+
+} // namespace
+
+AutofillKey::AutofillKey() {}
+
+AutofillKey::AutofillKey(const base::string16& name,
+ const base::string16& value)
+ : name_(name),
+ value_(value) {
+}
+
+AutofillKey::AutofillKey(const char* name, const char* value)
+ : name_(UTF8ToUTF16(name)),
+ value_(UTF8ToUTF16(value)) {
+}
+
+AutofillKey::AutofillKey(const AutofillKey& key)
+ : name_(key.name()),
+ value_(key.value()) {
+}
+
+AutofillKey::~AutofillKey() {}
+
+bool AutofillKey::operator==(const AutofillKey& key) const {
+ return name_ == key.name() && value_ == key.value();
+}
+
+bool AutofillKey::operator<(const AutofillKey& key) const {
+ int diff = name_.compare(key.name());
+ if (diff < 0) {
+ return true;
+ } else if (diff == 0) {
+ return value_.compare(key.value()) < 0;
+ } else {
+ return false;
+ }
+}
+
+AutofillEntry::AutofillEntry(const AutofillKey& key,
+ const std::vector<base::Time>& timestamps)
+ : key_(key) {
+ timestamps_culled_ = CullTimeStamps(timestamps, &timestamps_);
+}
+
+AutofillEntry::~AutofillEntry() {}
+
+bool AutofillEntry::operator==(const AutofillEntry& entry) const {
+ if (!(key_ == entry.key()))
+ return false;
+
+ if (timestamps_.size() != entry.timestamps().size())
+ return false;
+
+ std::set<base::Time> other_timestamps(entry.timestamps().begin(),
+ entry.timestamps().end());
+ for (size_t i = 0; i < timestamps_.size(); i++) {
+ if (other_timestamps.count(timestamps_[i]) == 0)
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillEntry::operator<(const AutofillEntry& entry) const {
+ return key_ < entry.key();
+}
+
+bool AutofillEntry::IsExpired() const {
+ base::Time time = ExpirationTime();
+ // TODO(georgey): add DCHECK(!timestamps_.empty()) after conversion of the db
+ // is complete.
+ return (timestamps_.empty() || timestamps_.back() < time);
+}
+
+// static
+base::Time AutofillEntry::ExpirationTime() {
+ return base::Time::Now() - base::TimeDelta::FromDays(kExpirationPeriodInDays);
+}
+
+// Culls the list of timestamps to the first and last used.
+// If sync is enabled, at every browser restart, sync will do a match up of all
+// autofill items on the server with all items on the web db. When webdb loads
+// all the items in memory(for sync to process. The method is
+// |GetAllAutofillEntries|) they will pass through this method for culling. If
+// sync finds any of these items were culled it will updates the server AND the
+// web db with these new timestamps. However after restart if an autofill item
+// exceeds the |kMaxAutofillTimeStamps| it will NOT be written to web db until
+// restart. But sync when uploading to the server will only upload this culled
+// list. Meaning until restart there will be mis-match in timestamps but
+// it should correct itself at startup.
+bool AutofillEntry::CullTimeStamps(const std::vector<base::Time>& source,
+ std::vector<base::Time>* result) {
+ DCHECK(result);
+ DCHECK(&source != result);
+
+ // First copy the source to result.
+ result->clear();
+
+ if (source.size() <= 2) {
+ result->insert(result->begin(), source.begin(), source.end());
+ return false;
+ }
+
+ result->push_back(source.front());
+ result->push_back(source.back());
+
+ DVLOG(1) << "Culling timestamps. Current count is : " << source.size();
+
+ return true;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry.h b/chromium/components/autofill/core/browser/webdata/autofill_entry.h
new file mode 100644
index 00000000000..33a467df59e
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_entry.h
@@ -0,0 +1,75 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__
+
+#include <stddef.h>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+
+namespace autofill {
+
+class AutofillKey {
+ public:
+ AutofillKey();
+ AutofillKey(const base::string16& name, const base::string16& value);
+ AutofillKey(const char* name, const char* value);
+ AutofillKey(const AutofillKey& key);
+ virtual ~AutofillKey();
+
+ const base::string16& name() const { return name_; }
+ const base::string16& value() const { return value_; }
+
+ bool operator==(const AutofillKey& key) const;
+ bool operator<(const AutofillKey& key) const;
+
+ private:
+ base::string16 name_;
+ base::string16 value_;
+};
+
+class AutofillEntry {
+ public:
+ AutofillEntry(const AutofillKey& key,
+ const std::vector<base::Time>& timestamps);
+ ~AutofillEntry();
+
+ const AutofillKey& key() const { return key_; }
+ const std::vector<base::Time>& timestamps() const { return timestamps_; }
+
+ bool operator==(const AutofillEntry& entry) const;
+ bool operator<(const AutofillEntry& entry) const;
+
+ bool timestamps_culled() const { return timestamps_culled_; }
+
+ // Checks if last of the timestamps are older than ExpirationTime().
+ bool IsExpired() const;
+
+ // The entries last accessed before this time should expire.
+ static base::Time ExpirationTime();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AutofillEntryTest, NoCulling);
+ FRIEND_TEST_ALL_PREFIXES(AutofillEntryTest, Culling);
+ FRIEND_TEST_ALL_PREFIXES(AutofillEntryTest, CullByTime);
+
+ // Culls the list of timestamps to 2 - the oldest and most recent. This is a
+ // precursor to getting rid of the timestamps db altogether.
+ // See http://crbug.com/118696.
+ // |source| is expected to be sorted from oldest to newest.
+ static bool CullTimeStamps(const std::vector<base::Time>& source,
+ std::vector<base::Time>* result);
+
+ AutofillKey key_;
+ std::vector<base::Time> timestamps_;
+ bool timestamps_culled_;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_ENTRY_H__
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_entry_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_entry_unittest.cc
new file mode 100644
index 00000000000..bef7d578cbf
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_entry_unittest.cc
@@ -0,0 +1,82 @@
+// 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 <algorithm>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+const unsigned int kMaxAutofillTimeStamps = 2;
+
+TEST(AutofillEntryTest, NoCulling) {
+ std::vector<base::Time> source, result;
+ base::Time current = base::Time::Now();
+ for (size_t i = 0; i < kMaxAutofillTimeStamps; ++i)
+ source.push_back(current);
+
+ EXPECT_FALSE(AutofillEntry::CullTimeStamps(source, &result));
+ EXPECT_EQ(result.size(), kMaxAutofillTimeStamps);
+ for (std::vector<base::Time>::const_iterator it = result.begin();
+ it != result.end(); ++it) {
+ EXPECT_EQ(*it, current);
+ }
+}
+
+TEST(AutofillEntryTest, Culling) {
+ std::vector<base::Time> source, result;
+ base::Time current = base::Time::Now();
+ const int offset = 10000;
+
+ int64 internal_value = current.ToInternalValue();
+ for (size_t i = 0; i < kMaxAutofillTimeStamps * 2 ; ++i) {
+ source.push_back(base::Time::FromInternalValue(
+ internal_value + i * offset));
+ }
+ std::sort(source.begin(), source.end());
+ EXPECT_TRUE(AutofillEntry::CullTimeStamps(source, &result));
+
+ EXPECT_EQ(result.size(), kMaxAutofillTimeStamps);
+ EXPECT_EQ(result.front(), base::Time::FromInternalValue(internal_value));
+ int last_offset = (kMaxAutofillTimeStamps * 2 - 1) * offset;
+ EXPECT_EQ(result.back(),
+ base::Time::FromInternalValue(last_offset + internal_value));
+}
+
+TEST(AutofillEntryTest, CullByTime) {
+ base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
+
+ std::vector<base::Time> timestamps;
+ base::Time cutoff_time = AutofillEntry::ExpirationTime();
+
+ // Within the time limit.
+ timestamps.push_back(cutoff_time + one_hour);
+
+ AutofillKey key(UTF8ToUTF16("test_key"), UTF8ToUTF16("test_value"));
+
+ AutofillEntry entry_within_the_limits(key, timestamps);
+ EXPECT_FALSE(entry_within_the_limits.IsExpired());
+
+ // One within the time limit, one outside.
+ timestamps.push_back(cutoff_time - one_hour);
+
+ AutofillEntry entry_partially_within_the_limits(key, timestamps);
+ EXPECT_TRUE(
+ entry_partially_within_the_limits.IsExpired());
+
+ // All outside the time limit.
+ timestamps.clear();
+ timestamps.push_back(cutoff_time - one_hour);
+ timestamps.push_back(cutoff_time - one_hour * 2);
+ timestamps.push_back(cutoff_time - one_hour * 3);
+
+ AutofillEntry entry_outside_the_limits(key, timestamps);
+ EXPECT_TRUE(entry_outside_the_limits.IsExpired());
+ EXPECT_TRUE(entry_outside_the_limits.timestamps_culled());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
new file mode 100644
index 00000000000..9066d9c0df0
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
@@ -0,0 +1,2193 @@
+// 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/core/browser/webdata/autofill_table.h"
+
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/i18n/case_conversion.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/tuple.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/encryptor/encryptor.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+
+using base::Time;
+
+namespace autofill {
+namespace {
+
+typedef std::vector<Tuple3<int64, base::string16, base::string16> >
+ AutofillElementList;
+
+// TODO(dhollowa): Find a common place for this. It is duplicated in
+// personal_data_manager.cc.
+template<typename T>
+T* address_of(T& v) {
+ return &v;
+}
+
+base::string16 LimitDataSize(const base::string16& data) {
+ if (data.size() > AutofillTable::kMaxDataLength)
+ return data.substr(0, AutofillTable::kMaxDataLength);
+
+ return data;
+}
+
+void BindAutofillProfileToStatement(const AutofillProfile& profile,
+ sql::Statement* s,
+ const std::string& app_locale) {
+ DCHECK(base::IsValidGUID(profile.guid()));
+ s->BindString(0, profile.guid());
+
+ base::string16 text = profile.GetRawInfo(COMPANY_NAME);
+ s->BindString16(1, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_LINE1);
+ s->BindString16(2, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_LINE2);
+ s->BindString16(3, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_CITY);
+ s->BindString16(4, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_STATE);
+ s->BindString16(5, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_ZIP);
+ s->BindString16(6, LimitDataSize(text));
+ text = profile.GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), app_locale);
+ s->BindString16(7, LimitDataSize(text));
+ text = profile.GetRawInfo(ADDRESS_HOME_COUNTRY);
+ s->BindString16(8, LimitDataSize(text));
+ s->BindInt64(9, Time::Now().ToTimeT());
+ s->BindString(10, profile.origin());
+}
+
+AutofillProfile* AutofillProfileFromStatement(const sql::Statement& s,
+ const std::string& app_locale) {
+ AutofillProfile* profile = new AutofillProfile;
+ profile->set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(profile->guid()));
+
+ profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(1));
+ profile->SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(2));
+ profile->SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(3));
+ profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(4));
+ profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(5));
+ profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(6));
+ // Intentionally skip column 7, which stores the localized country name.
+ profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(8));
+ // Intentionally skip column 9, which stores the profile's modification date.
+ profile->set_origin(s.ColumnString(10));
+
+ return profile;
+}
+
+void BindCreditCardToStatement(const CreditCard& credit_card,
+ sql::Statement* s) {
+ DCHECK(base::IsValidGUID(credit_card.guid()));
+ s->BindString(0, credit_card.guid());
+
+ base::string16 text = credit_card.GetRawInfo(CREDIT_CARD_NAME);
+ s->BindString16(1, LimitDataSize(text));
+ text = credit_card.GetRawInfo(CREDIT_CARD_EXP_MONTH);
+ s->BindString16(2, LimitDataSize(text));
+ text = credit_card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ s->BindString16(3, LimitDataSize(text));
+ text = credit_card.GetRawInfo(CREDIT_CARD_NUMBER);
+ std::string encrypted_data;
+ Encryptor::EncryptString16(text, &encrypted_data);
+ s->BindBlob(4, encrypted_data.data(),
+ static_cast<int>(encrypted_data.length()));
+ s->BindInt64(5, Time::Now().ToTimeT());
+ s->BindString(6, credit_card.origin());
+}
+
+CreditCard* CreditCardFromStatement(const sql::Statement& s) {
+ CreditCard* credit_card = new CreditCard;
+
+ credit_card->set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(credit_card->guid()));
+
+ credit_card->SetRawInfo(CREDIT_CARD_NAME, s.ColumnString16(1));
+ credit_card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(2));
+ credit_card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(3));
+ int encrypted_number_len = s.ColumnByteLength(4);
+ base::string16 credit_card_number;
+ if (encrypted_number_len) {
+ std::string encrypted_number;
+ encrypted_number.resize(encrypted_number_len);
+ memcpy(&encrypted_number[0], s.ColumnBlob(4), encrypted_number_len);
+ Encryptor::DecryptString16(encrypted_number, &credit_card_number);
+ }
+ credit_card->SetRawInfo(CREDIT_CARD_NUMBER, credit_card_number);
+ // Intentionally skip column 5, which stores the modification date.
+ credit_card->set_origin(s.ColumnString(6));
+
+ return credit_card;
+}
+
+bool AddAutofillProfileNamesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name "
+ "FROM autofill_profile_names "
+ "WHERE guid=?"));
+ s.BindString(0, profile->guid());
+
+ if (!s.is_valid())
+ return false;
+
+ std::vector<base::string16> first_names;
+ std::vector<base::string16> middle_names;
+ std::vector<base::string16> last_names;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ first_names.push_back(s.ColumnString16(1));
+ middle_names.push_back(s.ColumnString16(2));
+ last_names.push_back(s.ColumnString16(3));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ profile->SetRawMultiInfo(NAME_FIRST, first_names);
+ profile->SetRawMultiInfo(NAME_MIDDLE, middle_names);
+ profile->SetRawMultiInfo(NAME_LAST, last_names);
+ return true;
+}
+
+bool AddAutofillProfileEmailsToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, email "
+ "FROM autofill_profile_emails "
+ "WHERE guid=?"));
+ s.BindString(0, profile->guid());
+
+ if (!s.is_valid())
+ return false;
+
+ std::vector<base::string16> emails;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ emails.push_back(s.ColumnString16(1));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ profile->SetRawMultiInfo(EMAIL_ADDRESS, emails);
+ return true;
+}
+
+bool AddAutofillProfilePhonesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, type, number "
+ "FROM autofill_profile_phones "
+ "WHERE guid=? AND type=?"));
+
+ // Value used to be either [(0, phone), (1, fax)] but fax has been removed.
+ s.BindString(0, profile->guid());
+ s.BindInt(1, 0);
+
+ if (!s.is_valid())
+ return false;
+
+ std::vector<base::string16> numbers;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ numbers.push_back(s.ColumnString16(2));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ profile->SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, numbers);
+ return true;
+}
+
+bool AddAutofillProfileNames(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<base::string16> first_names;
+ profile.GetRawMultiInfo(NAME_FIRST, &first_names);
+ std::vector<base::string16> middle_names;
+ profile.GetRawMultiInfo(NAME_MIDDLE, &middle_names);
+ std::vector<base::string16> last_names;
+ profile.GetRawMultiInfo(NAME_LAST, &last_names);
+ DCHECK_EQ(first_names.size(), middle_names.size());
+ DCHECK_EQ(middle_names.size(), last_names.size());
+
+ for (size_t i = 0; i < first_names.size(); ++i) {
+ // Add the new name.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_names"
+ " (guid, first_name, middle_name, last_name) "
+ "VALUES (?,?,?,?)"));
+ s.BindString(0, profile.guid());
+ s.BindString16(1, first_names[i]);
+ s.BindString16(2, middle_names[i]);
+ s.BindString16(3, last_names[i]);
+
+ if (!s.Run())
+ return false;
+ }
+ return true;
+}
+
+bool AddAutofillProfileEmails(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<base::string16> emails;
+ profile.GetRawMultiInfo(EMAIL_ADDRESS, &emails);
+
+ for (size_t i = 0; i < emails.size(); ++i) {
+ // Add the new email.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_emails"
+ " (guid, email) "
+ "VALUES (?,?)"));
+ s.BindString(0, profile.guid());
+ s.BindString16(1, emails[i]);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AddAutofillProfilePhones(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<base::string16> numbers;
+ profile.GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &numbers);
+
+ for (size_t i = 0; i < numbers.size(); ++i) {
+ // Add the new number.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_phones"
+ " (guid, type, number) "
+ "VALUES (?,?,?)"));
+ s.BindString(0, profile.guid());
+ // Value used to be either [(0, phone), (1, fax)] but fax has been removed.
+ s.BindInt(1, 0);
+ s.BindString16(2, numbers[i]);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AddAutofillProfilePieces(const AutofillProfile& profile,
+ sql::Connection* db) {
+ if (!AddAutofillProfileNames(profile, db))
+ return false;
+
+ if (!AddAutofillProfileEmails(profile, db))
+ return false;
+
+ if (!AddAutofillProfilePhones(profile, db))
+ return false;
+
+ return true;
+}
+
+bool RemoveAutofillProfilePieces(const std::string& guid, sql::Connection* db) {
+ sql::Statement s1(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_names WHERE guid = ?"));
+ s1.BindString(0, guid);
+
+ if (!s1.Run())
+ return false;
+
+ sql::Statement s2(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_emails WHERE guid = ?"));
+ s2.BindString(0, guid);
+
+ if (!s2.Run())
+ return false;
+
+ sql::Statement s3(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_phones WHERE guid = ?"));
+ s3.BindString(0, guid);
+
+ return s3.Run();
+}
+
+WebDatabaseTable::TypeKey GetKey() {
+ // We just need a unique constant. Use the address of a static that
+ // COMDAT folding won't touch in an optimizing linker.
+ static int table_key = 0;
+ return reinterpret_cast<void*>(&table_key);
+}
+
+time_t GetEndTime(const base::Time& end) {
+ if (end.is_null() || end == base::Time::Max())
+ return std::numeric_limits<time_t>::max();
+
+ return end.ToTimeT();
+}
+
+} // namespace
+
+// The maximum length allowed for form data.
+const size_t AutofillTable::kMaxDataLength = 1024;
+
+AutofillTable::AutofillTable(const std::string& app_locale)
+ : app_locale_(app_locale) {
+}
+
+AutofillTable::~AutofillTable() {
+}
+
+AutofillTable* AutofillTable::FromWebDatabase(WebDatabase* db) {
+ return static_cast<AutofillTable*>(db->GetTable(GetKey()));
+}
+
+WebDatabaseTable::TypeKey AutofillTable::GetTypeKey() const {
+ return GetKey();
+}
+
+bool AutofillTable::Init(sql::Connection* db, sql::MetaTable* meta_table) {
+ WebDatabaseTable::Init(db, meta_table);
+ return (InitMainTable() && InitCreditCardsTable() && InitDatesTable() &&
+ InitProfilesTable() && InitProfileNamesTable() &&
+ InitProfileEmailsTable() && InitProfilePhonesTable() &&
+ InitProfileTrashTable());
+}
+
+bool AutofillTable::IsSyncable() {
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion(int version,
+ bool* update_compatible_version) {
+ // Migrate if necessary.
+ switch (version) {
+ case 22:
+ return ClearAutofillEmptyValueElements();
+ case 23:
+ return MigrateToVersion23AddCardNumberEncryptedColumn();
+ case 24:
+ return MigrateToVersion24CleanupOversizedStringFields();
+ case 27:
+ *update_compatible_version = true;
+ return MigrateToVersion27UpdateLegacyCreditCards();
+ case 30:
+ *update_compatible_version = true;
+ return MigrateToVersion30AddDateModifed();
+ case 31:
+ *update_compatible_version = true;
+ return MigrateToVersion31AddGUIDToCreditCardsAndProfiles();
+ case 32:
+ *update_compatible_version = true;
+ return MigrateToVersion32UpdateProfilesAndCreditCards();
+ case 33:
+ *update_compatible_version = true;
+ return MigrateToVersion33ProfilesBasedOnFirstName();
+ case 34:
+ *update_compatible_version = true;
+ return MigrateToVersion34ProfilesBasedOnCountryCode();
+ case 35:
+ *update_compatible_version = true;
+ return MigrateToVersion35GreatBritainCountryCodes();
+ // Combine migrations 36 and 37. This is due to enhancements to the merge
+ // step when migrating profiles. The original migration from 35 to 36 did
+ // not merge profiles with identical addresses, but the migration from 36 to
+ // 37 does. The step from 35 to 36 should only happen on the Chrome 12 dev
+ // channel. Chrome 12 beta and release users will jump from 35 to 37
+ // directly getting the full benefits of the multi-valued merge as well as
+ // the culling of bad data.
+ case 37:
+ *update_compatible_version = true;
+ return MigrateToVersion37MergeAndCullOlderProfiles();
+ case 51:
+ // Combine migrations 50 and 51. The migration code from version 49 to 50
+ // worked correctly for users with existing 'origin' columns, but failed
+ // to create these columns for new users.
+ return MigrateToVersion51AddOriginColumn();
+ }
+ return true;
+}
+
+bool AutofillTable::AddFormFieldValues(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes) {
+ return AddFormFieldValuesTime(elements, changes, Time::Now());
+}
+
+bool AutofillTable::AddFormFieldValue(const FormFieldData& element,
+ std::vector<AutofillChange>* changes) {
+ return AddFormFieldValueTime(element, changes, Time::Now());
+}
+
+bool AutofillTable::GetFormValuesForElementName(
+ const base::string16& name,
+ const base::string16& prefix,
+ std::vector<base::string16>* values,
+ int limit) {
+ DCHECK(values);
+ sql::Statement s;
+
+ if (prefix.empty()) {
+ s.Assign(db_->GetUniqueStatement(
+ "SELECT value FROM autofill "
+ "WHERE name = ? "
+ "ORDER BY count DESC "
+ "LIMIT ?"));
+ s.BindString16(0, name);
+ s.BindInt(1, limit);
+ } else {
+ base::string16 prefix_lower = base::i18n::ToLower(prefix);
+ base::string16 next_prefix = prefix_lower;
+ next_prefix[next_prefix.length() - 1]++;
+
+ s.Assign(db_->GetUniqueStatement(
+ "SELECT value FROM autofill "
+ "WHERE name = ? AND "
+ "value_lower >= ? AND "
+ "value_lower < ? "
+ "ORDER BY count DESC "
+ "LIMIT ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, prefix_lower);
+ s.BindString16(2, next_prefix);
+ s.BindInt(3, limit);
+ }
+
+ values->clear();
+ while (s.Step())
+ values->push_back(s.ColumnString16(0));
+ return s.Succeeded();
+}
+
+bool AutofillTable::HasFormElements() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT COUNT(*) FROM autofill"));
+ if (!s.Step()) {
+ NOTREACHED();
+ return false;
+ }
+ return s.ColumnInt(0) > 0;
+}
+
+bool AutofillTable::RemoveFormElementsAddedBetween(
+ const Time& delete_begin,
+ const Time& delete_end,
+ std::vector<AutofillChange>* changes) {
+ DCHECK(changes);
+ // Query for the pair_id, name, and value of all form elements that
+ // were used between the given times.
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT DISTINCT a.pair_id, a.name, a.value "
+ "FROM autofill_dates ad JOIN autofill a ON ad.pair_id = a.pair_id "
+ "WHERE ad.date_created >= ? AND ad.date_created < ?"));
+ s.BindInt64(0, delete_begin.ToTimeT());
+ s.BindInt64(1,
+ (delete_end.is_null() || delete_end == base::Time::Max()) ?
+ std::numeric_limits<int64>::max() :
+ delete_end.ToTimeT());
+
+ AutofillElementList elements;
+ while (s.Step()) {
+ elements.push_back(MakeTuple(s.ColumnInt64(0),
+ s.ColumnString16(1),
+ s.ColumnString16(2)));
+ }
+ if (!s.Succeeded())
+ return false;
+
+ for (AutofillElementList::iterator itr = elements.begin();
+ itr != elements.end(); ++itr) {
+ int how_many = 0;
+ if (!RemoveFormElementForTimeRange(itr->a, delete_begin, delete_end,
+ &how_many)) {
+ return false;
+ }
+ // We store at most 2 time stamps. If we remove both of them we should
+ // delete the corresponding data. If we delete only one it could still be
+ // the last timestamp for the data, so check how many timestamps do remain.
+ bool should_remove = (CountTimestampsData(itr->a) == 0);
+ if (should_remove) {
+ if (!RemoveFormElementForID(itr->a))
+ return false;
+ } else {
+ if (!AddToCountOfFormElement(itr->a, -how_many))
+ return false;
+ }
+ AutofillChange::Type change_type =
+ should_remove ? AutofillChange::REMOVE : AutofillChange::UPDATE;
+ changes->push_back(AutofillChange(change_type,
+ AutofillKey(itr->b, itr->c)));
+ }
+
+ return true;
+}
+
+bool AutofillTable::RemoveExpiredFormElements(
+ std::vector<AutofillChange>* changes) {
+ DCHECK(changes);
+
+ base::Time delete_end = AutofillEntry::ExpirationTime();
+ // Query for the pair_id, name, and value of all form elements that
+ // were last used before the |delete_end|.
+ sql::Statement select_for_delete(db_->GetUniqueStatement(
+ "SELECT DISTINCT pair_id, name, value "
+ "FROM autofill WHERE pair_id NOT IN "
+ "(SELECT DISTINCT pair_id "
+ "FROM autofill_dates WHERE date_created >= ?)"));
+ select_for_delete.BindInt64(0, delete_end.ToTimeT());
+ AutofillElementList entries_to_delete;
+ while (select_for_delete.Step()) {
+ entries_to_delete.push_back(MakeTuple(select_for_delete.ColumnInt64(0),
+ select_for_delete.ColumnString16(1),
+ select_for_delete.ColumnString16(2)));
+ }
+
+ if (!select_for_delete.Succeeded())
+ return false;
+
+ sql::Statement delete_data_statement(db_->GetUniqueStatement(
+ "DELETE FROM autofill WHERE pair_id NOT IN ("
+ "SELECT pair_id FROM autofill_dates WHERE date_created >= ?)"));
+ delete_data_statement.BindInt64(0, delete_end.ToTimeT());
+ if (!delete_data_statement.Run())
+ return false;
+
+ sql::Statement delete_times_statement(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates WHERE pair_id NOT IN ("
+ "SELECT pair_id FROM autofill_dates WHERE date_created >= ?)"));
+ delete_times_statement.BindInt64(0, delete_end.ToTimeT());
+ if (!delete_times_statement.Run())
+ return false;
+
+ // Cull remaining entries' timestamps.
+ std::vector<AutofillEntry> entries;
+ if (!GetAllAutofillEntries(&entries))
+ return false;
+ sql::Statement cull_date_entry(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates "
+ "WHERE pair_id == (SELECT pair_id FROM autofill "
+ "WHERE name = ? and value = ?)"
+ "AND date_created != ? AND date_created != ?"));
+ for (size_t i = 0; i < entries.size(); ++i) {
+ cull_date_entry.BindString16(0, entries[i].key().name());
+ cull_date_entry.BindString16(1, entries[i].key().value());
+ cull_date_entry.BindInt64(2,
+ entries[i].timestamps().empty() ? 0 :
+ entries[i].timestamps().front().ToTimeT());
+ cull_date_entry.BindInt64(3,
+ entries[i].timestamps().empty() ? 0 :
+ entries[i].timestamps().back().ToTimeT());
+ if (!cull_date_entry.Run())
+ return false;
+ cull_date_entry.Reset(true);
+ }
+
+ changes->clear();
+ changes->reserve(entries_to_delete.size());
+
+ for (AutofillElementList::iterator it = entries_to_delete.begin();
+ it != entries_to_delete.end(); ++it) {
+ changes->push_back(AutofillChange(
+ AutofillChange::REMOVE, AutofillKey(it->b, it->c)));
+ }
+ return true;
+}
+
+bool AutofillTable::RemoveFormElementForTimeRange(int64 pair_id,
+ const Time& delete_begin,
+ const Time& delete_end,
+ int* how_many) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates WHERE pair_id = ? AND "
+ "date_created >= ? AND date_created < ?"));
+ s.BindInt64(0, pair_id);
+ s.BindInt64(1, delete_begin.is_null() ? 0 : delete_begin.ToTimeT());
+ s.BindInt64(2, delete_end.is_null() ? std::numeric_limits<int64>::max() :
+ delete_end.ToTimeT());
+
+ bool result = s.Run();
+ if (how_many)
+ *how_many = db_->GetLastChangeCount();
+
+ return result;
+}
+
+int AutofillTable::CountTimestampsData(int64 pair_id) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT COUNT(*) FROM autofill_dates WHERE pair_id = ?"));
+ s.BindInt64(0, pair_id);
+ if (!s.Step()) {
+ NOTREACHED();
+ return 0;
+ } else {
+ return s.ColumnInt(0);
+ }
+}
+
+bool AutofillTable::AddToCountOfFormElement(int64 pair_id,
+ int delta) {
+ int count = 0;
+
+ if (!GetCountOfFormElement(pair_id, &count))
+ return false;
+
+ if (count + delta == 0) {
+ // Should remove the element earlier in the code.
+ NOTREACHED();
+ return false;
+ } else {
+ if (!SetCountOfFormElement(pair_id, count + delta))
+ return false;
+ }
+ return true;
+}
+
+bool AutofillTable::GetIDAndCountOfFormElement(
+ const FormFieldData& element,
+ int64* pair_id,
+ int* count) {
+ DCHECK(pair_id);
+ DCHECK(count);
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT pair_id, count FROM autofill "
+ "WHERE name = ? AND value = ?"));
+ s.BindString16(0, element.name);
+ s.BindString16(1, element.value);
+
+ if (!s.is_valid())
+ return false;
+
+ *pair_id = 0;
+ *count = 0;
+
+ if (s.Step()) {
+ *pair_id = s.ColumnInt64(0);
+ *count = s.ColumnInt(1);
+ }
+
+ return true;
+}
+
+bool AutofillTable::GetCountOfFormElement(int64 pair_id, int* count) {
+ DCHECK(count);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT count FROM autofill WHERE pair_id = ?"));
+ s.BindInt64(0, pair_id);
+
+ if (s.Step()) {
+ *count = s.ColumnInt(0);
+ return true;
+ }
+ return false;
+}
+
+bool AutofillTable::SetCountOfFormElement(int64 pair_id, int count) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill SET count = ? WHERE pair_id = ?"));
+ s.BindInt(0, count);
+ s.BindInt64(1, pair_id);
+
+ return s.Run();
+}
+
+bool AutofillTable::InsertFormElement(const FormFieldData& element,
+ int64* pair_id) {
+ DCHECK(pair_id);
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill (name, value, value_lower) VALUES (?,?,?)"));
+ s.BindString16(0, element.name);
+ s.BindString16(1, element.value);
+ s.BindString16(2, base::i18n::ToLower(element.value));
+
+ if (!s.Run())
+ return false;
+
+ *pair_id = db_->GetLastInsertRowId();
+ return true;
+}
+
+bool AutofillTable::InsertPairIDAndDate(int64 pair_id,
+ const Time& date_created) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_dates "
+ "(pair_id, date_created) VALUES (?, ?)"));
+ s.BindInt64(0, pair_id);
+ s.BindInt64(1, date_created.ToTimeT());
+
+ return s.Run();
+}
+
+bool AutofillTable::DeleteLastAccess(int64 pair_id) {
+ // Inner SELECT selects the newest |date_created| for a given |pair_id|.
+ // DELETE deletes only that entry.
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_dates WHERE pair_id = ? and date_created IN "
+ "(SELECT date_created FROM autofill_dates WHERE pair_id = ? "
+ "ORDER BY date_created DESC LIMIT 1)"));
+ s.BindInt64(0, pair_id);
+ s.BindInt64(1, pair_id);
+
+ return s.Run();
+}
+
+bool AutofillTable::AddFormFieldValuesTime(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes,
+ Time time) {
+ // Only add one new entry for each unique element name. Use |seen_names| to
+ // track this. Add up to |kMaximumUniqueNames| unique entries per form.
+ const size_t kMaximumUniqueNames = 256;
+ std::set<base::string16> seen_names;
+ bool result = true;
+ for (std::vector<FormFieldData>::const_iterator itr = elements.begin();
+ itr != elements.end(); ++itr) {
+ if (seen_names.size() >= kMaximumUniqueNames)
+ break;
+ if (seen_names.find(itr->name) != seen_names.end())
+ continue;
+ result = result && AddFormFieldValueTime(*itr, changes, time);
+ seen_names.insert(itr->name);
+ }
+ return result;
+}
+
+bool AutofillTable::ClearAutofillEmptyValueElements() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT pair_id FROM autofill WHERE TRIM(value)= \"\""));
+ if (!s.is_valid())
+ return false;
+
+ std::set<int64> ids;
+ while (s.Step())
+ ids.insert(s.ColumnInt64(0));
+ if (!s.Succeeded())
+ return false;
+
+ bool success = true;
+ for (std::set<int64>::const_iterator iter = ids.begin(); iter != ids.end();
+ ++iter) {
+ if (!RemoveFormElementForID(*iter))
+ success = false;
+ }
+
+ return success;
+}
+
+bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) {
+ DCHECK(entries);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT name, value, date_created FROM autofill a JOIN "
+ "autofill_dates ad ON a.pair_id=ad.pair_id"));
+
+ bool first_entry = true;
+ AutofillKey* current_key_ptr = NULL;
+ std::vector<Time>* timestamps_ptr = NULL;
+ base::string16 name, value;
+ Time time;
+ while (s.Step()) {
+ name = s.ColumnString16(0);
+ value = s.ColumnString16(1);
+ time = Time::FromTimeT(s.ColumnInt64(2));
+
+ if (first_entry) {
+ current_key_ptr = new AutofillKey(name, value);
+
+ timestamps_ptr = new std::vector<Time>;
+ timestamps_ptr->push_back(time);
+
+ first_entry = false;
+ } else {
+ // we've encountered the next entry
+ if (current_key_ptr->name().compare(name) != 0 ||
+ current_key_ptr->value().compare(value) != 0) {
+ AutofillEntry entry(*current_key_ptr, *timestamps_ptr);
+ entries->push_back(entry);
+
+ delete current_key_ptr;
+ delete timestamps_ptr;
+
+ current_key_ptr = new AutofillKey(name, value);
+ timestamps_ptr = new std::vector<Time>;
+ }
+ timestamps_ptr->push_back(time);
+ }
+ }
+
+ // If there is at least one result returned, first_entry will be false.
+ // For this case we need to do a final cleanup step.
+ if (!first_entry) {
+ AutofillEntry entry(*current_key_ptr, *timestamps_ptr);
+ entries->push_back(entry);
+ delete current_key_ptr;
+ delete timestamps_ptr;
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::GetAutofillTimestamps(const base::string16& name,
+ const base::string16& value,
+ std::vector<Time>* timestamps) {
+ DCHECK(timestamps);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT date_created FROM autofill a JOIN "
+ "autofill_dates ad ON a.pair_id=ad.pair_id "
+ "WHERE a.name = ? AND a.value = ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, value);
+
+ while (s.Step())
+ timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0)));
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::UpdateAutofillEntries(
+ const std::vector<AutofillEntry>& entries) {
+ if (!entries.size())
+ return true;
+
+ // Remove all existing entries.
+ for (size_t i = 0; i < entries.size(); i++) {
+ std::string sql = "SELECT pair_id FROM autofill "
+ "WHERE name = ? AND value = ?";
+ sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
+ s.BindString16(0, entries[i].key().name());
+ s.BindString16(1, entries[i].key().value());
+
+ if (!s.is_valid())
+ return false;
+
+ if (s.Step()) {
+ if (!RemoveFormElementForID(s.ColumnInt64(0)))
+ return false;
+ }
+ }
+
+ // Insert all the supplied autofill entries.
+ for (size_t i = 0; i < entries.size(); i++) {
+ if (!InsertAutofillEntry(entries[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) {
+ std::string sql = "INSERT INTO autofill (name, value, value_lower, count) "
+ "VALUES (?, ?, ?, ?)";
+ sql::Statement s(db_->GetUniqueStatement(sql.c_str()));
+ s.BindString16(0, entry.key().name());
+ s.BindString16(1, entry.key().value());
+ s.BindString16(2, base::i18n::ToLower(entry.key().value()));
+ s.BindInt(3, entry.timestamps().size());
+
+ if (!s.Run())
+ return false;
+
+ int64 pair_id = db_->GetLastInsertRowId();
+ for (size_t i = 0; i < entry.timestamps().size(); i++) {
+ if (!InsertPairIDAndDate(pair_id, entry.timestamps()[i]))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element,
+ std::vector<AutofillChange>* changes,
+ Time time) {
+ int count = 0;
+ int64 pair_id;
+
+ if (!GetIDAndCountOfFormElement(element, &pair_id, &count))
+ return false;
+
+ if (count == 0 && !InsertFormElement(element, &pair_id))
+ return false;
+
+ if (!SetCountOfFormElement(pair_id, count + 1))
+ return false;
+
+ // If we already have more than 2 times delete last one, before adding new
+ // one.
+ if (count >= 2 && !DeleteLastAccess(pair_id))
+ return false;
+
+ if (!InsertPairIDAndDate(pair_id, time))
+ return false;
+
+ AutofillChange::Type change_type =
+ count == 0 ? AutofillChange::ADD : AutofillChange::UPDATE;
+ changes->push_back(
+ AutofillChange(change_type,
+ AutofillKey(element.name, element.value)));
+ return true;
+}
+
+
+bool AutofillTable::RemoveFormElement(const base::string16& name,
+ const base::string16& value) {
+ // Find the id for that pair.
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT pair_id FROM autofill WHERE name = ? AND value= ?"));
+ s.BindString16(0, name);
+ s.BindString16(1, value);
+
+ if (s.Step())
+ return RemoveFormElementForID(s.ColumnInt64(0));
+ return false;
+}
+
+bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
+ if (IsAutofillGUIDInTrash(profile.guid()))
+ return true;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles"
+ "(guid, company_name, address_line_1, address_line_2, city, state,"
+ " zipcode, country, country_code, date_modified, origin)"
+ "VALUES (?,?,?,?,?,?,?,?,?,?,?)"));
+ BindAutofillProfileToStatement(profile, &s, app_locale_);
+
+ if (!s.Run())
+ return false;
+
+ return AddAutofillProfilePieces(profile, db_);
+}
+
+bool AutofillTable::GetAutofillProfile(const std::string& guid,
+ AutofillProfile** profile) {
+ DCHECK(base::IsValidGUID(guid));
+ DCHECK(profile);
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, company_name, address_line_1, address_line_2, city, state,"
+ " zipcode, country, country_code, date_modified, origin "
+ "FROM autofill_profiles "
+ "WHERE guid=?"));
+ s.BindString(0, guid);
+
+ if (!s.Step())
+ return false;
+
+ scoped_ptr<AutofillProfile> p(AutofillProfileFromStatement(s, app_locale_));
+
+ // Get associated name info.
+ AddAutofillProfileNamesToProfile(db_, p.get());
+
+ // Get associated email info.
+ AddAutofillProfileEmailsToProfile(db_, p.get());
+
+ // Get associated phone info.
+ AddAutofillProfilePhonesToProfile(db_, p.get());
+
+ *profile = p.release();
+ return true;
+}
+
+bool AutofillTable::GetAutofillProfiles(
+ std::vector<AutofillProfile*>* profiles) {
+ DCHECK(profiles);
+ profiles->clear();
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ AutofillProfile* profile = NULL;
+ if (!GetAutofillProfile(guid, &profile))
+ return false;
+ profiles->push_back(profile);
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) {
+ DCHECK(base::IsValidGUID(profile.guid()));
+
+ // Don't update anything until the trash has been emptied. There may be
+ // pending modifications to process.
+ if (!IsAutofillProfilesTrashEmpty())
+ return true;
+
+ AutofillProfile* tmp_profile = NULL;
+ if (!GetAutofillProfile(profile.guid(), &tmp_profile))
+ return false;
+
+ // Preserve appropriate modification dates by not updating unchanged profiles.
+ scoped_ptr<AutofillProfile> old_profile(tmp_profile);
+ if (old_profile->Compare(profile) == 0 &&
+ old_profile->origin() == profile.origin())
+ return true;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles "
+ "SET guid=?, company_name=?, address_line_1=?, address_line_2=?, "
+ " city=?, state=?, zipcode=?, country=?, country_code=?, "
+ " date_modified=?, origin=? "
+ "WHERE guid=?"));
+ BindAutofillProfileToStatement(profile, &s, app_locale_);
+ s.BindString(11, profile.guid());
+
+ bool result = s.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ if (!result)
+ return result;
+
+ // Remove the old names, emails, and phone numbers.
+ if (!RemoveAutofillProfilePieces(profile.guid(), db_))
+ return false;
+
+ return AddAutofillProfilePieces(profile, db_);
+}
+
+bool AutofillTable::RemoveAutofillProfile(const std::string& guid) {
+ DCHECK(base::IsValidGUID(guid));
+
+ if (IsAutofillGUIDInTrash(guid)) {
+ sql::Statement s_trash(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles_trash WHERE guid = ?"));
+ s_trash.BindString(0, guid);
+
+ bool success = s_trash.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0) << "Expected item in trash";
+ return success;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ if (!s.Run())
+ return false;
+
+ return RemoveAutofillProfilePieces(guid, db_);
+}
+
+bool AutofillTable::ClearAutofillProfiles() {
+ sql::Statement s1(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles"));
+
+ if (!s1.Run())
+ return false;
+
+ sql::Statement s2(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profile_names"));
+
+ if (!s2.Run())
+ return false;
+
+ sql::Statement s3(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profile_emails"));
+
+ if (!s3.Run())
+ return false;
+
+ sql::Statement s4(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profile_phones"));
+
+ return s4.Run();
+}
+
+bool AutofillTable::AddCreditCard(const CreditCard& credit_card) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO credit_cards"
+ "(guid, name_on_card, expiration_month, expiration_year, "
+ " card_number_encrypted, date_modified, origin)"
+ "VALUES (?,?,?,?,?,?,?)"));
+ BindCreditCardToStatement(credit_card, &s);
+
+ if (!s.Run())
+ return false;
+
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ return true;
+}
+
+bool AutofillTable::GetCreditCard(const std::string& guid,
+ CreditCard** credit_card) {
+ DCHECK(base::IsValidGUID(guid));
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ " card_number_encrypted, date_modified, origin "
+ "FROM credit_cards "
+ "WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ if (!s.Step())
+ return false;
+
+ *credit_card = CreditCardFromStatement(s);
+ return true;
+}
+
+bool AutofillTable::GetCreditCards(
+ std::vector<CreditCard*>* credit_cards) {
+ DCHECK(credit_cards);
+ credit_cards->clear();
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM credit_cards"));
+
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ CreditCard* credit_card = NULL;
+ if (!GetCreditCard(guid, &credit_card))
+ return false;
+ credit_cards->push_back(credit_card);
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) {
+ DCHECK(base::IsValidGUID(credit_card.guid()));
+
+ CreditCard* tmp_credit_card = NULL;
+ if (!GetCreditCard(credit_card.guid(), &tmp_credit_card))
+ return false;
+
+ // Preserve appropriate modification dates by not updating unchanged cards.
+ scoped_ptr<CreditCard> old_credit_card(tmp_credit_card);
+ if (*old_credit_card == credit_card)
+ return true;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE credit_cards "
+ "SET guid=?, name_on_card=?, expiration_month=?, "
+ " expiration_year=?, card_number_encrypted=?, date_modified=?, "
+ " origin=? "
+ "WHERE guid=?"));
+ BindCreditCardToStatement(credit_card, &s);
+ s.BindString(7, credit_card.guid());
+
+ bool result = s.Run();
+ DCHECK_GT(db_->GetLastChangeCount(), 0);
+ return result;
+}
+
+bool AutofillTable::RemoveCreditCard(const std::string& guid) {
+ DCHECK(base::IsValidGUID(guid));
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM credit_cards WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ return s.Run();
+}
+
+bool AutofillTable::RemoveAutofillDataModifiedBetween(
+ const Time& delete_begin,
+ const Time& delete_end,
+ std::vector<std::string>* profile_guids,
+ std::vector<std::string>* credit_card_guids) {
+ DCHECK(delete_end.is_null() || delete_begin < delete_end);
+
+ time_t delete_begin_t = delete_begin.ToTimeT();
+ time_t delete_end_t = GetEndTime(delete_end);
+
+ // Remember Autofill profiles in the time range.
+ sql::Statement s_profiles_get(db_->GetUniqueStatement(
+ "SELECT guid FROM autofill_profiles "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_profiles_get.BindInt64(0, delete_begin_t);
+ s_profiles_get.BindInt64(1, delete_end_t);
+
+ profile_guids->clear();
+ while (s_profiles_get.Step()) {
+ std::string guid = s_profiles_get.ColumnString(0);
+ profile_guids->push_back(guid);
+ }
+ if (!s_profiles_get.Succeeded())
+ return false;
+
+ // Remove Autofill profiles in the time range.
+ sql::Statement s_profiles(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_profiles.BindInt64(0, delete_begin_t);
+ s_profiles.BindInt64(1, delete_end_t);
+
+ if (!s_profiles.Run())
+ return false;
+
+ // Remember Autofill credit cards in the time range.
+ sql::Statement s_credit_cards_get(db_->GetUniqueStatement(
+ "SELECT guid FROM credit_cards "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_credit_cards_get.BindInt64(0, delete_begin_t);
+ s_credit_cards_get.BindInt64(1, delete_end_t);
+
+ credit_card_guids->clear();
+ while (s_credit_cards_get.Step()) {
+ std::string guid = s_credit_cards_get.ColumnString(0);
+ credit_card_guids->push_back(guid);
+ }
+ if (!s_credit_cards_get.Succeeded())
+ return false;
+
+ // Remove Autofill credit cards in the time range.
+ sql::Statement s_credit_cards(db_->GetUniqueStatement(
+ "DELETE FROM credit_cards "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_credit_cards.BindInt64(0, delete_begin_t);
+ s_credit_cards.BindInt64(1, delete_end_t);
+
+ return s_credit_cards.Run();
+}
+
+bool AutofillTable::RemoveOriginURLsModifiedBetween(
+ const Time& delete_begin,
+ const Time& delete_end,
+ ScopedVector<AutofillProfile>* profiles) {
+ DCHECK(delete_end.is_null() || delete_begin < delete_end);
+
+ time_t delete_begin_t = delete_begin.ToTimeT();
+ time_t delete_end_t = GetEndTime(delete_end);
+
+ // Remember Autofill profiles with URL origins in the time range.
+ sql::Statement s_profiles_get(db_->GetUniqueStatement(
+ "SELECT guid, origin FROM autofill_profiles "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_profiles_get.BindInt64(0, delete_begin_t);
+ s_profiles_get.BindInt64(1, delete_end_t);
+
+ std::vector<std::string> profile_guids;
+ while (s_profiles_get.Step()) {
+ std::string guid = s_profiles_get.ColumnString(0);
+ std::string origin = s_profiles_get.ColumnString(1);
+ if (GURL(origin).is_valid())
+ profile_guids.push_back(guid);
+ }
+ if (!s_profiles_get.Succeeded())
+ return false;
+
+ // Clear out the origins for the found Autofill profiles.
+ for (std::vector<std::string>::const_iterator it = profile_guids.begin();
+ it != profile_guids.end(); ++it) {
+ sql::Statement s_profile(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET origin='' WHERE guid=?"));
+ s_profile.BindString(0, *it);
+ if (!s_profile.Run())
+ return false;
+
+ AutofillProfile* profile;
+ if (!GetAutofillProfile(*it, &profile))
+ return false;
+
+ profiles->push_back(profile);
+ }
+
+ // Remember Autofill credit cards with URL origins in the time range.
+ sql::Statement s_credit_cards_get(db_->GetUniqueStatement(
+ "SELECT guid, origin FROM credit_cards "
+ "WHERE date_modified >= ? AND date_modified < ?"));
+ s_credit_cards_get.BindInt64(0, delete_begin_t);
+ s_credit_cards_get.BindInt64(1, delete_end_t);
+
+ std::vector<std::string> credit_card_guids;
+ while (s_credit_cards_get.Step()) {
+ std::string guid = s_credit_cards_get.ColumnString(0);
+ std::string origin = s_credit_cards_get.ColumnString(1);
+ if (GURL(origin).is_valid())
+ credit_card_guids.push_back(guid);
+ }
+ if (!s_credit_cards_get.Succeeded())
+ return false;
+
+ // Clear out the origins for the found credit cards.
+ for (std::vector<std::string>::const_iterator it = credit_card_guids.begin();
+ it != credit_card_guids.end(); ++it) {
+ sql::Statement s_credit_card(db_->GetUniqueStatement(
+ "UPDATE credit_cards SET origin='' WHERE guid=?"));
+ s_credit_card.BindString(0, *it);
+ if (!s_credit_card.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::GetAutofillProfilesInTrash(
+ std::vector<std::string>* guids) {
+ guids->clear();
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles_trash"));
+
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ guids->push_back(guid);
+ }
+
+ return s.Succeeded();
+}
+
+bool AutofillTable::EmptyAutofillProfilesTrash() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill_profiles_trash"));
+
+ return s.Run();
+}
+
+
+bool AutofillTable::RemoveFormElementForID(int64 pair_id) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "DELETE FROM autofill WHERE pair_id = ?"));
+ s.BindInt64(0, pair_id);
+
+ if (s.Run())
+ return RemoveFormElementForTimeRange(pair_id, Time(), Time(), NULL);
+
+ return false;
+}
+
+bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles_trash"
+ " (guid) "
+ "VALUES (?)"));
+ s.BindString(0, guid);
+
+ return s.Run();
+}
+
+bool AutofillTable::IsAutofillProfilesTrashEmpty() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles_trash"));
+
+ return !s.Step();
+}
+
+bool AutofillTable::IsAutofillGUIDInTrash(const std::string& guid) {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid "
+ "FROM autofill_profiles_trash "
+ "WHERE guid = ?"));
+ s.BindString(0, guid);
+
+ return s.Step();
+}
+
+bool AutofillTable::InitMainTable() {
+ if (!db_->DoesTableExist("autofill")) {
+ if (!db_->Execute("CREATE TABLE autofill ("
+ "name VARCHAR, "
+ "value VARCHAR, "
+ "value_lower VARCHAR, "
+ "pair_id INTEGER PRIMARY KEY, "
+ "count INTEGER DEFAULT 1)")) {
+ NOTREACHED();
+ return false;
+ }
+ if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)")) {
+ NOTREACHED();
+ return false;
+ }
+ if (!db_->Execute("CREATE INDEX autofill_name_value_lower ON "
+ "autofill (name, value_lower)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitCreditCardsTable() {
+ if (!db_->DoesTableExist("credit_cards")) {
+ if (!db_->Execute("CREATE TABLE credit_cards ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0, "
+ "origin VARCHAR DEFAULT '')")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AutofillTable::InitDatesTable() {
+ if (!db_->DoesTableExist("autofill_dates")) {
+ if (!db_->Execute("CREATE TABLE autofill_dates ( "
+ "pair_id INTEGER DEFAULT 0, "
+ "date_created INTEGER DEFAULT 0)")) {
+ NOTREACHED();
+ return false;
+ }
+ if (!db_->Execute("CREATE INDEX autofill_dates_pair_id ON "
+ "autofill_dates (pair_id)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfilesTable() {
+ if (!db_->DoesTableExist("autofill_profiles")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "country_code VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0, "
+ "origin VARCHAR DEFAULT '')")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileNamesTable() {
+ if (!db_->DoesTableExist("autofill_profile_names")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_names ( "
+ "guid VARCHAR, "
+ "first_name VARCHAR, "
+ "middle_name VARCHAR, "
+ "last_name VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileEmailsTable() {
+ if (!db_->DoesTableExist("autofill_profile_emails")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_emails ( "
+ "guid VARCHAR, "
+ "email VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfilePhonesTable() {
+ if (!db_->DoesTableExist("autofill_profile_phones")) {
+ if (!db_->Execute("CREATE TABLE autofill_profile_phones ( "
+ "guid VARCHAR, "
+ "type INTEGER DEFAULT 0, "
+ "number VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AutofillTable::InitProfileTrashTable() {
+ if (!db_->DoesTableExist("autofill_profiles_trash")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_trash ( "
+ "guid VARCHAR)")) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+// Add the card_number_encrypted column if credit card table was not
+// created in this build (otherwise the column already exists).
+// WARNING: Do not change the order of the execution of the SQL
+// statements in this case! Profile corruption and data migration
+// issues WILL OCCUR. See http://crbug.com/10913
+//
+// The problem is that if a user has a profile which was created before
+// r37036, when the credit_cards table was added, and then failed to
+// update this profile between the credit card addition and the addition
+// of the "encrypted" columns (44963), the next data migration will put
+// the user's profile in an incoherent state: The user will update from
+// a data profile set to be earlier than 22, and therefore pass through
+// this update case. But because the user did not have a credit_cards
+// table before starting Chrome, it will have just been initialized
+// above, and so already have these columns -- and thus this data
+// update step will have failed.
+//
+// The false assumption in this case is that at this step in the
+// migration, the user has a credit card table, and that this
+// table does not include encrypted columns!
+// Because this case does not roll back the complete set of SQL
+// transactions properly in case of failure (that is, it does not
+// roll back the table initialization done above), the incoherent
+// profile will now see itself as being at version 22 -- but include a
+// fully initialized credit_cards table. Every time Chrome runs, it
+// will try to update the web database and fail at this step, unless
+// we allow for the faulty assumption described above by checking for
+// the existence of the columns only AFTER we've executed the commands
+// to add them.
+bool AutofillTable::MigrateToVersion23AddCardNumberEncryptedColumn() {
+ if (!db_->DoesColumnExist("credit_cards", "card_number_encrypted")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "card_number_encrypted BLOB DEFAULT NULL")) {
+ LOG(WARNING) << "Could not add card_number_encrypted to "
+ "credit_cards table.";
+ return false;
+ }
+ }
+
+ if (!db_->DoesColumnExist("credit_cards", "verification_code_encrypted")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "verification_code_encrypted BLOB DEFAULT NULL")) {
+ LOG(WARNING) << "Could not add verification_code_encrypted to "
+ "credit_cards table.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// One-time cleanup for http://crbug.com/38364 - In the presence of
+// multi-byte UTF-8 characters, that bug could cause Autofill strings
+// to grow larger and more corrupt with each save. The cleanup removes
+// any row with a string field larger than a reasonable size. The string
+// fields examined here are precisely the ones that were subject to
+// corruption by the original bug.
+bool AutofillTable::MigrateToVersion24CleanupOversizedStringFields() {
+ const std::string autofill_is_too_big =
+ "max(length(name), length(value)) > 500";
+
+ const std::string credit_cards_is_too_big =
+ "max(length(label), length(name_on_card), length(type), "
+ " length(expiration_month), length(expiration_year), "
+ " length(billing_address), length(shipping_address) "
+ ") > 500";
+
+ const std::string autofill_profiles_is_too_big =
+ "max(length(label), length(first_name), "
+ " length(middle_name), length(last_name), length(email), "
+ " length(company_name), length(address_line_1), "
+ " length(address_line_2), length(city), length(state), "
+ " length(zipcode), length(country), length(phone)) > 500";
+
+ std::string query = "DELETE FROM autofill_dates WHERE pair_id IN ("
+ "SELECT pair_id FROM autofill WHERE " + autofill_is_too_big + ")";
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+
+ query = "DELETE FROM autofill WHERE " + autofill_is_too_big;
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+
+ // Only delete from legacy credit card tables where specific columns exist.
+ if (db_->DoesColumnExist("credit_cards", "label") &&
+ db_->DoesColumnExist("credit_cards", "name_on_card") &&
+ db_->DoesColumnExist("credit_cards", "type") &&
+ db_->DoesColumnExist("credit_cards", "expiration_month") &&
+ db_->DoesColumnExist("credit_cards", "expiration_year") &&
+ db_->DoesColumnExist("credit_cards", "billing_address") &&
+ db_->DoesColumnExist("credit_cards", "shipping_address") &&
+ db_->DoesColumnExist("autofill_profiles", "label")) {
+ query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big +
+ ") OR label IN (SELECT label FROM autofill_profiles WHERE " +
+ autofill_profiles_is_too_big + ")";
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+ }
+
+ if (db_->DoesColumnExist("autofill_profiles", "label")) {
+ query = "DELETE FROM autofill_profiles WHERE " +
+ autofill_profiles_is_too_big;
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+ }
+
+ return true;
+}
+
+// Change the credit_cards.billing_address column from a string to an
+// int. The stored string is the label of an address, so we have to
+// select the unique ID of this address using the label as a foreign
+// key into the |autofill_profiles| table.
+bool AutofillTable::MigrateToVersion27UpdateLegacyCreditCards() {
+ // Only migrate from legacy credit card tables where specific columns
+ // exist.
+ if (!(db_->DoesColumnExist("credit_cards", "unique_id") &&
+ db_->DoesColumnExist("credit_cards", "billing_address") &&
+ db_->DoesColumnExist("autofill_profiles", "unique_id"))) {
+ return true;
+ }
+
+ std::string stmt =
+ "SELECT credit_cards.unique_id, autofill_profiles.unique_id "
+ "FROM autofill_profiles, credit_cards "
+ "WHERE credit_cards.billing_address = autofill_profiles.label";
+ sql::Statement s(db_->GetUniqueStatement(stmt.c_str()));
+
+ std::map<int, int> cc_billing_map;
+ while (s.Step())
+ cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1);
+ if (!s.Succeeded())
+ return false;
+
+ // Windows already stores the IDs as strings in |billing_address|. Try
+ // to convert those.
+ if (cc_billing_map.empty()) {
+ std::string stmt = "SELECT unique_id,billing_address FROM credit_cards";
+ sql::Statement s(db_->GetUniqueStatement(stmt.c_str()));
+
+ while (s.Step()) {
+ int id = 0;
+ if (base::StringToInt(s.ColumnString(1), &id))
+ cc_billing_map[s.ColumnInt(0)] = id;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "label VARCHAR, "
+ "unique_id INTEGER PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "type VARCHAR, "
+ "card_number VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "verification_code VARCHAR, "
+ "billing_address INTEGER, "
+ "shipping_address VARCHAR, "
+ "card_number_encrypted BLOB, "
+ "verification_code_encrypted BLOB)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT label,unique_id,name_on_card,type,card_number,"
+ "expiration_month,expiration_year,verification_code,0,"
+ "shipping_address,card_number_encrypted,"
+ "verification_code_encrypted FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+
+ for (std::map<int, int>::const_iterator iter = cc_billing_map.begin();
+ iter != cc_billing_map.end(); ++iter) {
+ sql::Statement s(db_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "UPDATE credit_cards SET billing_address=? WHERE unique_id=?"));
+ s.BindInt(0, (*iter).second);
+ s.BindInt(1, (*iter).first);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion30AddDateModifed() {
+ // Add date_modified to autofill_profiles.
+ if (!db_->DoesColumnExist("autofill_profiles", "date_modified")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "date_modified INTEGER NON NULL DEFAULT 0")) {
+ return false;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified=?"));
+ s.BindInt64(0, Time::Now().ToTimeT());
+
+ if (!s.Run())
+ return false;
+ }
+
+ // Add date_modified to credit_cards.
+ if (!db_->DoesColumnExist("credit_cards", "date_modified")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "date_modified INTEGER NON NULL DEFAULT 0")) {
+ return false;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified=?"));
+ s.BindInt64(0, Time::Now().ToTimeT());
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion31AddGUIDToCreditCardsAndProfiles() {
+ // Note that we need to check for the guid column's existence due to the
+ // fact that for a version 22 database the |autofill_profiles| table
+ // gets created fresh with |InitAutofillProfilesTable|.
+ if (!db_->DoesColumnExist("autofill_profiles", "guid")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "guid VARCHAR NOT NULL DEFAULT \"\"")) {
+ return false;
+ }
+
+ // Set all the |guid| fields to valid values.
+
+ sql::Statement s(db_->GetUniqueStatement("SELECT unique_id "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE autofill_profiles "
+ "SET guid=? WHERE unique_id=?"));
+ update_s.BindString(0, base::GenerateGUID());
+ update_s.BindInt(1, s.ColumnInt(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ // Note that we need to check for the guid column's existence due to the
+ // fact that for a version 22 database the |autofill_profiles| table
+ // gets created fresh with |InitAutofillProfilesTable|.
+ if (!db_->DoesColumnExist("credit_cards", "guid")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "guid VARCHAR NOT NULL DEFAULT \"\"")) {
+ return false;
+ }
+
+ // Set all the |guid| fields to valid values.
+
+ sql::Statement s(db_->GetUniqueStatement("SELECT unique_id "
+ "FROM credit_cards"));
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE credit_cards "
+ "set guid=? WHERE unique_id=?"));
+ update_s.BindString(0, base::GenerateGUID());
+ update_s.BindInt(1, s.ColumnInt(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion32UpdateProfilesAndCreditCards() {
+ if (db_->DoesColumnExist("autofill_profiles", "unique_id")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "label VARCHAR, "
+ "first_name VARCHAR, "
+ "middle_name VARCHAR, "
+ "last_name VARCHAR, "
+ "email VARCHAR, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "phone VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO autofill_profiles_temp "
+ "SELECT guid, label, first_name, middle_name, last_name, email, "
+ "company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, date_modified "
+ "FROM autofill_profiles")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE autofill_profiles"))
+ return false;
+
+ if (!db_->Execute(
+ "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
+ return false;
+ }
+ }
+
+ if (db_->DoesColumnExist("credit_cards", "unique_id")) {
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "label VARCHAR, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT guid, label, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+ }
+
+ return true;
+}
+
+// Test the existence of the |first_name| column as an indication that
+// we need a migration. It is possible that the new |autofill_profiles|
+// schema is in place because the table was newly created when migrating
+// from a pre-version-22 database.
+bool AutofillTable::MigrateToVersion33ProfilesBasedOnFirstName() {
+ if (db_->DoesColumnExist("autofill_profiles", "first_name")) {
+ // Create autofill_profiles_temp table that will receive the data.
+ if (!db_->DoesTableExist("autofill_profiles_temp")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name, email, "
+ "company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, date_modified "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ AutofillProfile profile;
+ profile.set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(profile.guid()));
+
+ profile.SetRawInfo(NAME_FIRST, s.ColumnString16(1));
+ profile.SetRawInfo(NAME_MIDDLE, s.ColumnString16(2));
+ profile.SetRawInfo(NAME_LAST, s.ColumnString16(3));
+ profile.SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(4));
+ profile.SetRawInfo(COMPANY_NAME, s.ColumnString16(5));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(6));
+ profile.SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(7));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(8));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(9));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(10));
+ profile.SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), s.ColumnString16(11),
+ app_locale_);
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(12));
+ int64 date_modified = s.ColumnInt64(13);
+
+ sql::Statement s_insert(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles_temp"
+ "(guid, company_name, address_line_1, address_line_2, city,"
+ " state, zipcode, country, date_modified)"
+ "VALUES (?,?,?,?,?,?,?,?,?)"));
+ s_insert.BindString(0, profile.guid());
+ s_insert.BindString16(1, profile.GetRawInfo(COMPANY_NAME));
+ s_insert.BindString16(2, profile.GetRawInfo(ADDRESS_HOME_LINE1));
+ s_insert.BindString16(3, profile.GetRawInfo(ADDRESS_HOME_LINE2));
+ s_insert.BindString16(4, profile.GetRawInfo(ADDRESS_HOME_CITY));
+ s_insert.BindString16(5, profile.GetRawInfo(ADDRESS_HOME_STATE));
+ s_insert.BindString16(6, profile.GetRawInfo(ADDRESS_HOME_ZIP));
+ s_insert.BindString16(7, profile.GetRawInfo(ADDRESS_HOME_COUNTRY));
+ s_insert.BindInt64(8, date_modified);
+
+ if (!s_insert.Run())
+ return false;
+
+ // Add the other bits: names, emails, and phone numbers.
+ if (!AddAutofillProfilePieces(profile, db_))
+ return false;
+ } // endwhile
+ if (!s.Succeeded())
+ return false;
+
+ if (!db_->Execute("DROP TABLE autofill_profiles"))
+ return false;
+
+ if (!db_->Execute(
+ "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
+ return false;
+ }
+ }
+
+ // Remove the labels column from the credit_cards table.
+ if (db_->DoesColumnExist("credit_cards", "label")) {
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT guid, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+ }
+
+ return true;
+}
+
+// Test the existence of the |country_code| column as an indication that
+// we need a migration. It is possible that the new |autofill_profiles|
+// schema is in place because the table was newly created when migrating
+// from a pre-version-22 database.
+bool AutofillTable::MigrateToVersion34ProfilesBasedOnCountryCode() {
+ if (!db_->DoesColumnExist("autofill_profiles", "country_code")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "country_code VARCHAR")) {
+ return false;
+ }
+
+ // Set all the |country_code| fields to match existing |country| values.
+ sql::Statement s(db_->GetUniqueStatement("SELECT guid, country "
+ "FROM autofill_profiles"));
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE autofill_profiles "
+ "SET country_code=? WHERE guid=?"));
+
+ base::string16 country = s.ColumnString16(1);
+ update_s.BindString(0, AutofillCountry::GetCountryCode(country,
+ app_locale_));
+ update_s.BindString(1, s.ColumnString(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ if (!s.Succeeded())
+ return false;
+ }
+
+ return true;
+}
+
+// Correct all country codes with value "UK" to be "GB". This data
+// was mistakenly introduced in build 686.0. This migration is to clean
+// it up. See http://crbug.com/74511 for details.
+bool AutofillTable::MigrateToVersion35GreatBritainCountryCodes() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET country_code=\"GB\" "
+ "WHERE country_code=\"UK\""));
+
+ return s.Run();
+}
+
+// Merge and cull older profiles where possible.
+bool AutofillTable::MigrateToVersion37MergeAndCullOlderProfiles() {
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, date_modified FROM autofill_profiles"));
+
+ // Accumulate the good profiles.
+ std::vector<AutofillProfile> accumulated_profiles;
+ std::vector<AutofillProfile*> accumulated_profiles_p;
+ std::map<std::string, int64> modification_map;
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ int64 date_modified = s.ColumnInt64(1);
+ modification_map.insert(
+ std::pair<std::string, int64>(guid, date_modified));
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, company_name, address_line_1, address_line_2, city, "
+ " state, zipcode, country, country_code, date_modified "
+ "FROM autofill_profiles "
+ "WHERE guid=?"));
+ s.BindString(0, guid);
+
+ if (!s.Step())
+ return false;
+
+ scoped_ptr<AutofillProfile> profile(new AutofillProfile);
+ profile->set_guid(s.ColumnString(0));
+ DCHECK(base::IsValidGUID(profile->guid()));
+
+ profile->SetRawInfo(COMPANY_NAME, s.ColumnString16(1));
+ profile->SetRawInfo(ADDRESS_HOME_LINE1, s.ColumnString16(2));
+ profile->SetRawInfo(ADDRESS_HOME_LINE2, s.ColumnString16(3));
+ profile->SetRawInfo(ADDRESS_HOME_CITY, s.ColumnString16(4));
+ profile->SetRawInfo(ADDRESS_HOME_STATE, s.ColumnString16(5));
+ profile->SetRawInfo(ADDRESS_HOME_ZIP, s.ColumnString16(6));
+ // Intentionally skip column 7, which stores the localized country name.
+ profile->SetRawInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(8));
+ // Intentionally skip column 9, which stores the profile's modification
+ // date.
+ profile->set_origin(s.ColumnString(10));
+
+ // Get associated name info.
+ AddAutofillProfileNamesToProfile(db_, profile.get());
+
+ // Get associated email info.
+ AddAutofillProfileEmailsToProfile(db_, profile.get());
+
+ // Get associated phone info.
+ AddAutofillProfilePhonesToProfile(db_, profile.get());
+
+ if (PersonalDataManager::IsValidLearnableProfile(*profile, app_locale_)) {
+ std::vector<AutofillProfile> merged_profiles;
+ bool merged = PersonalDataManager::MergeProfile(
+ *profile, accumulated_profiles_p, app_locale_, &merged_profiles);
+
+ std::swap(accumulated_profiles, merged_profiles);
+
+ accumulated_profiles_p.clear();
+ accumulated_profiles_p.resize(accumulated_profiles.size());
+ std::transform(accumulated_profiles.begin(),
+ accumulated_profiles.end(),
+ accumulated_profiles_p.begin(),
+ address_of<AutofillProfile>);
+
+ // If the profile got merged trash the original.
+ if (merged)
+ AddAutofillGUIDToTrash(profile->guid());
+
+ } else {
+ // An invalid profile, so trash it.
+ AddAutofillGUIDToTrash(profile->guid());
+ }
+ } // endwhile
+ if (!s.Succeeded())
+ return false;
+
+ // Drop the current profiles.
+ if (!ClearAutofillProfiles())
+ return false;
+
+ // Add the newly merged profiles back in.
+ for (std::vector<AutofillProfile>::const_iterator
+ iter = accumulated_profiles.begin();
+ iter != accumulated_profiles.end();
+ ++iter) {
+ // Save the profile with its original modification date.
+ std::map<std::string, int64>::const_iterator date_item =
+ modification_map.find(iter->guid());
+ if (date_item == modification_map.end())
+ return false;
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles"
+ "(guid, company_name, address_line_1, address_line_2, city, state,"
+ " zipcode, country, country_code, date_modified)"
+ "VALUES (?,?,?,?,?,?,?,?,?,?)"));
+ s.BindString(0, iter->guid());
+ base::string16 text = iter->GetRawInfo(COMPANY_NAME);
+ s.BindString16(1, LimitDataSize(text));
+ text = iter->GetRawInfo(ADDRESS_HOME_LINE1);
+ s.BindString16(2, LimitDataSize(text));
+ text = iter->GetRawInfo(ADDRESS_HOME_LINE2);
+ s.BindString16(3, LimitDataSize(text));
+ text = iter->GetRawInfo(ADDRESS_HOME_CITY);
+ s.BindString16(4, LimitDataSize(text));
+ text = iter->GetRawInfo(ADDRESS_HOME_STATE);
+ s.BindString16(5, LimitDataSize(text));
+ text = iter->GetRawInfo(ADDRESS_HOME_ZIP);
+ s.BindString16(6, LimitDataSize(text));
+ text = iter->GetInfo(AutofillType(ADDRESS_HOME_COUNTRY), app_locale_);
+ s.BindString16(7, LimitDataSize(text));
+ text = iter->GetRawInfo(ADDRESS_HOME_COUNTRY);
+ s.BindString16(8, LimitDataSize(text));
+ s.BindInt64(9, date_item->second);
+
+ if (!s.Run())
+ return false;
+
+ if (!AddAutofillProfilePieces(*iter, db_))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::MigrateToVersion51AddOriginColumn() {
+ sql::Transaction transaction(db_);
+ if (!transaction.Begin())
+ return false;
+
+ // Add origin to autofill_profiles.
+ if (!db_->DoesColumnExist("autofill_profiles", "origin") &&
+ !db_->Execute("ALTER TABLE autofill_profiles "
+ "ADD COLUMN origin VARCHAR DEFAULT ''")) {
+ return false;
+ }
+
+ // Add origin to credit_cards.
+ if (!db_->DoesColumnExist("credit_cards", "origin") &&
+ !db_->Execute("ALTER TABLE credit_cards "
+ "ADD COLUMN origin VARCHAR DEFAULT ''")) {
+ return false;
+ }
+
+ return transaction.Commit();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.h b/chromium/components/autofill/core/browser/webdata/autofill_table.h
new file mode 100644
index 00000000000..52012e24810
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.h
@@ -0,0 +1,388 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string16.h"
+#include "components/webdata/common/web_database_table.h"
+
+class WebDatabase;
+
+namespace base {
+class Time;
+}
+
+namespace autofill {
+
+class AutofillChange;
+class AutofillEntry;
+class AutofillProfile;
+class AutofillTableTest;
+class CreditCard;
+
+struct FormFieldData;
+
+// This class manages the various Autofill tables within the SQLite database
+// passed to the constructor. It expects the following schemas:
+//
+// Note: The database stores time in seconds, UTC.
+//
+// autofill
+// name The name of the input as specified in the html.
+// value The literal contents of the text field.
+// value_lower The contents of the text field made lower_case.
+// pair_id An ID number unique to the row in the table.
+// count How many times the user has entered the string |value|
+// in a field of name |name|.
+//
+// autofill_dates This table associates a row to each separate time the
+// user submits a form containing a certain name/value
+// pair. The |pair_id| should match the |pair_id| field
+// in the appropriate row of the autofill table.
+// pair_id
+// date_created
+//
+// autofill_profiles This table contains Autofill profile data added by the
+// user with the Autofill dialog. Most of the columns are
+// standard entries in a contact information form.
+//
+// guid A guid string to uniquely identify the profile.
+// Added in version 31.
+// company_name
+// address_line_1
+// address_line_2
+// city
+// state
+// zipcode
+// country The country name. Deprecated, should be removed once
+// the stable channel reaches version 11.
+// country_code
+// date_modified The date on which this profile was last modified.
+// Added in version 30.
+// origin The domain of origin for this profile.
+// Added in version 50.
+//
+// autofill_profile_names
+// This table contains the multi-valued name fields
+// associated with a profile.
+//
+// guid The guid string that identifies the profile to which
+// the name belongs.
+// first_name
+// middle_name
+// last_name
+//
+// autofill_profile_emails
+// This table contains the multi-valued email fields
+// associated with a profile.
+//
+// guid The guid string that identifies the profile to which
+// the email belongs.
+// email
+//
+// autofill_profile_phones
+// This table contains the multi-valued phone fields
+// associated with a profile.
+//
+// guid The guid string that identifies the profile to which
+// the phone number belongs.
+// type An integer constant designating either phone type of the
+// number.
+// TODO(jhawkins): Remove the type column and migrate the
+// database.
+// number
+//
+// autofill_profiles_trash
+// This table contains guids of "trashed" autofill
+// profiles. When a profile is removed its guid is added
+// to this table so that Sync can perform deferred removal.
+//
+// guid The guid string that identifies the trashed profile.
+//
+// credit_cards This table contains credit card data added by the user
+// with the Autofill dialog. Most of the columns are
+// standard entries in a credit card form.
+//
+// guid A guid string to uniquely identify the profile.
+// Added in version 31.
+// name_on_card
+// expiration_month
+// expiration_year
+// card_number_encrypted Stores encrypted credit card number.
+// date_modified The date on which this entry was last modified.
+// Added in version 30.
+// origin The domain of origin for this profile.
+// Added in version 50.
+//
+class AutofillTable : public WebDatabaseTable {
+ public:
+ explicit AutofillTable(const std::string& app_locale);
+ virtual ~AutofillTable();
+
+ // Retrieves the AutofillTable* owned by |database|.
+ static AutofillTable* FromWebDatabase(WebDatabase* db);
+
+ virtual WebDatabaseTable::TypeKey GetTypeKey() const OVERRIDE;
+ virtual bool Init(sql::Connection* db, sql::MetaTable* meta_table) OVERRIDE;
+ virtual bool IsSyncable() OVERRIDE;
+ virtual bool MigrateToVersion(int version,
+ bool* update_compatible_version) OVERRIDE;
+
+ // Records the form elements in |elements| in the database in the
+ // autofill table. A list of all added and updated autofill entries
+ // is returned in the changes out parameter.
+ bool AddFormFieldValues(const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes);
+
+ // Records a single form element in the database in the autofill table. A list
+ // of all added and updated autofill entries is returned in the changes out
+ // parameter.
+ bool AddFormFieldValue(const FormFieldData& element,
+ std::vector<AutofillChange>* changes);
+
+ // Retrieves a vector of all values which have been recorded in the autofill
+ // table as the value in a form element with name |name| and which start with
+ // |prefix|. The comparison of the prefix is case insensitive.
+ bool GetFormValuesForElementName(const base::string16& name,
+ const base::string16& prefix,
+ std::vector<base::string16>* values,
+ int limit);
+
+ // Returns whether any form elements are stored in the database.
+ bool HasFormElements();
+
+ // Removes rows from autofill_dates if they were created on or after
+ // |delete_begin| and strictly before |delete_end|. Decrements the
+ // count of the corresponding rows in the autofill table, and
+ // removes those rows if the count goes to 0. A list of all changed
+ // keys and whether each was updater or removed is returned in the
+ // changes out parameter.
+ bool RemoveFormElementsAddedBetween(const base::Time& delete_begin,
+ const base::Time& delete_end,
+ std::vector<AutofillChange>* changes);
+
+ // Removes rows from autofill_dates if they were accessed strictly before
+ // |AutofillEntry::ExpirationTime()|. Removes the corresponding row from the
+ // autofill table. Also culls timestamps to only two. TODO(georgey): remove
+ // culling in future versions.
+ bool RemoveExpiredFormElements(std::vector<AutofillChange>* changes);
+
+ // Removes from autofill_dates rows with given pair_id where date_created lies
+ // between |delete_begin| and |delete_end|.
+ bool RemoveFormElementForTimeRange(int64 pair_id,
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ int* how_many);
+
+ // Increments the count in the row corresponding to |pair_id| by |delta|.
+ bool AddToCountOfFormElement(int64 pair_id, int delta);
+
+ // Counts how many timestamp data rows are in the |autofill_dates| table for
+ // a given |pair_id|. GetCountOfFormElement() on the other hand gives the
+ // |count| property for a given id.
+ int CountTimestampsData(int64 pair_id);
+
+ // Gets the pair_id and count entries from name and value specified in
+ // |element|. Sets *pair_id and *count to 0 if there is no such row in
+ // the table.
+ bool GetIDAndCountOfFormElement(const FormFieldData& element,
+ int64* pair_id,
+ int* count);
+
+ // Gets the count only given the pair_id.
+ bool GetCountOfFormElement(int64 pair_id, int* count);
+
+ // Updates the count entry in the row corresponding to |pair_id| to |count|.
+ bool SetCountOfFormElement(int64 pair_id, int count);
+
+ // Adds a new row to the autofill table with name and value given in
+ // |element|. Sets *pair_id to the pair_id of the new row.
+ bool InsertFormElement(const FormFieldData& element,
+ int64* pair_id);
+
+ // Adds a new row to the autofill_dates table.
+ bool InsertPairIDAndDate(int64 pair_id, const base::Time& date_created);
+
+ // Deletes last access to the Autofill data from the autofill_dates table.
+ bool DeleteLastAccess(int64 pair_id);
+
+ // Removes row from the autofill tables given |pair_id|.
+ bool RemoveFormElementForID(int64 pair_id);
+
+ // Removes row from the autofill tables for the given |name| |value| pair.
+ virtual bool RemoveFormElement(const base::string16& name,
+ const base::string16& value);
+
+ // Retrieves all of the entries in the autofill table.
+ virtual bool GetAllAutofillEntries(std::vector<AutofillEntry>* entries);
+
+ // Retrieves a single entry from the autofill table.
+ virtual bool GetAutofillTimestamps(const base::string16& name,
+ const base::string16& value,
+ std::vector<base::Time>* timestamps);
+
+ // Replaces existing autofill entries with the entries supplied in
+ // the argument. If the entry does not already exist, it will be
+ // added.
+ virtual bool UpdateAutofillEntries(const std::vector<AutofillEntry>& entries);
+
+ // Records a single Autofill profile in the autofill_profiles table.
+ virtual bool AddAutofillProfile(const AutofillProfile& profile);
+
+ // Updates the database values for the specified profile. Mulit-value aware.
+ virtual bool UpdateAutofillProfile(const AutofillProfile& profile);
+
+ // Removes a row from the autofill_profiles table. |guid| is the identifier
+ // of the profile to remove.
+ virtual bool RemoveAutofillProfile(const std::string& guid);
+
+ // Retrieves a profile with guid |guid|. The caller owns |profile|.
+ bool GetAutofillProfile(const std::string& guid, AutofillProfile** profile);
+
+ // Retrieves all profiles in the database. Caller owns the returned profiles.
+ virtual bool GetAutofillProfiles(std::vector<AutofillProfile*>* profiles);
+
+ // Records a single credit card in the credit_cards table.
+ bool AddCreditCard(const CreditCard& credit_card);
+
+ // Updates the database values for the specified credit card.
+ bool UpdateCreditCard(const CreditCard& credit_card);
+
+ // Removes a row from the credit_cards table. |guid| is the identifer of the
+ // credit card to remove.
+ bool RemoveCreditCard(const std::string& guid);
+
+ // Retrieves a credit card with guid |guid|. The caller owns
+ // |credit_card_id|.
+ bool GetCreditCard(const std::string& guid, CreditCard** credit_card);
+
+ // Retrieves all credit cards in the database. Caller owns the returned
+ // credit cards.
+ virtual bool GetCreditCards(std::vector<CreditCard*>* credit_cards);
+
+ // Removes rows from autofill_profiles and credit_cards if they were created
+ // on or after |delete_begin| and strictly before |delete_end|. Returns the
+ // list of deleted profile guids in |profile_guids|. Return value is true if
+ // all rows were successfully removed. Returns false on database error. In
+ // that case, the output vector state is undefined, and may be partially
+ // filled.
+ bool RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ std::vector<std::string>* profile_guids,
+ std::vector<std::string>* credit_card_guids);
+
+ // Removes origin URLs from the autofill_profiles and credit_cards tables if
+ // they were written on or after |delete_begin| and strictly before
+ // |delete_end|. Returns the list of modified profiles in |profiles|. Return
+ // value is true if all rows were successfully updated. Returns false on
+ // database error. In that case, the output vector state is undefined, and
+ // may be partially filled.
+ bool RemoveOriginURLsModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ ScopedVector<AutofillProfile>* profiles);
+
+ // Retrieves all profiles in the database that have been deleted since last
+ // "empty" of the trash.
+ bool GetAutofillProfilesInTrash(std::vector<std::string>* guids);
+
+ // Empties the Autofill profiles "trash can".
+ bool EmptyAutofillProfilesTrash();
+
+ // Removes empty values for autofill that were incorrectly stored in the DB
+ // See bug http://crbug.com/6111
+ bool ClearAutofillEmptyValueElements();
+
+ // Retrieves all profiles in the database that have been deleted since last
+ // "empty" of the trash.
+ bool AddAutofillGUIDToTrash(const std::string& guid);
+
+ // Clear all profiles.
+ bool ClearAutofillProfiles();
+
+ // Table migration functions.
+ bool MigrateToVersion23AddCardNumberEncryptedColumn();
+ bool MigrateToVersion24CleanupOversizedStringFields();
+ bool MigrateToVersion27UpdateLegacyCreditCards();
+ bool MigrateToVersion30AddDateModifed();
+ bool MigrateToVersion31AddGUIDToCreditCardsAndProfiles();
+ bool MigrateToVersion32UpdateProfilesAndCreditCards();
+ bool MigrateToVersion33ProfilesBasedOnFirstName();
+ bool MigrateToVersion34ProfilesBasedOnCountryCode();
+ bool MigrateToVersion35GreatBritainCountryCodes();
+ bool MigrateToVersion37MergeAndCullOlderProfiles();
+ bool MigrateToVersion51AddOriginColumn();
+
+ // Max data length saved in the table;
+ static const size_t kMaxDataLength;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddChanges);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_RemoveBetweenChanges);
+
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_UpdateDontReplace);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddFormFieldValues);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfile);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateAutofillProfile);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfileTrash);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfileTrashInteraction);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ RemoveAutofillDataModifiedBetween);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, CreditCard);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateCreditCard);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetAllAutofillEntries_OneResult);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetAllAutofillEntries_TwoDistinct);
+ FRIEND_TEST_ALL_PREFIXES(AutofillTableTest,
+ Autofill_GetAllAutofillEntries_TwoSame);
+
+ // Methods for adding autofill entries at a specified time. For
+ // testing only.
+ bool AddFormFieldValuesTime(
+ const std::vector<FormFieldData>& elements,
+ std::vector<AutofillChange>* changes,
+ base::Time time);
+ bool AddFormFieldValueTime(const FormFieldData& element,
+ std::vector<AutofillChange>* changes,
+ base::Time time);
+
+ // Insert a single AutofillEntry into the autofill/autofill_dates tables.
+ bool InsertAutofillEntry(const AutofillEntry& entry);
+
+ // Checks if the trash is empty.
+ bool IsAutofillProfilesTrashEmpty();
+
+ // Checks if the guid is in the trash.
+ bool IsAutofillGUIDInTrash(const std::string& guid);
+
+ bool InitMainTable();
+ bool InitCreditCardsTable();
+ bool InitDatesTable();
+ bool InitProfilesTable();
+ bool InitProfileNamesTable();
+ bool InitProfileEmailsTable();
+ bool InitProfilePhonesTable();
+ bool InitProfileTrashTable();
+
+ // The application locale. The locale is needed for the migration to version
+ // 35. Since it must be read on the UI thread, it is set when the table is
+ // created (on the UI thread), and cached here so that it can be used for
+ // migrations (on the DB thread).
+ std::string app_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillTable);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_TABLE_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
new file mode 100644
index 00000000000..eff54f856f1
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table_unittest.cc
@@ -0,0 +1,1495 @@
+// 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 <vector>
+
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/encryptor/encryptor.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace autofill {
+
+// So we can compare AutofillKeys with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillKey& key) {
+ return os << UTF16ToASCII(key.name()) << ", " << UTF16ToASCII(key.value());
+}
+
+// So we can compare AutofillChanges with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const AutofillChange& change) {
+ switch (change.type()) {
+ case AutofillChange::ADD: {
+ os << "ADD";
+ break;
+ }
+ case AutofillChange::UPDATE: {
+ os << "UPDATE";
+ break;
+ }
+ case AutofillChange::REMOVE: {
+ os << "REMOVE";
+ break;
+ }
+ }
+ return os << " " << change.key();
+}
+
+namespace {
+
+bool CompareAutofillEntries(const AutofillEntry& a, const AutofillEntry& b) {
+ std::set<Time> timestamps1(a.timestamps().begin(), a.timestamps().end());
+ std::set<Time> timestamps2(b.timestamps().begin(), b.timestamps().end());
+
+ int compVal = a.key().name().compare(b.key().name());
+ if (compVal != 0) {
+ return compVal < 0;
+ }
+
+ compVal = a.key().value().compare(b.key().value());
+ if (compVal != 0) {
+ return compVal < 0;
+ }
+
+ if (timestamps1.size() != timestamps2.size()) {
+ return timestamps1.size() < timestamps2.size();
+ }
+
+ std::set<Time>::iterator it;
+ for (it = timestamps1.begin(); it != timestamps1.end(); it++) {
+ timestamps2.erase(*it);
+ }
+
+ return !timestamps2.empty();
+}
+
+} // anonymous namespace
+
+class AutofillTableTest : public testing::Test {
+ public:
+ AutofillTableTest() {}
+ virtual ~AutofillTableTest() {}
+
+ protected:
+ typedef std::set<AutofillEntry,
+ bool (*)(const AutofillEntry&, const AutofillEntry&)> AutofillEntrySet;
+ typedef std::set<AutofillEntry, bool (*)(const AutofillEntry&,
+ const AutofillEntry&)>::iterator AutofillEntrySetIterator;
+
+ virtual void SetUp() {
+#if defined(OS_MACOSX)
+ Encryptor::UseMockKeychain(true);
+#endif
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ file_ = temp_dir_.path().AppendASCII("TestWebDatabase");
+
+ table_.reset(new AutofillTable("en-US"));
+ db_.reset(new WebDatabase);
+ db_->AddTable(table_.get());
+ ASSERT_EQ(sql::INIT_OK, db_->Init(file_));
+ }
+
+ static AutofillEntry MakeAutofillEntry(const char* name,
+ const char* value,
+ time_t timestamp0,
+ time_t timestamp1) {
+ std::vector<Time> timestamps;
+ if (timestamp0 >= 0)
+ timestamps.push_back(Time::FromTimeT(timestamp0));
+ if (timestamp1 >= 0)
+ timestamps.push_back(Time::FromTimeT(timestamp1));
+ return AutofillEntry(
+ AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)), timestamps);
+ }
+
+ base::FilePath file_;
+ base::ScopedTempDir temp_dir_;
+ scoped_ptr<AutofillTable> table_;
+ scoped_ptr<WebDatabase> db_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AutofillTableTest);
+};
+
+TEST_F(AutofillTableTest, Autofill) {
+ Time t1 = Time::Now();
+
+ // Simulate the submission of a handful of entries in a field called "Name",
+ // some more often than others.
+ AutofillChangeList changes;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ base::Time now = base::Time::Now();
+ base::TimeDelta two_seconds = base::TimeDelta::FromSeconds(2);
+ EXPECT_FALSE(table_->HasFormElements());
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ EXPECT_TRUE(table_->HasFormElements());
+ std::vector<base::string16> v;
+ for (int i = 0; i < 5; i++) {
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ now + i * two_seconds));
+ }
+ for (int i = 0; i < 3; i++) {
+ field.value = ASCIIToUTF16("Clark Sutter");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ now + i * two_seconds));
+ }
+ for (int i = 0; i < 2; i++) {
+ field.name = ASCIIToUTF16("Favorite Color");
+ field.value = ASCIIToUTF16("Green");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ now + i * two_seconds));
+ }
+
+ int count = 0;
+ int64 pair_id = 0;
+
+ // We have added the name Clark Kent 5 times, so count should be 5 and pair_id
+ // should be somthing non-zero.
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(5, count);
+ EXPECT_NE(0, pair_id);
+
+ // Storing in the data base should be case sensitive, so there should be no
+ // database entry for clark kent lowercase.
+ field.value = ASCIIToUTF16("clark kent");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(0, count);
+
+ field.name = ASCIIToUTF16("Favorite Color");
+ field.value = ASCIIToUTF16("Green");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(2, count);
+
+ // This is meant to get a list of suggestions for Name. The empty prefix
+ // in the second argument means it should return all suggestions for a name
+ // no matter what they start with. The order that the names occur in the list
+ // should be decreasing order by count.
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), base::string16(), &v, 6));
+ EXPECT_EQ(3U, v.size());
+ if (v.size() == 3) {
+ EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]);
+ EXPECT_EQ(ASCIIToUTF16("Clark Sutter"), v[1]);
+ EXPECT_EQ(ASCIIToUTF16("Superman"), v[2]);
+ }
+
+ // If we query again limiting the list size to 1, we should only get the most
+ // frequent entry.
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), base::string16(), &v, 1));
+ EXPECT_EQ(1U, v.size());
+ if (v.size() == 1) {
+ EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]);
+ }
+
+ // Querying for suggestions given a prefix is case-insensitive, so the prefix
+ // "cLa" shoud get suggestions for both Clarks.
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), ASCIIToUTF16("cLa"), &v, 6));
+ EXPECT_EQ(2U, v.size());
+ if (v.size() == 2) {
+ EXPECT_EQ(ASCIIToUTF16("Clark Kent"), v[0]);
+ EXPECT_EQ(ASCIIToUTF16("Clark Sutter"), v[1]);
+ }
+
+ // Removing all elements since the beginning of this function should remove
+ // everything from the database.
+ changes.clear();
+ EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, Time(), &changes));
+
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Clark Kent"))),
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Clark Sutter"))),
+ AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Favorite Color"),
+ ASCIIToUTF16("Green"))),
+ };
+ EXPECT_EQ(arraysize(expected_changes), changes.size());
+ for (size_t i = 0; i < arraysize(expected_changes); i++) {
+ EXPECT_EQ(expected_changes[i], changes[i]);
+ }
+
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_EQ(0, count);
+
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("Name"), base::string16(), &v, 6));
+ EXPECT_EQ(0U, v.size());
+
+ // Now add some values with empty strings.
+ const base::string16 kValue = ASCIIToUTF16(" toto ");
+ field.name = ASCIIToUTF16("blank");
+ field.value = base::string16();
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ field.name = ASCIIToUTF16("blank");
+ field.value = ASCIIToUTF16(" ");
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ field.name = ASCIIToUTF16("blank");
+ field.value = ASCIIToUTF16(" ");
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+ field.name = ASCIIToUTF16("blank");
+ field.value = kValue;
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+
+ // They should be stored normally as the DB layer does not check for empty
+ // values.
+ v.clear();
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("blank"), base::string16(), &v, 10));
+ EXPECT_EQ(4U, v.size());
+
+ // Now we'll check that ClearAutofillEmptyValueElements() works as expected.
+ table_->ClearAutofillEmptyValueElements();
+
+ v.clear();
+ EXPECT_TRUE(table_->GetFormValuesForElementName(
+ ASCIIToUTF16("blank"), base::string16(), &v, 10));
+ ASSERT_EQ(1U, v.size());
+
+ EXPECT_EQ(kValue, v[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_RemoveBetweenChanges) {
+ TimeDelta one_day(TimeDelta::FromDays(1));
+ Time t1 = Time::Now();
+ Time t2 = t1 + one_day;
+
+ AutofillChangeList changes;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1));
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t2));
+
+ changes.clear();
+ EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(t1, t2, &changes));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::UPDATE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+ changes.clear();
+
+ EXPECT_TRUE(
+ table_->RemoveFormElementsAddedBetween(t2, t2 + one_day, &changes));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::REMOVE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_AddChanges) {
+ TimeDelta one_day(TimeDelta::FromDays(1));
+ Time t1 = Time::Now();
+ Time t2 = t1 + one_day;
+
+ AutofillChangeList changes;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t1));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::ADD,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+
+ changes.clear();
+ EXPECT_TRUE(
+ table_->AddFormFieldValueTime(field, &changes, t2));
+ ASSERT_EQ(1U, changes.size());
+ EXPECT_EQ(AutofillChange(AutofillChange::UPDATE,
+ AutofillKey(ASCIIToUTF16("Name"),
+ ASCIIToUTF16("Superman"))),
+ changes[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateOneWithOneTimestamp) {
+ AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, -1));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ FormFieldData field;
+ field.name = ASCIIToUTF16("foo");
+ field.value = ASCIIToUTF16("bar");
+ int64 pair_id;
+ int count;
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(1, count);
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(1U, all_entries.size());
+ EXPECT_TRUE(entry == all_entries[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateOneWithTwoTimestamps) {
+ AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ FormFieldData field;
+ field.name = ASCIIToUTF16("foo");
+ field.value = ASCIIToUTF16("bar");
+ int64 pair_id;
+ int count;
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(2, count);
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(1U, all_entries.size());
+ EXPECT_TRUE(entry == all_entries[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAutofillTimestamps) {
+ AutofillEntry entry(MakeAutofillEntry("foo", "bar", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ std::vector<Time> timestamps;
+ ASSERT_TRUE(table_->GetAutofillTimestamps(ASCIIToUTF16("foo"),
+ ASCIIToUTF16("bar"),
+ &timestamps));
+ ASSERT_EQ(2U, timestamps.size());
+ EXPECT_TRUE(Time::FromTimeT(1) == timestamps[0]);
+ EXPECT_TRUE(Time::FromTimeT(2) == timestamps[1]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateTwo) {
+ AutofillEntry entry0(MakeAutofillEntry("foo", "bar0", 1, -1));
+ AutofillEntry entry1(MakeAutofillEntry("foo", "bar1", 2, 3));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry0);
+ entries.push_back(entry1);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ FormFieldData field0;
+ field0.name = ASCIIToUTF16("foo");
+ field0.value = ASCIIToUTF16("bar0");
+ int64 pair_id;
+ int count;
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field0, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(1, count);
+
+ FormFieldData field1;
+ field1.name = ASCIIToUTF16("foo");
+ field1.value = ASCIIToUTF16("bar1");
+ ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field1, &pair_id, &count));
+ EXPECT_LE(0, pair_id);
+ EXPECT_EQ(2, count);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateReplace) {
+ AutofillChangeList changes;
+ // Add a form field. This will be replaced.
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValue(field, &changes));
+
+ AutofillEntry entry(MakeAutofillEntry("Name", "Superman", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(1U, all_entries.size());
+ EXPECT_TRUE(entry == all_entries[0]);
+}
+
+TEST_F(AutofillTableTest, Autofill_UpdateDontReplace) {
+ Time t = Time::Now();
+ AutofillEntry existing(
+ MakeAutofillEntry("Name", "Superman", t.ToTimeT(), -1));
+
+ AutofillChangeList changes;
+ // Add a form field. This will NOT be replaced.
+ FormFieldData field;
+ field.name = existing.key().name();
+ field.value = existing.key().value();
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, t));
+ AutofillEntry entry(MakeAutofillEntry("Name", "Clark Kent", 1, 2));
+ std::vector<AutofillEntry> entries;
+ entries.push_back(entry);
+ ASSERT_TRUE(table_->UpdateAutofillEntries(entries));
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(2U, all_entries.size());
+ AutofillEntrySet expected_entries(all_entries.begin(),
+ all_entries.end(),
+ CompareAutofillEntries);
+ EXPECT_EQ(1U, expected_entries.count(existing));
+ EXPECT_EQ(1U, expected_entries.count(entry));
+}
+
+TEST_F(AutofillTableTest, Autofill_AddFormFieldValues) {
+ Time t = Time::Now();
+
+ // Add multiple values for "firstname" and "lastname" names. Test that only
+ // first value of each gets added. Related to security issue:
+ // http://crbug.com/51727.
+ std::vector<FormFieldData> elements;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("firstname");
+ field.value = ASCIIToUTF16("Joe");
+ elements.push_back(field);
+
+ field.name = ASCIIToUTF16("firstname");
+ field.value = ASCIIToUTF16("Jane");
+ elements.push_back(field);
+
+ field.name = ASCIIToUTF16("lastname");
+ field.value = ASCIIToUTF16("Smith");
+ elements.push_back(field);
+
+ field.name = ASCIIToUTF16("lastname");
+ field.value = ASCIIToUTF16("Jones");
+ elements.push_back(field);
+
+ std::vector<AutofillChange> changes;
+ table_->AddFormFieldValuesTime(elements, &changes, t);
+
+ ASSERT_EQ(2U, changes.size());
+ EXPECT_EQ(changes[0], AutofillChange(AutofillChange::ADD,
+ AutofillKey(ASCIIToUTF16("firstname"),
+ ASCIIToUTF16("Joe"))));
+ EXPECT_EQ(changes[1], AutofillChange(AutofillChange::ADD,
+ AutofillKey(ASCIIToUTF16("lastname"),
+ ASCIIToUTF16("Smith"))));
+
+ std::vector<AutofillEntry> all_entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries));
+ ASSERT_EQ(2U, all_entries.size());
+}
+
+TEST_F(AutofillTableTest, AutofillProfile) {
+ // Add a 'Home' profile.
+ AutofillProfile home_profile;
+ home_profile.set_origin(std::string());
+ home_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ home_profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ home_profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ home_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz"));
+ home_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google"));
+ home_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way"));
+ home_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5"));
+ home_profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ home_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ home_profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ home_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ home_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567"));
+
+ Time pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddAutofillProfile(home_profile));
+ Time post_creation_time = Time::Now();
+
+ // Get the 'Home' profile.
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(home_profile.guid(), &db_profile));
+ EXPECT_EQ(home_profile, *db_profile);
+ sql::Statement s_home(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified "
+ "FROM autofill_profiles WHERE guid=?"));
+ s_home.BindString(0, home_profile.guid());
+ ASSERT_TRUE(s_home.is_valid());
+ ASSERT_TRUE(s_home.Step());
+ EXPECT_GE(s_home.ColumnInt64(0), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_home.ColumnInt64(0), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_home.Step());
+ delete db_profile;
+
+ // Add a 'Billing' profile.
+ AutofillProfile billing_profile = home_profile;
+ billing_profile.set_guid(base::GenerateGUID());
+ billing_profile.set_origin("https://www.example.com/");
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE1,
+ ASCIIToUTF16("5678 Bottom Street"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("suite 3"));
+
+ pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddAutofillProfile(billing_profile));
+ post_creation_time = Time::Now();
+
+ // Get the 'Billing' profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+ EXPECT_EQ(billing_profile, *db_profile);
+ sql::Statement s_billing(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles WHERE guid=?"));
+ s_billing.BindString(0, billing_profile.guid());
+ ASSERT_TRUE(s_billing.is_valid());
+ ASSERT_TRUE(s_billing.Step());
+ EXPECT_GE(s_billing.ColumnInt64(0), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_billing.ColumnInt64(0), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_billing.Step());
+ delete db_profile;
+
+ // Update the 'Billing' profile, name only.
+ billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
+ Time pre_modification_time = Time::Now();
+ EXPECT_TRUE(table_->UpdateAutofillProfile(billing_profile));
+ Time post_modification_time = Time::Now();
+ ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+ EXPECT_EQ(billing_profile, *db_profile);
+ sql::Statement s_billing_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles WHERE guid=?"));
+ s_billing_updated.BindString(0, billing_profile.guid());
+ ASSERT_TRUE(s_billing_updated.is_valid());
+ ASSERT_TRUE(s_billing_updated.Step());
+ EXPECT_GE(s_billing_updated.ColumnInt64(0),
+ pre_modification_time.ToTimeT());
+ EXPECT_LE(s_billing_updated.ColumnInt64(0),
+ post_modification_time.ToTimeT());
+ EXPECT_FALSE(s_billing_updated.Step());
+ delete db_profile;
+
+ // Update the 'Billing' profile.
+ billing_profile.set_origin("Chrome settings");
+ billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Janice"));
+ billing_profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("C."));
+ billing_profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Joplin"));
+ billing_profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("jane@singer.com"));
+ billing_profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Indy"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("Open Road"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("Route 66"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("NFA"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("NY"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("10011"));
+ billing_profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ billing_profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
+ ASCIIToUTF16("18181230000"));
+ Time pre_modification_time_2 = Time::Now();
+ EXPECT_TRUE(table_->UpdateAutofillProfile(billing_profile));
+ Time post_modification_time_2 = Time::Now();
+ ASSERT_TRUE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+ EXPECT_EQ(billing_profile, *db_profile);
+ sql::Statement s_billing_updated_2(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles WHERE guid=?"));
+ s_billing_updated_2.BindString(0, billing_profile.guid());
+ ASSERT_TRUE(s_billing_updated_2.is_valid());
+ ASSERT_TRUE(s_billing_updated_2.Step());
+ EXPECT_GE(s_billing_updated_2.ColumnInt64(0),
+ pre_modification_time_2.ToTimeT());
+ EXPECT_LE(s_billing_updated_2.ColumnInt64(0),
+ post_modification_time_2.ToTimeT());
+ EXPECT_FALSE(s_billing_updated_2.Step());
+ delete db_profile;
+
+ // Remove the 'Billing' profile.
+ EXPECT_TRUE(table_->RemoveAutofillProfile(billing_profile.guid()));
+ EXPECT_FALSE(table_->GetAutofillProfile(billing_profile.guid(), &db_profile));
+}
+
+TEST_F(AutofillTableTest, AutofillProfileMultiValueNames) {
+ AutofillProfile p;
+ const base::string16 kJohnDoe(ASCIIToUTF16("John Doe"));
+ const base::string16 kJohnPDoe(ASCIIToUTF16("John P. Doe"));
+ std::vector<base::string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Update the values.
+ const base::string16 kNoOne(ASCIIToUTF16("No One"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(NAME_FULL, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ EXPECT_EQ(base::string16(), db_profile->GetRawInfo(NAME_FULL));
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileMultiValueEmails) {
+ AutofillProfile p;
+ const base::string16 kJohnDoe(ASCIIToUTF16("john@doe.com"));
+ const base::string16 kJohnPDoe(ASCIIToUTF16("john_p@doe.com"));
+ std::vector<base::string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Update the values.
+ const base::string16 kNoOne(ASCIIToUTF16("no@one.com"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(EMAIL_ADDRESS, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ EXPECT_EQ(base::string16(), db_profile->GetRawInfo(EMAIL_ADDRESS));
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileMultiValuePhone) {
+ AutofillProfile p;
+ const base::string16 kJohnDoe(ASCIIToUTF16("4151112222"));
+ const base::string16 kJohnPDoe(ASCIIToUTF16("4151113333"));
+ std::vector<base::string16> set_values;
+ set_values.push_back(kJohnDoe);
+ set_values.push_back(kJohnPDoe);
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+
+ EXPECT_TRUE(table_->AddAutofillProfile(p));
+
+ AutofillProfile* db_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Update the values.
+ const base::string16 kNoOne(ASCIIToUTF16("4151110000"));
+ set_values[1] = kNoOne;
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ delete db_profile;
+
+ // Delete values.
+ set_values.clear();
+ p.SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, set_values);
+ EXPECT_TRUE(table_->UpdateAutofillProfile(p));
+ ASSERT_TRUE(table_->GetAutofillProfile(p.guid(), &db_profile));
+ EXPECT_EQ(p, *db_profile);
+ EXPECT_EQ(0, p.Compare(*db_profile));
+ EXPECT_EQ(base::string16(), db_profile->GetRawInfo(EMAIL_ADDRESS));
+ delete db_profile;
+}
+
+TEST_F(AutofillTableTest, AutofillProfileTrash) {
+ std::vector<std::string> guids;
+ table_->GetAutofillProfilesInTrash(&guids);
+ EXPECT_TRUE(guids.empty());
+
+ ASSERT_TRUE(table_->AddAutofillGUIDToTrash(
+ "00000000-0000-0000-0000-000000000000"));
+ ASSERT_TRUE(table_->AddAutofillGUIDToTrash(
+ "00000000-0000-0000-0000-000000000001"));
+ ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
+ EXPECT_EQ(2UL, guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000000", guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000001", guids[1]);
+
+ ASSERT_TRUE(table_->EmptyAutofillProfilesTrash());
+ ASSERT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
+ EXPECT_TRUE(guids.empty());
+}
+
+TEST_F(AutofillTableTest, AutofillProfileTrashInteraction) {
+ std::vector<std::string> guids;
+ table_->GetAutofillProfilesInTrash(&guids);
+ EXPECT_TRUE(guids.empty());
+
+ AutofillProfile profile;
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ profile.SetRawInfo(EMAIL_ADDRESS,ASCIIToUTF16("js@smith.xyz"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1 Main St"));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+
+ // Mark this profile as in the trash. This stops |AddAutofillProfile| from
+ // adding it.
+ EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid()));
+ EXPECT_TRUE(table_->AddAutofillProfile(profile));
+ AutofillProfile* added_profile = NULL;
+ EXPECT_FALSE(table_->GetAutofillProfile(profile.guid(), &added_profile));
+ EXPECT_EQ(static_cast<AutofillProfile*>(NULL), added_profile);
+
+ // Add the profile for real this time.
+ EXPECT_TRUE(table_->EmptyAutofillProfilesTrash());
+ EXPECT_TRUE(table_->GetAutofillProfilesInTrash(&guids));
+ EXPECT_TRUE(guids.empty());
+ EXPECT_TRUE(table_->AddAutofillProfile(profile));
+ EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(),
+ &added_profile));
+ ASSERT_NE(static_cast<AutofillProfile*>(NULL), added_profile);
+ delete added_profile;
+
+ // Mark this profile as in the trash. This stops |UpdateAutofillProfileMulti|
+ // from updating it. In normal operation a profile should not be both in the
+ // trash and in the profiles table simultaneously.
+ EXPECT_TRUE(table_->AddAutofillGUIDToTrash(profile.guid()));
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Jane"));
+ EXPECT_TRUE(table_->UpdateAutofillProfile(profile));
+ AutofillProfile* updated_profile = NULL;
+ EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), &updated_profile));
+ ASSERT_NE(static_cast<AutofillProfile*>(NULL), added_profile);
+ EXPECT_EQ(ASCIIToUTF16("John"), updated_profile->GetRawInfo(NAME_FIRST));
+ delete updated_profile;
+
+ // Try to delete the trashed profile. This stops |RemoveAutofillProfile| from
+ // deleting it. In normal operation deletion is done by migration step, and
+ // removal from trash is done by |WebDataService|. |RemoveAutofillProfile|
+ // does remove the item from the trash if it is found however, so that if
+ // other clients remove it (via Sync say) then it is gone and doesn't need to
+ // be processed further by |WebDataService|.
+ EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid()));
+ AutofillProfile* removed_profile = NULL;
+ EXPECT_TRUE(table_->GetAutofillProfile(profile.guid(), &removed_profile));
+ EXPECT_FALSE(table_->IsAutofillGUIDInTrash(profile.guid()));
+ ASSERT_NE(static_cast<AutofillProfile*>(NULL), removed_profile);
+ delete removed_profile;
+
+ // Check that emptying the trash now allows removal to occur.
+ EXPECT_TRUE(table_->EmptyAutofillProfilesTrash());
+ EXPECT_TRUE(table_->RemoveAutofillProfile(profile.guid()));
+ removed_profile = NULL;
+ EXPECT_FALSE(table_->GetAutofillProfile(profile.guid(), &removed_profile));
+ EXPECT_EQ(static_cast<AutofillProfile*>(NULL), removed_profile);
+}
+
+TEST_F(AutofillTableTest, CreditCard) {
+ // Add a 'Work' credit card.
+ CreditCard work_creditcard;
+ work_creditcard.set_origin("https://www.example.com/");
+ work_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ work_creditcard.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("1234567890123456"));
+ work_creditcard.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04"));
+ work_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ ASCIIToUTF16("2013"));
+
+ Time pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddCreditCard(work_creditcard));
+ Time post_creation_time = Time::Now();
+
+ // Get the 'Work' credit card.
+ CreditCard* db_creditcard;
+ ASSERT_TRUE(table_->GetCreditCard(work_creditcard.guid(), &db_creditcard));
+ EXPECT_EQ(work_creditcard, *db_creditcard);
+ sql::Statement s_work(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards WHERE guid=?"));
+ s_work.BindString(0, work_creditcard.guid());
+ ASSERT_TRUE(s_work.is_valid());
+ ASSERT_TRUE(s_work.Step());
+ EXPECT_GE(s_work.ColumnInt64(5), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_work.ColumnInt64(5), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_work.Step());
+ delete db_creditcard;
+
+ // Add a 'Target' credit card.
+ CreditCard target_creditcard;
+ target_creditcard.set_origin(std::string());
+ target_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ target_creditcard.SetRawInfo(CREDIT_CARD_NUMBER,
+ ASCIIToUTF16("1111222233334444"));
+ target_creditcard.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("06"));
+ target_creditcard.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ ASCIIToUTF16("2012"));
+
+ pre_creation_time = Time::Now();
+ EXPECT_TRUE(table_->AddCreditCard(target_creditcard));
+ post_creation_time = Time::Now();
+ ASSERT_TRUE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard));
+ EXPECT_EQ(target_creditcard, *db_creditcard);
+ sql::Statement s_target(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards WHERE guid=?"));
+ s_target.BindString(0, target_creditcard.guid());
+ ASSERT_TRUE(s_target.is_valid());
+ ASSERT_TRUE(s_target.Step());
+ EXPECT_GE(s_target.ColumnInt64(5), pre_creation_time.ToTimeT());
+ EXPECT_LE(s_target.ColumnInt64(5), post_creation_time.ToTimeT());
+ EXPECT_FALSE(s_target.Step());
+ delete db_creditcard;
+
+ // Update the 'Target' credit card.
+ target_creditcard.set_origin("Interactive Autofill dialog");
+ target_creditcard.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Charles Grady"));
+ Time pre_modification_time = Time::Now();
+ EXPECT_TRUE(table_->UpdateCreditCard(target_creditcard));
+ Time post_modification_time = Time::Now();
+ ASSERT_TRUE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard));
+ EXPECT_EQ(target_creditcard, *db_creditcard);
+ sql::Statement s_target_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards WHERE guid=?"));
+ s_target_updated.BindString(0, target_creditcard.guid());
+ ASSERT_TRUE(s_target_updated.is_valid());
+ ASSERT_TRUE(s_target_updated.Step());
+ EXPECT_GE(s_target_updated.ColumnInt64(5), pre_modification_time.ToTimeT());
+ EXPECT_LE(s_target_updated.ColumnInt64(5), post_modification_time.ToTimeT());
+ EXPECT_FALSE(s_target_updated.Step());
+ delete db_creditcard;
+
+ // Remove the 'Target' credit card.
+ EXPECT_TRUE(table_->RemoveCreditCard(target_creditcard.guid()));
+ EXPECT_FALSE(table_->GetCreditCard(target_creditcard.guid(), &db_creditcard));
+}
+
+TEST_F(AutofillTableTest, UpdateAutofillProfile) {
+ // Add a profile to the db.
+ AutofillProfile profile;
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@example.com"));
+ profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5"));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567"));
+ table_->AddAutofillProfile(profile);
+
+ // Set a mocked value for the profile's creation time.
+ const time_t mock_creation_date = Time::Now().ToTimeT() - 13;
+ sql::Statement s_mock_creation_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_creation_date.is_valid());
+ s_mock_creation_date.BindInt64(0, mock_creation_date);
+ ASSERT_TRUE(s_mock_creation_date.Run());
+
+ // Get the profile.
+ AutofillProfile* tmp_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ scoped_ptr<AutofillProfile> db_profile(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_original.is_valid());
+ ASSERT_TRUE(s_original.Step());
+ EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0));
+ EXPECT_FALSE(s_original.Step());
+
+ // Now, update the profile and save the update to the database.
+ // The modification date should change to reflect the update.
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@smith.xyz"));
+ table_->UpdateAutofillProfile(profile);
+
+ // Get the profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ db_profile.reset(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_updated.is_valid());
+ ASSERT_TRUE(s_updated.Step());
+ EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0));
+ EXPECT_FALSE(s_updated.Step());
+
+ // Set a mocked value for the profile's modification time.
+ const time_t mock_modification_date = Time::Now().ToTimeT() - 7;
+ sql::Statement s_mock_modification_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_modification_date.is_valid());
+ s_mock_modification_date.BindInt64(0, mock_modification_date);
+ ASSERT_TRUE(s_mock_modification_date.Run());
+
+ // Finally, call into |UpdateAutofillProfile()| without changing the
+ // profile. The modification date should not change.
+ table_->UpdateAutofillProfile(profile);
+
+ // Get the profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ db_profile.reset(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_unchanged(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_unchanged.is_valid());
+ ASSERT_TRUE(s_unchanged.Step());
+ EXPECT_EQ(mock_modification_date, s_unchanged.ColumnInt64(0));
+ EXPECT_FALSE(s_unchanged.Step());
+}
+
+TEST_F(AutofillTableTest, UpdateCreditCard) {
+ // Add a credit card to the db.
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1234567890123456"));
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04"));
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2013"));
+ table_->AddCreditCard(credit_card);
+
+ // Set a mocked value for the credit card's creation time.
+ const time_t mock_creation_date = Time::Now().ToTimeT() - 13;
+ sql::Statement s_mock_creation_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_creation_date.is_valid());
+ s_mock_creation_date.BindInt64(0, mock_creation_date);
+ ASSERT_TRUE(s_mock_creation_date.Run());
+
+ // Get the credit card.
+ CreditCard* tmp_credit_card;
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ scoped_ptr<CreditCard> db_credit_card(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_original.is_valid());
+ ASSERT_TRUE(s_original.Step());
+ EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0));
+ EXPECT_FALSE(s_original.Step());
+
+ // Now, update the credit card and save the update to the database.
+ // The modification date should change to reflect the update.
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("01"));
+ table_->UpdateCreditCard(credit_card);
+
+ // Get the credit card.
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ db_credit_card.reset(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_updated.is_valid());
+ ASSERT_TRUE(s_updated.Step());
+ EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0));
+ EXPECT_FALSE(s_updated.Step());
+
+ // Set a mocked value for the credit card's modification time.
+ const time_t mock_modification_date = Time::Now().ToTimeT() - 7;
+ sql::Statement s_mock_modification_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_modification_date.is_valid());
+ s_mock_modification_date.BindInt64(0, mock_modification_date);
+ ASSERT_TRUE(s_mock_modification_date.Run());
+
+ // Finally, call into |UpdateCreditCard()| without changing the credit card.
+ // The modification date should not change.
+ table_->UpdateCreditCard(credit_card);
+
+ // Get the credit card.
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ db_credit_card.reset(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_unchanged(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_unchanged.is_valid());
+ ASSERT_TRUE(s_unchanged.Step());
+ EXPECT_EQ(mock_modification_date, s_unchanged.ColumnInt64(0));
+ EXPECT_FALSE(s_unchanged.Step());
+}
+
+TEST_F(AutofillTableTest, UpdateProfileOriginOnly) {
+ // Add a profile to the db.
+ AutofillProfile profile;
+ profile.SetRawInfo(NAME_FIRST, ASCIIToUTF16("John"));
+ profile.SetRawInfo(NAME_MIDDLE, ASCIIToUTF16("Q."));
+ profile.SetRawInfo(NAME_LAST, ASCIIToUTF16("Smith"));
+ profile.SetRawInfo(EMAIL_ADDRESS, ASCIIToUTF16("js@example.com"));
+ profile.SetRawInfo(COMPANY_NAME, ASCIIToUTF16("Google"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE1, ASCIIToUTF16("1234 Apple Way"));
+ profile.SetRawInfo(ADDRESS_HOME_LINE2, ASCIIToUTF16("unit 5"));
+ profile.SetRawInfo(ADDRESS_HOME_CITY, ASCIIToUTF16("Los Angeles"));
+ profile.SetRawInfo(ADDRESS_HOME_STATE, ASCIIToUTF16("CA"));
+ profile.SetRawInfo(ADDRESS_HOME_ZIP, ASCIIToUTF16("90025"));
+ profile.SetRawInfo(ADDRESS_HOME_COUNTRY, ASCIIToUTF16("US"));
+ profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, ASCIIToUTF16("18181234567"));
+ table_->AddAutofillProfile(profile);
+
+ // Set a mocked value for the profile's creation time.
+ const time_t mock_creation_date = Time::Now().ToTimeT() - 13;
+ sql::Statement s_mock_creation_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_creation_date.is_valid());
+ s_mock_creation_date.BindInt64(0, mock_creation_date);
+ ASSERT_TRUE(s_mock_creation_date.Run());
+
+ // Get the profile.
+ AutofillProfile* tmp_profile;
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ scoped_ptr<AutofillProfile> db_profile(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_original.is_valid());
+ ASSERT_TRUE(s_original.Step());
+ EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0));
+ EXPECT_FALSE(s_original.Step());
+
+ // Now, update just the profile's origin and save the update to the database.
+ // The modification date should change to reflect the update.
+ profile.set_origin("https://www.example.com/");
+ table_->UpdateAutofillProfile(profile);
+
+ // Get the profile.
+ ASSERT_TRUE(table_->GetAutofillProfile(profile.guid(), &tmp_profile));
+ db_profile.reset(tmp_profile);
+ EXPECT_EQ(profile, *db_profile);
+ sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_updated.is_valid());
+ ASSERT_TRUE(s_updated.Step());
+ EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0));
+ EXPECT_FALSE(s_updated.Step());
+}
+
+TEST_F(AutofillTableTest, UpdateCreditCardOriginOnly) {
+ // Add a credit card to the db.
+ CreditCard credit_card;
+ credit_card.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Jack Torrance"));
+ credit_card.SetRawInfo(CREDIT_CARD_NUMBER, ASCIIToUTF16("1234567890123456"));
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_MONTH, ASCIIToUTF16("04"));
+ credit_card.SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, ASCIIToUTF16("2013"));
+ table_->AddCreditCard(credit_card);
+
+ // Set a mocked value for the credit card's creation time.
+ const time_t mock_creation_date = Time::Now().ToTimeT() - 13;
+ sql::Statement s_mock_creation_date(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified = ?"));
+ ASSERT_TRUE(s_mock_creation_date.is_valid());
+ s_mock_creation_date.BindInt64(0, mock_creation_date);
+ ASSERT_TRUE(s_mock_creation_date.Run());
+
+ // Get the credit card.
+ CreditCard* tmp_credit_card;
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ scoped_ptr<CreditCard> db_credit_card(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_original(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_original.is_valid());
+ ASSERT_TRUE(s_original.Step());
+ EXPECT_EQ(mock_creation_date, s_original.ColumnInt64(0));
+ EXPECT_FALSE(s_original.Step());
+
+ // Now, update just the credit card's origin and save the update to the
+ // database. The modification date should change to reflect the update.
+ credit_card.set_origin("https://www.example.com/");
+ table_->UpdateCreditCard(credit_card);
+
+ // Get the credit card.
+ ASSERT_TRUE(table_->GetCreditCard(credit_card.guid(), &tmp_credit_card));
+ db_credit_card.reset(tmp_credit_card);
+ EXPECT_EQ(credit_card, *db_credit_card);
+ sql::Statement s_updated(db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_updated.is_valid());
+ ASSERT_TRUE(s_updated.Step());
+ EXPECT_LT(mock_creation_date, s_updated.ColumnInt64(0));
+ EXPECT_FALSE(s_updated.Step());
+}
+
+TEST_F(AutofillTableTest, RemoveAutofillDataModifiedBetween) {
+ // Populate the autofill_profiles and credit_cards tables.
+ ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000000', 11);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000001', 21);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000002', 31);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000003', 41);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000004', 51);"
+ "INSERT INTO autofill_profiles (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000005', 61);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000006', 17);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000007', 27);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000008', 37);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000009', 47);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000010', 57);"
+ "INSERT INTO credit_cards (guid, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000011', 67);"));
+
+ // Remove all entries modified in the bounded time range [17,41).
+ std::vector<std::string> profile_guids;
+ std::vector<std::string> credit_card_guids;
+ table_->RemoveAutofillDataModifiedBetween(
+ Time::FromTimeT(17), Time::FromTimeT(41),
+ &profile_guids, &credit_card_guids);
+ ASSERT_EQ(2UL, profile_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000001", profile_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000002", profile_guids[1]);
+ sql::Statement s_autofill_profiles_bounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_bounded.is_valid());
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(41, s_autofill_profiles_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(51, s_autofill_profiles_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(61, s_autofill_profiles_bounded.ColumnInt64(0));
+ EXPECT_FALSE(s_autofill_profiles_bounded.Step());
+ ASSERT_EQ(3UL, credit_card_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000006", credit_card_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000007", credit_card_guids[1]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000008", credit_card_guids[2]);
+ sql::Statement s_credit_cards_bounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_bounded.is_valid());
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(47, s_credit_cards_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(57, s_credit_cards_bounded.ColumnInt64(0));
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(67, s_credit_cards_bounded.ColumnInt64(0));
+ EXPECT_FALSE(s_credit_cards_bounded.Step());
+
+ // Remove all entries modified on or after time 51 (unbounded range).
+ table_->RemoveAutofillDataModifiedBetween(
+ Time::FromTimeT(51), Time(),
+ &profile_guids, &credit_card_guids);
+ ASSERT_EQ(2UL, profile_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000004", profile_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000005", profile_guids[1]);
+ sql::Statement s_autofill_profiles_unbounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_unbounded.is_valid());
+ ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
+ EXPECT_EQ(11, s_autofill_profiles_unbounded.ColumnInt64(0));
+ ASSERT_TRUE(s_autofill_profiles_unbounded.Step());
+ EXPECT_EQ(41, s_autofill_profiles_unbounded.ColumnInt64(0));
+ EXPECT_FALSE(s_autofill_profiles_unbounded.Step());
+ ASSERT_EQ(2UL, credit_card_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000010", credit_card_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000011", credit_card_guids[1]);
+ sql::Statement s_credit_cards_unbounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_unbounded.is_valid());
+ ASSERT_TRUE(s_credit_cards_unbounded.Step());
+ EXPECT_EQ(47, s_credit_cards_unbounded.ColumnInt64(0));
+ EXPECT_FALSE(s_credit_cards_unbounded.Step());
+
+ // Remove all remaining entries.
+ table_->RemoveAutofillDataModifiedBetween(
+ Time(), Time(),
+ &profile_guids, &credit_card_guids);
+ ASSERT_EQ(2UL, profile_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000000", profile_guids[0]);
+ EXPECT_EQ("00000000-0000-0000-0000-000000000003", profile_guids[1]);
+ sql::Statement s_autofill_profiles_empty(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_empty.is_valid());
+ EXPECT_FALSE(s_autofill_profiles_empty.Step());
+ ASSERT_EQ(1UL, credit_card_guids.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000009", credit_card_guids[0]);
+ sql::Statement s_credit_cards_empty(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_empty.is_valid());
+ EXPECT_FALSE(s_credit_cards_empty.Step());
+}
+
+TEST_F(AutofillTableTest, RemoveOriginURLsModifiedBetween) {
+ // Populate the autofill_profiles and credit_cards tables.
+ ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+ "INSERT INTO autofill_profiles (guid, origin, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000000', '', 11);"
+ "INSERT INTO autofill_profiles (guid, origin, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000001', "
+ " 'https://www.example.com/', 21);"
+ "INSERT INTO autofill_profiles (guid, origin, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000002', 'Chrome settings', 31);"
+ "INSERT INTO credit_cards (guid, origin, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000003', '', 17);"
+ "INSERT INTO credit_cards (guid, origin, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000004', "
+ " 'https://www.example.com/', 27);"
+ "INSERT INTO credit_cards (guid, origin, date_modified) "
+ "VALUES('00000000-0000-0000-0000-000000000005', 'Chrome settings', "
+ " 37);"));
+
+ // Remove all origin URLs set in the bounded time range [21,27).
+ ScopedVector<AutofillProfile> profiles;
+ table_->RemoveOriginURLsModifiedBetween(
+ Time::FromTimeT(21), Time::FromTimeT(27), &profiles);
+ ASSERT_EQ(1UL, profiles.size());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000001", profiles[0]->guid());
+ sql::Statement s_autofill_profiles_bounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified, origin FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_bounded.is_valid());
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(11, s_autofill_profiles_bounded.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_autofill_profiles_bounded.ColumnString(1));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(21, s_autofill_profiles_bounded.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_autofill_profiles_bounded.ColumnString(1));
+ ASSERT_TRUE(s_autofill_profiles_bounded.Step());
+ EXPECT_EQ(31, s_autofill_profiles_bounded.ColumnInt64(0));
+ EXPECT_EQ("Chrome settings", s_autofill_profiles_bounded.ColumnString(1));
+ sql::Statement s_credit_cards_bounded(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified, origin FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_bounded.is_valid());
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(17, s_credit_cards_bounded.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_credit_cards_bounded.ColumnString(1));
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(27, s_credit_cards_bounded.ColumnInt64(0));
+ EXPECT_EQ("https://www.example.com/",
+ s_credit_cards_bounded.ColumnString(1));
+ ASSERT_TRUE(s_credit_cards_bounded.Step());
+ EXPECT_EQ(37, s_credit_cards_bounded.ColumnInt64(0));
+ EXPECT_EQ("Chrome settings", s_credit_cards_bounded.ColumnString(1));
+
+ // Remove all origin URLS.
+ profiles.clear();
+ table_->RemoveOriginURLsModifiedBetween(Time(), Time(), &profiles);
+ EXPECT_EQ(0UL, profiles.size());
+ sql::Statement s_autofill_profiles_all(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified, origin FROM autofill_profiles"));
+ ASSERT_TRUE(s_autofill_profiles_all.is_valid());
+ ASSERT_TRUE(s_autofill_profiles_all.Step());
+ EXPECT_EQ(11, s_autofill_profiles_all.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_autofill_profiles_all.ColumnString(1));
+ ASSERT_TRUE(s_autofill_profiles_all.Step());
+ EXPECT_EQ(21, s_autofill_profiles_all.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_autofill_profiles_all.ColumnString(1));
+ ASSERT_TRUE(s_autofill_profiles_all.Step());
+ EXPECT_EQ(31, s_autofill_profiles_all.ColumnInt64(0));
+ EXPECT_EQ("Chrome settings", s_autofill_profiles_all.ColumnString(1));
+ sql::Statement s_credit_cards_all(
+ db_->GetSQLConnection()->GetUniqueStatement(
+ "SELECT date_modified, origin FROM credit_cards"));
+ ASSERT_TRUE(s_credit_cards_all.is_valid());
+ ASSERT_TRUE(s_credit_cards_all.Step());
+ EXPECT_EQ(17, s_credit_cards_all.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_credit_cards_all.ColumnString(1));
+ ASSERT_TRUE(s_credit_cards_all.Step());
+ EXPECT_EQ(27, s_credit_cards_all.ColumnInt64(0));
+ EXPECT_EQ(std::string(), s_credit_cards_all.ColumnString(1));
+ ASSERT_TRUE(s_credit_cards_all.Step());
+ EXPECT_EQ(37, s_credit_cards_all.ColumnInt64(0));
+ EXPECT_EQ("Chrome settings", s_credit_cards_all.ColumnString(1));
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_NoResults) {
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+
+ EXPECT_EQ(0U, entries.size());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_OneResult) {
+ AutofillChangeList changes;
+ std::map<std::string, std::vector<Time> > name_value_times_map;
+
+ time_t start = 0;
+ std::vector<Time> timestamps1;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps1.push_back(Time::FromTimeT(start));
+ std::string key1("NameSuperman");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key1, timestamps1));
+
+ AutofillEntrySet expected_entries(CompareAutofillEntries);
+ AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"));
+ AutofillEntry ae1(ak1, timestamps1);
+
+ expected_entries.insert(ae1);
+
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+ AutofillEntrySet entry_set(entries.begin(), entries.end(),
+ CompareAutofillEntries);
+
+ // make sure the lists of entries match
+ ASSERT_EQ(expected_entries.size(), entry_set.size());
+ AutofillEntrySetIterator it;
+ for (it = entry_set.begin(); it != entry_set.end(); it++) {
+ expected_entries.erase(*it);
+ }
+
+ EXPECT_EQ(0U, expected_entries.size());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoDistinct) {
+ AutofillChangeList changes;
+ std::map<std::string, std::vector<Time> > name_value_times_map;
+ time_t start = 0;
+
+ std::vector<Time> timestamps1;
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps1.push_back(Time::FromTimeT(start));
+ std::string key1("NameSuperman");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key1, timestamps1));
+
+ start++;
+ std::vector<Time> timestamps2;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Clark Kent");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps2.push_back(Time::FromTimeT(start));
+ std::string key2("NameClark Kent");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key2, timestamps2));
+
+ AutofillEntrySet expected_entries(CompareAutofillEntries);
+ AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"));
+ AutofillKey ak2(ASCIIToUTF16("Name"), ASCIIToUTF16("Clark Kent"));
+ AutofillEntry ae1(ak1, timestamps1);
+ AutofillEntry ae2(ak2, timestamps2);
+
+ expected_entries.insert(ae1);
+ expected_entries.insert(ae2);
+
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+ AutofillEntrySet entry_set(entries.begin(), entries.end(),
+ CompareAutofillEntries);
+
+ // make sure the lists of entries match
+ ASSERT_EQ(expected_entries.size(), entry_set.size());
+ AutofillEntrySetIterator it;
+ for (it = entry_set.begin(); it != entry_set.end(); it++) {
+ expected_entries.erase(*it);
+ }
+
+ EXPECT_EQ(0U, expected_entries.size());
+}
+
+TEST_F(AutofillTableTest, Autofill_GetAllAutofillEntries_TwoSame) {
+ AutofillChangeList changes;
+ std::map<std::string, std::vector<Time> > name_value_times_map;
+
+ time_t start = 0;
+ std::vector<Time> timestamps;
+ for (int i = 0; i < 2; i++) {
+ FormFieldData field;
+ field.name = ASCIIToUTF16("Name");
+ field.value = ASCIIToUTF16("Superman");
+ EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes,
+ Time::FromTimeT(start)));
+ timestamps.push_back(Time::FromTimeT(start));
+ start++;
+ }
+
+ std::string key("NameSuperman");
+ name_value_times_map.insert(std::pair<std::string,
+ std::vector<Time> > (key, timestamps));
+
+ AutofillEntrySet expected_entries(CompareAutofillEntries);
+ AutofillKey ak1(ASCIIToUTF16("Name"), ASCIIToUTF16("Superman"));
+ AutofillEntry ae1(ak1, timestamps);
+
+ expected_entries.insert(ae1);
+
+ std::vector<AutofillEntry> entries;
+ ASSERT_TRUE(table_->GetAllAutofillEntries(&entries));
+ AutofillEntrySet entry_set(entries.begin(), entries.end(),
+ CompareAutofillEntries);
+
+ // make sure the lists of entries match
+ ASSERT_EQ(expected_entries.size(), entry_set.size());
+ AutofillEntrySetIterator it;
+ for (it = entry_set.begin(); it != entry_set.end(); it++) {
+ expected_entries.erase(*it);
+ }
+
+ EXPECT_EQ(0U, expected_entries.size());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata.h
new file mode 100644
index 00000000000..fbd4107b894
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata.h
@@ -0,0 +1,107 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "components/webdata/common/web_data_service_base.h"
+
+namespace base {
+
+class Time;
+
+} // namespace base
+
+class Profile;
+class WebDataServiceConsumer;
+
+namespace autofill {
+
+class AutofillProfile;
+class CreditCard;
+struct FormFieldData;
+
+// Pure virtual interface for retrieving Autofill data. API users
+// should use AutofillWebDataService.
+class AutofillWebData {
+ public:
+ virtual ~AutofillWebData() {}
+
+ // Schedules a task to add form fields to the web database.
+ virtual void AddFormFields(
+ const std::vector<FormFieldData>& fields) = 0;
+
+ // Initiates the request for a vector of values which have been entered in
+ // form input fields named |name|. The method OnWebDataServiceRequestDone of
+ // |consumer| gets called back when the request is finished, with the vector
+ // included in the argument |result|.
+ virtual WebDataServiceBase::Handle GetFormValuesForElementName(
+ const base::string16& name,
+ const base::string16& prefix,
+ int limit,
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Checks if there are any form elements in the database.
+ virtual WebDataServiceBase::Handle HasFormElements(
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Removes form elements recorded for Autocomplete from the database.
+ virtual void RemoveFormElementsAddedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) = 0;
+
+ virtual void RemoveFormValueForElementName(const base::string16& name,
+ const base::string16& value) = 0;
+
+ // Schedules a task to add an Autofill profile to the web database.
+ virtual void AddAutofillProfile(const AutofillProfile& profile) = 0;
+
+ // Schedules a task to update an Autofill profile in the web database.
+ virtual void UpdateAutofillProfile(const AutofillProfile& profile) = 0;
+
+ // Schedules a task to remove an Autofill profile from the web database.
+ // |guid| is the identifer of the profile to remove.
+ virtual void RemoveAutofillProfile(const std::string& guid) = 0;
+
+ // Initiates the request for all Autofill profiles. The method
+ // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+ // finished, with the profiles included in the argument |result|. The
+ // consumer owns the profiles.
+ virtual WebDataServiceBase::Handle GetAutofillProfiles(
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Schedules a task to add credit card to the web database.
+ virtual void AddCreditCard(const CreditCard& credit_card) = 0;
+
+ // Schedules a task to update credit card in the web database.
+ virtual void UpdateCreditCard(const CreditCard& credit_card) = 0;
+
+ // Schedules a task to remove a credit card from the web database.
+ // |guid| is identifer of the credit card to remove.
+ virtual void RemoveCreditCard(const std::string& guid) = 0;
+
+ // Initiates the request for all credit cards. The method
+ // OnWebDataServiceRequestDone of |consumer| gets called when the request is
+ // finished, with the credit cards included in the argument |result|. The
+ // consumer owns the credit cards.
+ virtual WebDataServiceBase::Handle GetCreditCards(
+ WebDataServiceConsumer* consumer) = 0;
+
+ // Removes Autofill records from the database.
+ virtual void RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) = 0;
+
+ // Removes origin URLs associated with Autofill profiles and credit cards from
+ // the database.
+ virtual void RemoveOriginURLsModifiedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend.h
new file mode 100644
index 00000000000..2f463018dc3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend.h
@@ -0,0 +1,43 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_BACKEND_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_BACKEND_H_
+
+class WebDatabase;
+
+namespace autofill {
+
+class AutofillWebDataServiceObserverOnDBThread;
+
+// Interface for doing Autofill work directly on the DB thread (used by
+// Sync, mostly), without fully exposing the AutofillWebDataBackend to clients.
+class AutofillWebDataBackend {
+ public:
+ virtual ~AutofillWebDataBackend() {}
+
+ // Get a raw pointer to the WebDatabase.
+ virtual WebDatabase* GetDatabase() = 0;
+
+ // Add an observer to be notified of changes on the DB thread.
+ virtual void AddObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) = 0;
+
+ // Remove an observer.
+ virtual void RemoveObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) = 0;
+
+ // Remove expired elements from the database and commit if needed.
+ virtual void RemoveExpiredFormElements() = 0;
+
+ // Notifies listeners on the UI thread that multiple changes have been made to
+ // to Autofill records of the database.
+ // NOTE: This method is intended to be called from the DB thread. It
+ // asynchronously notifies listeners on the UI thread.
+ virtual void NotifyOfMultipleAutofillChanges() = 0;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_BACKEND_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
new file mode 100644
index 00000000000..b3da704df06
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.cc
@@ -0,0 +1,384 @@
+// 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/core/browser/webdata/autofill_webdata_backend_impl.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/stl_util.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_data_service_backend.h"
+
+using base::Bind;
+using base::Time;
+using content::BrowserThread;
+
+namespace autofill {
+
+AutofillWebDataBackendImpl::AutofillWebDataBackendImpl(
+ scoped_refptr<WebDataServiceBackend> web_database_backend,
+ const base::Closure& on_changed_callback)
+ : web_database_backend_(web_database_backend),
+ on_changed_callback_(on_changed_callback) {
+}
+
+void AutofillWebDataBackendImpl::AddObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ db_observer_list_.AddObserver(observer);
+}
+
+void AutofillWebDataBackendImpl::RemoveObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ db_observer_list_.RemoveObserver(observer);
+}
+
+AutofillWebDataBackendImpl::~AutofillWebDataBackendImpl() {
+ DCHECK(!user_data_.get()); // Forgot to call ResetUserData?
+}
+
+WebDatabase* AutofillWebDataBackendImpl::GetDatabase() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ return web_database_backend_->database();
+}
+
+void AutofillWebDataBackendImpl::RemoveExpiredFormElements() {
+ web_database_backend_->ExecuteWriteTask(
+ Bind(&AutofillWebDataBackendImpl::RemoveExpiredFormElementsImpl,
+ this));
+}
+
+void AutofillWebDataBackendImpl::NotifyOfMultipleAutofillChanges() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ on_changed_callback_);
+}
+
+base::SupportsUserData* AutofillWebDataBackendImpl::GetDBUserData() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!user_data_)
+ user_data_.reset(new SupportsUserDataAggregatable());
+ return user_data_.get();
+}
+
+void AutofillWebDataBackendImpl::ResetUserData() {
+ user_data_.reset();
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::AddFormElements(
+ const std::vector<FormFieldData>& fields, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillChangeList changes;
+ if (!AutofillTable::FromWebDatabase(db)->AddFormFieldValues(
+ fields, &changes)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ // Post the notifications including the list of affected keys.
+ // This is sent here so that work resulting from this notification will be
+ // done on the DB thread, and not the UI thread.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult>
+AutofillWebDataBackendImpl::GetFormValuesForElementName(
+ const base::string16& name, const base::string16& prefix, int limit,
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<base::string16> values;
+ AutofillTable::FromWebDatabase(db)->GetFormValuesForElementName(
+ name, prefix, &values, limit);
+ return scoped_ptr<WDTypedResult>(
+ new WDResult<std::vector<base::string16> >(AUTOFILL_VALUE_RESULT,
+ values));
+}
+
+scoped_ptr<WDTypedResult> AutofillWebDataBackendImpl::HasFormElements(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ bool value = AutofillTable::FromWebDatabase(db)->HasFormElements();
+ return scoped_ptr<WDTypedResult>(
+ new WDResult<bool>(AUTOFILL_VALUE_RESULT, value));
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::RemoveFormElementsAddedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillChangeList changes;
+
+ if (AutofillTable::FromWebDatabase(db)->RemoveFormElementsAddedBetween(
+ delete_begin, delete_end, &changes)) {
+ if (!changes.empty()) {
+ // Post the notifications including the list of affected keys.
+ // This is sent here so that work resulting from this notification
+ // will be done on the DB thread, and not the UI thread.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+ }
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::RemoveFormValueForElementName(
+ const base::string16& name, const base::string16& value, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+
+ if (AutofillTable::FromWebDatabase(db)->RemoveFormElement(name, value)) {
+ AutofillChangeList changes;
+ changes.push_back(
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name, value)));
+
+ // Post the notifications including the list of affected keys.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::AddAutofillProfile(
+ const AutofillProfile& profile, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!AutofillTable::FromWebDatabase(db)->AddAutofillProfile(profile)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ // Send GUID-based notification.
+ AutofillProfileChange change(
+ AutofillProfileChange::ADD, profile.guid(), &profile);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::UpdateAutofillProfile(
+ const AutofillProfile& profile, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // Only perform the update if the profile exists. It is currently
+ // valid to try to update a missing profile. We simply drop the write and
+ // the caller will detect this on the next refresh.
+ AutofillProfile* original_profile = NULL;
+ if (!AutofillTable::FromWebDatabase(db)->GetAutofillProfile(profile.guid(),
+ &original_profile)) {
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ scoped_ptr<AutofillProfile> scoped_profile(original_profile);
+
+ if (!AutofillTable::FromWebDatabase(db)->UpdateAutofillProfile(profile)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NEEDED;
+ }
+
+ // Send GUID-based notification.
+ AutofillProfileChange change(
+ AutofillProfileChange::UPDATE, profile.guid(), &profile);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::RemoveAutofillProfile(
+ const std::string& guid, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillProfile* profile = NULL;
+ if (!AutofillTable::FromWebDatabase(db)->GetAutofillProfile(guid, &profile)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ scoped_ptr<AutofillProfile> scoped_profile(profile);
+
+ if (!AutofillTable::FromWebDatabase(db)->RemoveAutofillProfile(guid)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ // Send GUID-based notification.
+ AutofillProfileChange change(AutofillProfileChange::REMOVE, guid, NULL);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetAutofillProfiles(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<AutofillProfile*> profiles;
+ AutofillTable::FromWebDatabase(db)->GetAutofillProfiles(&profiles);
+ return scoped_ptr<WDTypedResult>(
+ new WDDestroyableResult<std::vector<AutofillProfile*> >(
+ AUTOFILL_PROFILES_RESULT,
+ profiles,
+ base::Bind(&AutofillWebDataBackendImpl::DestroyAutofillProfileResult,
+ base::Unretained(this))));
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::AddCreditCard(
+ const CreditCard& credit_card, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!AutofillTable::FromWebDatabase(db)->AddCreditCard(credit_card)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::UpdateCreditCard(
+ const CreditCard& credit_card, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ // It is currently valid to try to update a missing profile. We simply drop
+ // the write and the caller will detect this on the next refresh.
+ CreditCard* original_credit_card = NULL;
+ if (!AutofillTable::FromWebDatabase(db)->GetCreditCard(credit_card.guid(),
+ &original_credit_card)) {
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ scoped_ptr<CreditCard> scoped_credit_card(original_credit_card);
+
+ if (!AutofillTable::FromWebDatabase(db)->UpdateCreditCard(credit_card)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::RemoveCreditCard(
+ const std::string& guid, WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!AutofillTable::FromWebDatabase(db)->RemoveCreditCard(guid)) {
+ NOTREACHED();
+ return WebDatabase::COMMIT_NOT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NEEDED;
+}
+
+scoped_ptr<WDTypedResult> AutofillWebDataBackendImpl::GetCreditCards(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<CreditCard*> credit_cards;
+ AutofillTable::FromWebDatabase(db)->GetCreditCards(&credit_cards);
+ return scoped_ptr<WDTypedResult>(
+ new WDDestroyableResult<std::vector<CreditCard*> >(
+ AUTOFILL_CREDITCARDS_RESULT,
+ credit_cards,
+ base::Bind(&AutofillWebDataBackendImpl::DestroyAutofillCreditCardResult,
+ base::Unretained(this))));
+}
+
+WebDatabase::State
+ AutofillWebDataBackendImpl::RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ std::vector<std::string> profile_guids;
+ std::vector<std::string> credit_card_guids;
+ if (AutofillTable::FromWebDatabase(db)->RemoveAutofillDataModifiedBetween(
+ delete_begin,
+ delete_end,
+ &profile_guids,
+ &credit_card_guids)) {
+ for (std::vector<std::string>::iterator iter = profile_guids.begin();
+ iter != profile_guids.end(); ++iter) {
+ AutofillProfileChange change(AutofillProfileChange::REMOVE, *iter, NULL);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+ }
+ // Note: It is the caller's responsibility to post notifications for any
+ // changes, e.g. by calling the Refresh() method of PersonalDataManager.
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::RemoveOriginURLsModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ ScopedVector<AutofillProfile> profiles;
+ if (AutofillTable::FromWebDatabase(db)->RemoveOriginURLsModifiedBetween(
+ delete_begin, delete_end, &profiles)) {
+ for (std::vector<AutofillProfile*>::const_iterator it = profiles.begin();
+ it != profiles.end(); ++it) {
+ AutofillProfileChange change(AutofillProfileChange::UPDATE,
+ (*it)->guid(), *it);
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillProfileChanged(change));
+ }
+ // Note: It is the caller's responsibility to post notifications for any
+ // changes, e.g. by calling the Refresh() method of PersonalDataManager.
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+WebDatabase::State AutofillWebDataBackendImpl::RemoveExpiredFormElementsImpl(
+ WebDatabase* db) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ AutofillChangeList changes;
+
+ if (AutofillTable::FromWebDatabase(db)->RemoveExpiredFormElements(&changes)) {
+ if (!changes.empty()) {
+ // Post the notifications including the list of affected keys.
+ // This is sent here so that work resulting from this notification
+ // will be done on the DB thread, and not the UI thread.
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnDBThread,
+ db_observer_list_,
+ AutofillEntriesChanged(changes));
+ }
+ return WebDatabase::COMMIT_NEEDED;
+ }
+ return WebDatabase::COMMIT_NOT_NEEDED;
+}
+
+void AutofillWebDataBackendImpl::DestroyAutofillProfileResult(
+ const WDTypedResult* result) {
+ DCHECK(result->GetType() == AUTOFILL_PROFILES_RESULT);
+ const WDResult<std::vector<AutofillProfile*> >* r =
+ static_cast<const WDResult<std::vector<AutofillProfile*> >*>(result);
+ std::vector<AutofillProfile*> profiles = r->GetValue();
+ STLDeleteElements(&profiles);
+}
+
+void AutofillWebDataBackendImpl::DestroyAutofillCreditCardResult(
+ const WDTypedResult* result) {
+ DCHECK(result->GetType() == AUTOFILL_CREDITCARDS_RESULT);
+ const WDResult<std::vector<CreditCard*> >* r =
+ static_cast<const WDResult<std::vector<CreditCard*> >*>(result);
+
+ std::vector<CreditCard*> credit_cards = r->GetValue();
+ STLDeleteElements(&credit_cards);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
new file mode 100644
index 00000000000..8015dd4212c
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h
@@ -0,0 +1,185 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_BACKEND_IMPL_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_BACKEND_IMPL_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/supports_user_data.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+#include "components/webdata/common/web_database.h"
+#include "content/public/browser/browser_thread.h"
+
+class WebDataServiceBackend;
+
+namespace autofill {
+
+class AutofillChange;
+class AutofillProfile;
+class AutofillWebDataServiceObserverOnDBThread;
+class CreditCard;
+
+// Backend implentation for the AutofillWebDataService. This class runs on the
+// DB thread, as it handles reads and writes to the WebDatabase, and functions
+// in it should only be called from that thread. Most functions here are just
+// the implementations of the corresponding functions in the Autofill
+// WebDataService.
+class AutofillWebDataBackendImpl
+ : public base::RefCountedThreadSafe<AutofillWebDataBackendImpl,
+ content::BrowserThread::DeleteOnDBThread>,
+ public AutofillWebDataBackend {
+ public:
+ // |web_database_backend| is used to access the WebDatabase directly for
+ // Sync-related operations. |on_changed_callback| is a closure which can be
+ // used to notify the UI thread of changes initiated by Sync (this callback
+ // may be called multiple times).
+ AutofillWebDataBackendImpl(
+ scoped_refptr<WebDataServiceBackend> web_database_backend,
+ const base::Closure& on_changed_callback);
+
+ // AutofillWebDataBackend implementation.
+ virtual void AddObserver(AutofillWebDataServiceObserverOnDBThread* observer)
+ OVERRIDE;
+ virtual void RemoveObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) OVERRIDE;
+ virtual WebDatabase* GetDatabase() OVERRIDE;
+ virtual void RemoveExpiredFormElements() OVERRIDE;
+ virtual void NotifyOfMultipleAutofillChanges() OVERRIDE;
+
+ // Returns a SupportsUserData objects that may be used to store data
+ // owned by the DB thread on this object. Should be called only from
+ // the DB thread, and will be destroyed on the DB thread soon after
+ // |ShutdownOnUIThread()| is called.
+ base::SupportsUserData* GetDBUserData();
+
+ void ResetUserData();
+
+ // Adds form fields to the web database.
+ WebDatabase::State AddFormElements(const std::vector<FormFieldData>& fields,
+ WebDatabase* db);
+
+ // Returns a vector of values which have been entered in form input fields
+ // named |name|.
+ scoped_ptr<WDTypedResult> GetFormValuesForElementName(
+ const base::string16& name,
+ const base::string16& prefix,
+ int limit,
+ WebDatabase* db);
+
+ // Returns true if there are any elements in the form.
+ scoped_ptr<WDTypedResult> HasFormElements(WebDatabase* db);
+
+ // Removes form elements recorded for Autocomplete from the database.
+ WebDatabase::State RemoveFormElementsAddedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db);
+
+
+ // Removes the Form-value |value| which has been entered in form input fields
+ // named |name| from the database.
+ WebDatabase::State RemoveFormValueForElementName(const base::string16& name,
+ const base::string16& value,
+ WebDatabase* db);
+
+ // Adds an Autofill profile to the web database.
+ WebDatabase::State AddAutofillProfile(const AutofillProfile& profile,
+ WebDatabase* db);
+
+ // Updates an Autofill profile in the web database.
+ WebDatabase::State UpdateAutofillProfile(const AutofillProfile& profile,
+ WebDatabase* db);
+
+ // Removes an Autofill profile from the web database.
+ WebDatabase::State RemoveAutofillProfile(const std::string& guid,
+ WebDatabase* db);
+
+ // Returns all Autofill profiles from the web database.
+ scoped_ptr<WDTypedResult> GetAutofillProfiles(WebDatabase* db);
+
+ // Adds a credit card to the web database.
+ WebDatabase::State AddCreditCard(const CreditCard& credit_card,
+ WebDatabase* db);
+
+ // Updates a credit card in the web database.
+ WebDatabase::State UpdateCreditCard(const CreditCard& credit_card,
+ WebDatabase* db);
+
+ // Removes a credit card from the web database.
+ WebDatabase::State RemoveCreditCard(const std::string& guid,
+ WebDatabase* db);
+
+ // Returns a vector of all credit cards from the web database.
+ scoped_ptr<WDTypedResult> GetCreditCards(WebDatabase* db);
+
+ // Removes Autofill records from the database.
+ WebDatabase::State RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db);
+
+ // Removes origin URLs associated with Autofill profiles and credit cards from
+ // the database.
+ WebDatabase::State RemoveOriginURLsModifiedBetween(
+ const base::Time& delete_begin,
+ const base::Time& delete_end,
+ WebDatabase* db);
+
+ protected:
+ virtual ~AutofillWebDataBackendImpl();
+
+ private:
+ friend struct content::BrowserThread::DeleteOnThread<
+ content::BrowserThread::DB>;
+ friend class base::DeleteHelper<AutofillWebDataBackendImpl>;
+ // We have to friend RCTS<> so WIN shared-lib build is happy
+ // (http://crbug/112250).
+ friend class base::RefCountedThreadSafe<AutofillWebDataBackendImpl,
+ content::BrowserThread::DeleteOnDBThread>;
+
+ // This makes the destructor public, and thus allows us to aggregate
+ // SupportsUserData. It is private by default to prevent incorrect
+ // usage in class hierarchies where it is inherited by
+ // reference-counted objects.
+ class SupportsUserDataAggregatable : public base::SupportsUserData {
+ public:
+ SupportsUserDataAggregatable() {}
+ virtual ~SupportsUserDataAggregatable() {}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SupportsUserDataAggregatable);
+ };
+
+ // Storage for user data to be accessed only on the DB thread. May
+ // be used e.g. for SyncableService subclasses that need to be owned
+ // by this object. Is created on first call to |GetDBUserData()|.
+ scoped_ptr<SupportsUserDataAggregatable> user_data_;
+
+ WebDatabase::State RemoveExpiredFormElementsImpl(WebDatabase* db);
+
+ // Callbacks to ensure that sensitive info is destroyed if request is
+ // cancelled.
+ void DestroyAutofillProfileResult(const WDTypedResult* result);
+ void DestroyAutofillCreditCardResult(const WDTypedResult* result);
+
+ ObserverList<AutofillWebDataServiceObserverOnDBThread> db_observer_list_;
+
+ // WebDataServiceBackend allows direct access to DB.
+ // TODO(caitkp): Make it so nobody but us needs direct DB access anymore.
+ scoped_refptr<WebDataServiceBackend> web_database_backend_;
+
+ base::Closure on_changed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillWebDataBackendImpl);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_BACKEND_IMPL_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
new file mode 100644
index 00000000000..bc4f65a779b
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.cc
@@ -0,0 +1,217 @@
+// 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/core/browser/webdata/autofill_webdata_service.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_backend_impl.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_data_service_backend.h"
+#include "components/webdata/common/web_database_service.h"
+
+using base::Bind;
+using base::Time;
+using content::BrowserThread;
+
+namespace autofill {
+
+AutofillWebDataService::AutofillWebDataService(
+ scoped_refptr<WebDatabaseService> wdbs,
+ const ProfileErrorCallback& callback)
+ : WebDataServiceBase(wdbs, callback,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+ weak_ptr_factory_(this),
+ autofill_backend_(NULL) {
+
+ base::Closure on_changed_callback = Bind(
+ &AutofillWebDataService::NotifyAutofillMultipleChangedOnUIThread,
+ weak_ptr_factory_.GetWeakPtr());
+
+ autofill_backend_ = new AutofillWebDataBackendImpl(
+ wdbs_->GetBackend(),
+ on_changed_callback);
+}
+
+AutofillWebDataService::AutofillWebDataService()
+ : WebDataServiceBase(NULL, WebDataServiceBase::ProfileErrorCallback(),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)),
+ weak_ptr_factory_(this),
+ autofill_backend_(new AutofillWebDataBackendImpl(NULL, base::Closure())) {
+}
+
+void AutofillWebDataService::ShutdownOnUIThread() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::ResetUserData,
+ autofill_backend_));
+ WebDataServiceBase::ShutdownOnUIThread();
+}
+
+void AutofillWebDataService::AddFormFields(
+ const std::vector<FormFieldData>& fields) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::AddFormElements,
+ autofill_backend_, fields));
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::GetFormValuesForElementName(
+ const base::string16& name, const base::string16& prefix, int limit,
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::GetFormValuesForElementName,
+ autofill_backend_, name, prefix, limit), consumer);
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::HasFormElements(
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::HasFormElements, autofill_backend_),
+ consumer);
+}
+
+void AutofillWebDataService::RemoveFormElementsAddedBetween(
+ const Time& delete_begin, const Time& delete_end) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::RemoveFormElementsAddedBetween,
+ autofill_backend_, delete_begin, delete_end));
+}
+
+void AutofillWebDataService::RemoveFormValueForElementName(
+ const base::string16& name, const base::string16& value) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::RemoveFormValueForElementName,
+ autofill_backend_, name, value));
+}
+
+void AutofillWebDataService::AddAutofillProfile(
+ const AutofillProfile& profile) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::AddAutofillProfile,
+ autofill_backend_, profile));
+}
+
+void AutofillWebDataService::UpdateAutofillProfile(
+ const AutofillProfile& profile) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::UpdateAutofillProfile,
+ autofill_backend_, profile));
+}
+
+void AutofillWebDataService::RemoveAutofillProfile(
+ const std::string& guid) {
+ wdbs_->ScheduleDBTask(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::RemoveAutofillProfile,
+ autofill_backend_, guid));
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::GetAutofillProfiles(
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::GetAutofillProfiles, autofill_backend_),
+ consumer);
+}
+
+void AutofillWebDataService::AddCreditCard(const CreditCard& credit_card) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::AddCreditCard,
+ autofill_backend_, credit_card));
+}
+
+void AutofillWebDataService::UpdateCreditCard(
+ const CreditCard& credit_card) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::UpdateCreditCard,
+ autofill_backend_, credit_card));
+}
+
+void AutofillWebDataService::RemoveCreditCard(const std::string& guid) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::RemoveCreditCard,
+ autofill_backend_, guid));
+}
+
+WebDataServiceBase::Handle AutofillWebDataService::GetCreditCards(
+ WebDataServiceConsumer* consumer) {
+ return wdbs_->ScheduleDBTaskWithResult(FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::GetCreditCards, autofill_backend_),
+ consumer);
+}
+
+void AutofillWebDataService::RemoveAutofillDataModifiedBetween(
+ const Time& delete_begin,
+ const Time& delete_end) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::RemoveAutofillDataModifiedBetween,
+ autofill_backend_, delete_begin, delete_end));
+}
+
+void AutofillWebDataService::RemoveOriginURLsModifiedBetween(
+ const Time& delete_begin, const Time& delete_end) {
+ wdbs_->ScheduleDBTask(
+ FROM_HERE,
+ Bind(&AutofillWebDataBackendImpl::RemoveOriginURLsModifiedBetween,
+ autofill_backend_, delete_begin, delete_end));
+}
+
+void AutofillWebDataService::AddObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (autofill_backend_.get())
+ autofill_backend_->AddObserver(observer);
+}
+
+void AutofillWebDataService::RemoveObserver(
+ AutofillWebDataServiceObserverOnDBThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (autofill_backend_.get())
+ autofill_backend_->RemoveObserver(observer);
+}
+
+void AutofillWebDataService::AddObserver(
+ AutofillWebDataServiceObserverOnUIThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ui_observer_list_.AddObserver(observer);
+}
+
+void AutofillWebDataService::RemoveObserver(
+ AutofillWebDataServiceObserverOnUIThread* observer) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ui_observer_list_.RemoveObserver(observer);
+}
+
+base::SupportsUserData* AutofillWebDataService::GetDBUserData() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ return autofill_backend_->GetDBUserData();
+}
+
+void AutofillWebDataService::GetAutofillBackend(
+ const base::Callback<void(AutofillWebDataBackend*)>& callback) {
+ BrowserThread::PostTask(BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(callback, autofill_backend_));
+}
+
+AutofillWebDataService::~AutofillWebDataService() {
+}
+
+void AutofillWebDataService::NotifyAutofillMultipleChangedOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ FOR_EACH_OBSERVER(AutofillWebDataServiceObserverOnUIThread,
+ ui_observer_list_,
+ AutofillMultipleChanged());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
new file mode 100644
index 00000000000..59907fd13d0
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service.h
@@ -0,0 +1,126 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/supports_user_data.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+#include "components/webdata/common/web_database.h"
+
+class WebDatabaseService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace autofill {
+
+class AutofillChange;
+class AutofillProfile;
+class AutofillWebDataBackend;
+class AutofillWebDataBackendImpl;
+class AutofillWebDataServiceObserverOnDBThread;
+class AutofillWebDataServiceObserverOnUIThread;
+class CreditCard;
+
+// API for Autofill web data.
+class AutofillWebDataService : public AutofillWebData,
+ public WebDataServiceBase {
+ public:
+ AutofillWebDataService();
+
+ AutofillWebDataService(scoped_refptr<WebDatabaseService> wdbs,
+ const ProfileErrorCallback& callback);
+
+ // Retrieve an AutofillWebDataService for the given context.
+ // Can return NULL in some contexts.
+ static scoped_refptr<AutofillWebDataService> FromBrowserContext(
+ content::BrowserContext* context);
+
+ // WebDataServiceBase implementation.
+ virtual void ShutdownOnUIThread() OVERRIDE;
+
+ // AutofillWebData implementation.
+ virtual void AddFormFields(
+ const std::vector<FormFieldData>& fields) OVERRIDE;
+ virtual WebDataServiceBase::Handle GetFormValuesForElementName(
+ const base::string16& name,
+ const base::string16& prefix,
+ int limit,
+ WebDataServiceConsumer* consumer) OVERRIDE;
+
+ virtual WebDataServiceBase::Handle HasFormElements(
+ WebDataServiceConsumer* consumer) OVERRIDE;
+ virtual void RemoveFormElementsAddedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) OVERRIDE;
+ virtual void RemoveFormValueForElementName(
+ const base::string16& name,
+ const base::string16& value) OVERRIDE;
+ virtual void AddAutofillProfile(const AutofillProfile& profile) OVERRIDE;
+ virtual void UpdateAutofillProfile(const AutofillProfile& profile) OVERRIDE;
+ virtual void RemoveAutofillProfile(const std::string& guid) OVERRIDE;
+ virtual WebDataServiceBase::Handle GetAutofillProfiles(
+ WebDataServiceConsumer* consumer) OVERRIDE;
+ virtual void AddCreditCard(const CreditCard& credit_card) OVERRIDE;
+ virtual void UpdateCreditCard(const CreditCard& credit_card) OVERRIDE;
+ virtual void RemoveCreditCard(const std::string& guid) OVERRIDE;
+ virtual WebDataServiceBase::Handle GetCreditCards(
+ WebDataServiceConsumer* consumer) OVERRIDE;
+ virtual void RemoveAutofillDataModifiedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) OVERRIDE;
+ virtual void RemoveOriginURLsModifiedBetween(
+ const base::Time& delete_begin, const base::Time& delete_end) OVERRIDE;
+
+ void AddObserver(AutofillWebDataServiceObserverOnDBThread* observer);
+ void RemoveObserver(AutofillWebDataServiceObserverOnDBThread* observer);
+
+ void AddObserver(AutofillWebDataServiceObserverOnUIThread* observer);
+ void RemoveObserver(AutofillWebDataServiceObserverOnUIThread* observer);
+
+ // Returns a SupportsUserData objects that may be used to store data
+ // owned by the DB thread on this object. Should be called only from
+ // the DB thread, and will be destroyed on the DB thread soon after
+ // |ShutdownOnUIThread()| is called.
+ base::SupportsUserData* GetDBUserData();
+
+ // Takes a callback which will be called on the DB thread with a pointer to an
+ // |AutofillWebdataBackend|. This backend can be used to access or update the
+ // WebDatabase directly on the DB thread.
+ void GetAutofillBackend(
+ const base::Callback<void(AutofillWebDataBackend*)>& callback);
+
+ protected:
+ virtual ~AutofillWebDataService();
+
+ virtual void NotifyAutofillMultipleChangedOnUIThread();
+
+ base::WeakPtr<AutofillWebDataService> AsWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ private:
+ ObserverList<AutofillWebDataServiceObserverOnUIThread> ui_observer_list_;
+
+ // This factory is used on the UI thread. All vended weak pointers are
+ // invalidated in ShutdownOnUIThread().
+ base::WeakPtrFactory<AutofillWebDataService> weak_ptr_factory_;
+
+ scoped_refptr<AutofillWebDataBackendImpl> autofill_backend_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutofillWebDataService);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_H_
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h
new file mode 100644
index 00000000000..f4333e58bc6
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/autofill_webdata_service_observer.h
@@ -0,0 +1,37 @@
+// 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_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_OBSERVER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_OBSERVER_H_
+
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+
+namespace autofill {
+
+class AutofillWebDataServiceObserverOnDBThread {
+ public:
+ // Called on DB thread whenever Autofill entries are changed.
+ virtual void AutofillEntriesChanged(const AutofillChangeList& changes) {}
+
+ // Called on DB thread when an AutofillProfile has been added/removed/updated
+ // in the WebDatabase.
+ virtual void AutofillProfileChanged(const AutofillProfileChange& change) {}
+
+ protected:
+ virtual ~AutofillWebDataServiceObserverOnDBThread() {}
+};
+
+class AutofillWebDataServiceObserverOnUIThread {
+ public:
+ // Called on UI thread when multiple Autofill entries have been modified by
+ // Sync.
+ virtual void AutofillMultipleChanged() {}
+
+ protected:
+ virtual ~AutofillWebDataServiceObserverOnUIThread() {}
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_WEBDATA_SERVICE_OBSERVER_H_
diff --git a/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc b/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc
new file mode 100644
index 00000000000..ae7c25cb9d3
--- /dev/null
+++ b/chromium/components/autofill/core/browser/webdata/web_data_service_unittest.cc
@@ -0,0 +1,574 @@
+// 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 <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+#include "components/autofill/core/browser/webdata/autofill_webdata_service_observer.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+#include "components/webdata/common/web_database_service.h"
+#include "content/public/test/test_browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::Time;
+using base::TimeDelta;
+using base::WaitableEvent;
+using content::BrowserThread;
+using testing::_;
+using testing::DoDefault;
+using testing::ElementsAreArray;
+using testing::Pointee;
+using testing::Property;
+
+namespace {
+
+template <class T>
+class AutofillWebDataServiceConsumer: public WebDataServiceConsumer {
+ public:
+ AutofillWebDataServiceConsumer() : handle_(0) {}
+ virtual ~AutofillWebDataServiceConsumer() {}
+
+ virtual void OnWebDataServiceRequestDone(WebDataService::Handle handle,
+ const WDTypedResult* result) {
+ using content::BrowserThread;
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ handle_ = handle;
+ const WDResult<T>* wrapped_result =
+ static_cast<const WDResult<T>*>(result);
+ result_ = wrapped_result->GetValue();
+
+ base::MessageLoop::current()->Quit();
+ }
+
+ WebDataService::Handle handle() { return handle_; }
+ T& result() { return result_; }
+
+ private:
+ WebDataService::Handle handle_;
+ T result_;
+ DISALLOW_COPY_AND_ASSIGN(AutofillWebDataServiceConsumer);
+};
+
+} // namespace
+
+namespace autofill {
+
+static const int kWebDataServiceTimeoutSeconds = 8;
+
+ACTION_P(SignalEvent, event) {
+ event->Signal();
+}
+
+class MockAutofillWebDataServiceObserver
+ : public AutofillWebDataServiceObserverOnDBThread {
+ public:
+ MOCK_METHOD1(AutofillEntriesChanged,
+ void(const AutofillChangeList& changes));
+ MOCK_METHOD1(AutofillProfileChanged,
+ void(const AutofillProfileChange& change));
+};
+
+class WebDataServiceTest : public testing::Test {
+ public:
+ WebDataServiceTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ db_thread_(BrowserThread::DB) {}
+
+ protected:
+ virtual void SetUp() {
+ db_thread_.Start();
+
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ base::FilePath path = temp_dir_.path().AppendASCII("TestWebDB");
+
+ wdbs_ = new WebDatabaseService(
+ path,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB));
+ wdbs_->AddTable(scoped_ptr<WebDatabaseTable>(new AutofillTable("en-US")));
+ wdbs_->LoadDatabase();
+
+ wds_ = new AutofillWebDataService(
+ wdbs_, WebDataServiceBase::ProfileErrorCallback());
+ wds_->Init();
+ }
+
+ virtual void TearDown() {
+ wds_->ShutdownOnUIThread();
+ wdbs_->ShutdownDatabase();
+ wds_ = NULL;
+ wdbs_ = NULL;
+ WaitForDatabaseThread();
+
+ base::MessageLoop::current()->PostTask(FROM_HERE,
+ base::MessageLoop::QuitClosure());
+ base::MessageLoop::current()->Run();
+ db_thread_.Stop();
+ }
+
+ void WaitForDatabaseThread() {
+ base::WaitableEvent done(false, false);
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
+ done.Wait();
+ }
+
+ base::MessageLoopForUI message_loop_;
+ content::TestBrowserThread ui_thread_;
+ content::TestBrowserThread db_thread_;
+ base::FilePath profile_dir_;
+ scoped_refptr<AutofillWebDataService> wds_;
+ scoped_refptr<WebDatabaseService> wdbs_;
+ base::ScopedTempDir temp_dir_;
+};
+
+class WebDataServiceAutofillTest : public WebDataServiceTest {
+ public:
+ WebDataServiceAutofillTest()
+ : WebDataServiceTest(),
+ unique_id1_(1),
+ unique_id2_(2),
+ test_timeout_(TimeDelta::FromSeconds(kWebDataServiceTimeoutSeconds)),
+ done_event_(false, false) {}
+
+ protected:
+ virtual void SetUp() {
+ WebDataServiceTest::SetUp();
+ name1_ = ASCIIToUTF16("name1");
+ name2_ = ASCIIToUTF16("name2");
+ value1_ = ASCIIToUTF16("value1");
+ value2_ = ASCIIToUTF16("value2");
+
+ void(AutofillWebDataService::*add_observer_func)(
+ AutofillWebDataServiceObserverOnDBThread*) =
+ &AutofillWebDataService::AddObserver;
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(add_observer_func, wds_, &observer_));
+ WaitForDatabaseThread();
+ }
+
+ virtual void TearDown() {
+ void(AutofillWebDataService::*remove_observer_func)(
+ AutofillWebDataServiceObserverOnDBThread*) =
+ &AutofillWebDataService::RemoveObserver;
+ BrowserThread::PostTask(
+ BrowserThread::DB,
+ FROM_HERE,
+ base::Bind(remove_observer_func, wds_, &observer_));
+ WaitForDatabaseThread();
+
+ WebDataServiceTest::TearDown();
+ }
+
+ void AppendFormField(const base::string16& name,
+ const base::string16& value,
+ std::vector<FormFieldData>* form_fields) {
+ FormFieldData field;
+ field.name = name;
+ field.value = value;
+ form_fields->push_back(field);
+ }
+
+ base::string16 name1_;
+ base::string16 name2_;
+ base::string16 value1_;
+ base::string16 value2_;
+ int unique_id1_, unique_id2_;
+ const TimeDelta test_timeout_;
+ testing::NiceMock<MockAutofillWebDataServiceObserver> observer_;
+ WaitableEvent done_event_;
+};
+
+// Simple consumer for Keywords data. Stores the result data and quits UI
+// message loop when callback is invoked.
+class KeywordsConsumer : public WebDataServiceConsumer {
+ public:
+ KeywordsConsumer() : load_succeeded(false) {}
+
+ virtual void OnWebDataServiceRequestDone(
+ WebDataService::Handle h,
+ const WDTypedResult* result) OVERRIDE {
+ if (result) {
+ load_succeeded = true;
+ DCHECK(result->GetType() == KEYWORDS_RESULT);
+ keywords_result =
+ reinterpret_cast<const WDResult<WDKeywordsResult>*>(
+ result)->GetValue();
+ }
+
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ base::MessageLoop::current()->Quit();
+ }
+
+ // True if keywords data was loaded successfully.
+ bool load_succeeded;
+ // Result data from completion callback.
+ WDKeywordsResult keywords_result;
+};
+
+TEST_F(WebDataServiceAutofillTest, FormFillAdd) {
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::ADD, AutofillKey(name1_, value1_)),
+ AutofillChange(AutofillChange::ADD, AutofillKey(name2_, value2_))
+ };
+
+ // This will verify that the correct notification is triggered,
+ // passing the correct list of autofill keys in the details.
+ EXPECT_CALL(observer_,
+ AutofillEntriesChanged(ElementsAreArray(expected_changes)))
+ .WillOnce(SignalEvent(&done_event_));
+
+ std::vector<FormFieldData> form_fields;
+ AppendFormField(name1_, value1_, &form_fields);
+ AppendFormField(name2_, value2_, &form_fields);
+ wds_->AddFormFields(form_fields);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+
+ AutofillWebDataServiceConsumer<std::vector<base::string16> > consumer;
+ WebDataService::Handle handle;
+ static const int limit = 10;
+ handle = wds_->GetFormValuesForElementName(
+ name1_, base::string16(), limit, &consumer);
+
+ // The message loop will exit when the consumer is called.
+ base::MessageLoop::current()->Run();
+
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(value1_, consumer.result()[0]);
+}
+
+TEST_F(WebDataServiceAutofillTest, FormFillRemoveOne) {
+ // First add some values to autofill.
+ EXPECT_CALL(observer_, AutofillEntriesChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+ std::vector<FormFieldData> form_fields;
+ AppendFormField(name1_, value1_, &form_fields);
+ wds_->AddFormFields(form_fields);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+
+ // This will verify that the correct notification is triggered,
+ // passing the correct list of autofill keys in the details.
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_))
+ };
+ EXPECT_CALL(observer_,
+ AutofillEntriesChanged(ElementsAreArray(expected_changes)))
+ .WillOnce(SignalEvent(&done_event_));
+ wds_->RemoveFormValueForElementName(name1_, value1_);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+}
+
+TEST_F(WebDataServiceAutofillTest, FormFillRemoveMany) {
+ TimeDelta one_day(TimeDelta::FromDays(1));
+ Time t = Time::Now();
+
+ EXPECT_CALL(observer_, AutofillEntriesChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+
+ std::vector<FormFieldData> form_fields;
+ AppendFormField(name1_, value1_, &form_fields);
+ AppendFormField(name2_, value2_, &form_fields);
+ wds_->AddFormFields(form_fields);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+
+ // This will verify that the correct notification is triggered,
+ // passing the correct list of autofill keys in the details.
+ const AutofillChange expected_changes[] = {
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name1_, value1_)),
+ AutofillChange(AutofillChange::REMOVE, AutofillKey(name2_, value2_))
+ };
+ EXPECT_CALL(observer_,
+ AutofillEntriesChanged(ElementsAreArray(expected_changes)))
+ .WillOnce(SignalEvent(&done_event_));
+ wds_->RemoveFormElementsAddedBetween(t, t + one_day);
+
+ // The event will be signaled when the mock observer is notified.
+ done_event_.TimedWait(test_timeout_);
+}
+
+TEST_F(WebDataServiceAutofillTest, ProfileAdd) {
+ AutofillProfile profile;
+
+ // Check that GUID-based notification was sent.
+ const AutofillProfileChange expected_change(
+ AutofillProfileChange::ADD, profile.guid(), &profile);
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ wds_->AddAutofillProfile(profile);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(profile, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, ProfileRemove) {
+ AutofillProfile profile;
+
+ // Add a profile.
+ EXPECT_CALL(observer_, AutofillProfileChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+ wds_->AddAutofillProfile(profile);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(profile, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+
+ // Check that GUID-based notification was sent.
+ const AutofillProfileChange expected_change(
+ AutofillProfileChange::REMOVE, profile.guid(), NULL);
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ // Remove the profile.
+ wds_->RemoveAutofillProfile(profile.guid());
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was removed.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetAutofillProfiles(&consumer2);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(0U, consumer2.result().size());
+}
+
+TEST_F(WebDataServiceAutofillTest, ProfileUpdate) {
+ AutofillProfile profile1;
+ profile1.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Abe"));
+ AutofillProfile profile2;
+ profile2.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Alice"));
+
+ EXPECT_CALL(observer_, AutofillProfileChanged(_))
+ .WillOnce(DoDefault())
+ .WillOnce(SignalEvent(&done_event_));
+
+ wds_->AddAutofillProfile(profile1);
+ wds_->AddAutofillProfile(profile2);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that they were added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(2U, consumer.result().size());
+ EXPECT_EQ(profile1, *consumer.result()[0]);
+ EXPECT_EQ(profile2, *consumer.result()[1]);
+ STLDeleteElements(&consumer.result());
+
+ AutofillProfile profile1_changed(profile1);
+ profile1_changed.SetRawInfo(NAME_FIRST, ASCIIToUTF16("Bill"));
+ const AutofillProfileChange expected_change(
+ AutofillProfileChange::UPDATE, profile1.guid(), &profile1_changed);
+
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ // Update the profile.
+ wds_->UpdateAutofillProfile(profile1_changed);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that the updates were made.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetAutofillProfiles(&consumer2);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(2U, consumer2.result().size());
+ EXPECT_NE(profile1, *consumer2.result()[0]);
+ EXPECT_EQ(profile1_changed, *consumer2.result()[0]);
+ EXPECT_EQ(profile2, *consumer2.result()[1]);
+ STLDeleteElements(&consumer2.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, CreditAdd) {
+ CreditCard card;
+ wds_->AddCreditCard(card);
+ WaitForDatabaseThread();
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer;
+ WebDataService::Handle handle = wds_->GetCreditCards(&consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(card, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, CreditCardRemove) {
+ CreditCard credit_card;
+
+ // Add a credit card.
+ wds_->AddCreditCard(credit_card);
+ WaitForDatabaseThread();
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer;
+ WebDataService::Handle handle = wds_->GetCreditCards(&consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(1U, consumer.result().size());
+ EXPECT_EQ(credit_card, *consumer.result()[0]);
+ STLDeleteElements(&consumer.result());
+
+ // Remove the credit card.
+ wds_->RemoveCreditCard(credit_card.guid());
+ WaitForDatabaseThread();
+
+ // Check that it was removed.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(0U, consumer2.result().size());
+}
+
+TEST_F(WebDataServiceAutofillTest, CreditUpdate) {
+ CreditCard card1;
+ card1.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Abe"));
+ CreditCard card2;
+ card2.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Alice"));
+
+ wds_->AddCreditCard(card1);
+ wds_->AddCreditCard(card2);
+ WaitForDatabaseThread();
+
+ // Check that they got added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer;
+ WebDataService::Handle handle = wds_->GetCreditCards(&consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, consumer.handle());
+ ASSERT_EQ(2U, consumer.result().size());
+ EXPECT_EQ(card1, *consumer.result()[0]);
+ EXPECT_EQ(card2, *consumer.result()[1]);
+ STLDeleteElements(&consumer.result());
+
+ CreditCard card1_changed(card1);
+ card1_changed.SetRawInfo(CREDIT_CARD_NAME, ASCIIToUTF16("Bill"));
+
+ wds_->UpdateCreditCard(card1_changed);
+ WaitForDatabaseThread();
+
+ // Check that the updates were made.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > consumer2;
+ WebDataService::Handle handle2 = wds_->GetCreditCards(&consumer2);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, consumer2.handle());
+ ASSERT_EQ(2U, consumer2.result().size());
+ EXPECT_NE(card1, *consumer2.result()[0]);
+ EXPECT_EQ(card1_changed, *consumer2.result()[0]);
+ EXPECT_EQ(card2, *consumer2.result()[1]);
+ STLDeleteElements(&consumer2.result());
+}
+
+TEST_F(WebDataServiceAutofillTest, AutofillRemoveModifiedBetween) {
+ // Add a profile.
+ EXPECT_CALL(observer_, AutofillProfileChanged(_))
+ .WillOnce(SignalEvent(&done_event_));
+ AutofillProfile profile;
+ wds_->AddAutofillProfile(profile);
+ done_event_.TimedWait(test_timeout_);
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> >
+ profile_consumer;
+ WebDataService::Handle handle = wds_->GetAutofillProfiles(&profile_consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, profile_consumer.handle());
+ ASSERT_EQ(1U, profile_consumer.result().size());
+ EXPECT_EQ(profile, *profile_consumer.result()[0]);
+ STLDeleteElements(&profile_consumer.result());
+
+ // Add a credit card.
+ CreditCard credit_card;
+ wds_->AddCreditCard(credit_card);
+ WaitForDatabaseThread();
+
+ // Check that it was added.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > card_consumer;
+ handle = wds_->GetCreditCards(&card_consumer);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle, card_consumer.handle());
+ ASSERT_EQ(1U, card_consumer.result().size());
+ EXPECT_EQ(credit_card, *card_consumer.result()[0]);
+ STLDeleteElements(&card_consumer.result());
+
+ // Check that GUID-based notification was sent for the profile.
+ const AutofillProfileChange expected_profile_change(
+ AutofillProfileChange::REMOVE, profile.guid(), NULL);
+ EXPECT_CALL(observer_, AutofillProfileChanged(expected_profile_change))
+ .WillOnce(SignalEvent(&done_event_));
+
+ // Remove the profile using time range of "all time".
+ wds_->RemoveAutofillDataModifiedBetween(Time(), Time());
+ done_event_.TimedWait(test_timeout_);
+ WaitForDatabaseThread();
+
+ // Check that the profile was removed.
+ AutofillWebDataServiceConsumer<std::vector<AutofillProfile*> >
+ profile_consumer2;
+ WebDataService::Handle handle2 =
+ wds_->GetAutofillProfiles(&profile_consumer2);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, profile_consumer2.handle());
+ ASSERT_EQ(0U, profile_consumer2.result().size());
+
+ // Check that the credit card was removed.
+ AutofillWebDataServiceConsumer<std::vector<CreditCard*> > card_consumer2;
+ handle2 = wds_->GetCreditCards(&card_consumer2);
+ base::MessageLoop::current()->Run();
+ EXPECT_EQ(handle2, card_consumer2.handle());
+ ASSERT_EQ(0U, card_consumer2.result().size());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/OWNERS b/chromium/components/autofill/core/common/OWNERS
new file mode 100644
index 00000000000..0c955d37e53
--- /dev/null
+++ b/chromium/components/autofill/core/common/OWNERS
@@ -0,0 +1,9 @@
+# Changes to IPC messages require a security review to avoid introducing
+# new sandbox escapes.
+per-file autofill_messages*.h=set noparent
+per-file autofill_messages*.h=cdn@chromium.org
+per-file autofill_messages*.h=cevans@chromium.org
+per-file autofill_messages*.h=jln@chromium.org
+per-file autofill_messages*.h=jschuh@chromium.org
+per-file autofill_messages*.h=palmer@chromium.org
+per-file autofill_messages*.h=tsepez@chromium.org
diff --git a/chromium/components/autofill/core/common/autocheckout_status.h b/chromium/components/autofill/core/common/autocheckout_status.h
new file mode 100644
index 00000000000..81bc7005e80
--- /dev/null
+++ b/chromium/components/autofill/core/common/autocheckout_status.h
@@ -0,0 +1,22 @@
+// 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_CORE_COMMON_AUTOCHECKOUT_STATUS_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOCHECKOUT_STATUS_H_
+
+namespace autofill {
+
+enum AutocheckoutStatus {
+ SUCCESS,
+ MISSING_FIELDMAPPING,
+ MISSING_ADVANCE,
+ MISSING_CLICK_ELEMENT_BEFORE_FORM_FILLING,
+ MISSING_CLICK_ELEMENT_AFTER_FORM_FILLING,
+ CANNOT_PROCEED,
+ AUTOCHECKOUT_STATUS_NUM_STATUS,
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOCHECKOUT_STATUS_H_
diff --git a/chromium/components/autofill/core/common/autofill_constants.cc b/chromium/components/autofill/core/common/autofill_constants.cc
new file mode 100644
index 00000000000..ed4159af7ec
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_constants.cc
@@ -0,0 +1,20 @@
+// 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/core/common/autofill_constants.h"
+
+#include "build/build_config.h"
+
+namespace autofill {
+
+const char kHelpURL[] =
+#if defined(OS_CHROMEOS)
+ "https://support.google.com/chromeos/?p=settings_autofill";
+#else
+ "https://support.google.com/chrome/?p=settings_autofill";
+#endif
+
+const size_t kRequiredAutofillFields = 3;
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/autofill_constants.h b/chromium/components/autofill/core/common/autofill_constants.h
new file mode 100644
index 00000000000..9386b45f3c3
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_constants.h
@@ -0,0 +1,25 @@
+// 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.
+
+// Contains constants specific to the Autofill component.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_CONSTANTS_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_CONSTANTS_H_
+
+#include <stddef.h> // For size_t
+
+namespace autofill {
+
+// Help URL for the Autofill dialog.
+extern const char kHelpURL[];
+
+// 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.
+extern const size_t kRequiredAutofillFields;
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_CONSTANTS_H_
diff --git a/chromium/components/autofill/core/common/autofill_message_generator.cc b/chromium/components/autofill/core/common/autofill_message_generator.cc
new file mode 100644
index 00000000000..48988146ab4
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_message_generator.cc
@@ -0,0 +1,33 @@
+// 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.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "components/autofill/core/common/autofill_message_generator.h"
+
+// Generate constructors.
+#include "ipc/struct_constructor_macros.h"
+#include "components/autofill/core/common/autofill_message_generator.h"
+
+// Generate destructors.
+#include "ipc/struct_destructor_macros.h"
+#include "components/autofill/core/common/autofill_message_generator.h"
+
+// Generate param traits write methods.
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#include "components/autofill/core/common/autofill_message_generator.h"
+} // namespace IPC
+
+// Generate param traits read methods.
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#include "components/autofill/core/common/autofill_message_generator.h"
+} // namespace IPC
+
+// Generate param traits log methods.
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#include "components/autofill/core/common/autofill_message_generator.h"
+} // namespace IPC
diff --git a/chromium/components/autofill/core/common/autofill_message_generator.h b/chromium/components/autofill/core/common/autofill_message_generator.h
new file mode 100644
index 00000000000..31a29649550
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_message_generator.h
@@ -0,0 +1,7 @@
+// 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.
+
+// Multiply-included file, hence no include guard.
+
+#include "components/autofill/core/common/autofill_messages.h"
diff --git a/chromium/components/autofill/core/common/autofill_messages.h b/chromium/components/autofill/core/common/autofill_messages.h
new file mode 100644
index 00000000000..13524dfd7d2
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_messages.h
@@ -0,0 +1,297 @@
+// 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.
+
+// Multiply-included message file, hence no include guard.
+
+#include <string>
+
+#include "base/time/time.h"
+#include "components/autofill/core/common/autocheckout_status.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_data_predictions.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/form_field_data_predictions.h"
+#include "components/autofill/core/common/forms_seen_state.h"
+#include "components/autofill/core/common/password_form_fill_data.h"
+#include "components/autofill/core/common/web_element_descriptor.h"
+#include "content/public/common/common_param_traits.h"
+#include "content/public/common/common_param_traits_macros.h"
+#include "content/public/common/password_form.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_message_utils.h"
+#include "third_party/WebKit/public/web/WebFormElement.h"
+#include "ui/gfx/rect.h"
+#include "url/gurl.h"
+
+#define IPC_MESSAGE_START AutofillMsgStart
+
+IPC_ENUM_TRAITS_MAX_VALUE(autofill::AutocheckoutStatus,
+ autofill::AUTOCHECKOUT_STATUS_NUM_STATUS - 1)
+IPC_ENUM_TRAITS_MAX_VALUE(autofill::FormsSeenState,
+ autofill::FORMS_SEEN_STATE_NUM_STATES - 1)
+IPC_ENUM_TRAITS_MAX_VALUE(base::i18n::TextDirection,
+ base::i18n::TEXT_DIRECTION_NUM_DIRECTIONS - 1)
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::WebElementDescriptor)
+ IPC_STRUCT_TRAITS_MEMBER(descriptor)
+ IPC_STRUCT_TRAITS_MEMBER(retrieval_method)
+IPC_STRUCT_TRAITS_END()
+
+IPC_ENUM_TRAITS_MAX_VALUE(autofill::WebElementDescriptor::RetrievalMethod,
+ autofill::WebElementDescriptor::NONE)
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldData)
+ IPC_STRUCT_TRAITS_MEMBER(label)
+ IPC_STRUCT_TRAITS_MEMBER(name)
+ IPC_STRUCT_TRAITS_MEMBER(value)
+ IPC_STRUCT_TRAITS_MEMBER(form_control_type)
+ IPC_STRUCT_TRAITS_MEMBER(autocomplete_attribute)
+ IPC_STRUCT_TRAITS_MEMBER(max_length)
+ IPC_STRUCT_TRAITS_MEMBER(is_autofilled)
+ IPC_STRUCT_TRAITS_MEMBER(is_checked)
+ IPC_STRUCT_TRAITS_MEMBER(is_checkable)
+ IPC_STRUCT_TRAITS_MEMBER(is_focusable)
+ IPC_STRUCT_TRAITS_MEMBER(should_autocomplete)
+ IPC_STRUCT_TRAITS_MEMBER(text_direction)
+ IPC_STRUCT_TRAITS_MEMBER(option_values)
+ IPC_STRUCT_TRAITS_MEMBER(option_contents)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldDataPredictions)
+ IPC_STRUCT_TRAITS_MEMBER(field)
+ IPC_STRUCT_TRAITS_MEMBER(signature)
+ IPC_STRUCT_TRAITS_MEMBER(heuristic_type)
+ IPC_STRUCT_TRAITS_MEMBER(server_type)
+ IPC_STRUCT_TRAITS_MEMBER(overall_type)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::FormData)
+ IPC_STRUCT_TRAITS_MEMBER(name)
+ IPC_STRUCT_TRAITS_MEMBER(method)
+ IPC_STRUCT_TRAITS_MEMBER(origin)
+ IPC_STRUCT_TRAITS_MEMBER(action)
+ IPC_STRUCT_TRAITS_MEMBER(user_submitted)
+ IPC_STRUCT_TRAITS_MEMBER(fields)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::FormDataPredictions)
+ IPC_STRUCT_TRAITS_MEMBER(data)
+ IPC_STRUCT_TRAITS_MEMBER(signature)
+ IPC_STRUCT_TRAITS_MEMBER(experiment_id)
+ IPC_STRUCT_TRAITS_MEMBER(fields)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::UsernamesCollectionKey)
+ IPC_STRUCT_TRAITS_MEMBER(username)
+ IPC_STRUCT_TRAITS_MEMBER(password)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::PasswordFormFillData)
+ IPC_STRUCT_TRAITS_MEMBER(basic_data)
+ IPC_STRUCT_TRAITS_MEMBER(preferred_realm)
+ IPC_STRUCT_TRAITS_MEMBER(additional_logins)
+ IPC_STRUCT_TRAITS_MEMBER(other_possible_usernames)
+ IPC_STRUCT_TRAITS_MEMBER(wait_for_username)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(autofill::PasswordAndRealm)
+ IPC_STRUCT_TRAITS_MEMBER(password)
+ IPC_STRUCT_TRAITS_MEMBER(realm)
+IPC_STRUCT_TRAITS_END()
+
+IPC_ENUM_TRAITS_MAX_VALUE(
+ WebKit::WebFormElement::AutocompleteResult,
+ WebKit::WebFormElement::AutocompleteResultErrorInvalid)
+
+// Autofill messages sent from the browser to the renderer.
+
+// Request to parse all the forms without field count limit.
+IPC_MESSAGE_ROUTED0(AutofillMsg_GetAllForms)
+
+// Reply to the AutofillHostMsg_FillAutofillFormData message with the
+// Autofill form data.
+IPC_MESSAGE_ROUTED2(AutofillMsg_FormDataFilled,
+ int /* id of the request message */,
+ autofill::FormData /* form data */)
+
+// Fill a password form and prepare field autocomplete for multiple
+// matching logins. Lets the renderer know if it should disable the popup
+// because the browser process will own the popup UI.
+IPC_MESSAGE_ROUTED1(AutofillMsg_FillPasswordForm,
+ autofill::PasswordFormFillData /* the fill form data*/)
+
+// Send the heuristic and server field type predictions to the renderer.
+IPC_MESSAGE_ROUTED1(
+ AutofillMsg_FieldTypePredictionsAvailable,
+ std::vector<autofill::FormDataPredictions> /* forms */)
+
+// Tells the renderer that the next form will be filled for real.
+IPC_MESSAGE_ROUTED0(AutofillMsg_SetAutofillActionFill)
+
+// Clears the currently displayed Autofill results.
+IPC_MESSAGE_ROUTED0(AutofillMsg_ClearForm)
+
+// Tells the renderer that the next form will be filled as a preview.
+IPC_MESSAGE_ROUTED0(AutofillMsg_SetAutofillActionPreview)
+
+// Tells the renderer that the Autofill previewed form should be cleared.
+IPC_MESSAGE_ROUTED0(AutofillMsg_ClearPreviewedForm)
+
+// Sets the currently selected node's value.
+IPC_MESSAGE_ROUTED1(AutofillMsg_SetNodeText,
+ base::string16 /* new node text */)
+
+// Sets the currently selected node's value to be the given data list value.
+IPC_MESSAGE_ROUTED1(AutofillMsg_AcceptDataListSuggestion,
+ base::string16 /* accepted data list value */)
+
+// Tells the renderer to populate the correct password fields with this
+// generated password.
+IPC_MESSAGE_ROUTED1(AutofillMsg_GeneratedPasswordAccepted,
+ base::string16 /* generated_password */)
+
+// Tells the renderer whether password generation is enabled.
+IPC_MESSAGE_ROUTED1(AutofillMsg_PasswordGenerationEnabled,
+ bool /* is_enabled */)
+
+// Tells the renderer that the password field has accept the suggestion.
+IPC_MESSAGE_ROUTED1(AutofillMsg_AcceptPasswordAutofillSuggestion,
+ base::string16 /* username value*/)
+
+// Tells the renderer that this password form is not blacklisted. A form can
+// be blacklisted if a user chooses "never save passwords for this site".
+IPC_MESSAGE_ROUTED1(AutofillMsg_FormNotBlacklisted,
+ content::PasswordForm /* form checked */)
+
+// Sent when requestAutocomplete() finishes (either succesfully or with an
+// error). If it was a success, the renderer fills the form that requested
+// autocomplete with the |form_data| values input by the user.
+IPC_MESSAGE_ROUTED2(AutofillMsg_RequestAutocompleteResult,
+ WebKit::WebFormElement::AutocompleteResult /* result */,
+ autofill::FormData /* form_data */)
+
+// Sent when a page should be filled using Autocheckout. This happens when the
+// Autofill server hints that a page is Autocheckout enabled.
+IPC_MESSAGE_ROUTED4(AutofillMsg_FillFormsAndClick,
+ std::vector<autofill::FormData> /* form_data */,
+ std::vector<autofill::WebElementDescriptor> /*
+ click_elements_before_form_fill */,
+ std::vector<autofill::WebElementDescriptor> /*
+ click_elements_after_form_fill */,
+ autofill::WebElementDescriptor /* element_descriptor */)
+
+// Sent when Autocheckout is supported for the current page. The page has to
+// be whitelisted and the Autofill server must have returned Autocheckout page
+// metadata.
+IPC_MESSAGE_ROUTED0(AutofillMsg_AutocheckoutSupported)
+
+// Sent when the current page is actually displayed in the browser, possibly
+// after being preloaded.
+IPC_MESSAGE_ROUTED0(AutofillMsg_PageShown)
+
+// Autofill messages sent from the renderer to the browser.
+
+// TODO(creis): check in the browser that the renderer actually has permission
+// for the URL to avoid compromised renderers talking to the browser.
+
+// Notification that forms have been seen that are candidates for
+// filling/submitting by the AutofillManager.
+IPC_MESSAGE_ROUTED3(AutofillHostMsg_FormsSeen,
+ std::vector<autofill::FormData> /* forms */,
+ base::TimeTicks /* timestamp */,
+ autofill::FormsSeenState /* state */)
+
+// Notification that password forms have been seen that are candidates for
+// filling/submitting by the password manager.
+IPC_MESSAGE_ROUTED1(AutofillHostMsg_PasswordFormsParsed,
+ std::vector<content::PasswordForm> /* forms */)
+
+// Notification that initial layout has occurred and the following password
+// forms are visible on the page (e.g. not set to display:none.)
+IPC_MESSAGE_ROUTED1(AutofillHostMsg_PasswordFormsRendered,
+ std::vector<content::PasswordForm> /* forms */)
+
+// Notification that a form has been submitted. The user hit the button.
+IPC_MESSAGE_ROUTED2(AutofillHostMsg_FormSubmitted,
+ autofill::FormData /* form */,
+ base::TimeTicks /* timestamp */)
+
+// Notification that a form field's value has changed.
+IPC_MESSAGE_ROUTED3(AutofillHostMsg_TextFieldDidChange,
+ autofill::FormData /* the form */,
+ autofill::FormFieldData /* the form field */,
+ base::TimeTicks /* timestamp */)
+
+// Shows the Autocheckout bubble if the conditions are right.
+IPC_MESSAGE_ROUTED2(AutofillHostMsg_MaybeShowAutocheckoutBubble,
+ autofill::FormData /* form */,
+ gfx::RectF /* bounding_box */)
+
+// Queries the browser for Autofill suggestions for a form input field.
+IPC_MESSAGE_ROUTED5(AutofillHostMsg_QueryFormFieldAutofill,
+ int /* id of this message */,
+ autofill::FormData /* the form */,
+ autofill::FormFieldData /* the form field */,
+ gfx::RectF /* input field bounds, window-relative */,
+ bool /* display warning if autofill disabled */)
+
+// Instructs the browser to fill in the values for a form using Autofill
+// profile data.
+IPC_MESSAGE_ROUTED4(AutofillHostMsg_FillAutofillFormData,
+ int /* id of this message */,
+ autofill::FormData /* the form */,
+ autofill::FormFieldData /* the form field */,
+ int /* profile unique ID */)
+
+// Sent when a form is previewed with Autofill suggestions.
+IPC_MESSAGE_ROUTED0(AutofillHostMsg_DidPreviewAutofillFormData)
+
+// Sent when a form is filled with Autofill suggestions.
+IPC_MESSAGE_ROUTED1(AutofillHostMsg_DidFillAutofillFormData,
+ base::TimeTicks /* timestamp */)
+
+// Sent when a form receives a request to do interactive autocomplete.
+IPC_MESSAGE_ROUTED2(AutofillHostMsg_RequestAutocomplete,
+ autofill::FormData /* form_data */,
+ GURL /* frame_url */)
+
+// Instructs the browser to show the Autofill dialog.
+IPC_MESSAGE_ROUTED0(AutofillHostMsg_ShowAutofillDialog)
+
+// Send when a text field is done editing.
+IPC_MESSAGE_ROUTED0(AutofillHostMsg_DidEndTextFieldEditing)
+
+// Instructs the browser to hide the Autofill UI.
+IPC_MESSAGE_ROUTED0(AutofillHostMsg_HideAutofillUI)
+
+// Sent when the renderer filled an Autocheckout page and clicked the proceed
+// button or if there was an error.
+IPC_MESSAGE_ROUTED1(AutofillHostMsg_AutocheckoutPageCompleted,
+ autofill::AutocheckoutStatus /* status */)
+
+// Instructs the browser to show the password generation bubble at the
+// specified location. This location should be specified in the renderers
+// coordinate system. Form is the form associated with the password field.
+IPC_MESSAGE_ROUTED3(AutofillHostMsg_ShowPasswordGenerationPopup,
+ gfx::Rect /* source location */,
+ int /* max length of the password */,
+ content::PasswordForm)
+
+// Instruct the browser that a password mapping has been found for a field.
+IPC_MESSAGE_ROUTED2(AutofillHostMsg_AddPasswordFormMapping,
+ autofill::FormFieldData, /* the user name field */
+ autofill::PasswordFormFillData /* password pairings */)
+
+// Instruct the browser to show a popup with the following suggestions from the
+// password manager.
+IPC_MESSAGE_ROUTED4(AutofillHostMsg_ShowPasswordSuggestions,
+ autofill::FormFieldData /* the form field */,
+ gfx::RectF /* input field bounds, window-relative */,
+ std::vector<base::string16> /* suggestions */,
+ std::vector<base::string16> /* realms */)
+
+// Inform browser of data list values for the curent field.
+IPC_MESSAGE_ROUTED2(AutofillHostMsg_SetDataList,
+ std::vector<base::string16> /* values */,
+ std::vector<base::string16> /* labels */)
diff --git a/chromium/components/autofill/core/common/autofill_pref_names.cc b/chromium/components/autofill/core/common/autofill_pref_names.cc
new file mode 100644
index 00000000000..622783c0540
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_pref_names.cc
@@ -0,0 +1,25 @@
+// 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/core/common/autofill_pref_names.h"
+
+namespace autofill {
+namespace prefs {
+
+// Boolean that is true when auxiliary Autofill profiles are enabled.
+// Currently applies to Address Book "me" card on Mac. False on Win and Linux.
+const char kAutofillAuxiliaryProfilesEnabled[] =
+ "autofill.auxiliary_profiles_enabled";
+
+// Boolean that is true if Autofill is enabled and allowed to save profile data.
+const char kAutofillEnabled[] = "autofill.enabled";
+
+// Double that indicates negative (for not matched forms) upload rate.
+const char kAutofillNegativeUploadRate[] = "autofill.negative_upload_rate";
+
+// Double that indicates positive (for matched forms) upload rate.
+const char kAutofillPositiveUploadRate[] = "autofill.positive_upload_rate";
+
+} // namespace prefs
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/autofill_pref_names.h b/chromium/components/autofill/core/common/autofill_pref_names.h
new file mode 100644
index 00000000000..e52cc7285f8
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_pref_names.h
@@ -0,0 +1,21 @@
+// 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_CORE_COMMON_AUTOFILL_PREF_NAMES_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PREF_NAMES_H_
+
+namespace autofill {
+namespace prefs {
+
+// Alphabetical list of preference names specific to the Autofill
+// component. Keep alphabetized, and document each in the .cc file.
+extern const char kAutofillAuxiliaryProfilesEnabled[];
+extern const char kAutofillEnabled[];
+extern const char kAutofillNegativeUploadRate[];
+extern const char kAutofillPositiveUploadRate[];
+
+} // namespace prefs
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PREF_NAMES_H_
diff --git a/chromium/components/autofill/core/common/autofill_switches.cc b/chromium/components/autofill/core/common/autofill_switches.cc
new file mode 100644
index 00000000000..a41a9c7b96a
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_switches.cc
@@ -0,0 +1,49 @@
+// 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/core/common/autofill_switches.h"
+
+namespace autofill {
+namespace switches {
+
+// Flag used to tell Chrome the Autochecout whitelist url.
+const char kAutocheckoutWhitelistUrl[] = "autocheckout-whitelist-url";
+
+// Flag used to tell Chrome the base url of the Autofill service.
+const char kAutofillServiceUrl[] = "autofill-service-url";
+
+// Bypass autocheckout whitelist check, so all sites are enabled.
+const char kBypassAutocheckoutWhitelist[] = "bypass-autocheckout-whitelist";
+
+// Disables an interactive autocomplete UI. See kEnableInteractiveAutocomplete
+// for a description.
+const char kDisableInteractiveAutocomplete[] =
+ "disable-interactive-autocomplete";
+
+// Enable autofill for new elements like checkboxes. crbug.com/157636
+const char kEnableExperimentalFormFilling[] =
+ "enable-experimental-form-filling";
+
+// Enables an interactive autocomplete UI and a way to invoke this UI from
+// WebKit by enabling HTMLFormElement#requestAutocomplete (and associated
+// autocomplete* events and logic).
+const char kEnableInteractiveAutocomplete[] = "enable-interactive-autocomplete";
+
+// Annotates forms with Autofill field type predictions.
+const char kShowAutofillTypePredictions[] = "show-autofill-type-predictions";
+
+// Secure service URL for Online Wallet service. Used as the base url to escrow
+// credit card numbers.
+const char kWalletSecureServiceUrl[] = "wallet-secure-service-url";
+
+// Service URL for Online Wallet service. Used as the base url for Online Wallet
+// API calls.
+const char kWalletServiceUrl[] = "wallet-service-url";
+
+// Enable production Online Wallet service. If this flag is not set, the sandbox
+// service will be used.
+const char kWalletServiceUseProd[] = "wallet-service-use-prod";
+
+} // namespace switches
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/autofill_switches.h b/chromium/components/autofill/core/common/autofill_switches.h
new file mode 100644
index 00000000000..84d76d59c59
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_switches.h
@@ -0,0 +1,27 @@
+// 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_CORE_COMMON_AUTOFILL_SWITCHES_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_SWITCHES_H_
+
+namespace autofill {
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kAutocheckoutWhitelistUrl[];
+extern const char kAutofillServiceUrl[];
+extern const char kBypassAutocheckoutWhitelist[];
+extern const char kDisableInteractiveAutocomplete[];
+extern const char kEnableExperimentalFormFilling[];
+extern const char kEnableInteractiveAutocomplete[];
+extern const char kShowAutofillTypePredictions[];
+extern const char kWalletSecureServiceUrl[];
+extern const char kWalletServiceUrl[];
+extern const char kWalletServiceUseProd[];
+
+} // namespace switches
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_SWITCHES_H_
diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc
new file mode 100644
index 00000000000..e0c3c1c4636
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_data.cc
@@ -0,0 +1,40 @@
+// 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/core/common/form_data.h"
+
+#include "base/strings/string_util.h"
+
+namespace autofill {
+
+FormData::FormData()
+ : user_submitted(false) {
+}
+
+FormData::FormData(const FormData& data)
+ : name(data.name),
+ method(data.method),
+ origin(data.origin),
+ action(data.action),
+ user_submitted(data.user_submitted),
+ fields(data.fields) {
+}
+
+FormData::~FormData() {
+}
+
+bool FormData::operator==(const FormData& form) const {
+ return name == form.name &&
+ StringToLowerASCII(method) == StringToLowerASCII(form.method) &&
+ origin == form.origin &&
+ action == form.action &&
+ user_submitted == form.user_submitted &&
+ fields == form.fields;
+}
+
+bool FormData::operator!=(const FormData& form) const {
+ return !operator==(form);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h
new file mode 100644
index 00000000000..202fb8d3f95
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_data.h
@@ -0,0 +1,42 @@
+// 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_CORE_COMMON_FORM_DATA_H__
+#define COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H__
+
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "url/gurl.h"
+
+namespace autofill {
+
+// Holds information about a form to be filled and/or submitted.
+struct FormData {
+ FormData();
+ FormData(const FormData& data);
+ ~FormData();
+
+ // Used by FormStructureTest.
+ bool operator==(const FormData& form) const;
+ bool operator!=(const FormData& form) const;
+
+ // The name of the form.
+ base::string16 name;
+ // GET or POST.
+ base::string16 method;
+ // The URL (minus query parameters) containing the form.
+ GURL origin;
+ // The action target of the form.
+ GURL action;
+ // true if this form was submitted by a user gesture and not javascript.
+ bool user_submitted;
+ // A vector of all the input fields in the form.
+ std::vector<FormFieldData> fields;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H__
diff --git a/chromium/components/autofill/core/common/form_data_predictions.cc b/chromium/components/autofill/core/common/form_data_predictions.cc
new file mode 100644
index 00000000000..8375c3066c9
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_data_predictions.cc
@@ -0,0 +1,35 @@
+// 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/core/common/form_data_predictions.h"
+
+namespace autofill {
+
+FormDataPredictions::FormDataPredictions() {
+}
+
+FormDataPredictions::FormDataPredictions(const FormDataPredictions& other)
+ : data(other.data),
+ signature(other.signature),
+ experiment_id(other.experiment_id),
+ fields(other.fields) {
+}
+
+FormDataPredictions::~FormDataPredictions() {
+}
+
+bool FormDataPredictions::operator==(
+ const FormDataPredictions& predictions) const {
+ return (data == predictions.data &&
+ signature == predictions.signature &&
+ experiment_id == predictions.experiment_id &&
+ fields == predictions.fields);
+}
+
+bool FormDataPredictions::operator!=(
+ const FormDataPredictions& predictions) const {
+ return !operator==(predictions);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_data_predictions.h b/chromium/components/autofill/core/common/form_data_predictions.h
new file mode 100644
index 00000000000..80b04be16cb
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_data_predictions.h
@@ -0,0 +1,38 @@
+// 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_CORE_COMMON_FORM_DATA_PREDICTIONS_H__
+#define COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_PREDICTIONS_H__
+
+#include <string>
+#include <vector>
+
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data_predictions.h"
+
+namespace autofill {
+
+// Holds information about a form to be filled and/or submitted.
+struct FormDataPredictions {
+ // Data for this form.
+ FormData data;
+ // The form signature for communication with the crowdsourcing server.
+ std::string signature;
+ // The experiment id for the server predictions.
+ std::string experiment_id;
+ // The form fields and their predicted field types.
+ std::vector<FormFieldDataPredictions> fields;
+
+ FormDataPredictions();
+ FormDataPredictions(const FormDataPredictions& other);
+ ~FormDataPredictions();
+
+ // Added for the sake of testing.
+ bool operator==(const FormDataPredictions& predictions) const;
+ bool operator!=(const FormDataPredictions& predictions) const;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_PREDICTIONS_H__
diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc
new file mode 100644
index 00000000000..1de786dcf3c
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_field_data.cc
@@ -0,0 +1,73 @@
+// 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/core/common/form_field_data.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace autofill {
+
+FormFieldData::FormFieldData()
+ : max_length(0),
+ is_autofilled(false),
+ is_checked(false),
+ is_checkable(false),
+ is_focusable(false),
+ should_autocomplete(true),
+ text_direction(base::i18n::UNKNOWN_DIRECTION) {
+}
+
+FormFieldData::~FormFieldData() {
+}
+
+bool FormFieldData::operator==(const FormFieldData& field) const {
+ // A FormFieldData stores a value, but the value is not part of the identity
+ // of the field, so we don't want to compare the values.
+ return (label == field.label &&
+ name == field.name &&
+ form_control_type == field.form_control_type &&
+ autocomplete_attribute == field.autocomplete_attribute &&
+ max_length == field.max_length);
+}
+
+bool FormFieldData::operator!=(const FormFieldData& field) const {
+ return !operator==(field);
+}
+
+bool FormFieldData::operator<(const FormFieldData& field) const {
+ if (label == field.label)
+ return name < field.name;
+
+ return label < field.label;
+}
+
+std::ostream& operator<<(std::ostream& os, const FormFieldData& field) {
+ return os
+ << UTF16ToUTF8(field.label)
+ << " "
+ << UTF16ToUTF8(field.name)
+ << " "
+ << UTF16ToUTF8(field.value)
+ << " "
+ << field.form_control_type
+ << " "
+ << field.autocomplete_attribute
+ << " "
+ << field.max_length
+ << " "
+ << (field.is_autofilled ? "true" : "false")
+ << " "
+ << (field.is_checked ? "true" : "false")
+ << " "
+ << (field.is_checkable ? "true" : "false")
+ << " "
+ << (field.is_focusable ? "true" : "false")
+ << " "
+ << (field.should_autocomplete ? "true" : "false")
+ << " "
+ << field.text_direction;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h
new file mode 100644
index 00000000000..f0e7579cd0a
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_field_data.h
@@ -0,0 +1,69 @@
+// 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_CORE_COMMON_FORM_FIELD_DATA_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_H_
+
+#include <vector>
+
+#include "base/i18n/rtl.h"
+#include "base/strings/string16.h"
+
+namespace autofill {
+
+// Stores information about a field in a form.
+struct FormFieldData {
+ FormFieldData();
+ ~FormFieldData();
+
+ // Equality tests for identity which does not include |value| or
+ // |is_autofilled|.
+ // TODO(dhollowa): These operators need to be revised when we implement field
+ // ids.
+ bool operator==(const FormFieldData& field) const;
+ bool operator!=(const FormFieldData& field) const;
+ // Comparison operator exposed for STL map. Uses label, then name to sort.
+ bool operator<(const FormFieldData& field) const;
+
+ base::string16 label;
+ base::string16 name;
+ base::string16 value;
+ std::string form_control_type;
+ std::string autocomplete_attribute;
+ size_t max_length;
+ bool is_autofilled;
+ bool is_checked;
+ bool is_checkable;
+ bool is_focusable;
+ bool should_autocomplete;
+ base::i18n::TextDirection text_direction;
+
+ // For the HTML snippet |<option value="US">United States</option>|, the
+ // value is "US" and the contents are "United States".
+ std::vector<base::string16> option_values;
+ std::vector<base::string16> option_contents;
+};
+
+// So we can compare FormFieldDatas with EXPECT_EQ().
+std::ostream& operator<<(std::ostream& os, const FormFieldData& field);
+
+// Prefer to use this macro in place of |EXPECT_EQ()| for comparing
+// |FormFieldData|s in test code.
+#define EXPECT_FORM_FIELD_DATA_EQUALS(expected, actual) \
+ do { \
+ EXPECT_EQ(expected.label, actual.label); \
+ EXPECT_EQ(expected.name, actual.name); \
+ EXPECT_EQ(expected.value, actual.value); \
+ EXPECT_EQ(expected.form_control_type, actual.form_control_type); \
+ EXPECT_EQ(expected.autocomplete_attribute, actual.autocomplete_attribute); \
+ EXPECT_EQ(expected.max_length, actual.max_length); \
+ EXPECT_EQ(expected.is_autofilled, actual.is_autofilled); \
+ EXPECT_EQ(expected.is_checked, actual.is_checked); \
+ EXPECT_EQ(expected.is_checkable, actual.is_checkable); \
+ } while (0)
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_H_
+
diff --git a/chromium/components/autofill/core/common/form_field_data_predictions.cc b/chromium/components/autofill/core/common/form_field_data_predictions.cc
new file mode 100644
index 00000000000..6e06559fd35
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_field_data_predictions.cc
@@ -0,0 +1,38 @@
+// 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/core/common/form_field_data_predictions.h"
+
+namespace autofill {
+
+FormFieldDataPredictions::FormFieldDataPredictions() {
+}
+
+FormFieldDataPredictions::FormFieldDataPredictions(
+ const FormFieldDataPredictions& other)
+ : field(other.field),
+ signature(other.signature),
+ heuristic_type(other.heuristic_type),
+ server_type(other.server_type),
+ overall_type(other.overall_type) {
+}
+
+FormFieldDataPredictions::~FormFieldDataPredictions() {
+}
+
+bool FormFieldDataPredictions::operator==(
+ const FormFieldDataPredictions& predictions) const {
+ return (field == predictions.field &&
+ signature == predictions.signature &&
+ heuristic_type == predictions.heuristic_type &&
+ server_type == predictions.server_type &&
+ overall_type == predictions.overall_type);
+}
+
+bool FormFieldDataPredictions::operator!=(
+ const FormFieldDataPredictions& predictions) const {
+ return !operator==(predictions);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_field_data_predictions.h b/chromium/components/autofill/core/common/form_field_data_predictions.h
new file mode 100644
index 00000000000..86f8bdaccdf
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_field_data_predictions.h
@@ -0,0 +1,34 @@
+// 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_CORE_COMMON_FORM_FIELD_DATA_PREDICTIONS_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_PREDICTIONS_H_
+
+#include <string>
+#include <vector>
+
+#include "components/autofill/core/common/form_field_data.h"
+
+namespace autofill {
+
+// Stores information about a field in a form.
+struct FormFieldDataPredictions {
+ FormFieldDataPredictions();
+ FormFieldDataPredictions(const FormFieldDataPredictions& other);
+ ~FormFieldDataPredictions();
+
+ FormFieldData field;
+ std::string signature;
+ std::string heuristic_type;
+ std::string server_type;
+ std::string overall_type;
+
+ // Added for the sake of testing.
+ bool operator==(const FormFieldDataPredictions& predictions) const;
+ bool operator!=(const FormFieldDataPredictions& predictions) const;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_PREDICTIONS_H_
diff --git a/chromium/components/autofill/core/common/forms_seen_state.h b/chromium/components/autofill/core/common/forms_seen_state.h
new file mode 100644
index 00000000000..da417f96b54
--- /dev/null
+++ b/chromium/components/autofill/core/common/forms_seen_state.h
@@ -0,0 +1,24 @@
+// 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_COMMON_FORMS_SEEN_PARAM_H_
+#define COMPONENTS_AUTOFILL_COMMON_FORMS_SEEN_PARAM_H_
+
+namespace autofill {
+
+// Specifies the nature of the forms triggering the call.
+enum FormsSeenState {
+ // Forms present on page load with no additional unsent forms.
+ NO_SPECIAL_FORMS_SEEN = 0,
+ // If this an Autocheckout page, there are more that should be checked.
+ PARTIAL_FORMS_SEEN = 1,
+ // Forms were added dynamically post-page load.
+ DYNAMIC_FORMS_SEEN = 2,
+ // Number of states.
+ FORMS_SEEN_STATE_NUM_STATES = 3,
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_COMMON_FORMS_SEEN_PARAM_H_
diff --git a/chromium/components/autofill/core/common/password_form_fill_data.cc b/chromium/components/autofill/core/common/password_form_fill_data.cc
new file mode 100644
index 00000000000..80b26989cce
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_form_fill_data.cc
@@ -0,0 +1,84 @@
+// 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/core/common/password_form_fill_data.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/form_field_data.h"
+
+namespace autofill {
+
+UsernamesCollectionKey::UsernamesCollectionKey() {}
+
+UsernamesCollectionKey::~UsernamesCollectionKey() {}
+
+bool UsernamesCollectionKey::operator<(
+ const UsernamesCollectionKey& other) const {
+ if (username != other.username)
+ return username < other.username;
+ if (password != other.password)
+ return password < other.password;
+ return realm < other.realm;
+}
+
+PasswordFormFillData::PasswordFormFillData() : wait_for_username(false) {
+}
+
+PasswordFormFillData::~PasswordFormFillData() {
+}
+
+void InitPasswordFormFillData(
+ const content::PasswordForm& form_on_page,
+ const content::PasswordFormMap& matches,
+ const content::PasswordForm* const preferred_match,
+ bool wait_for_username_before_autofill,
+ bool enable_other_possible_usernames,
+ PasswordFormFillData* result) {
+ // Note that many of the |FormFieldData| members are not initialized for
+ // |username_field| and |password_field| because they are currently not used
+ // by the password autocomplete code.
+ FormFieldData username_field;
+ username_field.name = form_on_page.username_element;
+ username_field.value = preferred_match->username_value;
+ FormFieldData password_field;
+ password_field.name = form_on_page.password_element;
+ password_field.value = preferred_match->password_value;
+ password_field.form_control_type = "password";
+
+ // Fill basic form data.
+ result->basic_data.origin = form_on_page.origin;
+ result->basic_data.action = form_on_page.action;
+ result->basic_data.fields.push_back(username_field);
+ result->basic_data.fields.push_back(password_field);
+ result->wait_for_username = wait_for_username_before_autofill;
+
+ result->preferred_realm = preferred_match->original_signon_realm;
+
+ // Copy additional username/value pairs.
+ content::PasswordFormMap::const_iterator iter;
+ for (iter = matches.begin(); iter != matches.end(); iter++) {
+ if (iter->second != preferred_match) {
+ PasswordAndRealm value;
+ value.password = iter->second->password_value;
+ value.realm = iter->second->original_signon_realm;
+ result->additional_logins[iter->first] = value;
+ }
+ if (enable_other_possible_usernames &&
+ !iter->second->other_possible_usernames.empty()) {
+ // Note that there may be overlap between other_possible_usernames and
+ // other saved usernames or with other other_possible_usernames. For now
+ // we will ignore this overlap as it should be a rare occurence. We may
+ // want to revisit this in the future.
+ UsernamesCollectionKey key;
+ key.username = iter->first;
+ key.password = iter->second->password_value;
+ key.realm = iter->second->original_signon_realm;
+ result->other_possible_usernames[key] =
+ iter->second->other_possible_usernames;
+ }
+ }
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/password_form_fill_data.h b/chromium/components/autofill/core/common/password_form_fill_data.h
new file mode 100644
index 00000000000..9bcd5388d7a
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_form_fill_data.h
@@ -0,0 +1,87 @@
+// 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_CORE_COMMON_PASSWORD_FORM_FILL_DATA_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_FILL_DATA_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/core/common/form_data.h"
+#include "content/public/common/password_form.h"
+
+namespace autofill {
+
+// Helper struct for PasswordFormFillData
+struct UsernamesCollectionKey {
+ UsernamesCollectionKey();
+ ~UsernamesCollectionKey();
+
+ // Defined so that this struct can be used as a key in a std::map.
+ bool operator<(const UsernamesCollectionKey& other) const;
+
+ base::string16 username;
+ base::string16 password;
+ std::string realm;
+};
+
+struct PasswordAndRealm {
+ base::string16 password;
+ std::string realm;
+};
+
+// Structure used for autofilling password forms. Note that the realms in this
+// struct are only set when the password's realm differs from the realm of the
+// form that we are filling.
+struct PasswordFormFillData {
+ typedef std::map<base::string16, PasswordAndRealm> LoginCollection;
+ typedef std::map<UsernamesCollectionKey,
+ std::vector<base::string16> > UsernamesCollection;
+
+ // Identifies the HTML form on the page and preferred username/password for
+ // login.
+ FormData basic_data;
+
+ // The signon realm of the preferred user/pass pair.
+ std::string preferred_realm;
+
+ // A list of other matching username->PasswordAndRealm pairs for the form.
+ LoginCollection additional_logins;
+
+ // A list of possible usernames in the case where we aren't completely sure
+ // that the original saved username is correct. This data is keyed by the
+ // saved username/password to ensure uniqueness, though the username is not
+ // used.
+ UsernamesCollection other_possible_usernames;
+
+ // Tells us whether we need to wait for the user to enter a valid username
+ // before we autofill the password. By default, this is off unless the
+ // PasswordManager determined there is an additional risk associated with this
+ // form. This can happen, for example, if action URI's of the observed form
+ // and our saved representation don't match up.
+ bool wait_for_username;
+
+ PasswordFormFillData();
+ ~PasswordFormFillData();
+};
+
+// Create a FillData structure in preparation for autofilling a form,
+// from basic_data identifying which form to fill, and a collection of
+// matching stored logins to use as username/password values.
+// |preferred_match| should equal (address) one of matches.
+// |wait_for_username_before_autofill| is true if we should not autofill
+// anything until the user typed in a valid username and blurred the field.
+// If |enable_possible_usernames| is true, we will populate possible_usernames
+// in |result|.
+void InitPasswordFormFillData(
+ const content::PasswordForm& form_on_page,
+ const content::PasswordFormMap& matches,
+ const content::PasswordForm* const preferred_match,
+ bool wait_for_username_before_autofill,
+ bool enable_other_possible_usernames,
+ PasswordFormFillData* result);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_FILL_DATA_H__
diff --git a/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
new file mode 100644
index 00000000000..1242774b12a
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
@@ -0,0 +1,171 @@
+// 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/core/common/password_form_fill_data.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/common/password_form.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::PasswordForm;
+using content::PasswordFormMap;
+
+namespace autofill {
+
+// Tests that the when there is a single preferred match, and no extra
+// matches, the PasswordFormFillData is filled in correctly.
+TEST(PasswordFormFillDataTest, TestSinglePreferredMatch) {
+ // Create the current form on the page.
+ PasswordForm form_on_page;
+ form_on_page.origin = GURL("https://foo.com/");
+ form_on_page.action = GURL("https://foo.com/login");
+ form_on_page.username_element = ASCIIToUTF16("username");
+ form_on_page.username_value = ASCIIToUTF16("test@gmail.com");
+ form_on_page.password_element = ASCIIToUTF16("password");
+ form_on_page.password_value = ASCIIToUTF16("test");
+ form_on_page.submit_element = ASCIIToUTF16("");
+ form_on_page.signon_realm = "https://foo.com/";
+ form_on_page.ssl_valid = true;
+ form_on_page.preferred = false;
+ form_on_page.scheme = PasswordForm::SCHEME_HTML;
+
+ // Create an exact match in the database.
+ PasswordForm preferred_match;
+ preferred_match.origin = GURL("https://foo.com/");
+ preferred_match.action = GURL("https://foo.com/login");
+ preferred_match.username_element = ASCIIToUTF16("username");
+ preferred_match.username_value = ASCIIToUTF16("test@gmail.com");
+ preferred_match.password_element = ASCIIToUTF16("password");
+ preferred_match.password_value = ASCIIToUTF16("test");
+ preferred_match.submit_element = ASCIIToUTF16("");
+ preferred_match.signon_realm = "https://foo.com/";
+ preferred_match.ssl_valid = true;
+ preferred_match.preferred = true;
+ preferred_match.scheme = PasswordForm::SCHEME_HTML;
+
+ PasswordFormMap matches;
+
+ PasswordFormFillData result;
+ InitPasswordFormFillData(form_on_page,
+ matches,
+ &preferred_match,
+ true,
+ false,
+ &result);
+
+ // |wait_for_username| should reflect the |wait_for_username_before_autofill|
+ // argument of InitPasswordFormFillData which in this case is true.
+ EXPECT_TRUE(result.wait_for_username);
+ // The preferred realm should be empty since it's the same as the realm of
+ // the form.
+ EXPECT_EQ(result.preferred_realm, "");
+
+ PasswordFormFillData result2;
+ InitPasswordFormFillData(form_on_page,
+ matches,
+ &preferred_match,
+ false,
+ false,
+ &result2);
+
+ // |wait_for_username| should reflect the |wait_for_username_before_autofill|
+ // argument of InitPasswordFormFillData which in this case is false.
+ EXPECT_FALSE(result2.wait_for_username);
+}
+
+// Tests that the InitPasswordFormFillData behaves correctly when there is a
+// preferred match that was found using public suffix matching, an additional
+// result that also used public suffix matching, and a third result that was
+// found without using public suffix matching.
+TEST(PasswordFormFillDataTest, TestPublicSuffixDomainMatching) {
+ // Create the current form on the page.
+ PasswordForm form_on_page;
+ form_on_page.origin = GURL("https://foo.com/");
+ form_on_page.action = GURL("https://foo.com/login");
+ form_on_page.username_element = ASCIIToUTF16("username");
+ form_on_page.username_value = ASCIIToUTF16("test@gmail.com");
+ form_on_page.password_element = ASCIIToUTF16("password");
+ form_on_page.password_value = ASCIIToUTF16("test");
+ form_on_page.submit_element = ASCIIToUTF16("");
+ form_on_page.signon_realm = "https://foo.com/";
+ form_on_page.ssl_valid = true;
+ form_on_page.preferred = false;
+ form_on_page.scheme = PasswordForm::SCHEME_HTML;
+
+ // Create a match from the database that matches using public suffix.
+ PasswordForm preferred_match;
+ preferred_match.origin = GURL("https://mobile.foo.com/");
+ preferred_match.action = GURL("https://mobile.foo.com/login");
+ preferred_match.username_element = ASCIIToUTF16("username");
+ preferred_match.username_value = ASCIIToUTF16("test@gmail.com");
+ preferred_match.password_element = ASCIIToUTF16("password");
+ preferred_match.password_value = ASCIIToUTF16("test");
+ preferred_match.submit_element = ASCIIToUTF16("");
+ preferred_match.signon_realm = "https://mobile.foo.com/";
+ preferred_match.original_signon_realm = "https://foo.com/";
+ preferred_match.ssl_valid = true;
+ preferred_match.preferred = true;
+ preferred_match.scheme = PasswordForm::SCHEME_HTML;
+
+ // Create a match that matches exactly, so |original_signon_realm| is not set.
+ PasswordForm exact_match;
+ exact_match.origin = GURL("https://foo.com/");
+ exact_match.action = GURL("https://foo.com/login");
+ exact_match.username_element = ASCIIToUTF16("username");
+ exact_match.username_value = ASCIIToUTF16("test1@gmail.com");
+ exact_match.password_element = ASCIIToUTF16("password");
+ exact_match.password_value = ASCIIToUTF16("test");
+ exact_match.submit_element = ASCIIToUTF16("");
+ exact_match.signon_realm = "https://foo.com/";
+ exact_match.ssl_valid = true;
+ exact_match.preferred = false;
+ exact_match.scheme = PasswordForm::SCHEME_HTML;
+
+ // Create a match that was matched using public suffix, so
+ // |original_signon_realm| is set to where the result came from.
+ PasswordForm public_suffix_match;
+ public_suffix_match.origin = GURL("https://foo.com/");
+ public_suffix_match.action = GURL("https://foo.com/login");
+ public_suffix_match.username_element = ASCIIToUTF16("username");
+ public_suffix_match.username_value = ASCIIToUTF16("test2@gmail.com");
+ public_suffix_match.password_element = ASCIIToUTF16("password");
+ public_suffix_match.password_value = ASCIIToUTF16("test");
+ public_suffix_match.submit_element = ASCIIToUTF16("");
+ public_suffix_match.original_signon_realm = "https://subdomain.foo.com/";
+ public_suffix_match.signon_realm = "https://foo.com/";
+ public_suffix_match.ssl_valid = true;
+ public_suffix_match.preferred = false;
+ public_suffix_match.scheme = PasswordForm::SCHEME_HTML;
+
+ // Add one exact match and one public suffix match.
+ PasswordFormMap matches;
+ matches[exact_match.username_value] = &exact_match;
+ matches[public_suffix_match.username_value] = &public_suffix_match;
+
+ PasswordFormFillData result;
+ InitPasswordFormFillData(form_on_page,
+ matches,
+ &preferred_match,
+ true,
+ false,
+ &result);
+ EXPECT_TRUE(result.wait_for_username);
+ // The preferred realm should match the original signon realm from the
+ // preferred match so the user can see where the result came from.
+ EXPECT_EQ(result.preferred_realm,
+ preferred_match.original_signon_realm);
+
+ // The realm of the exact match should be empty.
+ PasswordFormFillData::LoginCollection::const_iterator iter =
+ result.additional_logins.find(exact_match.username_value);
+ EXPECT_EQ(iter->second.realm, "");
+
+ // The realm of the public suffix match should be set to the original signon
+ // realm so the user can see where the result came from.
+ iter = result.additional_logins.find(public_suffix_match.username_value);
+ EXPECT_EQ(iter->second.realm, public_suffix_match.original_signon_realm);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/password_generation_util.cc b/chromium/components/autofill/core/common/password_generation_util.cc
new file mode 100644
index 00000000000..15a3c4c1188
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_generation_util.cc
@@ -0,0 +1,42 @@
+// 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/core/common/password_generation_util.h"
+
+#include "base/metrics/histogram.h"
+
+namespace autofill {
+namespace password_generation {
+
+PasswordGenerationActions::PasswordGenerationActions()
+ : learn_more_visited(false),
+ password_accepted(false),
+ password_edited(false),
+ password_regenerated(false) {
+}
+
+PasswordGenerationActions::~PasswordGenerationActions() {
+}
+
+void LogUserActions(PasswordGenerationActions actions) {
+ UserAction action = IGNORE_FEATURE;
+ if (actions.password_accepted) {
+ if (actions.password_edited)
+ action = ACCEPT_AFTER_EDITING;
+ else
+ action = ACCEPT_ORIGINAL_PASSWORD;
+ } else if (actions.learn_more_visited) {
+ action = LEARN_MORE;
+ }
+ UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.UserActions",
+ action, ACTION_ENUM_COUNT);
+}
+
+void LogPasswordGenerationEvent(PasswordGenerationEvent event) {
+ UMA_HISTOGRAM_ENUMERATION("PasswordGeneration.Event",
+ event, EVENT_ENUM_COUNT);
+}
+
+} // namespace password_generation
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/password_generation_util.h b/chromium/components/autofill/core/common/password_generation_util.h
new file mode 100644
index 00000000000..f3f47b398a7
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_generation_util.h
@@ -0,0 +1,77 @@
+// 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_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
+
+namespace autofill {
+namespace password_generation {
+
+// Enumerates various events related to the password generation process.
+enum PasswordGenerationEvent {
+ // No Account creation form is detected.
+ NO_SIGN_UP_DETECTED,
+
+ // Account creation form is detected.
+ SIGN_UP_DETECTED,
+
+ // Password generation icon is shown inside the first password field.
+ ICON_SHOWN,
+
+ // Password generation bubble is shown after user clicks on the icon.
+ BUBBLE_SHOWN,
+
+ // Number of enum entries, used for UMA histogram reporting macros.
+ EVENT_ENUM_COUNT
+};
+
+// Wrapper to store the user interactions with the password generation bubble.
+struct PasswordGenerationActions {
+ // Whether the user has clicked on the learn more link.
+ bool learn_more_visited;
+
+ // Whether the user has accepted the generated password.
+ bool password_accepted;
+
+ // Whether the user has manually edited password entry.
+ bool password_edited;
+
+ // Whether the user has clicked on the regenerate button.
+ bool password_regenerated;
+
+ PasswordGenerationActions();
+ ~PasswordGenerationActions();
+};
+
+void LogUserActions(PasswordGenerationActions actions);
+
+void LogPasswordGenerationEvent(PasswordGenerationEvent event);
+
+// Enumerates user actions after password generation bubble is shown.
+// These are visible for testing purposes.
+enum UserAction {
+ // User closes the bubble without any meaningful actions (e.g. use backspace
+ // key, close the bubble, click outside the bubble, etc).
+ IGNORE_FEATURE,
+
+ // User navigates to the learn more page. Note that in the current
+ // implementation this will result in closing the bubble so this action
+ // doesn't overlap with the following two actions.
+ LEARN_MORE,
+
+ // User accepts the generated password without manually editing it (but
+ // including changing it through the regenerate button).
+ ACCEPT_ORIGINAL_PASSWORD,
+
+ // User accepts the gererated password after manually editing it.
+ ACCEPT_AFTER_EDITING,
+
+ // Number of enum entries, used for UMA histogram reporting macros.
+ ACTION_ENUM_COUNT
+};
+
+} // namespace password_generation
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_GENERATION_UTIL_H_
diff --git a/chromium/components/autofill/core/common/web_element_descriptor.cc b/chromium/components/autofill/core/common/web_element_descriptor.cc
new file mode 100644
index 00000000000..ffb5c05e27f
--- /dev/null
+++ b/chromium/components/autofill/core/common/web_element_descriptor.cc
@@ -0,0 +1,12 @@
+// 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/core/common/web_element_descriptor.h"
+
+namespace autofill {
+
+autofill::WebElementDescriptor::WebElementDescriptor()
+ : retrieval_method(NONE) {}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/web_element_descriptor.h b/chromium/components/autofill/core/common/web_element_descriptor.h
new file mode 100644
index 00000000000..6f41cbb5d73
--- /dev/null
+++ b/chromium/components/autofill/core/common/web_element_descriptor.h
@@ -0,0 +1,31 @@
+// 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_CORE_COMMON_WEB_ELEMENT_DESCRIPTOR_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_WEB_ELEMENT_DESCRIPTOR_H_
+
+#include <string>
+
+namespace autofill {
+
+// Holds information that can be used to retrieve an element.
+struct WebElementDescriptor {
+ enum RetrievalMethod {
+ CSS_SELECTOR,
+ ID,
+ NONE,
+ };
+
+ WebElementDescriptor();
+
+ // Information to retrieve element with.
+ std::string descriptor;
+
+ // Which retrieval method to use.
+ RetrievalMethod retrieval_method;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_WEB_ELEMENT_DESCRIPTOR_H_
diff --git a/chromium/components/autofill_strings.grdp b/chromium/components/autofill_strings.grdp
new file mode 100644
index 00000000000..16fe9390d27
--- /dev/null
+++ b/chromium/components/autofill_strings.grdp
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+
+ <message name="IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM" desc="The entry in the suggestions dropdown that clears an auto-filled form.">
+ Clear form
+ </message>
+ <message name="IDS_AUTOFILL_WARNING_FORM_DISABLED" desc="Warning text to show when autofill is disabled by the website for a given form.">
+ This webpage has disabled automatic filling for this form.
+ </message>
+ <message name="IDS_AUTOFILL_WARNING_INSECURE_CONNECTION" desc="Warning text to show when credit card autofill is disabled because the website is not using a secure connection.">
+ Automatic credit card filling is disabled because this form does not use a secure connection.
+ </message>
+ <message name="IDS_AUTOFILL_CC_AMEX" desc="American Express credit card name.">
+ American Express
+ </message>
+ <message name="IDS_AUTOFILL_CC_DINERS" desc="Diners Club credit card name.">
+ Diners Club
+ </message>
+ <message name="IDS_AUTOFILL_CC_DISCOVER" desc="Discover credit card name.">
+ Discover
+ </message>
+ <message name="IDS_AUTOFILL_CC_JCB" desc="JCB credit card name.">
+ JCB
+ </message>
+ <message name="IDS_AUTOFILL_CC_MASTERCARD" desc="MasterCard credit card name.">
+ MasterCard
+ </message>
+ <message name="IDS_AUTOFILL_CC_UNION_PAY" desc="China UnionPay credit card name.">
+ China UnionPay
+ </message>
+ <message name="IDS_AUTOFILL_CC_VISA" desc="Visa credit card name.">
+ Visa
+ </message>
+ <message name="IDS_AUTOFILL_MAC_ADDRESS_LINE_SEPARATOR" desc="The separator character used to join multi-line addresses on the Mac.">
+ , '''
+ </message>
+ <message name="IDS_AUTOFILL_ADDRESS_SUMMARY_SEPARATOR" desc="The separator character used in the summary of an address.">
+ , '''
+ </message>
+ <message name="IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT" desc="Credit card preview format">
+ <ph name="OBFUSCATED_CC_NUMBER">$1<ex>************1234</ex>
+ </ph>, Exp: <ph name="CC_EXPIRATION_DATE">$2<ex>03/2020</ex>
+ </ph>
+ </message>
+
+ <!-- These are all variants of the same logical field: The major subdivision below the "country" level. -->
+ <message name="IDS_AUTOFILL_FIELD_LABEL_STATE" desc="The label of the State entry.">
+ State
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_AREA" desc="The label of the Area entry.">
+ Area
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_COUNTY" desc="The label of the County entry.">
+ County
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_DEPARTMENT" desc="The label of the Department entry.">
+ Department
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_DISTRICT" desc="The label of the District entry.">
+ District
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_EMIRATE" desc="The label of the Emirate entry.">
+ Emirate
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_ISLAND" desc="The label of the Island entry.">
+ Island
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_PARISH" desc="The label of the Parish entry.">
+ Parish
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_PREFECTURE" desc="The label of the Prefecture entry.">
+ Prefecture
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_PROVINCE" desc="The label of the Province entry.">
+ Province
+ </message>
+ <!-- End state variants. -->
+
+ <message name="IDS_AUTOFILL_FIELD_LABEL_ZIP_CODE" desc="The label of the ZIP code entry.">
+ ZIP code
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_POSTAL_CODE" desc="The label of the Postal code entry.">
+ Postal code
+ </message>
+ <message name="IDS_AUTOFILL_FIELD_LABEL_COUNTRY" desc="The label of the Country entry.">
+ Country
+ </message>
+
+ <message name="IDS_AUTOFILL_SHOW_PREDICTIONS_TITLE" desc="The title for form elements when annotated with Autofill predictions.">
+ heuristic type: <ph name="HEURISTIC_TYPE">$1<ex>NAME_FIRST</ex></ph>
+ server type: <ph name="SERVER_TYPE">$2<ex>NAME_FIRST</ex></ph>
+ field signature: <ph name="FIELD_SIGNATURE">$3<ex>12345678</ex></ph>
+ form signature: <ph name="FORM_SIGNATURE">$4<ex>1234567812345678</ex></ph>
+ experiment id: "<ph name="EXPERIMENT_ID">$5<ex>ar1</ex></ph>"
+ </message>
+
+ <!-- Autofill dialog: legal documents -->
+ <message name="IDS_AUTOFILL_DIALOG_PRIVACY_POLICY_LINK" desc="The text of an extra link that is appended to the end of whatever other legal documents need to be accepted or updated.">
+ Privacy Policy
+ </message>
+
+ <message name="IDS_AUTOFILL_OPTIONS_POPUP" desc="The value of the Autofill options menu entry.">
+ Chrome Autofill settings
+ </message>
+
+</grit-part>
diff --git a/chromium/components/breakpad.gypi b/chromium/components/breakpad.gypi
new file mode 100644
index 00000000000..c8938709265
--- /dev/null
+++ b/chromium/components/breakpad.gypi
@@ -0,0 +1,68 @@
+# 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.
+
+{
+ 'target_defaults': {
+ 'variables': {
+ 'breakpad_component_target': 0,
+ },
+ 'target_conditions': [
+ ['breakpad_component_target==1', {
+ 'sources': [
+ 'breakpad/breakpad_client.cc',
+ 'breakpad/breakpad_client.h',
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'breakpad_component',
+ 'type': 'static_library',
+ 'variables': {
+ 'breakpad_component_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ 'target_name': 'breakpad_win64',
+ 'type': 'static_library',
+ 'variables': {
+ 'breakpad_component_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base_nacl_win64',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ['OS=="mac"', {
+ 'targets': [
+ {
+ # TODO(jochen): for now, this target is a copy of breakpad, however,
+ # in the future, it should provide a dummy implementation for Mac.
+ 'target_name': 'breakpad_stubs',
+ 'type': 'static_library',
+ 'variables': {
+ 'breakpad_component_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/breakpad/OWNERS b/chromium/components/breakpad/OWNERS
new file mode 100644
index 00000000000..cf2020f2a0d
--- /dev/null
+++ b/chromium/components/breakpad/OWNERS
@@ -0,0 +1,4 @@
+cpu@chromium.org
+jochen@chromium.org
+rsesek@chromium.org
+thestig@chromum.org
diff --git a/chromium/components/breakpad/breakpad_client.cc b/chromium/components/breakpad/breakpad_client.cc
new file mode 100644
index 00000000000..a4c8c45562c
--- /dev/null
+++ b/chromium/components/breakpad/breakpad_client.cc
@@ -0,0 +1,118 @@
+// 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/breakpad/breakpad_client.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+
+namespace breakpad {
+
+namespace {
+
+BreakpadClient* g_client = NULL;
+
+} // namespace
+
+void SetBreakpadClient(BreakpadClient* client) {
+ g_client = client;
+}
+
+BreakpadClient* GetBreakpadClient() {
+ DCHECK(g_client);
+ return g_client;
+}
+
+BreakpadClient::BreakpadClient() {}
+BreakpadClient::~BreakpadClient() {}
+
+#if defined(OS_WIN)
+bool BreakpadClient::GetAlternativeCrashDumpLocation(
+ base::FilePath* crash_dir) {
+ return false;
+}
+
+void BreakpadClient::GetProductNameAndVersion(const base::FilePath& exe_path,
+ base::string16* product_name,
+ base::string16* version,
+ base::string16* special_build,
+ base::string16* channel_name) {
+}
+
+bool BreakpadClient::ShouldShowRestartDialog(base::string16* title,
+ base::string16* message,
+ bool* is_rtl_locale) {
+ return false;
+}
+
+bool BreakpadClient::AboutToRestart() {
+ return true;
+}
+
+base::string16 BreakpadClient::GetCrashGUID() {
+ return base::string16();
+}
+
+bool BreakpadClient::GetDeferredUploadsSupported(bool is_per_usr_install) {
+ return false;
+}
+
+bool BreakpadClient::GetIsPerUserInstall(const base::FilePath& exe_path) {
+ return false;
+}
+
+bool BreakpadClient::GetShouldDumpLargerDumps(bool is_per_user_install) {
+ return false;
+}
+
+int BreakpadClient::GetResultCodeRespawnFailed() {
+ return 0;
+}
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
+void BreakpadClient::GetProductNameAndVersion(std::string* product_name,
+ std::string* version) {
+}
+
+base::FilePath BreakpadClient::GetReporterLogFilename() {
+ return base::FilePath();
+}
+#endif
+
+bool BreakpadClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
+ return false;
+}
+
+#if defined(OS_POSIX)
+void BreakpadClient::SetDumpWithoutCrashingFunction(void (*function)()) {
+}
+#endif
+
+size_t BreakpadClient::RegisterCrashKeys() {
+ return 0;
+}
+
+bool BreakpadClient::IsRunningUnattended() {
+ return false;
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+bool BreakpadClient::GetCollectStatsConsent() {
+ return false;
+}
+#endif
+
+#if defined(OS_ANDROID)
+int BreakpadClient::GetAndroidMinidumpDescriptor() {
+ return 0;
+}
+#endif
+
+#if defined(OS_MACOSX)
+void BreakpadClient::InstallAdditionalFilters(BreakpadRef breakpad) {
+}
+#endif
+
+} // namespace breakpad
diff --git a/chromium/components/breakpad/breakpad_client.h b/chromium/components/breakpad/breakpad_client.h
new file mode 100644
index 00000000000..35b430807ba
--- /dev/null
+++ b/chromium/components/breakpad/breakpad_client.h
@@ -0,0 +1,131 @@
+// 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_BREAKPAD_BREAKPAD_CLIENT_H_
+#define COMPONENTS_BREAKPAD_BREAKPAD_CLIENT_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "build/build_config.h"
+
+namespace base {
+class FilePath;
+}
+
+#if defined(OS_MACOSX)
+// We don't want to directly include
+// breakpad/src/client/mac/Framework/Breakpad.h here, so we repeat the
+// definition of BreakpadRef.
+//
+// On Mac, when compiling without breakpad support, a stub implementation is
+// compiled in. Not having any includes of the breakpad library allows for
+// reusing this header for the stub.
+typedef void* BreakpadRef;
+#endif
+
+namespace breakpad {
+
+class BreakpadClient;
+
+// Setter and getter for the client. The client should be set early, before any
+// breakpad code is called, and should stay alive throughout the entire runtime.
+void SetBreakpadClient(BreakpadClient* client);
+
+// Breakpad's embedder API should only be used by breakpad.
+BreakpadClient* GetBreakpadClient();
+
+// Interface that the embedder implements.
+class BreakpadClient {
+ public:
+ BreakpadClient();
+ virtual ~BreakpadClient();
+
+#if defined(OS_WIN)
+ // Returns true if an alternative location to store the minidump files was
+ // specified. Returns true if |crash_dir| was set.
+ virtual bool GetAlternativeCrashDumpLocation(base::FilePath* crash_dir);
+
+ // Returns a textual description of the product type and version to include
+ // in the crash report.
+ virtual void GetProductNameAndVersion(const base::FilePath& exe_path,
+ base::string16* product_name,
+ base::string16* version,
+ base::string16* special_build,
+ base::string16* channel_name);
+
+ // Returns true if a restart dialog should be displayed. In that case,
+ // |message| and |title| are set to a message to display in a dialog box with
+ // the given title before restarting, and |is_rtl_locale| indicates whether
+ // to display the text as RTL.
+ virtual bool ShouldShowRestartDialog(base::string16* title,
+ base::string16* message,
+ bool* is_rtl_locale);
+
+ // Returns true if it is ok to restart the application. Invoked right before
+ // restarting after a crash.
+ virtual bool AboutToRestart();
+
+ // Returns a GUID to embed in the crash report.
+ virtual base::string16 GetCrashGUID();
+
+ // Returns true if the crash report uploader supports deferred uploads.
+ virtual bool GetDeferredUploadsSupported(bool is_per_user_install);
+
+ // Returns true if the running binary is a per-user installation.
+ virtual bool GetIsPerUserInstall(const base::FilePath& exe_path);
+
+ // Returns true if larger crash dumps should be dumped.
+ virtual bool GetShouldDumpLargerDumps(bool is_per_user_install);
+
+ // Returns the result code to return when breakpad failed to respawn a
+ // crashed process.
+ virtual int GetResultCodeRespawnFailed();
+#endif
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
+ // Returns a textual description of the product type and version to include
+ // in the crash report.
+ virtual void GetProductNameAndVersion(std::string* product_name,
+ std::string* version);
+
+ virtual base::FilePath GetReporterLogFilename();
+#endif
+
+ // The location where minidump files should be written. Returns true if
+ // |crash_dir| was set.
+ virtual bool GetCrashDumpLocation(base::FilePath* crash_dir);
+
+#if defined(OS_POSIX)
+ // Sets a function that'll be invoked to dump the current process when
+ // without crashing.
+ virtual void SetDumpWithoutCrashingFunction(void (*function)());
+#endif
+
+ // Register all of the potential crash keys that can be sent to the crash
+ // reporting server. Returns the size of the union of all keys.
+ virtual size_t RegisterCrashKeys();
+
+ // Returns true if running in unattended mode (for automated testing).
+ virtual bool IsRunningUnattended();
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // Returns true if the user has given consent to collect stats.
+ virtual bool GetCollectStatsConsent();
+#endif
+
+#if defined(OS_ANDROID)
+ // Returns the descriptor key of the android minidump global descriptor.
+ virtual int GetAndroidMinidumpDescriptor();
+#endif
+
+#if defined(OS_MACOSX)
+ // Install additional breakpad filter callbacks.
+ virtual void InstallAdditionalFilters(BreakpadRef breakpad);
+#endif
+};
+
+} // namespace breakpad
+
+#endif // COMPONENTS_BREAKPAD_BREAKPAD_CLIENT_H_
diff --git a/chromium/components/browser_context_keyed_service.gypi b/chromium/components/browser_context_keyed_service.gypi
new file mode 100644
index 00000000000..f9cbee0c4bc
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service.gypi
@@ -0,0 +1,44 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'browser_context_keyed_service',
+ 'type': '<(component)',
+ 'defines': [
+ 'BROWSER_CONTEXT_KEYED_SERVICE_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [ 4267, ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_prefs',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../content/content.gyp:content_common',
+ 'user_prefs',
+ ],
+ 'sources': [
+ 'browser_context_keyed_service/browser_context_keyed_service_export.h',
+ 'browser_context_keyed_service/browser_context_dependency_manager.cc',
+ 'browser_context_keyed_service/browser_context_dependency_manager.h',
+ 'browser_context_keyed_service/browser_context_keyed_base_factory.h',
+ 'browser_context_keyed_service/browser_context_keyed_base_factory.cc',
+ 'browser_context_keyed_service/browser_context_keyed_service.h',
+ 'browser_context_keyed_service/browser_context_keyed_service_factory.cc',
+ 'browser_context_keyed_service/browser_context_keyed_service_factory.h',
+ 'browser_context_keyed_service/dependency_graph.cc',
+ 'browser_context_keyed_service/dependency_graph.h',
+ 'browser_context_keyed_service/dependency_node.h',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service.cc',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service.h',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc',
+ 'browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/browser_context_keyed_service/DEPS b/chromium/components/browser_context_keyed_service/DEPS
new file mode 100644
index 00000000000..1c35d9ca694
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/browser",
+]
diff --git a/chromium/components/browser_context_keyed_service/OWNERS b/chromium/components/browser_context_keyed_service/OWNERS
new file mode 100644
index 00000000000..0a6b6d0a421
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/OWNERS
@@ -0,0 +1,4 @@
+erg@chromium.org
+mirandac@chromium.org
+phajdan.jr@chromium.org
+rlp@chromium.org
diff --git a/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc
new file mode 100644
index 00000000000..1031f4e8310
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc
@@ -0,0 +1,159 @@
+// 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/browser_context_keyed_service/browser_context_dependency_manager.h"
+
+#include <algorithm>
+#include <deque>
+#include <iterator>
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_base_factory.h"
+#include "content/public/browser/browser_context.h"
+
+#ifndef NDEBUG
+#include "base/command_line.h"
+#include "base/file_util.h"
+
+// Dumps dependency information about our browser context keyed services
+// into a dot file in the browser context directory.
+const char kDumpBrowserContextDependencyGraphFlag[] =
+ "dump-browser-context-graph";
+#endif // NDEBUG
+
+void BrowserContextDependencyManager::AddComponent(
+ BrowserContextKeyedBaseFactory* component) {
+ dependency_graph_.AddNode(component);
+}
+
+void BrowserContextDependencyManager::RemoveComponent(
+ BrowserContextKeyedBaseFactory* component) {
+ dependency_graph_.RemoveNode(component);
+}
+
+void BrowserContextDependencyManager::AddEdge(
+ BrowserContextKeyedBaseFactory* depended,
+ BrowserContextKeyedBaseFactory* dependee) {
+ dependency_graph_.AddEdge(depended, dependee);
+}
+
+void BrowserContextDependencyManager::CreateBrowserContextServices(
+ content::BrowserContext* context, bool is_testing_context) {
+ TRACE_EVENT0("browser",
+ "BrowserContextDependencyManager::CreateBrowserContextServices")
+#ifndef NDEBUG
+ // Unmark |context| as dead. This exists because of unit tests, which will
+ // often have similar stack structures. 0xWhatever might be created, go out
+ // of scope, and then a new BrowserContext object might be created
+ // at 0xWhatever.
+ dead_context_pointers_.erase(context);
+#endif
+
+ std::vector<DependencyNode*> construction_order;
+ if (!dependency_graph_.GetConstructionOrder(&construction_order)) {
+ NOTREACHED();
+ }
+
+#ifndef NDEBUG
+ DumpBrowserContextDependencies(context);
+#endif
+
+ for (size_t i = 0; i < construction_order.size(); i++) {
+ BrowserContextKeyedBaseFactory* factory =
+ static_cast<BrowserContextKeyedBaseFactory*>(construction_order[i]);
+
+ if (!context->IsOffTheRecord()) {
+ // We only register preferences on normal contexts because the incognito
+ // context shares the pref service with the normal one.
+ factory->RegisterUserPrefsOnBrowserContext(context);
+ }
+
+ if (is_testing_context && factory->ServiceIsNULLWhileTesting()) {
+ factory->SetEmptyTestingFactory(context);
+ } else if (factory->ServiceIsCreatedWithBrowserContext()) {
+ // Create the service.
+ factory->CreateServiceNow(context);
+ }
+ }
+}
+
+void BrowserContextDependencyManager::DestroyBrowserContextServices(
+ content::BrowserContext* context) {
+ std::vector<DependencyNode*> destruction_order;
+ if (!dependency_graph_.GetDestructionOrder(&destruction_order)) {
+ NOTREACHED();
+ }
+
+#ifndef NDEBUG
+ DumpBrowserContextDependencies(context);
+#endif
+
+ for (size_t i = 0; i < destruction_order.size(); i++) {
+ BrowserContextKeyedBaseFactory* factory =
+ static_cast<BrowserContextKeyedBaseFactory*>(destruction_order[i]);
+ factory->BrowserContextShutdown(context);
+ }
+
+#ifndef NDEBUG
+ // The context is now dead to the rest of the program.
+ dead_context_pointers_.insert(context);
+#endif
+
+ for (size_t i = 0; i < destruction_order.size(); i++) {
+ BrowserContextKeyedBaseFactory* factory =
+ static_cast<BrowserContextKeyedBaseFactory*>(destruction_order[i]);
+ factory->BrowserContextDestroyed(context);
+ }
+}
+
+#ifndef NDEBUG
+void BrowserContextDependencyManager::AssertBrowserContextWasntDestroyed(
+ content::BrowserContext* context) {
+ if (dead_context_pointers_.find(context) != dead_context_pointers_.end()) {
+ NOTREACHED() << "Attempted to access a BrowserContext that was ShutDown(). "
+ << "This is most likely a heap smasher in progress. After "
+ << "BrowserContextKeyedService::Shutdown() completes, your "
+ << "service MUST NOT refer to depended BrowserContext "
+ << "services again.";
+ }
+}
+#endif
+
+// static
+BrowserContextDependencyManager*
+BrowserContextDependencyManager::GetInstance() {
+ return Singleton<BrowserContextDependencyManager>::get();
+}
+
+BrowserContextDependencyManager::BrowserContextDependencyManager() {
+}
+
+BrowserContextDependencyManager::~BrowserContextDependencyManager() {
+}
+
+#ifndef NDEBUG
+namespace {
+
+std::string BrowserContextKeyedBaseFactoryGetNodeName(DependencyNode* node) {
+ return static_cast<BrowserContextKeyedBaseFactory*>(node)->name();
+}
+
+} // namespace
+
+void BrowserContextDependencyManager::DumpBrowserContextDependencies(
+ content::BrowserContext* context) {
+ // Whenever we try to build a destruction ordering, we should also dump a
+ // dependency graph to "/path/to/context/context-dependencies.dot".
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ kDumpBrowserContextDependencyGraphFlag)) {
+ base::FilePath dot_file =
+ context->GetPath().AppendASCII("browser-context-dependencies.dot");
+ std::string contents = dependency_graph_.DumpAsGraphviz(
+ "BrowserContext",
+ base::Bind(&BrowserContextKeyedBaseFactoryGetNodeName));
+ file_util::WriteFile(dot_file, contents.c_str(), contents.size());
+ }
+}
+#endif // NDEBUG
diff --git a/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h
new file mode 100644
index 00000000000..3e96eff98b5
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h
@@ -0,0 +1,79 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_DEPENDENCY_MANAGER_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_DEPENDENCY_MANAGER_H_
+
+#include "base/memory/singleton.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+#include "components/browser_context_keyed_service/dependency_graph.h"
+
+#ifndef NDEBUG
+#include <set>
+#endif
+
+class BrowserContextKeyedBaseFactory;
+
+namespace content {
+class BrowserContext;
+}
+
+// A singleton that listens for context destruction notifications and
+// rebroadcasts them to each BrowserContextKeyedBaseFactory in a safe order
+// based on the stated dependencies by each service.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT BrowserContextDependencyManager {
+ public:
+ // Adds/Removes a component from our list of live components. Removing will
+ // also remove live dependency links.
+ void AddComponent(BrowserContextKeyedBaseFactory* component);
+ void RemoveComponent(BrowserContextKeyedBaseFactory* component);
+
+ // Adds a dependency between two factories.
+ void AddEdge(BrowserContextKeyedBaseFactory* depended,
+ BrowserContextKeyedBaseFactory* dependee);
+
+ // Called by each BrowserContext to alert us of its creation. Several services
+ // want to be started when a context is created. Testing configuration is also
+ // done at this time. (If you want your BrowserContextKeyedService to be
+ // started with the BrowserContext, override BrowserContextKeyedBaseFactory::
+ // ServiceIsCreatedWithBrowserContext() to return true.)
+ void CreateBrowserContextServices(content::BrowserContext* context,
+ bool is_testing_context);
+
+ // Called by each BrowserContext to alert us that we should destroy services
+ // associated with it.
+ void DestroyBrowserContextServices(content::BrowserContext* context);
+
+#ifndef NDEBUG
+ // Debugging assertion called as part of GetServiceForBrowserContext in debug
+ // mode. This will NOTREACHED() whenever the user is trying to access a stale
+ // BrowserContext*.
+ void AssertBrowserContextWasntDestroyed(content::BrowserContext* context);
+#endif
+
+ static BrowserContextDependencyManager* GetInstance();
+
+ private:
+ friend class BrowserContextDependencyManagerUnittests;
+ friend struct DefaultSingletonTraits<BrowserContextDependencyManager>;
+
+ BrowserContextDependencyManager();
+ virtual ~BrowserContextDependencyManager();
+
+#ifndef NDEBUG
+ void DumpBrowserContextDependencies(content::BrowserContext* context);
+#endif
+
+ DependencyGraph dependency_graph_;
+
+#ifndef NDEBUG
+ // A list of context objects that have gone through the Shutdown()
+ // phase. These pointers are most likely invalid, but we keep track of their
+ // locations in memory so we can nicely assert if we're asked to do anything
+ // with them.
+ std::set<content::BrowserContext*> dead_context_pointers_;
+#endif
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_DEPENDENCY_MANAGER_H_
diff --git a/chromium/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc
new file mode 100644
index 00000000000..e55294182d4
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager_unittest.cc
@@ -0,0 +1,174 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+class BrowserContextDependencyManagerUnittests : public ::testing::Test {
+ protected:
+ // To get around class access:
+ void DependOn(BrowserContextKeyedServiceFactory* child,
+ BrowserContextKeyedServiceFactory* parent) {
+ child->DependsOn(parent);
+ }
+
+ BrowserContextDependencyManager* manager() { return &dependency_manager_; }
+
+ std::vector<std::string>* shutdown_order() { return &shutdown_order_; }
+
+ private:
+ BrowserContextDependencyManager dependency_manager_;
+
+ std::vector<std::string> shutdown_order_;
+};
+
+class TestService : public BrowserContextKeyedServiceFactory {
+ public:
+ TestService(const std::string& name,
+ std::vector<std::string>* fill_on_shutdown,
+ BrowserContextDependencyManager* manager)
+ : BrowserContextKeyedServiceFactory("TestService", manager),
+ name_(name),
+ fill_on_shutdown_(fill_on_shutdown) {
+ }
+
+ virtual BrowserContextKeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const OVERRIDE {
+ ADD_FAILURE() << "This isn't part of the tests!";
+ return NULL;
+ }
+
+ virtual void BrowserContextShutdown(
+ content::BrowserContext* context) OVERRIDE {
+ fill_on_shutdown_->push_back(name_);
+ }
+
+ private:
+ const std::string name_;
+ std::vector<std::string>* fill_on_shutdown_;
+};
+
+// Tests that we can deal with a single component.
+TEST_F(BrowserContextDependencyManagerUnittests, SingleCase) {
+ TestService service("service", shutdown_order(), manager());
+
+ manager()->DestroyBrowserContextServices(NULL);
+
+ ASSERT_EQ(1U, shutdown_order()->size());
+ EXPECT_STREQ("service", (*shutdown_order())[0].c_str());
+}
+
+// Tests that we get a simple one component depends on the other case.
+TEST_F(BrowserContextDependencyManagerUnittests, SimpleDependency) {
+ TestService parent("parent", shutdown_order(), manager());
+ TestService child("child", shutdown_order(), manager());
+ DependOn(&child, &parent);
+
+ manager()->DestroyBrowserContextServices(NULL);
+
+ ASSERT_EQ(2U, shutdown_order()->size());
+ EXPECT_STREQ("child", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("parent", (*shutdown_order())[1].c_str());
+}
+
+// Tests two children, one parent
+TEST_F(BrowserContextDependencyManagerUnittests, TwoChildrenOneParent) {
+ TestService parent("parent", shutdown_order(), manager());
+ TestService child1("child1", shutdown_order(), manager());
+ TestService child2("child2", shutdown_order(), manager());
+ DependOn(&child1, &parent);
+ DependOn(&child2, &parent);
+
+ manager()->DestroyBrowserContextServices(NULL);
+
+ ASSERT_EQ(3U, shutdown_order()->size());
+ EXPECT_STREQ("child2", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("child1", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("parent", (*shutdown_order())[2].c_str());
+}
+
+// Tests an M configuration
+TEST_F(BrowserContextDependencyManagerUnittests, MConfiguration) {
+ TestService parent1("parent1", shutdown_order(), manager());
+ TestService parent2("parent2", shutdown_order(), manager());
+
+ TestService child_of_1("child_of_1", shutdown_order(), manager());
+ DependOn(&child_of_1, &parent1);
+
+ TestService child_of_12("child_of_12", shutdown_order(), manager());
+ DependOn(&child_of_12, &parent1);
+ DependOn(&child_of_12, &parent2);
+
+ TestService child_of_2("child_of_2", shutdown_order(), manager());
+ DependOn(&child_of_2, &parent2);
+
+ manager()->DestroyBrowserContextServices(NULL);
+
+ ASSERT_EQ(5U, shutdown_order()->size());
+ EXPECT_STREQ("child_of_2", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("child_of_12", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("child_of_1", (*shutdown_order())[2].c_str());
+ EXPECT_STREQ("parent2", (*shutdown_order())[3].c_str());
+ EXPECT_STREQ("parent1", (*shutdown_order())[4].c_str());
+}
+
+// Tests that it can deal with a simple diamond.
+TEST_F(BrowserContextDependencyManagerUnittests, DiamondConfiguration) {
+ TestService parent("parent", shutdown_order(), manager());
+
+ TestService middle_row_1("middle_row_1", shutdown_order(), manager());
+ DependOn(&middle_row_1, &parent);
+
+ TestService middle_row_2("middle_row_2", shutdown_order(), manager());
+ DependOn(&middle_row_2, &parent);
+
+ TestService bottom("bottom", shutdown_order(), manager());
+ DependOn(&bottom, &middle_row_1);
+ DependOn(&bottom, &middle_row_2);
+
+ manager()->DestroyBrowserContextServices(NULL);
+
+ ASSERT_EQ(4U, shutdown_order()->size());
+ EXPECT_STREQ("bottom", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("middle_row_2", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("middle_row_1", (*shutdown_order())[2].c_str());
+ EXPECT_STREQ("parent", (*shutdown_order())[3].c_str());
+}
+
+// A final test that works with a more complex graph.
+TEST_F(BrowserContextDependencyManagerUnittests, ComplexGraph) {
+ TestService everything_depends_on_me("everything_depends_on_me",
+ shutdown_order(), manager());
+
+ TestService intermediary_service("intermediary_service",
+ shutdown_order(), manager());
+ DependOn(&intermediary_service, &everything_depends_on_me);
+
+ TestService specialized_service("specialized_service",
+ shutdown_order(), manager());
+ DependOn(&specialized_service, &everything_depends_on_me);
+ DependOn(&specialized_service, &intermediary_service);
+
+ TestService other_root("other_root", shutdown_order(), manager());
+
+ TestService other_intermediary("other_intermediary",
+ shutdown_order(), manager());
+ DependOn(&other_intermediary, &other_root);
+
+ TestService bottom("bottom", shutdown_order(), manager());
+ DependOn(&bottom, &specialized_service);
+ DependOn(&bottom, &other_intermediary);
+
+ manager()->DestroyBrowserContextServices(NULL);
+
+ ASSERT_EQ(6U, shutdown_order()->size());
+ EXPECT_STREQ("bottom", (*shutdown_order())[0].c_str());
+ EXPECT_STREQ("specialized_service", (*shutdown_order())[1].c_str());
+ EXPECT_STREQ("other_intermediary", (*shutdown_order())[2].c_str());
+ EXPECT_STREQ("intermediary_service", (*shutdown_order())[3].c_str());
+ EXPECT_STREQ("other_root", (*shutdown_order())[4].c_str());
+ EXPECT_STREQ("everything_depends_on_me", (*shutdown_order())[5].c_str());
+}
diff --git a/chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc b/chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc
new file mode 100644
index 00000000000..e6caa69d63c
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc
@@ -0,0 +1,113 @@
+// 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/browser_context_keyed_service/browser_context_keyed_base_factory.h"
+
+#include "base/prefs/pref_service.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#include "components/user_prefs/pref_registry_syncable.h"
+#include "components/user_prefs/user_prefs.h"
+#include "content/public/browser/browser_context.h"
+
+BrowserContextKeyedBaseFactory::BrowserContextKeyedBaseFactory(
+ const char* name, BrowserContextDependencyManager* manager)
+ : dependency_manager_(manager)
+#ifndef NDEBUG
+ , service_name_(name)
+#endif
+{
+ dependency_manager_->AddComponent(this);
+}
+
+BrowserContextKeyedBaseFactory::~BrowserContextKeyedBaseFactory() {
+ dependency_manager_->RemoveComponent(this);
+}
+
+void BrowserContextKeyedBaseFactory::DependsOn(
+ BrowserContextKeyedBaseFactory* rhs) {
+ dependency_manager_->AddEdge(rhs, this);
+}
+
+content::BrowserContext* BrowserContextKeyedBaseFactory::GetBrowserContextToUse(
+ content::BrowserContext* context) const {
+ DCHECK(CalledOnValidThread());
+
+#ifndef NDEBUG
+ dependency_manager_->AssertBrowserContextWasntDestroyed(context);
+#endif
+
+ // Safe default for the Incognito mode: no service.
+ if (context->IsOffTheRecord())
+ return NULL;
+
+ return context;
+}
+
+void BrowserContextKeyedBaseFactory::RegisterUserPrefsOnBrowserContext(
+ content::BrowserContext* context) {
+ // Safe timing for pref registration is hard. Previously, we made
+ // BrowserContext responsible for all pref registration on every service
+ // that used BrowserContext. Now we don't and there are timing issues.
+ //
+ // With normal contexts, prefs can simply be registered at
+ // BrowserContextDependencyManager::CreateBrowserContextServices time.
+ // With incognito contexts, we just never register since incognito contexts
+ // share the same pref services with their parent contexts.
+ //
+ // TestingBrowserContexts throw a wrench into the mix, in that some tests will
+ // swap out the PrefService after we've registered user prefs on the original
+ // PrefService. Test code that does this is responsible for either manually
+ // invoking RegisterProfilePrefs() on the appropriate
+ // BrowserContextKeyedServiceFactory associated with the prefs they need,
+ // or they can use SetTestingFactory() and create a service (since service
+ // creation with a factory method causes registration to happen at service
+ // creation time).
+ //
+ // Now that services are responsible for declaring their preferences, we have
+ // to enforce a uniquenes check here because some tests create one context and
+ // multiple services of the same type attached to that context (serially, not
+ // parallel) and we don't want to register multiple times on the same context.
+ DCHECK(!context->IsOffTheRecord());
+
+ std::set<content::BrowserContext*>::iterator it =
+ registered_preferences_.find(context);
+ if (it == registered_preferences_.end()) {
+ PrefService* prefs = user_prefs::UserPrefs::Get(context);
+ user_prefs::PrefRegistrySyncable* registry =
+ static_cast<user_prefs::PrefRegistrySyncable*>(
+ prefs->DeprecatedGetPrefRegistry());
+ RegisterProfilePrefs(registry);
+ registered_preferences_.insert(context);
+ }
+}
+
+bool
+BrowserContextKeyedBaseFactory::ServiceIsCreatedWithBrowserContext() const {
+ return false;
+}
+
+bool BrowserContextKeyedBaseFactory::ServiceIsNULLWhileTesting() const {
+ return false;
+}
+
+void BrowserContextKeyedBaseFactory::BrowserContextDestroyed(
+ content::BrowserContext* context) {
+ // While object destruction can be customized in ways where the object is
+ // only dereferenced, this still must run on the UI thread.
+ DCHECK(CalledOnValidThread());
+
+ registered_preferences_.erase(context);
+}
+
+bool BrowserContextKeyedBaseFactory::ArePreferencesSetOn(
+ content::BrowserContext* context) const {
+ return registered_preferences_.find(context) !=
+ registered_preferences_.end();
+}
+
+void BrowserContextKeyedBaseFactory::MarkPreferencesSetOn(
+ content::BrowserContext* context) {
+ DCHECK(!ArePreferencesSetOn(context));
+ registered_preferences_.insert(context);
+}
diff --git a/chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.h b/chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.h
new file mode 100644
index 00000000000..1c937d3edeb
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.h
@@ -0,0 +1,140 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_BASE_FACTORY_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_BASE_FACTORY_H_
+
+#include <set>
+
+#include "base/threading/non_thread_safe.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+#include "components/browser_context_keyed_service/dependency_node.h"
+
+class BrowserContextDependencyManager;
+class PrefService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+// Base class for Factories that take a BrowserContext object and return some
+// service.
+//
+// Unless you're trying to make a new type of Factory, you probably don't want
+// this class, but its subclasses: BrowserContextKeyedServiceFactory and
+// RefcountedBrowserContextKeyedServiceFactory. This object describes general
+// dependency management between Factories; subclasses react to lifecycle
+// events and implement memory management.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT
+BrowserContextKeyedBaseFactory
+ : public base::NonThreadSafe,
+ NON_EXPORTED_BASE(public DependencyNode) {
+ public:
+ // Registers preferences used in this service on the pref service of
+ // |context|. This is the public interface and is safe to be called multiple
+ // times because testing code can have multiple services of the same type
+ // attached to a single |context|.
+ void RegisterUserPrefsOnBrowserContext(content::BrowserContext* context);
+
+#ifndef NDEBUG
+ // Returns our name. We don't keep track of this in release mode.
+ const char* name() const { return service_name_; }
+#endif
+
+ protected:
+ BrowserContextKeyedBaseFactory(const char* name,
+ BrowserContextDependencyManager* manager);
+ virtual ~BrowserContextKeyedBaseFactory();
+
+ // The main public interface for declaring dependencies between services
+ // created by factories.
+ void DependsOn(BrowserContextKeyedBaseFactory* rhs);
+
+ // Interface for people building a concrete FooServiceFactory: --------------
+
+ // Finds which browser context (if any) to use.
+ virtual content::BrowserContext* GetBrowserContextToUse(
+ content::BrowserContext* context) const;
+
+ // Register any user preferences on this service. This is called during
+ // CreateBrowserContextService() since preferences are registered on a per
+ // BrowserContext basis.
+ virtual void RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {}
+
+ // By default, we create instances of a service lazily and wait until
+ // GetForBrowserContext() is called on our subclass. Some services need to be
+ // created as soon as the BrowserContext has been brought up.
+ virtual bool ServiceIsCreatedWithBrowserContext() const;
+
+ // By default, TestingBrowserContexts will be treated like normal contexts.
+ // You can override this so that by default, the service associated with the
+ // TestingBrowserContext is NULL. (This is just a shortcut around
+ // SetTestingFactory() to make sure our contexts don't directly refer to the
+ // services they use.)
+ virtual bool ServiceIsNULLWhileTesting() const;
+
+ // Interface for people building a type of BrowserContextKeyedFactory: -------
+
+ // A helper object actually listens for notifications about BrowserContext
+ // destruction, calculates the order in which things are destroyed and then
+ // does a two pass shutdown.
+ //
+ // It is up to the individual factory types to determine what this two pass
+ // shutdown means. The general framework guarantees the following:
+ //
+ // - Each BrowserContextShutdown() is called in dependency order (and you may
+ // reach out to other services during this phase).
+ //
+ // - Each BrowserContextDestroyed() is called in dependency order. We will
+ // NOTREACHED() if you attempt to GetForBrowserContext() any other service.
+ // You should delete/deref/do other final memory management things during
+ // this phase. You must also call the base class method as the last thing
+ // you do.
+ virtual void BrowserContextShutdown(content::BrowserContext* context) = 0;
+ virtual void BrowserContextDestroyed(content::BrowserContext* context);
+
+ // Returns whether we've registered the preferences on this context.
+ bool ArePreferencesSetOn(content::BrowserContext* context) const;
+
+ // Mark context as Preferences set.
+ void MarkPreferencesSetOn(content::BrowserContext* context);
+
+ private:
+ friend class BrowserContextDependencyManager;
+ friend class BrowserContextDependencyManagerUnittests;
+
+ // These two methods are for tight integration with the
+ // BrowserContextDependencyManager.
+
+ // Because of ServiceIsNULLWhileTesting(), we need a way to tell different
+ // subclasses that they should disable testing.
+ virtual void SetEmptyTestingFactory(content::BrowserContext* context) = 0;
+
+ // We also need a generalized, non-returning method that generates the object
+ // now for when we're creating the context.
+ virtual void CreateServiceNow(content::BrowserContext* context) = 0;
+
+ // Which BrowserContextDependencyManager we should communicate with.
+ // In real code, this will always be
+ // BrowserContextDependencyManager::GetInstance(), but unit tests will want
+ // to use their own copy.
+ BrowserContextDependencyManager* dependency_manager_;
+
+ // BrowserContexts that have this service's preferences registered on them.
+ std::set<content::BrowserContext*> registered_preferences_;
+
+#if !defined(NDEBUG)
+ // A static string passed in to our constructor. Should be unique across all
+ // services. This is used only for debugging in debug mode. (We can print
+ // pretty graphs with GraphViz with this information.)
+ const char* service_name_;
+#endif
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_BASE_FACTORY_H_
diff --git a/chromium/components/browser_context_keyed_service/browser_context_keyed_service.h b/chromium/components/browser_context_keyed_service/browser_context_keyed_service.h
new file mode 100644
index 00000000000..9bf286e37d2
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_keyed_service.h
@@ -0,0 +1,32 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_H_
+
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+
+class BrowserContextKeyedServiceFactory;
+
+// Base class for all BrowserContextKeyedServices to allow for correct
+// destruction order.
+//
+// Many services that hang off BrowserContext have a two-pass shutdown. Many
+// subsystems need a first pass shutdown phase where they drop references. Not
+// all services will need this, so there's a default implementation. Only once
+// every system has been given a chance to drop references do we start deleting
+// objects.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT BrowserContextKeyedService {
+ public:
+ // The first pass is to call Shutdown on a BrowserContextKeyedService.
+ virtual void Shutdown() {}
+
+ protected:
+ friend class BrowserContextKeyedServiceFactory;
+
+ // The second pass is the actual deletion of each object.
+ virtual ~BrowserContextKeyedService() {}
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_H_
diff --git a/chromium/components/browser_context_keyed_service/browser_context_keyed_service_export.h b/chromium/components/browser_context_keyed_service/browser_context_keyed_service_export.h
new file mode 100644
index 00000000000..0d0978486c0
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_keyed_service_export.h
@@ -0,0 +1,29 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_EXPORT_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(BROWSER_CONTEXT_KEYED_SERVICE_IMPLEMENTATION)
+#define BROWSER_CONTEXT_KEYED_SERVICE_EXPORT __declspec(dllexport)
+#else
+#define BROWSER_CONTEXT_KEYED_SERVICE_EXPORT __declspec(dllimport)
+#endif // defined(BROWSER_CONTEXT_KEYED_SERVICE_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(BROWSER_CONTEXT_KEYED_SERVICE_IMPLEMENTATION)
+#define BROWSER_CONTEXT_KEYED_SERVICE_EXPORT __attribute__((visibility("default")))
+#else
+#define BROWSER_CONTEXT_KEYED_SERVICE_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define BROWSER_CONTEXT_KEYED_SERVICE_EXPORT
+#endif
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_EXPORT_H_
diff --git a/chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc b/chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc
new file mode 100644
index 00000000000..619318ef584
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.cc
@@ -0,0 +1,130 @@
+// 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/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "content/public/browser/browser_context.h"
+
+void BrowserContextKeyedServiceFactory::SetTestingFactory(
+ content::BrowserContext* context, FactoryFunction factory) {
+ // Destroying the context may cause us to lose data about whether |context|
+ // has our preferences registered on it (since the context object itself
+ // isn't dead). See if we need to readd it once we've gone through normal
+ // destruction.
+ bool add_context = ArePreferencesSetOn(context);
+
+ // We have to go through the shutdown and destroy mechanisms because there
+ // are unit tests that create a service on a context and then change the
+ // testing service mid-test.
+ BrowserContextShutdown(context);
+ BrowserContextDestroyed(context);
+
+ if (add_context)
+ MarkPreferencesSetOn(context);
+
+ factories_[context] = factory;
+}
+
+BrowserContextKeyedService*
+BrowserContextKeyedServiceFactory::SetTestingFactoryAndUse(
+ content::BrowserContext* context,
+ FactoryFunction factory) {
+ DCHECK(factory);
+ SetTestingFactory(context, factory);
+ return GetServiceForBrowserContext(context, true);
+}
+
+BrowserContextKeyedServiceFactory::BrowserContextKeyedServiceFactory(
+ const char* name, BrowserContextDependencyManager* manager)
+ : BrowserContextKeyedBaseFactory(name, manager) {
+}
+
+BrowserContextKeyedServiceFactory::~BrowserContextKeyedServiceFactory() {
+ DCHECK(mapping_.empty());
+}
+
+BrowserContextKeyedService*
+BrowserContextKeyedServiceFactory::GetServiceForBrowserContext(
+ content::BrowserContext* context,
+ bool create) {
+ context = GetBrowserContextToUse(context);
+ if (!context)
+ return NULL;
+
+ // NOTE: If you modify any of the logic below, make sure to update the
+ // refcounted version in refcounted_context_keyed_service_factory.cc!
+ BrowserContextKeyedServices::const_iterator it = mapping_.find(context);
+ if (it != mapping_.end())
+ return it->second;
+
+ // Object not found.
+ if (!create)
+ return NULL; // And we're forbidden from creating one.
+
+ // Create new object.
+ // Check to see if we have a per-BrowserContext testing factory that we should
+ // use instead of default behavior.
+ BrowserContextKeyedService* service = NULL;
+ BrowserContextOverriddenFunctions::const_iterator jt =
+ factories_.find(context);
+ if (jt != factories_.end()) {
+ if (jt->second) {
+ if (!context->IsOffTheRecord())
+ RegisterUserPrefsOnBrowserContext(context);
+ service = jt->second(context);
+ }
+ } else {
+ service = BuildServiceInstanceFor(context);
+ }
+
+ Associate(context, service);
+ return service;
+}
+
+void BrowserContextKeyedServiceFactory::Associate(
+ content::BrowserContext* context,
+ BrowserContextKeyedService* service) {
+ DCHECK(!ContainsKey(mapping_, context));
+ mapping_.insert(std::make_pair(context, service));
+}
+
+void BrowserContextKeyedServiceFactory::BrowserContextShutdown(
+ content::BrowserContext* context) {
+ BrowserContextKeyedServices::iterator it = mapping_.find(context);
+ if (it != mapping_.end() && it->second)
+ it->second->Shutdown();
+}
+
+void BrowserContextKeyedServiceFactory::BrowserContextDestroyed(
+ content::BrowserContext* context) {
+ BrowserContextKeyedServices::iterator it = mapping_.find(context);
+ if (it != mapping_.end()) {
+ delete it->second;
+ mapping_.erase(it);
+ }
+
+ // For unit tests, we also remove the factory function both so we don't
+ // maintain a big map of dead pointers, but also since we may have a second
+ // object that lives at the same address (see other comments about unit tests
+ // in this file).
+ factories_.erase(context);
+
+ BrowserContextKeyedBaseFactory::BrowserContextDestroyed(context);
+}
+
+void BrowserContextKeyedServiceFactory::SetEmptyTestingFactory(
+ content::BrowserContext* context) {
+ SetTestingFactory(context, NULL);
+}
+
+void BrowserContextKeyedServiceFactory::CreateServiceNow(
+ content::BrowserContext* context) {
+ GetServiceForBrowserContext(context, true);
+}
diff --git a/chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.h b/chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.h
new file mode 100644
index 00000000000..2775e4cc45b
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/browser_context_keyed_service_factory.h
@@ -0,0 +1,122 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_base_factory.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+
+class BrowserContextDependencyManager;
+class BrowserContextKeyedService;
+
+// Base class for Factories that take a BrowserContext object and return some
+// service on a one-to-one mapping. Each factory that derives from this class
+// *must* be a Singleton (only unit tests don't do that).
+//
+// We do this because services depend on each other and we need to control
+// shutdown/destruction order. In each derived classes' constructors, the
+// implementors must explicitly state which services are depended on.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT BrowserContextKeyedServiceFactory
+ : public BrowserContextKeyedBaseFactory {
+ public:
+ // A function that supplies the instance of a BrowserContextKeyedService
+ // for a given BrowserContext. This is used primarily for testing, where
+ // we want to feed a specific mock into the BCKSF system.
+ typedef BrowserContextKeyedService*
+ (*FactoryFunction)(content::BrowserContext* context);
+
+ // Associates |factory| with |context| so that |factory| is used to create
+ // the BrowserContextKeyedService when requested. |factory| can be NULL
+ // to signal that BrowserContextKeyedService should be NULL. Multiple calls to
+ // SetTestingFactory() are allowed; previous services will be shut down.
+ void SetTestingFactory(content::BrowserContext* context,
+ FactoryFunction factory);
+
+ // Associates |factory| with |context| and immediately returns the created
+ // BrowserContextKeyedService. Since the factory will be used immediately,
+ // it may not be NULL.
+ BrowserContextKeyedService* SetTestingFactoryAndUse(
+ content::BrowserContext* context,
+ FactoryFunction factory);
+
+ protected:
+ // BrowserContextKeyedServiceFactories must communicate with a
+ // BrowserContextDependencyManager. For all non-test code, write your subclass
+ // constructors like this:
+ //
+ // MyServiceFactory::MyServiceFactory()
+ // : BrowserContextKeyedServiceFactory(
+ // "MyService",
+ // BrowserContextDependencyManager::GetInstance())
+ // {}
+ BrowserContextKeyedServiceFactory(const char* name,
+ BrowserContextDependencyManager* manager);
+ virtual ~BrowserContextKeyedServiceFactory();
+
+ // Common implementation that maps |context| to some service object. Deals
+ // with incognito contexts per subclass instructions with
+ // GetBrowserContextRedirectedInIncognito() and
+ // GetBrowserContextOwnInstanceInIncognito() through the
+ // GetBrowserContextToUse() method on the base. If |create| is true, the
+ // service will be created using BuildServiceInstanceFor() if it doesn't
+ // already exist.
+ BrowserContextKeyedService* GetServiceForBrowserContext(
+ content::BrowserContext* context,
+ bool create);
+
+ // Maps |context| to |service| with debug checks to prevent duplication.
+ void Associate(content::BrowserContext* context,
+ BrowserContextKeyedService* service);
+
+ // All subclasses of BrowserContextKeyedServiceFactory must return a
+ // BrowserContextKeyedService instead of just a BrowserContextKeyedBase.
+ virtual BrowserContextKeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const = 0;
+
+ // A helper object actually listens for notifications about BrowserContext
+ // destruction, calculates the order in which things are destroyed and then
+ // does a two pass shutdown.
+ //
+ // First, BrowserContextShutdown() is called on every ServiceFactory and will
+ // usually call BrowserContextKeyedService::Shutdown(), which gives each
+ // BrowserContextKeyedService a chance to remove dependencies on other
+ // services that it may be holding.
+ //
+ // Secondly, BrowserContextDestroyed() is called on every ServiceFactory
+ // and the default implementation removes it from |mapping_| and deletes
+ // the pointer.
+ virtual void BrowserContextShutdown(
+ content::BrowserContext* context) OVERRIDE;
+ virtual void BrowserContextDestroyed(
+ content::BrowserContext* context) OVERRIDE;
+
+ virtual void SetEmptyTestingFactory(
+ content::BrowserContext* context) OVERRIDE;
+ virtual void CreateServiceNow(content::BrowserContext* context) OVERRIDE;
+
+ private:
+ friend class BrowserContextDependencyManager;
+ friend class BrowserContextDependencyManagerUnittests;
+
+ typedef std::map<content::BrowserContext*, BrowserContextKeyedService*>
+ BrowserContextKeyedServices;
+ typedef std::map<content::BrowserContext*, FactoryFunction>
+ BrowserContextOverriddenFunctions;
+
+ // The mapping between a BrowserContext and its service.
+ std::map<content::BrowserContext*, BrowserContextKeyedService*> mapping_;
+
+ // The mapping between a BrowserContext and its overridden FactoryFunction.
+ std::map<content::BrowserContext*, FactoryFunction> factories_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserContextKeyedServiceFactory);
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
diff --git a/chromium/components/browser_context_keyed_service/dependency_graph.cc b/chromium/components/browser_context_keyed_service/dependency_graph.cc
new file mode 100644
index 00000000000..253f5dd0fd1
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/dependency_graph.cc
@@ -0,0 +1,166 @@
+// 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/browser_context_keyed_service/dependency_graph.h"
+
+#include <algorithm>
+#include <deque>
+#include <iterator>
+
+DependencyGraph::DependencyGraph() {
+}
+
+DependencyGraph::~DependencyGraph() {
+}
+
+void DependencyGraph::AddNode(DependencyNode* node) {
+ all_nodes_.push_back(node);
+ construction_order_.clear();
+}
+
+void DependencyGraph::RemoveNode(DependencyNode* node) {
+ all_nodes_.erase(std::remove(all_nodes_.begin(),
+ all_nodes_.end(),
+ node),
+ all_nodes_.end());
+
+ // Remove all dependency edges that contain this node.
+ EdgeMap::iterator it = edges_.begin();
+ while (it != edges_.end()) {
+ EdgeMap::iterator temp = it;
+ ++it;
+
+ if (temp->first == node || temp->second == node)
+ edges_.erase(temp);
+ }
+
+ construction_order_.clear();
+}
+
+void DependencyGraph::AddEdge(DependencyNode* depended,
+ DependencyNode* dependee) {
+ edges_.insert(std::make_pair(depended, dependee));
+ construction_order_.clear();
+}
+
+bool DependencyGraph::GetConstructionOrder(
+ std::vector<DependencyNode*>* order) {
+ if (construction_order_.empty() && !BuildConstructionOrder())
+ return false;
+
+ *order = construction_order_;
+ return true;
+}
+
+bool DependencyGraph::GetDestructionOrder(
+ std::vector<DependencyNode*>* order) {
+ if (construction_order_.empty() && !BuildConstructionOrder())
+ return false;
+
+ *order = construction_order_;
+
+ // Destroy nodes in reverse order.
+ std::reverse(order->begin(), order->end());
+
+ return true;
+}
+
+bool DependencyGraph::BuildConstructionOrder() {
+ // Step 1: Build a set of nodes with no incoming edges.
+ std::deque<DependencyNode*> queue;
+ std::copy(all_nodes_.begin(),
+ all_nodes_.end(),
+ std::back_inserter(queue));
+
+ std::deque<DependencyNode*>::iterator queue_end = queue.end();
+ for (EdgeMap::const_iterator it = edges_.begin();
+ it != edges_.end(); ++it) {
+ queue_end = std::remove(queue.begin(), queue_end, it->second);
+ }
+ queue.erase(queue_end, queue.end());
+
+ // Step 2: Do the Kahn topological sort.
+ std::vector<DependencyNode*> output;
+ EdgeMap edges(edges_);
+ while (!queue.empty()) {
+ DependencyNode* node = queue.front();
+ queue.pop_front();
+ output.push_back(node);
+
+ std::pair<EdgeMap::iterator, EdgeMap::iterator> range =
+ edges.equal_range(node);
+ EdgeMap::iterator it = range.first;
+ while (it != range.second) {
+ DependencyNode* dest = it->second;
+ EdgeMap::iterator temp = it;
+ it++;
+ edges.erase(temp);
+
+ bool has_incoming_edges = false;
+ for (EdgeMap::iterator jt = edges.begin(); jt != edges.end(); ++jt) {
+ if (jt->second == dest) {
+ has_incoming_edges = true;
+ break;
+ }
+ }
+
+ if (!has_incoming_edges)
+ queue.push_back(dest);
+ }
+ }
+
+ if (!edges.empty()) {
+ // Dependency graph has a cycle.
+ return false;
+ }
+
+ construction_order_ = output;
+ return true;
+}
+
+std::string DependencyGraph::DumpAsGraphviz(
+ const std::string& toplevel_name,
+ const base::Callback<std::string(DependencyNode*)>&
+ node_name_callback) const {
+ std::string result("digraph {\n");
+
+ // Make a copy of all nodes.
+ std::deque<DependencyNode*> nodes;
+ std::copy(all_nodes_.begin(), all_nodes_.end(), std::back_inserter(nodes));
+
+ // State all dependencies and remove |second| so we don't generate an
+ // implicit dependency on the top level node.
+ std::deque<DependencyNode*>::iterator nodes_end(nodes.end());
+ result.append(" /* Dependencies */\n");
+ for (EdgeMap::const_iterator it = edges_.begin(); it != edges_.end(); ++it) {
+ result.append(" ");
+ result.append(node_name_callback.Run(it->second));
+ result.append(" -> ");
+ result.append(node_name_callback.Run(it->first));
+ result.append(";\n");
+
+ nodes_end = std::remove(nodes.begin(), nodes_end, it->second);
+ }
+ nodes.erase(nodes_end, nodes.end());
+
+ // Every node that doesn't depend on anything else will implicitly depend on
+ // the top level node.
+ result.append("\n /* Toplevel attachments */\n");
+ for (std::deque<DependencyNode*>::const_iterator it =
+ nodes.begin(); it != nodes.end(); ++it) {
+ result.append(" ");
+ result.append(node_name_callback.Run(*it));
+ result.append(" -> ");
+ result.append(toplevel_name);
+ result.append(";\n");
+ }
+
+ result.append("\n /* Toplevel node */\n");
+ result.append(" ");
+ result.append(toplevel_name);
+ result.append(" [shape=box];\n");
+
+ result.append("}\n");
+ return result;
+}
diff --git a/chromium/components/browser_context_keyed_service/dependency_graph.h b/chromium/components/browser_context_keyed_service/dependency_graph.h
new file mode 100644
index 00000000000..017049f8bf0
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/dependency_graph.h
@@ -0,0 +1,68 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_GRAPH_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_GRAPH_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+
+class DependencyNode;
+
+// Dynamic graph of dependencies between nodes.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT DependencyGraph {
+ public:
+ DependencyGraph();
+ ~DependencyGraph();
+
+ // Adds/Removes a node from our list of live nodes. Removing will
+ // also remove live dependency links.
+ void AddNode(DependencyNode* node);
+ void RemoveNode(DependencyNode* node);
+
+ // Adds a dependency between two nodes.
+ void AddEdge(DependencyNode* depended, DependencyNode* dependee);
+
+ // Topologically sorts nodes to produce a safe construction order
+ // (all nodes after their dependees).
+ bool GetConstructionOrder(
+ std::vector<DependencyNode*>* order) WARN_UNUSED_RESULT;
+
+ // Topologically sorts nodes to produce a safe destruction order
+ // (all nodes before their dependees).
+ bool GetDestructionOrder(
+ std::vector<DependencyNode*>* order) WARN_UNUSED_RESULT;
+
+ // Returns representation of the dependency graph in graphviz format.
+ std::string DumpAsGraphviz(
+ const std::string& toplevel_name,
+ const base::Callback<std::string(DependencyNode*)>&
+ node_name_callback) const;
+
+ private:
+ typedef std::multimap<DependencyNode*, DependencyNode*> EdgeMap;
+
+ // Populates |construction_order_| with computed construction order.
+ // Returns true on success.
+ bool BuildConstructionOrder() WARN_UNUSED_RESULT;
+
+ // Keeps track of all live nodes (see AddNode, RemoveNode).
+ std::vector<DependencyNode*> all_nodes_;
+
+ // Keeps track of edges of the dependency graph.
+ EdgeMap edges_;
+
+ // Cached construction order (needs rebuild with BuildConstructionOrder
+ // when empty).
+ std::vector<DependencyNode*> construction_order_;
+
+ DISALLOW_COPY_AND_ASSIGN(DependencyGraph);
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_GRAPH_H_
diff --git a/chromium/components/browser_context_keyed_service/dependency_graph_unittest.cc b/chromium/components/browser_context_keyed_service/dependency_graph_unittest.cc
new file mode 100644
index 00000000000..541ada720a6
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/dependency_graph_unittest.cc
@@ -0,0 +1,161 @@
+// 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/browser_context_keyed_service/dependency_graph.h"
+#include "components/browser_context_keyed_service/dependency_node.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class DependencyGraphTest : public testing::Test {
+};
+
+class DummyNode : public DependencyNode {
+ public:
+ explicit DummyNode(DependencyGraph* graph) : dependency_graph_(graph) {
+ dependency_graph_->AddNode(this);
+ }
+
+ ~DummyNode() {
+ dependency_graph_->RemoveNode(this);
+ }
+
+ private:
+ DependencyGraph* dependency_graph_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyNode);
+};
+
+// Tests that we can deal with a single component.
+TEST_F(DependencyGraphTest, SingleCase) {
+ DependencyGraph graph;
+ DummyNode node(&graph);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(1U, construction_order.size());
+ EXPECT_EQ(&node, construction_order[0]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(1U, destruction_order.size());
+ EXPECT_EQ(&node, destruction_order[0]);
+}
+
+// Tests that we get a simple one component depends on the other case.
+TEST_F(DependencyGraphTest, SimpleDependency) {
+ DependencyGraph graph;
+ DummyNode parent(&graph);
+ DummyNode child(&graph);
+
+ graph.AddEdge(&parent, &child);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(2U, construction_order.size());
+ EXPECT_EQ(&parent, construction_order[0]);
+ EXPECT_EQ(&child, construction_order[1]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(2U, destruction_order.size());
+ EXPECT_EQ(&child, destruction_order[0]);
+ EXPECT_EQ(&parent, destruction_order[1]);
+}
+
+// Tests two children, one parent.
+TEST_F(DependencyGraphTest, TwoChildrenOneParent) {
+ DependencyGraph graph;
+ DummyNode parent(&graph);
+ DummyNode child1(&graph);
+ DummyNode child2(&graph);
+
+ graph.AddEdge(&parent, &child1);
+ graph.AddEdge(&parent, &child2);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(3U, construction_order.size());
+ EXPECT_EQ(&parent, construction_order[0]);
+ EXPECT_EQ(&child1, construction_order[1]);
+ EXPECT_EQ(&child2, construction_order[2]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(3U, destruction_order.size());
+ EXPECT_EQ(&child2, destruction_order[0]);
+ EXPECT_EQ(&child1, destruction_order[1]);
+ EXPECT_EQ(&parent, destruction_order[2]);
+}
+
+// Tests an M configuration.
+TEST_F(DependencyGraphTest, MConfiguration) {
+ DependencyGraph graph;
+
+ DummyNode parent1(&graph);
+ DummyNode parent2(&graph);
+
+ DummyNode child_of_1(&graph);
+ graph.AddEdge(&parent1, &child_of_1);
+
+ DummyNode child_of_12(&graph);
+ graph.AddEdge(&parent1, &child_of_12);
+ graph.AddEdge(&parent2, &child_of_12);
+
+ DummyNode child_of_2(&graph);
+ graph.AddEdge(&parent2, &child_of_2);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(5U, construction_order.size());
+ EXPECT_EQ(&parent1, construction_order[0]);
+ EXPECT_EQ(&parent2, construction_order[1]);
+ EXPECT_EQ(&child_of_1, construction_order[2]);
+ EXPECT_EQ(&child_of_12, construction_order[3]);
+ EXPECT_EQ(&child_of_2, construction_order[4]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(5U, destruction_order.size());
+ EXPECT_EQ(&child_of_2, destruction_order[0]);
+ EXPECT_EQ(&child_of_12, destruction_order[1]);
+ EXPECT_EQ(&child_of_1, destruction_order[2]);
+ EXPECT_EQ(&parent2, destruction_order[3]);
+ EXPECT_EQ(&parent1, destruction_order[4]);
+}
+
+// Tests that it can deal with a simple diamond.
+TEST_F(DependencyGraphTest, DiamondConfiguration) {
+ DependencyGraph graph;
+
+ DummyNode parent(&graph);
+
+ DummyNode middle1(&graph);
+ graph.AddEdge(&parent, &middle1);
+
+ DummyNode middle2(&graph);
+ graph.AddEdge(&parent, &middle2);
+
+ DummyNode bottom(&graph);
+ graph.AddEdge(&middle1, &bottom);
+ graph.AddEdge(&middle2, &bottom);
+
+ std::vector<DependencyNode*> construction_order;
+ EXPECT_TRUE(graph.GetConstructionOrder(&construction_order));
+ ASSERT_EQ(4U, construction_order.size());
+ EXPECT_EQ(&parent, construction_order[0]);
+ EXPECT_EQ(&middle1, construction_order[1]);
+ EXPECT_EQ(&middle2, construction_order[2]);
+ EXPECT_EQ(&bottom, construction_order[3]);
+
+ std::vector<DependencyNode*> destruction_order;
+ EXPECT_TRUE(graph.GetDestructionOrder(&destruction_order));
+ ASSERT_EQ(4U, destruction_order.size());
+ EXPECT_EQ(&bottom, destruction_order[0]);
+ EXPECT_EQ(&middle2, destruction_order[1]);
+ EXPECT_EQ(&middle1, destruction_order[2]);
+ EXPECT_EQ(&parent, destruction_order[3]);
+}
+
+} // namespace
diff --git a/chromium/components/browser_context_keyed_service/dependency_node.h b/chromium/components/browser_context_keyed_service/dependency_node.h
new file mode 100644
index 00000000000..d870b673295
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/dependency_node.h
@@ -0,0 +1,16 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_NODE_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_NODE_H_
+
+// Base class representing a node in a DependencyGraph.
+class DependencyNode {
+ protected:
+ // This is intended to be used by the subclasses, not directly.
+ DependencyNode() {}
+ ~DependencyNode() {}
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_DEPENDENCY_NODE_H_
diff --git a/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc
new file mode 100644
index 00000000000..ee06dd05dd5
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.cc
@@ -0,0 +1,34 @@
+// 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/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
+
+namespace impl {
+
+// static
+void RefcountedBrowserContextKeyedServiceTraits::Destruct(
+ const RefcountedBrowserContextKeyedService* obj) {
+ if (obj->requires_destruction_on_thread_ &&
+ !content::BrowserThread::CurrentlyOn(obj->thread_id_)) {
+ content::BrowserThread::DeleteSoon(obj->thread_id_, FROM_HERE, obj);
+ } else {
+ delete obj;
+ }
+}
+
+} // namespace impl
+
+RefcountedBrowserContextKeyedService::RefcountedBrowserContextKeyedService()
+ : requires_destruction_on_thread_(false),
+ thread_id_(content::BrowserThread::UI) {
+}
+
+RefcountedBrowserContextKeyedService::RefcountedBrowserContextKeyedService(
+ const content::BrowserThread::ID thread_id)
+ : requires_destruction_on_thread_(true),
+ thread_id_(thread_id) {
+}
+
+RefcountedBrowserContextKeyedService::~RefcountedBrowserContextKeyedService() {}
+
diff --git a/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h
new file mode 100644
index 00000000000..c705898afac
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service.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_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+#include "content/public/browser/browser_thread.h"
+
+class RefcountedBrowserContextKeyedService;
+
+namespace impl {
+
+struct BROWSER_CONTEXT_KEYED_SERVICE_EXPORT
+ RefcountedBrowserContextKeyedServiceTraits {
+ static void Destruct(const RefcountedBrowserContextKeyedService* obj);
+};
+
+} // namespace impl
+
+// Base class for refcounted objects that hang off the BrowserContext.
+//
+// The two pass shutdown described in BrowserContextKeyedService works a bit
+// differently because there could be outstanding references on other
+// threads. ShutdownOnUIThread() will be called on the UI thread, and then the
+// destructor will run when the last reference is dropped, which may or may not
+// be after the corresponding BrowserContext has been destroyed.
+//
+// Optionally, if you initialize your service with the constructor that takes a
+// thread ID, your service will be deleted on that thread. We can't use
+// content::DeleteOnThread<> directly because
+// RefcountedBrowserContextKeyedService must be one type that
+// RefcountedBrowserContextKeyedServiceFactory can use.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT RefcountedBrowserContextKeyedService
+ : public base::RefCountedThreadSafe<
+ RefcountedBrowserContextKeyedService,
+ impl::RefcountedBrowserContextKeyedServiceTraits> {
+ public:
+ // Unlike BrowserContextKeyedService, ShutdownOnUI is not optional. You must
+ // do something to drop references during the first pass Shutdown() because
+ // this is the only point where you are guaranteed that something is running
+ // on the UI thread. The PKSF framework will ensure that this is only called
+ // on the UI thread; you do not need to check for that yourself.
+ virtual void ShutdownOnUIThread() = 0;
+
+ protected:
+ // If your service does not need to be deleted on a specific thread, use the
+ // default constructor.
+ RefcountedBrowserContextKeyedService();
+
+ // If you need your service to be deleted on a specific thread (for example,
+ // you're converting a service that used content::DeleteOnThread<IO>), then
+ // use this constructor with the ID of the thread.
+ explicit RefcountedBrowserContextKeyedService(
+ const content::BrowserThread::ID thread_id);
+
+ // The second pass destruction can happen anywhere unless you specify which
+ // thread this service must be destroyed on by using the second constructor.
+ virtual ~RefcountedBrowserContextKeyedService();
+
+ private:
+ friend struct impl::RefcountedBrowserContextKeyedServiceTraits;
+ friend class base::DeleteHelper<RefcountedBrowserContextKeyedService>;
+ friend class base::RefCountedThreadSafe<RefcountedBrowserContextKeyedService,
+ impl::RefcountedBrowserContextKeyedServiceTraits>;
+
+ // Do we have to delete this object on a specific thread?
+ bool requires_destruction_on_thread_;
+ content::BrowserThread::ID thread_id_;
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_H_
diff --git a/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc
new file mode 100644
index 00000000000..060ddc1eb1d
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.cc
@@ -0,0 +1,130 @@
+// 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/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "content/public/browser/browser_context.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
+#include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
+
+void RefcountedBrowserContextKeyedServiceFactory::SetTestingFactory(
+ content::BrowserContext* context,
+ FactoryFunction factory) {
+ // Destroying the context may cause us to lose data about whether |context|
+ // has our preferences registered on it (since the context object itself
+ // isn't dead). See if we need to readd it once we've gone through normal
+ // destruction.
+ bool add_context = ArePreferencesSetOn(context);
+
+ // We have to go through the shutdown and destroy mechanisms because there
+ // are unit tests that create a service on a context and then change the
+ // testing service mid-test.
+ BrowserContextShutdown(context);
+ BrowserContextDestroyed(context);
+
+ if (add_context)
+ MarkPreferencesSetOn(context);
+
+ factories_[context] = factory;
+}
+
+scoped_refptr<RefcountedBrowserContextKeyedService>
+RefcountedBrowserContextKeyedServiceFactory::SetTestingFactoryAndUse(
+ content::BrowserContext* context,
+ FactoryFunction factory) {
+ DCHECK(factory);
+ SetTestingFactory(context, factory);
+ return GetServiceForBrowserContext(context, true);
+}
+
+RefcountedBrowserContextKeyedServiceFactory::
+RefcountedBrowserContextKeyedServiceFactory(
+ const char* name,
+ BrowserContextDependencyManager* manager)
+ : BrowserContextKeyedBaseFactory(name, manager) {
+}
+
+RefcountedBrowserContextKeyedServiceFactory::
+~RefcountedBrowserContextKeyedServiceFactory() {
+ DCHECK(mapping_.empty());
+}
+
+scoped_refptr<RefcountedBrowserContextKeyedService>
+RefcountedBrowserContextKeyedServiceFactory::GetServiceForBrowserContext(
+ content::BrowserContext* context,
+ bool create) {
+ context = GetBrowserContextToUse(context);
+ if (!context)
+ return NULL;
+
+ // NOTE: If you modify any of the logic below, make sure to update the
+ // non-refcounted version in context_keyed_service_factory.cc!
+ RefCountedStorage::const_iterator it = mapping_.find(context);
+ if (it != mapping_.end())
+ return it->second;
+
+ // Object not found.
+ if (!create)
+ return NULL; // And we're forbidden from creating one.
+
+ // Create new object.
+ // Check to see if we have a per-BrowserContext testing factory that we should
+ // use instead of default behavior.
+ scoped_refptr<RefcountedBrowserContextKeyedService> service;
+ BrowserContextOverriddenFunctions::const_iterator jt =
+ factories_.find(context);
+ if (jt != factories_.end()) {
+ if (jt->second) {
+ if (!context->IsOffTheRecord())
+ RegisterUserPrefsOnBrowserContext(context);
+ service = jt->second(context);
+ }
+ } else {
+ service = BuildServiceInstanceFor(context);
+ }
+
+ Associate(context, service);
+ return service;
+}
+
+void RefcountedBrowserContextKeyedServiceFactory::Associate(
+ content::BrowserContext* context,
+ const scoped_refptr<RefcountedBrowserContextKeyedService>& service) {
+ DCHECK(!ContainsKey(mapping_, context));
+ mapping_.insert(std::make_pair(context, service));
+}
+
+void RefcountedBrowserContextKeyedServiceFactory::BrowserContextShutdown(
+ content::BrowserContext* context) {
+ RefCountedStorage::iterator it = mapping_.find(context);
+ if (it != mapping_.end() && it->second.get())
+ it->second->ShutdownOnUIThread();
+}
+
+void RefcountedBrowserContextKeyedServiceFactory::BrowserContextDestroyed(
+ content::BrowserContext* context) {
+ // We "merely" drop our reference to the service. Hopefully this will cause
+ // the service to be destroyed. If not, oh well.
+ mapping_.erase(context);
+
+ // For unit tests, we also remove the factory function both so we don't
+ // maintain a big map of dead pointers, but also since we may have a second
+ // object that lives at the same address (see other comments about unit tests
+ // in this file).
+ factories_.erase(context);
+
+ BrowserContextKeyedBaseFactory::BrowserContextDestroyed(context);
+}
+
+void RefcountedBrowserContextKeyedServiceFactory::SetEmptyTestingFactory(
+ content::BrowserContext* context) {
+ SetTestingFactory(context, NULL);
+}
+
+void RefcountedBrowserContextKeyedServiceFactory::CreateServiceNow(
+ content::BrowserContext* context) {
+ GetServiceForBrowserContext(context, true);
+}
diff --git a/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h
new file mode 100644
index 00000000000..34eff157e90
--- /dev/null
+++ b/chromium/components/browser_context_keyed_service/refcounted_browser_context_keyed_service_factory.h
@@ -0,0 +1,100 @@
+// 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_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+#define COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
+#include "components/browser_context_keyed_service/browser_context_keyed_service_export.h"
+#include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
+
+class RefcountedBrowserContextKeyedService;
+
+namespace content {
+class BrowserContext;
+}
+
+// A specialized BrowserContextKeyedServiceFactory that manages a
+// RefcountedThreadSafe<>.
+//
+// While the factory returns RefcountedThreadSafe<>s, the factory itself is a
+// base::NotThreadSafe. Only call methods on this object on the UI thread.
+//
+// Implementers of RefcountedBrowserContextKeyedService should note that
+// we guarantee that ShutdownOnUIThread() is called on the UI thread, but actual
+// object destruction can happen anywhere.
+class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT
+RefcountedBrowserContextKeyedServiceFactory
+ : public BrowserContextKeyedBaseFactory {
+ public:
+ // A function that supplies the instance of a BrowserContextKeyedService for
+ // a given BrowserContext. This is used primarily for testing, where we want
+ // to feed a specific mock into the BCKSF system.
+ typedef scoped_refptr<RefcountedBrowserContextKeyedService>
+ (*FactoryFunction)(content::BrowserContext* context);
+
+ // Associates |factory| with |context| so that |factory| is used to create
+ // the BrowserContextKeyedService when requested. |factory| can be NULL
+ // to signal that BrowserContextKeyedService should be NULL. Multiple calls to
+ // SetTestingFactory() are allowed; previous services will be shut down.
+ void SetTestingFactory(content::BrowserContext* context,
+ FactoryFunction factory);
+
+ // Associates |factory| with |context| and immediately returns the created
+ // BrowserContextKeyedService. Since the factory will be used immediately,
+ // it may not be NULL.
+ scoped_refptr<RefcountedBrowserContextKeyedService> SetTestingFactoryAndUse(
+ content::BrowserContext* context,
+ FactoryFunction factory);
+
+ protected:
+ RefcountedBrowserContextKeyedServiceFactory(
+ const char* name,
+ BrowserContextDependencyManager* manager);
+ virtual ~RefcountedBrowserContextKeyedServiceFactory();
+
+ scoped_refptr<RefcountedBrowserContextKeyedService>
+ GetServiceForBrowserContext(
+ content::BrowserContext* context,
+ bool create);
+
+ // Maps |context| to |service| with debug checks to prevent duplication.
+ void Associate(
+ content::BrowserContext* context,
+ const scoped_refptr<RefcountedBrowserContextKeyedService>& service);
+
+ // All subclasses of RefcountedBrowserContextKeyedServiceFactory must return
+ // a RefcountedBrowserContextKeyedService instead of just
+ // a BrowserContextKeyedBase.
+ virtual scoped_refptr<RefcountedBrowserContextKeyedService>
+ BuildServiceInstanceFor(content::BrowserContext* context) const = 0;
+
+ virtual void BrowserContextShutdown(
+ content::BrowserContext* context) OVERRIDE;
+ virtual void BrowserContextDestroyed(
+ content::BrowserContext* context) OVERRIDE;
+ virtual void SetEmptyTestingFactory(
+ content::BrowserContext* context) OVERRIDE;
+ virtual void CreateServiceNow(content::BrowserContext* context) OVERRIDE;
+
+ private:
+ typedef std::map<content::BrowserContext*,
+ scoped_refptr<RefcountedBrowserContextKeyedService> >
+ RefCountedStorage;
+ typedef std::map<content::BrowserContext*,
+ FactoryFunction> BrowserContextOverriddenFunctions;
+
+ // The mapping between a BrowserContext and its refcounted service.
+ RefCountedStorage mapping_;
+
+ // The mapping between a BrowserContext and its overridden FactoryFunction.
+ BrowserContextOverriddenFunctions factories_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefcountedBrowserContextKeyedServiceFactory);
+};
+
+#endif // COMPONENTS_BROWSER_CONTEXT_KEYED_SERVICE_REFCOUNTED_BROWSER_CONTEXT_KEYED_SERVICE_FACTORY_H_
diff --git a/chromium/components/component_strings.grd b/chromium/components/component_strings.grd
new file mode 100644
index 00000000000..ba3840e2dbb
--- /dev/null
+++ b/chromium/components/component_strings.grd
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<grit latest_public_release="0" current_release="1"
+ source_lang_id="en" enc_check="möl">
+ <outputs>
+ <output filename="grit/component_strings.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="component_strings_am.pak" type="data_package" lang="am" />
+ <output filename="component_strings_ar.pak" type="data_package" lang="ar" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_ast.pak" type="data_package" lang="ast" />
+ </if>
+ <output filename="component_strings_bg.pak" type="data_package" lang="bg" />
+ <output filename="component_strings_bn.pak" type="data_package" lang="bn" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_bs.pak" type="data_package" lang="bs" />
+ </if>
+ <output filename="component_strings_ca.pak" type="data_package" lang="ca" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_ca@valencia.pak" type="data_package" lang="ca@valencia" />
+ </if>
+ <output filename="component_strings_cs.pak" type="data_package" lang="cs" />
+ <output filename="component_strings_da.pak" type="data_package" lang="da" />
+ <output filename="component_strings_de.pak" type="data_package" lang="de" />
+ <output filename="component_strings_el.pak" type="data_package" lang="el" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_en-AU.pak" type="data_package" lang="en-AU" />
+ </if>
+ <output filename="component_strings_en-GB.pak" type="data_package" lang="en-GB" />
+ <output filename="component_strings_en-US.pak" type="data_package" lang="en" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_eo.pak" type="data_package" lang="eo" />
+ </if>
+ <output filename="component_strings_es.pak" type="data_package" lang="es" />
+ <output filename="component_strings_es-419.pak" type="data_package" lang="es-419" />
+ <output filename="component_strings_et.pak" type="data_package" lang="et" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_eu.pak" type="data_package" lang="eu" />
+ </if>
+ <output filename="component_strings_fa.pak" type="data_package" lang="fa" />
+ <output filename="component_strings_fake-bidi.pak" type="data_package" lang="fake-bidi" />
+ <output filename="component_strings_fi.pak" type="data_package" lang="fi" />
+ <output filename="component_strings_fil.pak" type="data_package" lang="fil" />
+ <output filename="component_strings_fr.pak" type="data_package" lang="fr" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_gl.pak" type="data_package" lang="gl" />
+ </if>
+ <output filename="component_strings_gu.pak" type="data_package" lang="gu" />
+ <output filename="component_strings_he.pak" type="data_package" lang="he" />
+ <output filename="component_strings_hi.pak" type="data_package" lang="hi" />
+ <output filename="component_strings_hr.pak" type="data_package" lang="hr" />
+ <output filename="component_strings_hu.pak" type="data_package" lang="hu" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_hy.pak" type="data_package" lang="hy" />
+ <output filename="component_strings_ia.pak" type="data_package" lang="ia" />
+ </if>
+ <output filename="component_strings_id.pak" type="data_package" lang="id" />
+ <output filename="component_strings_it.pak" type="data_package" lang="it" />
+ <output filename="component_strings_ja.pak" type="data_package" lang="ja" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_ka.pak" type="data_package" lang="ka" />
+ </if>
+ <output filename="component_strings_kn.pak" type="data_package" lang="kn" />
+ <output filename="component_strings_ko.pak" type="data_package" lang="ko" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_ku.pak" type="data_package" lang="ku" />
+ <output filename="component_strings_kw.pak" type="data_package" lang="kw" />
+ </if>
+ <output filename="component_strings_lt.pak" type="data_package" lang="lt" />
+ <output filename="component_strings_lv.pak" type="data_package" lang="lv" />
+ <output filename="component_strings_ml.pak" type="data_package" lang="ml" />
+ <output filename="component_strings_mr.pak" type="data_package" lang="mr" />
+ <output filename="component_strings_ms.pak" type="data_package" lang="ms" />
+ <output filename="component_strings_nl.pak" type="data_package" lang="nl" />
+ <!-- The translation console uses 'no' for Norwegian Bokmål. It should
+ be 'nb'. -->
+ <output filename="component_strings_nb.pak" type="data_package" lang="no" />
+ <output filename="component_strings_pl.pak" type="data_package" lang="pl" />
+ <if expr="pp_ifdef('ios')">
+ <!-- iOS uses pt for pt-BR -->
+ <output filename="component_strings_pt.pak" type="data_package" lang="pt-BR" />
+ </if>
+ <if expr="not pp_ifdef('ios')">
+ <output filename="component_strings_pt-BR.pak" type="data_package" lang="pt-BR" />
+ </if>
+ <output filename="component_strings_pt-PT.pak" type="data_package" lang="pt-PT" />
+ <output filename="component_strings_ro.pak" type="data_package" lang="ro" />
+ <output filename="component_strings_ru.pak" type="data_package" lang="ru" />
+ <output filename="component_strings_sk.pak" type="data_package" lang="sk" />
+ <output filename="component_strings_sl.pak" type="data_package" lang="sl" />
+ <output filename="component_strings_sr.pak" type="data_package" lang="sr" />
+ <output filename="component_strings_sv.pak" type="data_package" lang="sv" />
+ <output filename="component_strings_sw.pak" type="data_package" lang="sw" />
+ <output filename="component_strings_ta.pak" type="data_package" lang="ta" />
+ <output filename="component_strings_te.pak" type="data_package" lang="te" />
+ <output filename="component_strings_th.pak" type="data_package" lang="th" />
+ <output filename="component_strings_tr.pak" type="data_package" lang="tr" />
+ <if expr="pp_ifdef('use_third_party_translations')">
+ <output filename="component_strings_ug.pak" type="data_package" lang="ug" />
+ </if>
+ <output filename="component_strings_uk.pak" type="data_package" lang="uk" />
+ <output filename="component_strings_vi.pak" type="data_package" lang="vi" />
+ <output filename="component_strings_zh-CN.pak" type="data_package" lang="zh-CN" />
+ <output filename="component_strings_zh-TW.pak" type="data_package" lang="zh-TW" />
+ </outputs>
+ <translations>
+ <file path="strings/component_strings_am.xtb" lang="am" />
+ <file path="strings/component_strings_ar.xtb" lang="ar" />
+ <file path="strings/component_strings_bg.xtb" lang="bg" />
+ <file path="strings/component_strings_bn.xtb" lang="bn" />
+ <file path="strings/component_strings_ca.xtb" lang="ca" />
+ <file path="strings/component_strings_cs.xtb" lang="cs" />
+ <file path="strings/component_strings_da.xtb" lang="da" />
+ <file path="strings/component_strings_de.xtb" lang="de" />
+ <file path="strings/component_strings_el.xtb" lang="el" />
+ <file path="strings/component_strings_en-GB.xtb" lang="en-GB" />
+ <file path="strings/component_strings_es.xtb" lang="es" />
+ <file path="strings/component_strings_es-419.xtb" lang="es-419" />
+ <file path="strings/component_strings_et.xtb" lang="et" />
+ <file path="strings/component_strings_fa.xtb" lang="fa" />
+ <file path="strings/component_strings_fi.xtb" lang="fi" />
+ <file path="strings/component_strings_fil.xtb" lang="fil" />
+ <file path="strings/component_strings_fr.xtb" lang="fr" />
+ <file path="strings/component_strings_gu.xtb" lang="gu" />
+ <file path="strings/component_strings_hi.xtb" lang="hi" />
+ <file path="strings/component_strings_hr.xtb" lang="hr" />
+ <file path="strings/component_strings_hu.xtb" lang="hu" />
+ <file path="strings/component_strings_id.xtb" lang="id" />
+ <file path="strings/component_strings_it.xtb" lang="it" />
+ <!-- The translation console uses 'iw' for Hebrew, but we use 'he'. -->
+ <file path="strings/component_strings_iw.xtb" lang="he" />
+ <file path="strings/component_strings_ja.xtb" lang="ja" />
+ <file path="strings/component_strings_kn.xtb" lang="kn" />
+ <file path="strings/component_strings_ko.xtb" lang="ko" />
+ <file path="strings/component_strings_lt.xtb" lang="lt" />
+ <file path="strings/component_strings_lv.xtb" lang="lv" />
+ <file path="strings/component_strings_ml.xtb" lang="ml" />
+ <file path="strings/component_strings_mr.xtb" lang="mr" />
+ <file path="strings/component_strings_ms.xtb" lang="ms" />
+ <file path="strings/component_strings_nl.xtb" lang="nl" />
+ <file path="strings/component_strings_no.xtb" lang="no" />
+ <file path="strings/component_strings_pl.xtb" lang="pl" />
+ <file path="strings/component_strings_pt-BR.xtb" lang="pt-BR" />
+ <file path="strings/component_strings_pt-PT.xtb" lang="pt-PT" />
+ <file path="strings/component_strings_ro.xtb" lang="ro" />
+ <file path="strings/component_strings_ru.xtb" lang="ru" />
+ <file path="strings/component_strings_sk.xtb" lang="sk" />
+ <file path="strings/component_strings_sl.xtb" lang="sl" />
+ <file path="strings/component_strings_sr.xtb" lang="sr" />
+ <file path="strings/component_strings_sv.xtb" lang="sv" />
+ <file path="strings/component_strings_sw.xtb" lang="sw" />
+ <file path="strings/component_strings_ta.xtb" lang="ta" />
+ <file path="strings/component_strings_te.xtb" lang="te" />
+ <file path="strings/component_strings_th.xtb" lang="th" />
+ <file path="strings/component_strings_tr.xtb" lang="tr" />
+ <file path="strings/component_strings_uk.xtb" lang="uk" />
+ <file path="strings/component_strings_vi.xtb" lang="vi" />
+ <file path="strings/component_strings_zh-CN.xtb" lang="zh-CN" />
+ <file path="strings/component_strings_zh-TW.xtb" lang="zh-TW" />
+ </translations>
+ <release seq="1" allow_pseudo="false">
+ <messages fallback_to_english="true">
+ <part file="autofill_strings.grdp" />
+ </messages>
+ </release>
+</grit>
diff --git a/chromium/components/component_strings.gyp b/chromium/components/component_strings.gyp
new file mode 100644
index 00000000000..a186f1ebe17
--- /dev/null
+++ b/chromium/components/component_strings.gyp
@@ -0,0 +1,30 @@
+# 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.
+
+{
+ 'variables': {
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/components',
+ },
+ 'targets': [
+ {
+ 'target_name': 'component_strings',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'component_strings',
+ 'variables': {
+ 'grit_grd_file': 'component_strings.grd',
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/components/strings',
+ },
+ 'includes': [ '../build/grit_action.gypi' ],
+ },
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/components/strings',
+ ],
+ },
+ },
+ ],
+}
diff --git a/chromium/components/components.gyp b/chromium/components/components.gyp
new file mode 100644
index 00000000000..602650c37d0
--- /dev/null
+++ b/chromium/components/components.gyp
@@ -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.
+
+{
+ 'variables': {
+ # This turns on e.g. the filename-based detection of which
+ # platforms to include source files on (e.g. files ending in
+ # _mac.h or _mac.cc are only compiled on MacOSX).
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ 'autofill.gypi',
+ 'auto_login_parser.gypi',
+ 'breakpad.gypi',
+ 'browser_context_keyed_service.gypi',
+ 'components_tests.gypi',
+ 'json_schema.gypi',
+ 'navigation_interception.gypi',
+ 'policy.gypi',
+ 'sessions.gypi',
+ 'user_prefs.gypi',
+ 'visitedlink.gypi',
+ 'webdata.gypi',
+ 'web_contents_delegate_android.gypi',
+ 'web_modal.gypi',
+ ],
+}
diff --git a/chromium/components/components_tests.gypi b/chromium/components/components_tests.gypi
new file mode 100644
index 00000000000..61346d07a88
--- /dev/null
+++ b/chromium/components/components_tests.gypi
@@ -0,0 +1,156 @@
+# 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.
+
+{
+ 'conditions': [
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ 'target_name': 'components_unittests',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'auto_login_parser/auto_login_parser_unittest.cc',
+ 'browser_context_keyed_service/browser_context_dependency_manager_unittest.cc',
+ 'browser_context_keyed_service/dependency_graph_unittest.cc',
+ 'json_schema/json_schema_validator_unittest.cc',
+ 'json_schema/json_schema_validator_unittest_base.cc',
+ 'json_schema/json_schema_validator_unittest_base.h',
+ 'navigation_interception/intercept_navigation_resource_throttle_unittest.cc',
+ 'sessions/serialized_navigation_entry_unittest.cc',
+ 'test/run_all_unittests.cc',
+ 'visitedlink/test/visitedlink_unittest.cc',
+ 'webdata/encryptor/encryptor_password_mac_unittest.cc',
+ 'webdata/encryptor/encryptor_unittest.cc',
+ 'web_modal/web_contents_modal_dialog_manager_unittest.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ '../testing/gmock.gyp:gmock',
+ '../testing/gtest.gyp:gtest',
+
+ # Dependencies of auto_login_parser
+ 'auto_login_parser',
+
+ # Dependencies of browser_context_keyed_service
+ 'browser_context_keyed_service',
+
+ # Dependencies of encryptor
+ 'encryptor',
+
+ # Dependencies of json_schema
+ 'json_schema',
+
+ # Dependencies of intercept_navigation_resource_throttle_unittest.cc
+ '../content/content.gyp:test_support_content',
+ '../skia/skia.gyp:skia',
+ 'navigation_interception',
+
+ # Dependencies of policy
+ 'policy_component',
+
+ # Dependencies of sessions
+ '../third_party/protobuf/protobuf.gyp:protobuf_lite',
+ 'sessions',
+ 'sessions_test_support',
+
+ # Dependencies of visitedlink
+ 'visitedlink_browser',
+ 'visitedlink_renderer',
+ '../content/content_resources.gyp:content_resources',
+
+ 'web_modal',
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'sources!': [
+ 'web_modal/web_contents_modal_dialog_manager_unittest.cc',
+ ],
+ 'dependencies!': [
+ 'web_modal',
+ ],
+ }],
+ ['OS == "android" and gtest_target_type == "shared_library"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ]
+ }],
+ ['OS=="win" and win_use_allocator_shim==1', {
+ 'dependencies': [
+ '../base/allocator/allocator.gyp:allocator',
+ ],
+ }],
+ ['android_webview_build == 0', {
+ 'dependencies': [
+ '../sync/sync.gyp:sync',
+ ],
+ }],
+ ['OS=="linux" and component=="shared_library" and linux_use_tcmalloc==1', {
+ 'dependencies': [
+ '<(DEPTH)/base/allocator/allocator.gyp:allocator',
+ ],
+ 'link_settings': {
+ 'ldflags': ['-rdynamic'],
+ },
+ }],
+ ['configuration_policy==1', {
+ 'sources': [
+ 'policy/core/common/policy_schema_unittest.cc',
+ ],
+ }],
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [4267, ],
+ },
+ {
+ 'target_name': 'components_perftests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_perf',
+ '../content/content.gyp:test_support_content',
+ '../testing/gtest.gyp:gtest',
+ '../ui/compositor/compositor.gyp:compositor',
+ 'visitedlink_browser',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'visitedlink/test/visitedlink_perftest.cc',
+ ],
+ 'conditions': [
+ ['OS == "android" and gtest_target_type == "shared_library"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [ 4267, ],
+ },
+ ],
+ 'conditions': [
+ ['OS == "android" and gtest_target_type == "shared_library"', {
+ 'targets': [
+ {
+ 'target_name': 'components_unittests_apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'components_unittests',
+ ],
+ 'variables': {
+ 'test_suite_name': 'components_unittests',
+ 'input_shlib_path': '<(SHARED_LIB_DIR)/<(SHARED_LIB_PREFIX)components_unittests<(SHARED_LIB_SUFFIX)',
+ },
+ 'includes': [ '../build/apk_test.gypi' ],
+ },
+ ],
+ }],
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/components_unittests.isolate b/chromium/components/components_unittests.isolate
new file mode 100644
index 00000000000..f60a380f60d
--- /dev/null
+++ b/chromium/components/components_unittests.isolate
@@ -0,0 +1,17 @@
+# 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.
+{
+ 'conditions': [
+ ['OS=="android"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/content_resources.pak',
+ ],
+ 'isolate_dependency_untracked': [
+ 'test/data/',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/chromium/components/json_schema.gypi b/chromium/components/json_schema.gypi
new file mode 100644
index 00000000000..510f97d6fe4
--- /dev/null
+++ b/chromium/components/json_schema.gypi
@@ -0,0 +1,25 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'json_schema',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ui/ui.gyp:ui',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'json_schema/json_schema_constants.cc',
+ 'json_schema/json_schema_constants.h',
+ 'json_schema/json_schema_validator.cc',
+ 'json_schema/json_schema_validator.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/json_schema/DEPS b/chromium/components/json_schema/DEPS
new file mode 100644
index 00000000000..e7cf2c6ff61
--- /dev/null
+++ b/chromium/components/json_schema/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/base",
+]
diff --git a/chromium/components/json_schema/OWNERS b/chromium/components/json_schema/OWNERS
new file mode 100644
index 00000000000..f7e95c9f18f
--- /dev/null
+++ b/chromium/components/json_schema/OWNERS
@@ -0,0 +1,5 @@
+asargent@chromium.org
+calamity@chromium.org
+kalman@chromium.org
+koz@chromium.org
+mpcomplete@chromium.org
diff --git a/chromium/components/json_schema/README b/chromium/components/json_schema/README
new file mode 100644
index 00000000000..c7453db06dc
--- /dev/null
+++ b/chromium/components/json_schema/README
@@ -0,0 +1,6 @@
+The //components/json_schema component provides:
+
+a) JSON schema constants, which can be used to inspect schema objects.
+
+b) The JSONSchemaValidator class, which can be used to parse and validate JSON
+schemas, and to validate JSON objects against the parsed schema.
diff --git a/chromium/components/json_schema/json_schema_constants.cc b/chromium/components/json_schema/json_schema_constants.cc
new file mode 100644
index 00000000000..0152cfc054d
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_constants.cc
@@ -0,0 +1,38 @@
+// 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/json_schema/json_schema_constants.h"
+
+namespace json_schema_constants {
+
+const char kAdditionalProperties[] = "additionalProperties";
+const char kAny[] = "any";
+const char kArray[] = "array";
+const char kBoolean[] = "boolean";
+const char kChoices[] = "choices";
+const char kDescription[] = "description";
+const char kEnum[] = "enum";
+const char kId[] = "id";
+const char kInteger[] = "integer";
+const char kItems[] = "items";
+const char kMaximum[] = "maximum";
+const char kMaxItems[] = "maxItems";
+const char kMaxLength[] = "maxLength";
+const char kMinimum[] = "minimum";
+const char kMinItems[] = "minItems";
+const char kMinLength[] = "minLength";
+const char kNull[] = "null";
+const char kNumber[] = "number";
+const char kObject[] = "object";
+const char kOptional[] = "optional";
+const char kPattern[] = "pattern";
+const char kPatternProperties[] = "patternProperties";
+const char kProperties[] = "properties";
+const char kRef[] = "$ref";
+const char kSchema[] = "$schema";
+const char kString[] = "string";
+const char kTitle[] = "title";
+const char kType[] = "type";
+
+} // namespace json_schema_constants
diff --git a/chromium/components/json_schema/json_schema_constants.h b/chromium/components/json_schema/json_schema_constants.h
new file mode 100644
index 00000000000..5c64ebb5606
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_constants.h
@@ -0,0 +1,42 @@
+// 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_JSON_SCHEMA_JSON_SCHEMA_CONSTANTS_H_
+#define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_CONSTANTS_H_
+
+// These constants are shared by code that uses JSON schemas.
+namespace json_schema_constants {
+
+extern const char kAdditionalProperties[];
+extern const char kAny[];
+extern const char kArray[];
+extern const char kBoolean[];
+extern const char kChoices[];
+extern const char kDescription[];
+extern const char kEnum[];
+extern const char kId[];
+extern const char kInteger[];
+extern const char kItems[];
+extern const char kMaximum[];
+extern const char kMaxItems[];
+extern const char kMaxLength[];
+extern const char kMinimum[];
+extern const char kMinItems[];
+extern const char kMinLength[];
+extern const char kNull[];
+extern const char kNumber[];
+extern const char kObject[];
+extern const char kOptional[];
+extern const char kPattern[];
+extern const char kPatternProperties[];
+extern const char kProperties[];
+extern const char kRef[];
+extern const char kSchema[];
+extern const char kString[];
+extern const char kTitle[];
+extern const char kType[];
+
+} // namespace json_schema_constants
+
+#endif // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_CONSTANTS_H_
diff --git a/chromium/components/json_schema/json_schema_validator.cc b/chromium/components/json_schema/json_schema_validator.cc
new file mode 100644
index 00000000000..3816a760970
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_validator.cc
@@ -0,0 +1,727 @@
+// 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/json_schema/json_schema_validator.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <cmath>
+
+#include "base/json/json_reader.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace schema = json_schema_constants;
+
+namespace {
+
+double GetNumberValue(const base::Value* value) {
+ double result = 0;
+ CHECK(value->GetAsDouble(&result))
+ << "Unexpected value type: " << value->GetType();
+ return result;
+}
+
+bool IsValidType(const std::string& type) {
+ static const char* kValidTypes[] = {
+ schema::kAny,
+ schema::kArray,
+ schema::kBoolean,
+ schema::kInteger,
+ schema::kNull,
+ schema::kNumber,
+ schema::kObject,
+ schema::kString,
+ };
+ const char** end = kValidTypes + arraysize(kValidTypes);
+ return std::find(kValidTypes, end, type) != end;
+}
+
+// Maps a schema attribute name to its expected type.
+struct ExpectedType {
+ const char* key;
+ base::Value::Type type;
+};
+
+// Helper for std::lower_bound.
+bool CompareToString(const ExpectedType& entry, const std::string& key) {
+ return entry.key < key;
+}
+
+bool IsValidSchema(const base::DictionaryValue* dict, std::string* error) {
+ // This array must be sorted, so that std::lower_bound can perform a
+ // binary search.
+ static const ExpectedType kExpectedTypes[] = {
+ // Note: kRef == "$ref", kSchema == "$schema"
+ { schema::kRef, base::Value::TYPE_STRING },
+ { schema::kSchema, base::Value::TYPE_STRING },
+
+ { schema::kAdditionalProperties, base::Value::TYPE_DICTIONARY },
+ { schema::kChoices, base::Value::TYPE_LIST },
+ { schema::kDescription, base::Value::TYPE_STRING },
+ { schema::kEnum, base::Value::TYPE_LIST },
+ { schema::kId, base::Value::TYPE_STRING },
+ { schema::kMaxItems, base::Value::TYPE_INTEGER },
+ { schema::kMaxLength, base::Value::TYPE_INTEGER },
+ { schema::kMaximum, base::Value::TYPE_DOUBLE },
+ { schema::kMinItems, base::Value::TYPE_INTEGER },
+ { schema::kMinLength, base::Value::TYPE_INTEGER },
+ { schema::kMinimum, base::Value::TYPE_DOUBLE },
+ { schema::kOptional, base::Value::TYPE_BOOLEAN },
+ { schema::kProperties, base::Value::TYPE_DICTIONARY },
+ { schema::kTitle, base::Value::TYPE_STRING },
+ };
+
+ bool has_type = false;
+ const base::ListValue* list_value = NULL;
+ const base::DictionaryValue* dictionary_value = NULL;
+ std::string string_value;
+
+ for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
+ // Validate the "type" attribute, which may be a string or a list.
+ if (it.key() == schema::kType) {
+ switch (it.value().GetType()) {
+ case base::Value::TYPE_STRING:
+ it.value().GetAsString(&string_value);
+ if (!IsValidType(string_value)) {
+ *error = "Invalid value for type attribute";
+ return false;
+ }
+ break;
+ case base::Value::TYPE_LIST:
+ it.value().GetAsList(&list_value);
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ if (!list_value->GetString(i, &string_value) ||
+ !IsValidType(string_value)) {
+ *error = "Invalid value for type attribute";
+ return false;
+ }
+ }
+ break;
+ default:
+ *error = "Invalid value for type attribute";
+ return false;
+ }
+ has_type = true;
+ continue;
+ }
+
+ // Validate the "items" attribute, which is a schema or a list of schemas.
+ if (it.key() == schema::kItems) {
+ if (it.value().GetAsDictionary(&dictionary_value)) {
+ if (!IsValidSchema(dictionary_value, error)) {
+ DCHECK(!error->empty());
+ return false;
+ }
+ } else if (it.value().GetAsList(&list_value)) {
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ if (!list_value->GetDictionary(i, &dictionary_value)) {
+ *error = base::StringPrintf(
+ "Invalid entry in items attribute at index %d",
+ static_cast<int>(i));
+ return false;
+ }
+ if (!IsValidSchema(dictionary_value, error)) {
+ DCHECK(!error->empty());
+ return false;
+ }
+ }
+ } else {
+ *error = "Invalid value for items attribute";
+ return false;
+ }
+ continue;
+ }
+
+ // All the other attributes have a single valid type.
+ const ExpectedType* end = kExpectedTypes + arraysize(kExpectedTypes);
+ const ExpectedType* entry = std::lower_bound(
+ kExpectedTypes, end, it.key(), CompareToString);
+ if (entry == end || entry->key != it.key()) {
+ *error = base::StringPrintf("Invalid attribute %s", it.key().c_str());
+ return false;
+ }
+ if (!it.value().IsType(entry->type)) {
+ *error = base::StringPrintf("Invalid value for %s attribute",
+ it.key().c_str());
+ return false;
+ }
+
+ // base::Value::TYPE_INTEGER attributes must be >= 0.
+ // This applies to "minItems", "maxItems", "minLength" and "maxLength".
+ if (it.value().IsType(base::Value::TYPE_INTEGER)) {
+ int integer_value;
+ it.value().GetAsInteger(&integer_value);
+ if (integer_value < 0) {
+ *error = base::StringPrintf("Value of %s must be >= 0, got %d",
+ it.key().c_str(), integer_value);
+ return false;
+ }
+ }
+
+ // Validate the "properties" attribute. Each entry maps a key to a schema.
+ if (it.key() == schema::kProperties) {
+ it.value().GetAsDictionary(&dictionary_value);
+ for (base::DictionaryValue::Iterator it(*dictionary_value);
+ !it.IsAtEnd(); it.Advance()) {
+ if (!it.value().GetAsDictionary(&dictionary_value)) {
+ *error = "Invalid value for properties attribute";
+ return false;
+ }
+ if (!IsValidSchema(dictionary_value, error)) {
+ DCHECK(!error->empty());
+ return false;
+ }
+ }
+ }
+
+ // Validate "additionalProperties" attribute, which is a schema.
+ if (it.key() == schema::kAdditionalProperties) {
+ it.value().GetAsDictionary(&dictionary_value);
+ if (!IsValidSchema(dictionary_value, error)) {
+ DCHECK(!error->empty());
+ return false;
+ }
+ }
+
+ // Validate the values contained in an "enum" attribute.
+ if (it.key() == schema::kEnum) {
+ it.value().GetAsList(&list_value);
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ const base::Value* value = NULL;
+ list_value->Get(i, &value);
+ switch (value->GetType()) {
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_BOOLEAN:
+ case base::Value::TYPE_INTEGER:
+ case base::Value::TYPE_DOUBLE:
+ case base::Value::TYPE_STRING:
+ break;
+ default:
+ *error = "Invalid value in enum attribute";
+ return false;
+ }
+ }
+ }
+
+ // Validate the schemas contained in a "choices" attribute.
+ if (it.key() == schema::kChoices) {
+ it.value().GetAsList(&list_value);
+ for (size_t i = 0; i < list_value->GetSize(); ++i) {
+ if (!list_value->GetDictionary(i, &dictionary_value)) {
+ *error = "Invalid choices attribute";
+ return false;
+ }
+ if (!IsValidSchema(dictionary_value, error)) {
+ DCHECK(!error->empty());
+ return false;
+ }
+ }
+ }
+ }
+
+ if (!has_type) {
+ *error = "Schema must have a type attribute";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+
+JSONSchemaValidator::Error::Error() {
+}
+
+JSONSchemaValidator::Error::Error(const std::string& message)
+ : path(message) {
+}
+
+JSONSchemaValidator::Error::Error(const std::string& path,
+ const std::string& message)
+ : path(path), message(message) {
+}
+
+
+const char JSONSchemaValidator::kUnknownTypeReference[] =
+ "Unknown schema reference: *.";
+const char JSONSchemaValidator::kInvalidChoice[] =
+ "Value does not match any valid type choices.";
+const char JSONSchemaValidator::kInvalidEnum[] =
+ "Value does not match any valid enum choices.";
+const char JSONSchemaValidator::kObjectPropertyIsRequired[] =
+ "Property is required.";
+const char JSONSchemaValidator::kUnexpectedProperty[] =
+ "Unexpected property.";
+const char JSONSchemaValidator::kArrayMinItems[] =
+ "Array must have at least * items.";
+const char JSONSchemaValidator::kArrayMaxItems[] =
+ "Array must not have more than * items.";
+const char JSONSchemaValidator::kArrayItemRequired[] =
+ "Item is required.";
+const char JSONSchemaValidator::kStringMinLength[] =
+ "String must be at least * characters long.";
+const char JSONSchemaValidator::kStringMaxLength[] =
+ "String must not be more than * characters long.";
+const char JSONSchemaValidator::kStringPattern[] =
+ "String must match the pattern: *.";
+const char JSONSchemaValidator::kNumberMinimum[] =
+ "Value must not be less than *.";
+const char JSONSchemaValidator::kNumberMaximum[] =
+ "Value must not be greater than *.";
+const char JSONSchemaValidator::kInvalidType[] =
+ "Expected '*' but got '*'.";
+const char JSONSchemaValidator::kInvalidTypeIntegerNumber[] =
+ "Expected 'integer' but got 'number', consider using Math.round().";
+
+
+// static
+std::string JSONSchemaValidator::GetJSONSchemaType(const base::Value* value) {
+ switch (value->GetType()) {
+ case base::Value::TYPE_NULL:
+ return schema::kNull;
+ case base::Value::TYPE_BOOLEAN:
+ return schema::kBoolean;
+ case base::Value::TYPE_INTEGER:
+ return schema::kInteger;
+ case base::Value::TYPE_DOUBLE: {
+ double double_value = 0;
+ value->GetAsDouble(&double_value);
+ if (std::abs(double_value) <= std::pow(2.0, DBL_MANT_DIG) &&
+ double_value == floor(double_value)) {
+ return schema::kInteger;
+ } else {
+ return schema::kNumber;
+ }
+ }
+ case base::Value::TYPE_STRING:
+ return schema::kString;
+ case base::Value::TYPE_DICTIONARY:
+ return schema::kObject;
+ case base::Value::TYPE_LIST:
+ return schema::kArray;
+ default:
+ NOTREACHED() << "Unexpected value type: " << value->GetType();
+ return std::string();
+ }
+}
+
+// static
+std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
+ const std::string& s1) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ return ret_val;
+}
+
+// static
+std::string JSONSchemaValidator::FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2) {
+ std::string ret_val = format;
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1);
+ ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2);
+ return ret_val;
+}
+
+// static
+scoped_ptr<base::DictionaryValue> JSONSchemaValidator::IsValidSchema(
+ const std::string& schema,
+ std::string* error) {
+ base::JSONParserOptions options = base::JSON_PARSE_RFC;
+ scoped_ptr<base::Value> json(
+ base::JSONReader::ReadAndReturnError(schema, options, NULL, error));
+ if (!json)
+ return scoped_ptr<base::DictionaryValue>();
+ base::DictionaryValue* dict = NULL;
+ if (!json->GetAsDictionary(&dict)) {
+ *error = "Schema must be a JSON object";
+ return scoped_ptr<base::DictionaryValue>();
+ }
+ if (!::IsValidSchema(dict, error))
+ return scoped_ptr<base::DictionaryValue>();
+ ignore_result(json.release());
+ return make_scoped_ptr(dict);
+}
+
+JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema)
+ : schema_root_(schema), default_allow_additional_properties_(false) {
+}
+
+JSONSchemaValidator::JSONSchemaValidator(base::DictionaryValue* schema,
+ base::ListValue* types)
+ : schema_root_(schema), default_allow_additional_properties_(false) {
+ if (!types)
+ return;
+
+ for (size_t i = 0; i < types->GetSize(); ++i) {
+ base::DictionaryValue* type = NULL;
+ CHECK(types->GetDictionary(i, &type));
+
+ std::string id;
+ CHECK(type->GetString(schema::kId, &id));
+
+ CHECK(types_.find(id) == types_.end());
+ types_[id] = type;
+ }
+}
+
+JSONSchemaValidator::~JSONSchemaValidator() {}
+
+bool JSONSchemaValidator::Validate(const base::Value* instance) {
+ errors_.clear();
+ Validate(instance, schema_root_, std::string());
+ return errors_.empty();
+}
+
+void JSONSchemaValidator::Validate(const base::Value* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path) {
+ // If this schema defines itself as reference type, save it in this.types.
+ std::string id;
+ if (schema->GetString(schema::kId, &id)) {
+ TypeMap::iterator iter = types_.find(id);
+ if (iter == types_.end())
+ types_[id] = schema;
+ else
+ DCHECK(iter->second == schema);
+ }
+
+ // If the schema has a $ref property, the instance must validate against
+ // that schema. It must be present in types_ to be referenced.
+ std::string ref;
+ if (schema->GetString(schema::kRef, &ref)) {
+ TypeMap::iterator type = types_.find(ref);
+ if (type == types_.end()) {
+ errors_.push_back(
+ Error(path, FormatErrorMessage(kUnknownTypeReference, ref)));
+ } else {
+ Validate(instance, type->second, path);
+ }
+ return;
+ }
+
+ // If the schema has a choices property, the instance must validate against at
+ // least one of the items in that array.
+ const base::ListValue* choices = NULL;
+ if (schema->GetList(schema::kChoices, &choices)) {
+ ValidateChoices(instance, choices, path);
+ return;
+ }
+
+ // If the schema has an enum property, the instance must be one of those
+ // values.
+ const base::ListValue* enumeration = NULL;
+ if (schema->GetList(schema::kEnum, &enumeration)) {
+ ValidateEnum(instance, enumeration, path);
+ return;
+ }
+
+ std::string type;
+ schema->GetString(schema::kType, &type);
+ CHECK(!type.empty());
+ if (type != schema::kAny) {
+ if (!ValidateType(instance, type, path))
+ return;
+
+ // These casts are safe because of checks in ValidateType().
+ if (type == schema::kObject) {
+ ValidateObject(static_cast<const base::DictionaryValue*>(instance),
+ schema,
+ path);
+ } else if (type == schema::kArray) {
+ ValidateArray(static_cast<const base::ListValue*>(instance),
+ schema, path);
+ } else if (type == schema::kString) {
+ // Intentionally NOT downcasting to StringValue*. TYPE_STRING only implies
+ // GetAsString() can safely be carried out, not that it's a StringValue.
+ ValidateString(instance, schema, path);
+ } else if (type == schema::kNumber || type == schema::kInteger) {
+ ValidateNumber(instance, schema, path);
+ } else if (type != schema::kBoolean && type != schema::kNull) {
+ NOTREACHED() << "Unexpected type: " << type;
+ }
+ }
+}
+
+void JSONSchemaValidator::ValidateChoices(const base::Value* instance,
+ const base::ListValue* choices,
+ const std::string& path) {
+ size_t original_num_errors = errors_.size();
+
+ for (size_t i = 0; i < choices->GetSize(); ++i) {
+ const base::DictionaryValue* choice = NULL;
+ CHECK(choices->GetDictionary(i, &choice));
+
+ Validate(instance, choice, path);
+ if (errors_.size() == original_num_errors)
+ return;
+
+ // We discard the error from each choice. We only want to know if any of the
+ // validations succeeded.
+ errors_.resize(original_num_errors);
+ }
+
+ // Now add a generic error that no choices matched.
+ errors_.push_back(Error(path, kInvalidChoice));
+ return;
+}
+
+void JSONSchemaValidator::ValidateEnum(const base::Value* instance,
+ const base::ListValue* choices,
+ const std::string& path) {
+ for (size_t i = 0; i < choices->GetSize(); ++i) {
+ const base::Value* choice = NULL;
+ CHECK(choices->Get(i, &choice));
+ switch (choice->GetType()) {
+ case base::Value::TYPE_NULL:
+ case base::Value::TYPE_BOOLEAN:
+ case base::Value::TYPE_STRING:
+ if (instance->Equals(choice))
+ return;
+ break;
+
+ case base::Value::TYPE_INTEGER:
+ case base::Value::TYPE_DOUBLE:
+ if (instance->IsType(base::Value::TYPE_INTEGER) ||
+ instance->IsType(base::Value::TYPE_DOUBLE)) {
+ if (GetNumberValue(choice) == GetNumberValue(instance))
+ return;
+ }
+ break;
+
+ default:
+ NOTREACHED() << "Unexpected type in enum: " << choice->GetType();
+ }
+ }
+
+ errors_.push_back(Error(path, kInvalidEnum));
+}
+
+void JSONSchemaValidator::ValidateObject(const base::DictionaryValue* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path) {
+ const base::DictionaryValue* properties = NULL;
+ schema->GetDictionary(schema::kProperties, &properties);
+ if (properties) {
+ for (base::DictionaryValue::Iterator it(*properties); !it.IsAtEnd();
+ it.Advance()) {
+ std::string prop_path = path.empty() ? it.key() : (path + "." + it.key());
+ const base::DictionaryValue* prop_schema = NULL;
+ CHECK(it.value().GetAsDictionary(&prop_schema));
+
+ const base::Value* prop_value = NULL;
+ if (instance->Get(it.key(), &prop_value)) {
+ Validate(prop_value, prop_schema, prop_path);
+ } else {
+ // Properties are required unless there is an optional field set to
+ // 'true'.
+ bool is_optional = false;
+ prop_schema->GetBoolean(schema::kOptional, &is_optional);
+ if (!is_optional) {
+ errors_.push_back(Error(prop_path, kObjectPropertyIsRequired));
+ }
+ }
+ }
+ }
+
+ const base::DictionaryValue* additional_properties_schema = NULL;
+ if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema))
+ return;
+
+ // Validate additional properties.
+ for (base::DictionaryValue::Iterator it(*instance); !it.IsAtEnd();
+ it.Advance()) {
+ if (properties && properties->HasKey(it.key()))
+ continue;
+
+ std::string prop_path = path.empty() ? it.key() : path + "." + it.key();
+ if (!additional_properties_schema) {
+ errors_.push_back(Error(prop_path, kUnexpectedProperty));
+ } else {
+ Validate(&it.value(), additional_properties_schema, prop_path);
+ }
+ }
+}
+
+void JSONSchemaValidator::ValidateArray(const base::ListValue* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path) {
+ const base::DictionaryValue* single_type = NULL;
+ size_t instance_size = instance->GetSize();
+ if (schema->GetDictionary(schema::kItems, &single_type)) {
+ int min_items = 0;
+ if (schema->GetInteger(schema::kMinItems, &min_items)) {
+ CHECK(min_items >= 0);
+ if (instance_size < static_cast<size_t>(min_items)) {
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kArrayMinItems, base::IntToString(min_items))));
+ }
+ }
+
+ int max_items = 0;
+ if (schema->GetInteger(schema::kMaxItems, &max_items)) {
+ CHECK(max_items >= 0);
+ if (instance_size > static_cast<size_t>(max_items)) {
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kArrayMaxItems, base::IntToString(max_items))));
+ }
+ }
+
+ // If the items property is a single schema, each item in the array must
+ // validate against that schema.
+ for (size_t i = 0; i < instance_size; ++i) {
+ const base::Value* item = NULL;
+ CHECK(instance->Get(i, &item));
+ std::string i_str = base::Uint64ToString(i);
+ std::string item_path = path.empty() ? i_str : (path + "." + i_str);
+ Validate(item, single_type, item_path);
+ }
+
+ return;
+ }
+
+ // Otherwise, the list must be a tuple type, where each item in the list has a
+ // particular schema.
+ ValidateTuple(instance, schema, path);
+}
+
+void JSONSchemaValidator::ValidateTuple(const base::ListValue* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path) {
+ const base::ListValue* tuple_type = NULL;
+ schema->GetList(schema::kItems, &tuple_type);
+ size_t tuple_size = tuple_type ? tuple_type->GetSize() : 0;
+ if (tuple_type) {
+ for (size_t i = 0; i < tuple_size; ++i) {
+ std::string i_str = base::Uint64ToString(i);
+ std::string item_path = path.empty() ? i_str : (path + "." + i_str);
+ const base::DictionaryValue* item_schema = NULL;
+ CHECK(tuple_type->GetDictionary(i, &item_schema));
+ const base::Value* item_value = NULL;
+ instance->Get(i, &item_value);
+ if (item_value && item_value->GetType() != base::Value::TYPE_NULL) {
+ Validate(item_value, item_schema, item_path);
+ } else {
+ bool is_optional = false;
+ item_schema->GetBoolean(schema::kOptional, &is_optional);
+ if (!is_optional) {
+ errors_.push_back(Error(item_path, kArrayItemRequired));
+ return;
+ }
+ }
+ }
+ }
+
+ const base::DictionaryValue* additional_properties_schema = NULL;
+ if (SchemaAllowsAnyAdditionalItems(schema, &additional_properties_schema))
+ return;
+
+ size_t instance_size = instance->GetSize();
+ if (additional_properties_schema) {
+ // Any additional properties must validate against the additionalProperties
+ // schema.
+ for (size_t i = tuple_size; i < instance_size; ++i) {
+ std::string i_str = base::Uint64ToString(i);
+ std::string item_path = path.empty() ? i_str : (path + "." + i_str);
+ const base::Value* item_value = NULL;
+ CHECK(instance->Get(i, &item_value));
+ Validate(item_value, additional_properties_schema, item_path);
+ }
+ } else if (instance_size > tuple_size) {
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kArrayMaxItems, base::Uint64ToString(tuple_size))));
+ }
+}
+
+void JSONSchemaValidator::ValidateString(const base::Value* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path) {
+ std::string value;
+ CHECK(instance->GetAsString(&value));
+
+ int min_length = 0;
+ if (schema->GetInteger(schema::kMinLength, &min_length)) {
+ CHECK(min_length >= 0);
+ if (value.size() < static_cast<size_t>(min_length)) {
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kStringMinLength, base::IntToString(min_length))));
+ }
+ }
+
+ int max_length = 0;
+ if (schema->GetInteger(schema::kMaxLength, &max_length)) {
+ CHECK(max_length >= 0);
+ if (value.size() > static_cast<size_t>(max_length)) {
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kStringMaxLength, base::IntToString(max_length))));
+ }
+ }
+
+ CHECK(!schema->HasKey(schema::kPattern)) << "Pattern is not supported.";
+}
+
+void JSONSchemaValidator::ValidateNumber(const base::Value* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path) {
+ double value = GetNumberValue(instance);
+
+ // TODO(aa): It would be good to test that the double is not infinity or nan,
+ // but isnan and isinf aren't defined on Windows.
+
+ double minimum = 0;
+ if (schema->GetDouble(schema::kMinimum, &minimum)) {
+ if (value < minimum)
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kNumberMinimum, base::DoubleToString(minimum))));
+ }
+
+ double maximum = 0;
+ if (schema->GetDouble(schema::kMaximum, &maximum)) {
+ if (value > maximum)
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kNumberMaximum, base::DoubleToString(maximum))));
+ }
+}
+
+bool JSONSchemaValidator::ValidateType(const base::Value* instance,
+ const std::string& expected_type,
+ const std::string& path) {
+ std::string actual_type = GetJSONSchemaType(instance);
+ if (expected_type == actual_type ||
+ (expected_type == schema::kNumber && actual_type == schema::kInteger)) {
+ return true;
+ } else if (expected_type == schema::kInteger &&
+ actual_type == schema::kNumber) {
+ errors_.push_back(Error(path, kInvalidTypeIntegerNumber));
+ return false;
+ } else {
+ errors_.push_back(Error(path, FormatErrorMessage(
+ kInvalidType, expected_type, actual_type)));
+ return false;
+ }
+}
+
+bool JSONSchemaValidator::SchemaAllowsAnyAdditionalItems(
+ const base::DictionaryValue* schema,
+ const base::DictionaryValue** additional_properties_schema) {
+ // If the validator allows additional properties globally, and this schema
+ // doesn't override, then we can exit early.
+ schema->GetDictionary(schema::kAdditionalProperties,
+ additional_properties_schema);
+
+ if (*additional_properties_schema) {
+ std::string additional_properties_type(schema::kAny);
+ CHECK((*additional_properties_schema)->GetString(
+ schema::kType, &additional_properties_type));
+ return additional_properties_type == schema::kAny;
+ } else {
+ return default_allow_additional_properties_;
+ }
+}
diff --git a/chromium/components/json_schema/json_schema_validator.h b/chromium/components/json_schema/json_schema_validator.h
new file mode 100644
index 00000000000..4584a9da77a
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_validator.h
@@ -0,0 +1,235 @@
+// 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_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_
+#define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class StringValue;
+class Value;
+}
+
+//==============================================================================
+// This class implements a subset of JSON Schema.
+// See: http://www.json.com/json-schema-proposal/ for more details.
+//
+// There is also an older JavaScript implementation of the same functionality in
+// chrome/renderer/resources/json_schema.js.
+//
+// The following features of JSON Schema are not implemented:
+// - requires
+// - unique
+// - disallow
+// - union types (but replaced with 'choices')
+// - number.maxDecimal
+// - string.pattern
+//
+// The following properties are not applicable to the interface exposed by
+// this class:
+// - options
+// - readonly
+// - title
+// - description
+// - format
+// - default
+// - transient
+// - hidden
+//
+// There are also these departures from the JSON Schema proposal:
+// - null counts as 'unspecified' for optional values
+// - added the 'choices' property, to allow specifying a list of possible types
+// for a value
+// - by default an "object" typed schema does not allow additional properties.
+// if present, "additionalProperties" is to be a schema against which all
+// additional properties will be validated.
+//==============================================================================
+class JSONSchemaValidator {
+ public:
+ // Details about a validation error.
+ struct Error {
+ Error();
+
+ explicit Error(const std::string& message);
+
+ Error(const std::string& path, const std::string& message);
+
+ // The path to the location of the error in the JSON structure.
+ std::string path;
+
+ // An english message describing the error.
+ std::string message;
+ };
+
+ // Error messages.
+ static const char kUnknownTypeReference[];
+ static const char kInvalidChoice[];
+ static const char kInvalidEnum[];
+ static const char kObjectPropertyIsRequired[];
+ static const char kUnexpectedProperty[];
+ static const char kArrayMinItems[];
+ static const char kArrayMaxItems[];
+ static const char kArrayItemRequired[];
+ static const char kStringMinLength[];
+ static const char kStringMaxLength[];
+ static const char kStringPattern[];
+ static const char kNumberMinimum[];
+ static const char kNumberMaximum[];
+ static const char kInvalidType[];
+ static const char kInvalidTypeIntegerNumber[];
+
+ // Classifies a Value as one of the JSON schema primitive types.
+ static std::string GetJSONSchemaType(const base::Value* value);
+
+ // Utility methods to format error messages. The first method can have one
+ // wildcard represented by '*', which is replaced with s1. The second method
+ // can have two, which are replaced by s1 and s2.
+ static std::string FormatErrorMessage(const std::string& format,
+ const std::string& s1);
+ static std::string FormatErrorMessage(const std::string& format,
+ const std::string& s1,
+ const std::string& s2);
+
+ // Verifies if |schema| is a valid JSON v3 schema. When this validation passes
+ // then |schema| is valid JSON that can be parsed into a DictionaryValue,
+ // and that DictionaryValue can be used to build a JSONSchemaValidator.
+ // Returns the parsed DictionaryValue when |schema| validated, otherwise
+ // returns NULL. In that case, |error| contains an error description.
+ static scoped_ptr<base::DictionaryValue> IsValidSchema(
+ const std::string& schema,
+ std::string* error);
+
+ // Creates a validator for the specified schema.
+ //
+ // NOTE: This constructor assumes that |schema| is well formed and valid.
+ // Errors will result in CHECK at runtime; this constructor should not be used
+ // with untrusted schemas.
+ explicit JSONSchemaValidator(base::DictionaryValue* schema);
+
+ // Creates a validator for the specified schema and user-defined types. Each
+ // type must be a valid JSONSchema type description with an additional "id"
+ // field. Schema objects in |schema| can refer to these types with the "$ref"
+ // property.
+ //
+ // NOTE: This constructor assumes that |schema| and |types| are well-formed
+ // and valid. Errors will result in CHECK at runtime; this constructor should
+ // not be used with untrusted schemas.
+ JSONSchemaValidator(base::DictionaryValue* schema, base::ListValue* types);
+
+ ~JSONSchemaValidator();
+
+ // Whether the validator allows additional items for objects and lists, beyond
+ // those defined by their schema, by default.
+ //
+ // This setting defaults to false: all items in an instance list or object
+ // must be defined by the corresponding schema.
+ //
+ // This setting can be overridden on individual object and list schemas by
+ // setting the "additionalProperties" field.
+ bool default_allow_additional_properties() const {
+ return default_allow_additional_properties_;
+ }
+
+ void set_default_allow_additional_properties(bool val) {
+ default_allow_additional_properties_ = val;
+ }
+
+ // Returns any errors from the last call to to Validate().
+ const std::vector<Error>& errors() const {
+ return errors_;
+ }
+
+ // Validates a JSON value. Returns true if the instance is valid, false
+ // otherwise. If false is returned any errors are available from the errors()
+ // getter.
+ bool Validate(const base::Value* instance);
+
+ private:
+ typedef std::map<std::string, const base::DictionaryValue*> TypeMap;
+
+ // Each of the below methods handle a subset of the validation process. The
+ // path paramater is the path to |instance| from the root of the instance tree
+ // and is used in error messages.
+
+ // Validates any instance node against any schema node. This is called for
+ // every node in the instance tree, and it just decides which of the more
+ // detailed methods to call.
+ void Validate(const base::Value* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path);
+
+ // Validates a node against a list of possible schemas. If any one of the
+ // schemas match, the node is valid.
+ void ValidateChoices(const base::Value* instance,
+ const base::ListValue* choices,
+ const std::string& path);
+
+ // Validates a node against a list of exact primitive values, eg 42, "foobar".
+ void ValidateEnum(const base::Value* instance,
+ const base::ListValue* choices,
+ const std::string& path);
+
+ // Validates a JSON object against an object schema node.
+ void ValidateObject(const base::DictionaryValue* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path);
+
+ // Validates a JSON array against an array schema node.
+ void ValidateArray(const base::ListValue* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path);
+
+ // Validates a JSON array against an array schema node configured to be a
+ // tuple. In a tuple, there is one schema node for each item expected in the
+ // array.
+ void ValidateTuple(const base::ListValue* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path);
+
+ // Validate a JSON string against a string schema node.
+ void ValidateString(const base::Value* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path);
+
+ // Validate a JSON number against a number schema node.
+ void ValidateNumber(const base::Value* instance,
+ const base::DictionaryValue* schema,
+ const std::string& path);
+
+ // Validates that the JSON node |instance| has |expected_type|.
+ bool ValidateType(const base::Value* instance,
+ const std::string& expected_type,
+ const std::string& path);
+
+ // Returns true if |schema| will allow additional items of any type.
+ bool SchemaAllowsAnyAdditionalItems(
+ const base::DictionaryValue* schema,
+ const base::DictionaryValue** addition_items_schema);
+
+ // The root schema node.
+ base::DictionaryValue* schema_root_;
+
+ // Map of user-defined name to type.
+ TypeMap types_;
+
+ // Whether we allow additional properties on objects by default. This can be
+ // overridden by the allow_additional_properties flag on an Object schema.
+ bool default_allow_additional_properties_;
+
+ // Errors accumulated since the last call to Validate().
+ std::vector<Error> errors_;
+
+
+ DISALLOW_COPY_AND_ASSIGN(JSONSchemaValidator);
+};
+
+#endif // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_H_
diff --git a/chromium/components/json_schema/json_schema_validator_unittest.cc b/chromium/components/json_schema/json_schema_validator_unittest.cc
new file mode 100644
index 00000000000..4844ed1a888
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_validator_unittest.cc
@@ -0,0 +1,129 @@
+// 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 "base/values.h"
+#include "components/json_schema/json_schema_validator.h"
+#include "components/json_schema/json_schema_validator_unittest_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class JSONSchemaValidatorCPPTest : public JSONSchemaValidatorTestBase {
+ public:
+ JSONSchemaValidatorCPPTest()
+ : JSONSchemaValidatorTestBase(JSONSchemaValidatorTestBase::CPP) {
+ }
+
+ protected:
+ virtual void ExpectValid(const std::string& test_source,
+ base::Value* instance,
+ base::DictionaryValue* schema,
+ base::ListValue* types) OVERRIDE {
+ JSONSchemaValidator validator(schema, types);
+ if (validator.Validate(instance))
+ return;
+
+ for (size_t i = 0; i < validator.errors().size(); ++i) {
+ ADD_FAILURE() << test_source << ": "
+ << validator.errors()[i].path << ": "
+ << validator.errors()[i].message;
+ }
+ }
+
+ virtual void ExpectNotValid(
+ const std::string& test_source,
+ base::Value* instance, base::DictionaryValue* schema,
+ base::ListValue* types,
+ const std::string& expected_error_path,
+ const std::string& expected_error_message) OVERRIDE {
+ JSONSchemaValidator validator(schema, types);
+ if (validator.Validate(instance)) {
+ ADD_FAILURE() << test_source;
+ return;
+ }
+
+ ASSERT_EQ(1u, validator.errors().size()) << test_source;
+ EXPECT_EQ(expected_error_path, validator.errors()[0].path) << test_source;
+ EXPECT_EQ(expected_error_message, validator.errors()[0].message)
+ << test_source;
+ }
+};
+
+TEST_F(JSONSchemaValidatorCPPTest, Test) {
+ RunTests();
+}
+
+TEST(JSONSchemaValidator, IsValidSchema) {
+ std::string error;
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("\0", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("string", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("\"string\"", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("[]", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema("{}", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+ "{ \"type\": 123 }", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+ "{ \"type\": \"invalid\" }", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+ "{"
+ " \"type\": \"object\","
+ " \"properties\": []" // Invalid properties type.
+ "}", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+ "{"
+ " \"type\": \"string\","
+ " \"maxLength\": -1" // Must be >= 0.
+ "}", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+ "{"
+ " \"type\": \"string\","
+ " \"enum\": [ {} ]," // "enum" must contain simple values.
+ "}", &error));
+ EXPECT_FALSE(JSONSchemaValidator::IsValidSchema(
+ "{"
+ " \"type\": \"array\","
+ " \"items\": [ 123 ]," // "items" must contain a schema or schemas.
+ "}", &error));
+ EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+ "{ \"type\": \"object\" }", &error)) << error;
+ EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+ "{ \"type\": [\"object\", \"array\"] }", &error)) << error;
+ EXPECT_TRUE(JSONSchemaValidator::IsValidSchema(
+ "{"
+ " \"type\": [\"object\", \"array\"],"
+ " \"properties\": {"
+ " \"string-property\": {"
+ " \"type\": \"string\","
+ " \"minLength\": 1,"
+ " \"maxLength\": 100,"
+ " \"title\": \"The String Policy\","
+ " \"description\": \"This policy controls the String widget.\""
+ " },"
+ " \"integer-property\": {"
+ " \"type\": \"number\","
+ " \"minimum\": 1000.0,"
+ " \"maximum\": 9999.0"
+ " },"
+ " \"enum-property\": {"
+ " \"type\": \"integer\","
+ " \"enum\": [0, 1, 10, 100]"
+ " },"
+ " \"items-property\": {"
+ " \"type\": \"array\","
+ " \"items\": {"
+ " \"type\": \"string\""
+ " }"
+ " },"
+ " \"items-list-property\": {"
+ " \"type\": \"array\","
+ " \"items\": ["
+ " { \"type\": \"string\" },"
+ " { \"type\": \"integer\" }"
+ " ]"
+ " }"
+ " },"
+ " \"additionalProperties\": {"
+ " \"type\": \"any\""
+ " }"
+ "}", &error)) << error;
+}
diff --git a/chromium/components/json_schema/json_schema_validator_unittest_base.cc b/chromium/components/json_schema/json_schema_validator_unittest_base.cc
new file mode 100644
index 00000000000..2e936a2eb9a
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_validator_unittest_base.cc
@@ -0,0 +1,730 @@
+// 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/json_schema/json_schema_validator_unittest_base.h"
+
+#include <cfloat>
+#include <cmath>
+#include <limits>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "components/json_schema/json_schema_validator.h"
+
+namespace schema = json_schema_constants;
+
+namespace {
+
+#define TEST_SOURCE base::StringPrintf("%s:%i", __FILE__, __LINE__)
+
+base::Value* LoadValue(const std::string& filename) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("json_schema")
+ .AppendASCII(filename);
+ EXPECT_TRUE(base::PathExists(path));
+
+ std::string error_message;
+ JSONFileValueSerializer serializer(path);
+ base::Value* result = serializer.Deserialize(NULL, &error_message);
+ if (!result)
+ ADD_FAILURE() << "Could not parse JSON: " << error_message;
+ return result;
+}
+
+base::Value* LoadValue(const std::string& filename, base::Value::Type type) {
+ scoped_ptr<base::Value> result(LoadValue(filename));
+ if (!result.get())
+ return NULL;
+ if (!result->IsType(type)) {
+ ADD_FAILURE() << "Expected type " << type << ", got: " << result->GetType();
+ return NULL;
+ }
+ return result.release();
+}
+
+base::ListValue* LoadList(const std::string& filename) {
+ return static_cast<base::ListValue*>(
+ LoadValue(filename, base::Value::TYPE_LIST));
+}
+
+base::DictionaryValue* LoadDictionary(const std::string& filename) {
+ return static_cast<base::DictionaryValue*>(
+ LoadValue(filename, base::Value::TYPE_DICTIONARY));
+}
+
+} // namespace
+
+
+JSONSchemaValidatorTestBase::JSONSchemaValidatorTestBase(
+ JSONSchemaValidatorTestBase::ValidatorType type)
+ : type_(type) {
+}
+
+void JSONSchemaValidatorTestBase::RunTests() {
+ TestComplex();
+ TestStringPattern();
+ TestEnum();
+ TestChoices();
+ TestExtends();
+ TestObject();
+ TestTypeReference();
+ TestArrayTuple();
+ TestArrayNonTuple();
+ TestString();
+ TestNumber();
+ TestTypeClassifier();
+ TestTypes();
+}
+
+void JSONSchemaValidatorTestBase::TestComplex() {
+ scoped_ptr<base::DictionaryValue> schema(
+ LoadDictionary("complex_schema.json"));
+ scoped_ptr<base::ListValue> instance(LoadList("complex_instance.json"));
+
+ ASSERT_TRUE(schema.get());
+ ASSERT_TRUE(instance.get());
+
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Remove(instance->GetSize() - 1, NULL);
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Append(new base::DictionaryValue());
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "1",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kNumber,
+ schema::kObject));
+ instance->Remove(instance->GetSize() - 1, NULL);
+
+ base::DictionaryValue* item = NULL;
+ ASSERT_TRUE(instance->GetDictionary(0, &item));
+ item->SetString("url", "xxxxxxxxxxx");
+
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
+ "0.url",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kStringMaxLength, "10"));
+}
+
+void JSONSchemaValidatorTestBase::TestStringPattern() {
+ // Regex patterns not supported in CPP validator.
+ if (type_ == CPP)
+ return;
+
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+ schema->SetString(schema::kType, schema::kString);
+ schema->SetString(schema::kPattern, "foo+");
+
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("foo")).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("foooooo")).get(),
+ schema.get(), NULL);
+ ExpectNotValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("bar")).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kStringPattern, "foo+"));
+}
+
+void JSONSchemaValidatorTestBase::TestEnum() {
+ scoped_ptr<base::DictionaryValue> schema(LoadDictionary("enum_schema.json"));
+
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("foo")).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(false)).get(),
+ schema.get(), NULL);
+
+ ExpectNotValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("42")).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::kInvalidEnum);
+ ExpectNotValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(base::Value::CreateNullValue()).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::kInvalidEnum);
+}
+
+void JSONSchemaValidatorTestBase::TestChoices() {
+ scoped_ptr<base::DictionaryValue> schema(
+ LoadDictionary("choices_schema.json"));
+
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(base::Value::CreateNullValue()).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(), NULL);
+
+ scoped_ptr<base::DictionaryValue> instance(new base::DictionaryValue());
+ instance->SetString("foo", "bar");
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ ExpectNotValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("foo")).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::kInvalidChoice);
+ ExpectNotValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::ListValue()).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::kInvalidChoice);
+
+ instance->SetInteger("foo", 42);
+ ExpectNotValid(TEST_SOURCE,
+ instance.get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::kInvalidChoice);
+}
+
+void JSONSchemaValidatorTestBase::TestExtends() {
+ // TODO(aa): JS only
+}
+
+void JSONSchemaValidatorTestBase::TestObject() {
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+ schema->SetString(schema::kType, schema::kObject);
+ schema->SetString("properties.foo.type", schema::kString);
+ schema->SetString("properties.bar.type", schema::kInteger);
+
+ scoped_ptr<base::DictionaryValue> instance(new base::DictionaryValue());
+ instance->SetString("foo", "foo");
+ instance->SetInteger("bar", 42);
+
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ instance->SetBoolean("extra", true);
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
+ "extra", JSONSchemaValidator::kUnexpectedProperty);
+
+ instance->Remove("extra", NULL);
+ instance->Remove("bar", NULL);
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "bar",
+ JSONSchemaValidator::kObjectPropertyIsRequired);
+
+ instance->SetString("bar", "42");
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "bar",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kInteger,
+ schema::kString));
+
+ base::DictionaryValue* additional_properties = new base::DictionaryValue();
+ additional_properties->SetString(schema::kType, schema::kAny);
+ schema->Set(schema::kAdditionalProperties, additional_properties);
+
+ instance->SetInteger("bar", 42);
+ instance->SetBoolean("extra", true);
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ instance->SetString("extra", "foo");
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ additional_properties->SetString(schema::kType, schema::kBoolean);
+ instance->SetBoolean("extra", true);
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ instance->SetString("extra", "foo");
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
+ "extra", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kBoolean,
+ schema::kString));
+
+ base::DictionaryValue* properties = NULL;
+ base::DictionaryValue* bar_property = NULL;
+ ASSERT_TRUE(schema->GetDictionary(schema::kProperties, &properties));
+ ASSERT_TRUE(properties->GetDictionary("bar", &bar_property));
+
+ bar_property->SetBoolean(schema::kOptional, true);
+ instance->Remove("extra", NULL);
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Remove("bar", NULL);
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Set("bar", base::Value::CreateNullValue());
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
+ "bar", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kInteger,
+ schema::kNull));
+ instance->SetString("bar", "42");
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL,
+ "bar", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kInteger,
+ schema::kString));
+}
+
+void JSONSchemaValidatorTestBase::TestTypeReference() {
+ scoped_ptr<base::ListValue> types(LoadList("reference_types.json"));
+ ASSERT_TRUE(types.get());
+
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+ schema->SetString(schema::kType, schema::kObject);
+ schema->SetString("properties.foo.type", schema::kString);
+ schema->SetString("properties.bar.$ref", "Max10Int");
+ schema->SetString("properties.baz.$ref", "MinLengthString");
+
+ scoped_ptr<base::DictionaryValue> schema_inline(new base::DictionaryValue());
+ schema_inline->SetString(schema::kType, schema::kObject);
+ schema_inline->SetString("properties.foo.type", schema::kString);
+ schema_inline->SetString("properties.bar.id", "NegativeInt");
+ schema_inline->SetString("properties.bar.type", schema::kInteger);
+ schema_inline->SetInteger("properties.bar.maximum", 0);
+ schema_inline->SetString("properties.baz.$ref", "NegativeInt");
+
+ scoped_ptr<base::DictionaryValue> instance(new base::DictionaryValue());
+ instance->SetString("foo", "foo");
+ instance->SetInteger("bar", 4);
+ instance->SetString("baz", "ab");
+
+ scoped_ptr<base::DictionaryValue> instance_inline(
+ new base::DictionaryValue());
+ instance_inline->SetString("foo", "foo");
+ instance_inline->SetInteger("bar", -4);
+ instance_inline->SetInteger("baz", -2);
+
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), types.get());
+ ExpectValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), NULL);
+
+ // Validation failure, but successful schema reference.
+ instance->SetString("baz", "a");
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), types.get(),
+ "baz", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kStringMinLength, "2"));
+
+ instance_inline->SetInteger("bar", 20);
+ ExpectNotValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), NULL,
+ "bar", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kNumberMaximum, "0"));
+
+ // Remove MinLengthString type.
+ types->Remove(types->GetSize() - 1, NULL);
+ instance->SetString("baz", "ab");
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), types.get(),
+ "bar", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kUnknownTypeReference,
+ "Max10Int"));
+
+ // Remove internal type "NegativeInt".
+ schema_inline->Remove("properties.bar", NULL);
+ instance_inline->Remove("bar", NULL);
+ ExpectNotValid(TEST_SOURCE, instance_inline.get(), schema_inline.get(), NULL,
+ "baz", JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kUnknownTypeReference,
+ "NegativeInt"));
+}
+
+void JSONSchemaValidatorTestBase::TestArrayTuple() {
+ scoped_ptr<base::DictionaryValue> schema(
+ LoadDictionary("array_tuple_schema.json"));
+ ASSERT_TRUE(schema.get());
+
+ scoped_ptr<base::ListValue> instance(new base::ListValue());
+ instance->Append(new base::StringValue("42"));
+ instance->Append(new base::FundamentalValue(42));
+
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ instance->Append(new base::StringValue("anything"));
+ ExpectNotValid(TEST_SOURCE,
+ instance.get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kArrayMaxItems, "2"));
+
+ instance->Remove(1, NULL);
+ instance->Remove(1, NULL);
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "1",
+ JSONSchemaValidator::kArrayItemRequired);
+
+ instance->Set(0, new base::FundamentalValue(42));
+ instance->Append(new base::FundamentalValue(42));
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "0",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kString,
+ schema::kInteger));
+
+ base::DictionaryValue* additional_properties = new base::DictionaryValue();
+ additional_properties->SetString(schema::kType, schema::kAny);
+ schema->Set(schema::kAdditionalProperties, additional_properties);
+ instance->Set(0, new base::StringValue("42"));
+ instance->Append(new base::StringValue("anything"));
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Set(2, new base::ListValue());
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ additional_properties->SetString(schema::kType, schema::kBoolean);
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "2",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kBoolean,
+ schema::kArray));
+ instance->Set(2, new base::FundamentalValue(false));
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ base::ListValue* items_schema = NULL;
+ base::DictionaryValue* item0_schema = NULL;
+ ASSERT_TRUE(schema->GetList(schema::kItems, &items_schema));
+ ASSERT_TRUE(items_schema->GetDictionary(0, &item0_schema));
+ item0_schema->SetBoolean(schema::kOptional, true);
+ instance->Remove(2, NULL);
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ // TODO(aa): I think this is inconsistent with the handling of NULL+optional
+ // for objects.
+ instance->Set(0, base::Value::CreateNullValue());
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Set(0, new base::FundamentalValue(42));
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "0",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kString,
+ schema::kInteger));
+}
+
+void JSONSchemaValidatorTestBase::TestArrayNonTuple() {
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+ schema->SetString(schema::kType, schema::kArray);
+ schema->SetString("items.type", schema::kString);
+ schema->SetInteger(schema::kMinItems, 2);
+ schema->SetInteger(schema::kMaxItems, 3);
+
+ scoped_ptr<base::ListValue> instance(new base::ListValue());
+ instance->Append(new base::StringValue("x"));
+ instance->Append(new base::StringValue("x"));
+
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+ instance->Append(new base::StringValue("x"));
+ ExpectValid(TEST_SOURCE, instance.get(), schema.get(), NULL);
+
+ instance->Append(new base::StringValue("x"));
+ ExpectNotValid(TEST_SOURCE,
+ instance.get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kArrayMaxItems, "3"));
+ instance->Remove(1, NULL);
+ instance->Remove(1, NULL);
+ instance->Remove(1, NULL);
+ ExpectNotValid(TEST_SOURCE,
+ instance.get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kArrayMinItems, "2"));
+
+ instance->Remove(1, NULL);
+ instance->Append(new base::FundamentalValue(42));
+ ExpectNotValid(TEST_SOURCE, instance.get(), schema.get(), NULL, "1",
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType,
+ schema::kString,
+ schema::kInteger));
+}
+
+void JSONSchemaValidatorTestBase::TestString() {
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+ schema->SetString(schema::kType, schema::kString);
+ schema->SetInteger(schema::kMinLength, 1);
+ schema->SetInteger(schema::kMaxLength, 10);
+
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("x")).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(
+ new base::StringValue("xxxxxxxxxx")).get(),
+ schema.get(), NULL);
+
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue(std::string())).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kStringMinLength, "1"));
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("xxxxxxxxxxx")).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kStringMaxLength, "10"));
+}
+
+void JSONSchemaValidatorTestBase::TestNumber() {
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+ schema->SetString(schema::kType, schema::kNumber);
+ schema->SetInteger(schema::kMinimum, 1);
+ schema->SetInteger(schema::kMaximum, 100);
+ schema->SetInteger("maxDecimal", 2);
+
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(1)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(50)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(100)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(88.88)).get(),
+ schema.get(), NULL);
+
+ ExpectNotValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(0.5)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kNumberMinimum, "1"));
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(100.1)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kNumberMaximum, "100"));
+}
+
+void JSONSchemaValidatorTestBase::TestTypeClassifier() {
+ EXPECT_EQ(std::string(schema::kBoolean),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(
+ new base::FundamentalValue(true)).get()));
+ EXPECT_EQ(std::string(schema::kBoolean),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(
+ new base::FundamentalValue(false)).get()));
+
+ // It doesn't matter whether the C++ type is 'integer' or 'real'. If the
+ // number is integral and within the representable range of integers in
+ // double, it's classified as 'integer'.
+ EXPECT_EQ(std::string(schema::kInteger),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get()));
+ EXPECT_EQ(std::string(schema::kInteger),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(new base::FundamentalValue(0)).get()));
+ EXPECT_EQ(std::string(schema::kInteger),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get()));
+ EXPECT_EQ(std::string(schema::kInteger),
+ JSONSchemaValidator::GetJSONSchemaType(scoped_ptr<base::Value>(
+ new base::FundamentalValue(pow(2.0, DBL_MANT_DIG))).get()));
+ EXPECT_EQ(std::string(schema::kInteger),
+ JSONSchemaValidator::GetJSONSchemaType(scoped_ptr<base::Value>(
+ new base::FundamentalValue(pow(-2.0, DBL_MANT_DIG))).get()));
+
+ // "number" is only used for non-integral numbers, or numbers beyond what
+ // double can accurately represent.
+ EXPECT_EQ(std::string(schema::kNumber),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(
+ new base::FundamentalValue(88.8)).get()));
+ EXPECT_EQ(std::string(schema::kNumber),
+ JSONSchemaValidator::GetJSONSchemaType(scoped_ptr<base::Value>(
+ new base::FundamentalValue(pow(2.0, DBL_MANT_DIG) * 2)).get()));
+ EXPECT_EQ(std::string(schema::kNumber),
+ JSONSchemaValidator::GetJSONSchemaType(scoped_ptr<base::Value>(
+ new base::FundamentalValue(
+ pow(-2.0, DBL_MANT_DIG) * 2)).get()));
+
+ EXPECT_EQ(std::string(schema::kString),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(new base::StringValue("foo")).get()));
+ EXPECT_EQ(std::string(schema::kArray),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(new base::ListValue()).get()));
+ EXPECT_EQ(std::string(schema::kObject),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(new base::DictionaryValue()).get()));
+ EXPECT_EQ(std::string(schema::kNull),
+ JSONSchemaValidator::GetJSONSchemaType(
+ scoped_ptr<base::Value>(base::Value::CreateNullValue()).get()));
+}
+
+void JSONSchemaValidatorTestBase::TestTypes() {
+ scoped_ptr<base::DictionaryValue> schema(new base::DictionaryValue());
+
+ // valid
+ schema->SetString(schema::kType, schema::kObject);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::DictionaryValue()).get(),
+ schema.get(), NULL);
+
+ schema->SetString(schema::kType, schema::kArray);
+ ExpectValid(TEST_SOURCE, scoped_ptr<base::Value>(new base::ListValue()).get(),
+ schema.get(), NULL);
+
+ schema->SetString(schema::kType, schema::kString);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("foobar")).get(),
+ schema.get(), NULL);
+
+ schema->SetString(schema::kType, schema::kNumber);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(88.8)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(0)).get(),
+ schema.get(), NULL);
+
+ schema->SetString(schema::kType, schema::kInteger);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(0)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(
+ new base::FundamentalValue(pow(2.0, DBL_MANT_DIG))).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(
+ new base::FundamentalValue(pow(-2.0, DBL_MANT_DIG))).get(),
+ schema.get(), NULL);
+
+ schema->SetString(schema::kType, schema::kBoolean);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(false)).get(),
+ schema.get(), NULL);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(true)).get(),
+ schema.get(), NULL);
+
+ schema->SetString(schema::kType, schema::kNull);
+ ExpectValid(TEST_SOURCE,
+ scoped_ptr<base::Value>(base::Value::CreateNullValue()).get(),
+ schema.get(), NULL);
+
+ // not valid
+ schema->SetString(schema::kType, schema::kObject);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::ListValue()).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType, schema::kObject, schema::kArray));
+
+ schema->SetString(schema::kType, schema::kObject);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(base::Value::CreateNullValue()).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType, schema::kObject, schema::kNull));
+
+ schema->SetString(schema::kType, schema::kArray);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType, schema::kArray, schema::kInteger));
+
+ schema->SetString(schema::kType, schema::kString);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(42)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(JSONSchemaValidator::kInvalidType,
+ schema::kString,
+ schema::kInteger));
+
+ schema->SetString(schema::kType, schema::kNumber);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::StringValue("42")).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType, schema::kNumber, schema::kString));
+
+ schema->SetString(schema::kType, schema::kInteger);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(88.8)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::kInvalidTypeIntegerNumber);
+
+ schema->SetString(schema::kType, schema::kBoolean);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(1)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(JSONSchemaValidator::kInvalidType,
+ schema::kBoolean,
+ schema::kInteger));
+
+ schema->SetString(schema::kType, schema::kNull);
+ ExpectNotValid(
+ TEST_SOURCE,
+ scoped_ptr<base::Value>(new base::FundamentalValue(false)).get(),
+ schema.get(),
+ NULL,
+ std::string(),
+ JSONSchemaValidator::FormatErrorMessage(
+ JSONSchemaValidator::kInvalidType, schema::kNull, schema::kBoolean));
+}
diff --git a/chromium/components/json_schema/json_schema_validator_unittest_base.h b/chromium/components/json_schema/json_schema_validator_unittest_base.h
new file mode 100644
index 00000000000..7b4854e21e8
--- /dev/null
+++ b/chromium/components/json_schema/json_schema_validator_unittest_base.h
@@ -0,0 +1,63 @@
+// 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_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_UNITTEST_BASE_H_
+#define COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_UNITTEST_BASE_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+class Value;
+}
+
+// Base class for unit tests for JSONSchemaValidator. There is currently only
+// one implementation, JSONSchemaValidatorCPPTest.
+//
+// TODO(aa): Refactor chrome/test/data/json_schema_test.js into
+// JSONSchemaValidatorJSTest that inherits from this.
+class JSONSchemaValidatorTestBase : public testing::Test {
+ public:
+ enum ValidatorType {
+ CPP = 1,
+ JS = 2
+ };
+
+ explicit JSONSchemaValidatorTestBase(ValidatorType type);
+
+ void RunTests();
+
+ protected:
+ virtual void ExpectValid(const std::string& test_source,
+ base::Value* instance,
+ base::DictionaryValue* schema,
+ base::ListValue* types) = 0;
+
+ virtual void ExpectNotValid(const std::string& test_source,
+ base::Value* instance,
+ base::DictionaryValue* schema,
+ base::ListValue* types,
+ const std::string& expected_error_path,
+ const std::string& expected_error_message) = 0;
+
+ private:
+ void TestComplex();
+ void TestStringPattern();
+ void TestEnum();
+ void TestChoices();
+ void TestExtends();
+ void TestObject();
+ void TestTypeReference();
+ void TestArrayTuple();
+ void TestArrayNonTuple();
+ void TestString();
+ void TestNumber();
+ void TestTypeClassifier();
+ void TestTypes();
+
+ ValidatorType type_;
+};
+
+#endif // COMPONENTS_JSON_SCHEMA_JSON_SCHEMA_VALIDATOR_UNITTEST_BASE_H_
diff --git a/chromium/components/nacl.gyp b/chromium/components/nacl.gyp
new file mode 100644
index 00000000000..f3ded8354a8
--- /dev/null
+++ b/chromium/components/nacl.gyp
@@ -0,0 +1,167 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../native_client/build/untrusted.gypi',
+ 'nacl/nacl_defines.gypi',
+ ],
+ 'target_defaults': {
+ 'variables': {
+ 'nacl_target': 0,
+ },
+ 'target_conditions': [
+ # This part is shared between the targets defined below. Only files and
+ # settings relevant for building the Win64 target should be added here.
+ ['nacl_target==1', {
+ 'include_dirs': [
+ '<(INTERMEDIATE_DIR)',
+ ],
+ 'defines': [
+ '<@(nacl_defines)',
+ ],
+ 'sources': [
+ # .cc, .h, and .mm files under nacl that are used on all
+ # platforms, including both 32-bit and 64-bit Windows.
+ # Test files are also not included.
+ 'nacl/loader/nacl_ipc_adapter.cc',
+ 'nacl/loader/nacl_ipc_adapter.h',
+ 'nacl/loader/nacl_main.cc',
+ 'nacl/loader/nacl_main_platform_delegate.h',
+ 'nacl/loader/nacl_main_platform_delegate_linux.cc',
+ 'nacl/loader/nacl_main_platform_delegate_mac.mm',
+ 'nacl/loader/nacl_main_platform_delegate_win.cc',
+ 'nacl/loader/nacl_listener.cc',
+ 'nacl/loader/nacl_listener.h',
+ 'nacl/loader/nacl_validation_db.h',
+ 'nacl/loader/nacl_validation_query.cc',
+ 'nacl/loader/nacl_validation_query.h',
+ ],
+ # TODO(gregoryd): consider switching NaCl to use Chrome OS defines
+ 'conditions': [
+ ['OS=="win"', {
+ 'defines': [
+ '__STDC_LIMIT_MACROS=1',
+ ],
+ 'include_dirs': [
+ '<(DEPTH)/third_party/wtl/include',
+ ],
+ },],
+ ['OS=="linux"', {
+ 'defines': [
+ '__STDC_LIMIT_MACROS=1',
+ ],
+ 'sources': [
+ '../components/nacl/common/nacl_paths.cc',
+ '../components/nacl/common/nacl_paths.h',
+ '../components/nacl/zygote/nacl_fork_delegate_linux.cc',
+ '../components/nacl/zygote/nacl_fork_delegate_linux.h',
+ ],
+ },],
+ ],
+ }],
+ ],
+ },
+ 'conditions': [
+ ['disable_nacl!=1', {
+ 'targets': [
+ {
+ 'target_name': 'nacl',
+ 'type': 'static_library',
+ 'variables': {
+ 'nacl_target': 1,
+ },
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ipc/ipc.gyp:ipc',
+ '../ppapi/native_client/src/trusted/plugin/plugin.gyp:ppGoogleNaClPluginChrome',
+ '../ppapi/ppapi_internal.gyp:ppapi_shared',
+ '../ppapi/ppapi_internal.gyp:ppapi_ipc',
+ '../native_client/src/trusted/service_runtime/service_runtime.gyp:sel_main_chrome',
+ ],
+ 'conditions': [
+ ['disable_nacl_untrusted==0', {
+ 'dependencies': [
+ '../ppapi/native_client/native_client.gyp:nacl_irt',
+ '../ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:pnacl_irt_shim',
+ '../ppapi/native_client/src/untrusted/pnacl_support_extension/pnacl_support_extension.gyp:pnacl_support_extension',
+ ],
+ }],
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ '<@(nacl_defines)',
+ ],
+ },
+ },
+ ],
+ 'conditions': [
+ ['OS=="win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ 'target_name': 'nacl_win64',
+ 'type': 'static_library',
+ 'variables': {
+ 'nacl_target': 1,
+ },
+ 'dependencies': [
+ '../native_client/src/trusted/service_runtime/service_runtime.gyp:sel_main_chrome64',
+ '../ppapi/ppapi_internal.gyp:ppapi_shared_win64',
+ '../ppapi/ppapi_internal.gyp:ppapi_ipc_win64',
+ '../components/nacl_common.gyp:nacl_common_win64',
+ ],
+ 'export_dependent_settings': [
+ '../ppapi/ppapi_internal.gyp:ppapi_ipc_win64',
+ ],
+ 'sources': [
+ '../components/nacl/broker/nacl_broker_listener.cc',
+ '../components/nacl/broker/nacl_broker_listener.h',
+ '../components/nacl/common/nacl_debug_exception_handler_win.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ 'COMPILE_CONTENT_STATICALLY',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'direct_dependent_settings': {
+ 'defines': [
+ '<@(nacl_defines)',
+ ],
+ },
+ },
+ ],
+ }],
+ ],
+ }, { # else (disable_nacl==1)
+ 'targets': [
+ {
+ 'target_name': 'nacl',
+ 'type': 'none',
+ 'sources': [],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win"', {
+ 'targets': [
+ {
+ 'target_name': 'nacl_win64',
+ 'type': 'none',
+ 'sources': [],
+ },
+ ],
+ }],
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/nacl/OWNERS b/chromium/components/nacl/OWNERS
new file mode 100644
index 00000000000..7b60d6b9fc0
--- /dev/null
+++ b/chromium/components/nacl/OWNERS
@@ -0,0 +1,7 @@
+bradchen@chromium.org
+bradnelson@chromium.org
+jvoung@chromium.org
+mseaborn@chromium.org
+noelallen@chromium.org
+sehr@chromium.org
+
diff --git a/chromium/components/nacl/broker/DEPS b/chromium/components/nacl/broker/DEPS
new file mode 100644
index 00000000000..cd726c6aae0
--- /dev/null
+++ b/chromium/components/nacl/broker/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/app/startup_helper_win.h",
+ "+sandbox/win/src",
+]
diff --git a/chromium/components/nacl/broker/nacl_broker_listener.cc b/chromium/components/nacl/broker/nacl_broker_listener.cc
new file mode 100644
index 00000000000..cc365d64a7f
--- /dev/null
+++ b/chromium/components/nacl/broker/nacl_broker_listener.cc
@@ -0,0 +1,129 @@
+// 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/nacl/broker/nacl_broker_listener.h"
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/path_service.h"
+#include "components/nacl/common/nacl_cmd_line.h"
+#include "components/nacl/common/nacl_debug_exception_handler_win.h"
+#include "components/nacl/common/nacl_messages.h"
+#include "components/nacl/common/nacl_switches.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/sandbox_init.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_switches.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace {
+
+void SendReply(IPC::Channel* channel, int32 pid, bool result) {
+ channel->Send(new NaClProcessMsg_DebugExceptionHandlerLaunched(pid, result));
+}
+
+} // namespace
+
+NaClBrokerListener::NaClBrokerListener()
+ : browser_handle_(base::kNullProcessHandle) {
+}
+
+NaClBrokerListener::~NaClBrokerListener() {
+ base::CloseProcessHandle(browser_handle_);
+}
+
+void NaClBrokerListener::Listen() {
+ std::string channel_name =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kProcessChannelID);
+ channel_.reset(new IPC::Channel(
+ channel_name, IPC::Channel::MODE_CLIENT, this));
+ CHECK(channel_->Connect());
+ base::MessageLoop::current()->Run();
+}
+
+// NOTE: changes to this method need to be reviewed by the security team.
+void NaClBrokerListener::PreSpawnTarget(sandbox::TargetPolicy* policy,
+ bool* success) {
+ // This code is duplicated in chrome_content_browser_client.cc.
+
+ // Allow the server side of a pipe restricted to the "chrome.nacl."
+ // namespace so that it cannot impersonate other system or other chrome
+ // service pipes.
+ sandbox::ResultCode result = policy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
+ sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\chrome.nacl.*");
+ *success = (result == sandbox::SBOX_ALL_OK);
+}
+
+void NaClBrokerListener::OnChannelConnected(int32 peer_pid) {
+ bool res = base::OpenPrivilegedProcessHandle(peer_pid, &browser_handle_);
+ CHECK(res);
+}
+
+bool NaClBrokerListener::OnMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(NaClBrokerListener, msg)
+ IPC_MESSAGE_HANDLER(NaClProcessMsg_LaunchLoaderThroughBroker,
+ OnLaunchLoaderThroughBroker)
+ IPC_MESSAGE_HANDLER(NaClProcessMsg_LaunchDebugExceptionHandler,
+ OnLaunchDebugExceptionHandler)
+ IPC_MESSAGE_HANDLER(NaClProcessMsg_StopBroker, OnStopBroker)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void NaClBrokerListener::OnChannelError() {
+ // The browser died unexpectedly, quit to avoid a zombie process.
+ base::MessageLoop::current()->Quit();
+}
+
+void NaClBrokerListener::OnLaunchLoaderThroughBroker(
+ const std::string& loader_channel_id) {
+ base::ProcessHandle loader_process = 0;
+ base::ProcessHandle loader_handle_in_browser = 0;
+
+ // Create the path to the nacl broker/loader executable - it's the executable
+ // this code is running in.
+ base::FilePath exe_path;
+ PathService::Get(base::FILE_EXE, &exe_path);
+ if (!exe_path.empty()) {
+ CommandLine* cmd_line = new CommandLine(exe_path);
+ nacl::CopyNaClCommandLineArguments(cmd_line);
+
+ cmd_line->AppendSwitchASCII(switches::kProcessType,
+ switches::kNaClLoaderProcess);
+
+ cmd_line->AppendSwitchASCII(switches::kProcessChannelID,
+ loader_channel_id);
+
+ loader_process = content::StartSandboxedProcess(this, cmd_line);
+ if (loader_process) {
+ DuplicateHandle(::GetCurrentProcess(), loader_process,
+ browser_handle_, &loader_handle_in_browser,
+ PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION , FALSE, 0);
+ base::CloseProcessHandle(loader_process);
+ }
+ }
+ channel_->Send(new NaClProcessMsg_LoaderLaunched(loader_channel_id,
+ loader_handle_in_browser));
+}
+
+void NaClBrokerListener::OnLaunchDebugExceptionHandler(
+ int32 pid, base::ProcessHandle process_handle,
+ const std::string& startup_info) {
+ NaClStartDebugExceptionHandlerThread(
+ process_handle, startup_info,
+ base::MessageLoopProxy::current(),
+ base::Bind(SendReply, channel_.get(), pid));
+}
+
+void NaClBrokerListener::OnStopBroker() {
+ base::MessageLoop::current()->Quit();
+}
diff --git a/chromium/components/nacl/broker/nacl_broker_listener.h b/chromium/components/nacl/broker/nacl_broker_listener.h
new file mode 100644
index 00000000000..0b0fff5cf8b
--- /dev/null
+++ b/chromium/components/nacl/broker/nacl_broker_listener.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_NACL_BROKER_NACL_BROKER_LISTENER_H_
+#define COMPONENTS_NACL_BROKER_NACL_BROKER_LISTENER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/process/process.h"
+#include "components/nacl/common/nacl_types.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "ipc/ipc_listener.h"
+
+namespace IPC {
+class Channel;
+}
+
+// The BrokerThread class represents the thread that handles the messages from
+// the browser process and starts NaCl loader processes.
+class NaClBrokerListener : public content::SandboxedProcessLauncherDelegate,
+ public IPC::Listener {
+ public:
+ NaClBrokerListener();
+ ~NaClBrokerListener();
+
+ void Listen();
+
+ // content::SandboxedProcessLauncherDelegate implementation:
+ virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
+ bool* success) OVERRIDE;
+
+ // IPC::Listener implementation.
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+
+ private:
+ void OnLaunchLoaderThroughBroker(const std::string& loader_channel_id);
+ void OnLaunchDebugExceptionHandler(int32 pid,
+ base::ProcessHandle process_handle,
+ const std::string& startup_info);
+ void OnStopBroker();
+
+ base::ProcessHandle browser_handle_;
+ scoped_ptr<IPC::Channel> channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(NaClBrokerListener);
+};
+
+#endif // COMPONENTS_NACL_BROKER_NACL_BROKER_LISTENER_H_
diff --git a/chromium/components/nacl/common/DEPS b/chromium/components/nacl/common/DEPS
new file mode 100644
index 00000000000..9583dc6ba82
--- /dev/null
+++ b/chromium/components/nacl/common/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+native_client/src",
+]
diff --git a/chromium/components/nacl/common/OWNERS b/chromium/components/nacl/common/OWNERS
new file mode 100644
index 00000000000..6a8067ecf2f
--- /dev/null
+++ b/chromium/components/nacl/common/OWNERS
@@ -0,0 +1,10 @@
+# Changes to IPC messages require a security review to avoid introducing
+# new sandbox escapes.
+per-file *_messages*.h=set noparent
+per-file *_messages*.h=cdn@chromium.org
+per-file *_messages*.h=cevans@chromium.org
+per-file *_messages*.h=jln@chromium.org
+per-file *_messages*.h=jschuh@chromium.org
+per-file *_messages*.h=palmer@chromium.org
+per-file *_messages*.h=tsepez@chromium.org
+
diff --git a/chromium/components/nacl/common/nacl_browser_delegate.h b/chromium/components/nacl/common/nacl_browser_delegate.h
new file mode 100644
index 00000000000..6cff071716e
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_browser_delegate.h
@@ -0,0 +1,66 @@
+// 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_NACL_COMMON_NACL_BROWSER_DELEGATE_H_
+#define COMPONENTS_NACL_COMMON_NACL_BROWSER_DELEGATE_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace ppapi {
+namespace host {
+class HostFactory;
+}
+}
+
+namespace content {
+class BrowserPpapiHost;
+}
+
+// Encapsulates the dependencies of NaCl code on chrome/, to avoid a direct
+// dependency on chrome/.
+class NaClBrowserDelegate {
+ public:
+ virtual ~NaClBrowserDelegate() {}
+
+ // Show an infobar to the user.
+ virtual void ShowNaClInfobar(int render_process_id, int render_view_id,
+ int error_id) = 0;
+ // Returns whether dialogs are allowed. This is used to decide if to add the
+ // command line switch kNoErrorDialogs.
+ virtual bool DialogsAreSuppressed() = 0;
+ // Returns true on success, false otherwise. On success |cache_dir| contains
+ // the cache directory. On failure, it is not changed.
+ virtual bool GetCacheDirectory(base::FilePath* cache_dir) = 0;
+ // Returns true on success, false otherwise. On success |plugin_dir| contains
+ // the directory where the plugins are located. On failure, it is not
+ // changed.
+ virtual bool GetPluginDirectory(base::FilePath* plugin_dir) = 0;
+ // Returns true on success, false otherwise. On success |pnacl_dir| contains
+ // the directory where the PNaCl files are located. On failure, it is not
+ // changed.
+ virtual bool GetPnaclDirectory(base::FilePath* pnacl_dir) = 0;
+ // Returns true on success, false otherwise. On success |user_dir| contains
+ // the user data directory. On failure, it is not changed.
+ virtual bool GetUserDirectory(base::FilePath* user_dir) = 0;
+ // Returns the version as a string. This string is used to invalidate
+ // validator cache entries when Chromium is upgraded
+ virtual std::string GetVersionString() const = 0;
+ // Returns a HostFactory that hides the details of its embedder.
+ virtual ppapi::host::HostFactory* CreatePpapiHostFactory(
+ content::BrowserPpapiHost* ppapi_host) = 0;
+ // Install PNaCl if this operation is supported. On success, the |installed|
+ // callback should be called with true, and on failure (or not supported),
+ // the |installed| callback should be called with false.
+ // TODO(jvoung): Add the progress callback as well.
+ virtual void TryInstallPnacl(
+ const base::Callback<void(bool)>& installed) = 0;
+};
+
+#endif // COMPONENTS_NACL_COMMON_NACL_BROWSER_DELEGATE_H_
diff --git a/chromium/components/nacl/common/nacl_cmd_line.cc b/chromium/components/nacl/common/nacl_cmd_line.cc
new file mode 100644
index 00000000000..d9bbd6557f4
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_cmd_line.cc
@@ -0,0 +1,36 @@
+// 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 "base/base_switches.h"
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "components/nacl/common/nacl_switches.h"
+#include "content/public/common/content_switches.h"
+
+namespace nacl {
+
+void CopyNaClCommandLineArguments(CommandLine* cmd_line) {
+ const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+
+ // Propagate the following switches to the NaCl loader command line (along
+ // with any associated values) if present in the browser command line.
+ // TODO(gregoryd): check which flags of those below can be supported.
+ static const char* const kSwitchNames[] = {
+ switches::kNoSandbox,
+ switches::kDisableBreakpad,
+ switches::kFullMemoryCrashReport,
+ switches::kEnableLogging,
+ switches::kDisableLogging,
+ switches::kLoggingLevel,
+ switches::kEnableDCHECK,
+ switches::kNoErrorDialogs,
+#if defined(OS_MACOSX)
+ switches::kEnableSandboxLogging,
+#endif
+ };
+ cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
+ arraysize(kSwitchNames));
+}
+
+} // namespace nacl
diff --git a/chromium/components/nacl/common/nacl_cmd_line.h b/chromium/components/nacl/common/nacl_cmd_line.h
new file mode 100644
index 00000000000..4e4e7536135
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_cmd_line.h
@@ -0,0 +1,16 @@
+// 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_NACL_COMMON_NACL_CMD_LINE_H_
+#define COMPONENTS_NACL_COMMON_NACL_CMD_LINE_H_
+
+class CommandLine;
+
+namespace nacl {
+ // Copy all the relevant arguments from the command line of the current
+ // process to cmd_line that will be used for launching the NaCl loader/broker.
+ void CopyNaClCommandLineArguments(CommandLine* cmd_line);
+}
+
+#endif // COMPONENTS_NACL_COMMON_NACL_CMD_LINE_H_
diff --git a/chromium/components/nacl/common/nacl_debug_exception_handler_win.cc b/chromium/components/nacl/common/nacl_debug_exception_handler_win.cc
new file mode 100644
index 00000000000..f661391c4f4
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_debug_exception_handler_win.cc
@@ -0,0 +1,78 @@
+// 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/nacl/common/nacl_debug_exception_handler_win.h"
+
+#include "base/bind.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+#include "native_client/src/trusted/service_runtime/win/debug_exception_handler.h"
+
+namespace {
+
+class DebugExceptionHandler : public base::PlatformThread::Delegate {
+ public:
+ DebugExceptionHandler(base::ProcessHandle nacl_process,
+ const std::string& startup_info,
+ base::MessageLoopProxy* message_loop,
+ const base::Callback<void(bool)>& on_connected)
+ : nacl_process_(nacl_process),
+ startup_info_(startup_info),
+ message_loop_(message_loop),
+ on_connected_(on_connected) {
+ }
+
+ virtual void ThreadMain() OVERRIDE {
+ // In the Windows API, the set of processes being debugged is
+ // thread-local, so we have to attach to the process (using
+ // DebugActiveProcess()) on the same thread on which
+ // NaClDebugExceptionHandlerRun() receives debug events for the
+ // process.
+ bool attached = false;
+ int pid = GetProcessId(nacl_process_);
+ if (pid == 0) {
+ LOG(ERROR) << "Invalid process handle";
+ } else {
+ if (!DebugActiveProcess(pid)) {
+ LOG(ERROR) << "Failed to connect to the process";
+ } else {
+ attached = true;
+ }
+ }
+ message_loop_->PostTask(FROM_HERE, base::Bind(on_connected_, attached));
+
+ if (attached) {
+ NaClDebugExceptionHandlerRun(
+ nacl_process_,
+ reinterpret_cast<const void*>(startup_info_.data()),
+ startup_info_.size());
+ }
+ delete this;
+ }
+
+ private:
+ base::win::ScopedHandle nacl_process_;
+ std::string startup_info_;
+ base::MessageLoopProxy* message_loop_;
+ base::Callback<void(bool)> on_connected_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugExceptionHandler);
+};
+
+} // namespace
+
+void NaClStartDebugExceptionHandlerThread(
+ base::ProcessHandle nacl_process,
+ const std::string& startup_info,
+ base::MessageLoopProxy* message_loop,
+ const base::Callback<void(bool)>& on_connected) {
+ // The new PlatformThread will take ownership of the
+ // DebugExceptionHandler object, which will delete itself on exit.
+ DebugExceptionHandler* handler = new DebugExceptionHandler(
+ nacl_process, startup_info, message_loop, on_connected);
+ if (!base::PlatformThread::CreateNonJoinable(0, handler)) {
+ on_connected.Run(false);
+ delete handler;
+ }
+}
diff --git a/chromium/components/nacl/common/nacl_debug_exception_handler_win.h b/chromium/components/nacl/common/nacl_debug_exception_handler_win.h
new file mode 100644
index 00000000000..42beefe8514
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_debug_exception_handler_win.h
@@ -0,0 +1,18 @@
+// 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_NACL_COMMON_NACL_DEBUG_EXCEPTION_HANDLER_WIN_H_
+#define COMPONENTS_NACL_COMMON_NACL_DEBUG_EXCEPTION_HANDLER_WIN_H_
+
+#include "base/callback.h"
+#include "base/message_loop/message_loop.h"
+#include "base/process/process.h"
+
+void NaClStartDebugExceptionHandlerThread(
+ base::ProcessHandle nacl_process,
+ const std::string& startup_info,
+ base::MessageLoopProxy* message_loop,
+ const base::Callback<void(bool)>& on_connected);
+
+#endif // COMPONENTS_NACL_COMMON_NACL_DEBUG_EXCEPTION_HANDLER_WIN_H_
diff --git a/chromium/components/nacl/common/nacl_helper_linux.h b/chromium/components/nacl/common/nacl_helper_linux.h
new file mode 100644
index 00000000000..732b21570a9
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_helper_linux.h
@@ -0,0 +1,42 @@
+// 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_NACL_COMMON_NACL_HELPER_LINUX_H_
+#define COMPONENTS_NACL_COMMON_NACL_HELPER_LINUX_H_
+
+// A mini-zygote specifically for Native Client. This file defines
+// constants used to implement communication between the nacl_helper
+// process and the Chrome zygote.
+
+// Used by Helper to tell Zygote it has started successfully.
+#define kNaClHelperStartupAck "NACLHELPER_OK"
+// Used by Zygote to ask Helper to fork a new NaCl loader.
+#define kNaClForkRequest "NACLFORK"
+
+// The next set of constants define global Linux file descriptors.
+// For communications between NaCl loader and browser.
+// See also content/common/zygote_main_linux.cc and
+// http://code.google.com/p/chromium/wiki/LinuxZygote
+
+// For communications between NaCl loader and zygote.
+#define kNaClZygoteDescriptor 3
+// For communications between the NaCl loader process and
+// the SUID sandbox.
+#define kNaClSandboxDescriptor 5
+// NOTE: kNaClSandboxDescriptor must match
+// content/browser/zygote_main_linux.cc
+// kMagicSandboxIPCDescriptor.
+
+// A fork request from the Zygote to the helper includes an array
+// of three file descriptors. These constants are used as indicies
+// into the array.
+// Used to pass in the descriptor for talking to the Browser
+#define kNaClBrowserFDIndex 0
+// The next two are used in the protocol for discovering the
+// child processes real PID from within the SUID sandbox. See
+// http://code.google.com/p/chromium/wiki/LinuxZygote
+#define kNaClDummyFDIndex 1
+#define kNaClParentFDIndex 2
+
+#endif // COMPONENTS_NACL_COMMON_NACL_HELPER_LINUX_H_
diff --git a/chromium/components/nacl/common/nacl_host_messages.h b/chromium/components/nacl/common/nacl_host_messages.h
new file mode 100644
index 00000000000..d9a835ba28c
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_host_messages.h
@@ -0,0 +1,110 @@
+// 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.
+
+// Multiply-included file, no traditional include guard.
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/process/process.h"
+#include "build/build_config.h"
+#include "components/nacl/common/nacl_types.h"
+#include "components/nacl/common/pnacl_types.h"
+#include "content/public/common/common_param_traits.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_platform_file.h"
+#include "url/gurl.h"
+
+#define IPC_MESSAGE_START NaClHostMsgStart
+
+IPC_STRUCT_TRAITS_BEGIN(nacl::NaClLaunchParams)
+ IPC_STRUCT_TRAITS_MEMBER(manifest_url)
+ IPC_STRUCT_TRAITS_MEMBER(render_view_id)
+ IPC_STRUCT_TRAITS_MEMBER(permission_bits)
+ IPC_STRUCT_TRAITS_MEMBER(uses_irt)
+ IPC_STRUCT_TRAITS_MEMBER(enable_dyncode_syscalls)
+ IPC_STRUCT_TRAITS_MEMBER(enable_exception_handling)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(nacl::NaClLaunchResult)
+ IPC_STRUCT_TRAITS_MEMBER(imc_channel_handle)
+ IPC_STRUCT_TRAITS_MEMBER(ipc_channel_handle)
+ IPC_STRUCT_TRAITS_MEMBER(plugin_pid)
+ IPC_STRUCT_TRAITS_MEMBER(plugin_child_id)
+IPC_STRUCT_TRAITS_END()
+
+IPC_STRUCT_TRAITS_BEGIN(nacl::PnaclCacheInfo)
+ IPC_STRUCT_TRAITS_MEMBER(pexe_url)
+ IPC_STRUCT_TRAITS_MEMBER(abi_version)
+ IPC_STRUCT_TRAITS_MEMBER(opt_level)
+ IPC_STRUCT_TRAITS_MEMBER(last_modified)
+ IPC_STRUCT_TRAITS_MEMBER(etag)
+IPC_STRUCT_TRAITS_END()
+
+// A renderer sends this to the browser process when it wants to start
+// a new instance of the Native Client process. The browser will launch
+// the process and return an IPC channel handle. This handle will only
+// be valid if the NaCl IPC proxy is enabled.
+IPC_SYNC_MESSAGE_CONTROL1_2(NaClHostMsg_LaunchNaCl,
+ nacl::NaClLaunchParams /* launch_params */,
+ nacl::NaClLaunchResult /* launch_result */,
+ std::string /* error_message */)
+
+// A renderer sends this to the browser process when it wants to
+// ensure that PNaCl is installed.
+IPC_MESSAGE_CONTROL1(NaClHostMsg_EnsurePnaclInstalled,
+ int /* pp_instance */)
+
+// The browser replies to the renderer's request to ensure that
+// PNaCl is installed.
+IPC_MESSAGE_CONTROL2(NaClViewMsg_EnsurePnaclInstalledReply,
+ int /* pp_instance */,
+ bool /* success */)
+
+// A renderer sends this to the browser process when it wants to
+// open a file for from the Pnacl component directory.
+IPC_SYNC_MESSAGE_CONTROL1_1(NaClHostMsg_GetReadonlyPnaclFD,
+ std::string /* name of requested PNaCl file */,
+ IPC::PlatformFileForTransit /* output file */)
+
+// A renderer sends this to the browser process when it wants to
+// create a temporary file.
+IPC_SYNC_MESSAGE_CONTROL0_1(NaClHostMsg_NaClCreateTemporaryFile,
+ IPC::PlatformFileForTransit /* out file */)
+
+// A renderer sends this to the browser to request a file descriptor for
+// a translated nexe.
+IPC_MESSAGE_CONTROL3(NaClHostMsg_NexeTempFileRequest,
+ int /* render_view_id */,
+ int /* instance */,
+ nacl::PnaclCacheInfo /* cache info */)
+
+// The browser replies to a renderer's temp file request with output_file,
+// which is either a writeable temp file to use for translation, or a
+// read-only file containing the translated nexe from the cache.
+IPC_MESSAGE_CONTROL3(NaClViewMsg_NexeTempFileReply,
+ int /* instance */,
+ bool /* is_cache_hit */,
+ IPC::PlatformFileForTransit /* output file */)
+
+// A renderer sends this to the browser to report that its translation has
+// finished and its temp file contains the translated nexe.
+IPC_MESSAGE_CONTROL2(NaClHostMsg_ReportTranslationFinished,
+ int /* instance */,
+ bool /* success */)
+
+// A renderer sends this to the browser process to report an error.
+IPC_MESSAGE_CONTROL2(NaClHostMsg_NaClErrorStatus,
+ int /* render_view_id */,
+ int /* Error ID */)
+
+// A renderer sends this to the browser process when it wants to
+// open a NaCl executable file from an installed application directory.
+IPC_SYNC_MESSAGE_CONTROL2_3(NaClHostMsg_OpenNaClExecutable,
+ int /* render_view_id */,
+ GURL /* URL of NaCl executable file */,
+ IPC::PlatformFileForTransit /* output file */,
+ uint64 /* file_token_lo */,
+ uint64 /* file_token_hi */)
diff --git a/chromium/components/nacl/common/nacl_messages.cc b/chromium/components/nacl/common/nacl_messages.cc
new file mode 100644
index 00000000000..21407368aef
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_messages.cc
@@ -0,0 +1,34 @@
+// 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.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "components/nacl/common/nacl_messages.h"
+
+// Generate constructors.
+#include "ipc/struct_constructor_macros.h"
+#include "components/nacl/common/nacl_messages.h"
+
+// Generate destructors.
+#include "ipc/struct_destructor_macros.h"
+#include "components/nacl/common/nacl_messages.h"
+
+// Generate param traits write methods.
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#include "components/nacl/common/nacl_messages.h"
+} // namespace IPC
+
+// Generate param traits read methods.
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#include "components/nacl/common/nacl_messages.h"
+} // namespace IPC
+
+// Generate param traits log methods.
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#include "components/nacl/common/nacl_messages.h"
+} // namespace IPC
+
diff --git a/chromium/components/nacl/common/nacl_messages.h b/chromium/components/nacl/common/nacl_messages.h
new file mode 100644
index 00000000000..068a0f9cc5e
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_messages.h
@@ -0,0 +1,94 @@
+// 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.
+
+// Defines messages between the browser and NaCl process.
+
+// Multiply-included message file, no traditional include guard.
+#include "base/process/process.h"
+#include "components/nacl/common/nacl_types.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_platform_file.h"
+
+#define IPC_MESSAGE_START NaClMsgStart
+
+IPC_STRUCT_TRAITS_BEGIN(nacl::NaClStartParams)
+ IPC_STRUCT_TRAITS_MEMBER(handles)
+ IPC_STRUCT_TRAITS_MEMBER(debug_stub_server_bound_socket)
+ IPC_STRUCT_TRAITS_MEMBER(validation_cache_enabled)
+ IPC_STRUCT_TRAITS_MEMBER(validation_cache_key)
+ IPC_STRUCT_TRAITS_MEMBER(version)
+ IPC_STRUCT_TRAITS_MEMBER(enable_exception_handling)
+ IPC_STRUCT_TRAITS_MEMBER(enable_debug_stub)
+ IPC_STRUCT_TRAITS_MEMBER(enable_ipc_proxy)
+ IPC_STRUCT_TRAITS_MEMBER(uses_irt)
+ IPC_STRUCT_TRAITS_MEMBER(enable_dyncode_syscalls)
+IPC_STRUCT_TRAITS_END()
+
+//-----------------------------------------------------------------------------
+// NaClProcess messages
+// These are messages sent between the browser and the NaCl process.
+// Tells the NaCl process to start.
+IPC_MESSAGE_CONTROL1(NaClProcessMsg_Start,
+ nacl::NaClStartParams /* params */)
+
+#if defined(OS_WIN)
+// Tells the NaCl broker to launch a NaCl loader process.
+IPC_MESSAGE_CONTROL1(NaClProcessMsg_LaunchLoaderThroughBroker,
+ std::string /* channel ID for the loader */)
+
+// Notify the browser process that the loader was launched successfully.
+IPC_MESSAGE_CONTROL2(NaClProcessMsg_LoaderLaunched,
+ std::string, /* channel ID for the loader */
+ base::ProcessHandle /* loader process handle */)
+
+// Tells the NaCl broker to attach a debug exception handler to the
+// given NaCl loader process.
+IPC_MESSAGE_CONTROL3(NaClProcessMsg_LaunchDebugExceptionHandler,
+ int32 /* pid of the NaCl process */,
+ base::ProcessHandle /* handle of the NaCl process */,
+ std::string /* NaCl internal process layout info */)
+
+// Notify the browser process that the broker process finished
+// attaching a debug exception handler to the given NaCl loader
+// process.
+IPC_MESSAGE_CONTROL2(NaClProcessMsg_DebugExceptionHandlerLaunched,
+ int32 /* pid */,
+ bool /* success */)
+
+// Notify the broker that all loader processes have been terminated and it
+// should shutdown.
+IPC_MESSAGE_CONTROL0(NaClProcessMsg_StopBroker)
+
+// Used by the NaCl process to request that a Windows debug exception
+// handler be attached to it.
+IPC_SYNC_MESSAGE_CONTROL1_1(NaClProcessMsg_AttachDebugExceptionHandler,
+ std::string, /* Internal process info */
+ bool /* Result */)
+#endif
+
+// Used by the NaCl process to query a database in the browser. The database
+// contains the signatures of previously validated code chunks.
+IPC_SYNC_MESSAGE_CONTROL1_1(NaClProcessMsg_QueryKnownToValidate,
+ std::string, /* A validation signature */
+ bool /* Can validation be skipped? */)
+
+// Used by the NaCl process to add a validation signature to the validation
+// database in the browser.
+IPC_MESSAGE_CONTROL1(NaClProcessMsg_SetKnownToValidate,
+ std::string /* A validation signature */)
+
+// Used by the NaCl process to acquire trusted information about a file directly
+// from the browser, including the file's path as well as a fresh version of the
+// file handle.
+IPC_SYNC_MESSAGE_CONTROL2_2(NaClProcessMsg_ResolveFileToken,
+ uint64, /* file_token_lo */
+ uint64, /* file_token_hi */
+ IPC::PlatformFileForTransit, /* fd */
+ base::FilePath /* Path opened to get fd */)
+
+// Notify the browser process that the server side of the PPAPI channel was
+// created successfully.
+IPC_MESSAGE_CONTROL1(NaClProcessHostMsg_PpapiChannelCreated,
+ IPC::ChannelHandle /* channel_handle */)
diff --git a/chromium/components/nacl/common/nacl_paths.cc b/chromium/components/nacl/common/nacl_paths.cc
new file mode 100644
index 00000000000..cb046c23eac
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_paths.cc
@@ -0,0 +1,53 @@
+// 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/nacl/common/nacl_paths.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+namespace {
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+// File name of the nacl_helper and nacl_helper_bootstrap, Linux only.
+const base::FilePath::CharType kInternalNaClHelperFileName[] =
+ FILE_PATH_LITERAL("nacl_helper");
+const base::FilePath::CharType kInternalNaClHelperBootstrapFileName[] =
+ FILE_PATH_LITERAL("nacl_helper_bootstrap");
+#endif
+
+} // namespace
+
+namespace nacl {
+
+bool PathProvider(int key, base::FilePath* result) {
+ base::FilePath cur;
+ switch (key) {
+#if defined(OS_LINUX)
+ case FILE_NACL_HELPER:
+ if (!PathService::Get(base::DIR_MODULE, &cur))
+ return false;
+ cur = cur.Append(kInternalNaClHelperFileName);
+ break;
+ case FILE_NACL_HELPER_BOOTSTRAP:
+ if (!PathService::Get(base::DIR_MODULE, &cur))
+ return false;
+ cur = cur.Append(kInternalNaClHelperBootstrapFileName);
+ break;
+#endif
+ default:
+ return false;
+ }
+
+ *result = cur;
+ return true;
+}
+
+// This cannot be done as a static initializer sadly since Visual Studio will
+// eliminate this object file if there is no direct entry point into it.
+void RegisterPathProvider() {
+ PathService::RegisterProvider(PathProvider, PATH_START, PATH_END);
+}
+
+} // namespace nacl
diff --git a/chromium/components/nacl/common/nacl_paths.h b/chromium/components/nacl/common/nacl_paths.h
new file mode 100644
index 00000000000..424d8bdb487
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_paths.h
@@ -0,0 +1,31 @@
+// 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_NACL_COMMON_NACL_PATHS_H_
+#define COMPONENTS_NACL_COMMON_NACL_PATHS_H_
+
+#include "build/build_config.h"
+
+// This file declares path keys for the chrome module. These can be used with
+// the PathService to access various special directories and files.
+
+namespace nacl {
+
+enum {
+ PATH_START = 9000,
+
+#if defined(OS_LINUX)
+ FILE_NACL_HELPER = PATH_START, // Full path to Linux nacl_helper executable.
+ FILE_NACL_HELPER_BOOTSTRAP, // ... and nacl_helper_bootstrap executable.
+#endif
+
+ PATH_END
+};
+
+// Call once to register the provider for the path keys defined above.
+void RegisterPathProvider();
+
+} // namespace nacl
+
+#endif // COMPONENTS_NACL_COMMON_NACL_PATHS_H_
diff --git a/chromium/components/nacl/common/nacl_process_type.h b/chromium/components/nacl/common/nacl_process_type.h
new file mode 100644
index 00000000000..779cc80546a
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_process_type.h
@@ -0,0 +1,18 @@
+// 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_NACL_COMMON_NACL_PROCESS_TYPE_H_
+#define COMPONENTS_NACL_COMMON_NACL_PROCESS_TYPE_H_
+
+#include "content/public/common/process_type.h"
+
+// Defines the process types that are custom to NaCl.
+enum NaClProcessType {
+ // Start at +1 because we removed an unused value and didn't want to change
+ // the IDs as they're used in UMA (see the comment for ProcessType).
+ PROCESS_TYPE_NACL_LOADER = content::PROCESS_TYPE_CONTENT_END + 1,
+ PROCESS_TYPE_NACL_BROKER,
+};
+
+#endif // COMPONENTS_NACL_COMMON_NACL_PROCESS_TYPE_H_
diff --git a/chromium/components/nacl/common/nacl_sandbox_type_mac.h b/chromium/components/nacl/common/nacl_sandbox_type_mac.h
new file mode 100644
index 00000000000..b1817f30d4e
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_sandbox_type_mac.h
@@ -0,0 +1,16 @@
+// 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_NACL_COMMON_NACL_SANDBOX_TYPE_MAC_H_
+#define COMPONENTS_NACL_COMMON_NACL_SANDBOX_TYPE_MAC_H_
+
+#include "content/public/common/sandbox_type_mac.h"
+
+enum NaClSandboxType {
+ NACL_SANDBOX_TYPE_FIRST_TYPE = content::SANDBOX_TYPE_AFTER_LAST_TYPE,
+
+ NACL_SANDBOX_TYPE_NACL_LOADER = NACL_SANDBOX_TYPE_FIRST_TYPE,
+};
+
+#endif // COMPONENTS_NACL_COMMON_NACL_SANDBOX_TYPE_MAC_H_
diff --git a/chromium/components/nacl/common/nacl_switches.cc b/chromium/components/nacl/common/nacl_switches.cc
new file mode 100644
index 00000000000..0dfdc949857
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_switches.cc
@@ -0,0 +1,39 @@
+// 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/nacl/common/nacl_switches.h"
+
+namespace switches {
+
+// Enables debugging via RSP over a socket.
+const char kEnableNaClDebug[] = "enable-nacl-debug";
+
+// Causes the process to run as a NativeClient broker
+// (used for launching NaCl loader processes on 64-bit Windows).
+const char kNaClBrokerProcess[] = "nacl-broker";
+
+// Uses NaCl manifest URL to choose whether NaCl program will be debugged by
+// debug stub.
+// Switch value format: [!]pattern1,pattern2,...,patternN. Each pattern uses
+// the same syntax as patterns in Chrome extension manifest. The only difference
+// is that * scheme matches all schemes instead of matching only http and https.
+// If the value doesn't start with !, a program will be debugged if manifest URL
+// matches any pattern. If the value starts with !, a program will be debugged
+// if manifest URL does not match any pattern.
+const char kNaClDebugMask[] = "nacl-debug-mask";
+
+// Native Client GDB debugger that will be launched automatically when needed.
+const char kNaClGdb[] = "nacl-gdb";
+
+// GDB script to pass to the nacl-gdb debugger at startup.
+const char kNaClGdbScript[] = "nacl-gdb-script";
+
+// On POSIX only: the contents of this flag are prepended to the nacl-loader
+// command line. Useful values might be "valgrind" or "xterm -e gdb --args".
+const char kNaClLoaderCmdPrefix[] = "nacl-loader-cmd-prefix";
+
+// Causes the process to run as a NativeClient loader.
+const char kNaClLoaderProcess[] = "nacl-loader";
+
+} // namespace switches
diff --git a/chromium/components/nacl/common/nacl_switches.h b/chromium/components/nacl/common/nacl_switches.h
new file mode 100644
index 00000000000..9bc1bcb4697
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_switches.h
@@ -0,0 +1,24 @@
+// 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.
+
+// Defines all the command-line switches used by Chrome.
+
+#ifndef COMPONENTS_NACL_COMMON_NACL_SWITCHES_H_
+#define COMPONENTS_NACL_COMMON_NACL_SWITCHES_H_
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kEnableNaClDebug[];
+extern const char kNaClBrokerProcess[];
+extern const char kNaClDebugMask[];
+extern const char kNaClGdb[];
+extern const char kNaClGdbScript[];
+extern const char kNaClLoaderCmdPrefix[];
+extern const char kNaClLoaderProcess[];
+
+} // namespace switches
+
+#endif // COMPONENTS_NACL_COMMON_NACL_SWITCHES_H_
diff --git a/chromium/components/nacl/common/nacl_types.cc b/chromium/components/nacl/common/nacl_types.cc
new file mode 100644
index 00000000000..dea02391125
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_types.cc
@@ -0,0 +1,77 @@
+// 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/nacl/common/nacl_types.h"
+#include "ipc/ipc_platform_file.h"
+
+namespace nacl {
+
+NaClStartParams::NaClStartParams()
+ : validation_cache_enabled(false),
+ enable_exception_handling(false),
+ enable_debug_stub(false),
+ enable_ipc_proxy(false),
+ uses_irt(false),
+ enable_dyncode_syscalls(false) {
+}
+
+NaClStartParams::~NaClStartParams() {
+}
+
+NaClLaunchParams::NaClLaunchParams()
+ : render_view_id(0),
+ permission_bits(0),
+ uses_irt(false),
+ enable_dyncode_syscalls(false),
+ enable_exception_handling(false) {
+}
+
+NaClLaunchParams::NaClLaunchParams(const std::string& manifest_url,
+ int render_view_id,
+ uint32 permission_bits,
+ bool uses_irt,
+ bool enable_dyncode_syscalls,
+ bool enable_exception_handling)
+ : manifest_url(manifest_url),
+ render_view_id(render_view_id),
+ permission_bits(permission_bits),
+ uses_irt(uses_irt),
+ enable_dyncode_syscalls(enable_dyncode_syscalls),
+ enable_exception_handling(enable_exception_handling) {
+}
+
+NaClLaunchParams::NaClLaunchParams(const NaClLaunchParams& l) {
+ manifest_url = l.manifest_url;
+ render_view_id = l.render_view_id;
+ permission_bits = l.permission_bits;
+ uses_irt = l.uses_irt;
+ enable_dyncode_syscalls = l.enable_dyncode_syscalls;
+ enable_exception_handling = l.enable_exception_handling;
+}
+
+NaClLaunchParams::~NaClLaunchParams() {
+}
+
+NaClLaunchResult::NaClLaunchResult()
+ : imc_channel_handle(IPC::InvalidPlatformFileForTransit()),
+ ipc_channel_handle(),
+ plugin_pid(base::kNullProcessId),
+ plugin_child_id(0) {
+}
+
+NaClLaunchResult::NaClLaunchResult(
+ FileDescriptor imc_channel_handle,
+ const IPC::ChannelHandle& ipc_channel_handle,
+ base::ProcessId plugin_pid,
+ int plugin_child_id)
+ : imc_channel_handle(imc_channel_handle),
+ ipc_channel_handle(ipc_channel_handle),
+ plugin_pid(plugin_pid),
+ plugin_child_id(plugin_child_id) {
+}
+
+NaClLaunchResult::~NaClLaunchResult() {
+}
+
+} // namespace nacl
diff --git a/chromium/components/nacl/common/nacl_types.h b/chromium/components/nacl/common/nacl_types.h
new file mode 100644
index 00000000000..b3fee25c869
--- /dev/null
+++ b/chromium/components/nacl/common/nacl_types.h
@@ -0,0 +1,101 @@
+// 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_NACL_COMMON_NACL_TYPES_H_
+#define COMPONENTS_NACL_COMMON_NACL_TYPES_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+#include "ipc/ipc_channel.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+#if defined(OS_WIN)
+#include <windows.h> // for HANDLE
+#endif
+
+// TODO(gregoryd): add a Windows definition for base::FileDescriptor
+namespace nacl {
+
+#if defined(OS_WIN)
+typedef HANDLE FileDescriptor;
+inline HANDLE ToNativeHandle(const FileDescriptor& desc) {
+ return desc;
+}
+#elif defined(OS_POSIX)
+typedef base::FileDescriptor FileDescriptor;
+inline int ToNativeHandle(const FileDescriptor& desc) {
+ return desc.fd;
+}
+#endif
+
+
+// Parameters sent to the NaCl process when we start it.
+//
+// If you change this, you will also need to update the IPC serialization in
+// nacl_messages.h.
+struct NaClStartParams {
+ NaClStartParams();
+ ~NaClStartParams();
+
+ std::vector<FileDescriptor> handles;
+ FileDescriptor debug_stub_server_bound_socket;
+
+ bool validation_cache_enabled;
+ std::string validation_cache_key;
+ // Chrome version string. Sending the version string over IPC avoids linkage
+ // issues in cases where NaCl is not compiled into the main Chromium
+ // executable or DLL.
+ std::string version;
+
+ bool enable_exception_handling;
+ bool enable_debug_stub;
+ bool enable_ipc_proxy;
+ bool uses_irt;
+ bool enable_dyncode_syscalls;
+};
+
+// Parameters sent to the browser process to have it launch a NaCl process.
+//
+// If you change this, you will also need to update the IPC serialization in
+// nacl_host_messages.h.
+struct NaClLaunchParams {
+ NaClLaunchParams();
+ NaClLaunchParams(const std::string& u, int r, uint32 p, bool uses_irt,
+ bool enable_dyncode_syscalls,
+ bool enable_exception_handling);
+ NaClLaunchParams(const NaClLaunchParams& l);
+ ~NaClLaunchParams();
+
+ std::string manifest_url;
+ int render_view_id;
+ uint32 permission_bits;
+ bool uses_irt;
+ bool enable_dyncode_syscalls;
+ bool enable_exception_handling;
+};
+
+struct NaClLaunchResult {
+ NaClLaunchResult();
+ NaClLaunchResult(FileDescriptor imc_channel_handle,
+ const IPC::ChannelHandle& ipc_channel_handle,
+ base::ProcessId plugin_pid,
+ int plugin_child_id);
+ ~NaClLaunchResult();
+
+ FileDescriptor imc_channel_handle;
+ IPC::ChannelHandle ipc_channel_handle;
+ base::ProcessId plugin_pid;
+ int plugin_child_id;
+};
+
+} // namespace nacl
+
+#endif // COMPONENTS_NACL_COMMON_NACL_TYPES_H_
diff --git a/chromium/components/nacl/common/pnacl_types.cc b/chromium/components/nacl/common/pnacl_types.cc
new file mode 100644
index 00000000000..6c43319c6b2
--- /dev/null
+++ b/chromium/components/nacl/common/pnacl_types.cc
@@ -0,0 +1,28 @@
+// 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/nacl/common/pnacl_types.h"
+
+namespace nacl {
+
+PnaclCacheInfo::PnaclCacheInfo() {}
+PnaclCacheInfo::~PnaclCacheInfo() {}
+
+// static
+bool PnaclInstallProgress::progress_known(const PnaclInstallProgress& p) {
+ return p.total_size >= 0;
+}
+
+// static
+PnaclInstallProgress PnaclInstallProgress::Unknown() {
+ PnaclInstallProgress p;
+ p.current = 0;
+ // Use -1 to indicate that total is not determined.
+ // This matches the -1 of the OnURLFetchDownloadProgress interface in
+ // net/url_request/url_fetcher_delegate.h
+ p.total_size = -1;
+ return p;
+}
+
+} // namespace nacl
diff --git a/chromium/components/nacl/common/pnacl_types.h b/chromium/components/nacl/common/pnacl_types.h
new file mode 100644
index 00000000000..3fc405a980d
--- /dev/null
+++ b/chromium/components/nacl/common/pnacl_types.h
@@ -0,0 +1,48 @@
+// 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_NACL_COMMON_PNACL_TYPES_H_
+#define COMPONENTS_NACL_COMMON_PNACL_TYPES_H_
+
+// This file exists (instead of putting this type into nacl_types.h) because
+// nacl_types is built into nacl_helper in addition to chrome, and we don't
+// want to pull src/url/ into there, since it would be unnecessary bloat.
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace nacl {
+// Cache-related information about pexe files, sent from the plugin/renderer
+// to the browser.
+//
+// If you change this, you will also need to update the IPC serialization in
+// nacl_host_messages.h.
+struct PnaclCacheInfo {
+ PnaclCacheInfo();
+ ~PnaclCacheInfo();
+ GURL pexe_url;
+ int abi_version;
+ int opt_level;
+ base::Time last_modified;
+ std::string etag;
+};
+
+// Progress information for PNaCl on-demand installs.
+struct PnaclInstallProgress {
+ int64 current;
+ int64 total_size;
+
+ // Returns an instance of PnaclInstallProgress where the
+ // total is marked as unknown.
+ static PnaclInstallProgress Unknown();
+
+ // Returns true if the given instance of PnaclInstallProgress has
+ // an unknown total.
+ static bool progress_known(const PnaclInstallProgress& p);
+};
+
+} // namespace nacl
+
+#endif // COMPONENTS_NACL_COMMON_PNACL_TYPES_H_
diff --git a/chromium/components/nacl/loader/DEPS b/chromium/components/nacl/loader/DEPS
new file mode 100644
index 00000000000..aa045563f80
--- /dev/null
+++ b/chromium/components/nacl/loader/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+ "+components/nacl",
+ "+content/public/app/startup_helper_win.h",
+ "+crypto",
+ "+sandbox/linux/seccomp-bpf",
+ "+sandbox/linux/services",
+ "+sandbox/win/src",
+ "+native_client/src",
+ "+ppapi/c", # header files only
+
+ # For handle conversion in nacl_ipc_adapter.cc:
+ "+ppapi/proxy/handle_converter.h",
+ "+ppapi/proxy/serialized_handle.h",
+
+ # For sending PpapiHostMsg_ChannelCreated in nacl_ipc_adapter.cc:
+ "+ppapi/proxy/ppapi_messages.h"
+]
diff --git a/chromium/components/nacl/loader/OWNERS b/chromium/components/nacl/loader/OWNERS
new file mode 100644
index 00000000000..9693b6b0b26
--- /dev/null
+++ b/chromium/components/nacl/loader/OWNERS
@@ -0,0 +1,2 @@
+per-file nacl_sandbox_linux.*=jln@chromium.org
+per-file nacl_sandbox_linux.*=mseaborn@chromium.org
diff --git a/chromium/components/nacl/loader/nacl_ipc_adapter.cc b/chromium/components/nacl/loader/nacl_ipc_adapter.cc
new file mode 100644
index 00000000000..3399bbbf356
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_ipc_adapter.cc
@@ -0,0 +1,592 @@
+// 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/nacl/loader/nacl_ipc_adapter.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/shared_memory.h"
+#include "build/build_config.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_platform_file.h"
+#include "native_client/src/trusted/desc/nacl_desc_base.h"
+#include "native_client/src/trusted/desc/nacl_desc_custom.h"
+#include "native_client/src/trusted/desc/nacl_desc_imc_shm.h"
+#include "native_client/src/trusted/desc/nacl_desc_io.h"
+#include "native_client/src/trusted/desc/nacl_desc_sync_socket.h"
+#include "native_client/src/trusted/desc/nacl_desc_wrapper.h"
+#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
+#include "ppapi/c/ppb_file_io.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/serialized_handle.h"
+
+namespace {
+
+enum BufferSizeStatus {
+ // The buffer contains a full message with no extra bytes.
+ MESSAGE_IS_COMPLETE,
+
+ // The message doesn't fit and the buffer contains only some of it.
+ MESSAGE_IS_TRUNCATED,
+
+ // The buffer contains a full message + extra data.
+ MESSAGE_HAS_EXTRA_DATA
+};
+
+BufferSizeStatus GetBufferStatus(const char* data, size_t len) {
+ if (len < sizeof(NaClIPCAdapter::NaClMessageHeader))
+ return MESSAGE_IS_TRUNCATED;
+
+ const NaClIPCAdapter::NaClMessageHeader* header =
+ reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(data);
+ uint32 message_size =
+ sizeof(NaClIPCAdapter::NaClMessageHeader) + header->payload_size;
+
+ if (len == message_size)
+ return MESSAGE_IS_COMPLETE;
+ if (len > message_size)
+ return MESSAGE_HAS_EXTRA_DATA;
+ return MESSAGE_IS_TRUNCATED;
+}
+
+// This object allows the NaClDesc to hold a reference to a NaClIPCAdapter and
+// forward calls to it.
+struct DescThunker {
+ explicit DescThunker(NaClIPCAdapter* adapter_param)
+ : adapter(adapter_param) {
+ }
+ scoped_refptr<NaClIPCAdapter> adapter;
+};
+
+NaClIPCAdapter* ToAdapter(void* handle) {
+ return static_cast<DescThunker*>(handle)->adapter.get();
+}
+
+// NaClDescCustom implementation.
+void NaClDescCustomDestroy(void* handle) {
+ delete static_cast<DescThunker*>(handle);
+}
+
+ssize_t NaClDescCustomSendMsg(void* handle, const NaClImcTypedMsgHdr* msg,
+ int /* flags */) {
+ return static_cast<ssize_t>(ToAdapter(handle)->Send(msg));
+}
+
+ssize_t NaClDescCustomRecvMsg(void* handle, NaClImcTypedMsgHdr* msg,
+ int /* flags */) {
+ return static_cast<ssize_t>(ToAdapter(handle)->BlockingReceive(msg));
+}
+
+NaClDesc* MakeNaClDescCustom(NaClIPCAdapter* adapter) {
+ NaClDescCustomFuncs funcs = NACL_DESC_CUSTOM_FUNCS_INITIALIZER;
+ funcs.Destroy = NaClDescCustomDestroy;
+ funcs.SendMsg = NaClDescCustomSendMsg;
+ funcs.RecvMsg = NaClDescCustomRecvMsg;
+ // NaClDescMakeCustomDesc gives us a reference on the returned NaClDesc.
+ return NaClDescMakeCustomDesc(new DescThunker(adapter), &funcs);
+}
+
+void DeleteChannel(IPC::Channel* channel) {
+ delete channel;
+}
+
+// Translates Pepper's read/write open flags into the NaCl equivalents.
+// Since the host has already opened the file, flags such as O_CREAT, O_TRUNC,
+// and O_EXCL don't make sense, so we filter those out. If no read or write
+// flags are set, the function returns NACL_ABI_O_RDONLY as a safe fallback.
+int TranslatePepperFileReadWriteOpenFlags(int32_t pp_open_flags) {
+ bool read = (pp_open_flags & PP_FILEOPENFLAG_READ) != 0;
+ bool write = (pp_open_flags & PP_FILEOPENFLAG_WRITE) != 0;
+ bool append = (pp_open_flags & PP_FILEOPENFLAG_APPEND) != 0;
+
+ int nacl_open_flag = NACL_ABI_O_RDONLY; // NACL_ABI_O_RDONLY == 0.
+ if (read && (write || append)) {
+ nacl_open_flag = NACL_ABI_O_RDWR;
+ } else if (write || append) {
+ nacl_open_flag = NACL_ABI_O_WRONLY;
+ } else if (!read) {
+ DLOG(WARNING) << "One of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, "
+ << "or PP_FILEOPENFLAG_APPEND should be set.";
+ }
+ if (append)
+ nacl_open_flag |= NACL_ABI_O_APPEND;
+
+ return nacl_open_flag;
+}
+
+class NaClDescWrapper {
+ public:
+ explicit NaClDescWrapper(NaClDesc* desc): desc_(desc) {}
+ ~NaClDescWrapper() {
+ NaClDescUnref(desc_);
+ }
+
+ NaClDesc* desc() { return desc_; }
+
+ private:
+ NaClDesc* desc_;
+ DISALLOW_COPY_AND_ASSIGN(NaClDescWrapper);
+};
+
+} // namespace
+
+class NaClIPCAdapter::RewrittenMessage
+ : public base::RefCounted<RewrittenMessage> {
+ public:
+ RewrittenMessage();
+
+ bool is_consumed() const { return data_read_cursor_ == data_len_; }
+
+ void SetData(const NaClIPCAdapter::NaClMessageHeader& header,
+ const void* payload, size_t payload_length);
+
+ int Read(NaClImcTypedMsgHdr* msg);
+
+ void AddDescriptor(NaClDescWrapper* desc) { descs_.push_back(desc); }
+
+ size_t desc_count() const { return descs_.size(); }
+
+ private:
+ friend class base::RefCounted<RewrittenMessage>;
+ ~RewrittenMessage() {}
+
+ scoped_ptr<char[]> data_;
+ size_t data_len_;
+
+ // Offset into data where the next read will happen. This will be equal to
+ // data_len_ when all data has been consumed.
+ size_t data_read_cursor_;
+
+ // Wrapped descriptors for transfer to untrusted code.
+ ScopedVector<NaClDescWrapper> descs_;
+};
+
+NaClIPCAdapter::RewrittenMessage::RewrittenMessage()
+ : data_len_(0),
+ data_read_cursor_(0) {
+}
+
+void NaClIPCAdapter::RewrittenMessage::SetData(
+ const NaClIPCAdapter::NaClMessageHeader& header,
+ const void* payload,
+ size_t payload_length) {
+ DCHECK(!data_.get() && data_len_ == 0);
+ size_t header_len = sizeof(NaClIPCAdapter::NaClMessageHeader);
+ data_len_ = header_len + payload_length;
+ data_.reset(new char[data_len_]);
+
+ memcpy(data_.get(), &header, sizeof(NaClIPCAdapter::NaClMessageHeader));
+ memcpy(&data_[header_len], payload, payload_length);
+}
+
+int NaClIPCAdapter::RewrittenMessage::Read(NaClImcTypedMsgHdr* msg) {
+ CHECK(data_len_ >= data_read_cursor_);
+ char* dest_buffer = static_cast<char*>(msg->iov[0].base);
+ size_t dest_buffer_size = msg->iov[0].length;
+ size_t bytes_to_write = std::min(dest_buffer_size,
+ data_len_ - data_read_cursor_);
+ if (bytes_to_write == 0)
+ return 0;
+
+ memcpy(dest_buffer, &data_[data_read_cursor_], bytes_to_write);
+ data_read_cursor_ += bytes_to_write;
+
+ // Once all data has been consumed, transfer any file descriptors.
+ if (is_consumed()) {
+ nacl_abi_size_t desc_count = static_cast<nacl_abi_size_t>(descs_.size());
+ CHECK(desc_count <= msg->ndesc_length);
+ msg->ndesc_length = desc_count;
+ for (nacl_abi_size_t i = 0; i < desc_count; i++) {
+ // Copy the NaClDesc to the buffer and add a ref so it won't be freed
+ // when we clear our ScopedVector.
+ msg->ndescv[i] = descs_[i]->desc();
+ NaClDescRef(descs_[i]->desc());
+ }
+ descs_.clear();
+ } else {
+ msg->ndesc_length = 0;
+ }
+ return static_cast<int>(bytes_to_write);
+}
+
+NaClIPCAdapter::LockedData::LockedData()
+ : channel_closed_(false) {
+}
+
+NaClIPCAdapter::LockedData::~LockedData() {
+}
+
+NaClIPCAdapter::IOThreadData::IOThreadData() {
+}
+
+NaClIPCAdapter::IOThreadData::~IOThreadData() {
+}
+
+NaClIPCAdapter::NaClIPCAdapter(const IPC::ChannelHandle& handle,
+ base::TaskRunner* runner)
+ : lock_(),
+ cond_var_(&lock_),
+ task_runner_(runner),
+ locked_data_() {
+ io_thread_data_.channel_.reset(
+ new IPC::Channel(handle, IPC::Channel::MODE_SERVER, this));
+ // Note, we can not PostTask for ConnectChannelOnIOThread here. If we did,
+ // and that task ran before this constructor completes, the reference count
+ // would go to 1 and then to 0 because of the Task, before we've been returned
+ // to the owning scoped_refptr, which is supposed to give us our first
+ // ref-count.
+}
+
+NaClIPCAdapter::NaClIPCAdapter(scoped_ptr<IPC::Channel> channel,
+ base::TaskRunner* runner)
+ : lock_(),
+ cond_var_(&lock_),
+ task_runner_(runner),
+ locked_data_() {
+ io_thread_data_.channel_ = channel.Pass();
+}
+
+void NaClIPCAdapter::ConnectChannel() {
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&NaClIPCAdapter::ConnectChannelOnIOThread, this));
+}
+
+// Note that this message is controlled by the untrusted code. So we should be
+// skeptical of anything it contains and quick to give up if anything is fishy.
+int NaClIPCAdapter::Send(const NaClImcTypedMsgHdr* msg) {
+ if (msg->iov_length != 1)
+ return -1;
+
+ base::AutoLock lock(lock_);
+
+ const char* input_data = static_cast<char*>(msg->iov[0].base);
+ size_t input_data_len = msg->iov[0].length;
+ if (input_data_len > IPC::Channel::kMaximumMessageSize) {
+ ClearToBeSent();
+ return -1;
+ }
+
+ // current_message[_len] refers to the total input data received so far.
+ const char* current_message;
+ size_t current_message_len;
+ bool did_append_input_data;
+ if (locked_data_.to_be_sent_.empty()) {
+ // No accumulated data, we can avoid a copy by referring to the input
+ // buffer (the entire message fitting in one call is the common case).
+ current_message = input_data;
+ current_message_len = input_data_len;
+ did_append_input_data = false;
+ } else {
+ // We've already accumulated some data, accumulate this new data and
+ // point to the beginning of the buffer.
+
+ // Make sure our accumulated message size doesn't overflow our max. Since
+ // we know that data_len < max size (checked above) and our current
+ // accumulated value is also < max size, we just need to make sure that
+ // 2x max size can never overflow.
+ COMPILE_ASSERT(IPC::Channel::kMaximumMessageSize < (UINT_MAX / 2),
+ MaximumMessageSizeWillOverflow);
+ size_t new_size = locked_data_.to_be_sent_.size() + input_data_len;
+ if (new_size > IPC::Channel::kMaximumMessageSize) {
+ ClearToBeSent();
+ return -1;
+ }
+
+ locked_data_.to_be_sent_.append(input_data, input_data_len);
+ current_message = &locked_data_.to_be_sent_[0];
+ current_message_len = locked_data_.to_be_sent_.size();
+ did_append_input_data = true;
+ }
+
+ // Check the total data we've accumulated so far to see if it contains a full
+ // message.
+ switch (GetBufferStatus(current_message, current_message_len)) {
+ case MESSAGE_IS_COMPLETE: {
+ // Got a complete message, can send it out. This will be the common case.
+ bool success = SendCompleteMessage(current_message, current_message_len);
+ ClearToBeSent();
+ return success ? static_cast<int>(input_data_len) : -1;
+ }
+ case MESSAGE_IS_TRUNCATED:
+ // For truncated messages, just accumulate the new data (if we didn't
+ // already do so above) and go back to waiting for more.
+ if (!did_append_input_data)
+ locked_data_.to_be_sent_.append(input_data, input_data_len);
+ return static_cast<int>(input_data_len);
+ case MESSAGE_HAS_EXTRA_DATA:
+ default:
+ // When the plugin gives us too much data, it's an error.
+ ClearToBeSent();
+ return -1;
+ }
+}
+
+int NaClIPCAdapter::BlockingReceive(NaClImcTypedMsgHdr* msg) {
+ if (msg->iov_length != 1)
+ return -1;
+
+ int retval = 0;
+ {
+ base::AutoLock lock(lock_);
+ while (locked_data_.to_be_received_.empty() &&
+ !locked_data_.channel_closed_)
+ cond_var_.Wait();
+ if (locked_data_.channel_closed_) {
+ retval = -1;
+ } else {
+ retval = LockedReceive(msg);
+ DCHECK(retval > 0);
+ }
+ }
+ cond_var_.Signal();
+ return retval;
+}
+
+void NaClIPCAdapter::CloseChannel() {
+ {
+ base::AutoLock lock(lock_);
+ locked_data_.channel_closed_ = true;
+ }
+ cond_var_.Signal();
+
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&NaClIPCAdapter::CloseChannelOnIOThread, this));
+}
+
+NaClDesc* NaClIPCAdapter::MakeNaClDesc() {
+ return MakeNaClDescCustom(this);
+}
+
+#if defined(OS_POSIX)
+int NaClIPCAdapter::TakeClientFileDescriptor() {
+ return io_thread_data_.channel_->TakeClientFileDescriptor();
+}
+#endif
+
+bool NaClIPCAdapter::OnMessageReceived(const IPC::Message& msg) {
+ {
+ base::AutoLock lock(lock_);
+
+ scoped_refptr<RewrittenMessage> rewritten_msg(new RewrittenMessage);
+
+ typedef std::vector<ppapi::proxy::SerializedHandle> Handles;
+ Handles handles;
+ scoped_ptr<IPC::Message> new_msg_ptr;
+ bool success = locked_data_.handle_converter_.ConvertNativeHandlesToPosix(
+ msg, &handles, &new_msg_ptr);
+ if (!success)
+ return false;
+
+ // Now add any descriptors we found to rewritten_msg. |handles| is usually
+ // empty, unless we read a message containing a FD or handle.
+ for (Handles::const_iterator iter = handles.begin();
+ iter != handles.end();
+ ++iter) {
+ scoped_ptr<NaClDescWrapper> nacl_desc;
+ switch (iter->type()) {
+ case ppapi::proxy::SerializedHandle::SHARED_MEMORY: {
+ const base::SharedMemoryHandle& shm_handle = iter->shmem();
+ uint32_t size = iter->size();
+ nacl_desc.reset(new NaClDescWrapper(NaClDescImcShmMake(
+#if defined(OS_WIN)
+ shm_handle,
+#else
+ shm_handle.fd,
+#endif
+ static_cast<size_t>(size))));
+ break;
+ }
+ case ppapi::proxy::SerializedHandle::SOCKET: {
+ nacl_desc.reset(new NaClDescWrapper(NaClDescSyncSocketMake(
+#if defined(OS_WIN)
+ iter->descriptor()
+#else
+ iter->descriptor().fd
+#endif
+ )));
+ break;
+ }
+ case ppapi::proxy::SerializedHandle::CHANNEL_HANDLE: {
+ // Check that this came from a PpapiMsg_CreateNaClChannel message.
+ // This code here is only appropriate for that message.
+ DCHECK(msg.type() == PpapiMsg_CreateNaClChannel::ID);
+ IPC::ChannelHandle channel_handle =
+ IPC::Channel::GenerateVerifiedChannelID("nacl");
+ scoped_refptr<NaClIPCAdapter> ipc_adapter(
+ new NaClIPCAdapter(channel_handle, task_runner_.get()));
+ ipc_adapter->ConnectChannel();
+#if defined(OS_POSIX)
+ channel_handle.socket = base::FileDescriptor(
+ ipc_adapter->TakeClientFileDescriptor(), true);
+#endif
+ nacl_desc.reset(new NaClDescWrapper(ipc_adapter->MakeNaClDesc()));
+ // Send back a message that the channel was created.
+ scoped_ptr<IPC::Message> response(
+ new PpapiHostMsg_ChannelCreated(channel_handle));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
+ base::Passed(&response)));
+ break;
+ }
+ case ppapi::proxy::SerializedHandle::FILE:
+ // IMPORTANT: The NaClDescIoDescFromHandleAllocCtor function creates
+ // a NaClDesc that checks file flags before reading and writing. This
+ // is essential since PPB_FileIO now sends a file descriptor to the
+ // plugin which may have write capabilities. We can't allow the plugin
+ // to write with it since it could bypass quota checks, which still
+ // happen in the host.
+ nacl_desc.reset(new NaClDescWrapper(NaClDescIoDescFromHandleAllocCtor(
+#if defined(OS_WIN)
+ iter->descriptor(),
+#else
+ iter->descriptor().fd,
+#endif
+ TranslatePepperFileReadWriteOpenFlags(iter->open_flag()))));
+ break;
+ case ppapi::proxy::SerializedHandle::INVALID: {
+ // Nothing to do. TODO(dmichael): Should we log this? Or is it
+ // sometimes okay to pass an INVALID handle?
+ break;
+ }
+ // No default, so the compiler will warn us if new types get added.
+ }
+ if (nacl_desc.get())
+ rewritten_msg->AddDescriptor(nacl_desc.release());
+ }
+ if (new_msg_ptr && !handles.empty())
+ SaveMessage(*new_msg_ptr, rewritten_msg.get());
+ else
+ SaveMessage(msg, rewritten_msg.get());
+ }
+ cond_var_.Signal();
+ return true;
+}
+
+void NaClIPCAdapter::OnChannelConnected(int32 peer_pid) {
+}
+
+void NaClIPCAdapter::OnChannelError() {
+ CloseChannel();
+}
+
+NaClIPCAdapter::~NaClIPCAdapter() {
+ // Make sure the channel is deleted on the IO thread.
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&DeleteChannel, io_thread_data_.channel_.release()));
+}
+
+int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) {
+ lock_.AssertAcquired();
+
+ if (locked_data_.to_be_received_.empty())
+ return 0;
+ scoped_refptr<RewrittenMessage> current =
+ locked_data_.to_be_received_.front();
+
+ int retval = current->Read(msg);
+
+ // When a message is entirely consumed, remove if from the waiting queue.
+ if (current->is_consumed())
+ locked_data_.to_be_received_.pop();
+
+ return retval;
+}
+
+bool NaClIPCAdapter::SendCompleteMessage(const char* buffer,
+ size_t buffer_len) {
+ lock_.AssertAcquired();
+ // The message will have already been validated, so we know it's large enough
+ // for our header.
+ const NaClMessageHeader* header =
+ reinterpret_cast<const NaClMessageHeader*>(buffer);
+
+ // Length of the message not including the body. The data passed to us by the
+ // plugin should match that in the message header. This should have already
+ // been validated by GetBufferStatus.
+ int body_len = static_cast<int>(buffer_len - sizeof(NaClMessageHeader));
+ DCHECK(body_len == static_cast<int>(header->payload_size));
+
+ // We actually discard the flags and only copy the ones we care about. This
+ // is just because message doesn't have a constructor that takes raw flags.
+ scoped_ptr<IPC::Message> msg(
+ new IPC::Message(header->routing, header->type,
+ IPC::Message::PRIORITY_NORMAL));
+ if (header->flags & IPC::Message::SYNC_BIT)
+ msg->set_sync();
+ if (header->flags & IPC::Message::REPLY_BIT)
+ msg->set_reply();
+ if (header->flags & IPC::Message::REPLY_ERROR_BIT)
+ msg->set_reply_error();
+ if (header->flags & IPC::Message::UNBLOCK_BIT)
+ msg->set_unblock(true);
+
+ msg->WriteBytes(&buffer[sizeof(NaClMessageHeader)], body_len);
+
+ // Technically we didn't have to do any of the previous work in the lock. But
+ // sometimes our buffer will point to the to_be_sent_ string which is
+ // protected by the lock, and it's messier to factor Send() such that it can
+ // unlock for us. Holding the lock for the message construction, which is
+ // just some memcpys, shouldn't be a big deal.
+ lock_.AssertAcquired();
+ if (locked_data_.channel_closed_)
+ return false; // TODO(brettw) clean up handles here when we add support!
+
+ if (msg->is_sync()) {
+ locked_data_.handle_converter_.RegisterSyncMessageForReply(*msg);
+ }
+ // Actual send must be done on the I/O thread.
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&NaClIPCAdapter::SendMessageOnIOThread, this,
+ base::Passed(&msg)));
+ return true;
+}
+
+void NaClIPCAdapter::ClearToBeSent() {
+ lock_.AssertAcquired();
+
+ // Don't let the string keep its buffer behind our back.
+ std::string empty;
+ locked_data_.to_be_sent_.swap(empty);
+}
+
+void NaClIPCAdapter::ConnectChannelOnIOThread() {
+ if (!io_thread_data_.channel_->Connect())
+ NOTREACHED();
+}
+
+void NaClIPCAdapter::CloseChannelOnIOThread() {
+ io_thread_data_.channel_->Close();
+}
+
+void NaClIPCAdapter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
+ io_thread_data_.channel_->Send(message.release());
+}
+
+void NaClIPCAdapter::SaveMessage(const IPC::Message& msg,
+ RewrittenMessage* rewritten_msg) {
+ lock_.AssertAcquired();
+ // There is some padding in this structure (the "padding" member is 16
+ // bits but this then gets padded to 32 bits). We want to be sure not to
+ // leak data to the untrusted plugin, so zero everything out first.
+ NaClMessageHeader header;
+ memset(&header, 0, sizeof(NaClMessageHeader));
+
+ header.payload_size = static_cast<uint32>(msg.payload_size());
+ header.routing = msg.routing_id();
+ header.type = msg.type();
+ header.flags = msg.flags();
+ header.num_fds = static_cast<int>(rewritten_msg->desc_count());
+
+ rewritten_msg->SetData(header, msg.payload(), msg.payload_size());
+ locked_data_.to_be_received_.push(rewritten_msg);
+}
+
+int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags) {
+ return TranslatePepperFileReadWriteOpenFlags(pp_open_flags);
+}
diff --git a/chromium/components/nacl/loader/nacl_ipc_adapter.h b/chromium/components/nacl/loader/nacl_ipc_adapter.h
new file mode 100644
index 00000000000..60ea855a083
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_ipc_adapter.h
@@ -0,0 +1,192 @@
+// 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 CHROME_NACL_NACL_IPC_ADAPTER_H_
+#define CHROME_NACL_NACL_IPC_ADAPTER_H_
+
+#include <map>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/shared_memory.h"
+#include "base/pickle.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "ipc/ipc_listener.h"
+#include "ppapi/c/pp_stdint.h"
+#include "ppapi/proxy/handle_converter.h"
+
+struct NaClDesc;
+struct NaClImcTypedMsgHdr;
+struct PP_Size;
+
+namespace IPC {
+class Channel;
+struct ChannelHandle;
+}
+
+namespace ppapi {
+class HostResource;
+}
+
+// Adapts a Chrome IPC channel to an IPC channel that we expose to Native
+// Client. This provides a mapping in both directions, so when IPC messages
+// come in from another process, we rewrite them and allow them to be received
+// via a recvmsg-like interface in the NaCl code. When NaCl code calls sendmsg,
+// we implement that as sending IPC messages on the channel.
+//
+// This object also provides the necessary logic for rewriting IPC messages.
+// NaCl code is platform-independent and runs in a Posix-like enviroment, but
+// some formatting in the message and the way handles are transferred varies
+// by platform. This class bridges that gap to provide what looks like a
+// normal platform-specific IPC implementation to Chrome, and a Posix-like
+// version on every platform to NaCl.
+//
+// This object must be threadsafe since the nacl environment determines which
+// thread every function is called on.
+class NaClIPCAdapter : public base::RefCountedThreadSafe<NaClIPCAdapter>,
+ public IPC::Listener {
+ public:
+ // Chrome's IPC message format varies by platform, NaCl's does not. In
+ // particular, the header has some extra fields on Posix platforms. Since
+ // NaCl is a Posix environment, it gets that version of the header. This
+ // header is duplicated here so we have a cross-platform definition of the
+ // header we're exposing to NaCl.
+#pragma pack(push, 4)
+ struct NaClMessageHeader : public Pickle::Header {
+ int32 routing;
+ uint32 type;
+ uint32 flags;
+ uint16 num_fds;
+ uint16 pad;
+ };
+#pragma pack(pop)
+
+ // Creates an adapter, using the thread associated with the given task
+ // runner for posting messages. In normal use, the task runner will post to
+ // the I/O thread of the process.
+ //
+ // If you use this constructor, you MUST call ConnectChannel after the
+ // NaClIPCAdapter is constructed, or the NaClIPCAdapter's channel will not be
+ // connected.
+ NaClIPCAdapter(const IPC::ChannelHandle& handle, base::TaskRunner* runner);
+
+ // Initializes with a given channel that's already created for testing
+ // purposes. This function will take ownership of the given channel.
+ NaClIPCAdapter(scoped_ptr<IPC::Channel> channel, base::TaskRunner* runner);
+
+ // Connect the channel. This must be called after the constructor that accepts
+ // an IPC::ChannelHandle, and causes the Channel to be connected on the IO
+ // thread.
+ void ConnectChannel();
+
+ // Implementation of sendmsg. Returns the number of bytes written or -1 on
+ // failure.
+ int Send(const NaClImcTypedMsgHdr* msg);
+
+ // Implementation of recvmsg. Returns the number of bytes read or -1 on
+ // failure. This will block until there's an error or there is data to
+ // read.
+ int BlockingReceive(NaClImcTypedMsgHdr* msg);
+
+ // Closes the IPC channel.
+ void CloseChannel();
+
+ // Make a NaClDesc that refers to this NaClIPCAdapter. Note that the returned
+ // NaClDesc is reference-counted, and a reference is returned.
+ NaClDesc* MakeNaClDesc();
+
+#if defined(OS_POSIX)
+ int TakeClientFileDescriptor();
+#endif
+
+ // Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<NaClIPCAdapter>;
+
+ class RewrittenMessage;
+
+ // This is the data that must only be accessed inside the lock. This struct
+ // just separates it so it's easier to see.
+ struct LockedData {
+ LockedData();
+ ~LockedData();
+
+ // Messages that we have read off of the Chrome IPC channel that are waiting
+ // to be received by the plugin.
+ std::queue< scoped_refptr<RewrittenMessage> > to_be_received_;
+
+ ppapi::proxy::HandleConverter handle_converter_;
+
+ // Data that we've queued from the plugin to send, but doesn't consist of a
+ // full message yet. The calling code can break apart the message into
+ // smaller pieces, and we need to send the message to the other process in
+ // one chunk.
+ //
+ // The IPC channel always starts a new send() at the beginning of each
+ // message, so we don't need to worry about arbitrary message boundaries.
+ std::string to_be_sent_;
+
+ bool channel_closed_;
+ };
+
+ // This is the data that must only be accessed on the I/O thread (as defined
+ // by TaskRunner). This struct just separates it so it's easier to see.
+ struct IOThreadData {
+ IOThreadData();
+ ~IOThreadData();
+
+ scoped_ptr<IPC::Channel> channel_;
+ };
+
+ virtual ~NaClIPCAdapter();
+
+ // Returns 0 if nothing is waiting.
+ int LockedReceive(NaClImcTypedMsgHdr* msg);
+
+ // Sends a message that we know has been completed to the Chrome process.
+ bool SendCompleteMessage(const char* buffer, size_t buffer_len);
+
+ // Clears the LockedData.to_be_sent_ structure in a way to make sure that
+ // the memory is deleted. std::string can sometimes hold onto the buffer
+ // for future use which we don't want.
+ void ClearToBeSent();
+
+ void ConnectChannelOnIOThread();
+ void CloseChannelOnIOThread();
+ void SendMessageOnIOThread(scoped_ptr<IPC::Message> message);
+
+ // Saves the message to forward to NaCl. This method assumes that the caller
+ // holds the lock for locked_data_.
+ void SaveMessage(const IPC::Message& message,
+ RewrittenMessage* rewritten_message);
+
+ base::Lock lock_;
+ base::ConditionVariable cond_var_;
+
+ scoped_refptr<base::TaskRunner> task_runner_;
+
+ // To be accessed inside of lock_ only.
+ LockedData locked_data_;
+
+ // To be accessed on the I/O thread (via task runner) only.
+ IOThreadData io_thread_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(NaClIPCAdapter);
+};
+
+// Export TranslatePepperFileReadWriteOpenFlags for testing.
+int TranslatePepperFileReadWriteOpenFlagsForTesting(int32_t pp_open_flags);
+
+#endif // CHROME_NACL_NACL_IPC_ADAPTER_H_
diff --git a/chromium/components/nacl/loader/nacl_ipc_adapter_unittest.cc b/chromium/components/nacl/loader/nacl_ipc_adapter_unittest.cc
new file mode 100644
index 00000000000..80fd4080d49
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_ipc_adapter_unittest.cc
@@ -0,0 +1,357 @@
+// 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/nacl/loader/nacl_ipc_adapter.h"
+
+#include <string.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/simple_thread.h"
+#include "ipc/ipc_test_sink.h"
+#include "native_client/src/trusted/desc/nacl_desc_custom.h"
+#include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
+#include "ppapi/c/ppb_file_io.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class NaClIPCAdapterTest : public testing::Test {
+ public:
+ NaClIPCAdapterTest() {}
+
+ // testing::Test implementation.
+ virtual void SetUp() OVERRIDE {
+ sink_ = new IPC::TestSink;
+
+ // Takes ownership of the sink_ pointer. Note we provide the current message
+ // loop instead of using a real IO thread. This should work OK since we do
+ // not need real IPC for the tests.
+ adapter_ = new NaClIPCAdapter(scoped_ptr<IPC::Channel>(sink_),
+ base::MessageLoopProxy::current().get());
+ }
+ virtual void TearDown() OVERRIDE {
+ sink_ = NULL; // This pointer is actually owned by the IPCAdapter.
+ adapter_ = NULL;
+ // The adapter destructor has to post a task to destroy the Channel on the
+ // IO thread. For the purposes of the test, we just need to make sure that
+ // task gets run, or it will appear as a leak.
+ message_loop_.RunUntilIdle();
+ }
+
+ protected:
+ int BlockingReceive(void* buf, size_t buf_size) {
+ NaClImcMsgIoVec iov = {buf, buf_size};
+ NaClImcTypedMsgHdr msg = {&iov, 1};
+ return adapter_->BlockingReceive(&msg);
+ }
+
+ int Send(void* buf, size_t buf_size) {
+ NaClImcMsgIoVec iov = {buf, buf_size};
+ NaClImcTypedMsgHdr msg = {&iov, 1};
+ return adapter_->Send(&msg);
+ }
+
+ base::MessageLoop message_loop_;
+
+ scoped_refptr<NaClIPCAdapter> adapter_;
+
+ // Messages sent from nacl to the adapter end up here. Note that we create
+ // this pointer and pass ownership of it to the IPC adapter, who will keep
+ // it alive as long as the adapter is alive. This means that when the
+ // adapter goes away, this pointer will become invalid.
+ //
+ // In real life the adapter needs to take ownership so the channel can be
+ // destroyed on the right thread.
+ IPC::TestSink* sink_;
+};
+
+} // namespace
+
+// Tests a simple message getting rewritten sent from native code to NaCl.
+TEST_F(NaClIPCAdapterTest, SimpleReceiveRewriting) {
+ int routing_id = 0x89898989;
+ uint32 type = 0x55555555;
+ IPC::Message input(routing_id, type, IPC::Message::PRIORITY_NORMAL);
+ uint32 flags = input.flags();
+
+ int value = 0x12345678;
+ input.WriteInt(value);
+ adapter_->OnMessageReceived(input);
+
+ // Buffer just need to be big enough for our message with one int.
+ const int kBufSize = 64;
+ char buf[kBufSize];
+
+ int bytes_read = BlockingReceive(buf, kBufSize);
+ EXPECT_EQ(sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int),
+ static_cast<size_t>(bytes_read));
+
+ const NaClIPCAdapter::NaClMessageHeader* output_header =
+ reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(buf);
+ EXPECT_EQ(sizeof(int), output_header->payload_size);
+ EXPECT_EQ(routing_id, output_header->routing);
+ EXPECT_EQ(type, output_header->type);
+ EXPECT_EQ(flags, output_header->flags);
+ EXPECT_EQ(0u, output_header->num_fds);
+ EXPECT_EQ(0u, output_header->pad);
+
+ // Validate the payload.
+ EXPECT_EQ(value,
+ *reinterpret_cast<const int*>(&buf[
+ sizeof(NaClIPCAdapter::NaClMessageHeader)]));
+}
+
+// Tests a simple message getting rewritten sent from NaCl to native code.
+TEST_F(NaClIPCAdapterTest, SendRewriting) {
+ int routing_id = 0x89898989;
+ uint32 type = 0x55555555;
+ int value = 0x12345678;
+
+ // Send a message with one int inside it.
+ const int buf_size = sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int);
+ char buf[buf_size] = {0};
+
+ NaClIPCAdapter::NaClMessageHeader* header =
+ reinterpret_cast<NaClIPCAdapter::NaClMessageHeader*>(buf);
+ header->payload_size = sizeof(int);
+ header->routing = routing_id;
+ header->type = type;
+ header->flags = 0;
+ header->num_fds = 0;
+ *reinterpret_cast<int*>(
+ &buf[sizeof(NaClIPCAdapter::NaClMessageHeader)]) = value;
+
+ int result = Send(buf, buf_size);
+ EXPECT_EQ(buf_size, result);
+
+ // Check that the message came out the other end in the test sink
+ // (messages are posted, so we have to pump).
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(1u, sink_->message_count());
+ const IPC::Message* msg = sink_->GetMessageAt(0);
+
+ EXPECT_EQ(sizeof(int), msg->payload_size());
+ EXPECT_EQ(header->routing, msg->routing_id());
+ EXPECT_EQ(header->type, msg->type());
+
+ // Now test the partial send case. We should be able to break the message
+ // into two parts and it should still work.
+ sink_->ClearMessages();
+ int first_chunk_size = 7;
+ result = Send(buf, first_chunk_size);
+ EXPECT_EQ(first_chunk_size, result);
+
+ // First partial send should not have made any messages.
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(0u, sink_->message_count());
+
+ // Second partial send should do the same.
+ int second_chunk_size = 2;
+ result = Send(&buf[first_chunk_size], second_chunk_size);
+ EXPECT_EQ(second_chunk_size, result);
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(0u, sink_->message_count());
+
+ // Send the rest of the message in a third chunk.
+ int third_chunk_size = buf_size - first_chunk_size - second_chunk_size;
+ result = Send(&buf[first_chunk_size + second_chunk_size],
+ third_chunk_size);
+ EXPECT_EQ(third_chunk_size, result);
+
+ // Last send should have generated one message.
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(1u, sink_->message_count());
+ msg = sink_->GetMessageAt(0);
+ EXPECT_EQ(sizeof(int), msg->payload_size());
+ EXPECT_EQ(header->routing, msg->routing_id());
+ EXPECT_EQ(header->type, msg->type());
+}
+
+// Tests when a buffer is too small to receive the entire message.
+TEST_F(NaClIPCAdapterTest, PartialReceive) {
+ int routing_id_1 = 0x89898989;
+ uint32 type_1 = 0x55555555;
+ IPC::Message input_1(routing_id_1, type_1, IPC::Message::PRIORITY_NORMAL);
+ int value_1 = 0x12121212;
+ input_1.WriteInt(value_1);
+ adapter_->OnMessageReceived(input_1);
+
+ int routing_id_2 = 0x90909090;
+ uint32 type_2 = 0x66666666;
+ IPC::Message input_2(routing_id_2, type_2, IPC::Message::PRIORITY_NORMAL);
+ int value_2 = 0x23232323;
+ input_2.WriteInt(value_2);
+ adapter_->OnMessageReceived(input_2);
+
+ const int kBufSize = 64;
+ char buf[kBufSize];
+
+ // Read part of the first message.
+ int bytes_requested = 7;
+ int bytes_read = BlockingReceive(buf, bytes_requested);
+ ASSERT_EQ(bytes_requested, bytes_read);
+
+ // Read the rest, this should give us the rest of the first message only.
+ bytes_read += BlockingReceive(&buf[bytes_requested],
+ kBufSize - bytes_requested);
+ EXPECT_EQ(sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int),
+ static_cast<size_t>(bytes_read));
+
+ // Make sure we got the right message.
+ const NaClIPCAdapter::NaClMessageHeader* output_header =
+ reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(buf);
+ EXPECT_EQ(sizeof(int), output_header->payload_size);
+ EXPECT_EQ(routing_id_1, output_header->routing);
+ EXPECT_EQ(type_1, output_header->type);
+
+ // Read the second message to make sure we went on to it.
+ bytes_read = BlockingReceive(buf, kBufSize);
+ EXPECT_EQ(sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int),
+ static_cast<size_t>(bytes_read));
+ output_header =
+ reinterpret_cast<const NaClIPCAdapter::NaClMessageHeader*>(buf);
+ EXPECT_EQ(sizeof(int), output_header->payload_size);
+ EXPECT_EQ(routing_id_2, output_header->routing);
+ EXPECT_EQ(type_2, output_header->type);
+}
+
+// Tests sending messages that are too large. We test sends that are too
+// small implicitly here and in the success case because in that case it
+// succeeds and buffers the data.
+TEST_F(NaClIPCAdapterTest, SendOverflow) {
+ int routing_id = 0x89898989;
+ uint32 type = 0x55555555;
+ int value = 0x12345678;
+
+ // Make a message with one int inside it. Reserve some extra space so
+ // we can test what happens when we send too much data.
+ const int buf_size = sizeof(NaClIPCAdapter::NaClMessageHeader) + sizeof(int);
+ const int big_buf_size = buf_size + 4;
+ char buf[big_buf_size] = {0};
+
+ NaClIPCAdapter::NaClMessageHeader* header =
+ reinterpret_cast<NaClIPCAdapter::NaClMessageHeader*>(buf);
+ header->payload_size = sizeof(int);
+ header->routing = routing_id;
+ header->type = type;
+ header->flags = 0;
+ header->num_fds = 0;
+ *reinterpret_cast<int*>(
+ &buf[sizeof(NaClIPCAdapter::NaClMessageHeader)]) = value;
+
+ // Send too much data and make sure that the send fails.
+ int result = Send(buf, big_buf_size);
+ EXPECT_EQ(-1, result);
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(0u, sink_->message_count());
+
+ // Send too much data in two chunks and make sure that the send fails.
+ int first_chunk_size = 7;
+ result = Send(buf, first_chunk_size);
+ EXPECT_EQ(first_chunk_size, result);
+
+ // First partial send should not have made any messages.
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(0u, sink_->message_count());
+
+ int second_chunk_size = big_buf_size - first_chunk_size;
+ result = Send(&buf[first_chunk_size], second_chunk_size);
+ EXPECT_EQ(-1, result);
+ message_loop_.RunUntilIdle();
+ ASSERT_EQ(0u, sink_->message_count());
+}
+
+// Tests that when the IPC channel reports an error, that waiting reads are
+// unblocked and return a -1 error code.
+TEST_F(NaClIPCAdapterTest, ReadWithChannelError) {
+ // Have a background thread that waits a bit and calls the channel error
+ // handler. This should wake up any waiting threads and immediately return
+ // -1. There is an inherent race condition in that we can't be sure if the
+ // other thread is actually waiting when this happens. This is OK, since the
+ // behavior (which we also explicitly test later) is to return -1 if the
+ // channel has already had an error when you start waiting.
+ class MyThread : public base::SimpleThread {
+ public:
+ explicit MyThread(NaClIPCAdapter* adapter)
+ : SimpleThread("NaClIPCAdapterThread"),
+ adapter_(adapter) {}
+ virtual void Run() OVERRIDE {
+ base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+ adapter_->OnChannelError();
+ }
+ private:
+ scoped_refptr<NaClIPCAdapter> adapter_;
+ };
+ MyThread thread(adapter_.get());
+
+ // IMPORTANT: do not return early from here down (including ASSERT_*) because
+ // the thread needs to joined or it will assert.
+ thread.Start();
+
+ // Request data. This will normally (modulo races) block until data is
+ // received or there is an error, and the thread above will wake us up
+ // after 1s.
+ const int kBufSize = 64;
+ char buf[kBufSize];
+ int result = BlockingReceive(buf, kBufSize);
+ EXPECT_EQ(-1, result);
+
+ // Test the "previously had an error" case. BlockingReceive should return
+ // immediately if there was an error.
+ result = BlockingReceive(buf, kBufSize);
+ EXPECT_EQ(-1, result);
+
+ thread.Join();
+}
+
+// Tests that TranslatePepperFileOpenFlags translates pepper read/write open
+// flags into NaCl open flags correctly.
+TEST_F(NaClIPCAdapterTest, TranslatePepperFileReadWriteOpenFlags) {
+ EXPECT_EQ(NACL_ABI_O_RDONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(PP_FILEOPENFLAG_READ));
+ EXPECT_EQ(NACL_ABI_O_WRONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(PP_FILEOPENFLAG_WRITE));
+ EXPECT_EQ(NACL_ABI_O_WRONLY | NACL_ABI_O_APPEND,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_APPEND));
+ EXPECT_EQ(NACL_ABI_O_RDWR,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE));
+ EXPECT_EQ(NACL_ABI_O_WRONLY | NACL_ABI_O_APPEND,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_APPEND));
+ EXPECT_EQ(NACL_ABI_O_RDWR | NACL_ABI_O_APPEND,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_APPEND));
+
+ // Flags other than PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, and
+ // PP_FILEOPENFLAG_APPEND are discarded.
+ EXPECT_EQ(NACL_ABI_O_WRONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE));
+ EXPECT_EQ(NACL_ABI_O_WRONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_TRUNCATE));
+ EXPECT_EQ(NACL_ABI_O_WRONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_EXCLUSIVE));
+
+ // If none of PP_FILEOPENFLAG_READ, PP_FILEOPENFLAG_WRITE, and
+ // PP_FILEOPENFLAG_APPEND are set, the result should fall back to
+ // NACL_ABI_O_READONLY.
+ EXPECT_EQ(NACL_ABI_O_RDONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(0));
+ EXPECT_EQ(NACL_ABI_O_RDONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_CREATE));
+ EXPECT_EQ(NACL_ABI_O_RDONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_TRUNCATE));
+ EXPECT_EQ(NACL_ABI_O_RDONLY,
+ TranslatePepperFileReadWriteOpenFlagsForTesting(
+ PP_FILEOPENFLAG_EXCLUSIVE));
+}
diff --git a/chromium/components/nacl/loader/nacl_listener.cc b/chromium/components/nacl/loader/nacl_listener.cc
new file mode 100644
index 00000000000..cd175dda5f6
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_listener.cc
@@ -0,0 +1,336 @@
+// 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/nacl/loader/nacl_listener.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+#endif
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/rand_util.h"
+#include "components/nacl/common/nacl_messages.h"
+#include "components/nacl/loader/nacl_ipc_adapter.h"
+#include "components/nacl/loader/nacl_validation_db.h"
+#include "components/nacl/loader/nacl_validation_query.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_switches.h"
+#include "ipc/ipc_sync_channel.h"
+#include "ipc/ipc_sync_message_filter.h"
+#include "native_client/src/trusted/service_runtime/sel_main_chrome.h"
+#include "native_client/src/trusted/validator/nacl_file_info.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+#if defined(OS_LINUX)
+#include "content/public/common/child_process_sandbox_support_linux.h"
+#endif
+
+#if defined(OS_WIN)
+#include <fcntl.h>
+#include <io.h>
+
+#include "content/public/common/sandbox_init.h"
+#endif
+
+namespace {
+#if defined(OS_MACOSX)
+
+// On Mac OS X, shm_open() works in the sandbox but does not give us
+// an FD that we can map as PROT_EXEC. Rather than doing an IPC to
+// get an executable SHM region when CreateMemoryObject() is called,
+// we preallocate one on startup, since NaCl's sel_ldr only needs one
+// of them. This saves a round trip.
+
+base::subtle::Atomic32 g_shm_fd = -1;
+
+int CreateMemoryObject(size_t size, int executable) {
+ if (executable && size > 0) {
+ int result_fd = base::subtle::NoBarrier_AtomicExchange(&g_shm_fd, -1);
+ if (result_fd != -1) {
+ // ftruncate() is disallowed by the Mac OS X sandbox and
+ // returns EPERM. Luckily, we can get the same effect with
+ // lseek() + write().
+ if (lseek(result_fd, size - 1, SEEK_SET) == -1) {
+ LOG(ERROR) << "lseek() failed: " << errno;
+ return -1;
+ }
+ if (write(result_fd, "", 1) != 1) {
+ LOG(ERROR) << "write() failed: " << errno;
+ return -1;
+ }
+ return result_fd;
+ }
+ }
+ // Fall back to NaCl's default implementation.
+ return -1;
+}
+
+#elif defined(OS_LINUX)
+
+int CreateMemoryObject(size_t size, int executable) {
+ return content::MakeSharedMemorySegmentViaIPC(size, executable);
+}
+
+#elif defined(OS_WIN)
+
+NaClListener* g_listener;
+
+// We wrap the function to convert the bool return value to an int.
+int BrokerDuplicateHandle(NaClHandle source_handle,
+ uint32_t process_id,
+ NaClHandle* target_handle,
+ uint32_t desired_access,
+ uint32_t options) {
+ return content::BrokerDuplicateHandle(source_handle, process_id,
+ target_handle, desired_access,
+ options);
+}
+
+int AttachDebugExceptionHandler(const void* info, size_t info_size) {
+ std::string info_string(reinterpret_cast<const char*>(info), info_size);
+ bool result = false;
+ if (!g_listener->Send(new NaClProcessMsg_AttachDebugExceptionHandler(
+ info_string, &result)))
+ return false;
+ return result;
+}
+
+#endif
+
+} // namespace
+
+class BrowserValidationDBProxy : public NaClValidationDB {
+ public:
+ explicit BrowserValidationDBProxy(NaClListener* listener)
+ : listener_(listener) {
+ }
+
+ virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
+ // Initialize to false so that if the Send fails to write to the return
+ // value we're safe. For example if the message is (for some reason)
+ // dispatched as an async message the return parameter will not be written.
+ bool result = false;
+ if (!listener_->Send(new NaClProcessMsg_QueryKnownToValidate(signature,
+ &result))) {
+ LOG(ERROR) << "Failed to query NaCl validation cache.";
+ result = false;
+ }
+ return result;
+ }
+
+ virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
+ // Caching is optional: NaCl will still work correctly if the IPC fails.
+ if (!listener_->Send(new NaClProcessMsg_SetKnownToValidate(signature))) {
+ LOG(ERROR) << "Failed to update NaCl validation cache.";
+ }
+ }
+
+ virtual bool ResolveFileToken(struct NaClFileToken* file_token,
+ int32* fd, std::string* path) OVERRIDE {
+ *fd = -1;
+ *path = "";
+ if (file_token->lo == 0 && file_token->hi == 0) {
+ return false;
+ }
+ IPC::PlatformFileForTransit ipc_fd = IPC::InvalidPlatformFileForTransit();
+ base::FilePath ipc_path;
+ if (!listener_->Send(new NaClProcessMsg_ResolveFileToken(file_token->lo,
+ file_token->hi,
+ &ipc_fd,
+ &ipc_path))) {
+ return false;
+ }
+ if (ipc_fd == IPC::InvalidPlatformFileForTransit()) {
+ return false;
+ }
+ base::PlatformFile handle =
+ IPC::PlatformFileForTransitToPlatformFile(ipc_fd);
+#if defined(OS_WIN)
+ // On Windows, valid handles are 32 bit unsigned integers so this is safe.
+ *fd = reinterpret_cast<uintptr_t>(handle);
+#else
+ *fd = handle;
+#endif
+ // It doesn't matter if the path is invalid UTF8 as long as it's consistent
+ // and unforgeable.
+ *path = ipc_path.AsUTF8Unsafe();
+ return true;
+ }
+
+ private:
+ // The listener never dies, otherwise this might be a dangling reference.
+ NaClListener* listener_;
+};
+
+
+NaClListener::NaClListener() : shutdown_event_(true, false),
+ io_thread_("NaCl_IOThread"),
+#if defined(OS_LINUX)
+ prereserved_sandbox_size_(0),
+#endif
+#if defined(OS_POSIX)
+ number_of_cores_(-1), // unknown/error
+#endif
+ main_loop_(NULL) {
+ io_thread_.StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+#if defined(OS_WIN)
+ DCHECK(g_listener == NULL);
+ g_listener = this;
+#endif
+}
+
+NaClListener::~NaClListener() {
+ NOTREACHED();
+ shutdown_event_.Signal();
+#if defined(OS_WIN)
+ g_listener = NULL;
+#endif
+}
+
+bool NaClListener::Send(IPC::Message* msg) {
+ DCHECK(main_loop_ != NULL);
+ if (base::MessageLoop::current() == main_loop_) {
+ // This thread owns the channel.
+ return channel_->Send(msg);
+ } else {
+ // This thread does not own the channel.
+ return filter_->Send(msg);
+ }
+}
+
+void NaClListener::Listen() {
+ std::string channel_name =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kProcessChannelID);
+ channel_.reset(new IPC::SyncChannel(
+ this, io_thread_.message_loop_proxy().get(), &shutdown_event_));
+ filter_ = new IPC::SyncMessageFilter(&shutdown_event_);
+ channel_->AddFilter(filter_.get());
+ channel_->Init(channel_name, IPC::Channel::MODE_CLIENT, true);
+ main_loop_ = base::MessageLoop::current();
+ main_loop_->Run();
+}
+
+bool NaClListener::OnMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(NaClListener, msg)
+ IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void NaClListener::OnStart(const nacl::NaClStartParams& params) {
+ struct NaClChromeMainArgs *args = NaClChromeMainArgsCreate();
+ if (args == NULL) {
+ LOG(ERROR) << "NaClChromeMainArgsCreate() failed";
+ return;
+ }
+
+ if (params.enable_ipc_proxy) {
+ // Create the initial PPAPI IPC channel between the NaCl IRT and the
+ // browser process. The IRT uses this channel to communicate with the
+ // browser and to create additional IPC channels to renderer processes.
+ IPC::ChannelHandle handle =
+ IPC::Channel::GenerateVerifiedChannelID("nacl");
+ scoped_refptr<NaClIPCAdapter> ipc_adapter(
+ new NaClIPCAdapter(handle, io_thread_.message_loop_proxy().get()));
+ ipc_adapter->ConnectChannel();
+
+ // Pass a NaClDesc to the untrusted side. This will hold a ref to the
+ // NaClIPCAdapter.
+ args->initial_ipc_desc = ipc_adapter->MakeNaClDesc();
+#if defined(OS_POSIX)
+ handle.socket = base::FileDescriptor(
+ ipc_adapter->TakeClientFileDescriptor(), true);
+#endif
+ if (!Send(new NaClProcessHostMsg_PpapiChannelCreated(handle)))
+ LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost.";
+ }
+
+ std::vector<nacl::FileDescriptor> handles = params.handles;
+
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+ args->urandom_fd = dup(base::GetUrandomFD());
+ if (args->urandom_fd < 0) {
+ LOG(ERROR) << "Failed to dup() the urandom FD";
+ return;
+ }
+ args->number_of_cores = number_of_cores_;
+ args->create_memory_object_func = CreateMemoryObject;
+# if defined(OS_MACOSX)
+ CHECK(handles.size() >= 1);
+ g_shm_fd = nacl::ToNativeHandle(handles[handles.size() - 1]);
+ handles.pop_back();
+# endif
+#endif
+
+ if (params.uses_irt) {
+ CHECK(handles.size() >= 1);
+ NaClHandle irt_handle = nacl::ToNativeHandle(handles[handles.size() - 1]);
+ handles.pop_back();
+
+#if defined(OS_WIN)
+ args->irt_fd = _open_osfhandle(reinterpret_cast<intptr_t>(irt_handle),
+ _O_RDONLY | _O_BINARY);
+ if (args->irt_fd < 0) {
+ LOG(ERROR) << "_open_osfhandle() failed";
+ return;
+ }
+#else
+ args->irt_fd = irt_handle;
+#endif
+ } else {
+ // Otherwise, the IRT handle is not even sent.
+ args->irt_fd = -1;
+ }
+
+ if (params.validation_cache_enabled) {
+ // SHA256 block size.
+ CHECK_EQ(params.validation_cache_key.length(), (size_t) 64);
+ // The cache structure is not freed and exists until the NaCl process exits.
+ args->validation_cache = CreateValidationCache(
+ new BrowserValidationDBProxy(this), params.validation_cache_key,
+ params.version);
+ }
+
+ CHECK(handles.size() == 1);
+ args->imc_bootstrap_handle = nacl::ToNativeHandle(handles[0]);
+ args->enable_exception_handling = params.enable_exception_handling;
+ args->enable_debug_stub = params.enable_debug_stub;
+ args->enable_dyncode_syscalls = params.enable_dyncode_syscalls;
+ if (!params.enable_dyncode_syscalls) {
+ // Bound the initial nexe's code segment size under PNaCl to
+ // reduce the chance of a code spraying attack succeeding (see
+ // https://code.google.com/p/nativeclient/issues/detail?id=3572).
+ // We assume that !params.enable_dyncode_syscalls is synonymous
+ // with PNaCl. We can't apply this arbitrary limit outside of
+ // PNaCl because it might break existing NaCl apps, and this limit
+ // is only useful if the dyncode syscalls are disabled.
+ args->initial_nexe_max_code_bytes = 32 << 20; // 32 MB
+ }
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+ args->debug_stub_server_bound_socket_fd = nacl::ToNativeHandle(
+ params.debug_stub_server_bound_socket);
+#endif
+#if defined(OS_WIN)
+ args->broker_duplicate_handle_func = BrokerDuplicateHandle;
+ args->attach_debug_exception_handler_func = AttachDebugExceptionHandler;
+#endif
+#if defined(OS_LINUX)
+ args->prereserved_sandbox_size = prereserved_sandbox_size_;
+#endif
+ NaClChromeMainStart(args);
+ NOTREACHED();
+}
diff --git a/chromium/components/nacl/loader/nacl_listener.h b/chromium/components/nacl/loader/nacl_listener.h
new file mode 100644
index 00000000000..df8b04ecaeb
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_listener.h
@@ -0,0 +1,74 @@
+// 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 CHROME_NACL_NACL_LISTENER_H_
+#define CHROME_NACL_NACL_LISTENER_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "components/nacl/common/nacl_types.h"
+#include "ipc/ipc_listener.h"
+
+namespace IPC {
+class SyncChannel;
+class SyncMessageFilter;
+}
+
+// The NaClListener is an IPC channel listener that waits for a
+// request to start a NaCl module.
+class NaClListener : public IPC::Listener {
+ public:
+ NaClListener();
+ virtual ~NaClListener();
+ // Listen for a request to launch a NaCl module.
+ void Listen();
+
+ bool Send(IPC::Message* msg);
+
+#if defined(OS_LINUX)
+ void set_prereserved_sandbox_size(size_t prereserved_sandbox_size) {
+ prereserved_sandbox_size_ = prereserved_sandbox_size;
+ }
+#endif
+#if defined(OS_POSIX)
+ void set_number_of_cores(int number_of_cores) {
+ number_of_cores_ = number_of_cores;
+ }
+#endif
+
+ private:
+ void OnStart(const nacl::NaClStartParams& params);
+ virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE;
+
+ // A channel back to the browser.
+ scoped_ptr<IPC::SyncChannel> channel_;
+
+ // A filter that allows other threads to use the channel.
+ scoped_refptr<IPC::SyncMessageFilter> filter_;
+
+ base::WaitableEvent shutdown_event_;
+ base::Thread io_thread_;
+
+#if defined(OS_LINUX)
+ size_t prereserved_sandbox_size_;
+#endif
+#if defined(OS_POSIX)
+ // The outer sandbox on Linux and OSX prevents
+ // sysconf(_SC_NPROCESSORS) from working; in Windows, there are no
+ // problems with invoking GetSystemInfo. Therefore, only in
+ // OS_POSIX do we need to supply the number of cores into the
+ // NaClChromeMainArgs object.
+ int number_of_cores_;
+#endif
+
+ // Used to identify what thread we're on.
+ base::MessageLoop* main_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(NaClListener);
+};
+
+#endif // CHROME_NACL_NACL_LISTENER_H_
diff --git a/chromium/components/nacl/loader/nacl_main.cc b/chromium/components/nacl/loader/nacl_main.cc
new file mode 100644
index 00000000000..15b6fd2dc8c
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_main.cc
@@ -0,0 +1,53 @@
+// 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 "build/build_config.h"
+
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/power_monitor/power_monitor_device_source.h"
+#include "base/timer/hi_res_timer_manager.h"
+#include "components/nacl/loader/nacl_listener.h"
+#include "components/nacl/loader/nacl_main_platform_delegate.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/main_function_params.h"
+
+// main() routine for the NaCl loader process.
+int NaClMain(const content::MainFunctionParams& parameters) {
+ const CommandLine& parsed_command_line = parameters.command_line;
+
+ // The main thread of the plugin services IO.
+ base::MessageLoopForIO main_message_loop;
+ base::PlatformThread::SetName("CrNaClMain");
+
+ scoped_ptr<base::PowerMonitorSource> power_monitor_source(
+ new base::PowerMonitorDeviceSource());
+ base::PowerMonitor power_monitor(power_monitor_source.Pass());
+ base::HighResolutionTimerManager hi_res_timer_manager;
+
+#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
+ NaClMainPlatformDelegate platform(parameters);
+ bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox);
+
+#if defined(OS_POSIX)
+ // The number of cores must be obtained before the invocation of
+ // platform.EnableSandbox(), so cannot simply be inlined below.
+ int number_of_cores = sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+
+ if (!no_sandbox) {
+ platform.EnableSandbox();
+ }
+ NaClListener listener;
+#if defined(OS_POSIX)
+ listener.set_number_of_cores(number_of_cores);
+#endif
+
+ listener.Listen();
+#else
+ NOTIMPLEMENTED() << " not implemented startup, plugin startup dialog etc.";
+#endif
+ return 0;
+}
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate.h b/chromium/components/nacl/loader/nacl_main_platform_delegate.h
new file mode 100644
index 00000000000..ca740b85bcd
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate.h
@@ -0,0 +1,26 @@
+// 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 CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
+#define CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "content/public/common/main_function_params.h"
+
+class NaClMainPlatformDelegate {
+ public:
+ explicit NaClMainPlatformDelegate(
+ const content::MainFunctionParams& parameters);
+ ~NaClMainPlatformDelegate();
+
+ // Initiate Lockdown.
+ void EnableSandbox();
+
+ private:
+ const content::MainFunctionParams& parameters_;
+
+ DISALLOW_COPY_AND_ASSIGN(NaClMainPlatformDelegate);
+};
+
+#endif // CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc b/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc
new file mode 100644
index 00000000000..cbe886bc006
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc
@@ -0,0 +1,31 @@
+// 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/nacl/loader/nacl_main_platform_delegate.h"
+
+NaClMainPlatformDelegate::NaClMainPlatformDelegate(
+ const content::MainFunctionParams& parameters)
+ : parameters_(parameters) {
+}
+
+NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
+}
+
+void NaClMainPlatformDelegate::EnableSandbox() {
+ // The setuid sandbox is started in the zygote process: zygote_main_linux.cc
+ // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
+ //
+ // The seccomp sandbox is started in the renderer.
+ // http://code.google.com/p/seccompsandbox/
+ // seccomp is currently disabled for nacl.
+ // http://code.google.com/p/chromium/issues/detail?id=59423
+ // See the code in chrome/renderer/renderer_main_platform_delegate_linux.cc
+ // for how to turn seccomp on.
+ //
+ // The seccomp sandbox should not be enabled for Native Client until
+ // all of these issues are fixed:
+ // http://code.google.com/p/nativeclient/issues/list?q=label:Seccomp
+ // At best, NaCl will not work. At worst, enabling the seccomp sandbox
+ // could create a hole in the NaCl sandbox.
+}
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm b/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm
new file mode 100644
index 00000000000..78fa5390454
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm
@@ -0,0 +1,26 @@
+// 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/nacl/loader/nacl_main_platform_delegate.h"
+
+#import <Cocoa/Cocoa.h>
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "components/nacl/common/nacl_sandbox_type_mac.h"
+#include "components/nacl/common/nacl_switches.h"
+#include "content/public/common/sandbox_init.h"
+
+NaClMainPlatformDelegate::NaClMainPlatformDelegate(
+ const content::MainFunctionParams& parameters)
+ : parameters_(parameters) {
+}
+
+NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
+}
+
+void NaClMainPlatformDelegate::EnableSandbox() {
+ CHECK(content::InitializeSandbox(NACL_SANDBOX_TYPE_NACL_LOADER,
+ base::FilePath()))
+ << "Error initializing sandbox for " << switches::kNaClLoaderProcess;
+}
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc b/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc
new file mode 100644
index 00000000000..f530961700f
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc
@@ -0,0 +1,31 @@
+// 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/nacl/loader/nacl_main_platform_delegate.h"
+
+#include "base/logging.h"
+#include "sandbox/win/src/sandbox.h"
+
+NaClMainPlatformDelegate::NaClMainPlatformDelegate(
+ const content::MainFunctionParams& parameters)
+ : parameters_(parameters) {
+}
+
+NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
+}
+
+void NaClMainPlatformDelegate::EnableSandbox() {
+ sandbox::TargetServices* target_services =
+ parameters_.sandbox_info->target_services;
+
+ CHECK(target_services) << "NaCl-Win EnableSandbox: No Target Services!";
+ // Cause advapi32 to load before the sandbox is turned on.
+ unsigned int dummy_rand;
+ rand_s(&dummy_rand);
+ // Warm up language subsystems before the sandbox is turned on.
+ ::GetUserDefaultLangID();
+ ::GetUserDefaultLCID();
+ // Turn the sandbox on.
+ target_services->LowerToken();
+}
diff --git a/chromium/components/nacl/loader/nacl_sandbox_linux.cc b/chromium/components/nacl/loader/nacl_sandbox_linux.cc
new file mode 100644
index 00000000000..c93aba7cd86
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_sandbox_linux.cc
@@ -0,0 +1,143 @@
+// 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/nacl/loader/nacl_sandbox_linux.h"
+
+#include <signal.h>
+#include <sys/ptrace.h>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "content/public/common/sandbox_init.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+using playground2::ErrorCode;
+using playground2::Sandbox;
+
+namespace {
+
+// On ARM and x86_64, System V shared memory calls have each their own system
+// call, while on i386 they are multiplexed.
+#if defined(__x86_64__) || defined(__arm__)
+bool IsSystemVSharedMemory(int sysno) {
+ switch (sysno) {
+ case __NR_shmat:
+ case __NR_shmctl:
+ case __NR_shmdt:
+ case __NR_shmget:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__i386__)
+// Big system V multiplexing system call.
+bool IsSystemVIpc(int sysno) {
+ switch (sysno) {
+ case __NR_ipc:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+ErrorCode NaClBpfSandboxPolicy(
+ playground2::Sandbox* sb, int sysno, void* aux) {
+ const playground2::BpfSandboxPolicyCallback baseline_policy =
+ content::GetBpfSandboxBaselinePolicy();
+ switch (sysno) {
+ // TODO(jln): NaCl's GDB debug stub uses the following socket system calls,
+ // see if it can be restricted a bit.
+#if defined(__x86_64__) || defined(__arm__)
+ // transport_common.cc needs this.
+ case __NR_accept:
+ case __NR_setsockopt:
+#elif defined(__i386__)
+ case __NR_socketcall:
+#endif
+ // trusted/service_runtime/linux/thread_suspension.c needs sigwait() and is
+ // used by NaCl's GDB debug stub.
+ case __NR_rt_sigtimedwait:
+#if defined(__i386__)
+ // Needed on i386 to set-up the custom segments.
+ case __NR_modify_ldt:
+#endif
+ // NaClAddrSpaceBeforeAlloc needs prlimit64.
+ case __NR_prlimit64:
+ // NaCl uses custom signal stacks.
+ case __NR_sigaltstack:
+ // Below is fairly similar to the policy for a Chromium renderer.
+ // TODO(jln): restrict clone(), ioctl() and prctl().
+ case __NR_ioctl:
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_getrlimit:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_ugetrlimit:
+#endif
+ // NaCl runtime exposes clock_getres to untrusted code.
+ case __NR_clock_getres:
+ case __NR_pread64:
+ case __NR_pwrite64:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_getaffinity:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_setscheduler:
+ case __NR_setpriority:
+ case __NR_sysinfo:
+ // __NR_times needed as clock() is called by CommandBufferHelper, which is
+ // used by NaCl applications that use Pepper's 3D interfaces.
+ // See crbug.com/264856 for details.
+ case __NR_times:
+ case __NR_uname:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ case __NR_ptrace:
+ return ErrorCode(EPERM);
+ default:
+ // TODO(jln): look into getting rid of System V shared memory:
+ // platform_qualify/linux/sysv_shm_and_mmap.c makes it a requirement, but
+ // it may not be needed in all cases. Chromium renderers don't need
+ // System V shared memory on Aura.
+#if defined(__x86_64__) || defined(__arm__)
+ if (IsSystemVSharedMemory(sysno))
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+#elif defined(__i386__)
+ if (IsSystemVIpc(sysno))
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+#endif
+ return baseline_policy.Run(sb, sysno, aux);
+ }
+ NOTREACHED();
+ // GCC wants this.
+ return ErrorCode(EPERM);
+}
+
+void RunSandboxSanityChecks() {
+ errno = 0;
+ // Make a ptrace request with an invalid PID.
+ long ptrace_ret = ptrace(PTRACE_PEEKUSER, -1 /* pid */, NULL, NULL);
+ CHECK_EQ(-1, ptrace_ret);
+ // Without the sandbox on, this ptrace call would ESRCH instead.
+ CHECK_EQ(EPERM, errno);
+}
+
+} // namespace
+
+bool InitializeBpfSandbox() {
+ bool sandbox_is_initialized =
+ content::InitializeSandbox(NaClBpfSandboxPolicy);
+ if (sandbox_is_initialized) {
+ RunSandboxSanityChecks();
+ return true;
+ }
+ return false;
+}
diff --git a/chromium/components/nacl/loader/nacl_sandbox_linux.h b/chromium/components/nacl/loader/nacl_sandbox_linux.h
new file mode 100644
index 00000000000..12eea45fec8
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_sandbox_linux.h
@@ -0,0 +1,10 @@
+// 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_NACL_LOADER_NACL_SANDBOX_LINUX_H_
+#define COMPONENTS_NACL_LOADER_NACL_SANDBOX_LINUX_H_
+
+bool InitializeBpfSandbox();
+
+#endif // COMPONENTS_NACL_LOADER_NACL_SANDBOX_LINUX_H_
diff --git a/chromium/components/nacl/loader/nacl_validation_db.h b/chromium/components/nacl/loader/nacl_validation_db.h
new file mode 100644
index 00000000000..3f9de423d9c
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_validation_db.h
@@ -0,0 +1,28 @@
+// 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_NACL_LOADER_NACL_VALIDATION_DB_H_
+#define COMPONENTS_NACL_LOADER_NACL_VALIDATION_DB_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+struct NaClFileToken;
+
+class NaClValidationDB {
+ public:
+ NaClValidationDB() {}
+ virtual ~NaClValidationDB() {}
+
+ virtual bool QueryKnownToValidate(const std::string& signature) = 0;
+ virtual void SetKnownToValidate(const std::string& signature) = 0;
+ virtual bool ResolveFileToken(struct NaClFileToken* file_token,
+ int32* fd, std::string* path) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NaClValidationDB);
+};
+
+#endif // COMPONENTS_NACL_LOADER_NACL_VALIDATION_DB_H_
diff --git a/chromium/components/nacl/loader/nacl_validation_query.cc b/chromium/components/nacl/loader/nacl_validation_query.cc
new file mode 100644
index 00000000000..6bd8641460f
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_validation_query.cc
@@ -0,0 +1,172 @@
+// 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/nacl/loader/nacl_validation_query.h"
+
+#include "base/logging.h"
+#include "components/nacl/loader/nacl_validation_db.h"
+#include "crypto/nss_util.h"
+#include "native_client/src/include/portability.h"
+#include "native_client/src/trusted/validator/nacl_file_info.h"
+#include "native_client/src/trusted/validator/validation_cache.h"
+
+NaClValidationQueryContext::NaClValidationQueryContext(
+ NaClValidationDB* db,
+ const std::string& profile_key,
+ const std::string& nacl_version)
+ : db_(db),
+ profile_key_(profile_key),
+ nacl_version_(nacl_version) {
+
+ // Sanity checks.
+ CHECK(profile_key.length() >= 8);
+ CHECK(nacl_version.length() >= 4);
+}
+
+NaClValidationQuery* NaClValidationQueryContext::CreateQuery() {
+ NaClValidationQuery* query = new NaClValidationQuery(db_, profile_key_);
+ // Changing the version effectively invalidates existing hashes.
+ query->AddData(nacl_version_);
+ return query;
+}
+
+bool NaClValidationQueryContext::ResolveFileToken(
+ struct NaClFileToken* file_token,
+ int32* fd,
+ std::string* path) {
+ return db_->ResolveFileToken(file_token, fd, path);
+}
+
+NaClValidationQuery::NaClValidationQuery(NaClValidationDB* db,
+ const std::string& profile_key)
+ : state_(READY),
+ hasher_(crypto::HMAC::SHA256),
+ db_(db),
+ buffer_length_(0) {
+ // Without this line on Linux, HMAC::Init will instantiate a singleton that
+ // in turn attempts to open a file. Disabling this behavior avoids a ~70 ms
+ // stall the first time HMAC is used.
+ // This function is also called in nacl_helper_linux.cc, but nacl_helper may
+ // not be used in all cases.
+ // TODO(ncbray) remove when nacl_helper becomes the only code path.
+ // http://code.google.com/p/chromium/issues/detail?id=118263
+#if defined(USE_NSS)
+ crypto::ForceNSSNoDBInit();
+#endif
+ CHECK(hasher_.Init(profile_key));
+}
+
+void NaClValidationQuery::AddData(const char* data, size_t length) {
+ CHECK(state_ == READY);
+ CHECK(buffer_length_ <= sizeof(buffer_));
+ // Chrome's HMAC class doesn't support incremental signing. Work around
+ // this by using a (small) temporary buffer to accumulate data.
+ // Check if there is space in the buffer.
+ if (buffer_length_ + kDigestLength > sizeof(buffer_)) {
+ // Hash the buffer to make space.
+ CompressBuffer();
+ }
+ // Hash the input data into the buffer. Assumes that sizeof(buffer_) >=
+ // kDigestLength * 2 (the buffer can store at least two digests.)
+ CHECK(hasher_.Sign(base::StringPiece(data, length),
+ reinterpret_cast<unsigned char*>(buffer_ + buffer_length_),
+ kDigestLength));
+ buffer_length_ += kDigestLength;
+}
+
+void NaClValidationQuery::AddData(const unsigned char* data, size_t length) {
+ AddData(reinterpret_cast<const char*>(data), length);
+}
+
+void NaClValidationQuery::AddData(const base::StringPiece& data) {
+ AddData(data.data(), data.length());
+}
+
+int NaClValidationQuery::QueryKnownToValidate() {
+ CHECK(state_ == READY);
+ // It is suspicious if we have less than a digest's worth of data.
+ CHECK(buffer_length_ >= kDigestLength);
+ CHECK(buffer_length_ <= sizeof(buffer_));
+ state_ = GET_CALLED;
+ // Ensure the buffer contains only one digest worth of data.
+ CompressBuffer();
+ return db_->QueryKnownToValidate(std::string(buffer_, buffer_length_));
+}
+
+void NaClValidationQuery::SetKnownToValidate() {
+ CHECK(state_ == GET_CALLED);
+ CHECK(buffer_length_ == kDigestLength);
+ state_ = SET_CALLED;
+ db_->SetKnownToValidate(std::string(buffer_, buffer_length_));
+}
+
+// Reduce the size of the data in the buffer by hashing it and writing it back
+// to the buffer.
+void NaClValidationQuery::CompressBuffer() {
+ // Calculate the digest into a temp buffer. It is likely safe to calculate it
+ // directly back into the buffer, but this is an "accidental" semantic we're
+ // avoiding depending on.
+ unsigned char temp[kDigestLength];
+ CHECK(hasher_.Sign(base::StringPiece(buffer_, buffer_length_), temp,
+ kDigestLength));
+ memcpy(buffer_, temp, kDigestLength);
+ buffer_length_ = kDigestLength;
+}
+
+// OO wrappers
+
+static void* CreateQuery(void* handle) {
+ return static_cast<NaClValidationQueryContext*>(handle)->CreateQuery();
+}
+
+static void AddData(void* query, const uint8* data, size_t length) {
+ static_cast<NaClValidationQuery*>(query)->AddData(data, length);
+}
+
+static int QueryKnownToValidate(void* query) {
+ return static_cast<NaClValidationQuery*>(query)->QueryKnownToValidate();
+}
+
+static void SetKnownToValidate(void* query) {
+ static_cast<NaClValidationQuery*>(query)->SetKnownToValidate();
+}
+
+static void DestroyQuery(void* query) {
+ delete static_cast<NaClValidationQuery*>(query);
+}
+
+static int ResolveFileToken(void* handle, struct NaClFileToken* file_token,
+ int32* fd, char** file_path,
+ uint32* file_path_length) {
+ std::string path;
+ *file_path = NULL;
+ *file_path_length = 0;
+ bool ok = static_cast<NaClValidationQueryContext*>(handle)->
+ ResolveFileToken(file_token, fd, &path);
+ if (ok) {
+ *file_path = static_cast<char*>(malloc(path.length() + 1));
+ CHECK(*file_path);
+ memcpy(*file_path, path.data(), path.length());
+ (*file_path)[path.length()] = 0;
+ *file_path_length = static_cast<uint32>(path.length());
+ }
+ return ok;
+}
+
+struct NaClValidationCache* CreateValidationCache(
+ NaClValidationDB* db, const std::string& profile_key,
+ const std::string& nacl_version) {
+ NaClValidationCache* cache =
+ static_cast<NaClValidationCache*>(malloc(sizeof(NaClValidationCache)));
+ // Make sure any fields introduced in a cross-repo change are zeroed.
+ memset(cache, 0, sizeof(*cache));
+ cache->handle = new NaClValidationQueryContext(db, profile_key, nacl_version);
+ cache->CreateQuery = CreateQuery;
+ cache->AddData = AddData;
+ cache->QueryKnownToValidate = QueryKnownToValidate;
+ cache->SetKnownToValidate = SetKnownToValidate;
+ cache->DestroyQuery = DestroyQuery;
+ cache->ResolveFileToken = ResolveFileToken;
+ return cache;
+}
diff --git a/chromium/components/nacl/loader/nacl_validation_query.h b/chromium/components/nacl/loader/nacl_validation_query.h
new file mode 100644
index 00000000000..19ca8b9bda2
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_validation_query.h
@@ -0,0 +1,94 @@
+// 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_NACL_LOADER_NACL_VALIDATION_QUERY_H_
+#define COMPONENTS_NACL_LOADER_NACL_VALIDATION_QUERY_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "crypto/hmac.h"
+
+struct NaClFileToken;
+struct NaClValidationCache;
+class NaClValidationDB;
+class NaClValidationQuery;
+
+class NaClValidationQueryContext {
+ public:
+ NaClValidationQueryContext(NaClValidationDB* db,
+ const std::string& profile_key,
+ const std::string& nacl_version);
+
+ NaClValidationQuery* CreateQuery();
+
+ bool ResolveFileToken(struct NaClFileToken* file_token, int32* fd,
+ std::string* path);
+
+ private:
+ NaClValidationDB* db_;
+
+ // A key used by HMAC that is specific to this installation of Chrome.
+ std::string profile_key_;
+
+ // Bytes indicating the "version" of the validator being used. This is used
+ // to implicitly invalidate the cache - changing the version will change the
+ // hashes that are produced.
+ std::string nacl_version_;
+};
+
+class NaClValidationQuery {
+ public:
+ // SHA256 digest size.
+ static const size_t kDigestLength = 32;
+
+ NaClValidationQuery(NaClValidationDB* db, const std::string& profile_key);
+
+ void AddData(const char* data, size_t length);
+ void AddData(const unsigned char* data, size_t length);
+ void AddData(const base::StringPiece& data);
+
+ int QueryKnownToValidate();
+
+ void SetKnownToValidate();
+
+ private:
+ enum QueryState {
+ READY,
+ GET_CALLED,
+ SET_CALLED
+ };
+
+ // The HMAC interface currently does not support incremental signing. To work
+ // around this, each piece of data is signed and the signature is added to a
+ // buffer. If there is not enough space in the buffer to accommodate new
+ // data, the buffer contents are signed and the new signature replaces the
+ // contents of the buffer. CompressBuffer performs this operation. In
+ // affect, a hash tree is constructed to emulate incremental signing.
+ void CompressBuffer();
+
+ // Track the state of the query to detect suspicious method calls.
+ QueryState state_;
+
+ crypto::HMAC hasher_;
+ NaClValidationDB* db_;
+
+ // The size of buffer_ is a somewhat arbitrary choice. It needs to be at
+ // at least kDigestLength * 2, but it can be arbitrarily large. In practice
+ // there are 4 calls to AddData (version, architechture, cpu features, and
+ // code), so 4 times digest length means the buffer will not need to be
+ // compressed as an intermediate step in the expected use cases.
+ char buffer_[kDigestLength * 4];
+ size_t buffer_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(NaClValidationQuery);
+};
+
+// Create a validation cache interface for use by sel_ldr.
+struct NaClValidationCache* CreateValidationCache(
+ NaClValidationDB* db, const std::string& profile_key,
+ const std::string& nacl_version);
+
+#endif // COMPONENTS_NACL_LOADER_NACL_VALIDATION_QUERY_H_
diff --git a/chromium/components/nacl/loader/nacl_validation_query_unittest.cc b/chromium/components/nacl/loader/nacl_validation_query_unittest.cc
new file mode 100644
index 00000000000..3846effb38e
--- /dev/null
+++ b/chromium/components/nacl/loader/nacl_validation_query_unittest.cc
@@ -0,0 +1,283 @@
+// 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/nacl/loader/nacl_validation_db.h"
+#include "components/nacl/loader/nacl_validation_query.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This test makes sure that validation signature generation is performed
+// correctly. In effect, this means that we are checking all of the data
+// (and no other data) we are passing the signature generator affects the final
+// signature. To avoid tying the tests to a particular implementation, each
+// test generates two signatures and compares them rather than trying to compare
+// against a specified signature.
+
+namespace {
+
+const char kKey[] = "bogus key for HMAC...";
+const char kKeyAlt[] = "bogus key for HMAC!!!";
+
+const char kVersion[] = "bogus version";
+const char kVersionAlt[] = "bogus!version";
+
+
+const char kShortData[] = "Short data 1234567890";
+const char kAltShortData[] = "Short!data 1234567890";
+
+const char kLongData[] = "Long data."
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890";
+
+class MockValidationDB : public NaClValidationDB {
+ public:
+ MockValidationDB()
+ : did_query_(false),
+ did_set_(false),
+ status_(true) {
+ }
+
+ virtual bool QueryKnownToValidate(const std::string& signature) OVERRIDE {
+ // The typecast is needed to work around gtest trying to take the address
+ // of a constant.
+ EXPECT_EQ((int) NaClValidationQuery::kDigestLength,
+ (int) signature.length());
+ EXPECT_FALSE(did_query_);
+ EXPECT_FALSE(did_set_);
+ did_query_ = true;
+ memcpy(query_signature_, signature.data(),
+ NaClValidationQuery::kDigestLength);
+ return status_;
+ }
+
+ virtual void SetKnownToValidate(const std::string& signature) OVERRIDE {
+ // The typecast is needed to work around gtest trying to take the address
+ // of a constant.
+ ASSERT_EQ((int) NaClValidationQuery::kDigestLength,
+ (int) signature.length());
+ EXPECT_TRUE(did_query_);
+ EXPECT_FALSE(did_set_);
+ did_set_ = true;
+ memcpy(set_signature_, signature.data(),
+ NaClValidationQuery::kDigestLength);
+ // Signatures should be the same.
+ EXPECT_EQ(0, memcmp(query_signature_, set_signature_,
+ NaClValidationQuery::kDigestLength));
+ }
+
+ virtual bool ResolveFileToken(struct NaClFileToken* file_token, int32* fd,
+ std::string* path) OVERRIDE {
+ *fd = -1;
+ *path = "";
+ return false;
+ }
+
+ bool did_query_;
+ bool did_set_;
+ bool status_;
+
+ uint8 query_signature_[NaClValidationQuery::kDigestLength];
+ uint8 set_signature_[NaClValidationQuery::kDigestLength];
+};
+
+class TestQuery {
+ public:
+ TestQuery(const char* key, const char* version) {
+ db.reset(new MockValidationDB());
+ context.reset(new NaClValidationQueryContext(db.get(), key, version));
+ query.reset(context->CreateQuery());
+ }
+
+ scoped_ptr<MockValidationDB> db;
+ scoped_ptr<NaClValidationQueryContext> context;
+ scoped_ptr<NaClValidationQuery> query;
+};
+
+class NaClValidationQueryTest : public ::testing::Test {
+ protected:
+ scoped_ptr<TestQuery> query1;
+ scoped_ptr<TestQuery> query2;
+
+ virtual void SetUp() {
+ query1.reset(new TestQuery(kKey, kVersion));
+ query2.reset(new TestQuery(kKey, kVersion));
+ }
+
+ void AssertQuerySame() {
+ ASSERT_TRUE(query1->db->did_query_);
+ ASSERT_TRUE(query2->db->did_query_);
+ ASSERT_EQ(0, memcmp(query1->db->query_signature_,
+ query2->db->query_signature_,
+ NaClValidationQuery::kDigestLength));
+ }
+
+ void AssertQueryDifferent() {
+ ASSERT_TRUE(query1->db->did_query_);
+ ASSERT_TRUE(query2->db->did_query_);
+ ASSERT_NE(0, memcmp(query1->db->query_signature_,
+ query2->db->query_signature_,
+ NaClValidationQuery::kDigestLength));
+ }
+};
+
+TEST_F(NaClValidationQueryTest, Sanity) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ ASSERT_FALSE(query1->db->did_query_);
+ ASSERT_FALSE(query1->db->did_set_);
+ ASSERT_EQ(1, query1->query->QueryKnownToValidate());
+ ASSERT_TRUE(query1->db->did_query_);
+ ASSERT_FALSE(query1->db->did_set_);
+ query1->query->SetKnownToValidate();
+ ASSERT_TRUE(query1->db->did_query_);
+ ASSERT_TRUE(query1->db->did_set_);
+}
+
+TEST_F(NaClValidationQueryTest, ConsistentShort) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQuerySame();
+}
+
+TEST_F(NaClValidationQueryTest, InconsistentShort) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kAltShortData, sizeof(kAltShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQueryDifferent();
+}
+
+// Test for a bug caught during development where AddData would accidently
+// overwrite previously written data and add uninitialzied memory to the hash.
+TEST_F(NaClValidationQueryTest, ConsistentShortBug) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQuerySame();
+}
+
+// Test for a bug caught during development where AddData would accidently
+// overwrite previously written data and add uninitialzed memory to the hash.
+TEST_F(NaClValidationQueryTest, InconsistentShortBug1) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kAltShortData, sizeof(kAltShortData));
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQueryDifferent();
+}
+
+// Make sure we don't ignore the second bit of data.
+TEST_F(NaClValidationQueryTest, InconsistentShort2) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->AddData(kAltShortData, sizeof(kAltShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQueryDifferent();
+}
+
+TEST_F(NaClValidationQueryTest, InconsistentZeroSizedAdd) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->AddData(kShortData, 0);
+ query2->query->QueryKnownToValidate();
+
+ AssertQueryDifferent();
+}
+
+TEST_F(NaClValidationQueryTest, ConsistentZeroSizedAdd) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->AddData("a", 0);
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->AddData("b", 0);
+ query2->query->QueryKnownToValidate();
+
+ AssertQuerySame();
+}
+
+TEST_F(NaClValidationQueryTest, ConsistentRepeatedShort) {
+ for (int i = 0; i < 30; i++) {
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ }
+ query1->query->QueryKnownToValidate();
+
+ for (int i = 0; i < 30; i++) {
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ }
+ query2->query->QueryKnownToValidate();
+
+ AssertQuerySame();
+}
+
+TEST_F(NaClValidationQueryTest, ConsistentLong) {
+ query1->query->AddData(kLongData, sizeof(kLongData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kLongData, sizeof(kLongData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQuerySame();
+}
+
+TEST_F(NaClValidationQueryTest, ConsistentRepeatedLong) {
+ for (int i = 0; i < 30; i++) {
+ query1->query->AddData(kLongData, sizeof(kLongData));
+ }
+ query1->query->QueryKnownToValidate();
+
+ for (int i = 0; i < 30; i++) {
+ query2->query->AddData(kLongData, sizeof(kLongData));
+ }
+ query2->query->QueryKnownToValidate();
+
+ AssertQuerySame();
+}
+
+TEST_F(NaClValidationQueryTest, PerturbKey) {
+ query2.reset(new TestQuery(kKeyAlt, kVersion));
+
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQueryDifferent();
+}
+
+TEST_F(NaClValidationQueryTest, PerturbVersion) {
+ query2.reset(new TestQuery(kKey, kVersionAlt));
+
+ query1->query->AddData(kShortData, sizeof(kShortData));
+ query1->query->QueryKnownToValidate();
+
+ query2->query->AddData(kShortData, sizeof(kShortData));
+ query2->query->QueryKnownToValidate();
+
+ AssertQueryDifferent();
+}
+
+}
diff --git a/chromium/components/nacl/nacl_defines.gypi b/chromium/components/nacl/nacl_defines.gypi
new file mode 100644
index 00000000000..e00abaa35b1
--- /dev/null
+++ b/chromium/components/nacl/nacl_defines.gypi
@@ -0,0 +1,61 @@
+# 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.
+
+{
+ 'variables': {
+ 'conditions': [
+ ['OS=="win"', {
+ 'nacl_defines': [
+ 'NACL_WINDOWS=1',
+ 'NACL_LINUX=0',
+ 'NACL_OSX=0',
+ ],
+ }],
+ ['OS=="linux"', {
+ 'nacl_defines': [
+ 'NACL_WINDOWS=0',
+ 'NACL_LINUX=1',
+ 'NACL_OSX=0',
+ ],
+ }],
+ ['OS=="mac"', {
+ 'nacl_defines': [
+ 'NACL_WINDOWS=0',
+ 'NACL_LINUX=0',
+ 'NACL_OSX=1',
+ ],
+ }],
+ # TODO(mcgrathr): This duplicates native_client/build/common.gypi;
+ # we should figure out a way to unify the settings.
+ ['target_arch=="ia32"', {
+ 'nacl_defines': [
+ 'NACL_TARGET_SUBARCH=32',
+ 'NACL_TARGET_ARCH=x86',
+ 'NACL_BUILD_SUBARCH=32',
+ 'NACL_BUILD_ARCH=x86',
+ ],
+ }],
+ ['target_arch=="x64"', {
+ 'nacl_defines': [
+ 'NACL_TARGET_SUBARCH=64',
+ 'NACL_TARGET_ARCH=x86',
+ 'NACL_BUILD_SUBARCH=64',
+ 'NACL_BUILD_ARCH=x86',
+ ],
+ }],
+ ['target_arch=="arm"', {
+ 'nacl_defines': [
+ 'NACL_BUILD_ARCH=arm',
+ 'NACL_BUILD_SUBARCH=32',
+ 'NACL_TARGET_ARCH=arm',
+ 'NACL_TARGET_SUBARCH=32',
+ ],
+ }],
+ ['target_arch=="mipsel"', {
+ 'nacl_defines': [
+ ],
+ }],
+ ],
+ }
+}
diff --git a/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc
new file mode 100644
index 00000000000..8445342fdaa
--- /dev/null
+++ b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc
@@ -0,0 +1,248 @@
+// 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/nacl/zygote/nacl_fork_delegate_linux.h"
+
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/cpu.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/launch.h"
+#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
+#include "components/nacl/common/nacl_helper_linux.h"
+#include "components/nacl/common/nacl_paths.h"
+#include "components/nacl/common/nacl_switches.h"
+#include "content/public/common/content_switches.h"
+
+namespace {
+
+// Note these need to match up with their counterparts in nacl_helper_linux.c
+// and nacl_helper_bootstrap_linux.c.
+const char kNaClHelperReservedAtZero[] =
+ "--reserved_at_zero=0xXXXXXXXXXXXXXXXX";
+const char kNaClHelperRDebug[] = "--r_debug=0xXXXXXXXXXXXXXXXX";
+
+#if defined(ARCH_CPU_X86)
+bool NonZeroSegmentBaseIsSlow() {
+ base::CPU cpuid;
+ // Using a non-zero segment base is known to be very slow on Intel
+ // Atom CPUs. See "Segmentation-based Memory Protection Mechanism
+ // on Intel Atom Microarchitecture: Coding Optimizations" (Leonardo
+ // Potenza, Intel).
+ //
+ // The following list of CPU model numbers is taken from:
+ // "Intel 64 and IA-32 Architectures Software Developer's Manual"
+ // (http://download.intel.com/products/processor/manual/325462.pdf),
+ // "Table 35-1. CPUID Signature Values of DisplayFamily_DisplayModel"
+ // (Volume 3C, 35-1), which contains:
+ // "06_36H - Intel Atom S Processor Family
+ // 06_1CH, 06_26H, 06_27H, 06_35, 06_36 - Intel Atom Processor Family"
+ if (cpuid.family() == 6) {
+ switch (cpuid.model()) {
+ case 0x1c:
+ case 0x26:
+ case 0x27:
+ case 0x35:
+ case 0x36:
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+} // namespace.
+
+NaClForkDelegate::NaClForkDelegate()
+ : status_(kNaClHelperUnused),
+ fd_(-1) {}
+
+void NaClForkDelegate::Init(const int sandboxdesc) {
+ VLOG(1) << "NaClForkDelegate::Init()";
+ int fds[2];
+
+ // Confirm a hard-wired assumption.
+ // The NaCl constant is from chrome/nacl/nacl_linux_helper.h
+ DCHECK(kNaClSandboxDescriptor == sandboxdesc);
+
+ CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
+ base::FileHandleMappingVector fds_to_map;
+ fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor));
+ fds_to_map.push_back(std::make_pair(sandboxdesc, kNaClSandboxDescriptor));
+
+ // Using nacl_helper_bootstrap is not necessary on x86-64 because
+ // NaCl's x86-64 sandbox is not zero-address-based. Starting
+ // nacl_helper through nacl_helper_bootstrap works on x86-64, but it
+ // leaves nacl_helper_bootstrap mapped at a fixed address at the
+ // bottom of the address space, which is undesirable because it
+ // effectively defeats ASLR.
+#if defined(ARCH_CPU_X86_64)
+ bool kUseNaClBootstrap = false;
+#elif defined(ARCH_CPU_X86)
+ // Performance vs. security trade-off: We prefer using a
+ // non-zero-address-based sandbox on x86-32 because it provides some
+ // ASLR and so is more secure. However, on Atom CPUs, using a
+ // non-zero segment base is very slow, so we use a zero-based
+ // sandbox on those.
+ bool kUseNaClBootstrap = NonZeroSegmentBaseIsSlow();
+#else
+ bool kUseNaClBootstrap = true;
+#endif
+
+ status_ = kNaClHelperUnused;
+ base::FilePath helper_exe;
+ base::FilePath helper_bootstrap_exe;
+ if (!PathService::Get(nacl::FILE_NACL_HELPER, &helper_exe)) {
+ status_ = kNaClHelperMissing;
+ } else if (kUseNaClBootstrap &&
+ !PathService::Get(nacl::FILE_NACL_HELPER_BOOTSTRAP,
+ &helper_bootstrap_exe)) {
+ status_ = kNaClHelperBootstrapMissing;
+ } else if (RunningOnValgrind()) {
+ status_ = kNaClHelperValgrind;
+ } else {
+ CommandLine::StringVector argv_to_launch;
+ {
+ CommandLine cmd_line(CommandLine::NO_PROGRAM);
+ if (kUseNaClBootstrap)
+ cmd_line.SetProgram(helper_bootstrap_exe);
+ else
+ cmd_line.SetProgram(helper_exe);
+
+ // Append any switches that need to be forwarded to the NaCl helper.
+ static const char* kForwardSwitches[] = {
+ switches::kDisableSeccompFilterSandbox,
+ switches::kNoSandbox,
+ };
+ const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess();
+ cmd_line.CopySwitchesFrom(current_cmd_line, kForwardSwitches,
+ arraysize(kForwardSwitches));
+
+ // The command line needs to be tightly controlled to use
+ // |helper_bootstrap_exe|. So from now on, argv_to_launch should be
+ // modified directly.
+ argv_to_launch = cmd_line.argv();
+ }
+ if (kUseNaClBootstrap) {
+ // Arguments to the bootstrap helper which need to be at the start
+ // of the command line, right after the helper's path.
+ CommandLine::StringVector bootstrap_prepend;
+ bootstrap_prepend.push_back(helper_exe.value());
+ bootstrap_prepend.push_back(kNaClHelperReservedAtZero);
+ bootstrap_prepend.push_back(kNaClHelperRDebug);
+ argv_to_launch.insert(argv_to_launch.begin() + 1,
+ bootstrap_prepend.begin(),
+ bootstrap_prepend.end());
+ }
+ base::LaunchOptions options;
+ options.fds_to_remap = &fds_to_map;
+ options.clone_flags = CLONE_FS | SIGCHLD;
+
+ // The NaCl processes spawned may need to exceed the ambient soft limit
+ // on RLIMIT_AS to allocate the untrusted address space and its guard
+ // regions. The nacl_helper itself cannot just raise its own limit,
+ // because the existing limit may prevent the initial exec of
+ // nacl_helper_bootstrap from succeeding, with its large address space
+ // reservation.
+ std::set<int> max_these_limits;
+ max_these_limits.insert(RLIMIT_AS);
+ options.maximize_rlimits = &max_these_limits;
+
+ if (!base::LaunchProcess(argv_to_launch, options, NULL))
+ status_ = kNaClHelperLaunchFailed;
+ // parent and error cases are handled below
+ }
+ if (HANDLE_EINTR(close(fds[1])) != 0)
+ LOG(ERROR) << "close(fds[1]) failed";
+ if (status_ == kNaClHelperUnused) {
+ const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck);
+ char buf[kExpectedLength];
+
+ // Wait for ack from nacl_helper, indicating it is ready to help
+ const ssize_t nread = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
+ if (nread == kExpectedLength &&
+ memcmp(buf, kNaClHelperStartupAck, nread) == 0) {
+ // all is well
+ status_ = kNaClHelperSuccess;
+ fd_ = fds[0];
+ return;
+ }
+
+ status_ = kNaClHelperAckFailed;
+ LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)";
+ }
+ // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper
+ // becomes the default.
+ fd_ = -1;
+ if (HANDLE_EINTR(close(fds[0])) != 0)
+ LOG(ERROR) << "close(fds[0]) failed";
+}
+
+void NaClForkDelegate::InitialUMA(std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ *uma_name = "NaCl.Client.Helper.InitState";
+ *uma_sample = status_;
+ *uma_boundary_value = kNaClHelperStatusBoundary;
+}
+
+NaClForkDelegate::~NaClForkDelegate() {
+ // side effect of close: delegate process will terminate
+ if (status_ == kNaClHelperSuccess) {
+ if (HANDLE_EINTR(close(fd_)) != 0)
+ LOG(ERROR) << "close(fd_) failed";
+ }
+}
+
+bool NaClForkDelegate::CanHelp(const std::string& process_type,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ if (process_type != switches::kNaClLoaderProcess)
+ return false;
+ *uma_name = "NaCl.Client.Helper.StateOnFork";
+ *uma_sample = status_;
+ *uma_boundary_value = kNaClHelperStatusBoundary;
+ return status_ == kNaClHelperSuccess;
+}
+
+pid_t NaClForkDelegate::Fork(const std::vector<int>& fds) {
+ base::ProcessId naclchild;
+ VLOG(1) << "NaClForkDelegate::Fork";
+
+ DCHECK(fds.size() == kNaClParentFDIndex + 1);
+ if (!UnixDomainSocket::SendMsg(fd_, kNaClForkRequest,
+ strlen(kNaClForkRequest), fds)) {
+ LOG(ERROR) << "NaClForkDelegate::Fork: SendMsg failed";
+ return -1;
+ }
+ int nread = HANDLE_EINTR(read(fd_, &naclchild, sizeof(naclchild)));
+ if (nread != sizeof(naclchild)) {
+ LOG(ERROR) << "NaClForkDelegate::Fork: read failed";
+ return -1;
+ }
+ VLOG(1) << "nacl_child is " << naclchild << " (" << nread << " bytes)";
+ return naclchild;
+}
+
+bool NaClForkDelegate::AckChild(const int fd,
+ const std::string& channel_switch) {
+ int nwritten = HANDLE_EINTR(write(fd, channel_switch.c_str(),
+ channel_switch.length()));
+ if (nwritten != static_cast<int>(channel_switch.length())) {
+ return false;
+ }
+ return true;
+}
diff --git a/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h
new file mode 100644
index 00000000000..5812f33fe26
--- /dev/null
+++ b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h
@@ -0,0 +1,53 @@
+// 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_NACL_ZYGOTE_NACL_FORK_DELEGATE_LINUX_H_
+#define COMPONENTS_NACL_ZYGOTE_NACL_FORK_DELEGATE_LINUX_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "content/public/common/zygote_fork_delegate_linux.h"
+
+// The NaClForkDelegate is created during Chrome linux zygote
+// initialization, and provides "fork()" functionality with
+// NaCl specific process characteristics (specifically address
+// space layout) as an alternative to forking the zygote.
+// A new delegate is passed in as an argument to ZygoteMain().
+class NaClForkDelegate : public content::ZygoteForkDelegate {
+ public:
+ NaClForkDelegate();
+ virtual ~NaClForkDelegate();
+
+ virtual void Init(int sandboxdesc) OVERRIDE;
+ virtual void InitialUMA(std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) OVERRIDE;
+ virtual bool CanHelp(const std::string& process_type, std::string* uma_name,
+ int* uma_sample, int* uma_boundary_value) OVERRIDE;
+ virtual pid_t Fork(const std::vector<int>& fds) OVERRIDE;
+ virtual bool AckChild(int fd,
+ const std::string& channel_switch) OVERRIDE;
+
+ private:
+ // These values are reported via UMA and hence they become permanent
+ // constants. Old values cannot be reused, only new ones added.
+ enum NaClHelperStatus {
+ kNaClHelperUnused = 0,
+ kNaClHelperMissing = 1,
+ kNaClHelperBootstrapMissing = 2,
+ kNaClHelperValgrind = 3,
+ kNaClHelperLaunchFailed = 4,
+ kNaClHelperAckFailed = 5,
+ kNaClHelperSuccess = 6,
+ kNaClHelperStatusBoundary // Must be one greater than highest value used.
+ };
+
+ NaClHelperStatus status_;
+ int fd_;
+};
+
+#endif // COMPONENTS_NACL_ZYGOTE_NACL_FORK_DELEGATE_LINUX_H_
diff --git a/chromium/components/nacl_common.gyp b/chromium/components/nacl_common.gyp
new file mode 100644
index 00000000000..1364ef2cd64
--- /dev/null
+++ b/chromium/components/nacl_common.gyp
@@ -0,0 +1,76 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'nacl_switches',
+ 'type': 'static_library',
+ 'sources': [
+ 'nacl/common/nacl_switches.cc',
+ 'nacl/common/nacl_switches.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'nacl_common',
+ 'type': 'static_library',
+ 'sources': [
+ 'nacl/common/nacl_cmd_line.cc',
+ 'nacl/common/nacl_cmd_line.h',
+ 'nacl/common/nacl_messages.cc',
+ 'nacl/common/nacl_messages.h',
+ 'nacl/common/nacl_types.cc',
+ 'nacl/common/nacl_types.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ 'target_name': 'nacl_switches_win64',
+ 'type': 'static_library',
+ 'sources': [
+ 'nacl/common/nacl_switches.cc',
+ 'nacl/common/nacl_switches.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ 'target_name': 'nacl_common_win64',
+ 'type': 'static_library',
+ 'sources': [
+ 'nacl/common/nacl_cmd_line.cc',
+ 'nacl/common/nacl_cmd_line.h',
+ 'nacl/common/nacl_messages.cc',
+ 'nacl/common/nacl_messages.h',
+ 'nacl/common/nacl_types.cc',
+ 'nacl/common/nacl_types.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/navigation_interception.gypi b/chromium/components/navigation_interception.gypi
new file mode 100644
index 00000000000..993a916a02b
--- /dev/null
+++ b/chromium/components/navigation_interception.gypi
@@ -0,0 +1,80 @@
+# 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.
+
+{
+ 'conditions': [
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ 'target_name': 'navigation_interception',
+ 'type': 'static_library',
+ 'defines!': ['CONTENT_IMPLEMENTATION'],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../content/content.gyp:content_common',
+ '../net/net.gyp:net',
+ ],
+ 'include_dirs': [
+ '..',
+ '../skia/config',
+ '<(SHARED_INTERMEDIATE_DIR)/navigation_interception',
+
+ ],
+ 'sources': [
+ 'navigation_interception/intercept_navigation_resource_throttle.cc',
+ 'navigation_interception/intercept_navigation_resource_throttle.h',
+ 'navigation_interception/navigation_params.h',
+ 'navigation_interception/navigation_params.cc',
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'dependencies': [
+ 'navigation_interception_jni_headers',
+ ],
+ 'sources': [
+ 'navigation_interception/component_jni_registrar.cc',
+ 'navigation_interception/component_jni_registrar.h',
+ 'navigation_interception/intercept_navigation_delegate.cc',
+ 'navigation_interception/intercept_navigation_delegate.h',
+ 'navigation_interception/navigation_params_android.h',
+ 'navigation_interception/navigation_params_android.cc',
+ ],
+ }],
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'navigation_interception_java',
+ 'type': 'none',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'variables': {
+ 'java_in_dir': 'navigation_interception/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'navigation_interception_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java',
+ 'navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'navigation_interception',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ ],
+ }],
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/navigation_interception/DEPS b/chromium/components/navigation_interception/DEPS
new file mode 100644
index 00000000000..cee4d3e1b1c
--- /dev/null
+++ b/chromium/components/navigation_interception/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+jni",
+ "+net",
+
+ "+content/public/browser",
+ "+content/public/common",
+ "+content/public/test",
+]
diff --git a/chromium/components/navigation_interception/OWNERS b/chromium/components/navigation_interception/OWNERS
new file mode 100644
index 00000000000..82ab3736cc6
--- /dev/null
+++ b/chromium/components/navigation_interception/OWNERS
@@ -0,0 +1,2 @@
+joth@chromium.org
+mkosiba@chromium.org
diff --git a/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java
new file mode 100644
index 00000000000..f2144675e1b
--- /dev/null
+++ b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java
@@ -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.
+
+package org.chromium.components.navigation_interception;
+
+import org.chromium.base.CalledByNative;
+
+public interface InterceptNavigationDelegate {
+
+ /**
+ * This method is called for every top-level navigation within the associated WebContents.
+ * The method allows the embedder to ignore navigations. This is used on Android to 'convert'
+ * certain navigations to Intents to 3rd party applications.
+ *
+ * @param navigationParams parameters describing the navigation.
+ * @return true if the navigation should be ignored.
+ */
+ @CalledByNative
+ boolean shouldIgnoreNavigation(NavigationParams navigationParams);
+}
diff --git a/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java
new file mode 100644
index 00000000000..cdfd88313f7
--- /dev/null
+++ b/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java
@@ -0,0 +1,36 @@
+// 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.
+
+package org.chromium.components.navigation_interception;
+
+import org.chromium.base.CalledByNative;
+
+public class NavigationParams {
+ // Target url of the navigation.
+ public final String url;
+ // True if the the navigation method is "POST".
+ public final boolean isPost;
+ // True if the navigation was initiated by the user.
+ public final boolean hasUserGesture;
+ // Page transition type (e.g. link / typed).
+ public final int pageTransitionType;
+ // Is the navigation a redirect (in which case url is the "target" address).
+ public final boolean isRedirect;
+
+ public NavigationParams(String url, boolean isPost, boolean hasUserGesture,
+ int pageTransitionType, boolean isRedirect) {
+ this.url = url;
+ this.isPost = isPost;
+ this.hasUserGesture = hasUserGesture;
+ this.pageTransitionType = pageTransitionType;
+ this.isRedirect = isRedirect;
+ }
+
+ @CalledByNative
+ public static NavigationParams create(String url, boolean isPost, boolean hasUserGesture,
+ int pageTransitionType, boolean isRedirect) {
+ return new NavigationParams(url, isPost, hasUserGesture, pageTransitionType,
+ isRedirect);
+ }
+}
diff --git a/chromium/components/navigation_interception/component_jni_registrar.cc b/chromium/components/navigation_interception/component_jni_registrar.cc
new file mode 100644
index 00000000000..9663529d7cc
--- /dev/null
+++ b/chromium/components/navigation_interception/component_jni_registrar.cc
@@ -0,0 +1,24 @@
+// 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/navigation_interception/component_jni_registrar.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "components/navigation_interception/intercept_navigation_delegate.h"
+#include "components/navigation_interception/navigation_params_android.h"
+
+namespace navigation_interception {
+
+static base::android::RegistrationMethod kComponentRegisteredMethods[] = {
+ { "InterceptNavigationDelegate", RegisterInterceptNavigationDelegate },
+ { "NavigationParams", RegisterNavigationParams },
+};
+
+bool RegisterNavigationInterceptionJni(JNIEnv* env) {
+ return RegisterNativeMethods(
+ env, kComponentRegisteredMethods, arraysize(kComponentRegisteredMethods));
+}
+
+} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/component_jni_registrar.h b/chromium/components/navigation_interception/component_jni_registrar.h
new file mode 100644
index 00000000000..c79f3778e9f
--- /dev/null
+++ b/chromium/components/navigation_interception/component_jni_registrar.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_NAVIGATION_INTERCEPTION_COMPONENT_JNI_REGISTRAR_H_
+#define COMPONENTS_NAVIGATION_INTERCEPTION_COMPONENT_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+namespace navigation_interception {
+
+// Register all JNI bindings necessary for the navigation_interception
+// component.
+bool RegisterNavigationInterceptionJni(JNIEnv* env);
+
+} // namespace navigation_interception
+
+#endif // COMPONENTS_NAVIGATION_INTERCEPTION_COMPONENT_JNI_REGISTRAR_H_
diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.cc b/chromium/components/navigation_interception/intercept_navigation_delegate.cc
new file mode 100644
index 00000000000..1cbe04c8e7e
--- /dev/null
+++ b/chromium/components/navigation_interception/intercept_navigation_delegate.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/navigation_interception/intercept_navigation_delegate.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/callback.h"
+#include "components/navigation_interception/intercept_navigation_resource_throttle.h"
+#include "components/navigation_interception/navigation_params_android.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "jni/InterceptNavigationDelegate_jni.h"
+#include "net/url_request/url_request.h"
+#include "url/gurl.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+using content::BrowserThread;
+using content::PageTransition;
+using content::RenderViewHost;
+using content::WebContents;
+
+namespace navigation_interception {
+
+namespace {
+
+const void* kInterceptNavigationDelegateUserDataKey =
+ &kInterceptNavigationDelegateUserDataKey;
+
+bool CheckIfShouldIgnoreNavigationOnUIThread(RenderViewHost* source,
+ const NavigationParams& params) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(source);
+
+ WebContents* web_contents = WebContents::FromRenderViewHost(source);
+ if (!web_contents)
+ return false;
+ InterceptNavigationDelegate* intercept_navigation_delegate =
+ InterceptNavigationDelegate::Get(web_contents);
+ if (!intercept_navigation_delegate)
+ return false;
+
+ return intercept_navigation_delegate->ShouldIgnoreNavigation(params);
+}
+
+} // namespace
+
+// static
+void InterceptNavigationDelegate::Associate(
+ WebContents* web_contents,
+ scoped_ptr<InterceptNavigationDelegate> delegate) {
+ web_contents->SetUserData(kInterceptNavigationDelegateUserDataKey,
+ delegate.release());
+}
+
+// static
+InterceptNavigationDelegate* InterceptNavigationDelegate::Get(
+ WebContents* web_contents) {
+ return reinterpret_cast<InterceptNavigationDelegate*>(
+ web_contents->GetUserData(kInterceptNavigationDelegateUserDataKey));
+}
+
+// static
+content::ResourceThrottle* InterceptNavigationDelegate::CreateThrottleFor(
+ net::URLRequest* request) {
+ return new InterceptNavigationResourceThrottle(
+ request, base::Bind(&CheckIfShouldIgnoreNavigationOnUIThread));
+}
+
+InterceptNavigationDelegate::InterceptNavigationDelegate(
+ JNIEnv* env, jobject jdelegate)
+ : weak_jdelegate_(env, jdelegate) {
+}
+
+InterceptNavigationDelegate::~InterceptNavigationDelegate() {
+}
+
+bool InterceptNavigationDelegate::ShouldIgnoreNavigation(
+ const NavigationParams& navigation_params) {
+ if (!navigation_params.url().is_valid())
+ return false;
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
+
+ if (jdelegate.is_null())
+ return false;
+
+ ScopedJavaLocalRef<jobject> jobject_params =
+ CreateJavaNavigationParams(env, navigation_params);
+
+ return Java_InterceptNavigationDelegate_shouldIgnoreNavigation(
+ env,
+ jdelegate.obj(),
+ jobject_params.obj());
+}
+
+// Register native methods.
+
+bool RegisterInterceptNavigationDelegate(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/intercept_navigation_delegate.h b/chromium/components/navigation_interception/intercept_navigation_delegate.h
new file mode 100644
index 00000000000..c70beef978a
--- /dev/null
+++ b/chromium/components/navigation_interception/intercept_navigation_delegate.h
@@ -0,0 +1,69 @@
+// 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_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_DELEGATE_H_
+#define COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_DELEGATE_H_
+
+#include "base/android/jni_helper.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/supports_user_data.h"
+#include "content/public/common/page_transition_types.h"
+
+class GURL;
+
+namespace content {
+class ResourceThrottle;
+class WebContents;
+}
+
+namespace net {
+class URLRequest;
+}
+
+namespace navigation_interception {
+
+class NavigationParams;
+
+// Native side of the InterceptNavigationDelegate Java interface.
+// This is used to create a InterceptNavigationResourceThrottle that calls the
+// Java interface method to determine whether a navigation should be ignored or
+// not.
+// To us this class:
+// 1) the Java-side interface implementation must be associated (via the
+// Associate method) with a WebContents for which URLRequests are to be
+// intercepted,
+// 2) the ResourceThrottle obtained via CreateThrottleFor must be associated
+// with the URLRequests in the ResourceDispatcherHostDelegate
+// implementation.
+class InterceptNavigationDelegate : public base::SupportsUserData::Data {
+ public:
+ InterceptNavigationDelegate(JNIEnv* env, jobject jdelegate);
+ virtual ~InterceptNavigationDelegate();
+
+ // Associates the InterceptNavigationDelegate with a WebContents using the
+ // SupportsUserData mechanism.
+ // As implied by the use of scoped_ptr, the WebContents will assume ownership
+ // of |delegate|.
+ static void Associate(content::WebContents* web_contents,
+ scoped_ptr<InterceptNavigationDelegate> delegate);
+ // Gets the InterceptNavigationDelegate associated with the WebContents,
+ // can be null.
+ static InterceptNavigationDelegate* Get(content::WebContents* web_contents);
+
+ // Creates a InterceptNavigationResourceThrottle that will direct all
+ // callbacks to the InterceptNavigationDelegate.
+ static content::ResourceThrottle* CreateThrottleFor(
+ net::URLRequest* request);
+
+ virtual bool ShouldIgnoreNavigation(
+ const NavigationParams& navigation_params);
+ private:
+ JavaObjectWeakGlobalRef weak_jdelegate_;
+};
+
+bool RegisterInterceptNavigationDelegate(JNIEnv* env);
+
+} // namespace navigation_interception
+
+#endif // COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_DELEGATE_H_
diff --git a/chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc
new file mode 100644
index 00000000000..73513ef4752
--- /dev/null
+++ b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.cc
@@ -0,0 +1,140 @@
+// 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/navigation_interception/intercept_navigation_resource_throttle.h"
+
+#include "components/navigation_interception/navigation_params.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/common/referrer.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+
+using content::BrowserThread;
+using content::ChildProcessSecurityPolicy;
+using content::PageTransition;
+using content::Referrer;
+using content::RenderViewHost;
+using content::ResourceRequestInfo;
+
+namespace navigation_interception {
+
+namespace {
+
+void CheckIfShouldIgnoreNavigationOnUIThread(
+ int render_process_id,
+ int render_view_id,
+ const NavigationParams& navigation_params,
+ InterceptNavigationResourceThrottle::CheckOnUIThreadCallback
+ should_ignore_callback,
+ base::Callback<void(bool)> callback) {
+
+ bool should_ignore_navigation = false;
+ RenderViewHost* rvh =
+ RenderViewHost::FromID(render_process_id, render_view_id);
+
+ if (rvh) {
+ NavigationParams validated_params(navigation_params);
+ RenderViewHost::FilterURL(
+ rvh->GetProcess(), false, &validated_params.url());
+
+ should_ignore_navigation = should_ignore_callback.Run(rvh,
+ validated_params);
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(callback, should_ignore_navigation));
+}
+
+} // namespace
+
+InterceptNavigationResourceThrottle::InterceptNavigationResourceThrottle(
+ net::URLRequest* request,
+ CheckOnUIThreadCallback should_ignore_callback)
+ : request_(request),
+ should_ignore_callback_(should_ignore_callback),
+ weak_ptr_factory_(this) {
+}
+
+InterceptNavigationResourceThrottle::~InterceptNavigationResourceThrottle() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+void InterceptNavigationResourceThrottle::WillStartRequest(bool* defer) {
+ *defer =
+ CheckIfShouldIgnoreNavigation(request_->url(), request_->method(), false);
+}
+
+void InterceptNavigationResourceThrottle::WillRedirectRequest(
+ const GURL& new_url,
+ bool* defer) {
+ *defer =
+ CheckIfShouldIgnoreNavigation(new_url, GetMethodAfterRedirect(), true);
+}
+
+std::string InterceptNavigationResourceThrottle::GetMethodAfterRedirect() {
+ net::HttpResponseHeaders* headers = request_->response_headers();
+ if (!headers)
+ return request_->method();
+ return net::URLRequest::ComputeMethodForRedirect(
+ request_->method(), headers->response_code());
+}
+
+bool InterceptNavigationResourceThrottle::CheckIfShouldIgnoreNavigation(
+ const GURL& url,
+ const std::string& method,
+ bool is_redirect) {
+ const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
+ if (!info)
+ return false;
+
+ int render_process_id, render_view_id;
+ if (!info->GetAssociatedRenderView(&render_process_id, &render_view_id))
+ return false;
+
+ NavigationParams navigation_params(url,
+ Referrer(GURL(request_->referrer()),
+ info->GetReferrerPolicy()),
+ info->HasUserGesture(),
+ method == "POST",
+ info->GetPageTransition(),
+ is_redirect);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &CheckIfShouldIgnoreNavigationOnUIThread,
+ render_process_id,
+ render_view_id,
+ navigation_params,
+ should_ignore_callback_,
+ base::Bind(
+ &InterceptNavigationResourceThrottle::OnResultObtained,
+ weak_ptr_factory_.GetWeakPtr())));
+
+ // Defer request while we wait for the UI thread to check if the navigation
+ // should be ignored.
+ return true;
+}
+
+void InterceptNavigationResourceThrottle::OnResultObtained(
+ bool should_ignore_navigation) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (should_ignore_navigation) {
+ controller()->CancelAndIgnore();
+ } else {
+ controller()->Resume();
+ }
+}
+
+} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/intercept_navigation_resource_throttle.h b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.h
new file mode 100644
index 00000000000..79ed57e44c1
--- /dev/null
+++ b/chromium/components/navigation_interception/intercept_navigation_resource_throttle.h
@@ -0,0 +1,62 @@
+// 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_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_RESOURCE_THROTTLE_H_
+#define COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_RESOURCE_THROTTLE_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "content/public/browser/resource_throttle.h"
+
+class GURL;
+
+namespace content {
+class RenderViewHost;
+}
+
+namespace net {
+class URLRequest;
+}
+
+namespace navigation_interception {
+
+class NavigationParams;
+
+// This class allows the provider of the Callback to selectively ignore top
+// level navigations.
+class InterceptNavigationResourceThrottle : public content::ResourceThrottle {
+ public:
+ typedef base::Callback<bool(
+ content::RenderViewHost* /* source */,
+ const NavigationParams& /* navigation_params */)>
+ CheckOnUIThreadCallback;
+
+ InterceptNavigationResourceThrottle(
+ net::URLRequest* request,
+ CheckOnUIThreadCallback should_ignore_callback);
+ virtual ~InterceptNavigationResourceThrottle();
+
+ // content::ResourceThrottle implementation:
+ virtual void WillStartRequest(bool* defer) OVERRIDE;
+ virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
+
+ private:
+ std::string GetMethodAfterRedirect();
+ bool CheckIfShouldIgnoreNavigation(const GURL& url,
+ const std::string& method,
+ bool is_redirect);
+ void OnResultObtained(bool should_ignore_navigation);
+
+ net::URLRequest* request_;
+ CheckOnUIThreadCallback should_ignore_callback_;
+ base::WeakPtrFactory<InterceptNavigationResourceThrottle> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterceptNavigationResourceThrottle);
+};
+
+} // namespace navigation_interception
+
+#endif // COMPONENTS_NAVIGATION_INTERCEPTION_INTERCEPT_NAVIGATION_RESOURCE_THROTTLE_H_
diff --git a/chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc b/chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc
new file mode 100644
index 00000000000..e1bd566b54e
--- /dev/null
+++ b/chromium/components/navigation_interception/intercept_navigation_resource_throttle_unittest.cc
@@ -0,0 +1,475 @@
+// 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/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "components/navigation_interception/intercept_navigation_resource_throttle.h"
+#include "components/navigation_interception/navigation_params.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/browser/resource_controller.h"
+#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/browser/resource_dispatcher_host_delegate.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/browser/resource_throttle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/test/mock_resource_context.h"
+#include "content/public/test/test_renderer_host.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Eq;
+using testing::Ne;
+using testing::Property;
+using testing::Return;
+
+namespace navigation_interception {
+
+namespace {
+
+const char kTestUrl[] = "http://www.test.com/";
+const char kUnsafeTestUrl[] = "about:crash";
+
+// The MS C++ compiler complains about not being able to resolve which url()
+// method (const or non-const) to use if we use the Property matcher to check
+// the return value of the NavigationParams::url() method.
+// It is possible to suppress the error by specifying the types directly but
+// that results in very ugly syntax, which is why these custom matchers are
+// used instead.
+MATCHER(NavigationParamsUrlIsTest, "") {
+ return arg.url() == GURL(kTestUrl);
+}
+
+MATCHER(NavigationParamsUrlIsSafe, "") {
+ return arg.url() != GURL(kUnsafeTestUrl);
+}
+
+} // namespace
+
+
+// MockInterceptCallbackReceiver ----------------------------------------------
+
+class MockInterceptCallbackReceiver {
+ public:
+ MOCK_METHOD2(ShouldIgnoreNavigation,
+ bool(content::RenderViewHost* source,
+ const NavigationParams& navigation_params));
+};
+
+// MockResourceController -----------------------------------------------------
+class MockResourceController : public content::ResourceController {
+ public:
+ enum Status {
+ UNKNOWN,
+ RESUMED,
+ CANCELLED
+ };
+
+ MockResourceController()
+ : status_(UNKNOWN) {
+ }
+
+ Status status() const { return status_; }
+
+ // ResourceController:
+ virtual void Cancel() OVERRIDE {
+ NOTREACHED();
+ }
+ virtual void CancelAndIgnore() OVERRIDE {
+ status_ = CANCELLED;
+ }
+ virtual void CancelWithError(int error_code) OVERRIDE {
+ NOTREACHED();
+ }
+ virtual void Resume() OVERRIDE {
+ DCHECK(status_ == UNKNOWN);
+ status_ = RESUMED;
+ }
+
+ private:
+ Status status_;
+};
+
+// TestIOThreadState ----------------------------------------------------------
+
+enum RedirectMode {
+ REDIRECT_MODE_NO_REDIRECT,
+ REDIRECT_MODE_302,
+};
+
+class TestIOThreadState {
+ public:
+ TestIOThreadState(const GURL& url,
+ int render_process_id,
+ int render_view_id,
+ const std::string& request_method,
+ RedirectMode redirect_mode,
+ MockInterceptCallbackReceiver* callback_receiver)
+ : resource_context_(&test_url_request_context_),
+ request_(url, NULL, resource_context_.GetRequestContext()) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ if (render_process_id != MSG_ROUTING_NONE &&
+ render_view_id != MSG_ROUTING_NONE) {
+ content::ResourceRequestInfo::AllocateForTesting(
+ &request_,
+ ResourceType::MAIN_FRAME,
+ &resource_context_,
+ render_process_id,
+ render_view_id);
+ }
+ throttle_.reset(new InterceptNavigationResourceThrottle(
+ &request_,
+ base::Bind(&MockInterceptCallbackReceiver::ShouldIgnoreNavigation,
+ base::Unretained(callback_receiver))));
+ throttle_->set_controller_for_testing(&throttle_controller_);
+ request_.set_method(request_method);
+
+ if (redirect_mode == REDIRECT_MODE_302) {
+ net::HttpResponseInfo& response_info =
+ const_cast<net::HttpResponseInfo&>(request_.response_info());
+ response_info.headers = new net::HttpResponseHeaders(
+ "Status: 302 Found\0\0");
+ }
+ }
+
+ void ThrottleWillStartRequest(bool* defer) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ throttle_->WillStartRequest(defer);
+ }
+
+ void ThrottleWillRedirectRequest(const GURL& new_url, bool* defer) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ throttle_->WillRedirectRequest(new_url, defer);
+ }
+
+ bool request_resumed() const {
+ return throttle_controller_.status() ==
+ MockResourceController::RESUMED;
+ }
+
+ bool request_cancelled() const {
+ return throttle_controller_.status() ==
+ MockResourceController::CANCELLED;
+ }
+
+ private:
+ net::TestURLRequestContext test_url_request_context_;
+ content::MockResourceContext resource_context_;
+ net::URLRequest request_;
+ scoped_ptr<InterceptNavigationResourceThrottle> throttle_;
+ MockResourceController throttle_controller_;
+};
+
+// InterceptNavigationResourceThrottleTest ------------------------------------
+
+class InterceptNavigationResourceThrottleTest
+ : public content::RenderViewHostTestHarness {
+ public:
+ InterceptNavigationResourceThrottleTest()
+ : mock_callback_receiver_(new MockInterceptCallbackReceiver()),
+ io_thread_state_(NULL) {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ RenderViewHostTestHarness::SetUp();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (web_contents())
+ web_contents()->SetDelegate(NULL);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&base::DeletePointer<TestIOThreadState>, io_thread_state_));
+
+ RenderViewHostTestHarness::TearDown();
+ }
+
+ void SetIOThreadState(TestIOThreadState* io_thread_state) {
+ io_thread_state_ = io_thread_state;
+ }
+
+ void RunThrottleWillStartRequestOnIOThread(
+ const GURL& url,
+ const std::string& request_method,
+ RedirectMode redirect_mode,
+ int render_process_id,
+ int render_view_id,
+ bool* defer) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ TestIOThreadState* io_thread_state =
+ new TestIOThreadState(url, render_process_id, render_view_id,
+ request_method, redirect_mode,
+ mock_callback_receiver_.get());
+
+ SetIOThreadState(io_thread_state);
+
+ if (redirect_mode == REDIRECT_MODE_NO_REDIRECT)
+ io_thread_state->ThrottleWillStartRequest(defer);
+ else
+ io_thread_state->ThrottleWillRedirectRequest(url, defer);
+ }
+
+ protected:
+ enum ShouldIgnoreNavigationCallbackAction {
+ IgnoreNavigation,
+ DontIgnoreNavigation
+ };
+
+ void SetUpWebContentsDelegateAndDrainRunLoop(
+ ShouldIgnoreNavigationCallbackAction callback_action,
+ bool* defer) {
+
+ ON_CALL(*mock_callback_receiver_, ShouldIgnoreNavigation(_, _))
+ .WillByDefault(Return(callback_action == IgnoreNavigation));
+ EXPECT_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(rvh(), NavigationParamsUrlIsTest()))
+ .Times(1);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kTestUrl),
+ "GET",
+ REDIRECT_MODE_NO_REDIRECT,
+ web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+ web_contents()->GetRenderViewHost()->GetRoutingID(),
+ base::Unretained(defer)));
+
+ // Wait for the request to finish processing.
+ base::RunLoop().RunUntilIdle();
+ }
+
+ void WaitForPreviouslyScheduledIoThreadWork() {
+ base::WaitableEvent io_thread_work_done(true, false);
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &base::WaitableEvent::Signal,
+ base::Unretained(&io_thread_work_done)));
+ io_thread_work_done.Wait();
+ }
+
+ scoped_ptr<MockInterceptCallbackReceiver> mock_callback_receiver_;
+ TestIOThreadState* io_thread_state_;
+};
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ RequestDeferredAndResumedIfNavigationNotIgnored) {
+ bool defer = false;
+ SetUpWebContentsDelegateAndDrainRunLoop(DontIgnoreNavigation, &defer);
+
+ EXPECT_TRUE(defer);
+ EXPECT_TRUE(io_thread_state_);
+ EXPECT_TRUE(io_thread_state_->request_resumed());
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ RequestDeferredAndCancelledIfNavigationIgnored) {
+ bool defer = false;
+ SetUpWebContentsDelegateAndDrainRunLoop(IgnoreNavigation, &defer);
+
+ EXPECT_TRUE(defer);
+ EXPECT_TRUE(io_thread_state_);
+ EXPECT_TRUE(io_thread_state_->request_cancelled());
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ NoCallbackMadeIfContentsDeletedWhileThrottleRunning) {
+ bool defer = false;
+
+ // The tested scenario is when the WebContents is deleted after the
+ // ResourceThrottle has finished processing on the IO thread but before the
+ // UI thread callback has been processed. Since both threads in this test
+ // are serviced by one message loop, the post order is the execution order.
+ EXPECT_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(_, _))
+ .Times(0);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kTestUrl),
+ "GET",
+ REDIRECT_MODE_NO_REDIRECT,
+ web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+ web_contents()->GetRenderViewHost()->GetRoutingID(),
+ base::Unretained(&defer)));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(
+ &RenderViewHostTestHarness::DeleteContents,
+ base::Unretained(this)));
+
+ // The WebContents will now be deleted and only after that will the UI-thread
+ // callback posted by the ResourceThrottle be executed.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(defer);
+ EXPECT_TRUE(io_thread_state_);
+ EXPECT_TRUE(io_thread_state_->request_resumed());
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ RequestNotDeferredForRequestNotAssociatedWithARenderView) {
+ bool defer = false;
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kTestUrl),
+ "GET",
+ REDIRECT_MODE_NO_REDIRECT,
+ MSG_ROUTING_NONE,
+ MSG_ROUTING_NONE,
+ base::Unretained(&defer)));
+
+ // Wait for the request to finish processing.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(defer);
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ CallbackCalledWithFilteredUrl) {
+ bool defer = false;
+
+ ON_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
+ .WillByDefault(Return(false));
+ EXPECT_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(_, NavigationParamsUrlIsSafe()))
+ .Times(1);
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kUnsafeTestUrl),
+ "GET",
+ REDIRECT_MODE_NO_REDIRECT,
+ web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+ web_contents()->GetRenderViewHost()->GetRoutingID(),
+ base::Unretained(&defer)));
+
+ // Wait for the request to finish processing.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ CallbackIsPostFalseForGet) {
+ bool defer = false;
+
+ EXPECT_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(_, AllOf(
+ NavigationParamsUrlIsSafe(),
+ Property(&NavigationParams::is_post, Eq(false)))))
+ .WillOnce(Return(false));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kTestUrl),
+ "GET",
+ REDIRECT_MODE_NO_REDIRECT,
+ web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+ web_contents()->GetRenderViewHost()->GetRoutingID(),
+ base::Unretained(&defer)));
+
+ // Wait for the request to finish processing.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ CallbackIsPostTrueForPost) {
+ bool defer = false;
+
+ EXPECT_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(_, AllOf(
+ NavigationParamsUrlIsSafe(),
+ Property(&NavigationParams::is_post, Eq(true)))))
+ .WillOnce(Return(false));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kTestUrl),
+ "POST",
+ REDIRECT_MODE_NO_REDIRECT,
+ web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+ web_contents()->GetRenderViewHost()->GetRoutingID(),
+ base::Unretained(&defer)));
+
+ // Wait for the request to finish processing.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(InterceptNavigationResourceThrottleTest,
+ CallbackIsPostFalseForPostConvertedToGetBy302) {
+ bool defer = false;
+
+ EXPECT_CALL(*mock_callback_receiver_,
+ ShouldIgnoreNavigation(_, AllOf(
+ NavigationParamsUrlIsSafe(),
+ Property(&NavigationParams::is_post, Eq(false)))))
+ .WillOnce(Return(false));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &InterceptNavigationResourceThrottleTest::
+ RunThrottleWillStartRequestOnIOThread,
+ base::Unretained(this),
+ GURL(kTestUrl),
+ "POST",
+ REDIRECT_MODE_302,
+ web_contents()->GetRenderViewHost()->GetProcess()->GetID(),
+ web_contents()->GetRenderViewHost()->GetRoutingID(),
+ base::Unretained(&defer)));
+
+ // Wait for the request to finish processing.
+ base::RunLoop().RunUntilIdle();
+}
+
+} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/navigation_params.cc b/chromium/components/navigation_interception/navigation_params.cc
new file mode 100644
index 00000000000..2bb0e94b58d
--- /dev/null
+++ b/chromium/components/navigation_interception/navigation_params.cc
@@ -0,0 +1,42 @@
+// 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/navigation_interception/navigation_params.h"
+
+namespace navigation_interception {
+
+NavigationParams::NavigationParams(const NavigationParams& other) {
+ Assign(other);
+}
+
+NavigationParams::NavigationParams(
+ const GURL& url,
+ const content::Referrer& referrer,
+ bool has_user_gesture,
+ bool is_post,
+ content::PageTransition transition_type,
+ bool is_redirect)
+ : url_(url),
+ referrer_(referrer),
+ has_user_gesture_(has_user_gesture),
+ is_post_(is_post),
+ transition_type_(transition_type),
+ is_redirect_(is_redirect) {
+}
+
+void NavigationParams::operator=(const NavigationParams& rhs) {
+ Assign(rhs);
+}
+
+void NavigationParams::Assign(const NavigationParams& other) {
+ url_ = other.url();
+ referrer_ = other.referrer();
+ has_user_gesture_ = other.has_user_gesture();
+ is_post_ = other.is_post();
+ transition_type_ = other.transition_type();
+ is_redirect_ = other.is_redirect();
+}
+
+} // namespace navigation_interception
+
diff --git a/chromium/components/navigation_interception/navigation_params.h b/chromium/components/navigation_interception/navigation_params.h
new file mode 100644
index 00000000000..0144a5af023
--- /dev/null
+++ b/chromium/components/navigation_interception/navigation_params.h
@@ -0,0 +1,47 @@
+// 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_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_
+#define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_
+
+#include "content/public/common/page_transition_types.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/common/referrer.h"
+#include "url/gurl.h"
+
+namespace navigation_interception {
+
+class NavigationParams {
+ public:
+ NavigationParams(const GURL& url,
+ const content::Referrer& referrer,
+ bool has_user_gesture,
+ bool is_post,
+ content::PageTransition page_transition_type,
+ bool is_redirect);
+ NavigationParams(const NavigationParams& other);
+ void operator=(const NavigationParams& rhs);
+
+ const GURL& url() const { return url_; }
+ GURL& url() { return url_; }
+ const content::Referrer& referrer() const { return referrer_; }
+ bool has_user_gesture() const { return has_user_gesture_; }
+ bool is_post() const { return is_post_; }
+ content::PageTransition transition_type() const { return transition_type_; }
+ bool is_redirect() const { return is_redirect_; }
+
+ private:
+ void Assign(const NavigationParams& other);
+
+ GURL url_;
+ content::Referrer referrer_;
+ bool has_user_gesture_;
+ bool is_post_;
+ content::PageTransition transition_type_;
+ bool is_redirect_;
+};
+
+} // namespace navigation_interception
+
+#endif // COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_
diff --git a/chromium/components/navigation_interception/navigation_params_android.cc b/chromium/components/navigation_interception/navigation_params_android.cc
new file mode 100644
index 00000000000..769450fa9d2
--- /dev/null
+++ b/chromium/components/navigation_interception/navigation_params_android.cc
@@ -0,0 +1,34 @@
+// 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/navigation_interception/navigation_params_android.h"
+
+#include "base/android/jni_string.h"
+#include "jni/NavigationParams_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+
+namespace navigation_interception {
+
+base::android::ScopedJavaLocalRef<jobject> CreateJavaNavigationParams(
+ JNIEnv* env,
+ const NavigationParams& params) {
+ ScopedJavaLocalRef<jstring> jstring_url =
+ ConvertUTF8ToJavaString(env, params.url().spec());
+
+ return Java_NavigationParams_create(env,
+ jstring_url.obj(),
+ params.is_post(),
+ params.has_user_gesture(),
+ params.transition_type(),
+ params.is_redirect());
+}
+
+// Register native methods.
+
+bool RegisterNavigationParams(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace navigation_interception
diff --git a/chromium/components/navigation_interception/navigation_params_android.h b/chromium/components/navigation_interception/navigation_params_android.h
new file mode 100644
index 00000000000..004d4927384
--- /dev/null
+++ b/chromium/components/navigation_interception/navigation_params_android.h
@@ -0,0 +1,22 @@
+// 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_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_
+#define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/navigation_interception/navigation_params.h"
+
+namespace navigation_interception {
+
+base::android::ScopedJavaLocalRef<jobject> CreateJavaNavigationParams(
+ JNIEnv* env,
+ const NavigationParams& params);
+
+bool RegisterNavigationParams(JNIEnv* env);
+
+} // namespace navigation_interception
+
+#endif // COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_ANDROID_H_
diff --git a/chromium/components/policy.gypi b/chromium/components/policy.gypi
new file mode 100644
index 00000000000..5295ba46e96
--- /dev/null
+++ b/chromium/components/policy.gypi
@@ -0,0 +1,41 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'policy_component',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'json_schema',
+ ],
+ 'defines': [
+ 'POLICY_COMPONENT_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'conditions': [
+ ['configuration_policy==1', {
+ 'sources': [
+ 'policy/core/common/policy_schema.cc',
+ 'policy/core/common/policy_schema.h',
+ 'policy/policy_export.h',
+ ],
+ }, { # configuration_policy==0
+ # The target 'policy_component' always exists. Later it will include
+ # some stubs when configuration_policy==0. For now this stub file is
+ # compiled so that an output is produced, otherwise the shared build
+ # breaks on iOS.
+ # TODO(joaodasilva): remove this comment and the temporary stub after
+ # moving one of the real stubs. http://crbug.com/271392
+ 'sources': [
+ 'policy/stub_to_remove.cc',
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/policy/OWNERS b/chromium/components/policy/OWNERS
new file mode 100644
index 00000000000..dbccb7c3a0c
--- /dev/null
+++ b/chromium/components/policy/OWNERS
@@ -0,0 +1,6 @@
+mnissler@chromium.org
+pastarmovj@chromium.org
+joaodasilva@chromium.org
+bartfab@chromium.org
+atwilson@chromium.org
+pneubeck@chromium.org
diff --git a/chromium/components/policy/core/common/policy_schema.cc b/chromium/components/policy/core/common/policy_schema.cc
new file mode 100644
index 00000000000..8c3145ce43a
--- /dev/null
+++ b/chromium/components/policy/core/common/policy_schema.cc
@@ -0,0 +1,244 @@
+// 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/policy/core/common/policy_schema.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "components/json_schema/json_schema_validator.h"
+
+namespace policy {
+
+namespace {
+
+const char kJSONSchemaVersion[] = "http://json-schema.org/draft-03/schema#";
+
+// Describes the properties of a TYPE_DICTIONARY policy schema.
+class DictionaryPolicySchema : public PolicySchema {
+ public:
+ static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema,
+ std::string* error);
+
+ virtual ~DictionaryPolicySchema();
+
+ virtual const PolicySchemaMap* GetProperties() const OVERRIDE;
+ virtual const PolicySchema* GetSchemaForAdditionalProperties() const OVERRIDE;
+
+ private:
+ DictionaryPolicySchema();
+
+ PolicySchemaMap properties_;
+ scoped_ptr<PolicySchema> additional_properties_;
+
+ DISALLOW_COPY_AND_ASSIGN(DictionaryPolicySchema);
+};
+
+// Describes the items of a TYPE_LIST policy schema.
+class ListPolicySchema : public PolicySchema {
+ public:
+ static scoped_ptr<PolicySchema> Parse(const base::DictionaryValue& schema,
+ std::string* error);
+
+ virtual ~ListPolicySchema();
+
+ virtual const PolicySchema* GetSchemaForItems() const OVERRIDE;
+
+ private:
+ ListPolicySchema();
+
+ scoped_ptr<PolicySchema> items_schema_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListPolicySchema);
+};
+
+bool SchemaTypeToValueType(const std::string& type_string,
+ base::Value::Type* type) {
+ // Note: "any" is not an accepted type.
+ static const struct {
+ const char* schema_type;
+ base::Value::Type value_type;
+ } kSchemaToValueTypeMap[] = {
+ { json_schema_constants::kArray, base::Value::TYPE_LIST },
+ { json_schema_constants::kBoolean, base::Value::TYPE_BOOLEAN },
+ { json_schema_constants::kInteger, base::Value::TYPE_INTEGER },
+ { json_schema_constants::kNull, base::Value::TYPE_NULL },
+ { json_schema_constants::kNumber, base::Value::TYPE_DOUBLE },
+ { json_schema_constants::kObject, base::Value::TYPE_DICTIONARY },
+ { json_schema_constants::kString, base::Value::TYPE_STRING },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSchemaToValueTypeMap); ++i) {
+ if (kSchemaToValueTypeMap[i].schema_type == type_string) {
+ *type = kSchemaToValueTypeMap[i].value_type;
+ return true;
+ }
+ }
+ return false;
+}
+
+scoped_ptr<PolicySchema> ParseSchema(const base::DictionaryValue& schema,
+ std::string* error) {
+ std::string type_string;
+ if (!schema.GetString(json_schema_constants::kType, &type_string)) {
+ *error = "The schema type must be declared.";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ base::Value::Type type = base::Value::TYPE_NULL;
+ if (!SchemaTypeToValueType(type_string, &type)) {
+ *error = "The \"any\" type can't be used.";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ switch (type) {
+ case base::Value::TYPE_DICTIONARY:
+ return DictionaryPolicySchema::Parse(schema, error);
+ case base::Value::TYPE_LIST:
+ return ListPolicySchema::Parse(schema, error);
+ default:
+ return make_scoped_ptr(new PolicySchema(type));
+ }
+}
+
+DictionaryPolicySchema::DictionaryPolicySchema()
+ : PolicySchema(base::Value::TYPE_DICTIONARY) {}
+
+DictionaryPolicySchema::~DictionaryPolicySchema() {
+ STLDeleteValues(&properties_);
+}
+
+const PolicySchemaMap* DictionaryPolicySchema::GetProperties() const {
+ return &properties_;
+}
+
+const PolicySchema*
+ DictionaryPolicySchema::GetSchemaForAdditionalProperties() const {
+ return additional_properties_.get();
+}
+
+// static
+scoped_ptr<PolicySchema> DictionaryPolicySchema::Parse(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ scoped_ptr<DictionaryPolicySchema> dict_schema(new DictionaryPolicySchema());
+
+ const base::DictionaryValue* dict = NULL;
+ const base::DictionaryValue* properties = NULL;
+ if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
+ for (base::DictionaryValue::Iterator it(*properties);
+ !it.IsAtEnd(); it.Advance()) {
+ // This should have been verified by the JSONSchemaValidator.
+ CHECK(it.value().GetAsDictionary(&dict));
+ scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error);
+ if (!sub_schema)
+ return scoped_ptr<PolicySchema>();
+ dict_schema->properties_[it.key()] = sub_schema.release();
+ }
+ }
+
+ if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
+ &dict)) {
+ scoped_ptr<PolicySchema> sub_schema = ParseSchema(*dict, error);
+ if (!sub_schema)
+ return scoped_ptr<PolicySchema>();
+ dict_schema->additional_properties_ = sub_schema.Pass();
+ }
+
+ return dict_schema.PassAs<PolicySchema>();
+}
+
+ListPolicySchema::ListPolicySchema()
+ : PolicySchema(base::Value::TYPE_LIST) {}
+
+ListPolicySchema::~ListPolicySchema() {}
+
+const PolicySchema* ListPolicySchema::GetSchemaForItems() const {
+ return items_schema_.get();
+}
+
+scoped_ptr<PolicySchema> ListPolicySchema::Parse(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ const base::DictionaryValue* dict = NULL;
+ if (!schema.GetDictionary(json_schema_constants::kItems, &dict)) {
+ *error = "Arrays must declare a single schema for their items.";
+ return scoped_ptr<PolicySchema>();
+ }
+ scoped_ptr<PolicySchema> items_schema = ParseSchema(*dict, error);
+ if (!items_schema)
+ return scoped_ptr<PolicySchema>();
+
+ scoped_ptr<ListPolicySchema> list_schema(new ListPolicySchema());
+ list_schema->items_schema_ = items_schema.Pass();
+ return list_schema.PassAs<PolicySchema>();
+}
+
+} // namespace
+
+PolicySchema::PolicySchema(base::Value::Type type)
+ : type_(type) {}
+
+PolicySchema::~PolicySchema() {}
+
+const PolicySchemaMap* PolicySchema::GetProperties() const {
+ NOTREACHED();
+ return NULL;
+}
+
+const PolicySchema* PolicySchema::GetSchemaForAdditionalProperties() const {
+ NOTREACHED();
+ return NULL;
+}
+
+const PolicySchema* PolicySchema::GetSchemaForProperty(
+ const std::string& key) const {
+ const PolicySchemaMap* properties = GetProperties();
+ PolicySchemaMap::const_iterator it = properties->find(key);
+ return it == properties->end() ? GetSchemaForAdditionalProperties()
+ : it->second;
+}
+
+const PolicySchema* PolicySchema::GetSchemaForItems() const {
+ NOTREACHED();
+ return NULL;
+}
+
+// static
+scoped_ptr<PolicySchema> PolicySchema::Parse(const std::string& content,
+ std::string* error) {
+ // Validate as a generic JSON schema.
+ scoped_ptr<base::DictionaryValue> dict =
+ JSONSchemaValidator::IsValidSchema(content, error);
+ if (!dict)
+ return scoped_ptr<PolicySchema>();
+
+ // Validate the schema version.
+ std::string string_value;
+ if (!dict->GetString(json_schema_constants::kSchema, &string_value) ||
+ string_value != kJSONSchemaVersion) {
+ *error = "Must declare JSON Schema v3 version in \"$schema\".";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ // Validate the main type.
+ if (!dict->GetString(json_schema_constants::kType, &string_value) ||
+ string_value != json_schema_constants::kObject) {
+ *error =
+ "The main schema must have a type attribute with \"object\" value.";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ // Checks for invalid attributes at the top-level.
+ if (dict->HasKey(json_schema_constants::kAdditionalProperties) ||
+ dict->HasKey(json_schema_constants::kPatternProperties)) {
+ *error = "\"additionalProperties\" and \"patternProperties\" are not "
+ "supported at the main schema.";
+ return scoped_ptr<PolicySchema>();
+ }
+
+ return ParseSchema(*dict, error);
+}
+
+} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_schema.h b/chromium/components/policy/core/common/policy_schema.h
new file mode 100644
index 00000000000..c48ee6fc2b9
--- /dev/null
+++ b/chromium/components/policy/core/common/policy_schema.h
@@ -0,0 +1,70 @@
+// 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_POLICY_CORE_COMMON_POLICY_SCHEMA_H_
+#define COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEMA_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+
+class PolicySchema;
+typedef std::map<std::string, PolicySchema*> PolicySchemaMap;
+
+// Maps known policy keys to their expected types, and recursively describes
+// the known keys within dictionary or list types.
+class POLICY_EXPORT PolicySchema {
+ public:
+
+ // Parses |schema| as a JSON v3 schema, and additionally verifies that:
+ // - the version is JSON schema v3;
+ // - the top-level entry is of type "object";
+ // - the top-level object doesn't contain "additionalProperties" nor
+ // "patternProperties";
+ // - each "property" maps to a schema with one "type";
+ // - the type "any" is not used.
+ // If all the checks pass then the parsed PolicySchema is returned; otherwise
+ // returns NULL.
+ static scoped_ptr<PolicySchema> Parse(const std::string& schema,
+ std::string* error);
+
+ explicit PolicySchema(base::Value::Type type);
+ virtual ~PolicySchema();
+
+ // Returns the expected type for this policy. At the top-level PolicySchema
+ // this is always TYPE_DICTIONARY.
+ base::Value::Type type() const { return type_; }
+
+ // It is invalid to call these methods when type() is not TYPE_DICTIONARY.
+ //
+ // GetProperties() returns a map of the known property names to their schemas;
+ // the map is never NULL.
+ // GetSchemaForAdditionalProperties() returns the schema that should be used
+ // for keys not found in the map, and may be NULL.
+ // GetSchemaForProperty() is a utility method that combines both, returning
+ // the mapped schema if found in GetProperties(), otherwise returning
+ // GetSchemaForAdditionalProperties().
+ virtual const PolicySchemaMap* GetProperties() const;
+ virtual const PolicySchema* GetSchemaForAdditionalProperties() const;
+ const PolicySchema* GetSchemaForProperty(const std::string& key) const;
+
+ // It is invalid to call this method when type() is not TYPE_LIST.
+ // Returns the type of the entries of this "array", which is never NULL.
+ virtual const PolicySchema* GetSchemaForItems() const;
+
+ private:
+ const base::Value::Type type_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicySchema);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_POLICY_SCHEMA_H_
diff --git a/chromium/components/policy/core/common/policy_schema_unittest.cc b/chromium/components/policy/core/common/policy_schema_unittest.cc
new file mode 100644
index 00000000000..bfbdd652159
--- /dev/null
+++ b/chromium/components/policy/core/common/policy_schema_unittest.cc
@@ -0,0 +1,193 @@
+// 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/policy/core/common/policy_schema.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+#define SCHEMA_VERSION "\"$schema\":\"http://json-schema.org/draft-03/schema#\""
+#define OBJECT_TYPE "\"type\":\"object\""
+
+bool ParseFails(const std::string& content) {
+ std::string error;
+ scoped_ptr<PolicySchema> schema = PolicySchema::Parse(content, &error);
+ EXPECT_TRUE(schema || !error.empty());
+ return !schema;
+}
+
+} // namespace
+
+TEST(PolicySchemaTest, MinimalSchema) {
+ EXPECT_FALSE(ParseFails(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE
+ "}"));
+}
+
+TEST(PolicySchemaTest, InvalidSchemas) {
+ EXPECT_TRUE(ParseFails(""));
+ EXPECT_TRUE(ParseFails("omg"));
+ EXPECT_TRUE(ParseFails("\"omg\""));
+ EXPECT_TRUE(ParseFails("123"));
+ EXPECT_TRUE(ParseFails("[]"));
+ EXPECT_TRUE(ParseFails("null"));
+ EXPECT_TRUE(ParseFails("{}"));
+ EXPECT_TRUE(ParseFails("{" SCHEMA_VERSION "}"));
+ EXPECT_TRUE(ParseFails("{" OBJECT_TYPE "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE ","
+ "\"additionalProperties\": { \"type\":\"object\" }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE ","
+ "\"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": \"any\" } }"
+ "}"));
+}
+
+TEST(PolicySchemaTest, ValidSchema) {
+ std::string error;
+ scoped_ptr<PolicySchema> schema = PolicySchema::Parse(
+ "{"
+ SCHEMA_VERSION ","
+ OBJECT_TYPE ","
+ "\"properties\": {"
+ " \"Boolean\": { \"type\": \"boolean\" },"
+ " \"Integer\": { \"type\": \"integer\" },"
+ " \"Null\": { \"type\": \"null\" },"
+ " \"Number\": { \"type\": \"number\" },"
+ " \"String\": { \"type\": \"string\" },"
+ " \"Array\": {"
+ " \"type\": \"array\","
+ " \"items\": { \"type\": \"string\" }"
+ " },"
+ " \"ArrayOfObjects\": {"
+ " \"type\": \"array\","
+ " \"items\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"one\": { \"type\": \"string\" },"
+ " \"two\": { \"type\": \"integer\" }"
+ " }"
+ " }"
+ " },"
+ " \"ArrayOfArray\": {"
+ " \"type\": \"array\","
+ " \"items\": {"
+ " \"type\": \"array\","
+ " \"items\": { \"type\": \"string\" }"
+ " }"
+ " },"
+ " \"Object\": {"
+ " \"type\": \"object\","
+ " \"properties\": {"
+ " \"one\": { \"type\": \"boolean\" },"
+ " \"two\": { \"type\": \"integer\" }"
+ " },"
+ " \"additionalProperties\": { \"type\": \"string\" }"
+ " }"
+ "}"
+ "}", &error);
+ ASSERT_TRUE(schema) << error;
+
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema->type());
+ EXPECT_FALSE(schema->GetSchemaForProperty("invalid"));
+
+ const PolicySchema* sub = schema->GetSchemaForProperty("Boolean");
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub->type());
+
+ sub = schema->GetSchemaForProperty("Integer");
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, sub->type());
+
+ sub = schema->GetSchemaForProperty("Null");
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_NULL, sub->type());
+
+ sub = schema->GetSchemaForProperty("Number");
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_DOUBLE, sub->type());
+ sub = schema->GetSchemaForProperty("String");
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_STRING, sub->type());
+
+ sub = schema->GetSchemaForProperty("Array");
+ ASSERT_TRUE(sub);
+ ASSERT_EQ(base::Value::TYPE_LIST, sub->type());
+ sub = sub->GetSchemaForItems();
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_STRING, sub->type());
+
+ sub = schema->GetSchemaForProperty("ArrayOfObjects");
+ ASSERT_TRUE(sub);
+ ASSERT_EQ(base::Value::TYPE_LIST, sub->type());
+ sub = sub->GetSchemaForItems();
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub->type());
+ const PolicySchema* subsub = sub->GetSchemaForProperty("one");
+ ASSERT_TRUE(subsub);
+ EXPECT_EQ(base::Value::TYPE_STRING, subsub->type());
+ subsub = sub->GetSchemaForProperty("two");
+ ASSERT_TRUE(subsub);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, subsub->type());
+ subsub = sub->GetSchemaForProperty("invalid");
+ EXPECT_FALSE(subsub);
+
+ sub = schema->GetSchemaForProperty("ArrayOfArray");
+ ASSERT_TRUE(sub);
+ ASSERT_EQ(base::Value::TYPE_LIST, sub->type());
+ sub = sub->GetSchemaForItems();
+ ASSERT_TRUE(sub);
+ ASSERT_EQ(base::Value::TYPE_LIST, sub->type());
+ sub = sub->GetSchemaForItems();
+ ASSERT_TRUE(sub);
+ EXPECT_EQ(base::Value::TYPE_STRING, sub->type());
+
+ sub = schema->GetSchemaForProperty("Object");
+ ASSERT_TRUE(sub);
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub->type());
+ subsub = sub->GetSchemaForProperty("one");
+ ASSERT_TRUE(subsub);
+ EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub->type());
+ subsub = sub->GetSchemaForProperty("two");
+ ASSERT_TRUE(subsub);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, subsub->type());
+ subsub = sub->GetSchemaForProperty("undeclared");
+ ASSERT_TRUE(subsub);
+ EXPECT_EQ(base::Value::TYPE_STRING, subsub->type());
+}
+
+} // namespace policy
diff --git a/chromium/components/policy/policy_export.h b/chromium/components/policy/policy_export.h
new file mode 100644
index 00000000000..47376acaa85
--- /dev/null
+++ b/chromium/components/policy/policy_export.h
@@ -0,0 +1,29 @@
+// 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_POLICY_POLICY_EXPORT_H_
+#define COMPONENTS_POLICY_POLICY_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(POLICY_COMPONENT_IMPLEMENTATION)
+#define POLICY_EXPORT __declspec(dllexport)
+#else
+#define POLICY_EXPORT __declspec(dllimport)
+#endif // defined(BASE_PREFS_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(POLICY_COMPONENT_IMPLEMENTATION)
+#define POLICY_EXPORT __attribute__((visibility("default")))
+#else
+#define POLICY_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define POLICY_EXPORT
+#endif
+
+#endif // COMPONENTS_POLICY_POLICY_EXPORT_H_
diff --git a/chromium/components/policy/stub_to_remove.cc b/chromium/components/policy/stub_to_remove.cc
new file mode 100644
index 00000000000..6e352ace9cd
--- /dev/null
+++ b/chromium/components/policy/stub_to_remove.cc
@@ -0,0 +1,6 @@
+// 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.
+
+// TODO(joaodasilva): remove this file and update the comment on policy.gypi.
+// http://crbug.com/271392
diff --git a/chromium/components/sessions.gypi b/chromium/components/sessions.gypi
new file mode 100644
index 00000000000..401015eea04
--- /dev/null
+++ b/chromium/components/sessions.gypi
@@ -0,0 +1,64 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'sessions',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../skia/skia.gyp:skia',
+ '../third_party/protobuf/protobuf.gyp:protobuf_lite',
+ '../url/url.gyp:url_lib',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'defines': [
+ 'SESSIONS_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'sessions/serialized_navigation_entry.cc',
+ 'sessions/serialized_navigation_entry.h',
+ ],
+ 'conditions': [
+ ['OS != "ios" and chrome_multiple_dll != 1', {
+ 'dependencies': [
+ '../webkit/support/webkit_support.gyp:glue',
+ ]
+ }],
+ ['android_webview_build == 0', {
+ 'dependencies': [
+ '../sync/sync.gyp:sync',
+ ]
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sessions_test_support',
+ 'type': 'static_library',
+ 'defines!': ['SESSIONS_IMPLEMENTATION'],
+ 'dependencies': [
+ '../skia/skia.gyp:skia',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'sessions/serialized_navigation_entry_test_helper.cc',
+ 'sessions/serialized_navigation_entry_test_helper.h',
+ ],
+ 'conditions': [
+ ['android_webview_build == 0', {
+ 'dependencies': [
+ '../sync/sync.gyp:sync',
+ ]
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/sessions/DEPS b/chromium/components/sessions/DEPS
new file mode 100644
index 00000000000..fd2f190d0a5
--- /dev/null
+++ b/chromium/components/sessions/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+content/public/browser",
+ "+content/public/common",
+ "+sync",
+ "+third_party/WebKit/public/platform",
+ "+webkit",
+]
diff --git a/chromium/components/sessions/OWNERS b/chromium/components/sessions/OWNERS
new file mode 100644
index 00000000000..73674b786cb
--- /dev/null
+++ b/chromium/components/sessions/OWNERS
@@ -0,0 +1,2 @@
+marja@chromium.org
+sky@chromium.org
diff --git a/chromium/components/sessions/serialized_navigation_entry.cc b/chromium/components/sessions/serialized_navigation_entry.cc
new file mode 100644
index 00000000000..5e4183cf5d2
--- /dev/null
+++ b/chromium/components/sessions/serialized_navigation_entry.cc
@@ -0,0 +1,475 @@
+// 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/sessions/serialized_navigation_entry.h"
+
+#include "base/pickle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/browser/favicon_status.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/navigation_entry.h"
+#include "sync/protocol/session_specifics.pb.h"
+#include "sync/util/time.h"
+#include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
+
+using content::NavigationEntry;
+
+namespace sessions {
+
+const char kSearchTermsKey[] = "search_terms";
+
+SerializedNavigationEntry::SerializedNavigationEntry()
+ : index_(-1),
+ unique_id_(0),
+ transition_type_(content::PAGE_TRANSITION_TYPED),
+ has_post_data_(false),
+ post_id_(-1),
+ is_overriding_user_agent_(false),
+ blocked_state_(STATE_INVALID) {}
+
+SerializedNavigationEntry::~SerializedNavigationEntry() {}
+
+// static
+SerializedNavigationEntry SerializedNavigationEntry::FromNavigationEntry(
+ int index,
+ const NavigationEntry& entry) {
+ SerializedNavigationEntry navigation;
+ navigation.index_ = index;
+ navigation.unique_id_ = entry.GetUniqueID();
+ navigation.referrer_ = entry.GetReferrer();
+ navigation.virtual_url_ = entry.GetVirtualURL();
+ navigation.title_ = entry.GetTitle();
+ navigation.page_state_ = entry.GetPageState();
+ navigation.transition_type_ = entry.GetTransitionType();
+ navigation.has_post_data_ = entry.GetHasPostData();
+ navigation.post_id_ = entry.GetPostID();
+ navigation.original_request_url_ = entry.GetOriginalRequestURL();
+ navigation.is_overriding_user_agent_ = entry.GetIsOverridingUserAgent();
+ navigation.timestamp_ = entry.GetTimestamp();
+ // If you want to navigate a named frame in Chrome, you will first need to
+ // add support for persisting it. It is currently only used for layout tests.
+ CHECK(entry.GetFrameToNavigate().empty());
+ entry.GetExtraData(kSearchTermsKey, &navigation.search_terms_);
+ if (entry.GetFavicon().valid)
+ navigation.favicon_url_ = entry.GetFavicon().url;
+
+ return navigation;
+}
+
+SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
+ int index,
+ const sync_pb::TabNavigation& sync_data) {
+ SerializedNavigationEntry navigation;
+ navigation.index_ = index;
+ navigation.unique_id_ = sync_data.unique_id();
+ navigation.referrer_ =
+ content::Referrer(GURL(sync_data.referrer()),
+ WebKit::WebReferrerPolicyDefault);
+ navigation.virtual_url_ = GURL(sync_data.virtual_url());
+ navigation.title_ = UTF8ToUTF16(sync_data.title());
+ navigation.page_state_ =
+ content::PageState::CreateFromEncodedData(sync_data.state());
+
+ uint32 transition = 0;
+ if (sync_data.has_page_transition()) {
+ switch (sync_data.page_transition()) {
+ case sync_pb::SyncEnums_PageTransition_LINK:
+ transition = content::PAGE_TRANSITION_LINK;
+ break;
+ case sync_pb::SyncEnums_PageTransition_TYPED:
+ transition = content::PAGE_TRANSITION_TYPED;
+ break;
+ case sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK:
+ transition = content::PAGE_TRANSITION_AUTO_BOOKMARK;
+ break;
+ case sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME:
+ transition = content::PAGE_TRANSITION_AUTO_SUBFRAME;
+ break;
+ case sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME:
+ transition = content::PAGE_TRANSITION_MANUAL_SUBFRAME;
+ break;
+ case sync_pb::SyncEnums_PageTransition_GENERATED:
+ transition = content::PAGE_TRANSITION_GENERATED;
+ break;
+ case sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL:
+ transition = content::PAGE_TRANSITION_AUTO_TOPLEVEL;
+ break;
+ case sync_pb::SyncEnums_PageTransition_FORM_SUBMIT:
+ transition = content::PAGE_TRANSITION_FORM_SUBMIT;
+ break;
+ case sync_pb::SyncEnums_PageTransition_RELOAD:
+ transition = content::PAGE_TRANSITION_RELOAD;
+ break;
+ case sync_pb::SyncEnums_PageTransition_KEYWORD:
+ transition = content::PAGE_TRANSITION_KEYWORD;
+ break;
+ case sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED:
+ transition =
+ content::PAGE_TRANSITION_KEYWORD_GENERATED;
+ break;
+ default:
+ transition = content::PAGE_TRANSITION_LINK;
+ break;
+ }
+ }
+
+ if (sync_data.has_redirect_type()) {
+ switch (sync_data.redirect_type()) {
+ case sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT:
+ transition |= content::PAGE_TRANSITION_CLIENT_REDIRECT;
+ break;
+ case sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT:
+ transition |= content::PAGE_TRANSITION_SERVER_REDIRECT;
+ break;
+ }
+ }
+ if (sync_data.navigation_forward_back())
+ transition |= content::PAGE_TRANSITION_FORWARD_BACK;
+ if (sync_data.navigation_from_address_bar())
+ transition |= content::PAGE_TRANSITION_FROM_ADDRESS_BAR;
+ if (sync_data.navigation_home_page())
+ transition |= content::PAGE_TRANSITION_HOME_PAGE;
+ if (sync_data.navigation_chain_start())
+ transition |= content::PAGE_TRANSITION_CHAIN_START;
+ if (sync_data.navigation_chain_end())
+ transition |= content::PAGE_TRANSITION_CHAIN_END;
+
+ navigation.transition_type_ =
+ static_cast<content::PageTransition>(transition);
+
+ navigation.timestamp_ = base::Time();
+ navigation.search_terms_ = UTF8ToUTF16(sync_data.search_terms());
+ if (sync_data.has_favicon_url())
+ navigation.favicon_url_ = GURL(sync_data.favicon_url());
+
+ // We shouldn't sync session data for managed users down at the moment.
+ DCHECK(!sync_data.has_blocked_state());
+ DCHECK_EQ(0, sync_data.content_pack_categories_size());
+
+ return navigation;
+}
+
+namespace {
+
+// Helper used by SerializedNavigationEntry::WriteToPickle(). It writes |str| to
+// |pickle|, if and only if |str| fits within (|max_bytes| -
+// |*bytes_written|). |bytes_written| is incremented to reflect the
+// data written.
+//
+// TODO(akalin): Unify this with the same function in
+// base_session_service.cc.
+void WriteStringToPickle(Pickle* pickle,
+ int* bytes_written,
+ int max_bytes,
+ const std::string& str) {
+ int num_bytes = str.size() * sizeof(char);
+ if (*bytes_written + num_bytes < max_bytes) {
+ *bytes_written += num_bytes;
+ pickle->WriteString(str);
+ } else {
+ pickle->WriteString(std::string());
+ }
+}
+
+// string16 version of WriteStringToPickle.
+//
+// TODO(akalin): Unify this, too.
+void WriteString16ToPickle(Pickle* pickle,
+ int* bytes_written,
+ int max_bytes,
+ const string16& str) {
+ int num_bytes = str.size() * sizeof(char16);
+ if (*bytes_written + num_bytes < max_bytes) {
+ *bytes_written += num_bytes;
+ pickle->WriteString16(str);
+ } else {
+ pickle->WriteString16(string16());
+ }
+}
+
+// A mask used for arbitrary boolean values needed to represent a
+// NavigationEntry. Currently only contains HAS_POST_DATA.
+//
+// NOTE(akalin): We may want to just serialize |has_post_data_|
+// directly. Other bools (|is_overriding_user_agent_|) haven't been
+// added to this mask.
+enum TypeMask {
+ HAS_POST_DATA = 1
+};
+
+} // namespace
+
+// Pickle order:
+//
+// index_
+// virtual_url_
+// title_
+// page_state_
+// transition_type_
+//
+// Added on later:
+//
+// type_mask (has_post_data_)
+// referrer_
+// original_request_url_
+// is_overriding_user_agent_
+// timestamp_
+// search_terms_
+
+void SerializedNavigationEntry::WriteToPickle(int max_size,
+ Pickle* pickle) const {
+ pickle->WriteInt(index_);
+
+ int bytes_written = 0;
+
+ WriteStringToPickle(pickle, &bytes_written, max_size,
+ virtual_url_.spec());
+
+ WriteString16ToPickle(pickle, &bytes_written, max_size, title_);
+
+ content::PageState page_state = page_state_;
+ if (has_post_data_)
+ page_state = page_state.RemovePasswordData();
+
+ WriteStringToPickle(pickle, &bytes_written, max_size,
+ page_state.ToEncodedData());
+
+ pickle->WriteInt(transition_type_);
+
+ const int type_mask = has_post_data_ ? HAS_POST_DATA : 0;
+ pickle->WriteInt(type_mask);
+
+ WriteStringToPickle(
+ pickle, &bytes_written, max_size,
+ referrer_.url.is_valid() ? referrer_.url.spec() : std::string());
+
+ pickle->WriteInt(referrer_.policy);
+
+ // Save info required to override the user agent.
+ WriteStringToPickle(
+ pickle, &bytes_written, max_size,
+ original_request_url_.is_valid() ?
+ original_request_url_.spec() : std::string());
+ pickle->WriteBool(is_overriding_user_agent_);
+ pickle->WriteInt64(timestamp_.ToInternalValue());
+
+ WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
+}
+
+bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
+ *this = SerializedNavigationEntry();
+ std::string virtual_url_spec, page_state_data;
+ int transition_type_int = 0;
+ if (!iterator->ReadInt(&index_) ||
+ !iterator->ReadString(&virtual_url_spec) ||
+ !iterator->ReadString16(&title_) ||
+ !iterator->ReadString(&page_state_data) ||
+ !iterator->ReadInt(&transition_type_int))
+ return false;
+ virtual_url_ = GURL(virtual_url_spec);
+ page_state_ = content::PageState::CreateFromEncodedData(page_state_data);
+ transition_type_ = static_cast<content::PageTransition>(transition_type_int);
+
+ // type_mask did not always exist in the written stream. As such, we
+ // don't fail if it can't be read.
+ int type_mask = 0;
+ bool has_type_mask = iterator->ReadInt(&type_mask);
+
+ if (has_type_mask) {
+ has_post_data_ = type_mask & HAS_POST_DATA;
+ // the "referrer" property was added after type_mask to the written
+ // stream. As such, we don't fail if it can't be read.
+ std::string referrer_spec;
+ if (!iterator->ReadString(&referrer_spec))
+ referrer_spec = std::string();
+ // The "referrer policy" property was added even later, so we fall back to
+ // the default policy if the property is not present.
+ int policy_int;
+ WebKit::WebReferrerPolicy policy;
+ if (iterator->ReadInt(&policy_int))
+ policy = static_cast<WebKit::WebReferrerPolicy>(policy_int);
+ else
+ policy = WebKit::WebReferrerPolicyDefault;
+ referrer_ = content::Referrer(GURL(referrer_spec), policy);
+
+ // If the original URL can't be found, leave it empty.
+ std::string original_request_url_spec;
+ if (!iterator->ReadString(&original_request_url_spec))
+ original_request_url_spec = std::string();
+ original_request_url_ = GURL(original_request_url_spec);
+
+ // Default to not overriding the user agent if we don't have info.
+ if (!iterator->ReadBool(&is_overriding_user_agent_))
+ is_overriding_user_agent_ = false;
+
+ int64 timestamp_internal_value = 0;
+ if (iterator->ReadInt64(&timestamp_internal_value)) {
+ timestamp_ = base::Time::FromInternalValue(timestamp_internal_value);
+ } else {
+ timestamp_ = base::Time();
+ }
+
+ // If the search terms field can't be found, leave it empty.
+ if (!iterator->ReadString16(&search_terms_))
+ search_terms_.clear();
+ }
+
+ return true;
+}
+
+scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
+ int page_id,
+ content::BrowserContext* browser_context) const {
+ scoped_ptr<NavigationEntry> entry(
+ content::NavigationController::CreateNavigationEntry(
+ virtual_url_,
+ referrer_,
+ // Use a transition type of reload so that we don't incorrectly
+ // increase the typed count.
+ content::PAGE_TRANSITION_RELOAD,
+ false,
+ // The extra headers are not sync'ed across sessions.
+ std::string(),
+ browser_context));
+
+ entry->SetTitle(title_);
+ entry->SetPageState(page_state_);
+ entry->SetPageID(page_id);
+ entry->SetHasPostData(has_post_data_);
+ entry->SetPostID(post_id_);
+ entry->SetOriginalRequestURL(original_request_url_);
+ entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
+ entry->SetTimestamp(timestamp_);
+ entry->SetExtraData(kSearchTermsKey, search_terms_);
+
+ // These fields should have default values.
+ DCHECK_EQ(STATE_INVALID, blocked_state_);
+ DCHECK_EQ(0u, content_pack_categories_.size());
+
+ return entry.Pass();
+}
+
+// TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
+// See http://crbug.com/67068.
+sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
+ sync_pb::TabNavigation sync_data;
+ sync_data.set_virtual_url(virtual_url_.spec());
+ // FIXME(zea): Support referrer policy?
+ sync_data.set_referrer(referrer_.url.spec());
+ sync_data.set_title(UTF16ToUTF8(title_));
+
+ // Page transition core.
+ COMPILE_ASSERT(content::PAGE_TRANSITION_LAST_CORE ==
+ content::PAGE_TRANSITION_KEYWORD_GENERATED,
+ PageTransitionCoreBounds);
+ switch (PageTransitionStripQualifier(transition_type_)) {
+ case content::PAGE_TRANSITION_LINK:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_LINK);
+ break;
+ case content::PAGE_TRANSITION_TYPED:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_TYPED);
+ break;
+ case content::PAGE_TRANSITION_AUTO_BOOKMARK:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_AUTO_BOOKMARK);
+ break;
+ case content::PAGE_TRANSITION_AUTO_SUBFRAME:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
+ break;
+ case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_MANUAL_SUBFRAME);
+ break;
+ case content::PAGE_TRANSITION_GENERATED:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_GENERATED);
+ break;
+ case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_AUTO_TOPLEVEL);
+ break;
+ case content::PAGE_TRANSITION_FORM_SUBMIT:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_FORM_SUBMIT);
+ break;
+ case content::PAGE_TRANSITION_RELOAD:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_RELOAD);
+ break;
+ case content::PAGE_TRANSITION_KEYWORD:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_KEYWORD);
+ break;
+ case content::PAGE_TRANSITION_KEYWORD_GENERATED:
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_KEYWORD_GENERATED);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // Page transition qualifiers.
+ if (PageTransitionIsRedirect(transition_type_)) {
+ if (transition_type_ & content::PAGE_TRANSITION_CLIENT_REDIRECT) {
+ sync_data.set_redirect_type(
+ sync_pb::SyncEnums_PageTransitionRedirectType_CLIENT_REDIRECT);
+ } else if (transition_type_ & content::PAGE_TRANSITION_SERVER_REDIRECT) {
+ sync_data.set_redirect_type(
+ sync_pb::SyncEnums_PageTransitionRedirectType_SERVER_REDIRECT);
+ }
+ }
+ sync_data.set_navigation_forward_back(
+ (transition_type_ & content::PAGE_TRANSITION_FORWARD_BACK) != 0);
+ sync_data.set_navigation_from_address_bar(
+ (transition_type_ & content::PAGE_TRANSITION_FROM_ADDRESS_BAR) != 0);
+ sync_data.set_navigation_home_page(
+ (transition_type_ & content::PAGE_TRANSITION_HOME_PAGE) != 0);
+ sync_data.set_navigation_chain_start(
+ (transition_type_ & content::PAGE_TRANSITION_CHAIN_START) != 0);
+ sync_data.set_navigation_chain_end(
+ (transition_type_ & content::PAGE_TRANSITION_CHAIN_END) != 0);
+
+ sync_data.set_unique_id(unique_id_);
+ sync_data.set_timestamp_msec(syncer::TimeToProtoTime(timestamp_));
+ // The full-resolution timestamp works as a global ID.
+ sync_data.set_global_id(timestamp_.ToInternalValue());
+
+ sync_data.set_search_terms(UTF16ToUTF8(search_terms_));
+
+ if (favicon_url_.is_valid())
+ sync_data.set_favicon_url(favicon_url_.spec());
+
+ if (blocked_state_ != STATE_INVALID) {
+ sync_data.set_blocked_state(
+ static_cast<sync_pb::TabNavigation_BlockedState>(blocked_state_));
+ }
+
+ for (std::set<std::string>::const_iterator it =
+ content_pack_categories_.begin();
+ it != content_pack_categories_.end(); ++it) {
+ sync_data.add_content_pack_categories(*it);
+ }
+
+ return sync_data;
+}
+
+// static
+std::vector<NavigationEntry*> SerializedNavigationEntry::ToNavigationEntries(
+ const std::vector<SerializedNavigationEntry>& navigations,
+ content::BrowserContext* browser_context) {
+ int page_id = 0;
+ std::vector<NavigationEntry*> entries;
+ for (std::vector<SerializedNavigationEntry>::const_iterator
+ it = navigations.begin(); it != navigations.end(); ++it) {
+ entries.push_back(
+ it->ToNavigationEntry(page_id, browser_context).release());
+ ++page_id;
+ }
+ return entries;
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/serialized_navigation_entry.h b/chromium/components/sessions/serialized_navigation_entry.h
new file mode 100644
index 00000000000..e5813dce0d8
--- /dev/null
+++ b/chromium/components/sessions/serialized_navigation_entry.h
@@ -0,0 +1,160 @@
+// 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_SESSIONS_SERIALIZED_NAVIGATION_ENTRY_H_
+#define COMPONENTS_SESSIONS_SERIALIZED_NAVIGATION_ENTRY_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "components/sessions/sessions_export.h"
+#include "content/public/common/page_state.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/common/referrer.h"
+#include "url/gurl.h"
+
+class Pickle;
+class PickleIterator;
+
+namespace content {
+class BrowserContext;
+class NavigationEntry;
+}
+
+namespace sync_pb {
+class TabNavigation;
+}
+
+namespace sessions {
+
+class SerializedNavigationEntryTestHelper;
+
+// The key used to store search terms data in the NavigationEntry.
+SESSIONS_EXPORT extern const char kSearchTermsKey[];
+
+// SerializedNavigationEntry is a "freeze-dried" version of NavigationEntry. It
+// contains the data needed to restore a NavigationEntry during session restore
+// and tab restore, and it can also be pickled and unpickled. It is also
+// convertible to a sync protocol buffer for session syncing.
+//
+// Default copy constructor and assignment operator welcome.
+class SESSIONS_EXPORT SerializedNavigationEntry {
+ public:
+ enum BlockedState {
+ STATE_INVALID = 0,
+ STATE_ALLOWED = 1,
+ STATE_BLOCKED = 2,
+ };
+
+ // Creates an invalid (index < 0) SerializedNavigationEntry.
+ SerializedNavigationEntry();
+ ~SerializedNavigationEntry();
+
+ // Construct a SerializedNavigationEntry for a particular index from the given
+ // NavigationEntry.
+ static SerializedNavigationEntry FromNavigationEntry(
+ int index,
+ const content::NavigationEntry& entry);
+
+ // Construct a SerializedNavigationEntry for a particular index from a sync
+ // protocol buffer. Note that the sync protocol buffer doesn't contain all
+ // SerializedNavigationEntry fields. Also, the timestamp of the returned
+ // SerializedNavigationEntry is nulled out, as we assume that the protocol
+ // buffer is from a foreign session.
+ static SerializedNavigationEntry FromSyncData(
+ int index,
+ const sync_pb::TabNavigation& sync_data);
+
+ // Note that not all SerializedNavigationEntry fields are preserved.
+ // |max_size| is the max number of bytes to write.
+ void WriteToPickle(int max_size, Pickle* pickle) const;
+ bool ReadFromPickle(PickleIterator* iterator);
+
+ // Convert this SerializedNavigationEntry into a NavigationEntry with the
+ // given page ID and context. The NavigationEntry will have a transition type
+ // of PAGE_TRANSITION_RELOAD and a new unique ID.
+ scoped_ptr<content::NavigationEntry> ToNavigationEntry(
+ int page_id,
+ content::BrowserContext* browser_context) const;
+
+ // Convert this navigation into its sync protocol buffer equivalent. Note
+ // that the protocol buffer doesn't contain all SerializedNavigationEntry
+ // fields.
+ sync_pb::TabNavigation ToSyncData() const;
+
+ // The index in the NavigationController. This SerializedNavigationEntry is
+ // valid only when the index is non-negative.
+ int index() const { return index_; }
+ void set_index(int index) { index_ = index; }
+
+ // Accessors for some fields taken from NavigationEntry.
+ int unique_id() const { return unique_id_; }
+ const GURL& virtual_url() const { return virtual_url_; }
+ const string16& title() const { return title_; }
+ const content::PageState& page_state() const { return page_state_; }
+ const string16& search_terms() const { return search_terms_; }
+ const GURL& favicon_url() const { return favicon_url_; }
+ const content::Referrer& referrer() const { return referrer_; }
+ content::PageTransition transition_type() const {
+ return transition_type_;
+ }
+ bool has_post_data() const { return has_post_data_; }
+ int64 post_id() const { return post_id_; }
+ const GURL& original_request_url() const { return original_request_url_; }
+ bool is_overriding_user_agent() const { return is_overriding_user_agent_; }
+ base::Time timestamp() const { return timestamp_; }
+
+ BlockedState blocked_state() { return blocked_state_; }
+ void set_blocked_state(BlockedState blocked_state) {
+ blocked_state_ = blocked_state;
+ }
+ std::set<std::string> content_pack_categories() {
+ return content_pack_categories_;
+ }
+ void set_content_pack_categories(
+ const std::set<std::string>& content_pack_categories) {
+ content_pack_categories_ = content_pack_categories;
+ }
+
+ // Converts a set of SerializedNavigationEntrys into a list of
+ // NavigationEntrys with sequential page IDs and the given context. The caller
+ // owns the returned NavigationEntrys.
+ static std::vector<content::NavigationEntry*> ToNavigationEntries(
+ const std::vector<SerializedNavigationEntry>& navigations,
+ content::BrowserContext* browser_context);
+
+ private:
+ friend class SerializedNavigationEntryTestHelper;
+
+ // Index in the NavigationController.
+ int index_;
+
+ // Member variables corresponding to NavigationEntry fields.
+ int unique_id_;
+ content::Referrer referrer_;
+ GURL virtual_url_;
+ string16 title_;
+ content::PageState page_state_;
+ content::PageTransition transition_type_;
+ bool has_post_data_;
+ int64 post_id_;
+ GURL original_request_url_;
+ bool is_overriding_user_agent_;
+ base::Time timestamp_;
+ string16 search_terms_;
+ GURL favicon_url_;
+
+ // Additional information.
+ BlockedState blocked_state_;
+ std::set<std::string> content_pack_categories_;
+};
+
+} // namespace sessions
+
+#endif // COMPONENTS_SESSIONS_SERIALIZED_NAVIGATION_ENTRY_H_
diff --git a/chromium/components/sessions/serialized_navigation_entry_test_helper.cc b/chromium/components/sessions/serialized_navigation_entry_test_helper.cc
new file mode 100644
index 00000000000..f4a7e2eba6a
--- /dev/null
+++ b/chromium/components/sessions/serialized_navigation_entry_test_helper.cc
@@ -0,0 +1,84 @@
+// 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/sessions/serialized_navigation_entry_test_helper.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/sessions/serialized_navigation_entry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
+#include "url/gurl.h"
+
+namespace sessions {
+
+// static
+void SerializedNavigationEntryTestHelper::ExpectNavigationEquals(
+ const SerializedNavigationEntry& expected,
+ const SerializedNavigationEntry& actual) {
+ EXPECT_EQ(expected.referrer_.url, actual.referrer_.url);
+ EXPECT_EQ(expected.referrer_.policy, actual.referrer_.policy);
+ EXPECT_EQ(expected.virtual_url_, actual.virtual_url_);
+ EXPECT_EQ(expected.title_, actual.title_);
+ EXPECT_EQ(expected.page_state_, actual.page_state_);
+ EXPECT_EQ(expected.transition_type_, actual.transition_type_);
+ EXPECT_EQ(expected.has_post_data_, actual.has_post_data_);
+ EXPECT_EQ(expected.original_request_url_, actual.original_request_url_);
+ EXPECT_EQ(expected.is_overriding_user_agent_,
+ actual.is_overriding_user_agent_);
+}
+
+// static
+SerializedNavigationEntry SerializedNavigationEntryTestHelper::CreateNavigation(
+ const std::string& virtual_url,
+ const std::string& title) {
+ SerializedNavigationEntry navigation;
+ navigation.index_ = 0;
+ navigation.referrer_ =
+ content::Referrer(GURL("http://www.referrer.com"),
+ WebKit::WebReferrerPolicyDefault);
+ navigation.virtual_url_ = GURL(virtual_url);
+ navigation.title_ = UTF8ToUTF16(title);
+ navigation.page_state_ =
+ content::PageState::CreateFromEncodedData("fake_state");
+ navigation.timestamp_ = base::Time::Now();
+ return navigation;
+}
+
+// static
+void SerializedNavigationEntryTestHelper::SetPageState(
+ const content::PageState& page_state,
+ SerializedNavigationEntry* navigation) {
+ navigation->page_state_ = page_state;
+}
+
+// static
+void SerializedNavigationEntryTestHelper::SetHasPostData(
+ bool has_post_data,
+ SerializedNavigationEntry* navigation) {
+ navigation->has_post_data_ = has_post_data;
+}
+
+// static
+void SerializedNavigationEntryTestHelper::SetOriginalRequestURL(
+ const GURL& original_request_url,
+ SerializedNavigationEntry* navigation) {
+ navigation->original_request_url_ = original_request_url;
+}
+
+// static
+void SerializedNavigationEntryTestHelper::SetIsOverridingUserAgent(
+ bool is_overriding_user_agent,
+ SerializedNavigationEntry* navigation) {
+ navigation->is_overriding_user_agent_ = is_overriding_user_agent;
+}
+
+// static
+void SerializedNavigationEntryTestHelper::SetTimestamp(
+ base::Time timestamp,
+ SerializedNavigationEntry* navigation) {
+ navigation->timestamp_ = timestamp;
+}
+
+} // namespace sessions
diff --git a/chromium/components/sessions/serialized_navigation_entry_test_helper.h b/chromium/components/sessions/serialized_navigation_entry_test_helper.h
new file mode 100644
index 00000000000..a653f22d67e
--- /dev/null
+++ b/chromium/components/sessions/serialized_navigation_entry_test_helper.h
@@ -0,0 +1,63 @@
+// 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_SESSIONS_SESSION_TYPES_TEST_HELPER_H_
+#define COMPONENTS_SESSIONS_SESSION_TYPES_TEST_HELPER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "content/public/common/page_transition_types.h"
+
+class GURL;
+
+namespace base {
+class Time;
+}
+
+namespace content {
+class PageState;
+struct Referrer;
+}
+
+namespace sessions {
+
+class SerializedNavigationEntry;
+
+// Set of test functions to manipulate a SerializedNavigationEntry.
+class SerializedNavigationEntryTestHelper {
+ public:
+ // Compares the two entries. This uses EXPECT_XXX on each member, if your test
+ // needs to stop after this wrap calls to this in EXPECT_NO_FATAL_FAILURE.
+ static void ExpectNavigationEquals(const SerializedNavigationEntry& expected,
+ const SerializedNavigationEntry& actual);
+
+ // Create a SerializedNavigationEntry with the given URL and title and some
+ // common values for the other fields.
+ static SerializedNavigationEntry CreateNavigation(
+ const std::string& virtual_url,
+ const std::string& title);
+
+ static void SetPageState(const content::PageState& page_state,
+ SerializedNavigationEntry* navigation);
+
+ static void SetHasPostData(bool has_post_data,
+ SerializedNavigationEntry* navigation);
+
+ static void SetOriginalRequestURL(const GURL& original_request_url,
+ SerializedNavigationEntry* navigation);
+
+ static void SetIsOverridingUserAgent(bool is_overriding_user_agent,
+ SerializedNavigationEntry* navigation);
+
+ static void SetTimestamp(base::Time timestamp,
+ SerializedNavigationEntry* navigation);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SerializedNavigationEntryTestHelper);
+};
+
+} // sessions
+
+#endif // COMPONENTS_SESSIONS_SESSION_TYPES_TEST_HELPER_H_
diff --git a/chromium/components/sessions/serialized_navigation_entry_unittest.cc b/chromium/components/sessions/serialized_navigation_entry_unittest.cc
new file mode 100644
index 00000000000..5f052fe23e7
--- /dev/null
+++ b/chromium/components/sessions/serialized_navigation_entry_unittest.cc
@@ -0,0 +1,290 @@
+// 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/sessions/serialized_navigation_entry.h"
+
+#include <cstddef>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "content/public/browser/favicon_status.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/common/referrer.h"
+#include "sync/protocol/session_specifics.pb.h"
+#include "sync/util/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebReferrerPolicy.h"
+#include "url/gurl.h"
+
+namespace sessions {
+namespace {
+
+const int kIndex = 3;
+const int kUniqueID = 50;
+const content::Referrer kReferrer =
+ content::Referrer(GURL("http://www.referrer.com"),
+ WebKit::WebReferrerPolicyAlways);
+const GURL kVirtualURL("http://www.virtual-url.com");
+const string16 kTitle = ASCIIToUTF16("title");
+const content::PageState kPageState =
+ content::PageState::CreateFromEncodedData("page state");
+const content::PageTransition kTransitionType =
+ static_cast<content::PageTransition>(
+ content::PAGE_TRANSITION_AUTO_SUBFRAME |
+ content::PAGE_TRANSITION_HOME_PAGE |
+ content::PAGE_TRANSITION_CLIENT_REDIRECT);
+const bool kHasPostData = true;
+const int64 kPostID = 100;
+const GURL kOriginalRequestURL("http://www.original-request.com");
+const bool kIsOverridingUserAgent = true;
+const base::Time kTimestamp = syncer::ProtoTimeToTime(100);
+const string16 kSearchTerms = ASCIIToUTF16("my search terms");
+const GURL kFaviconURL("http://virtual-url.com/favicon.ico");
+
+const int kPageID = 10;
+
+// Create a NavigationEntry from the constants above.
+scoped_ptr<content::NavigationEntry> MakeNavigationEntryForTest() {
+ scoped_ptr<content::NavigationEntry> navigation_entry(
+ content::NavigationEntry::Create());
+ navigation_entry->SetReferrer(kReferrer);
+ navigation_entry->SetVirtualURL(kVirtualURL);
+ navigation_entry->SetTitle(kTitle);
+ navigation_entry->SetPageState(kPageState);
+ navigation_entry->SetTransitionType(kTransitionType);
+ navigation_entry->SetHasPostData(kHasPostData);
+ navigation_entry->SetPostID(kPostID);
+ navigation_entry->SetOriginalRequestURL(kOriginalRequestURL);
+ navigation_entry->SetIsOverridingUserAgent(kIsOverridingUserAgent);
+ navigation_entry->SetTimestamp(kTimestamp);
+ navigation_entry->SetExtraData(kSearchTermsKey, kSearchTerms);
+ navigation_entry->GetFavicon().valid = true;
+ navigation_entry->GetFavicon().url = kFaviconURL;
+ return navigation_entry.Pass();
+}
+
+// Create a sync_pb::TabNavigation from the constants above.
+sync_pb::TabNavigation MakeSyncDataForTest() {
+ sync_pb::TabNavigation sync_data;
+ sync_data.set_virtual_url(kVirtualURL.spec());
+ sync_data.set_referrer(kReferrer.url.spec());
+ sync_data.set_title(UTF16ToUTF8(kTitle));
+ sync_data.set_state(kPageState.ToEncodedData());
+ sync_data.set_page_transition(
+ sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME);
+ sync_data.set_unique_id(kUniqueID);
+ sync_data.set_timestamp_msec(syncer::TimeToProtoTime(kTimestamp));
+ sync_data.set_redirect_type(sync_pb::SyncEnums::CLIENT_REDIRECT);
+ sync_data.set_navigation_home_page(true);
+ sync_data.set_search_terms(UTF16ToUTF8(kSearchTerms));
+ sync_data.set_favicon_url(kFaviconURL.spec());
+ return sync_data;
+}
+
+// Create a default SerializedNavigationEntry. All its fields should be
+// initialized to their respective default values.
+TEST(SerializedNavigationEntryTest, DefaultInitializer) {
+ const SerializedNavigationEntry navigation;
+ EXPECT_EQ(-1, navigation.index());
+ EXPECT_EQ(0, navigation.unique_id());
+ EXPECT_EQ(GURL(), navigation.referrer().url);
+ EXPECT_EQ(WebKit::WebReferrerPolicyDefault, navigation.referrer().policy);
+ EXPECT_EQ(GURL(), navigation.virtual_url());
+ EXPECT_TRUE(navigation.title().empty());
+ EXPECT_FALSE(navigation.page_state().IsValid());
+ EXPECT_EQ(content::PAGE_TRANSITION_TYPED, navigation.transition_type());
+ EXPECT_FALSE(navigation.has_post_data());
+ EXPECT_EQ(-1, navigation.post_id());
+ EXPECT_EQ(GURL(), navigation.original_request_url());
+ EXPECT_FALSE(navigation.is_overriding_user_agent());
+ EXPECT_TRUE(navigation.timestamp().is_null());
+ EXPECT_TRUE(navigation.search_terms().empty());
+ EXPECT_FALSE(navigation.favicon_url().is_valid());
+}
+
+// Create a SerializedNavigationEntry from a NavigationEntry. All its fields
+// should match the NavigationEntry's.
+TEST(SerializedNavigationEntryTest, FromNavigationEntry) {
+ const scoped_ptr<content::NavigationEntry> navigation_entry(
+ MakeNavigationEntryForTest());
+
+ const SerializedNavigationEntry& navigation =
+ SerializedNavigationEntry::FromNavigationEntry(kIndex, *navigation_entry);
+
+ EXPECT_EQ(kIndex, navigation.index());
+
+ EXPECT_EQ(navigation_entry->GetUniqueID(), navigation.unique_id());
+ EXPECT_EQ(kReferrer.url, navigation.referrer().url);
+ EXPECT_EQ(kReferrer.policy, navigation.referrer().policy);
+ EXPECT_EQ(kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(kTitle, navigation.title());
+ EXPECT_EQ(kPageState, navigation.page_state());
+ EXPECT_EQ(kTransitionType, navigation.transition_type());
+ EXPECT_EQ(kHasPostData, navigation.has_post_data());
+ EXPECT_EQ(kPostID, navigation.post_id());
+ EXPECT_EQ(kOriginalRequestURL, navigation.original_request_url());
+ EXPECT_EQ(kIsOverridingUserAgent, navigation.is_overriding_user_agent());
+ EXPECT_EQ(kTimestamp, navigation.timestamp());
+ EXPECT_EQ(kFaviconURL, navigation.favicon_url());
+}
+
+// Create a SerializedNavigationEntry from a sync_pb::TabNavigation. All its
+// fields should match the protocol buffer's if it exists there, and
+// sbould be set to the default value otherwise.
+TEST(SerializedNavigationEntryTest, FromSyncData) {
+ const sync_pb::TabNavigation sync_data = MakeSyncDataForTest();
+
+ const SerializedNavigationEntry& navigation =
+ SerializedNavigationEntry::FromSyncData(kIndex, sync_data);
+
+ EXPECT_EQ(kIndex, navigation.index());
+ EXPECT_EQ(kUniqueID, navigation.unique_id());
+ EXPECT_EQ(kReferrer.url, navigation.referrer().url);
+ EXPECT_EQ(WebKit::WebReferrerPolicyDefault, navigation.referrer().policy);
+ EXPECT_EQ(kVirtualURL, navigation.virtual_url());
+ EXPECT_EQ(kTitle, navigation.title());
+ EXPECT_EQ(kPageState, navigation.page_state());
+ EXPECT_EQ(kTransitionType, navigation.transition_type());
+ EXPECT_FALSE(navigation.has_post_data());
+ EXPECT_EQ(-1, navigation.post_id());
+ EXPECT_EQ(GURL(), navigation.original_request_url());
+ EXPECT_FALSE(navigation.is_overriding_user_agent());
+ EXPECT_TRUE(navigation.timestamp().is_null());
+ EXPECT_EQ(kSearchTerms, navigation.search_terms());
+ EXPECT_EQ(kFaviconURL, navigation.favicon_url());
+}
+
+// Create a SerializedNavigationEntry, pickle it, then create another one by
+// unpickling. The new one should match the old one except for fields
+// that aren't pickled, which should be set to default values.
+TEST(SerializedNavigationEntryTest, Pickle) {
+ const SerializedNavigationEntry& old_navigation =
+ SerializedNavigationEntry::FromNavigationEntry(
+ kIndex, *MakeNavigationEntryForTest());
+
+ Pickle pickle;
+ old_navigation.WriteToPickle(30000, &pickle);
+
+ SerializedNavigationEntry new_navigation;
+ PickleIterator pickle_iterator(pickle);
+ EXPECT_TRUE(new_navigation.ReadFromPickle(&pickle_iterator));
+
+ EXPECT_EQ(kIndex, new_navigation.index());
+
+ EXPECT_EQ(0, new_navigation.unique_id());
+ EXPECT_EQ(kReferrer.url, new_navigation.referrer().url);
+ EXPECT_EQ(kReferrer.policy, new_navigation.referrer().policy);
+ EXPECT_EQ(kVirtualURL, new_navigation.virtual_url());
+ EXPECT_EQ(kTitle, new_navigation.title());
+ EXPECT_FALSE(new_navigation.page_state().IsValid());
+ EXPECT_EQ(kTransitionType, new_navigation.transition_type());
+ EXPECT_EQ(kHasPostData, new_navigation.has_post_data());
+ EXPECT_EQ(-1, new_navigation.post_id());
+ EXPECT_EQ(kOriginalRequestURL, new_navigation.original_request_url());
+ EXPECT_EQ(kIsOverridingUserAgent, new_navigation.is_overriding_user_agent());
+ EXPECT_EQ(kTimestamp, new_navigation.timestamp());
+ EXPECT_EQ(kSearchTerms, new_navigation.search_terms());
+}
+
+// Create a NavigationEntry, then create another one by converting to
+// a SerializedNavigationEntry and back. The new one should match the old one
+// except for fields that aren't preserved, which should be set to
+// expected values.
+TEST(SerializedNavigationEntryTest, ToNavigationEntry) {
+ const scoped_ptr<content::NavigationEntry> old_navigation_entry(
+ MakeNavigationEntryForTest());
+
+ const SerializedNavigationEntry& navigation =
+ SerializedNavigationEntry::FromNavigationEntry(kIndex,
+ *old_navigation_entry);
+
+ const scoped_ptr<content::NavigationEntry> new_navigation_entry(
+ navigation.ToNavigationEntry(kPageID, NULL));
+
+ EXPECT_EQ(kReferrer.url, new_navigation_entry->GetReferrer().url);
+ EXPECT_EQ(kReferrer.policy, new_navigation_entry->GetReferrer().policy);
+ EXPECT_EQ(kVirtualURL, new_navigation_entry->GetVirtualURL());
+ EXPECT_EQ(kTitle, new_navigation_entry->GetTitle());
+ EXPECT_EQ(kPageState, new_navigation_entry->GetPageState());
+ EXPECT_EQ(kPageID, new_navigation_entry->GetPageID());
+ EXPECT_EQ(content::PAGE_TRANSITION_RELOAD,
+ new_navigation_entry->GetTransitionType());
+ EXPECT_EQ(kHasPostData, new_navigation_entry->GetHasPostData());
+ EXPECT_EQ(kPostID, new_navigation_entry->GetPostID());
+ EXPECT_EQ(kOriginalRequestURL,
+ new_navigation_entry->GetOriginalRequestURL());
+ EXPECT_EQ(kIsOverridingUserAgent,
+ new_navigation_entry->GetIsOverridingUserAgent());
+ string16 search_terms;
+ new_navigation_entry->GetExtraData(kSearchTermsKey, &search_terms);
+ EXPECT_EQ(kSearchTerms, search_terms);
+}
+
+// Create a NavigationEntry, convert it to a SerializedNavigationEntry, then
+// create a sync protocol buffer from it. The protocol buffer should
+// have matching fields to the NavigationEntry (when applicable).
+TEST(SerializedNavigationEntryTest, ToSyncData) {
+ const scoped_ptr<content::NavigationEntry> navigation_entry(
+ MakeNavigationEntryForTest());
+
+ const SerializedNavigationEntry& navigation =
+ SerializedNavigationEntry::FromNavigationEntry(kIndex, *navigation_entry);
+
+ const sync_pb::TabNavigation sync_data = navigation.ToSyncData();
+
+ EXPECT_EQ(kVirtualURL.spec(), sync_data.virtual_url());
+ EXPECT_EQ(kReferrer.url.spec(), sync_data.referrer());
+ EXPECT_EQ(kTitle, ASCIIToUTF16(sync_data.title()));
+ EXPECT_TRUE(sync_data.state().empty());
+ EXPECT_EQ(sync_pb::SyncEnums_PageTransition_AUTO_SUBFRAME,
+ sync_data.page_transition());
+ EXPECT_TRUE(sync_data.has_redirect_type());
+ EXPECT_EQ(navigation_entry->GetUniqueID(), sync_data.unique_id());
+ EXPECT_EQ(syncer::TimeToProtoTime(kTimestamp), sync_data.timestamp_msec());
+ EXPECT_EQ(kTimestamp.ToInternalValue(), sync_data.global_id());
+ EXPECT_EQ(kFaviconURL.spec(), sync_data.favicon_url());
+}
+
+// Ensure all transition types and qualifiers are converted to/from the sync
+// SerializedNavigationEntry representation properly.
+TEST(SerializedNavigationEntryTest, TransitionTypes) {
+ scoped_ptr<content::NavigationEntry> navigation_entry(
+ MakeNavigationEntryForTest());
+ for (uint32 core_type = content::PAGE_TRANSITION_LINK;
+ core_type != content::PAGE_TRANSITION_LAST_CORE; ++core_type) {
+ // Because qualifier is a uint32, left shifting will eventually overflow
+ // and hit zero again. SERVER_REDIRECT, as the last qualifier and also
+ // in place of the sign bit, is therefore the last transition before
+ // breaking.
+ for (uint32 qualifier = content::PAGE_TRANSITION_FORWARD_BACK;
+ qualifier != 0; qualifier <<= 1) {
+ if (qualifier == 0x08000000)
+ continue; // 0x08000000 is not a valid qualifier.
+ content::PageTransition transition =
+ static_cast<content::PageTransition>(core_type | qualifier);
+
+ navigation_entry->SetTransitionType(transition);
+ const SerializedNavigationEntry& navigation =
+ SerializedNavigationEntry::FromNavigationEntry(kIndex,
+ *navigation_entry);
+ const sync_pb::TabNavigation& sync_data = navigation.ToSyncData();
+ const SerializedNavigationEntry& constructed_nav =
+ SerializedNavigationEntry::FromSyncData(kIndex, sync_data);
+ const content::PageTransition constructed_transition =
+ constructed_nav.transition_type();
+
+ EXPECT_EQ(transition, constructed_transition);
+ }
+ }
+}
+
+} // namespace
+} // namespace sessions
diff --git a/chromium/components/sessions/sessions_export.h b/chromium/components/sessions/sessions_export.h
new file mode 100644
index 00000000000..66ac82270bc
--- /dev/null
+++ b/chromium/components/sessions/sessions_export.h
@@ -0,0 +1,29 @@
+// 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_SESSIONS_SESSIONS_EXPORT_H_
+#define COMPONENTS_SESSIONS_SESSIONS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(SESSIONS_IMPLEMENTATION)
+#define SESSIONS_EXPORT __declspec(dllexport)
+#else
+#define SESSIONS_EXPORT __declspec(dllimport)
+#endif // defined(BASE_PREFS_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(SESSIONS_IMPLEMENTATION)
+#define SESSIONS_EXPORT __attribute__((visibility("default")))
+#else
+#define SESSIONS_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define SESSIONS_EXPORT
+#endif
+
+#endif // COMPONENTS_SESSIONS_SESSIONS_EXPORT_H_
diff --git a/chromium/components/strings/component_strings_am.xtb b/chromium/components/strings/component_strings_am.xtb
new file mode 100644
index 00000000000..6c985cb8784
--- /dev/null
+++ b/chromium/components/strings/component_strings_am.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="am">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ar.xtb b/chromium/components/strings/component_strings_ar.xtb
new file mode 100644
index 00000000000..e8b5562299a
--- /dev/null
+++ b/chromium/components/strings/component_strings_ar.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ar">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_bg.xtb b/chromium/components/strings/component_strings_bg.xtb
new file mode 100644
index 00000000000..ebab4736462
--- /dev/null
+++ b/chromium/components/strings/component_strings_bg.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bg">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_bn.xtb b/chromium/components/strings/component_strings_bn.xtb
new file mode 100644
index 00000000000..a66cc1fcd8f
--- /dev/null
+++ b/chromium/components/strings/component_strings_bn.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="bn">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ca.xtb b/chromium/components/strings/component_strings_ca.xtb
new file mode 100644
index 00000000000..1438d89736b
--- /dev/null
+++ b/chromium/components/strings/component_strings_ca.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ca">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_cs.xtb b/chromium/components/strings/component_strings_cs.xtb
new file mode 100644
index 00000000000..2d95130b933
--- /dev/null
+++ b/chromium/components/strings/component_strings_cs.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="cs">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_da.xtb b/chromium/components/strings/component_strings_da.xtb
new file mode 100644
index 00000000000..751fa4a8b98
--- /dev/null
+++ b/chromium/components/strings/component_strings_da.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="da">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_de.xtb b/chromium/components/strings/component_strings_de.xtb
new file mode 100644
index 00000000000..91de7f5115b
--- /dev/null
+++ b/chromium/components/strings/component_strings_de.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="de">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_el.xtb b/chromium/components/strings/component_strings_el.xtb
new file mode 100644
index 00000000000..6e5e7d8157a
--- /dev/null
+++ b/chromium/components/strings/component_strings_el.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="el">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_en-GB.xtb b/chromium/components/strings/component_strings_en-GB.xtb
new file mode 100644
index 00000000000..0fb2133a32a
--- /dev/null
+++ b/chromium/components/strings/component_strings_en-GB.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="en-GB">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_es-419.xtb b/chromium/components/strings/component_strings_es-419.xtb
new file mode 100644
index 00000000000..2fe4770c0d6
--- /dev/null
+++ b/chromium/components/strings/component_strings_es-419.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es-419">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_es.xtb b/chromium/components/strings/component_strings_es.xtb
new file mode 100644
index 00000000000..64022ecf43c
--- /dev/null
+++ b/chromium/components/strings/component_strings_es.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="es">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_et.xtb b/chromium/components/strings/component_strings_et.xtb
new file mode 100644
index 00000000000..5244dfd4bfd
--- /dev/null
+++ b/chromium/components/strings/component_strings_et.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="et">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_fa.xtb b/chromium/components/strings/component_strings_fa.xtb
new file mode 100644
index 00000000000..18626f8a294
--- /dev/null
+++ b/chromium/components/strings/component_strings_fa.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fa">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_fi.xtb b/chromium/components/strings/component_strings_fi.xtb
new file mode 100644
index 00000000000..4691cd56eaf
--- /dev/null
+++ b/chromium/components/strings/component_strings_fi.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fi">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_fil.xtb b/chromium/components/strings/component_strings_fil.xtb
new file mode 100644
index 00000000000..443630e740a
--- /dev/null
+++ b/chromium/components/strings/component_strings_fil.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fil">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_fr.xtb b/chromium/components/strings/component_strings_fr.xtb
new file mode 100644
index 00000000000..63026a3ad03
--- /dev/null
+++ b/chromium/components/strings/component_strings_fr.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="fr">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_gu.xtb b/chromium/components/strings/component_strings_gu.xtb
new file mode 100644
index 00000000000..7d8a4df1b08
--- /dev/null
+++ b/chromium/components/strings/component_strings_gu.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="gu">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_hi.xtb b/chromium/components/strings/component_strings_hi.xtb
new file mode 100644
index 00000000000..a6ddd5d3f83
--- /dev/null
+++ b/chromium/components/strings/component_strings_hi.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hi">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_hr.xtb b/chromium/components/strings/component_strings_hr.xtb
new file mode 100644
index 00000000000..26f99d08e94
--- /dev/null
+++ b/chromium/components/strings/component_strings_hr.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hr">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_hu.xtb b/chromium/components/strings/component_strings_hu.xtb
new file mode 100644
index 00000000000..7ef9a5e0bbe
--- /dev/null
+++ b/chromium/components/strings/component_strings_hu.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="hu">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_id.xtb b/chromium/components/strings/component_strings_id.xtb
new file mode 100644
index 00000000000..aa34783a27c
--- /dev/null
+++ b/chromium/components/strings/component_strings_id.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="id">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_it.xtb b/chromium/components/strings/component_strings_it.xtb
new file mode 100644
index 00000000000..a6ac8d46363
--- /dev/null
+++ b/chromium/components/strings/component_strings_it.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="it">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_iw.xtb b/chromium/components/strings/component_strings_iw.xtb
new file mode 100644
index 00000000000..86b55334c0b
--- /dev/null
+++ b/chromium/components/strings/component_strings_iw.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="iw">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ja.xtb b/chromium/components/strings/component_strings_ja.xtb
new file mode 100644
index 00000000000..23139e6dd1d
--- /dev/null
+++ b/chromium/components/strings/component_strings_ja.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ja">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_kn.xtb b/chromium/components/strings/component_strings_kn.xtb
new file mode 100644
index 00000000000..cc3643a208f
--- /dev/null
+++ b/chromium/components/strings/component_strings_kn.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="kn">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ko.xtb b/chromium/components/strings/component_strings_ko.xtb
new file mode 100644
index 00000000000..e0fc3707e1b
--- /dev/null
+++ b/chromium/components/strings/component_strings_ko.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ko">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_lt.xtb b/chromium/components/strings/component_strings_lt.xtb
new file mode 100644
index 00000000000..5804ae2a34c
--- /dev/null
+++ b/chromium/components/strings/component_strings_lt.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lt">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_lv.xtb b/chromium/components/strings/component_strings_lv.xtb
new file mode 100644
index 00000000000..a0a1c477043
--- /dev/null
+++ b/chromium/components/strings/component_strings_lv.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="lv">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ml.xtb b/chromium/components/strings/component_strings_ml.xtb
new file mode 100644
index 00000000000..f7db3152e4c
--- /dev/null
+++ b/chromium/components/strings/component_strings_ml.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ml">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_mr.xtb b/chromium/components/strings/component_strings_mr.xtb
new file mode 100644
index 00000000000..098d29c9921
--- /dev/null
+++ b/chromium/components/strings/component_strings_mr.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="mr">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ms.xtb b/chromium/components/strings/component_strings_ms.xtb
new file mode 100644
index 00000000000..1fb470abf06
--- /dev/null
+++ b/chromium/components/strings/component_strings_ms.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ms">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_nl.xtb b/chromium/components/strings/component_strings_nl.xtb
new file mode 100644
index 00000000000..e78241066f9
--- /dev/null
+++ b/chromium/components/strings/component_strings_nl.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="nl">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_no.xtb b/chromium/components/strings/component_strings_no.xtb
new file mode 100644
index 00000000000..913638ba4e9
--- /dev/null
+++ b/chromium/components/strings/component_strings_no.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="no">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_pl.xtb b/chromium/components/strings/component_strings_pl.xtb
new file mode 100644
index 00000000000..4519e3de389
--- /dev/null
+++ b/chromium/components/strings/component_strings_pl.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pl">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_pt-BR.xtb b/chromium/components/strings/component_strings_pt-BR.xtb
new file mode 100644
index 00000000000..e95eb56bb7b
--- /dev/null
+++ b/chromium/components/strings/component_strings_pt-BR.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-BR">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_pt-PT.xtb b/chromium/components/strings/component_strings_pt-PT.xtb
new file mode 100644
index 00000000000..1dcf557a081
--- /dev/null
+++ b/chromium/components/strings/component_strings_pt-PT.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="pt-PT">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ro.xtb b/chromium/components/strings/component_strings_ro.xtb
new file mode 100644
index 00000000000..9e434933f16
--- /dev/null
+++ b/chromium/components/strings/component_strings_ro.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ro">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ru.xtb b/chromium/components/strings/component_strings_ru.xtb
new file mode 100644
index 00000000000..c4a621b9fdd
--- /dev/null
+++ b/chromium/components/strings/component_strings_ru.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ru">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_sk.xtb b/chromium/components/strings/component_strings_sk.xtb
new file mode 100644
index 00000000000..00750d31cd0
--- /dev/null
+++ b/chromium/components/strings/component_strings_sk.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sk">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_sl.xtb b/chromium/components/strings/component_strings_sl.xtb
new file mode 100644
index 00000000000..489b7e46ba0
--- /dev/null
+++ b/chromium/components/strings/component_strings_sl.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sl">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_sr.xtb b/chromium/components/strings/component_strings_sr.xtb
new file mode 100644
index 00000000000..38f6f354db5
--- /dev/null
+++ b/chromium/components/strings/component_strings_sr.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sr">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_sv.xtb b/chromium/components/strings/component_strings_sv.xtb
new file mode 100644
index 00000000000..ddea3dcce5d
--- /dev/null
+++ b/chromium/components/strings/component_strings_sv.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sv">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_sw.xtb b/chromium/components/strings/component_strings_sw.xtb
new file mode 100644
index 00000000000..b7750886d22
--- /dev/null
+++ b/chromium/components/strings/component_strings_sw.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="sw">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_ta.xtb b/chromium/components/strings/component_strings_ta.xtb
new file mode 100644
index 00000000000..ef90687762c
--- /dev/null
+++ b/chromium/components/strings/component_strings_ta.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="ta">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_te.xtb b/chromium/components/strings/component_strings_te.xtb
new file mode 100644
index 00000000000..48c714ba18f
--- /dev/null
+++ b/chromium/components/strings/component_strings_te.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="te">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_th.xtb b/chromium/components/strings/component_strings_th.xtb
new file mode 100644
index 00000000000..fae31966a2f
--- /dev/null
+++ b/chromium/components/strings/component_strings_th.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="th">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_tr.xtb b/chromium/components/strings/component_strings_tr.xtb
new file mode 100644
index 00000000000..9a299515338
--- /dev/null
+++ b/chromium/components/strings/component_strings_tr.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="tr">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_uk.xtb b/chromium/components/strings/component_strings_uk.xtb
new file mode 100644
index 00000000000..f0db52c6692
--- /dev/null
+++ b/chromium/components/strings/component_strings_uk.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="uk">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_vi.xtb b/chromium/components/strings/component_strings_vi.xtb
new file mode 100644
index 00000000000..b2957daa924
--- /dev/null
+++ b/chromium/components/strings/component_strings_vi.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="vi">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_zh-CN.xtb b/chromium/components/strings/component_strings_zh-CN.xtb
new file mode 100644
index 00000000000..26e8b409cd0
--- /dev/null
+++ b/chromium/components/strings/component_strings_zh-CN.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-CN">
+</translationbundle>
diff --git a/chromium/components/strings/component_strings_zh-TW.xtb b/chromium/components/strings/component_strings_zh-TW.xtb
new file mode 100644
index 00000000000..935ef485a1c
--- /dev/null
+++ b/chromium/components/strings/component_strings_zh-TW.xtb
@@ -0,0 +1,4 @@
+<?xml version="1.0" ?>
+<!DOCTYPE translationbundle>
+<translationbundle lang="zh-TW">
+</translationbundle>
diff --git a/chromium/components/tools/metrics/browser_components_metrics.py b/chromium/components/tools/metrics/browser_components_metrics.py
new file mode 100755
index 00000000000..725295377ae
--- /dev/null
+++ b/chromium/components/tools/metrics/browser_components_metrics.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+# 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.
+
+"""Generates the metrics collected weekly for the Browser Components project.
+
+See
+http://www.chromium.org/developers/design-documents/browser-components
+for details.
+"""
+
+import os
+import sys
+
+
+# This is done so that we can import checkdeps. If not invoked as
+# main, our user must ensure it is in PYTHONPATH.
+if __name__ == '__main__':
+ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..',
+ 'tools', 'checkdeps'))
+
+
+import count_ifdefs
+import checkdeps
+import results
+
+
+# Preprocessor pattern to find OS_XYZ defines.
+PREPROCESSOR_PATTERN = 'OS_[A-Z]+'
+
+
+class BrowserComponentsMetricsGenerator(object):
+ def __init__(self, checkout_root):
+ self.checkout_root = checkout_root
+ self.chrome_browser = os.path.join(checkout_root, 'chrome', 'browser')
+
+ def CountIfdefs(self, skip_tests):
+ return count_ifdefs.CountIfdefs(
+ PREPROCESSOR_PATTERN, self.chrome_browser, skip_tests)
+
+ def CountViolations(self, skip_tests):
+ deps_checker = checkdeps.DepsChecker(self.checkout_root,
+ ignore_temp_rules=True,
+ skip_tests=skip_tests)
+ deps_checker.results_formatter = results.CountViolationsFormatter()
+ deps_checker.CheckDirectory(os.path.join('chrome', 'browser'))
+ return int(deps_checker.results_formatter.GetResults())
+
+
+def main():
+ generator = BrowserComponentsMetricsGenerator(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+
+ print "All metrics are for chrome/browser.\n"
+ print "OS ifdefs, all: %d" % generator.CountIfdefs(False)
+ print "OS ifdefs, -tests: %d" % generator.CountIfdefs(True)
+ print ("Intended DEPS violations, all: %d" %
+ generator.CountViolations(False))
+ print "Intended DEPS violations, -tests: %d" % generator.CountViolations(True)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/components/tools/metrics/count_ifdefs.py b/chromium/components/tools/metrics/count_ifdefs.py
new file mode 100755
index 00000000000..2e3c2adf02d
--- /dev/null
+++ b/chromium/components/tools/metrics/count_ifdefs.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+# 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.
+
+"""Counts the number of #if or #ifdef lines containing at least one
+preprocessor token that is a full match for the given pattern, in the
+given directory.
+"""
+
+
+import optparse
+import os
+import re
+import sys
+
+
+# Filename extensions we know will be handled by the C preprocessor.
+# We ignore files not matching these.
+CPP_EXTENSIONS = [
+ '.h',
+ '.cc',
+ '.m',
+ '.mm',
+]
+
+
+def _IsTestFile(filename):
+ """Does a rudimentary check to try to skip test files; this could be
+ improved but is good enough for basic metrics generation.
+ """
+ return re.match('(test|mock|dummy)_.*|.*_[a-z]*test\.(h|cc|mm)', filename)
+
+
+def CountIfdefs(token_pattern, directory, skip_tests=False):
+ """Returns the number of lines in files in |directory| and its
+ subdirectories that have an extension from |CPP_EXTENSIONS| and are
+ an #if or #ifdef line with a preprocessor token fully matching
+ the string |token_pattern|.
+
+ If |skip_tests| is true, a best effort is made to ignore test files.
+ """
+ token_line_re = re.compile(r'^#if(def)?.*\b(%s)\b.*$' % token_pattern)
+ count = 0
+ for root, dirs, files in os.walk(directory):
+ for filename in files:
+ if os.path.splitext(filename)[1] in CPP_EXTENSIONS:
+ if not skip_tests or not _IsTestFile(filename):
+ with open(os.path.join(root, filename)) as f:
+ for line in f:
+ line = line.strip()
+ if token_line_re.match(line):
+ count += 1
+ return count
+
+
+def PrintUsage():
+ print "Usage: %s [--skip-tests] TOKEN_PATTERN DIRECTORY" % sys.argv[0]
+
+
+def main():
+ option_parser = optparse.OptionParser()
+ option_parser.add_option('', '--skip-tests', action='store_true',
+ dest='skip_tests', default=False,
+ help='Skip test files.')
+ options, args = option_parser.parse_args()
+
+ if len(args) < 2:
+ PrintUsage()
+ return 1
+ else:
+ print CountIfdefs(args[0], args[1], options.skip_tests)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/components/tools/metrics/count_ifdefs_unittest.py b/chromium/components/tools/metrics/count_ifdefs_unittest.py
new file mode 100755
index 00000000000..9b8c3229a0f
--- /dev/null
+++ b/chromium/components/tools/metrics/count_ifdefs_unittest.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# 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.
+
+"""Tests for count_ifdefs.
+"""
+
+import os
+import unittest
+
+import count_ifdefs
+
+
+class CountIfdefsTest(unittest.TestCase):
+
+ def setUp(self):
+ self.root = os.path.join(os.path.dirname(__file__), 'testdata')
+
+ def testNormal(self):
+ count = count_ifdefs.CountIfdefs('OS_[A-Z]+', self.root)
+ self.failUnless(count == 6)
+
+ def testSkipTests(self):
+ count = count_ifdefs.CountIfdefs('OS_[A-Z]+', self.root, True)
+ self.failUnless(count == 4)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chromium/components/tools/metrics/testdata/foo.cc b/chromium/components/tools/metrics/testdata/foo.cc
new file mode 100644
index 00000000000..bd3d3c716d2
--- /dev/null
+++ b/chromium/components/tools/metrics/testdata/foo.cc
@@ -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.
+
+// Should not match
+#ifndef FOO_OS_ANDROID_BLAT
+#define FOO_OS_ANDROID_BLAT
+
+#if defined(OS_ANDROID)
+
+#if defined(OS_ANDROID) || defined(OS_IOS) || defined(OS_CHROMEOS)
+
+#if !defined(OS_CAT)
+
+#if defined(OS_WIN)
+
+#endif // !OS_ANDROID && !OS_IOS
+#endif // OS_CAT
+
+#endif // FOO_OS_ANDROID_BLAT
diff --git a/chromium/components/tools/metrics/testdata/foo_ignored.txt b/chromium/components/tools/metrics/testdata/foo_ignored.txt
new file mode 100644
index 00000000000..2b186f93da4
--- /dev/null
+++ b/chromium/components/tools/metrics/testdata/foo_ignored.txt
@@ -0,0 +1,4 @@
+#if defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+
diff --git a/chromium/components/tools/metrics/testdata/subdir/foo_test.mm b/chromium/components/tools/metrics/testdata/subdir/foo_test.mm
new file mode 100644
index 00000000000..adf1f891ea9
--- /dev/null
+++ b/chromium/components/tools/metrics/testdata/subdir/foo_test.mm
@@ -0,0 +1,8 @@
+// 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.
+
+#if defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+
diff --git a/chromium/components/tracing.gyp b/chromium/components/tracing.gyp
new file mode 100644
index 00000000000..fe7e588f3ff
--- /dev/null
+++ b/chromium/components/tracing.gyp
@@ -0,0 +1,31 @@
+# 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 is intentionally a gyp file rather than a gypi for dependencies
+# reasons. The other gypi files include content.gyp and content_common depends
+# on this, thus if you try to rename this to gypi and include it in
+# components.gyp, you will get a circular dependency error.
+{
+ 'targets' : [
+ {
+ 'target_name': 'tracing',
+ 'type': 'static_library',
+ 'defines!': ['CONTENT_IMPLEMENTATION'],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ipc/ipc.gyp:ipc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'tracing/child_trace_message_filter.cc',
+ 'tracing/child_trace_message_filter.h',
+ 'tracing/tracing_messages.cc',
+ 'tracing/tracing_messages.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/tracing/DEPS b/chromium/components/tracing/DEPS
new file mode 100644
index 00000000000..bd05a3eaf55
--- /dev/null
+++ b/chromium/components/tracing/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+base",
+ "+ipc",
+
+ # This component will be compiled into NaCl, so it shouldn't depend on
+ # anything in content.
+ "-content",
+]
diff --git a/chromium/components/tracing/OWNERS b/chromium/components/tracing/OWNERS
new file mode 100644
index 00000000000..9201ab2fca0
--- /dev/null
+++ b/chromium/components/tracing/OWNERS
@@ -0,0 +1,12 @@
+jbauman@chromium.org
+nduca@chromium.org
+
+# Changes to IPC messages require a security review to avoid introducing
+# new sandbox escapes.
+per-file *_messages*.h=set noparent
+per-file *_messages*.h=cdn@chromium.org
+per-file *_messages*.h=cevans@chromium.org
+per-file *_messages*.h=jln@chromium.org
+per-file *_messages*.h=jschuh@chromium.org
+per-file *_messages*.h=palmer@chromium.org
+per-file *_messages*.h=tsepez@chromium.org
diff --git a/chromium/components/tracing/child_trace_message_filter.cc b/chromium/components/tracing/child_trace_message_filter.cc
new file mode 100644
index 00000000000..154903a15d0
--- /dev/null
+++ b/chromium/components/tracing/child_trace_message_filter.cc
@@ -0,0 +1,118 @@
+// 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/tracing/child_trace_message_filter.h"
+
+#include "base/debug/trace_event.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "components/tracing/tracing_messages.h"
+
+using base::debug::TraceLog;
+
+namespace tracing {
+
+ChildTraceMessageFilter::ChildTraceMessageFilter(
+ base::MessageLoopProxy* ipc_message_loop)
+ : channel_(NULL),
+ ipc_message_loop_(ipc_message_loop) {}
+
+void ChildTraceMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+ channel_ = channel;
+ TraceLog::GetInstance()->SetNotificationCallback(
+ base::Bind(&ChildTraceMessageFilter::OnTraceNotification, this));
+ channel_->Send(new TracingHostMsg_ChildSupportsTracing());
+}
+
+void ChildTraceMessageFilter::OnFilterRemoved() {
+ TraceLog::GetInstance()->SetNotificationCallback(
+ TraceLog::NotificationCallback());
+}
+
+bool ChildTraceMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(ChildTraceMessageFilter, message)
+ IPC_MESSAGE_HANDLER(TracingMsg_BeginTracing, OnBeginTracing)
+ IPC_MESSAGE_HANDLER(TracingMsg_EndTracing, OnEndTracing)
+ IPC_MESSAGE_HANDLER(TracingMsg_GetTraceBufferPercentFull,
+ OnGetTraceBufferPercentFull)
+ IPC_MESSAGE_HANDLER(TracingMsg_SetWatchEvent, OnSetWatchEvent)
+ IPC_MESSAGE_HANDLER(TracingMsg_CancelWatchEvent, OnCancelWatchEvent)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+ChildTraceMessageFilter::~ChildTraceMessageFilter() {}
+
+void ChildTraceMessageFilter::OnBeginTracing(
+ const std::string& category_filter_str,
+ base::TimeTicks browser_time,
+ int options) {
+#if defined(__native_client__)
+ // NaCl and system times are offset by a bit, so subtract some time from
+ // the captured timestamps. The value might be off by a bit due to messaging
+ // latency.
+ base::TimeDelta time_offset = base::TimeTicks::NowFromSystemTraceTime() -
+ browser_time;
+ TraceLog::GetInstance()->SetTimeOffset(time_offset);
+#endif
+ TraceLog::GetInstance()->SetEnabled(
+ base::debug::CategoryFilter(category_filter_str),
+ static_cast<base::debug::TraceLog::Options>(options));
+}
+
+void ChildTraceMessageFilter::OnEndTracing() {
+ TraceLog::GetInstance()->SetDisabled();
+
+ // Flush will generate one or more callbacks to OnTraceDataCollected. It's
+ // important that the last OnTraceDataCollected gets called before
+ // EndTracingAck below. We are already on the IO thread, so the
+ // OnTraceDataCollected calls will not be deferred.
+ TraceLog::GetInstance()->Flush(
+ base::Bind(&ChildTraceMessageFilter::OnTraceDataCollected, this));
+
+ std::vector<std::string> category_groups;
+ TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
+ channel_->Send(new TracingHostMsg_EndTracingAck(category_groups));
+}
+
+void ChildTraceMessageFilter::OnGetTraceBufferPercentFull() {
+ float bpf = TraceLog::GetInstance()->GetBufferPercentFull();
+
+ channel_->Send(new TracingHostMsg_TraceBufferPercentFullReply(bpf));
+}
+
+void ChildTraceMessageFilter::OnSetWatchEvent(const std::string& category_name,
+ const std::string& event_name) {
+ TraceLog::GetInstance()->SetWatchEvent(category_name.c_str(),
+ event_name.c_str());
+}
+
+void ChildTraceMessageFilter::OnCancelWatchEvent() {
+ TraceLog::GetInstance()->CancelWatchEvent();
+}
+
+void ChildTraceMessageFilter::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ if (!ipc_message_loop_->BelongsToCurrentThread()) {
+ ipc_message_loop_->PostTask(FROM_HERE,
+ base::Bind(&ChildTraceMessageFilter::OnTraceDataCollected, this,
+ events_str_ptr));
+ return;
+ }
+ channel_->Send(new TracingHostMsg_TraceDataCollected(
+ events_str_ptr->data()));
+}
+
+void ChildTraceMessageFilter::OnTraceNotification(int notification) {
+ if (!ipc_message_loop_->BelongsToCurrentThread()) {
+ ipc_message_loop_->PostTask(FROM_HERE,
+ base::Bind(&ChildTraceMessageFilter::OnTraceNotification, this,
+ notification));
+ return;
+ }
+ channel_->Send(new TracingHostMsg_TraceNotification(notification));
+}
+
+} // namespace tracing
diff --git a/chromium/components/tracing/child_trace_message_filter.h b/chromium/components/tracing/child_trace_message_filter.h
new file mode 100644
index 00000000000..9631ced7b34
--- /dev/null
+++ b/chromium/components/tracing/child_trace_message_filter.h
@@ -0,0 +1,55 @@
+// 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_TRACING_CHILD_TRACE_MESSAGE_FILTER_H_
+#define COMPONENTS_TRACING_CHILD_TRACE_MESSAGE_FILTER_H_
+
+#include "base/bind.h"
+#include "base/memory/ref_counted_memory.h"
+#include "ipc/ipc_channel_proxy.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace tracing {
+
+// This class sends and receives trace messages on child processes.
+class ChildTraceMessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ explicit ChildTraceMessageFilter(base::MessageLoopProxy* ipc_message_loop);
+
+ // IPC::ChannelProxy::MessageFilter implementation.
+ virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE;
+ virtual void OnFilterRemoved() OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ protected:
+ virtual ~ChildTraceMessageFilter();
+
+ private:
+ // Message handlers.
+ void OnBeginTracing(const std::string& category_filter_str,
+ base::TimeTicks browser_time,
+ int mode);
+ void OnEndTracing();
+ void OnGetTraceBufferPercentFull();
+ void OnSetWatchEvent(const std::string& category_name,
+ const std::string& event_name);
+ void OnCancelWatchEvent();
+
+ // Callback from trace subsystem.
+ void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& events_str_ptr);
+ void OnTraceNotification(int notification);
+
+ IPC::Channel* channel_;
+ base::MessageLoopProxy* ipc_message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildTraceMessageFilter);
+};
+
+} // namespace tracing
+
+#endif // COMPONENTS_TRACING_CHILD_TRACE_MESSAGE_FILTER_H_
diff --git a/chromium/components/tracing/tracing_messages.cc b/chromium/components/tracing/tracing_messages.cc
new file mode 100644
index 00000000000..3d7637ae6b7
--- /dev/null
+++ b/chromium/components/tracing/tracing_messages.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2010 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.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "components/tracing/tracing_messages.h"
+
+// Generate constructors.
+#include "ipc/struct_constructor_macros.h"
+#include "components/tracing/tracing_messages.h"
+
+// Generate destructors.
+#include "ipc/struct_destructor_macros.h"
+#include "components/tracing/tracing_messages.h"
+
+// Generate param traits write methods.
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#include "components/tracing/tracing_messages.h"
+} // namespace IPC
+
+// Generate param traits read methods.
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#include "components/tracing/tracing_messages.h"
+} // namespace IPC
+
+// Generate param traits log methods.
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#include "components/tracing/tracing_messages.h"
+} // namespace IPC
+
diff --git a/chromium/components/tracing/tracing_messages.h b/chromium/components/tracing/tracing_messages.h
new file mode 100644
index 00000000000..9beb7e1ec37
--- /dev/null
+++ b/chromium/components/tracing/tracing_messages.h
@@ -0,0 +1,56 @@
+// 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.
+
+// Multiply-included message header, no traditional include guard.
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/sync_socket.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_platform_file.h"
+
+#define IPC_MESSAGE_START TracingMsgStart
+
+// Sent to all child processes to enable trace event recording.
+IPC_MESSAGE_CONTROL3(TracingMsg_BeginTracing,
+ std::string /* category_filter_str */,
+ base::TimeTicks /* browser_time */,
+ int /* base::debug::TraceLog::Options */)
+
+// Sent to all child processes to disable trace event recording.
+IPC_MESSAGE_CONTROL0(TracingMsg_EndTracing)
+
+// Sent to all child processes to get trace buffer fullness.
+IPC_MESSAGE_CONTROL0(TracingMsg_GetTraceBufferPercentFull)
+
+// Sent to all child processes to set watch event.
+IPC_MESSAGE_CONTROL2(TracingMsg_SetWatchEvent,
+ std::string /* category_name */,
+ std::string /* event_name */)
+
+// Sent to all child processes to clear watch event.
+IPC_MESSAGE_CONTROL0(TracingMsg_CancelWatchEvent)
+
+// Notify the browser that this child process supports tracing.
+IPC_MESSAGE_CONTROL0(TracingHostMsg_ChildSupportsTracing)
+
+// Reply from child processes acking ChildProcessMsg_TraceChangeStatus(false).
+IPC_MESSAGE_CONTROL1(TracingHostMsg_EndTracingAck,
+ std::vector<std::string> /* known_categories */)
+
+// Sent if the trace buffer becomes full.
+IPC_MESSAGE_CONTROL1(TracingHostMsg_TraceNotification,
+ int /* base::debug::TraceLog::Notification */)
+
+// Child processes send trace data back in JSON chunks.
+IPC_MESSAGE_CONTROL1(TracingHostMsg_TraceDataCollected,
+ std::string /*json trace data*/)
+
+// Reply to TracingMsg_GetTraceBufferPercentFull.
+IPC_MESSAGE_CONTROL1(TracingHostMsg_TraceBufferPercentFullReply,
+ float /*trace buffer percent full*/)
+
diff --git a/chromium/components/tracing_untrusted.gyp b/chromium/components/tracing_untrusted.gyp
new file mode 100644
index 00000000000..f2215de6a1b
--- /dev/null
+++ b/chromium/components/tracing_untrusted.gyp
@@ -0,0 +1,42 @@
+# 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.
+
+{
+ 'includes': [
+ '../build/common_untrusted.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'tracing_untrusted',
+ 'type': 'none',
+ 'defines!': ['CONTENT_IMPLEMENTATION'],
+ 'dependencies': [
+ '../base/base_untrusted.gyp:base_untrusted',
+ '../native_client/tools.gyp:prep_toolchain',
+ '../ipc/ipc.gyp:ipc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libtracing_untrusted.a',
+ 'build_glibc': 0,
+ 'build_newlib': 1,
+ 'build_irt': 1,
+ },
+ 'sources': [
+ 'tracing/child_trace_message_filter.cc',
+ 'tracing/child_trace_message_filter.h',
+ 'tracing/tracing_messages.cc',
+ 'tracing/tracing_messages.h',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/user_prefs.gypi b/chromium/components/user_prefs.gypi
new file mode 100644
index 00000000000..a556e708ce3
--- /dev/null
+++ b/chromium/components/user_prefs.gypi
@@ -0,0 +1,32 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'user_prefs',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_prefs',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../content/content.gyp:content_browser',
+ '../ui/ui.gyp:ui',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'defines': [
+ 'USER_PREFS_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'user_prefs/pref_registry_syncable.cc',
+ 'user_prefs/pref_registry_syncable.h',
+ 'user_prefs/user_prefs.cc',
+ 'user_prefs/user_prefs.h',
+ 'user_prefs/user_prefs_export.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/user_prefs/DEPS b/chromium/components/user_prefs/DEPS
new file mode 100644
index 00000000000..c36190b429d
--- /dev/null
+++ b/chromium/components/user_prefs/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/browser",
+ "+ui/base",
+]
diff --git a/chromium/components/user_prefs/OWNERS b/chromium/components/user_prefs/OWNERS
new file mode 100644
index 00000000000..024da1cb54c
--- /dev/null
+++ b/chromium/components/user_prefs/OWNERS
@@ -0,0 +1,4 @@
+battre@chromium.org
+bauerb@chromium.org
+mnissler@chromium.org
+pam@chromium.org
diff --git a/chromium/components/user_prefs/README b/chromium/components/user_prefs/README
new file mode 100644
index 00000000000..a7ca50936d2
--- /dev/null
+++ b/chromium/components/user_prefs/README
@@ -0,0 +1,8 @@
+The //components/user_pref component provides:
+
+a) The UserPrefs class, where components dependent on looking up
+preferences associated with users can look them up by
+content::BrowserContext.
+
+b) A place for PrefRegistrySyncable to live, where it can be used by
+components that need to register preferences associated with users.
diff --git a/chromium/components/user_prefs/pref_registry_syncable.cc b/chromium/components/user_prefs/pref_registry_syncable.cc
new file mode 100644
index 00000000000..4ffd6393ba4
--- /dev/null
+++ b/chromium/components/user_prefs/pref_registry_syncable.cc
@@ -0,0 +1,225 @@
+// 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/user_prefs/pref_registry_syncable.h"
+
+#include "base/files/file_path.h"
+#include "base/prefs/default_pref_store.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace user_prefs {
+
+namespace {
+
+// A helper function for RegisterLocalized*Pref that creates a Value*
+// based on a localized resource. Because we control the values in a
+// locale dll, this should always return a Value of the appropriate
+// type.
+base::Value* CreateLocaleDefaultValue(base::Value::Type type,
+ int message_id) {
+ const std::string resource_string = l10n_util::GetStringUTF8(message_id);
+ DCHECK(!resource_string.empty());
+ switch (type) {
+ case Value::TYPE_BOOLEAN: {
+ if ("true" == resource_string)
+ return Value::CreateBooleanValue(true);
+ if ("false" == resource_string)
+ return Value::CreateBooleanValue(false);
+ break;
+ }
+
+ case Value::TYPE_INTEGER: {
+ int val;
+ base::StringToInt(resource_string, &val);
+ return Value::CreateIntegerValue(val);
+ }
+
+ case Value::TYPE_DOUBLE: {
+ double val;
+ base::StringToDouble(resource_string, &val);
+ return Value::CreateDoubleValue(val);
+ }
+
+ case Value::TYPE_STRING: {
+ return Value::CreateStringValue(resource_string);
+ }
+
+ default: {
+ NOTREACHED() <<
+ "list and dictionary types cannot have default locale values";
+ }
+ }
+ NOTREACHED();
+ return Value::CreateNullValue();
+}
+
+} // namespace
+
+PrefRegistrySyncable::PrefRegistrySyncable() {
+}
+
+PrefRegistrySyncable::~PrefRegistrySyncable() {
+}
+
+const PrefRegistrySyncable::PrefToStatus&
+PrefRegistrySyncable::syncable_preferences() const {
+ return syncable_preferences_;
+}
+
+void PrefRegistrySyncable::SetSyncableRegistrationCallback(
+ const SyncableRegistrationCallback& cb) {
+ callback_ = cb;
+}
+
+void PrefRegistrySyncable::RegisterBooleanPref(const char* path,
+ bool default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path,
+ Value::CreateBooleanValue(default_value),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterIntegerPref(const char* path,
+ int default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path,
+ Value::CreateIntegerValue(default_value),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterDoublePref(const char* path,
+ double default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path,
+ Value::CreateDoubleValue(default_value),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterStringPref(const char* path,
+ const std::string& default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path,
+ Value::CreateStringValue(default_value),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterFilePathPref(
+ const char* path,
+ const base::FilePath& default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path,
+ Value::CreateStringValue(default_value.value()),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterListPref(const char* path,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path, new ListValue(), sync_status);
+}
+
+void PrefRegistrySyncable::RegisterListPref(const char* path,
+ ListValue* default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path, default_value, sync_status);
+}
+
+void PrefRegistrySyncable::RegisterDictionaryPref(const char* path,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path, new DictionaryValue(), sync_status);
+}
+
+void PrefRegistrySyncable::RegisterDictionaryPref(
+ const char* path,
+ DictionaryValue* default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(path, default_value, sync_status);
+}
+
+void PrefRegistrySyncable::RegisterLocalizedBooleanPref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(
+ path,
+ CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterLocalizedIntegerPref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(
+ path,
+ CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterLocalizedDoublePref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(
+ path,
+ CreateLocaleDefaultValue(Value::TYPE_DOUBLE, locale_default_message_id),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterLocalizedStringPref(
+ const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(
+ path,
+ CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterInt64Pref(
+ const char* path,
+ int64 default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(
+ path,
+ Value::CreateStringValue(base::Int64ToString(default_value)),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterUint64Pref(
+ const char* path,
+ uint64 default_value,
+ PrefSyncStatus sync_status) {
+ RegisterSyncablePreference(
+ path,
+ Value::CreateStringValue(base::Uint64ToString(default_value)),
+ sync_status);
+}
+
+void PrefRegistrySyncable::RegisterSyncablePreference(
+ const char* path,
+ base::Value* default_value,
+ PrefSyncStatus sync_status) {
+ PrefRegistry::RegisterPreference(path, default_value);
+
+ if (sync_status == PrefRegistrySyncable::SYNCABLE_PREF ||
+ sync_status == PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF) {
+ syncable_preferences_[path] = sync_status;
+
+ if (!callback_.is_null())
+ callback_.Run(path, sync_status);
+ }
+}
+
+scoped_refptr<PrefRegistrySyncable> PrefRegistrySyncable::ForkForIncognito() {
+ // TODO(joi): We can directly reuse the same PrefRegistry once
+ // PrefService no longer registers for callbacks on registration and
+ // unregistration.
+ scoped_refptr<PrefRegistrySyncable> registry(new PrefRegistrySyncable());
+ registry->defaults_ = defaults_;
+ return registry;
+}
+
+} // namespace user_prefs
diff --git a/chromium/components/user_prefs/pref_registry_syncable.h b/chromium/components/user_prefs/pref_registry_syncable.h
new file mode 100644
index 00000000000..9134d0b09d2
--- /dev/null
+++ b/chromium/components/user_prefs/pref_registry_syncable.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_USER_PREFS_PREF_REGISTRY_SYNCABLE_H_
+#define COMPONENTS_USER_PREFS_PREF_REGISTRY_SYNCABLE_H_
+
+#include <set>
+#include <string>
+
+#include "base/prefs/pref_registry.h"
+#include "components/user_prefs/user_prefs_export.h"
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+class ListValue;
+class Value;
+}
+
+namespace user_prefs {
+
+// A PrefRegistry that forces users to choose whether each registered
+// preference is syncable or not.
+//
+// Classes or components that want to register such preferences should
+// define a static function named RegisterUserPrefs that takes a
+// PrefRegistrySyncable*, and the top-level application using the
+// class or embedding the component should call this function at an
+// appropriate time before the PrefService for these preferences is
+// constructed. See e.g. chrome/browser/prefs/browser_prefs.cc which
+// does this for Chrome.
+class USER_PREFS_EXPORT PrefRegistrySyncable : public PrefRegistry {
+ public:
+ // Enum used when registering preferences to determine if it should
+ // be synced or not. Syncable priority preferences are preferences that are
+ // never encrypted and are synced before other datatypes. Because they're
+ // never encrypted, on first sync, they can be synced down before the user
+ // is prompted for a passphrase.
+ enum PrefSyncStatus {
+ UNSYNCABLE_PREF,
+ SYNCABLE_PREF,
+ SYNCABLE_PRIORITY_PREF,
+ };
+
+ typedef
+ base::Callback<void(const char* path, const PrefSyncStatus sync_status)>
+ SyncableRegistrationCallback;
+
+ PrefRegistrySyncable();
+
+ typedef std::map<std::string, PrefSyncStatus> PrefToStatus;
+
+ // Retrieve the set of syncable preferences currently registered.
+ const PrefToStatus& syncable_preferences() const;
+
+ // Exactly one callback can be set for the event of a syncable
+ // preference being registered. It will be fired after the
+ // registration has occurred.
+ //
+ // Calling this method after a callback has already been set will
+ // make the object forget the previous callback and use the new one
+ // instead.
+ void SetSyncableRegistrationCallback(const SyncableRegistrationCallback& cb);
+
+ void RegisterBooleanPref(const char* path,
+ bool default_value,
+ PrefSyncStatus sync_status);
+ void RegisterIntegerPref(const char* path,
+ int default_value,
+ PrefSyncStatus sync_status);
+ void RegisterDoublePref(const char* path,
+ double default_value,
+ PrefSyncStatus sync_status);
+ void RegisterStringPref(const char* path,
+ const std::string& default_value,
+ PrefSyncStatus sync_status);
+ void RegisterFilePathPref(const char* path,
+ const base::FilePath& default_value,
+ PrefSyncStatus sync_status);
+ void RegisterListPref(const char* path,
+ PrefSyncStatus sync_status);
+ void RegisterDictionaryPref(const char* path,
+ PrefSyncStatus sync_status);
+ void RegisterListPref(const char* path,
+ base::ListValue* default_value,
+ PrefSyncStatus sync_status);
+ void RegisterDictionaryPref(const char* path,
+ base::DictionaryValue* default_value,
+ PrefSyncStatus sync_status);
+ void RegisterLocalizedBooleanPref(const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status);
+ void RegisterLocalizedIntegerPref(const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status);
+ void RegisterLocalizedDoublePref(const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status);
+ void RegisterLocalizedStringPref(const char* path,
+ int locale_default_message_id,
+ PrefSyncStatus sync_status);
+ void RegisterInt64Pref(const char* path,
+ int64 default_value,
+ PrefSyncStatus sync_status);
+ void RegisterUint64Pref(const char* path,
+ uint64 default_value,
+ PrefSyncStatus sync_status);
+
+ // Returns a new PrefRegistrySyncable that uses the same defaults
+ // store.
+ scoped_refptr<PrefRegistrySyncable> ForkForIncognito();
+
+ private:
+ virtual ~PrefRegistrySyncable();
+
+ void RegisterSyncablePreference(const char* path,
+ base::Value* default_value,
+ PrefSyncStatus sync_status);
+
+ SyncableRegistrationCallback callback_;
+
+ // Contains the names of all registered preferences that are syncable.
+ PrefToStatus syncable_preferences_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefRegistrySyncable);
+};
+
+} // namespace user_prefs
+
+#endif // COMPONENTS_USER_PREFS_PREF_REGISTRY_SYNCABLE_H_
diff --git a/chromium/components/user_prefs/user_prefs.cc b/chromium/components/user_prefs/user_prefs.cc
new file mode 100644
index 00000000000..8ecdfe4bc93
--- /dev/null
+++ b/chromium/components/user_prefs/user_prefs.cc
@@ -0,0 +1,47 @@
+// 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/user_prefs/user_prefs.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/prefs/pref_service.h"
+#include "content/public/browser/browser_context.h"
+
+namespace user_prefs {
+
+namespace {
+
+void* UserDataKey() {
+ // We just need a unique constant. Use the address of a static that
+ // COMDAT folding won't touch in an optimizing linker.
+ static int data_key = 0;
+ return reinterpret_cast<void*>(&data_key);
+}
+
+} // namespace
+
+// static
+PrefService* UserPrefs::Get(content::BrowserContext* context) {
+ DCHECK(context);
+ DCHECK(context->GetUserData(UserDataKey()));
+ return static_cast<UserPrefs*>(
+ context->GetUserData(UserDataKey()))->prefs_;
+}
+
+// static
+void UserPrefs::Set(content::BrowserContext* context, PrefService* prefs) {
+ DCHECK(context);
+ DCHECK(prefs);
+ DCHECK(!context->GetUserData(UserDataKey()));
+ context->SetUserData(UserDataKey(), new UserPrefs(prefs));
+}
+
+UserPrefs::UserPrefs(PrefService* prefs) : prefs_(prefs) {
+}
+
+UserPrefs::~UserPrefs() {
+}
+
+} // namespace user_prefs
diff --git a/chromium/components/user_prefs/user_prefs.h b/chromium/components/user_prefs/user_prefs.h
new file mode 100644
index 00000000000..90bf9009399
--- /dev/null
+++ b/chromium/components/user_prefs/user_prefs.h
@@ -0,0 +1,48 @@
+// 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_USER_PREFS_USER_PREFS_H_
+#define COMPONENTS_USER_PREFS_USER_PREFS_H_
+
+#include "base/basictypes.h"
+#include "base/supports_user_data.h"
+#include "components/user_prefs/user_prefs_export.h"
+
+class PrefService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace user_prefs {
+
+// Components may use preferences associated with a given user. These
+// hang off of content::BrowserContext and can be retrieved using
+// UserPrefs::Get().
+//
+// It is up to the embedder to create and own the PrefService and
+// attach it to BrowserContext using the UserPrefs::Set() function.
+class USER_PREFS_EXPORT UserPrefs : public base::SupportsUserData::Data {
+ public:
+ // Retrieves the PrefService for a given BrowserContext, or NULL if
+ // none is attached.
+ static PrefService* Get(content::BrowserContext* context);
+
+ // Hangs the specified |prefs| off of |context|. Should be called
+ // only once per BrowserContext.
+ static void Set(content::BrowserContext* context, PrefService* prefs);
+
+ private:
+ explicit UserPrefs(PrefService* prefs);
+ virtual ~UserPrefs();
+
+ // Non-owning; owned by embedder.
+ PrefService* prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserPrefs);
+};
+
+} // namespace user_prefs
+
+#endif // COMPONENTS_USER_PREFS_USER_PREFS_H_
diff --git a/chromium/components/user_prefs/user_prefs_export.h b/chromium/components/user_prefs/user_prefs_export.h
new file mode 100644
index 00000000000..3d20222a2b9
--- /dev/null
+++ b/chromium/components/user_prefs/user_prefs_export.h
@@ -0,0 +1,29 @@
+// 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_USER_PREFS_USER_PREFS_EXPORT_H_
+#define COMPONENTS_USER_PREFS_USER_PREFS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(USER_PREFS_IMPLEMENTATION)
+#define USER_PREFS_EXPORT __declspec(dllexport)
+#else
+#define USER_PREFS_EXPORT __declspec(dllimport)
+#endif // defined(BASE_PREFS_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(USER_PREFS_IMPLEMENTATION)
+#define USER_PREFS_EXPORT __attribute__((visibility("default")))
+#else
+#define USER_PREFS_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define USER_PREFS_EXPORT
+#endif
+
+#endif // COMPONENTS_USER_PREFS_USER_PREFS_EXPORT_H_
diff --git a/chromium/components/visitedlink.gypi b/chromium/components/visitedlink.gypi
new file mode 100644
index 00000000000..bca40f2f9b5
--- /dev/null
+++ b/chromium/components/visitedlink.gypi
@@ -0,0 +1,66 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'visitedlink_common',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_common',
+ '../ipc/ipc.gyp:ipc',
+ '../url/url.gyp:url_lib',
+ ],
+ 'sources': [
+ 'visitedlink/common/visitedlink_common.cc',
+ 'visitedlink/common/visitedlink_common.h',
+ 'visitedlink/common/visitedlink_message_generator.cc',
+ 'visitedlink/common/visitedlink_message_generator.h',
+ 'visitedlink/common/visitedlink_messages.h',
+ ],
+ },
+ {
+ 'target_name': 'visitedlink_browser',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../skia/config',
+ ],
+ 'dependencies': [
+ 'visitedlink_common',
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../content/content.gyp:content_common',
+ ],
+ 'sources': [
+ 'visitedlink/browser/visitedlink_delegate.h',
+ 'visitedlink/browser/visitedlink_event_listener.cc',
+ 'visitedlink/browser/visitedlink_event_listener.h',
+ 'visitedlink/browser/visitedlink_master.cc',
+ 'visitedlink/browser/visitedlink_master.h',
+ ],
+ }
+ ],
+ 'conditions': [
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ 'target_name': 'visitedlink_renderer',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'visitedlink_common',
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_common',
+ '../content/content.gyp:content_renderer',
+ '../third_party/WebKit/public/blink.gyp:blink',
+ ],
+ 'sources': [
+ 'visitedlink/renderer/visitedlink_slave.cc',
+ 'visitedlink/renderer/visitedlink_slave.h',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/visitedlink/OWNERS b/chromium/components/visitedlink/OWNERS
new file mode 100644
index 00000000000..b221410c272
--- /dev/null
+++ b/chromium/components/visitedlink/OWNERS
@@ -0,0 +1,2 @@
+brettw@chromium.org
+sky@chromium.org
diff --git a/chromium/components/visitedlink/browser/DEPS b/chromium/components/visitedlink/browser/DEPS
new file mode 100644
index 00000000000..1c35d9ca694
--- /dev/null
+++ b/chromium/components/visitedlink/browser/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/browser",
+]
diff --git a/chromium/components/visitedlink/browser/visitedlink_delegate.h b/chromium/components/visitedlink/browser/visitedlink_delegate.h
new file mode 100644
index 00000000000..6adf7de5718
--- /dev/null
+++ b/chromium/components/visitedlink/browser/visitedlink_delegate.h
@@ -0,0 +1,51 @@
+// 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_VISITEDLINK_BROWSER_VISITEDLINK_DELEGATE_H_
+#define COMPONENTS_VISITEDLINK_BROWSER_VISITEDLINK_DELEGATE_H_
+
+#include "base/memory/ref_counted.h"
+
+class GURL;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace visitedlink {
+
+// Delegate class that clients of VisitedLinkMaster must implement.
+class VisitedLinkDelegate {
+ public:
+ // See RebuildTable.
+ class URLEnumerator : public base::RefCountedThreadSafe<URLEnumerator> {
+ public:
+ // Call this with each URL to rebuild the table.
+ virtual void OnURL(const GURL& url) = 0;
+
+ // This must be called by Delegate after RebuildTable is called. |success|
+ // indicates all URLs have been returned successfully. The URLEnumerator
+ // object cannot be used by the delegate after this call.
+ virtual void OnComplete(bool success) = 0;
+
+ protected:
+ virtual ~URLEnumerator() {}
+
+ private:
+ friend class base::RefCountedThreadSafe<URLEnumerator>;
+ };
+
+ // Delegate class is responsible for persisting the list of visited URLs
+ // across browser runs. This is called by VisitedLinkMaster to repopulate
+ // its internal table. Note that methods on enumerator can be called on any
+ // thread but the delegate is responsible for synchronizating the calls.
+ virtual void RebuildTable(const scoped_refptr<URLEnumerator>& enumerator) = 0;
+
+ protected:
+ virtual ~VisitedLinkDelegate() {}
+};
+
+} // namespace visitedlink
+
+#endif // COMPONENTS_VISITEDLINK_BROWSER_VISITEDLINK_DELEGATE_H_
diff --git a/chromium/components/visitedlink/browser/visitedlink_event_listener.cc b/chromium/components/visitedlink/browser/visitedlink_event_listener.cc
new file mode 100644
index 00000000000..a96a0e3aa55
--- /dev/null
+++ b/chromium/components/visitedlink/browser/visitedlink_event_listener.cc
@@ -0,0 +1,219 @@
+// 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/visitedlink/browser/visitedlink_event_listener.h"
+
+#include "base/memory/shared_memory.h"
+#include "components/visitedlink/browser/visitedlink_delegate.h"
+#include "components/visitedlink/common/visitedlink_messages.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host.h"
+
+using base::Time;
+using base::TimeDelta;
+using content::RenderWidgetHost;
+
+namespace {
+
+// The amount of time we wait to accumulate visited link additions.
+const int kCommitIntervalMs = 100;
+
+// Size of the buffer after which individual link updates deemed not warranted
+// and the overall update should be used instead.
+const unsigned kVisitedLinkBufferThreshold = 50;
+
+} // namespace
+
+namespace visitedlink {
+
+// This class manages buffering and sending visited link hashes (fingerprints)
+// to renderer based on widget visibility.
+// As opposed to the VisitedLinkEventListener, which coalesces to
+// reduce the rate of messages being sent to render processes, this class
+// ensures that the updates occur only when explicitly requested. This is
+// used for RenderProcessHostImpl to only send Add/Reset link events to the
+// renderers when their tabs are visible and the corresponding RenderViews are
+// created.
+class VisitedLinkUpdater {
+ public:
+ explicit VisitedLinkUpdater(int render_process_id)
+ : reset_needed_(false), render_process_id_(render_process_id) {
+ }
+
+ // Informs the renderer about a new visited link table.
+ void SendVisitedLinkTable(base::SharedMemory* table_memory) {
+ content::RenderProcessHost* process =
+ content::RenderProcessHost::FromID(render_process_id_);
+ if (!process)
+ return; // Happens in tests
+ base::SharedMemoryHandle handle_for_process;
+ table_memory->ShareToProcess(process->GetHandle(), &handle_for_process);
+ if (base::SharedMemory::IsHandleValid(handle_for_process))
+ process->Send(new ChromeViewMsg_VisitedLink_NewTable(
+ handle_for_process));
+ }
+
+ // Buffers |links| to update, but doesn't actually relay them.
+ void AddLinks(const VisitedLinkCommon::Fingerprints& links) {
+ if (reset_needed_)
+ return;
+
+ if (pending_.size() + links.size() > kVisitedLinkBufferThreshold) {
+ // Once the threshold is reached, there's no need to store pending visited
+ // link updates -- we opt for resetting the state for all links.
+ AddReset();
+ return;
+ }
+
+ pending_.insert(pending_.end(), links.begin(), links.end());
+ }
+
+ // Tells the updater that sending individual link updates is no longer
+ // necessary and the visited state for all links should be reset.
+ void AddReset() {
+ reset_needed_ = true;
+ pending_.clear();
+ }
+
+ // Sends visited link update messages: a list of links whose visited state
+ // changed or reset of visited state for all links.
+ void Update() {
+ content::RenderProcessHost* process =
+ content::RenderProcessHost::FromID(render_process_id_);
+ if (!process)
+ return; // Happens in tests
+
+ if (!process->VisibleWidgetCount())
+ return;
+
+ if (reset_needed_) {
+ process->Send(new ChromeViewMsg_VisitedLink_Reset());
+ reset_needed_ = false;
+ return;
+ }
+
+ if (pending_.empty())
+ return;
+
+ process->Send(new ChromeViewMsg_VisitedLink_Add(pending_));
+
+ pending_.clear();
+ }
+
+ private:
+ bool reset_needed_;
+ int render_process_id_;
+ VisitedLinkCommon::Fingerprints pending_;
+};
+
+VisitedLinkEventListener::VisitedLinkEventListener(
+ VisitedLinkMaster* master,
+ content::BrowserContext* browser_context)
+ : master_(master),
+ browser_context_(browser_context) {
+ registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
+ content::NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+ content::NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
+ content::NotificationService::AllBrowserContextsAndSources());
+}
+
+VisitedLinkEventListener::~VisitedLinkEventListener() {
+ if (!pending_visited_links_.empty())
+ pending_visited_links_.clear();
+}
+
+void VisitedLinkEventListener::NewTable(base::SharedMemory* table_memory) {
+ if (!table_memory)
+ return;
+
+ // Send to all RenderProcessHosts.
+ for (Updaters::iterator i = updaters_.begin(); i != updaters_.end(); ++i) {
+ // Make sure to not send to incognito renderers.
+ content::RenderProcessHost* process =
+ content::RenderProcessHost::FromID(i->first);
+ if (!process)
+ continue;
+
+ i->second->SendVisitedLinkTable(table_memory);
+ }
+}
+
+void VisitedLinkEventListener::Add(VisitedLinkMaster::Fingerprint fingerprint) {
+ pending_visited_links_.push_back(fingerprint);
+
+ if (!coalesce_timer_.IsRunning()) {
+ coalesce_timer_.Start(FROM_HERE,
+ TimeDelta::FromMilliseconds(kCommitIntervalMs), this,
+ &VisitedLinkEventListener::CommitVisitedLinks);
+ }
+}
+
+void VisitedLinkEventListener::Reset() {
+ pending_visited_links_.clear();
+ coalesce_timer_.Stop();
+
+ for (Updaters::iterator i = updaters_.begin(); i != updaters_.end(); ++i) {
+ i->second->AddReset();
+ i->second->Update();
+ }
+}
+
+void VisitedLinkEventListener::CommitVisitedLinks() {
+ // Send to all RenderProcessHosts.
+ for (Updaters::iterator i = updaters_.begin(); i != updaters_.end(); ++i) {
+ i->second->AddLinks(pending_visited_links_);
+ i->second->Update();
+ }
+
+ pending_visited_links_.clear();
+}
+
+void VisitedLinkEventListener::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ switch (type) {
+ case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
+ content::RenderProcessHost* process =
+ content::Source<content::RenderProcessHost>(source).ptr();
+ if (browser_context_ != process->GetBrowserContext())
+ return;
+
+ // Happens on browser start up.
+ if (!master_->shared_memory())
+ return;
+
+ updaters_[process->GetID()] =
+ make_linked_ptr(new VisitedLinkUpdater(process->GetID()));
+ updaters_[process->GetID()]->SendVisitedLinkTable(
+ master_->shared_memory());
+ break;
+ }
+ case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
+ content::RenderProcessHost* process =
+ content::Source<content::RenderProcessHost>(source).ptr();
+ if (updaters_.count(process->GetID())) {
+ updaters_.erase(process->GetID());
+ }
+ break;
+ }
+ case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
+ RenderWidgetHost* widget =
+ content::Source<RenderWidgetHost>(source).ptr();
+ int child_id = widget->GetProcess()->GetID();
+ if (updaters_.count(child_id))
+ updaters_[child_id]->Update();
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+} // namespace visitedlink
diff --git a/chromium/components/visitedlink/browser/visitedlink_event_listener.h b/chromium/components/visitedlink/browser/visitedlink_event_listener.h
new file mode 100644
index 00000000000..c2de5d205d0
--- /dev/null
+++ b/chromium/components/visitedlink/browser/visitedlink_event_listener.h
@@ -0,0 +1,70 @@
+// 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_VISITEDLINK_BROWSER_VISITEDLINK_EVENT_LISTENER_H_
+#define COMPONENTS_VISITEDLINK_BROWSER_VISITEDLINK_EVENT_LISTENER_H_
+
+#include <map>
+
+#include "base/memory/linked_ptr.h"
+#include "base/timer/timer.h"
+#include "components/visitedlink/browser/visitedlink_master.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace base {
+class SharedMemory;
+}
+
+namespace content {
+class BrowserContext;
+}
+
+namespace visitedlink {
+
+class VisitedLinkUpdater;
+
+// VisitedLinkEventListener broadcasts link coloring database updates to all
+// processes. It also coalesces the updates to avoid excessive broadcasting of
+// messages to the renderers.
+class VisitedLinkEventListener : public VisitedLinkMaster::Listener,
+ public content::NotificationObserver {
+ public:
+ VisitedLinkEventListener(VisitedLinkMaster* master,
+ content::BrowserContext* browser_context);
+ virtual ~VisitedLinkEventListener();
+
+ virtual void NewTable(base::SharedMemory* table_memory) OVERRIDE;
+ virtual void Add(VisitedLinkMaster::Fingerprint fingerprint) OVERRIDE;
+ virtual void Reset() OVERRIDE;
+
+ private:
+ void CommitVisitedLinks();
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ base::OneShotTimer<VisitedLinkEventListener> coalesce_timer_;
+ VisitedLinkCommon::Fingerprints pending_visited_links_;
+
+ content::NotificationRegistrar registrar_;
+
+ // Map between renderer child ids and their VisitedLinkUpdater.
+ typedef std::map<int, linked_ptr<VisitedLinkUpdater> > Updaters;
+ Updaters updaters_;
+
+ VisitedLinkMaster* master_;
+
+ // Used to filter RENDERER_PROCESS_CREATED notifications to renderers that
+ // belong to this BrowserContext.
+ content::BrowserContext* browser_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisitedLinkEventListener);
+};
+
+} // namespace visitedlink
+
+#endif // COMPONENTS_VISITEDLINK_BROWSER_VISITEDLINK_EVENT_LISTENER_H_
diff --git a/chromium/components/visitedlink/browser/visitedlink_master.cc b/chromium/components/visitedlink/browser/visitedlink_master.cc
new file mode 100644
index 00000000000..7cf68911af7
--- /dev/null
+++ b/chromium/components/visitedlink/browser/visitedlink_master.cc
@@ -0,0 +1,989 @@
+// 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/visitedlink/browser/visitedlink_master.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <io.h>
+#include <shlobj.h>
+#endif // defined(OS_WIN)
+#include <stdio.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/containers/stack_container.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/rand_util.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "components/visitedlink/browser/visitedlink_delegate.h"
+#include "components/visitedlink/browser/visitedlink_event_listener.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "url/gurl.h"
+
+using content::BrowserThread;
+using file_util::ScopedFILE;
+using file_util::OpenFile;
+using file_util::TruncateFile;
+
+namespace visitedlink {
+
+const int32 VisitedLinkMaster::kFileHeaderSignatureOffset = 0;
+const int32 VisitedLinkMaster::kFileHeaderVersionOffset = 4;
+const int32 VisitedLinkMaster::kFileHeaderLengthOffset = 8;
+const int32 VisitedLinkMaster::kFileHeaderUsedOffset = 12;
+const int32 VisitedLinkMaster::kFileHeaderSaltOffset = 16;
+
+const int32 VisitedLinkMaster::kFileCurrentVersion = 3;
+
+// the signature at the beginning of the URL table = "VLnk" (visited links)
+const int32 VisitedLinkMaster::kFileSignature = 0x6b6e4c56;
+const size_t VisitedLinkMaster::kFileHeaderSize =
+ kFileHeaderSaltOffset + LINK_SALT_LENGTH;
+
+// This value should also be the same as the smallest size in the lookup
+// table in NewTableSizeForCount (prime number).
+const unsigned VisitedLinkMaster::kDefaultTableSize = 16381;
+
+const size_t VisitedLinkMaster::kBigDeleteThreshold = 64;
+
+namespace {
+
+// Fills the given salt structure with some quasi-random values
+// It is not necessary to generate a cryptographically strong random string,
+// only that it be reasonably different for different users.
+void GenerateSalt(uint8 salt[LINK_SALT_LENGTH]) {
+ DCHECK_EQ(LINK_SALT_LENGTH, 8) << "This code assumes the length of the salt";
+ uint64 randval = base::RandUint64();
+ memcpy(salt, &randval, 8);
+}
+
+// Opens file on a background thread to not block UI thread.
+void AsyncOpen(FILE** file, const base::FilePath& filename) {
+ *file = OpenFile(filename, "wb+");
+ DLOG_IF(ERROR, !(*file)) << "Failed to open file " << filename.value();
+}
+
+// Returns true if the write was complete.
+static bool WriteToFile(FILE* file,
+ off_t offset,
+ const void* data,
+ size_t data_len) {
+ if (fseek(file, offset, SEEK_SET) != 0)
+ return false; // Don't write to an invalid part of the file.
+
+ size_t num_written = fwrite(data, 1, data_len, file);
+
+ // The write may not make it to the kernel (stdlib may buffer the write)
+ // until the next fseek/fclose call. If we crash, it's easy for our used
+ // item count to be out of sync with the number of hashes we write.
+ // Protect against this by calling fflush.
+ int ret = fflush(file);
+ DCHECK_EQ(0, ret);
+ return num_written == data_len;
+}
+
+// This task executes on a background thread and executes a write. This
+// prevents us from blocking the UI thread doing I/O. Double pointer to FILE
+// is used because file may still not be opened by the time of scheduling
+// the task for execution.
+void AsyncWrite(FILE** file, int32 offset, const std::string& data) {
+ if (*file)
+ WriteToFile(*file, offset, data.data(), data.size());
+}
+
+// Truncates the file to the current position asynchronously on a background
+// thread. Double pointer to FILE is used because file may still not be opened
+// by the time of scheduling the task for execution.
+void AsyncTruncate(FILE** file) {
+ if (*file)
+ base::IgnoreResult(TruncateFile(*file));
+}
+
+// Closes the file on a background thread and releases memory used for storage
+// of FILE* value. Double pointer to FILE is used because file may still not
+// be opened by the time of scheduling the task for execution.
+void AsyncClose(FILE** file) {
+ if (*file)
+ base::IgnoreResult(fclose(*file));
+ free(file);
+}
+
+} // namespace
+
+// TableBuilder ---------------------------------------------------------------
+
+// How rebuilding from history works
+// ---------------------------------
+//
+// We mark that we're rebuilding from history by setting the table_builder_
+// member in VisitedLinkMaster to the TableBuilder we create. This builder
+// will be called on the history thread by the history system for every URL
+// in the database.
+//
+// The builder will store the fingerprints for those URLs, and then marshalls
+// back to the main thread where the VisitedLinkMaster will be notified. The
+// master then replaces its table with a new table containing the computed
+// fingerprints.
+//
+// The builder must remain active while the history system is using it.
+// Sometimes, the master will be deleted before the rebuild is complete, in
+// which case it notifies the builder via DisownMaster(). The builder will
+// delete itself once rebuilding is complete, and not execute any callback.
+class VisitedLinkMaster::TableBuilder
+ : public VisitedLinkDelegate::URLEnumerator {
+ public:
+ TableBuilder(VisitedLinkMaster* master,
+ const uint8 salt[LINK_SALT_LENGTH]);
+
+ // Called on the main thread when the master is being destroyed. This will
+ // prevent a crash when the query completes and the master is no longer
+ // around. We can not actually do anything but mark this fact, since the
+ // table will be being rebuilt simultaneously on the other thread.
+ void DisownMaster();
+
+ // VisitedLinkDelegate::URLEnumerator
+ virtual void OnURL(const GURL& url) OVERRIDE;
+ virtual void OnComplete(bool succeed) OVERRIDE;
+
+ private:
+ virtual ~TableBuilder() {}
+
+ // OnComplete mashals to this function on the main thread to do the
+ // notification.
+ void OnCompleteMainThread();
+
+ // Owner of this object. MAY ONLY BE ACCESSED ON THE MAIN THREAD!
+ VisitedLinkMaster* master_;
+
+ // Indicates whether the operation has failed or not.
+ bool success_;
+
+ // Salt for this new table.
+ uint8 salt_[LINK_SALT_LENGTH];
+
+ // Stores the fingerprints we computed on the background thread.
+ VisitedLinkCommon::Fingerprints fingerprints_;
+
+ DISALLOW_COPY_AND_ASSIGN(TableBuilder);
+};
+
+// VisitedLinkMaster ----------------------------------------------------------
+
+VisitedLinkMaster::VisitedLinkMaster(content::BrowserContext* browser_context,
+ VisitedLinkDelegate* delegate,
+ bool persist_to_disk)
+ : browser_context_(browser_context),
+ delegate_(delegate),
+ listener_(new VisitedLinkEventListener(this, browser_context)),
+ persist_to_disk_(persist_to_disk) {
+ InitMembers();
+}
+
+VisitedLinkMaster::VisitedLinkMaster(Listener* listener,
+ VisitedLinkDelegate* delegate,
+ bool persist_to_disk,
+ bool suppress_rebuild,
+ const base::FilePath& filename,
+ int32 default_table_size)
+ : browser_context_(NULL),
+ delegate_(delegate),
+ persist_to_disk_(persist_to_disk) {
+ listener_.reset(listener);
+ DCHECK(listener_.get());
+ InitMembers();
+
+ database_name_override_ = filename;
+ table_size_override_ = default_table_size;
+ suppress_rebuild_ = suppress_rebuild;
+}
+
+VisitedLinkMaster::~VisitedLinkMaster() {
+ if (table_builder_.get()) {
+ // Prevent the table builder from calling us back now that we're being
+ // destroyed. Note that we DON'T delete the object, since the history
+ // system is still writing into it. When that is complete, the table
+ // builder will destroy itself when it finds we are gone.
+ table_builder_->DisownMaster();
+ }
+ FreeURLTable();
+ // FreeURLTable() will schedule closing of the file and deletion of |file_|.
+ // So nothing should be done here.
+}
+
+void VisitedLinkMaster::InitMembers() {
+ file_ = NULL;
+ shared_memory_ = NULL;
+ shared_memory_serial_ = 0;
+ used_items_ = 0;
+ table_size_override_ = 0;
+ suppress_rebuild_ = false;
+ sequence_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken();
+
+#ifndef NDEBUG
+ posted_asynchronous_operation_ = false;
+#endif
+}
+
+bool VisitedLinkMaster::Init() {
+ // We probably shouldn't be loading this from the UI thread,
+ // but it does need to happen early on in startup.
+ // http://code.google.com/p/chromium/issues/detail?id=24163
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ if (persist_to_disk_) {
+ if (InitFromFile())
+ return true;
+ }
+ return InitFromScratch(suppress_rebuild_);
+}
+
+VisitedLinkMaster::Hash VisitedLinkMaster::TryToAddURL(const GURL& url) {
+ // Extra check that we are not incognito. This should not happen.
+ // TODO(boliu): Move this check to HistoryService when IsOffTheRecord is
+ // removed from BrowserContext.
+ if (browser_context_ && browser_context_->IsOffTheRecord()) {
+ NOTREACHED();
+ return null_hash_;
+ }
+
+ if (!url.is_valid())
+ return null_hash_; // Don't add invalid URLs.
+
+ Fingerprint fingerprint = ComputeURLFingerprint(url.spec().data(),
+ url.spec().size(),
+ salt_);
+ if (table_builder_.get()) {
+ // If we have a pending delete for this fingerprint, cancel it.
+ std::set<Fingerprint>::iterator found =
+ deleted_since_rebuild_.find(fingerprint);
+ if (found != deleted_since_rebuild_.end())
+ deleted_since_rebuild_.erase(found);
+
+ // A rebuild is in progress, save this addition in the temporary list so
+ // it can be added once rebuild is complete.
+ added_since_rebuild_.insert(fingerprint);
+ }
+
+ // If the table is "full", we don't add URLs and just drop them on the floor.
+ // This can happen if we get thousands of new URLs and something causes
+ // the table resizing to fail. This check prevents a hang in that case. Note
+ // that this is *not* the resize limit, this is just a sanity check.
+ if (used_items_ / 8 > table_length_ / 10)
+ return null_hash_; // Table is more than 80% full.
+
+ return AddFingerprint(fingerprint, true);
+}
+
+void VisitedLinkMaster::PostIOTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ DCHECK(persist_to_disk_);
+ BrowserThread::GetBlockingPool()->PostSequencedWorkerTask(sequence_token_,
+ from_here, task);
+}
+
+void VisitedLinkMaster::AddURL(const GURL& url) {
+ Hash index = TryToAddURL(url);
+ if (!table_builder_.get() && index != null_hash_) {
+ // Not rebuilding, so we want to keep the file on disk up-to-date.
+ if (persist_to_disk_) {
+ WriteUsedItemCountToFile();
+ WriteHashRangeToFile(index, index);
+ }
+ ResizeTableIfNecessary();
+ }
+}
+
+void VisitedLinkMaster::AddURLs(const std::vector<GURL>& url) {
+ for (std::vector<GURL>::const_iterator i = url.begin();
+ i != url.end(); ++i) {
+ Hash index = TryToAddURL(*i);
+ if (!table_builder_.get() && index != null_hash_)
+ ResizeTableIfNecessary();
+ }
+
+ // Keeps the file on disk up-to-date.
+ if (!table_builder_.get() && persist_to_disk_)
+ WriteFullTable();
+}
+
+void VisitedLinkMaster::DeleteAllURLs() {
+ // Any pending modifications are invalid.
+ added_since_rebuild_.clear();
+ deleted_since_rebuild_.clear();
+
+ // Clear the hash table.
+ used_items_ = 0;
+ memset(hash_table_, 0, this->table_length_ * sizeof(Fingerprint));
+
+ // Resize it if it is now too empty. Resize may write the new table out for
+ // us, otherwise, schedule writing the new table to disk ourselves.
+ if (!ResizeTableIfNecessary() && persist_to_disk_)
+ WriteFullTable();
+
+ listener_->Reset();
+}
+
+VisitedLinkDelegate* VisitedLinkMaster::GetDelegate() {
+ return delegate_;
+}
+
+void VisitedLinkMaster::DeleteURLs(URLIterator* urls) {
+ if (!urls->HasNextURL())
+ return;
+
+ listener_->Reset();
+
+ if (table_builder_.get()) {
+ // A rebuild is in progress, save this deletion in the temporary list so
+ // it can be added once rebuild is complete.
+ while (urls->HasNextURL()) {
+ const GURL& url(urls->NextURL());
+ if (!url.is_valid())
+ continue;
+
+ Fingerprint fingerprint =
+ ComputeURLFingerprint(url.spec().data(), url.spec().size(), salt_);
+ deleted_since_rebuild_.insert(fingerprint);
+
+ // If the URL was just added and now we're deleting it, it may be in the
+ // list of things added since the last rebuild. Delete it from that list.
+ std::set<Fingerprint>::iterator found =
+ added_since_rebuild_.find(fingerprint);
+ if (found != added_since_rebuild_.end())
+ added_since_rebuild_.erase(found);
+
+ // Delete the URLs from the in-memory table, but don't bother writing
+ // to disk since it will be replaced soon.
+ DeleteFingerprint(fingerprint, false);
+ }
+ return;
+ }
+
+ // Compute the deleted URLs' fingerprints and delete them
+ std::set<Fingerprint> deleted_fingerprints;
+ while (urls->HasNextURL()) {
+ const GURL& url(urls->NextURL());
+ if (!url.is_valid())
+ continue;
+ deleted_fingerprints.insert(
+ ComputeURLFingerprint(url.spec().data(), url.spec().size(), salt_));
+ }
+ DeleteFingerprintsFromCurrentTable(deleted_fingerprints);
+}
+
+// See VisitedLinkCommon::IsVisited which should be in sync with this algorithm
+VisitedLinkMaster::Hash VisitedLinkMaster::AddFingerprint(
+ Fingerprint fingerprint,
+ bool send_notifications) {
+ if (!hash_table_ || table_length_ == 0) {
+ NOTREACHED(); // Not initialized.
+ return null_hash_;
+ }
+
+ Hash cur_hash = HashFingerprint(fingerprint);
+ Hash first_hash = cur_hash;
+ while (true) {
+ Fingerprint cur_fingerprint = FingerprintAt(cur_hash);
+ if (cur_fingerprint == fingerprint)
+ return null_hash_; // This fingerprint is already in there, do nothing.
+
+ if (cur_fingerprint == null_fingerprint_) {
+ // End of probe sequence found, insert here.
+ hash_table_[cur_hash] = fingerprint;
+ used_items_++;
+ // If allowed, notify listener that a new visited link was added.
+ if (send_notifications)
+ listener_->Add(fingerprint);
+ return cur_hash;
+ }
+
+ // Advance in the probe sequence.
+ cur_hash = IncrementHash(cur_hash);
+ if (cur_hash == first_hash) {
+ // This means that we've wrapped around and are about to go into an
+ // infinite loop. Something was wrong with the hashtable resizing
+ // logic, so stop here.
+ NOTREACHED();
+ return null_hash_;
+ }
+ }
+}
+
+void VisitedLinkMaster::DeleteFingerprintsFromCurrentTable(
+ const std::set<Fingerprint>& fingerprints) {
+ bool bulk_write = (fingerprints.size() > kBigDeleteThreshold);
+
+ // Delete the URLs from the table.
+ for (std::set<Fingerprint>::const_iterator i = fingerprints.begin();
+ i != fingerprints.end(); ++i)
+ DeleteFingerprint(*i, !bulk_write);
+
+ // These deleted fingerprints may make us shrink the table.
+ if (ResizeTableIfNecessary())
+ return; // The resize function wrote the new table to disk for us.
+
+ // Nobody wrote this out for us, write the full file to disk.
+ if (bulk_write && persist_to_disk_)
+ WriteFullTable();
+}
+
+bool VisitedLinkMaster::DeleteFingerprint(Fingerprint fingerprint,
+ bool update_file) {
+ if (!hash_table_ || table_length_ == 0) {
+ NOTREACHED(); // Not initialized.
+ return false;
+ }
+ if (!IsVisited(fingerprint))
+ return false; // Not in the database to delete.
+
+ // First update the header used count.
+ used_items_--;
+ if (update_file && persist_to_disk_)
+ WriteUsedItemCountToFile();
+
+ Hash deleted_hash = HashFingerprint(fingerprint);
+
+ // Find the range of "stuff" in the hash table that is adjacent to this
+ // fingerprint. These are things that could be affected by the change in
+ // the hash table. Since we use linear probing, anything after the deleted
+ // item up until an empty item could be affected.
+ Hash end_range = deleted_hash;
+ while (true) {
+ Hash next_hash = IncrementHash(end_range);
+ if (next_hash == deleted_hash)
+ break; // We wrapped around and the whole table is full.
+ if (!hash_table_[next_hash])
+ break; // Found the last spot.
+ end_range = next_hash;
+ }
+
+ // We could get all fancy and move the affected fingerprints around, but
+ // instead we just remove them all and re-add them (minus our deleted one).
+ // This will mean there's a small window of time where the affected links
+ // won't be marked visited.
+ base::StackVector<Fingerprint, 32> shuffled_fingerprints;
+ Hash stop_loop = IncrementHash(end_range); // The end range is inclusive.
+ for (Hash i = deleted_hash; i != stop_loop; i = IncrementHash(i)) {
+ if (hash_table_[i] != fingerprint) {
+ // Don't save the one we're deleting!
+ shuffled_fingerprints->push_back(hash_table_[i]);
+
+ // This will balance the increment of this value in AddFingerprint below
+ // so there is no net change.
+ used_items_--;
+ }
+ hash_table_[i] = null_fingerprint_;
+ }
+
+ if (!shuffled_fingerprints->empty()) {
+ // Need to add the new items back.
+ for (size_t i = 0; i < shuffled_fingerprints->size(); i++)
+ AddFingerprint(shuffled_fingerprints[i], false);
+ }
+
+ // Write the affected range to disk [deleted_hash, end_range].
+ if (update_file && persist_to_disk_)
+ WriteHashRangeToFile(deleted_hash, end_range);
+
+ return true;
+}
+
+void VisitedLinkMaster::WriteFullTable() {
+ // This function can get called when the file is open, for example, when we
+ // resize the table. We must handle this case and not try to reopen the file,
+ // since there may be write operations pending on the file I/O thread.
+ //
+ // Note that once we start writing, we do not delete on error. This means
+ // there can be a partial file, but the short file will be detected next time
+ // we start, and will be replaced.
+ //
+ // This might possibly get corrupted if we crash in the middle of writing.
+ // We should pick up the most common types of these failures when we notice
+ // that the file size is different when we load it back in, and then we will
+ // regenerate the table.
+ DCHECK(persist_to_disk_);
+
+ if (!file_) {
+ file_ = static_cast<FILE**>(calloc(1, sizeof(*file_)));
+ base::FilePath filename;
+ GetDatabaseFileName(&filename);
+ PostIOTask(FROM_HERE, base::Bind(&AsyncOpen, file_, filename));
+ }
+
+ // Write the new header.
+ int32 header[4];
+ header[0] = kFileSignature;
+ header[1] = kFileCurrentVersion;
+ header[2] = table_length_;
+ header[3] = used_items_;
+ WriteToFile(file_, 0, header, sizeof(header));
+ WriteToFile(file_, sizeof(header), salt_, LINK_SALT_LENGTH);
+
+ // Write the hash data.
+ WriteToFile(file_, kFileHeaderSize,
+ hash_table_, table_length_ * sizeof(Fingerprint));
+
+ // The hash table may have shrunk, so make sure this is the end.
+ PostIOTask(FROM_HERE, base::Bind(&AsyncTruncate, file_));
+}
+
+bool VisitedLinkMaster::InitFromFile() {
+ DCHECK(file_ == NULL);
+ DCHECK(persist_to_disk_);
+
+ base::FilePath filename;
+ GetDatabaseFileName(&filename);
+ ScopedFILE file_closer(OpenFile(filename, "rb+"));
+ if (!file_closer.get())
+ return false;
+
+ int32 num_entries, used_count;
+ if (!ReadFileHeader(file_closer.get(), &num_entries, &used_count, salt_))
+ return false; // Header isn't valid.
+
+ // Allocate and read the table.
+ if (!CreateURLTable(num_entries, false))
+ return false;
+ if (!ReadFromFile(file_closer.get(), kFileHeaderSize,
+ hash_table_, num_entries * sizeof(Fingerprint))) {
+ FreeURLTable();
+ return false;
+ }
+ used_items_ = used_count;
+
+#ifndef NDEBUG
+ DebugValidate();
+#endif
+
+ file_ = static_cast<FILE**>(malloc(sizeof(*file_)));
+ *file_ = file_closer.release();
+ return true;
+}
+
+bool VisitedLinkMaster::InitFromScratch(bool suppress_rebuild) {
+ int32 table_size = kDefaultTableSize;
+ if (table_size_override_)
+ table_size = table_size_override_;
+
+ // The salt must be generated before the table so that it can be copied to
+ // the shared memory.
+ GenerateSalt(salt_);
+ if (!CreateURLTable(table_size, true))
+ return false;
+
+#ifndef NDEBUG
+ DebugValidate();
+#endif
+
+ if (suppress_rebuild && persist_to_disk_) {
+ // When we disallow rebuilds (normally just unit tests), just use the
+ // current empty table.
+ WriteFullTable();
+ return true;
+ }
+
+ // This will build the table from history. On the first run, history will
+ // be empty, so this will be correct. This will also write the new table
+ // to disk. We don't want to save explicitly here, since the rebuild may
+ // not complete, leaving us with an empty but valid visited link database.
+ // In the future, we won't know we need to try rebuilding again.
+ return RebuildTableFromDelegate();
+}
+
+bool VisitedLinkMaster::ReadFileHeader(FILE* file,
+ int32* num_entries,
+ int32* used_count,
+ uint8 salt[LINK_SALT_LENGTH]) {
+ DCHECK(persist_to_disk_);
+
+ // Get file size.
+ // Note that there is no need to seek back to the original location in the
+ // file since ReadFromFile() [which is the next call accessing the file]
+ // seeks before reading.
+ if (fseek(file, 0, SEEK_END) == -1)
+ return false;
+ size_t file_size = ftell(file);
+
+ if (file_size <= kFileHeaderSize)
+ return false;
+
+ uint8 header[kFileHeaderSize];
+ if (!ReadFromFile(file, 0, &header, kFileHeaderSize))
+ return false;
+
+ // Verify the signature.
+ int32 signature;
+ memcpy(&signature, &header[kFileHeaderSignatureOffset], sizeof(signature));
+ if (signature != kFileSignature)
+ return false;
+
+ // Verify the version is up-to-date. As with other read errors, a version
+ // mistmatch will trigger a rebuild of the database from history, which will
+ // have the effect of migrating the database.
+ int32 version;
+ memcpy(&version, &header[kFileHeaderVersionOffset], sizeof(version));
+ if (version != kFileCurrentVersion)
+ return false; // Bad version.
+
+ // Read the table size and make sure it matches the file size.
+ memcpy(num_entries, &header[kFileHeaderLengthOffset], sizeof(*num_entries));
+ if (*num_entries * sizeof(Fingerprint) + kFileHeaderSize != file_size)
+ return false; // Bad size.
+
+ // Read the used item count.
+ memcpy(used_count, &header[kFileHeaderUsedOffset], sizeof(*used_count));
+ if (*used_count > *num_entries)
+ return false; // Bad used item count;
+
+ // Read the salt.
+ memcpy(salt, &header[kFileHeaderSaltOffset], LINK_SALT_LENGTH);
+
+ // This file looks OK from the header's perspective.
+ return true;
+}
+
+bool VisitedLinkMaster::GetDatabaseFileName(base::FilePath* filename) {
+ if (!database_name_override_.empty()) {
+ // use this filename, the directory must exist
+ *filename = database_name_override_;
+ return true;
+ }
+
+ if (!browser_context_ || browser_context_->GetPath().empty())
+ return false;
+
+ base::FilePath profile_dir = browser_context_->GetPath();
+ *filename = profile_dir.Append(FILE_PATH_LITERAL("Visited Links"));
+ return true;
+}
+
+// Initializes the shared memory structure. The salt should already be filled
+// in so that it can be written to the shared memory
+bool VisitedLinkMaster::CreateURLTable(int32 num_entries, bool init_to_empty) {
+ // The table is the size of the table followed by the entries.
+ uint32 alloc_size = num_entries * sizeof(Fingerprint) + sizeof(SharedHeader);
+
+ // Create the shared memory object.
+ shared_memory_ = new base::SharedMemory();
+ if (!shared_memory_)
+ return false;
+
+ if (!shared_memory_->CreateAndMapAnonymous(alloc_size)) {
+ delete shared_memory_;
+ shared_memory_ = NULL;
+ return false;
+ }
+
+ if (init_to_empty) {
+ memset(shared_memory_->memory(), 0, alloc_size);
+ used_items_ = 0;
+ }
+ table_length_ = num_entries;
+
+ // Save the header for other processes to read.
+ SharedHeader* header = static_cast<SharedHeader*>(shared_memory_->memory());
+ header->length = table_length_;
+ memcpy(header->salt, salt_, LINK_SALT_LENGTH);
+
+ // Our table pointer is just the data immediately following the size.
+ hash_table_ = reinterpret_cast<Fingerprint*>(
+ static_cast<char*>(shared_memory_->memory()) + sizeof(SharedHeader));
+
+ return true;
+}
+
+bool VisitedLinkMaster::BeginReplaceURLTable(int32 num_entries) {
+ base::SharedMemory *old_shared_memory = shared_memory_;
+ Fingerprint* old_hash_table = hash_table_;
+ int32 old_table_length = table_length_;
+ if (!CreateURLTable(num_entries, true)) {
+ // Try to put back the old state.
+ shared_memory_ = old_shared_memory;
+ hash_table_ = old_hash_table;
+ table_length_ = old_table_length;
+ return false;
+ }
+
+#ifndef NDEBUG
+ DebugValidate();
+#endif
+
+ return true;
+}
+
+void VisitedLinkMaster::FreeURLTable() {
+ if (shared_memory_) {
+ delete shared_memory_;
+ shared_memory_ = NULL;
+ }
+ if (!persist_to_disk_ || !file_)
+ return;
+ PostIOTask(FROM_HERE, base::Bind(&AsyncClose, file_));
+ // AsyncClose() will close the file and free the memory pointed by |file_|.
+ file_ = NULL;
+}
+
+bool VisitedLinkMaster::ResizeTableIfNecessary() {
+ DCHECK(table_length_ > 0) << "Must have a table";
+
+ // Load limits for good performance/space. We are pretty conservative about
+ // keeping the table not very full. This is because we use linear probing
+ // which increases the likelihood of clumps of entries which will reduce
+ // performance.
+ const float max_table_load = 0.5f; // Grow when we're > this full.
+ const float min_table_load = 0.2f; // Shrink when we're < this full.
+
+ float load = ComputeTableLoad();
+ if (load < max_table_load &&
+ (table_length_ <= static_cast<float>(kDefaultTableSize) ||
+ load > min_table_load))
+ return false;
+
+ // Table needs to grow or shrink.
+ int new_size = NewTableSizeForCount(used_items_);
+ DCHECK(new_size > used_items_);
+ DCHECK(load <= min_table_load || new_size > table_length_);
+ ResizeTable(new_size);
+ return true;
+}
+
+void VisitedLinkMaster::ResizeTable(int32 new_size) {
+ DCHECK(shared_memory_ && shared_memory_->memory() && hash_table_);
+ shared_memory_serial_++;
+
+#ifndef NDEBUG
+ DebugValidate();
+#endif
+
+ base::SharedMemory* old_shared_memory = shared_memory_;
+ Fingerprint* old_hash_table = hash_table_;
+ int32 old_table_length = table_length_;
+ if (!BeginReplaceURLTable(new_size))
+ return;
+
+ // Now we have two tables, our local copy which is the old one, and the new
+ // one loaded into this object where we need to copy the data.
+ for (int32 i = 0; i < old_table_length; i++) {
+ Fingerprint cur = old_hash_table[i];
+ if (cur)
+ AddFingerprint(cur, false);
+ }
+
+ // On error unmapping, just forget about it since we can't do anything
+ // else to release it.
+ delete old_shared_memory;
+
+ // Send an update notification to all child processes so they read the new
+ // table.
+ listener_->NewTable(shared_memory_);
+
+#ifndef NDEBUG
+ DebugValidate();
+#endif
+
+ // The new table needs to be written to disk.
+ if (persist_to_disk_)
+ WriteFullTable();
+}
+
+uint32 VisitedLinkMaster::NewTableSizeForCount(int32 item_count) const {
+ // These table sizes are selected to be the maximum prime number less than
+ // a "convenient" multiple of 1K.
+ static const int table_sizes[] = {
+ 16381, // 16K = 16384 <- don't shrink below this table size
+ // (should be == default_table_size)
+ 32767, // 32K = 32768
+ 65521, // 64K = 65536
+ 130051, // 128K = 131072
+ 262127, // 256K = 262144
+ 524269, // 512K = 524288
+ 1048549, // 1M = 1048576
+ 2097143, // 2M = 2097152
+ 4194301, // 4M = 4194304
+ 8388571, // 8M = 8388608
+ 16777199, // 16M = 16777216
+ 33554347}; // 32M = 33554432
+
+ // Try to leave the table 33% full.
+ int desired = item_count * 3;
+
+ // Find the closest prime.
+ for (size_t i = 0; i < arraysize(table_sizes); i ++) {
+ if (table_sizes[i] > desired)
+ return table_sizes[i];
+ }
+
+ // Growing very big, just approximate a "good" number, not growing as much
+ // as normal.
+ return item_count * 2 - 1;
+}
+
+// See the TableBuilder definition in the header file for how this works.
+bool VisitedLinkMaster::RebuildTableFromDelegate() {
+ DCHECK(!table_builder_.get());
+
+ // TODO(brettw) make sure we have reasonable salt!
+ table_builder_ = new TableBuilder(this, salt_);
+ delegate_->RebuildTable(table_builder_);
+ return true;
+}
+
+// See the TableBuilder declaration above for how this works.
+void VisitedLinkMaster::OnTableRebuildComplete(
+ bool success,
+ const std::vector<Fingerprint>& fingerprints) {
+ if (success) {
+ // Replace the old table with a new blank one.
+ shared_memory_serial_++;
+
+ // We are responsible for freeing it AFTER it has been replaced if
+ // replacement succeeds.
+ base::SharedMemory* old_shared_memory = shared_memory_;
+
+ int new_table_size = NewTableSizeForCount(
+ static_cast<int>(fingerprints.size() + added_since_rebuild_.size()));
+ if (BeginReplaceURLTable(new_table_size)) {
+ // Free the old table.
+ delete old_shared_memory;
+
+ // Add the stored fingerprints to the hash table.
+ for (size_t i = 0; i < fingerprints.size(); i++)
+ AddFingerprint(fingerprints[i], false);
+
+ // Also add anything that was added while we were asynchronously
+ // generating the new table.
+ for (std::set<Fingerprint>::iterator i = added_since_rebuild_.begin();
+ i != added_since_rebuild_.end(); ++i)
+ AddFingerprint(*i, false);
+ added_since_rebuild_.clear();
+
+ // Now handle deletions.
+ DeleteFingerprintsFromCurrentTable(deleted_since_rebuild_);
+ deleted_since_rebuild_.clear();
+
+ // Send an update notification to all child processes.
+ listener_->NewTable(shared_memory_);
+
+ if (persist_to_disk_)
+ WriteFullTable();
+ }
+ }
+ table_builder_ = NULL; // Will release our reference to the builder.
+
+ // Notify the unit test that the rebuild is complete (will be NULL in prod.)
+ if (!rebuild_complete_task_.is_null()) {
+ rebuild_complete_task_.Run();
+ rebuild_complete_task_.Reset();
+ }
+}
+
+void VisitedLinkMaster::WriteToFile(FILE** file,
+ off_t offset,
+ void* data,
+ int32 data_size) {
+ DCHECK(persist_to_disk_);
+#ifndef NDEBUG
+ posted_asynchronous_operation_ = true;
+#endif
+ PostIOTask(FROM_HERE,
+ base::Bind(&AsyncWrite, file, offset,
+ std::string(static_cast<const char*>(data), data_size)));
+}
+
+void VisitedLinkMaster::WriteUsedItemCountToFile() {
+ DCHECK(persist_to_disk_);
+ if (!file_)
+ return; // See comment on the file_ variable for why this might happen.
+ WriteToFile(file_, kFileHeaderUsedOffset, &used_items_, sizeof(used_items_));
+}
+
+void VisitedLinkMaster::WriteHashRangeToFile(Hash first_hash, Hash last_hash) {
+ DCHECK(persist_to_disk_);
+
+ if (!file_)
+ return; // See comment on the file_ variable for why this might happen.
+ if (last_hash < first_hash) {
+ // Handle wraparound at 0. This first write is first_hash->EOF
+ WriteToFile(file_, first_hash * sizeof(Fingerprint) + kFileHeaderSize,
+ &hash_table_[first_hash],
+ (table_length_ - first_hash + 1) * sizeof(Fingerprint));
+
+ // Now do 0->last_lash.
+ WriteToFile(file_, kFileHeaderSize, hash_table_,
+ (last_hash + 1) * sizeof(Fingerprint));
+ } else {
+ // Normal case, just write the range.
+ WriteToFile(file_, first_hash * sizeof(Fingerprint) + kFileHeaderSize,
+ &hash_table_[first_hash],
+ (last_hash - first_hash + 1) * sizeof(Fingerprint));
+ }
+}
+
+bool VisitedLinkMaster::ReadFromFile(FILE* file,
+ off_t offset,
+ void* data,
+ size_t data_size) {
+ DCHECK(persist_to_disk_);
+#ifndef NDEBUG
+ // Since this function is synchronous, we require that no asynchronous
+ // operations could possibly be pending.
+ DCHECK(!posted_asynchronous_operation_);
+#endif
+
+ if (fseek(file, offset, SEEK_SET) != 0)
+ return false;
+
+ size_t num_read = fread(data, 1, data_size, file);
+ return num_read == data_size;
+}
+
+// VisitedLinkTableBuilder ----------------------------------------------------
+
+VisitedLinkMaster::TableBuilder::TableBuilder(
+ VisitedLinkMaster* master,
+ const uint8 salt[LINK_SALT_LENGTH])
+ : master_(master),
+ success_(true) {
+ fingerprints_.reserve(4096);
+ memcpy(salt_, salt, LINK_SALT_LENGTH * sizeof(uint8));
+}
+
+// TODO(brettw): Do we want to try to cancel the request if this happens? It
+// could delay shutdown if there are a lot of URLs.
+void VisitedLinkMaster::TableBuilder::DisownMaster() {
+ master_ = NULL;
+}
+
+void VisitedLinkMaster::TableBuilder::OnURL(const GURL& url) {
+ if (!url.is_empty()) {
+ fingerprints_.push_back(VisitedLinkMaster::ComputeURLFingerprint(
+ url.spec().data(), url.spec().length(), salt_));
+ }
+}
+
+void VisitedLinkMaster::TableBuilder::OnComplete(bool success) {
+ success_ = success;
+ DLOG_IF(WARNING, !success) << "Unable to rebuild visited links";
+
+ // Marshal to the main thread to notify the VisitedLinkMaster that the
+ // rebuild is complete.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&TableBuilder::OnCompleteMainThread, this));
+}
+
+void VisitedLinkMaster::TableBuilder::OnCompleteMainThread() {
+ if (master_)
+ master_->OnTableRebuildComplete(success_, fingerprints_);
+}
+
+} // namespace visitedlink
diff --git a/chromium/components/visitedlink/browser/visitedlink_master.h b/chromium/components/visitedlink/browser/visitedlink_master.h
new file mode 100644
index 00000000000..aff4e498a7c
--- /dev/null
+++ b/chromium/components/visitedlink/browser/visitedlink_master.h
@@ -0,0 +1,445 @@
+// 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_VISITEDLINK_BROWSER_VISITEDLINK_MASTER_H_
+#define COMPONENTS_VISITEDLINK_BROWSER_VISITEDLINK_MASTER_H_
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+#include <set>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/shared_memory.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "components/visitedlink/common/visitedlink_common.h"
+
+#if defined(UNIT_TEST) || defined(PERF_TEST) || !defined(NDEBUG)
+#include "base/logging.h"
+#endif
+
+class GURL;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace visitedlink {
+
+class VisitedLinkDelegate;
+
+// Controls the link coloring database. The master controls all writing to the
+// database as well as disk I/O. There should be only one master.
+//
+// This class will defer writing operations to the file thread. This means that
+// class destruction, the file may still be open since operations are pending on
+// another thread.
+class VisitedLinkMaster : public VisitedLinkCommon {
+ public:
+ // Listens to the link coloring database events. The master is given this
+ // event as a constructor argument and dispatches events using it.
+ class Listener {
+ public:
+ virtual ~Listener() {}
+
+ // Called when link coloring database has been created or replaced. The
+ // argument is the new table handle.
+ virtual void NewTable(base::SharedMemory*) = 0;
+
+ // Called when new link has been added. The argument is the fingerprint
+ // (hash) of the link.
+ virtual void Add(Fingerprint fingerprint) = 0;
+
+ // Called when link coloring state has been reset. This may occur when
+ // entire or parts of history were deleted.
+ virtual void Reset() = 0;
+ };
+
+ VisitedLinkMaster(content::BrowserContext* browser_context,
+ VisitedLinkDelegate* delegate,
+ bool persist_to_disk);
+
+ // In unit test mode, we allow the caller to optionally specify the database
+ // filename so that it can be run from a unit test. The directory where this
+ // file resides must exist in this mode. You can also specify the default
+ // table size to test table resizing. If this parameter is 0, we will use the
+ // defaults.
+ //
+ // In the unit test mode, we also allow the caller to provide a history
+ // service pointer (the history service can't be fetched from the browser
+ // process when we're in unit test mode). This can be NULL to try to access
+ // the main version, which will probably fail (which can be good for testing
+ // this failure mode).
+ //
+ // When |suppress_rebuild| is set, we'll not attempt to load data from
+ // history if the file can't be loaded. This should generally be set for
+ // testing except when you want to test the rebuild process explicitly.
+ VisitedLinkMaster(Listener* listener,
+ VisitedLinkDelegate* delegate,
+ bool persist_to_disk,
+ bool suppress_rebuild,
+ const base::FilePath& filename,
+ int32 default_table_size);
+ virtual ~VisitedLinkMaster();
+
+ // Must be called immediately after object creation. Nothing else will work
+ // until this is called. Returns true on success, false means that this
+ // object won't work.
+ bool Init();
+
+ base::SharedMemory* shared_memory() { return shared_memory_; }
+
+ // Adds a URL to the table.
+ void AddURL(const GURL& url);
+
+ // Adds a set of URLs to the table.
+ void AddURLs(const std::vector<GURL>& url);
+
+ // See DeleteURLs.
+ class URLIterator {
+ public:
+ // HasNextURL must return true when this is called. Returns the next URL
+ // then advances the iterator. Note that the returned reference is only
+ // valid until the next call of NextURL.
+ virtual const GURL& NextURL() = 0;
+
+ // Returns true if still has URLs to be iterated.
+ virtual bool HasNextURL() const = 0;
+
+ protected:
+ virtual ~URLIterator() {}
+ };
+
+ // Deletes the specified URLs from |rows| from the table.
+ void DeleteURLs(URLIterator* iterator);
+
+ // Clears the visited links table by deleting the file from disk. Used as
+ // part of history clearing.
+ void DeleteAllURLs();
+
+ // Returns the Delegate of this Master.
+ VisitedLinkDelegate* GetDelegate();
+
+#if defined(UNIT_TEST) || !defined(NDEBUG) || defined(PERF_TEST)
+ // This is a debugging function that can be called to double-check internal
+ // data structures. It will assert if the check fails.
+ void DebugValidate();
+
+ // Sets a task to execute when the next rebuild from history is complete.
+ // This is used by unit tests to wait for the rebuild to complete before
+ // they continue. The pointer will be owned by this object after the call.
+ void set_rebuild_complete_task(const base::Closure& task) {
+ DCHECK(rebuild_complete_task_.is_null());
+ rebuild_complete_task_ = task;
+ }
+
+ // returns the number of items in the table for testing verification
+ int32 GetUsedCount() const {
+ return used_items_;
+ }
+
+ // Returns the listener.
+ VisitedLinkMaster::Listener* GetListener() const {
+ return listener_.get();
+ }
+
+ // Call to cause the entire database file to be re-written from scratch
+ // to disk. Used by the performance tester.
+ void RewriteFile() {
+ WriteFullTable();
+ }
+#endif
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, Delete);
+ FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, BigDelete);
+ FRIEND_TEST_ALL_PREFIXES(VisitedLinkTest, BigImport);
+
+ // Object to rebuild the table on the history thread (see the .cc file).
+ class TableBuilder;
+
+ // Byte offsets of values in the header.
+ static const int32 kFileHeaderSignatureOffset;
+ static const int32 kFileHeaderVersionOffset;
+ static const int32 kFileHeaderLengthOffset;
+ static const int32 kFileHeaderUsedOffset;
+ static const int32 kFileHeaderSaltOffset;
+
+ // The signature at the beginning of a file.
+ static const int32 kFileSignature;
+
+ // version of the file format this module currently uses
+ static const int32 kFileCurrentVersion;
+
+ // Bytes in the file header, including the salt.
+ static const size_t kFileHeaderSize;
+
+ // When creating a fresh new table, we use this many entries.
+ static const unsigned kDefaultTableSize;
+
+ // When the user is deleting a boatload of URLs, we don't really want to do
+ // individual writes for each of them. When the count exceeds this threshold,
+ // we will write the whole table to disk at once instead of individual items.
+ static const size_t kBigDeleteThreshold;
+
+ // Backend for the constructors initializing the members.
+ void InitMembers();
+
+ // If a rebuild is in progress, we save the URL in the temporary list.
+ // Otherwise, we add this to the table. Returns the index of the
+ // inserted fingerprint or null_hash_ on failure.
+ Hash TryToAddURL(const GURL& url);
+
+ // File I/O functions
+ // ------------------
+ // These functions are only called if |persist_to_disk_| is true.
+
+ // Posts the given task to the blocking worker pool with our options.
+ void PostIOTask(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ // Writes the entire table to disk. It will leave the table file open and
+ // the handle to it will be stored in file_.
+ void WriteFullTable();
+
+ // Try to load the table from the database file. If the file doesn't exist or
+ // is corrupt, this will return failure.
+ bool InitFromFile();
+
+ // Reads the header of the link coloring database from disk. Assumes the
+ // file pointer is at the beginning of the file and that there are no pending
+ // asynchronous I/O operations.
+ //
+ // Returns true on success and places the size of the table in num_entries
+ // and the number of nonzero fingerprints in used_count. This will fail if
+ // the version of the file is not the current version of the database.
+ bool ReadFileHeader(FILE* hfile, int32* num_entries, int32* used_count,
+ uint8 salt[LINK_SALT_LENGTH]);
+
+ // Fills *filename with the name of the link database filename
+ bool GetDatabaseFileName(base::FilePath* filename);
+
+ // Wrapper around Window's WriteFile using asynchronous I/O. This will proxy
+ // the write to a background thread.
+ void WriteToFile(FILE** hfile, off_t offset, void* data, int32 data_size);
+
+ // Helper function to schedule and asynchronous write of the used count to
+ // disk (this is a common operation).
+ void WriteUsedItemCountToFile();
+
+ // Helper function to schedule an asynchronous write of the given range of
+ // hash functions to disk. The range is inclusive on both ends. The range can
+ // wrap around at 0 and this function will handle it.
+ void WriteHashRangeToFile(Hash first_hash, Hash last_hash);
+
+ // Synchronous read from the file. Assumes there are no pending asynchronous
+ // I/O functions. Returns true if the entire buffer was successfully filled.
+ bool ReadFromFile(FILE* hfile, off_t offset, void* data, size_t data_size);
+
+ // General table handling
+ // ----------------------
+
+ // Called to add a fingerprint to the table. If |send_notifications| is true
+ // and the item is added successfully, Listener::Add will be invoked.
+ // Returns the index of the inserted fingerprint or null_hash_ if there was a
+ // duplicate and this item was skippped.
+ Hash AddFingerprint(Fingerprint fingerprint, bool send_notifications);
+
+ // Deletes all fingerprints from the given vector from the current hash table
+ // and syncs it to disk if there are changes. This does not update the
+ // deleted_since_rebuild_ list, the caller must update this itself if there
+ // is an update pending.
+ void DeleteFingerprintsFromCurrentTable(
+ const std::set<Fingerprint>& fingerprints);
+
+ // Removes the indicated fingerprint from the table. If the update_file flag
+ // is set, the changes will also be written to disk. Returns true if the
+ // fingerprint was deleted, false if it was not in the table to delete.
+ bool DeleteFingerprint(Fingerprint fingerprint, bool update_file);
+
+ // Creates a new empty table, call if InitFromFile() fails. Normally, when
+ // |suppress_rebuild| is false, the table will be rebuilt from history,
+ // keeping us in sync. When |suppress_rebuild| is true, the new table will be
+ // empty and we will not consult history. This is used when clearing the
+ // database and for unit tests.
+ bool InitFromScratch(bool suppress_rebuild);
+
+ // Allocates the Fingerprint structure and length. When init_to_empty is set,
+ // the table will be filled with 0s and used_items_ will be set to 0 as well.
+ // If the flag is not set, these things are untouched and it is the
+ // responsibility of the caller to fill them (like when we are reading from
+ // a file).
+ bool CreateURLTable(int32 num_entries, bool init_to_empty);
+
+ // A wrapper for CreateURLTable, this will allocate a new table, initialized
+ // to empty. The caller is responsible for saving the shared memory pointer
+ // and handles before this call (they will be replaced with new ones) and
+ // releasing them later. This is designed for callers that make a new table
+ // and then copy values from the old table to the new one, then release the
+ // old table.
+ //
+ // Returns true on success. On failure, the old table will be restored. The
+ // caller should not attemp to release the pointer/handle in this case.
+ bool BeginReplaceURLTable(int32 num_entries);
+
+ // unallocates the Fingerprint table
+ void FreeURLTable();
+
+ // For growing the table. ResizeTableIfNecessary will check to see if the
+ // table should be resized and calls ResizeTable if needed. Returns true if
+ // we decided to resize the table.
+ bool ResizeTableIfNecessary();
+
+ // Resizes the table (growing or shrinking) as necessary to accomodate the
+ // current count.
+ void ResizeTable(int32 new_size);
+
+ // Returns the desired table size for |item_count| URLs.
+ uint32 NewTableSizeForCount(int32 item_count) const;
+
+ // Computes the table load as fraction. For example, if 1/4 of the entries are
+ // full, this value will be 0.25
+ float ComputeTableLoad() const {
+ return static_cast<float>(used_items_) / static_cast<float>(table_length_);
+ }
+
+ // Initializes a rebuild of the visited link database based on the browser
+ // history. This will set table_builder_ while working, and there should not
+ // already be a rebuild in place when called. See the definition for more
+ // details on how this works.
+ //
+ // Returns true on success. Failure means we're not attempting to rebuild
+ // the database because something failed.
+ bool RebuildTableFromDelegate();
+
+ // Callback that the table rebuilder uses when the rebuild is complete.
+ // |success| is true if the fingerprint generation succeeded, in which case
+ // |fingerprints| will contain the computed fingerprints. On failure, there
+ // will be no fingerprints.
+ void OnTableRebuildComplete(bool success,
+ const std::vector<Fingerprint>& fingerprints);
+
+ // Increases or decreases the given hash value by one, wrapping around as
+ // necessary. Used for probing.
+ inline Hash IncrementHash(Hash hash) {
+ if (hash >= table_length_ - 1)
+ return 0; // Wrap around.
+ return hash + 1;
+ }
+ inline Hash DecrementHash(Hash hash) {
+ if (hash <= 0)
+ return table_length_ - 1; // Wrap around.
+ return hash - 1;
+ }
+
+#ifndef NDEBUG
+ // Indicates whether any asynchronous operation has ever been completed.
+ // We do some synchronous reads that require that no asynchronous operations
+ // are pending, yet we don't track whether they have been completed. This
+ // flag is a sanity check that these reads only happen before any
+ // asynchronous writes have been fired.
+ bool posted_asynchronous_operation_;
+#endif
+
+ // Reference to the browser context that this object belongs to
+ // (it knows the path to where the data is stored)
+ content::BrowserContext* browser_context_;
+
+ // Client owns the delegate and is responsible for it being valid through
+ // the life time this VisitedLinkMaster.
+ VisitedLinkDelegate* delegate_;
+
+ // VisitedLinkEventListener to handle incoming events.
+ scoped_ptr<Listener> listener_;
+
+ // Lazily initialized sequence token for posting file tasks.
+ base::SequencedWorkerPool::SequenceToken sequence_token_;
+
+ // When non-NULL, indicates we are in database rebuild mode and points to
+ // the class collecting fingerprint information from the history system.
+ // The pointer is owned by this class, but it must remain valid while the
+ // history query is running. We must only delete it when the query is done.
+ scoped_refptr<TableBuilder> table_builder_;
+
+ // Indicates URLs added and deleted since we started rebuilding the table.
+ std::set<Fingerprint> added_since_rebuild_;
+ std::set<Fingerprint> deleted_since_rebuild_;
+
+ // TODO(brettw) Support deletion, we need to track whether anything was
+ // deleted during the rebuild here. Then we should delete any of these
+ // entries from the complete table later.
+ // std::vector<Fingerprint> removed_since_rebuild_;
+
+ // The currently open file with the table in it. This may be NULL if we're
+ // rebuilding and haven't written a new version yet or if |persist_to_disk_|
+ // is false. Writing to the file may be safely ignored in this case. Also
+ // |file_| may be non-NULL but point to a NULL pointer. That would mean that
+ // opening of the file is already scheduled in a background thread and any
+ // writing to the file can also be scheduled to the background thread as it's
+ // guaranteed to be executed after the opening.
+ // The class owns both the |file_| pointer and the pointer pointed
+ // by |*file_|.
+ FILE** file_;
+
+ // If true, will try to persist the hash table to disk. Will rebuild from
+ // VisitedLinkDelegate::RebuildTable if there are disk corruptions.
+ bool persist_to_disk_;
+
+ // Shared memory consists of a SharedHeader followed by the table.
+ base::SharedMemory *shared_memory_;
+
+ // When we generate new tables, we increment the serial number of the
+ // shared memory object.
+ int32 shared_memory_serial_;
+
+ // Number of non-empty items in the table, used to compute fullness.
+ int32 used_items_;
+
+ // Testing values -----------------------------------------------------------
+ //
+ // The following fields exist for testing purposes. They are not used in
+ // release builds. It'd be nice to eliminate them in release builds, but we
+ // don't want to change the signature of the object between the unit test and
+ // regular builds. Instead, we just have "default" values that these take
+ // in release builds that give "regular" behavior.
+
+ // Overridden database file name for testing
+ base::FilePath database_name_override_;
+
+ // When nonzero, overrides the table size for new databases for testing
+ int32 table_size_override_;
+
+ // When set, indicates the task that should be run after the next rebuild from
+ // history is complete.
+ base::Closure rebuild_complete_task_;
+
+ // Set to prevent us from attempting to rebuild the database from global
+ // history if we have an error opening the file. This is used for testing,
+ // will be false in production.
+ bool suppress_rebuild_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisitedLinkMaster);
+};
+
+// NOTE: These methods are defined inline here, so we can share the compilation
+// of visitedlink_master.cc between the browser and the unit/perf tests.
+
+#if defined(UNIT_TEST) || defined(PERF_TEST) || !defined(NDEBUG)
+inline void VisitedLinkMaster::DebugValidate() {
+ int32 used_count = 0;
+ for (int32 i = 0; i < table_length_; i++) {
+ if (hash_table_[i])
+ used_count++;
+ }
+ DCHECK_EQ(used_count, used_items_);
+}
+#endif
+
+} // namespace visitedlink
+
+#endif // COMPONENTS_VISITEDLINK_BROWSER_VISITEDLINK_MASTER_H_
diff --git a/chromium/components/visitedlink/common/DEPS b/chromium/components/visitedlink/common/DEPS
new file mode 100644
index 00000000000..d5923ee96cb
--- /dev/null
+++ b/chromium/components/visitedlink/common/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+content/public/common",
+]
diff --git a/chromium/components/visitedlink/common/OWNERS b/chromium/components/visitedlink/common/OWNERS
new file mode 100644
index 00000000000..fbe7a20d52f
--- /dev/null
+++ b/chromium/components/visitedlink/common/OWNERS
@@ -0,0 +1,9 @@
+# Changes to IPC messages require a security review to avoid introducing
+# new sandbox escapes.
+per-file *_messages*.h=set noparent
+per-file *_messages*.h=cdn@chromium.org
+per-file *_messages*.h=cevans@chromium.org
+per-file *_messages*.h=jln@chromium.org
+per-file *_messages*.h=jschuh@chromium.org
+per-file *_messages*.h=palmer@chromium.org
+per-file *_messages*.h=tsepez@chromium.org
diff --git a/chromium/components/visitedlink/common/visitedlink_common.cc b/chromium/components/visitedlink/common/visitedlink_common.cc
new file mode 100644
index 00000000000..7576231bc05
--- /dev/null
+++ b/chromium/components/visitedlink/common/visitedlink_common.cc
@@ -0,0 +1,102 @@
+// 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/visitedlink/common/visitedlink_common.h"
+
+#include <string.h> // for memset()
+
+#include "base/logging.h"
+#include "base/md5.h"
+#include "url/gurl.h"
+
+namespace visitedlink {
+
+const VisitedLinkCommon::Fingerprint VisitedLinkCommon::null_fingerprint_ = 0;
+const VisitedLinkCommon::Hash VisitedLinkCommon::null_hash_ = -1;
+
+VisitedLinkCommon::VisitedLinkCommon()
+ : hash_table_(NULL),
+ table_length_(0) {
+ memset(salt_, 0, sizeof(salt_));
+}
+
+VisitedLinkCommon::~VisitedLinkCommon() {
+}
+
+// FIXME: this uses linear probing, it should be replaced with quadratic
+// probing or something better. See VisitedLinkMaster::AddFingerprint
+bool VisitedLinkCommon::IsVisited(const char* canonical_url,
+ size_t url_len) const {
+ if (url_len == 0)
+ return false;
+ if (!hash_table_ || table_length_ == 0)
+ return false;
+ return IsVisited(ComputeURLFingerprint(canonical_url, url_len));
+}
+
+bool VisitedLinkCommon::IsVisited(const GURL& url) const {
+ return IsVisited(url.spec().data(), url.spec().size());
+}
+
+bool VisitedLinkCommon::IsVisited(Fingerprint fingerprint) const {
+ // Go through the table until we find the item or an empty spot (meaning it
+ // wasn't found). This loop will terminate as long as the table isn't full,
+ // which should be enforced by AddFingerprint.
+ Hash first_hash = HashFingerprint(fingerprint);
+ Hash cur_hash = first_hash;
+ while (true) {
+ Fingerprint cur_fingerprint = FingerprintAt(cur_hash);
+ if (cur_fingerprint == null_fingerprint_)
+ return false; // End of probe sequence found.
+ if (cur_fingerprint == fingerprint)
+ return true; // Found a match.
+
+ // This spot was taken, but not by the item we're looking for, search in
+ // the next position.
+ cur_hash++;
+ if (cur_hash == table_length_)
+ cur_hash = 0;
+ if (cur_hash == first_hash) {
+ // Wrapped around and didn't find an empty space, this means we're in an
+ // infinite loop because AddFingerprint didn't do its job resizing.
+ NOTREACHED();
+ return false;
+ }
+ }
+}
+
+// Uses the top 64 bits of the MD5 sum of the canonical URL as the fingerprint,
+// this is as random as any other subset of the MD5SUM.
+//
+// FIXME: this uses the MD5SUM of the 16-bit character version. For systems
+// where wchar_t is not 16 bits (Linux uses 32 bits, I think), this will not be
+// compatable. We should define explicitly what should happen here across
+// platforms, and convert if necessary (probably to UTF-16).
+
+// static
+VisitedLinkCommon::Fingerprint VisitedLinkCommon::ComputeURLFingerprint(
+ const char* canonical_url,
+ size_t url_len,
+ const uint8 salt[LINK_SALT_LENGTH]) {
+ DCHECK(url_len > 0) << "Canonical URLs should not be empty";
+
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ base::MD5Update(&ctx, base::StringPiece(reinterpret_cast<const char*>(salt),
+ LINK_SALT_LENGTH));
+ base::MD5Update(&ctx, base::StringPiece(canonical_url, url_len));
+
+ base::MD5Digest digest;
+ base::MD5Final(&digest, &ctx);
+
+ // This is the same as "return *(Fingerprint*)&digest.a;" but if we do that
+ // direct cast the alignment could be wrong, and we can't access a 64-bit int
+ // on arbitrary alignment on some processors. This reinterpret_casts it
+ // down to a char array of the same size as fingerprint, and then does the
+ // bit cast, which amounts to a memcpy. This does not handle endian issues.
+ return bit_cast<Fingerprint, uint8[8]>(
+ *reinterpret_cast<uint8(*)[8]>(&digest.a));
+}
+
+} // namespace visitedlink
diff --git a/chromium/components/visitedlink/common/visitedlink_common.h b/chromium/components/visitedlink/common/visitedlink_common.h
new file mode 100644
index 00000000000..37afe1d1575
--- /dev/null
+++ b/chromium/components/visitedlink/common/visitedlink_common.h
@@ -0,0 +1,138 @@
+// Copyright (c) 2006-2008 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_VISITEDLINK_COMMON_VISITEDLINK_COMMON_H_
+#define COMPONENTS_VISITEDLINK_COMMON_VISITEDLINK_COMMON_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+class GURL;
+
+namespace visitedlink {
+
+// number of bytes in the salt
+#define LINK_SALT_LENGTH 8
+
+// A multiprocess-safe database of the visited links for the browser. There
+// should be exactly one process that has write access (implemented by
+// VisitedLinkMaster), while all other processes should be read-only
+// (implemented by VisitedLinkSlave). These other processes add links by calling
+// the writer process to add them for it. The writer may also notify the readers
+// to replace their table when the table is resized.
+//
+// IPC is not implemented in these classes. This is done through callback
+// functions supplied by the creator of these objects to allow more flexibility,
+// especially for testing.
+//
+// This class defines the common base for these others. We implement accessors
+// for looking things up in the hash table, and for computing hash values and
+// fingerprints. Both the master and the slave inherit from this, and add their
+// own code to set up and change these values as their design requires. The
+// slave pretty much just sets up the shared memory and saves the pointer. The
+// master does a lot of work to manage the table, reading and writing it to and
+// from disk, and resizing it when it gets too full.
+//
+// To ask whether a page is in history, we compute a 64-bit fingerprint of the
+// URL. This URL is hashed and we see if it is in the URL hashtable. If it is,
+// we consider it visited. Otherwise, it is unvisited. Note that it is possible
+// to get collisions, which is the penalty for not storing all URL strings in
+// memory (which could get to be more than we want to have in memory). We use
+// a salt value for the links on one computer so that an attacker can not
+// manually create a link that causes a collision.
+class VisitedLinkCommon {
+ public:
+ // A number that identifies the URL.
+ typedef uint64 Fingerprint;
+ typedef std::vector<Fingerprint> Fingerprints;
+
+ // A hash value of a fingerprint
+ typedef int32 Hash;
+
+ // A fingerprint or hash value that does not exist
+ static const Fingerprint null_fingerprint_;
+ static const Hash null_hash_;
+
+ VisitedLinkCommon();
+ virtual ~VisitedLinkCommon();
+
+ // Returns the fingerprint for the given URL.
+ Fingerprint ComputeURLFingerprint(const char* canonical_url,
+ size_t url_len) const {
+ return ComputeURLFingerprint(canonical_url, url_len, salt_);
+ }
+
+ // Looks up the given key in the table. The fingerprint for the URL is
+ // computed if you call one with the string argument. Returns true if found.
+ // Does not modify the hastable.
+ bool IsVisited(const char* canonical_url, size_t url_len) const;
+ bool IsVisited(const GURL& url) const;
+ bool IsVisited(Fingerprint fingerprint) const;
+
+#ifdef UNIT_TEST
+ // Returns statistics about DB usage
+ void GetUsageStatistics(int32* table_size,
+ VisitedLinkCommon::Fingerprint** fingerprints) {
+ *table_size = table_length_;
+ *fingerprints = hash_table_;
+ }
+#endif
+
+ protected:
+ // This structure is at the beginning of the shared memory so that the slaves
+ // can get stats on the table
+ struct SharedHeader {
+ // see goes into table_length_
+ uint32 length;
+
+ // goes into salt_
+ uint8 salt[LINK_SALT_LENGTH];
+ };
+
+ // Returns the fingerprint at the given index into the URL table. This
+ // function should be called instead of accessing the table directly to
+ // contain endian issues.
+ Fingerprint FingerprintAt(int32 table_offset) const {
+ if (!hash_table_)
+ return null_fingerprint_;
+ return hash_table_[table_offset];
+ }
+
+ // Computes the fingerprint of the given canonical URL. It is static so the
+ // same algorithm can be re-used by the table rebuilder, so you will have to
+ // pass the salt as a parameter. See the non-static version above if you
+ // want to use the current class' salt.
+ static Fingerprint ComputeURLFingerprint(const char* canonical_url,
+ size_t url_len,
+ const uint8 salt[LINK_SALT_LENGTH]);
+
+ // Computes the hash value of the given fingerprint, this is used as a lookup
+ // into the hashtable.
+ static Hash HashFingerprint(Fingerprint fingerprint, int32 table_length) {
+ if (table_length == 0)
+ return null_hash_;
+ return static_cast<Hash>(fingerprint % table_length);
+ }
+ // Uses the current hashtable.
+ Hash HashFingerprint(Fingerprint fingerprint) const {
+ return HashFingerprint(fingerprint, table_length_);
+ }
+
+ // pointer to the first item
+ VisitedLinkCommon::Fingerprint* hash_table_;
+
+ // the number of items in the hash table
+ int32 table_length_;
+
+ // salt used for each URL when computing the fingerprint
+ uint8 salt_[LINK_SALT_LENGTH];
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VisitedLinkCommon);
+};
+
+} // namespace visitedlink
+
+#endif // COMPONENTS_VISITEDLINK_COMMON_VISITEDLINK_COMMON_H_
diff --git a/chromium/components/visitedlink/common/visitedlink_message_generator.cc b/chromium/components/visitedlink/common/visitedlink_message_generator.cc
new file mode 100644
index 00000000000..29d31916e7f
--- /dev/null
+++ b/chromium/components/visitedlink/common/visitedlink_message_generator.cc
@@ -0,0 +1,34 @@
+// 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.
+
+// Get basic type definitions.
+#define IPC_MESSAGE_IMPL
+#include "components/visitedlink/common/visitedlink_message_generator.h"
+
+// Generate constructors.
+#include "ipc/struct_constructor_macros.h"
+#include "components/visitedlink/common/visitedlink_message_generator.h"
+
+// Generate destructors.
+#include "ipc/struct_destructor_macros.h"
+#include "components/visitedlink/common/visitedlink_message_generator.h"
+
+// Generate param traits write methods.
+#include "ipc/param_traits_write_macros.h"
+namespace IPC {
+#include "components/visitedlink/common/visitedlink_message_generator.h"
+} // namespace IPC
+
+// Generate param traits read methods.
+#include "ipc/param_traits_read_macros.h"
+namespace IPC {
+#include "components/visitedlink/common/visitedlink_message_generator.h"
+} // namespace IPC
+
+// Generate param traits log methods.
+#include "ipc/param_traits_log_macros.h"
+namespace IPC {
+#include "components/visitedlink/common/visitedlink_message_generator.h"
+} // namespace IPC
+
diff --git a/chromium/components/visitedlink/common/visitedlink_message_generator.h b/chromium/components/visitedlink/common/visitedlink_message_generator.h
new file mode 100644
index 00000000000..e4a496bafb3
--- /dev/null
+++ b/chromium/components/visitedlink/common/visitedlink_message_generator.h
@@ -0,0 +1,7 @@
+// 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.
+
+// Multiply-included file, no traditional include guard.
+
+#include "components/visitedlink/common/visitedlink_messages.h"
diff --git a/chromium/components/visitedlink/common/visitedlink_messages.h b/chromium/components/visitedlink/common/visitedlink_messages.h
new file mode 100644
index 00000000000..c0877765f12
--- /dev/null
+++ b/chromium/components/visitedlink/common/visitedlink_messages.h
@@ -0,0 +1,29 @@
+// 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.
+
+// Multiply-included file, no traditional include guard.
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/shared_memory.h"
+#include "content/public/common/common_param_traits_macros.h"
+#include "ipc/ipc_message_macros.h"
+
+#define IPC_MESSAGE_START VisitedLinkMsgStart
+
+// History system notification that the visited link database has been
+// replaced. It has one SharedMemoryHandle argument consisting of the table
+// handle. This handle is valid in the context of the renderer
+IPC_MESSAGE_CONTROL1(ChromeViewMsg_VisitedLink_NewTable,
+ base::SharedMemoryHandle)
+
+// History system notification that a link has been added and the link
+// coloring state for the given hash must be re-calculated.
+IPC_MESSAGE_CONTROL1(ChromeViewMsg_VisitedLink_Add, std::vector<uint64>)
+
+// History system notification that one or more history items have been
+// deleted, which at this point means that all link coloring state must be
+// re-calculated.
+IPC_MESSAGE_CONTROL0(ChromeViewMsg_VisitedLink_Reset)
+
diff --git a/chromium/components/visitedlink/renderer/DEPS b/chromium/components/visitedlink/renderer/DEPS
new file mode 100644
index 00000000000..ad0391cf78b
--- /dev/null
+++ b/chromium/components/visitedlink/renderer/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+content/public/renderer",
+ "+third_party/WebKit/public/platform",
+ "+third_party/WebKit/public/web",
+]
diff --git a/chromium/components/visitedlink/renderer/visitedlink_slave.cc b/chromium/components/visitedlink/renderer/visitedlink_slave.cc
new file mode 100644
index 00000000000..7d3e32123a5
--- /dev/null
+++ b/chromium/components/visitedlink/renderer/visitedlink_slave.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2006-2008 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/visitedlink/renderer/visitedlink_slave.h"
+
+#include "base/logging.h"
+#include "base/memory/shared_memory.h"
+#include "components/visitedlink/common/visitedlink_messages.h"
+#include "third_party/WebKit/public/web/WebView.h"
+
+using WebKit::WebView;
+
+namespace visitedlink {
+
+VisitedLinkSlave::VisitedLinkSlave() : shared_memory_(NULL) {}
+
+VisitedLinkSlave::~VisitedLinkSlave() {
+ FreeTable();
+}
+
+bool VisitedLinkSlave::OnControlMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(VisitedLinkSlave, message)
+ IPC_MESSAGE_HANDLER(ChromeViewMsg_VisitedLink_NewTable,
+ OnUpdateVisitedLinks)
+ IPC_MESSAGE_HANDLER(ChromeViewMsg_VisitedLink_Add, OnAddVisitedLinks)
+ IPC_MESSAGE_HANDLER(ChromeViewMsg_VisitedLink_Reset, OnResetVisitedLinks)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+// This function's job is to initialize the table with the given
+// shared memory handle. This memory is mapped into the process.
+void VisitedLinkSlave::OnUpdateVisitedLinks(base::SharedMemoryHandle table) {
+ DCHECK(base::SharedMemory::IsHandleValid(table)) << "Bad table handle";
+ // since this function may be called again to change the table, we may need
+ // to free old objects
+ FreeTable();
+ DCHECK(shared_memory_ == NULL && hash_table_ == NULL);
+
+ // create the shared memory object
+ shared_memory_ = new base::SharedMemory(table, true);
+ if (!shared_memory_)
+ return;
+
+ // map the header into our process so we can see how long the rest is,
+ // and set the salt
+ if (!shared_memory_->Map(sizeof(SharedHeader)))
+ return;
+ SharedHeader* header =
+ static_cast<SharedHeader*>(shared_memory_->memory());
+ DCHECK(header);
+ int32 table_len = header->length;
+ memcpy(salt_, header->salt, sizeof(salt_));
+ shared_memory_->Unmap();
+
+ // now do the whole table because we know the length
+ if (!shared_memory_->Map(sizeof(SharedHeader) +
+ table_len * sizeof(Fingerprint))) {
+ shared_memory_->Close();
+ return;
+ }
+
+ // commit the data
+ DCHECK(shared_memory_->memory());
+ hash_table_ = reinterpret_cast<Fingerprint*>(
+ static_cast<char*>(shared_memory_->memory()) + sizeof(SharedHeader));
+ table_length_ = table_len;
+}
+
+void VisitedLinkSlave::OnAddVisitedLinks(
+ const VisitedLinkSlave::Fingerprints& fingerprints) {
+ for (size_t i = 0; i < fingerprints.size(); ++i)
+ WebView::updateVisitedLinkState(fingerprints[i]);
+}
+
+void VisitedLinkSlave::OnResetVisitedLinks() {
+ WebView::resetVisitedLinkState();
+}
+
+void VisitedLinkSlave::FreeTable() {
+ if (shared_memory_) {
+ delete shared_memory_;
+ shared_memory_ = NULL;
+ }
+ hash_table_ = NULL;
+ table_length_ = 0;
+}
+
+} // namespace visitedlink
diff --git a/chromium/components/visitedlink/renderer/visitedlink_slave.h b/chromium/components/visitedlink/renderer/visitedlink_slave.h
new file mode 100644
index 00000000000..425c03e1be7
--- /dev/null
+++ b/chromium/components/visitedlink/renderer/visitedlink_slave.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_VISITEDLINK_RENDERER_VISITEDLINK_SLAVE_H_
+#define COMPONENTS_VISITEDLINK_RENDERER_VISITEDLINK_SLAVE_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/shared_memory.h"
+#include "components/visitedlink/common/visitedlink_common.h"
+#include "content/public/renderer/render_process_observer.h"
+
+namespace visitedlink {
+
+// Reads the link coloring database provided by the master. There can be any
+// number of slaves reading the same database.
+class VisitedLinkSlave : public VisitedLinkCommon,
+ public content::RenderProcessObserver {
+ public:
+ VisitedLinkSlave();
+ virtual ~VisitedLinkSlave();
+
+ // RenderProcessObserver implementation.
+ virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // Message handlers.
+ void OnUpdateVisitedLinks(base::SharedMemoryHandle table);
+ void OnAddVisitedLinks(const VisitedLinkSlave::Fingerprints& fingerprints);
+ void OnResetVisitedLinks();
+
+ private:
+ void FreeTable();
+
+ // shared memory consists of a SharedHeader followed by the table
+ base::SharedMemory* shared_memory_;
+
+ DISALLOW_COPY_AND_ASSIGN(VisitedLinkSlave);
+};
+
+} // namespace visitedlink
+
+#endif // COMPONENTS_VISITEDLINK_RENDERER_VISITEDLINK_SLAVE_H_
diff --git a/chromium/components/web_contents_delegate_android.gypi b/chromium/components/web_contents_delegate_android.gypi
new file mode 100644
index 00000000000..0a8b845dbb8
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android.gypi
@@ -0,0 +1,64 @@
+# 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.
+
+{
+ 'conditions': [
+ ['OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'web_contents_delegate_android',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../content/content.gyp:content_common',
+ '../net/net.gyp:net',
+ '../skia/skia.gyp:skia',
+ '../ui/ui.gyp:ui',
+ '../webkit/support/webkit_support.gyp:glue',
+ 'web_contents_delegate_android_jni_headers',
+ ],
+ 'include_dirs': [
+ '..',
+ '../skia/config',
+ '<(SHARED_INTERMEDIATE_DIR)/web_contents_delegate_android',
+ ],
+ 'sources': [
+ 'web_contents_delegate_android/color_chooser_android.cc',
+ 'web_contents_delegate_android/color_chooser_android.h',
+ 'web_contents_delegate_android/component_jni_registrar.cc',
+ 'web_contents_delegate_android/component_jni_registrar.h',
+ 'web_contents_delegate_android/web_contents_delegate_android.cc',
+ 'web_contents_delegate_android/web_contents_delegate_android.h',
+ ],
+ },
+ {
+ 'target_name': 'web_contents_delegate_android_java',
+ 'type': 'none',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_java',
+ ],
+ 'variables': {
+ 'java_in_dir': 'web_contents_delegate_android/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'web_contents_delegate_android_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java',
+ 'web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'web_contents_delegate_android',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/components/web_contents_delegate_android/DEPS b/chromium/components/web_contents_delegate_android/DEPS
new file mode 100644
index 00000000000..647531f7476
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+content/public/browser",
+ "+jni",
+ "+ui/base",
+ "+ui/gfx",
+]
diff --git a/chromium/components/web_contents_delegate_android/OWNERS b/chromium/components/web_contents_delegate_android/OWNERS
new file mode 100644
index 00000000000..f9dca5cf635
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/OWNERS
@@ -0,0 +1 @@
+joth@chromium.org
diff --git a/chromium/components/web_contents_delegate_android/android/DEPS b/chromium/components/web_contents_delegate_android/android/DEPS
new file mode 100644
index 00000000000..45a78e4e2ce
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/android/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/public/android/java",
+ "+ui/android/java",
+]
diff --git a/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java b/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java
new file mode 100644
index 00000000000..babf91ea359
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java
@@ -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.
+
+package org.chromium.components.web_contents_delegate_android;
+
+import android.content.Context;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.content.browser.ContentViewCore;
+import org.chromium.ui.ColorPickerDialog;
+import org.chromium.ui.OnColorChangedListener;
+
+/**
+ * ColorChooserAndroid communicates with the java ColorPickerDialog and the
+ * native color_chooser_android.cc
+ */
+@JNINamespace("web_contents_delegate_android")
+public class ColorChooserAndroid {
+ private final ColorPickerDialog mDialog;
+ private final int mNativeColorChooserAndroid;
+
+ private ColorChooserAndroid(int nativeColorChooserAndroid,
+ Context context, int initialColor) {
+ OnColorChangedListener listener = new OnColorChangedListener() {
+ @Override
+ public void onColorChanged(int color) {
+ mDialog.dismiss();
+ nativeOnColorChosen(mNativeColorChooserAndroid, color);
+ }
+ };
+
+ mNativeColorChooserAndroid = nativeColorChooserAndroid;
+ mDialog = new ColorPickerDialog(context, listener, initialColor);
+ }
+
+ private void openColorChooser() {
+ mDialog.show();
+ }
+
+ @CalledByNative
+ public void closeColorChooser() {
+ mDialog.dismiss();
+ }
+
+ @CalledByNative
+ public static ColorChooserAndroid createColorChooserAndroid(
+ int nativeColorChooserAndroid,
+ ContentViewCore contentViewCore,
+ int initialColor) {
+ ColorChooserAndroid chooser = new ColorChooserAndroid(nativeColorChooserAndroid,
+ contentViewCore.getContext(), initialColor);
+ chooser.openColorChooser();
+ return chooser;
+ }
+
+ // Implemented in color_chooser_android.cc
+ private native void nativeOnColorChosen(int nativeColorChooserAndroid, int color);
+}
diff --git a/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java b/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java
new file mode 100644
index 00000000000..c09cb89629c
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java
@@ -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.
+
+package org.chromium.components.web_contents_delegate_android;
+
+import android.graphics.Rect;
+import android.view.KeyEvent;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+import org.chromium.content.browser.ContentViewCore;
+
+/**
+ * Java peer of the native class of the same name.
+ */
+@JNINamespace("web_contents_delegate_android")
+public class WebContentsDelegateAndroid {
+
+ // Equivalent of WebCore::WebConsoleMessage::LevelTip.
+ public static final int LOG_LEVEL_TIP = 0;
+ // Equivalent of WebCore::WebConsoleMessage::LevelLog.
+ public static final int LOG_LEVEL_LOG = 1;
+ // Equivalent of WebCore::WebConsoleMessage::LevelWarning.
+ public static final int LOG_LEVEL_WARNING = 2;
+ // Equivalent of WebCore::WebConsoleMessage::LevelError.
+ public static final int LOG_LEVEL_ERROR = 3;
+
+ // Flags passed to the WebContentsDelegateAndroid.navigationStateChanged to tell it
+ // what has changed. Should match the values in invalidate_type.h.
+ // Equivalent of InvalidateTypes::INVALIDATE_TYPE_URL.
+ public static final int INVALIDATE_TYPE_URL = 1 << 0;
+ // Equivalent of InvalidateTypes::INVALIDATE_TYPE_TAB.
+ public static final int INVALIDATE_TYPE_TAB = 1 << 1;
+ // Equivalent of InvalidateTypes::INVALIDATE_TYPE_LOAD.
+ public static final int INVALIDATE_TYPE_LOAD = 1 << 2;
+ // Equivalent of InvalidateTypes::INVALIDATE_TYPE_PAGE_ACTIONS.
+ public static final int INVALIDATE_TYPE_PAGE_ACTIONS = 1 << 3;
+ // Equivalent of InvalidateTypes::INVALIDATE_TYPE_TITLE.
+ public static final int INVALIDATE_TYPE_TITLE = 1 << 4;
+
+ // The most recent load progress callback received from WebContents, as a percentage.
+ // Initialize to 100 to indicate that we're not in a loading state.
+ private int mMostRecentProgress = 100;
+
+ public int getMostRecentProgress() {
+ return mMostRecentProgress;
+ }
+
+ /**
+ * @param disposition The new tab disposition as per the constants in
+ * org.chromium.ui.WindowOpenDisposition (See window_open_disposition_list.h
+ * for the enumeration definitions).
+ */
+ @CalledByNative
+ public void openNewTab(String url, String extraHeaders, byte[] postData, int disposition) {
+ }
+
+ @CalledByNative
+ public boolean addNewContents(int nativeSourceWebContents, int nativeWebContents,
+ int disposition, Rect initialPosition, boolean userGesture) {
+ return false;
+ }
+
+ @CalledByNative
+ public void activateContents() {
+ }
+
+ @CalledByNative
+ public void closeContents() {
+ }
+
+ @CalledByNative
+ public void onLoadStarted() {
+ }
+
+ @CalledByNative
+ public void onLoadStopped() {
+ }
+
+ @CalledByNative
+ public void navigationStateChanged(int flags) {
+ }
+
+ @SuppressWarnings("unused")
+ @CalledByNative
+ private final void notifyLoadProgressChanged(double progress) {
+ mMostRecentProgress = (int) (100.0 * progress);
+ onLoadProgressChanged(mMostRecentProgress);
+ }
+
+ /**
+ * @param progress The load progress [0, 100] for the current web contents.
+ */
+ public void onLoadProgressChanged(int progress) {
+ }
+
+ /**
+ * Signaled when the renderer has been deemed to be unresponsive.
+ */
+ @CalledByNative
+ public void rendererUnresponsive() {
+ }
+
+ /**
+ * Signaled when the render has been deemed to be responsive.
+ */
+ @CalledByNative
+ public void rendererResponsive() {
+ }
+
+ @CalledByNative
+ public void onUpdateUrl(String url) {
+ }
+
+ @CalledByNative
+ public boolean takeFocus(boolean reverse) {
+ return false;
+ }
+
+ @CalledByNative
+ public void handleKeyboardEvent(KeyEvent event) {
+ // TODO(bulach): we probably want to re-inject the KeyEvent back into
+ // the system. Investigate if this is at all possible.
+ }
+
+ /**
+ * Report a JavaScript console message.
+ *
+ * @param level message level. One of WebContentsDelegateAndroid.LOG_LEVEL*.
+ * @param message the error message.
+ * @param lineNumber the line number int the source file at which the error is reported.
+ * @param sourceId the name of the source file that caused the error.
+ * @return true if the client will handle logging the message.
+ */
+ @CalledByNative
+ public boolean addMessageToConsole(int level, String message, int lineNumber,
+ String sourceId) {
+ return false;
+ }
+
+ /**
+ * Report a form resubmission. The overwriter of this function should eventually call
+ * either of ContentViewCore.ContinuePendingReload or ContentViewCore.CancelPendingReload.
+ */
+ @CalledByNative
+ public void showRepostFormWarningDialog(ContentViewCore contentViewCore) {
+ }
+
+ @CalledByNative
+ public void toggleFullscreenModeForTab(boolean enterFullscreen) {
+ }
+
+ @CalledByNative
+ public boolean isFullscreenForTabOrPending() {
+ return false;
+ }
+
+ /**
+ * Called from WebKit to request that the top controls be shown or hidden.
+ * The implementation should call ContentViewCore.showTopControls to actually
+ * show or hide the top controls.
+ */
+ @CalledByNative
+ public void didProgrammaticallyScroll(int scrollX, int scrollY) {
+ }
+}
diff --git a/chromium/components/web_contents_delegate_android/color_chooser_android.cc b/chromium/components/web_contents_delegate_android/color_chooser_android.cc
new file mode 100644
index 00000000000..4eb3d03d704
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/color_chooser_android.cc
@@ -0,0 +1,57 @@
+// 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/web_contents_delegate_android/color_chooser_android.h"
+
+#include "content/public/browser/android/content_view_core.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "jni/ColorChooserAndroid_jni.h"
+
+namespace web_contents_delegate_android {
+
+ColorChooserAndroid::ColorChooserAndroid(content::WebContents* web_contents,
+ SkColor initial_color)
+ : web_contents_(web_contents) {
+ JNIEnv* env = AttachCurrentThread();
+ content::ContentViewCore* content_view_core =
+ content::ContentViewCore::FromWebContents(web_contents);
+ DCHECK(content_view_core);
+
+ j_color_chooser_.Reset(Java_ColorChooserAndroid_createColorChooserAndroid(
+ env,
+ reinterpret_cast<intptr_t>(this),
+ content_view_core->GetJavaObject().obj(),
+ initial_color));
+}
+
+ColorChooserAndroid::~ColorChooserAndroid() {
+}
+
+void ColorChooserAndroid::End() {
+ if (!j_color_chooser_.is_null()) {
+ JNIEnv* env = AttachCurrentThread();
+ Java_ColorChooserAndroid_closeColorChooser(env, j_color_chooser_.obj());
+ }
+}
+
+void ColorChooserAndroid::SetSelectedColor(SkColor color) {
+ // Not implemented since the color is set on the java side only, in theory
+ // it can be set from JS which would override the user selection but
+ // we don't support that for now.
+}
+
+void ColorChooserAndroid::OnColorChosen(JNIEnv* env, jobject obj, jint color) {
+ web_contents_->DidChooseColorInColorChooser(color);
+ web_contents_->DidEndColorChooser();
+}
+
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+bool RegisterColorChooserAndroid(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace web_contents_delegate_android
diff --git a/chromium/components/web_contents_delegate_android/color_chooser_android.h b/chromium/components/web_contents_delegate_android/color_chooser_android.h
new file mode 100644
index 00000000000..8ab332603a8
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/color_chooser_android.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_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
+#define COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
+
+#include "base/android/jni_android.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "content/public/browser/color_chooser.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ScopedJavaLocalRef;
+
+namespace content {
+class WebContents;
+}
+
+namespace web_contents_delegate_android {
+
+// Glues the Java (ColorPickerChooser.java) picker with the native part.
+class ColorChooserAndroid : public content::ColorChooser {
+ public:
+ ColorChooserAndroid(content::WebContents* tab,
+ SkColor initial_color);
+ virtual ~ColorChooserAndroid();
+
+ void OnColorChosen(JNIEnv* env, jobject obj, jint color);
+
+ // ColorChooser interface
+ virtual void End() OVERRIDE;
+ virtual void SetSelectedColor(SkColor color) OVERRIDE;
+
+ private:
+ base::android::ScopedJavaGlobalRef<jobject> j_color_chooser_;
+
+ // The web contents invoking the color chooser. No ownership. because it will
+ // outlive this class.
+ content::WebContents* web_contents_;
+
+ DISALLOW_COPY_AND_ASSIGN(ColorChooserAndroid);
+};
+
+// Native JNI methods
+bool RegisterColorChooserAndroid(JNIEnv* env);
+
+} // namespace web_contents_delegate_android
+
+#endif // COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COLOR_CHOOSER_ANDROID_H_
diff --git a/chromium/components/web_contents_delegate_android/component_jni_registrar.cc b/chromium/components/web_contents_delegate_android/component_jni_registrar.cc
new file mode 100644
index 00000000000..b5c5e1590e8
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/component_jni_registrar.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/web_contents_delegate_android/component_jni_registrar.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "components/web_contents_delegate_android/color_chooser_android.h"
+#include "components/web_contents_delegate_android/web_contents_delegate_android.h"
+
+namespace web_contents_delegate_android {
+
+static base::android::RegistrationMethod kComponentRegisteredMethods[] = {
+ { "ColorChooserAndroid", RegisterColorChooserAndroid },
+ { "WebContentsDelegateAndroid", RegisterWebContentsDelegateAndroid },
+};
+
+bool RegisterWebContentsDelegateAndroidJni(JNIEnv* env) {
+ return RegisterNativeMethods(env,
+ kComponentRegisteredMethods, arraysize(kComponentRegisteredMethods));
+}
+
+} // namespace web_contents_delegate_android
+
diff --git a/chromium/components/web_contents_delegate_android/component_jni_registrar.h b/chromium/components/web_contents_delegate_android/component_jni_registrar.h
new file mode 100644
index 00000000000..203585d4972
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/component_jni_registrar.h
@@ -0,0 +1,19 @@
+// 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_WEB_CONTENTS_DELEGATE_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+#define COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+
+#include <jni.h>
+
+namespace web_contents_delegate_android {
+
+// Register all JNI bindings necessary for the web_contents_delegate_android
+// component.
+bool RegisterWebContentsDelegateAndroidJni(JNIEnv* env);
+
+} // namespace web_contents_delegate_android
+
+#endif // COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_COMPONENT_JNI_REGISTRAR_H_
+
diff --git a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
new file mode 100644
index 00000000000..8f599a16b03
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
@@ -0,0 +1,361 @@
+// 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/web_contents_delegate_android/web_contents_delegate_android.h"
+
+#include <android/keycodes.h>
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "components/web_contents_delegate_android/color_chooser_android.h"
+#include "content/public/browser/android/content_view_core.h"
+#include "content/public/browser/color_chooser.h"
+#include "content/public/browser/invalidate_type.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/page_navigator.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/page_transition_types.h"
+#include "content/public/common/referrer.h"
+#include "jni/WebContentsDelegateAndroid_jni.h"
+#include "ui/base/window_open_disposition.h"
+#include "ui/gfx/rect.h"
+#include "url/gurl.h"
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertUTF16ToJavaString;
+using base::android::HasClass;
+using base::android::ScopedJavaLocalRef;
+using content::ColorChooser;
+using content::WebContents;
+using content::WebContentsDelegate;
+
+namespace web_contents_delegate_android {
+
+WebContentsDelegateAndroid::WebContentsDelegateAndroid(JNIEnv* env, jobject obj)
+ : weak_java_delegate_(env, obj) {
+}
+
+WebContentsDelegateAndroid::~WebContentsDelegateAndroid() {
+}
+
+ScopedJavaLocalRef<jobject>
+WebContentsDelegateAndroid::GetJavaDelegate(JNIEnv* env) const {
+ return weak_java_delegate_.get(env);
+}
+
+// ----------------------------------------------------------------------------
+// WebContentsDelegate methods
+// ----------------------------------------------------------------------------
+
+ColorChooser* WebContentsDelegateAndroid::OpenColorChooser(WebContents* source,
+ SkColor color) {
+ return new ColorChooserAndroid(source, color);
+}
+
+// OpenURLFromTab() will be called when we're performing a browser-intiated
+// navigation. The most common scenario for this is opening new tabs (see
+// RenderViewImpl::decidePolicyForNavigation for more details).
+WebContents* WebContentsDelegateAndroid::OpenURLFromTab(
+ WebContents* source,
+ const content::OpenURLParams& params) {
+ const GURL& url = params.url;
+ WindowOpenDisposition disposition = params.disposition;
+ content::PageTransition transition(
+ PageTransitionFromInt(params.transition));
+
+ if (!source || (disposition != CURRENT_TAB &&
+ disposition != NEW_FOREGROUND_TAB &&
+ disposition != NEW_BACKGROUND_TAB &&
+ disposition != OFF_THE_RECORD)) {
+ NOTIMPLEMENTED();
+ return NULL;
+ }
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return WebContentsDelegate::OpenURLFromTab(source, params);
+
+ if (disposition == NEW_FOREGROUND_TAB ||
+ disposition == NEW_BACKGROUND_TAB ||
+ disposition == OFF_THE_RECORD) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> java_url =
+ ConvertUTF8ToJavaString(env, url.spec());
+ ScopedJavaLocalRef<jstring> extra_headers =
+ ConvertUTF8ToJavaString(env, params.extra_headers);
+ ScopedJavaLocalRef<jbyteArray> post_data;
+ if (params.uses_post &&
+ params.browser_initiated_post_data.get() &&
+ params.browser_initiated_post_data.get()->size()) {
+ post_data = base::android::ToJavaByteArray(
+ env,
+ reinterpret_cast<const uint8*>(
+ params.browser_initiated_post_data.get()->front()),
+ params.browser_initiated_post_data.get()->size());
+ }
+ Java_WebContentsDelegateAndroid_openNewTab(env,
+ obj.obj(),
+ java_url.obj(),
+ extra_headers.obj(),
+ post_data.obj(),
+ disposition);
+ return NULL;
+ }
+
+ source->GetController().LoadURL(url, params.referrer, transition,
+ std::string());
+ return source;
+}
+
+void WebContentsDelegateAndroid::NavigationStateChanged(
+ const WebContents* source, unsigned changed_flags) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_navigationStateChanged(
+ env,
+ obj.obj(),
+ changed_flags);
+}
+
+void WebContentsDelegateAndroid::AddNewContents(
+ WebContents* source,
+ WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture,
+ bool* was_blocked) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ bool handled = false;
+ if (!obj.is_null()) {
+ handled = Java_WebContentsDelegateAndroid_addNewContents(
+ env,
+ obj.obj(),
+ reinterpret_cast<jint>(source),
+ reinterpret_cast<jint>(new_contents),
+ static_cast<jint>(disposition),
+ NULL,
+ user_gesture);
+ }
+ if (!handled)
+ delete new_contents;
+}
+
+void WebContentsDelegateAndroid::ActivateContents(WebContents* contents) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_activateContents(env, obj.obj());
+}
+
+void WebContentsDelegateAndroid::DeactivateContents(WebContents* contents) {
+ // On desktop the current window is deactivated here, bringing the next window
+ // to focus. Not implemented on Android.
+}
+
+void WebContentsDelegateAndroid::LoadingStateChanged(WebContents* source) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ bool has_stopped = source == NULL || !source->IsLoading();
+
+ if (has_stopped)
+ Java_WebContentsDelegateAndroid_onLoadStopped(env, obj.obj());
+ else
+ Java_WebContentsDelegateAndroid_onLoadStarted(env, obj.obj());
+}
+
+void WebContentsDelegateAndroid::LoadProgressChanged(WebContents* source,
+ double progress) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_notifyLoadProgressChanged(
+ env,
+ obj.obj(),
+ progress);
+}
+
+void WebContentsDelegateAndroid::RendererUnresponsive(WebContents* source) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_rendererUnresponsive(env, obj.obj());
+}
+
+void WebContentsDelegateAndroid::RendererResponsive(WebContents* source) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_rendererResponsive(env, obj.obj());
+}
+
+void WebContentsDelegateAndroid::CloseContents(WebContents* source) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_closeContents(env, obj.obj());
+}
+
+void WebContentsDelegateAndroid::MoveContents(WebContents* source,
+ const gfx::Rect& pos) {
+ // Do nothing.
+}
+
+bool WebContentsDelegateAndroid::AddMessageToConsole(
+ WebContents* source,
+ int32 level,
+ const base::string16& message,
+ int32 line_no,
+ const base::string16& source_id) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return WebContentsDelegate::AddMessageToConsole(source, level, message,
+ line_no, source_id);
+ ScopedJavaLocalRef<jstring> jmessage(ConvertUTF16ToJavaString(env, message));
+ ScopedJavaLocalRef<jstring> jsource_id(
+ ConvertUTF16ToJavaString(env, source_id));
+ int jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG;
+ switch (level) {
+ case logging::LOG_VERBOSE:
+ jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG;
+ break;
+ case logging::LOG_INFO:
+ jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_LOG;
+ break;
+ case logging::LOG_WARNING:
+ jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_WARNING;
+ break;
+ case logging::LOG_ERROR:
+ jlevel = WEB_CONTENTS_DELEGATE_LOG_LEVEL_ERROR;
+ break;
+ default:
+ NOTREACHED();
+ }
+ return Java_WebContentsDelegateAndroid_addMessageToConsole(
+ env,
+ GetJavaDelegate(env).obj(),
+ jlevel,
+ jmessage.obj(),
+ line_no,
+ jsource_id.obj());
+}
+
+// This is either called from TabContents::DidNavigateMainFramePostCommit() with
+// an empty GURL or responding to RenderViewHost::OnMsgUpateTargetURL(). In
+// Chrome, the latter is not always called, especially not during history
+// navigation. So we only handle the first case and pass the source TabContents'
+// url to Java to update the UI.
+void WebContentsDelegateAndroid::UpdateTargetURL(WebContents* source,
+ int32 page_id,
+ const GURL& url) {
+ if (!url.is_empty())
+ return;
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ ScopedJavaLocalRef<jstring> java_url =
+ ConvertUTF8ToJavaString(env, source->GetURL().spec());
+ Java_WebContentsDelegateAndroid_onUpdateUrl(env,
+ obj.obj(),
+ java_url.obj());
+}
+
+void WebContentsDelegateAndroid::HandleKeyboardEvent(
+ WebContents* source,
+ const content::NativeWebKeyboardEvent& event) {
+ jobject key_event = event.os_event;
+ if (key_event) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_handleKeyboardEvent(
+ env, obj.obj(), key_event);
+ }
+}
+
+bool WebContentsDelegateAndroid::TakeFocus(WebContents* source, bool reverse) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return WebContentsDelegate::TakeFocus(source, reverse);
+ return Java_WebContentsDelegateAndroid_takeFocus(
+ env, obj.obj(), reverse);
+}
+
+void WebContentsDelegateAndroid::ShowRepostFormWarningDialog(
+ WebContents* source) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ ScopedJavaLocalRef<jobject> content_view_core =
+ content::ContentViewCore::FromWebContents(source)->GetJavaObject();
+ if (content_view_core.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_showRepostFormWarningDialog(env, obj.obj(),
+ content_view_core.obj());
+}
+
+void WebContentsDelegateAndroid::ToggleFullscreenModeForTab(
+ WebContents* web_contents,
+ bool enter_fullscreen) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_toggleFullscreenModeForTab(
+ env, obj.obj(), enter_fullscreen);
+}
+
+bool WebContentsDelegateAndroid::IsFullscreenForTabOrPending(
+ const WebContents* web_contents) const {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return false;
+ return Java_WebContentsDelegateAndroid_isFullscreenForTabOrPending(
+ env, obj.obj());
+}
+
+void WebContentsDelegateAndroid::DidProgrammaticallyScroll(
+ WebContents* web_contents, const gfx::Vector2d& scroll_point) {
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = GetJavaDelegate(env);
+ if (obj.is_null())
+ return;
+ Java_WebContentsDelegateAndroid_didProgrammaticallyScroll(
+ env, obj.obj(), scroll_point.x(), scroll_point.y());
+}
+
+// ----------------------------------------------------------------------------
+// Native JNI methods
+// ----------------------------------------------------------------------------
+
+// Register native methods
+
+bool RegisterWebContentsDelegateAndroid(JNIEnv* env) {
+ if (!HasClass(env, kWebContentsDelegateAndroidClassPath)) {
+ DLOG(ERROR) << "Unable to find class WebContentsDelegateAndroid!";
+ return false;
+ }
+ return RegisterNativesImpl(env);
+}
+
+} // namespace web_contents_delegate_android
diff --git a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
new file mode 100644
index 00000000000..68bca15cf44
--- /dev/null
+++ b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
@@ -0,0 +1,112 @@
+// 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_WEB_CONTENTS_DELEGATE_ANDROID_WEB_CONTENTS_DELEGATE_ANDROID_H_
+#define COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_WEB_CONTENTS_DELEGATE_ANDROID_H_
+
+#include "base/android/jni_helper.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "ui/gfx/vector2d.h"
+
+class GURL;
+
+namespace content {
+class WebContents;
+class WebContentsDelegate;
+struct NativeWebKeyboardEvent;
+struct OpenURLParams;
+}
+
+namespace web_contents_delegate_android {
+
+enum WebContentsDelegateLogLevel {
+ // Equivalent of WebCore::WebConsoleMessage::LevelDebug.
+ WEB_CONTENTS_DELEGATE_LOG_LEVEL_DEBUG = 0,
+ // Equivalent of WebCore::WebConsoleMessage::LevelLog.
+ WEB_CONTENTS_DELEGATE_LOG_LEVEL_LOG = 1,
+ // Equivalent of WebCore::WebConsoleMessage::LevelWarning.
+ WEB_CONTENTS_DELEGATE_LOG_LEVEL_WARNING = 2,
+ // Equivalent of WebCore::WebConsoleMessage::LevelError.
+ WEB_CONTENTS_DELEGATE_LOG_LEVEL_ERROR = 3,
+};
+
+
+// Native underpinnings of WebContentsDelegateAndroid.java. Provides a default
+// delegate for WebContents to forward calls to the java peer. The embedding
+// application may subclass and override methods on either the C++ or Java side
+// as required.
+class WebContentsDelegateAndroid : public content::WebContentsDelegate {
+ public:
+ WebContentsDelegateAndroid(JNIEnv* env, jobject obj);
+ virtual ~WebContentsDelegateAndroid();
+
+ // Binds this WebContentsDelegateAndroid to the passed WebContents instance,
+ // such that when that WebContents is destroyed, this
+ // WebContentsDelegateAndroid instance will be destroyed too.
+ void SetOwnerWebContents(content::WebContents* contents);
+
+ // Overridden from WebContentsDelegate:
+ virtual content::WebContents* OpenURLFromTab(
+ content::WebContents* source,
+ const content::OpenURLParams& params) OVERRIDE;
+ virtual content::ColorChooser* OpenColorChooser(content::WebContents* source,
+ SkColor color) OVERRIDE;
+ virtual void NavigationStateChanged(const content::WebContents* source,
+ unsigned changed_flags) OVERRIDE;
+ virtual void AddNewContents(content::WebContents* source,
+ content::WebContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture,
+ bool* was_blocked) OVERRIDE;
+ virtual void ActivateContents(content::WebContents* contents) OVERRIDE;
+ virtual void DeactivateContents(content::WebContents* contents) OVERRIDE;
+ virtual void LoadingStateChanged(content::WebContents* source) OVERRIDE;
+ virtual void LoadProgressChanged(content::WebContents* source,
+ double load_progress) OVERRIDE;
+ virtual void RendererUnresponsive(content::WebContents* source) OVERRIDE;
+ virtual void RendererResponsive(content::WebContents* source) OVERRIDE;
+ virtual void CloseContents(content::WebContents* source) OVERRIDE;
+ virtual void MoveContents(content::WebContents* source,
+ const gfx::Rect& pos) OVERRIDE;
+ virtual bool AddMessageToConsole(content::WebContents* source,
+ int32 level,
+ const base::string16& message,
+ int32 line_no,
+ const base::string16& source_id) OVERRIDE;
+ virtual void UpdateTargetURL(content::WebContents* source,
+ int32 page_id,
+ const GURL& url) OVERRIDE;
+ virtual void HandleKeyboardEvent(
+ content::WebContents* source,
+ const content::NativeWebKeyboardEvent& event) OVERRIDE;
+ virtual bool TakeFocus(content::WebContents* source, bool reverse) OVERRIDE;
+ virtual void ShowRepostFormWarningDialog(
+ content::WebContents* source) OVERRIDE;
+ virtual void ToggleFullscreenModeForTab(content::WebContents* web_contents,
+ bool enter_fullscreen) OVERRIDE;
+ virtual bool IsFullscreenForTabOrPending(
+ const content::WebContents* web_contents) const OVERRIDE;
+ virtual void DidProgrammaticallyScroll(
+ content::WebContents* web_contents,
+ const gfx::Vector2d& scroll_point) OVERRIDE;
+
+ protected:
+ base::android::ScopedJavaLocalRef<jobject> GetJavaDelegate(JNIEnv* env) const;
+
+ private:
+ // We depend on the java side user of WebContentDelegateAndroid to hold a
+ // strong reference to that object as long as they want to receive callbacks
+ // on it. Using a weak ref here allows it to be correctly GCed.
+ JavaObjectWeakGlobalRef weak_java_delegate_;
+};
+
+bool RegisterWebContentsDelegateAndroid(JNIEnv* env);
+
+} // namespace web_contents_delegate_android
+
+#endif // COMPONENTS_WEB_CONTENTS_DELEGATE_ANDROID_WEB_CONTENTS_DELEGATE_ANDROID_H_
diff --git a/chromium/components/web_modal.gypi b/chromium/components/web_modal.gypi
new file mode 100644
index 00000000000..f2db7d4cb83
--- /dev/null
+++ b/chromium/components/web_modal.gypi
@@ -0,0 +1,33 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'web_modal',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../skia/skia.gyp:skia',
+ ],
+ 'defines': [
+ 'WEB_MODAL_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'web_modal/native_web_contents_modal_dialog.h',
+ 'web_modal/native_web_contents_modal_dialog_manager.h',
+ 'web_modal/web_contents_modal_dialog_host.cc',
+ 'web_modal/web_contents_modal_dialog_host.h',
+ 'web_modal/web_contents_modal_dialog_manager.cc',
+ 'web_modal/web_contents_modal_dialog_manager.h',
+ 'web_modal/web_contents_modal_dialog_manager_delegate.cc',
+ 'web_modal/web_contents_modal_dialog_manager_delegate.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/web_modal/DEPS b/chromium/components/web_modal/DEPS
new file mode 100644
index 00000000000..3539cc43913
--- /dev/null
+++ b/chromium/components/web_modal/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+content/public/browser",
+ "+content/public/test",
+ "+net/base",
+ "+ui/gfx",
+]
diff --git a/chromium/components/web_modal/native_web_contents_modal_dialog.h b/chromium/components/web_modal/native_web_contents_modal_dialog.h
new file mode 100644
index 00000000000..266e073ab9c
--- /dev/null
+++ b/chromium/components/web_modal/native_web_contents_modal_dialog.h
@@ -0,0 +1,22 @@
+// 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_WEB_MODAL_NATIVE_WEB_CONTENTS_MODAL_DIALOG_H_
+#define COMPONENTS_WEB_MODAL_NATIVE_WEB_CONTENTS_MODAL_DIALOG_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+namespace web_modal {
+
+#if defined(OS_MACOSX)
+// Use a void* since none of the gfx::Native* types are suitable for
+// representing the web contents modal dialog under Cocoa.
+typedef void* NativeWebContentsModalDialog;
+#else
+typedef gfx::NativeView NativeWebContentsModalDialog;
+#endif
+
+} // namespace web_modal
+
+#endif // COMPONENTS_WEB_MODAL_NATIVE_WEB_CONTENTS_MODAL_DIALOG_H_
diff --git a/chromium/components/web_modal/native_web_contents_modal_dialog_manager.h b/chromium/components/web_modal/native_web_contents_modal_dialog_manager.h
new file mode 100644
index 00000000000..7145a08c088
--- /dev/null
+++ b/chromium/components/web_modal/native_web_contents_modal_dialog_manager.h
@@ -0,0 +1,66 @@
+// 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_WEB_MODAL_NATIVE_WEB_CONTENTS_MODAL_DIALOG_MANAGER_H_
+#define COMPONENTS_WEB_MODAL_NATIVE_WEB_CONTENTS_MODAL_DIALOG_MANAGER_H_
+
+#include "components/web_modal/native_web_contents_modal_dialog.h"
+
+namespace content {
+class WebContents;
+} // namespace content
+
+namespace web_modal {
+
+// Interface from NativeWebContentsModalDialogManager to
+// WebContentsModalDialogManager.
+class NativeWebContentsModalDialogManagerDelegate {
+ public:
+ NativeWebContentsModalDialogManagerDelegate() {}
+ virtual ~NativeWebContentsModalDialogManagerDelegate() {}
+
+ virtual content::WebContents* GetWebContents() const = 0;
+ virtual void WillClose(NativeWebContentsModalDialog dialog) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeWebContentsModalDialogManagerDelegate);
+};
+
+// Provides an interface for platform-specific UI implementation for the web
+// contents modal dialog.
+class NativeWebContentsModalDialogManager {
+ public:
+ virtual ~NativeWebContentsModalDialogManager() {}
+
+ // Starts management of the modal aspects of the dialog. This function should
+ // also register to be notified when the dialog is closing, so that it can
+ // notify the manager.
+ virtual void ManageDialog(NativeWebContentsModalDialog dialog) = 0;
+
+ // Makes the web contents modal dialog visible. Only one web contents modal
+ // dialog is shown at a time per tab.
+ virtual void ShowDialog(NativeWebContentsModalDialog dialog) = 0;
+
+ // Hides the web contents modal dialog without closing it.
+ virtual void HideDialog(NativeWebContentsModalDialog dialog) = 0;
+
+ // Closes the web contents modal dialog.
+ virtual void CloseDialog(NativeWebContentsModalDialog dialog) = 0;
+
+ // Sets focus on the web contents modal dialog.
+ virtual void FocusDialog(NativeWebContentsModalDialog dialog) = 0;
+
+ // Runs a pulse animation for the web contents modal dialog.
+ virtual void PulseDialog(NativeWebContentsModalDialog dialog) = 0;
+
+ protected:
+ NativeWebContentsModalDialogManager() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeWebContentsModalDialogManager);
+};
+
+} // namespace web_modal
+
+#endif // COMPONENTS_WEB_MODAL_NATIVE_WEB_CONTENTS_MODAL_DIALOG_MANAGER_H_
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_host.cc b/chromium/components/web_modal/web_contents_modal_dialog_host.cc
new file mode 100644
index 00000000000..1dcaa00508e
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_host.cc
@@ -0,0 +1,21 @@
+// 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/web_modal/web_contents_modal_dialog_host.h"
+
+namespace web_modal {
+
+WebContentsModalDialogHostObserver::~WebContentsModalDialogHostObserver() {
+}
+
+WebContentsModalDialogHostObserver::WebContentsModalDialogHostObserver() {
+}
+
+WebContentsModalDialogHost::~WebContentsModalDialogHost() {
+}
+
+WebContentsModalDialogHost::WebContentsModalDialogHost() {
+}
+
+} // namespace web_modal
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_host.h b/chromium/components/web_modal/web_contents_modal_dialog_host.h
new file mode 100644
index 00000000000..499925cac21
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_host.h
@@ -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.
+
+#ifndef COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_HOST_H_
+#define COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_HOST_H_
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/size.h"
+
+namespace web_modal {
+
+// Observer to be implemented to update web contents modal dialogs when the host
+// indicates their position needs to be changed.
+class WebContentsModalDialogHostObserver {
+ public:
+ virtual ~WebContentsModalDialogHostObserver();
+
+ virtual void OnPositionRequiresUpdate() = 0;
+
+ protected:
+ WebContentsModalDialogHostObserver();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogHostObserver);
+};
+
+// Interface for supporting positioning of web contents modal dialogs over a
+// window/widget.
+class WebContentsModalDialogHost {
+ public:
+ virtual ~WebContentsModalDialogHost();
+
+ // Returns the view against which the dialog is positioned and parented.
+ virtual gfx::NativeView GetHostView() const = 0;
+ // Gets the position for the dialog in coordinates relative to the host
+ // view.
+ virtual gfx::Point GetDialogPosition(const gfx::Size& size) = 0;
+
+ // Add/remove observer.
+ virtual void AddObserver(WebContentsModalDialogHostObserver* observer) = 0;
+ virtual void RemoveObserver(WebContentsModalDialogHostObserver* observer) = 0;
+
+ protected:
+ WebContentsModalDialogHost();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogHost);
+};
+
+} // namespace web_modal
+
+#endif // COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_HOST_H_
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager.cc b/chromium/components/web_modal/web_contents_modal_dialog_manager.cc
new file mode 100644
index 00000000000..0e07f8dd1d9
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager.cc
@@ -0,0 +1,149 @@
+// 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/web_modal/web_contents_modal_dialog_manager.h"
+
+#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
+#include "content/public/browser/navigation_details.h"
+#include "content/public/browser/navigation_entry.h"
+#include "content/public/browser/notification_details.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 "content/public/browser/web_contents_view.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
+
+using content::WebContents;
+
+DEFINE_WEB_CONTENTS_USER_DATA_KEY(web_modal::WebContentsModalDialogManager);
+
+namespace web_modal {
+
+WebContentsModalDialogManager::~WebContentsModalDialogManager() {
+ DCHECK(child_dialogs_.empty());
+}
+
+void WebContentsModalDialogManager::ShowDialog(
+ NativeWebContentsModalDialog dialog) {
+ child_dialogs_.push_back(dialog);
+
+ native_manager_->ManageDialog(dialog);
+
+ if (child_dialogs_.size() == 1) {
+ if (delegate_ && delegate_->IsWebContentsVisible(web_contents()))
+ native_manager_->ShowDialog(dialog);
+ BlockWebContentsInteraction(true);
+ }
+}
+
+bool WebContentsModalDialogManager::IsShowingDialog() const {
+ return !child_dialogs_.empty();
+}
+
+void WebContentsModalDialogManager::FocusTopmostDialog() {
+ DCHECK(!child_dialogs_.empty());
+ native_manager_->FocusDialog(child_dialogs_.front());
+}
+
+content::WebContents* WebContentsModalDialogManager::GetWebContents() const {
+ return web_contents();
+}
+
+void WebContentsModalDialogManager::WillClose(
+ NativeWebContentsModalDialog dialog) {
+ WebContentsModalDialogList::iterator i(
+ std::find(child_dialogs_.begin(), child_dialogs_.end(), dialog));
+
+ // The Views tab contents modal dialog calls WillClose twice. Ignore the
+ // second invocation.
+ if (i == child_dialogs_.end())
+ return;
+
+ bool removed_topmost_dialog = i == child_dialogs_.begin();
+ child_dialogs_.erase(i);
+ if (!child_dialogs_.empty() && removed_topmost_dialog &&
+ !closing_all_dialogs_)
+ native_manager_->ShowDialog(child_dialogs_.front());
+
+ BlockWebContentsInteraction(!child_dialogs_.empty());
+}
+
+void WebContentsModalDialogManager::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED);
+
+ if (child_dialogs_.empty())
+ return;
+
+ bool visible = *content::Details<bool>(details).ptr();
+ if (visible)
+ native_manager_->ShowDialog(child_dialogs_.front());
+ else
+ native_manager_->HideDialog(child_dialogs_.front());
+}
+
+WebContentsModalDialogManager::WebContentsModalDialogManager(
+ content::WebContents* web_contents)
+ : content::WebContentsObserver(web_contents),
+ delegate_(NULL),
+ native_manager_(CreateNativeManager(this)),
+ closing_all_dialogs_(false) {
+ DCHECK(native_manager_);
+ registrar_.Add(this,
+ content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ content::Source<content::WebContents>(web_contents));
+}
+
+void WebContentsModalDialogManager::BlockWebContentsInteraction(bool blocked) {
+ WebContents* contents = web_contents();
+ if (!contents) {
+ // The WebContents has already disconnected.
+ return;
+ }
+
+ // RenderViewHost may be NULL during shutdown.
+ content::RenderViewHost* host = contents->GetRenderViewHost();
+ if (host)
+ host->SetIgnoreInputEvents(blocked);
+ if (delegate_)
+ delegate_->SetWebContentsBlocked(contents, blocked);
+}
+
+void WebContentsModalDialogManager::CloseAllDialogs() {
+ closing_all_dialogs_ = true;
+
+ // Clear out any dialogs since we are leaving this page entirely.
+ while (!child_dialogs_.empty())
+ native_manager_->CloseDialog(child_dialogs_.front());
+
+ closing_all_dialogs_ = false;
+}
+
+void WebContentsModalDialogManager::DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) {
+ // Close constrained windows if necessary.
+ if (!net::registry_controlled_domains::SameDomainOrHost(
+ details.previous_url, details.entry->GetURL(),
+ net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES))
+ CloseAllDialogs();
+}
+
+void WebContentsModalDialogManager::DidGetIgnoredUIEvent() {
+ if (!child_dialogs_.empty())
+ native_manager_->FocusDialog(child_dialogs_.front());
+}
+
+void WebContentsModalDialogManager::WebContentsDestroyed(WebContents* tab) {
+ // First cleanly close all child dialogs.
+ // TODO(mpcomplete): handle case if MaybeCloseChildWindows() already asked
+ // some of these to close. CloseAllDialogs is async, so it might get called
+ // twice before it runs.
+ CloseAllDialogs();
+}
+
+} // namespace web_modal
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager.h b/chromium/components/web_modal/web_contents_modal_dialog_manager.h
new file mode 100644
index 00000000000..e2548c04e5c
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager.h
@@ -0,0 +1,116 @@
+// 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_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_MANAGER_H_
+#define COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_MANAGER_H_
+
+#include <deque>
+
+#include "base/memory/scoped_ptr.h"
+#include "components/web_modal/native_web_contents_modal_dialog_manager.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace web_modal {
+
+class WebContentsModalDialogManagerDelegate;
+
+// Per-WebContents class to manage WebContents-modal dialogs.
+class WebContentsModalDialogManager
+ : public NativeWebContentsModalDialogManagerDelegate,
+ public content::WebContentsObserver,
+ public content::WebContentsUserData<WebContentsModalDialogManager>,
+ public content::NotificationObserver {
+ public:
+ virtual ~WebContentsModalDialogManager();
+
+ WebContentsModalDialogManagerDelegate* delegate() const { return delegate_; }
+ void set_delegate(WebContentsModalDialogManagerDelegate* d) { delegate_ = d; }
+
+ static NativeWebContentsModalDialogManager* CreateNativeManager(
+ NativeWebContentsModalDialogManagerDelegate* native_delegate);
+
+ // Shows the dialog as a web contents modal dialog. The dialog will notify via
+ // WillClose() when it is being destroyed.
+ void ShowDialog(NativeWebContentsModalDialog dialog);
+
+ // Returns true if a dialog is currently being shown.
+ bool IsShowingDialog() const;
+
+ // Focus the topmost modal dialog. IsShowingDialog() must be true when
+ // calling this function.
+ void FocusTopmostDialog();
+
+ // Overriden from NativeWebContentsModalDialogManagerDelegate:
+ virtual content::WebContents* GetWebContents() const OVERRIDE;
+ // Called when a WebContentsModalDialogs we own is about to be closed.
+ virtual void WillClose(NativeWebContentsModalDialog dialog) OVERRIDE;
+
+ // content::NotificationObserver overrides
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE;
+
+ // For testing.
+ class TestApi {
+ public:
+ explicit TestApi(WebContentsModalDialogManager* manager)
+ : manager_(manager) {}
+
+ void CloseAllDialogs() { manager_->CloseAllDialogs(); }
+ void ResetNativeManager(NativeWebContentsModalDialogManager* delegate) {
+ manager_->native_manager_.reset(delegate);
+ }
+
+ private:
+ WebContentsModalDialogManager* manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestApi);
+ };
+
+ private:
+ explicit WebContentsModalDialogManager(content::WebContents* web_contents);
+ friend class content::WebContentsUserData<WebContentsModalDialogManager>;
+
+ typedef std::deque<NativeWebContentsModalDialog> WebContentsModalDialogList;
+
+ // Blocks/unblocks interaction with renderer process.
+ void BlockWebContentsInteraction(bool blocked);
+
+ bool IsWebContentsVisible() const;
+
+ // Closes all WebContentsModalDialogs.
+ void CloseAllDialogs();
+
+ // Overridden from content::WebContentsObserver:
+ virtual void DidNavigateMainFrame(
+ const content::LoadCommittedDetails& details,
+ const content::FrameNavigateParams& params) OVERRIDE;
+ virtual void DidGetIgnoredUIEvent() OVERRIDE;
+ virtual void WebContentsDestroyed(content::WebContents* tab) OVERRIDE;
+
+ // Delegate for notifying our owner about stuff. Not owned by us.
+ WebContentsModalDialogManagerDelegate* delegate_;
+
+ // Delegate for native UI-specific functions on the dialog.
+ scoped_ptr<NativeWebContentsModalDialogManager> native_manager_;
+
+ // All active dialogs.
+ WebContentsModalDialogList child_dialogs_;
+
+ // True while closing the dialogs on WebContents close.
+ bool closing_all_dialogs_;
+
+ // A scoped container for notification registries.
+ content::NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogManager);
+};
+
+} // namespace web_modal
+
+#endif // COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_MANAGER_H_
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.cc b/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.cc
new file mode 100644
index 00000000000..25a777cf4f5
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.cc
@@ -0,0 +1,28 @@
+// 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/web_modal/web_contents_modal_dialog_manager_delegate.h"
+
+#include <string.h>
+
+namespace web_modal {
+
+void WebContentsModalDialogManagerDelegate::SetWebContentsBlocked(
+ content::WebContents* web_contents, bool blocked) {
+}
+
+WebContentsModalDialogHost*
+ WebContentsModalDialogManagerDelegate::GetWebContentsModalDialogHost() {
+ return NULL;
+}
+
+bool WebContentsModalDialogManagerDelegate::IsWebContentsVisible(
+ content::WebContents* web_contents) {
+ return true;
+}
+
+WebContentsModalDialogManagerDelegate::~WebContentsModalDialogManagerDelegate(
+) {}
+
+} // namespace web_modal
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h b/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h
new file mode 100644
index 00000000000..76f74354de5
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager_delegate.h
@@ -0,0 +1,42 @@
+// 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_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_MANAGER_DELEGATE_H_
+#define COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_MANAGER_DELEGATE_H_
+
+namespace content {
+class WebContents;
+}
+
+namespace gfx {
+class Point;
+}
+
+namespace web_modal {
+
+class WebContentsModalDialogHost;
+
+class WebContentsModalDialogManagerDelegate {
+ public:
+ // Changes the blocked state of |web_contents|. WebContentses are considered
+ // blocked while displaying a web contents modal dialog. During that time
+ // renderer host will ignore any UI interaction within WebContents outside of
+ // the currently displaying dialog.
+ virtual void SetWebContentsBlocked(content::WebContents* web_contents,
+ bool blocked);
+
+ // Returns the WebContentsModalDialogHost for use in positioning web contents
+ // modal dialogs within the browser window.
+ virtual WebContentsModalDialogHost* GetWebContentsModalDialogHost();
+
+ // Returns whether the WebContents is currently visible or not.
+ virtual bool IsWebContentsVisible(content::WebContents* web_contents);
+
+ protected:
+ virtual ~WebContentsModalDialogManagerDelegate();
+};
+
+} // namespace web_modal
+
+#endif // COMPONENTS_WEB_MODAL_WEB_CONTENTS_MODAL_DIALOG_MANAGER_DELEGATE_H_
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc b/chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc
new file mode 100644
index 00000000000..1268896d6af
--- /dev/null
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc
@@ -0,0 +1,81 @@
+// 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/web_modal/native_web_contents_modal_dialog_manager.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_renderer_host.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using content::BrowserThread;
+
+namespace web_modal {
+
+class WebContentsModalDialogManagerTest
+ : public content::RenderViewHostTestHarness {
+ public:
+ virtual void SetUp() {
+ content::RenderViewHostTestHarness::SetUp();
+ WebContentsModalDialogManager::CreateForWebContents(web_contents());
+ }
+};
+
+class NativeWebContentsModalDialogManagerCloseTest
+ : public NativeWebContentsModalDialogManager {
+ public:
+ NativeWebContentsModalDialogManagerCloseTest(
+ NativeWebContentsModalDialogManagerDelegate* delegate)
+ : delegate_(delegate) {}
+ virtual void ManageDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ }
+ virtual void ShowDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ }
+ virtual void HideDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ }
+ virtual void CloseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ delegate_->WillClose(dialog);
+ close_count++;
+ }
+ virtual void FocusDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ }
+ virtual void PulseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ }
+
+ int close_count;
+ NativeWebContentsModalDialogManagerDelegate* delegate_;
+};
+
+NativeWebContentsModalDialogManager* WebContentsModalDialogManager::
+CreateNativeManager(
+ NativeWebContentsModalDialogManagerDelegate* native_delegate) {
+ return new NativeWebContentsModalDialogManagerCloseTest(native_delegate);
+}
+
+TEST_F(WebContentsModalDialogManagerTest, WebContentsModalDialogs) {
+ WebContentsModalDialogManager* web_contents_modal_dialog_manager =
+ WebContentsModalDialogManager::FromWebContents(web_contents());
+ WebContentsModalDialogManager::TestApi test_api(
+ web_contents_modal_dialog_manager);
+
+ NativeWebContentsModalDialogManagerCloseTest* native_manager =
+ new NativeWebContentsModalDialogManagerCloseTest(
+ web_contents_modal_dialog_manager);
+ native_manager->close_count = 0;
+
+ test_api.ResetNativeManager(native_manager);
+
+ const int kWindowCount = 4;
+ for (int i = 0; i < kWindowCount; i++)
+ // WebContentsModalDialogManager treats the NativeWebContentsModalDialog as
+ // an opaque type, so creating fake NativeWebContentsModalDialogs using
+ // reinterpret_cast is valid.
+ web_contents_modal_dialog_manager->ShowDialog(
+ reinterpret_cast<NativeWebContentsModalDialog>(i));
+ EXPECT_EQ(native_manager->close_count, 0);
+
+ test_api.CloseAllDialogs();
+ EXPECT_EQ(native_manager->close_count, kWindowCount);
+}
+
+} // namespace web_modal
diff --git a/chromium/components/webdata.gypi b/chromium/components/webdata.gypi
new file mode 100644
index 00000000000..f728c211010
--- /dev/null
+++ b/chromium/components/webdata.gypi
@@ -0,0 +1,75 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'encryptor',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../crypto/crypto.gyp:crypto',
+ ],
+ 'sources': [
+ 'webdata/encryptor/encryptor.h',
+ 'webdata/encryptor/encryptor_mac.mm',
+ 'webdata/encryptor/encryptor_password_mac.h',
+ 'webdata/encryptor/encryptor_password_mac.mm',
+ 'webdata/encryptor/encryptor_posix.cc',
+ 'webdata/encryptor/encryptor_win.cc',
+ 'webdata/encryptor/ie7_password.cc',
+ 'webdata/encryptor/ie7_password.h',
+ ],
+ 'conditions': [
+ ['OS!="win"', {
+ 'sources!': [
+ 'webdata/encryptor/ie7_password.cc'
+ ],
+ }],
+ ['OS=="mac"', {
+ 'sources!': [
+ 'webdata/encryptor/encryptor_posix.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'webdata_common',
+ 'type': '<(component)',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../sql/sql.gyp:sql',
+ ],
+ 'defines': [
+ 'WEBDATA_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'webdata/common/web_database.cc',
+ 'webdata/common/web_database.h',
+ 'webdata/common/web_database_service.cc',
+ 'webdata/common/web_database_service.h',
+ 'webdata/common/web_database_table.cc',
+ 'webdata/common/web_database_table.h',
+ 'webdata/common/web_data_request_manager.cc',
+ 'webdata/common/web_data_request_manager.h',
+ 'webdata/common/web_data_results.h',
+ 'webdata/common/web_data_service_backend.cc',
+ 'webdata/common/web_data_service_backend.h',
+ 'webdata/common/web_data_service_base.cc',
+ 'webdata/common/web_data_service_base.h',
+ 'webdata/common/web_data_service_consumer.h',
+ 'webdata/common/webdata_constants.cc',
+ 'webdata/common/webdata_constants.h',
+ 'webdata/common/webdata_export.h'
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/webdata/DEPS b/chromium/components/webdata/DEPS
new file mode 100644
index 00000000000..1d575c83aaf
--- /dev/null
+++ b/chromium/components/webdata/DEPS
@@ -0,0 +1,22 @@
+include_rules = [
+ # WebData is used by iOS, which does not use content.
+ "-content",
+ "+sql",
+ "+ui",
+]
+
+specific_include_rules = {
+ # TODO(caitkp): Extract unit tests from //chrome, at lower priority
+ # than production code.
+ r'(.*_unittest|.*_test_util)\.(cc|h)': [
+ "+chrome/browser/webdata/keyword_table.h",
+ "+chrome/browser/webdata/logins_table.h",
+ "+chrome/browser/webdata/token_service_table.h",
+ "+chrome/browser/webdata/token_web_data.h",
+ "+chrome/browser/webdata/web_apps_table.h",
+ "+chrome/browser/webdata/web_data_service.h",
+ "+chrome/browser/webdata/web_data_service_factory.h",
+ "+chrome/browser/webdata/web_intents_table.h",
+ "+content/public/test",
+ ],
+}
diff --git a/chromium/components/webdata/OWNERS b/chromium/components/webdata/OWNERS
new file mode 100644
index 00000000000..59f839504b3
--- /dev/null
+++ b/chromium/components/webdata/OWNERS
@@ -0,0 +1,5 @@
+joi@chromium.org
+pkasting@chromium.org
+
+# For sqlite stuff:
+shess@chromium.org
diff --git a/chromium/components/webdata/README b/chromium/components/webdata/README
new file mode 100644
index 00000000000..a82334ca409
--- /dev/null
+++ b/chromium/components/webdata/README
@@ -0,0 +1,5 @@
+WebData is not allowed to depend on content/, because it is used by iOS.
+If dependences on content/ need to be added to WebData, it will have to be made
+into a layered component: see
+http://www.chromium.org/developers/design-documents/layered-components-design
+for more information.
diff --git a/chromium/components/webdata/common/web_data_request_manager.cc b/chromium/components/webdata/common/web_data_request_manager.cc
new file mode 100644
index 00000000000..9aba721da25
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_request_manager.cc
@@ -0,0 +1,145 @@
+// Copyright 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/webdata/common/web_data_request_manager.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataRequest implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+WebDataRequest::WebDataRequest(WebDataServiceConsumer* consumer,
+ WebDataRequestManager* manager)
+ : manager_(manager), cancelled_(false), consumer_(consumer) {
+ handle_ = manager_->GetNextRequestHandle();
+ message_loop_ = base::MessageLoop::current();
+ manager_->RegisterRequest(this);
+}
+
+WebDataRequest::~WebDataRequest() {
+ if (manager_) {
+ manager_->CancelRequest(handle_);
+ }
+ if (result_.get()) {
+ result_->Destroy();
+ }
+}
+
+WebDataServiceBase::Handle WebDataRequest::GetHandle() const {
+ return handle_;
+}
+
+WebDataServiceConsumer* WebDataRequest::GetConsumer() const {
+ return consumer_;
+}
+
+base::MessageLoop* WebDataRequest::GetMessageLoop() const {
+ return message_loop_;
+}
+
+bool WebDataRequest::IsCancelled() const {
+ base::AutoLock l(cancel_lock_);
+ return cancelled_;
+}
+
+void WebDataRequest::Cancel() {
+ base::AutoLock l(cancel_lock_);
+ cancelled_ = true;
+ consumer_ = NULL;
+ manager_ = NULL;
+}
+
+void WebDataRequest::OnComplete() {
+ manager_= NULL;
+}
+
+void WebDataRequest::SetResult(scoped_ptr<WDTypedResult> r) {
+ result_ = r.Pass();
+}
+
+scoped_ptr<WDTypedResult> WebDataRequest::GetResult(){
+ return result_.Pass();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataRequestManager implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+WebDataRequestManager::WebDataRequestManager()
+ : next_request_handle_(1) {
+}
+
+WebDataRequestManager::~WebDataRequestManager() {
+ base::AutoLock l(pending_lock_);
+ for (RequestMap::iterator i = pending_requests_.begin();
+ i != pending_requests_.end(); ++i) {
+ i->second->Cancel();
+ }
+ pending_requests_.clear();
+}
+
+void WebDataRequestManager::RegisterRequest(WebDataRequest* request) {
+ base::AutoLock l(pending_lock_);
+ pending_requests_[request->GetHandle()] = request;
+}
+
+int WebDataRequestManager::GetNextRequestHandle() {
+ base::AutoLock l(pending_lock_);
+ return ++next_request_handle_;
+}
+
+void WebDataRequestManager::CancelRequest(WebDataServiceBase::Handle h) {
+ base::AutoLock l(pending_lock_);
+ RequestMap::iterator i = pending_requests_.find(h);
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Canceling a nonexistent web data service request";
+ return;
+ }
+ i->second->Cancel();
+ pending_requests_.erase(i);
+}
+
+void WebDataRequestManager::RequestCompleted(
+ scoped_ptr<WebDataRequest> request) {
+ base::MessageLoop* loop = request->GetMessageLoop();
+ loop->PostTask(FROM_HERE,
+ base::Bind(&WebDataRequestManager::RequestCompletedOnThread,
+ this,
+ base::Passed(&request)));
+}
+
+void WebDataRequestManager::RequestCompletedOnThread(
+ scoped_ptr<WebDataRequest> request) {
+ if (request->IsCancelled())
+ return;
+ {
+ base::AutoLock l(pending_lock_);
+ RequestMap::iterator i = pending_requests_.find(request->GetHandle());
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Request completed called for an unknown request";
+ return;
+ }
+
+ // Take ownership of the request object and remove it from the map.
+ pending_requests_.erase(i);
+ }
+
+ // Notify the consumer if needed.
+ if (!request->IsCancelled()) {
+ WebDataServiceConsumer* consumer = request->GetConsumer();
+ request->OnComplete();
+ if (consumer) {
+ scoped_ptr<WDTypedResult> r = request->GetResult();
+ consumer->OnWebDataServiceRequestDone(request->GetHandle(), r.get());
+ }
+ }
+
+}
diff --git a/chromium/components/webdata/common/web_data_request_manager.h b/chromium/components/webdata/common/web_data_request_manager.h
new file mode 100644
index 00000000000..90a5dd116d3
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_request_manager.h
@@ -0,0 +1,138 @@
+// Copyright 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.
+
+// Chromium settings and storage represent user-selected preferences and
+// information and MUST not be extracted, overwritten or modified except
+// through Chromium defined APIs.
+
+#ifndef COMPONENTS_WEBDATA_COMMON_WEB_DATA_REQUEST_MANAGER_H__
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_REQUEST_MANAGER_H__
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "components/webdata/common/web_database_service.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+
+class WebDataService;
+class WebDataServiceConsumer;
+class WebDataRequestManager;
+
+namespace base {
+class MessageLoop;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Webdata requests
+//
+// Every request is processed using a request object. The object contains
+// both the request parameters and the results.
+//////////////////////////////////////////////////////////////////////////////
+class WebDataRequest {
+ public:
+ WebDataRequest(WebDataServiceConsumer* consumer,
+ WebDataRequestManager* manager);
+
+ virtual ~WebDataRequest();
+
+ WebDataServiceBase::Handle GetHandle() const;
+
+ // Retrieves the |consumer_| set in the constructor.
+ WebDataServiceConsumer* GetConsumer() const;
+
+ // Retrieves the original message loop the of the request.
+ base::MessageLoop* GetMessageLoop() const;
+
+ // Returns |true| if the request was cancelled via the |Cancel()| method.
+ bool IsCancelled() const;
+
+ // This can be invoked from any thread. From this point we assume that
+ // our consumer_ reference is invalid.
+ void Cancel();
+
+ // Invoked when the request has been completed.
+ void OnComplete();
+
+ // The result is owned by the request.
+ void SetResult(scoped_ptr<WDTypedResult> r);
+
+ // Transfers ownership pof result to caller. Should only be called once per
+ // result.
+ scoped_ptr<WDTypedResult> GetResult();
+
+ private:
+ // Used to notify manager if request is cancelled. Uses a raw ptr instead of
+ // a ref_ptr so that it can be set to NULL when a request is cancelled.
+ WebDataRequestManager* manager_;
+
+ // Tracks loop that the request originated on.
+ base::MessageLoop* message_loop_;
+
+ // Identifier for this request.
+ WebDataServiceBase::Handle handle_;
+
+ // A lock to protect against simultaneous cancellations of the request.
+ // Cancellation affects both the |cancelled_| flag and |consumer_|.
+ mutable base::Lock cancel_lock_;
+ bool cancelled_;
+
+ // The originator of the service request.
+ WebDataServiceConsumer* consumer_;
+
+ scoped_ptr<WDTypedResult> result_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDataRequest);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Webdata Request Manager
+//
+// Tracks all WebDataRequests for a WebDataService.
+//
+// Note: This is an internal interface, not to be used outside of webdata/
+//////////////////////////////////////////////////////////////////////////////
+class WebDataRequestManager
+ : public base::RefCountedThreadSafe<WebDataRequestManager> {
+ public:
+ WebDataRequestManager();
+
+ // Cancel any pending request.
+ void CancelRequest(WebDataServiceBase::Handle h);
+
+ // Invoked by the WebDataService when |request| has been completed.
+ void RequestCompleted(scoped_ptr<WebDataRequest> request);
+
+ // Register the request as a pending request.
+ void RegisterRequest(WebDataRequest* request);
+
+ // Return the next request handle.
+ int GetNextRequestHandle();
+
+ private:
+ friend class base::RefCountedThreadSafe<WebDataRequestManager>;
+
+ ~WebDataRequestManager();
+
+ // This will notify the consumer in whatever thread was used to create this
+ // request.
+ void RequestCompletedOnThread(scoped_ptr<WebDataRequest> request);
+
+ // A lock to protect pending requests and next request handle.
+ base::Lock pending_lock_;
+
+ // Next handle to be used for requests. Incremented for each use.
+ WebDataServiceBase::Handle next_request_handle_;
+
+ typedef std::map<WebDataServiceBase::Handle, WebDataRequest*> RequestMap;
+ RequestMap pending_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDataRequestManager);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_REQUEST_MANAGER_H__
diff --git a/chromium/components/webdata/common/web_data_results.h b/chromium/components/webdata/common/web_data_results.h
new file mode 100644
index 00000000000..7a225e9c6ee
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_results.h
@@ -0,0 +1,135 @@
+// 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_WEBDATA_COMMON_WEB_DATA_RESULTS_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_RESULTS_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "components/webdata/common/webdata_export.h"
+
+class WDTypedResult;
+
+//
+// Result types for WebDataService.
+//
+typedef enum {
+ BOOL_RESULT = 1, // WDResult<bool>
+ KEYWORDS_RESULT, // WDResult<WDKeywordsResult>
+ INT64_RESULT, // WDResult<int64>
+#if defined(OS_WIN)
+ PASSWORD_IE7_RESULT, // WDResult<IE7PasswordInfo>
+#endif
+ WEB_APP_IMAGES, // WDResult<WDAppImagesResult>
+ TOKEN_RESULT, // WDResult<std::vector<std::string>>
+ AUTOFILL_VALUE_RESULT, // WDResult<std::vector<string16>>
+ AUTOFILL_CHANGES, // WDResult<std::vector<AutofillChange>>
+ AUTOFILL_PROFILE_RESULT, // WDResult<AutofillProfile>
+ AUTOFILL_PROFILES_RESULT, // WDResult<std::vector<AutofillProfile*>>
+ AUTOFILL_CREDITCARD_RESULT, // WDResult<CreditCard>
+ AUTOFILL_CREDITCARDS_RESULT, // WDResult<std::vector<CreditCard*>>
+ WEB_INTENTS_RESULT, // WDResult<std::vector<WebIntentServiceData>>
+ WEB_INTENTS_DEFAULTS_RESULT, // WDResult<std::vector<DefaultWebIntentService>>
+} WDResultType;
+
+
+typedef base::Callback<void(const WDTypedResult*)> DestroyCallback;
+
+//
+// The top level class for a result.
+//
+class WEBDATA_EXPORT WDTypedResult {
+ public:
+ virtual ~WDTypedResult() {
+ }
+
+ // Return the result type.
+ WDResultType GetType() const {
+ return type_;
+ }
+
+ virtual void Destroy() {
+ }
+
+ protected:
+ explicit WDTypedResult(WDResultType type)
+ : type_(type) {
+ }
+
+ private:
+ WDResultType type_;
+ DISALLOW_COPY_AND_ASSIGN(WDTypedResult);
+};
+
+// A result containing one specific pointer or literal value.
+template <class T> class WDResult : public WDTypedResult {
+ public:
+ WDResult(WDResultType type, const T& v)
+ : WDTypedResult(type), value_(v) {
+ }
+
+ virtual ~WDResult() {
+ }
+
+ // Return a single value result.
+ T GetValue() const {
+ return value_;
+ }
+
+ private:
+ T value_;
+
+ DISALLOW_COPY_AND_ASSIGN(WDResult);
+};
+
+template <class T> class WDDestroyableResult : public WDTypedResult {
+ public:
+ WDDestroyableResult(
+ WDResultType type,
+ const T& v,
+ const DestroyCallback& callback)
+ : WDTypedResult(type),
+ value_(v),
+ callback_(callback) {
+ }
+
+ virtual ~WDDestroyableResult() {
+ }
+
+
+ virtual void Destroy() OVERRIDE {
+ if (!callback_.is_null()) {
+ callback_.Run(this);
+ }
+ }
+
+ // Return a single value result.
+ T GetValue() const {
+ return value_;
+ }
+
+ private:
+ T value_;
+ DestroyCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WDDestroyableResult);
+};
+
+template <class T> class WDObjectResult : public WDTypedResult {
+ public:
+ explicit WDObjectResult(WDResultType type)
+ : WDTypedResult(type) {
+ }
+
+ T* GetValue() const {
+ return &value_;
+ }
+
+ private:
+ // mutable to keep GetValue() const.
+ mutable T value_;
+ DISALLOW_COPY_AND_ASSIGN(WDObjectResult);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_RESULTS_H_
diff --git a/chromium/components/webdata/common/web_data_service_backend.cc b/chromium/components/webdata/common/web_data_service_backend.cc
new file mode 100644
index 00000000000..9f48e52be69
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_backend.cc
@@ -0,0 +1,124 @@
+// 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/webdata/common/web_data_service_backend.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "components/webdata/common/web_data_request_manager.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/common/web_database_table.h"
+
+using base::Bind;
+using base::FilePath;
+
+WebDataServiceBackend::WebDataServiceBackend(
+ const FilePath& path,
+ Delegate* delegate,
+ const scoped_refptr<base::MessageLoopProxy>& db_thread)
+ : base::RefCountedDeleteOnMessageLoop<WebDataServiceBackend>(db_thread),
+ db_path_(path),
+ request_manager_(new WebDataRequestManager()),
+ init_status_(sql::INIT_FAILURE),
+ init_complete_(false),
+ delegate_(delegate) {
+}
+
+void WebDataServiceBackend::AddTable(scoped_ptr<WebDatabaseTable> table) {
+ DCHECK(!db_.get());
+ tables_.push_back(table.release());
+}
+
+void WebDataServiceBackend::InitDatabase() {
+ LoadDatabaseIfNecessary();
+ if (delegate_) {
+ delegate_->DBLoaded(init_status_);
+ }
+}
+
+sql::InitStatus WebDataServiceBackend::LoadDatabaseIfNecessary() {
+ if (init_complete_ || db_path_.empty()) {
+ return init_status_;
+ }
+ init_complete_ = true;
+ db_.reset(new WebDatabase());
+
+ for (ScopedVector<WebDatabaseTable>::iterator it = tables_.begin();
+ it != tables_.end();
+ ++it) {
+ db_->AddTable(*it);
+ }
+
+ init_status_ = db_->Init(db_path_);
+ if (init_status_ != sql::INIT_OK) {
+ LOG(ERROR) << "Cannot initialize the web database: " << init_status_;
+ db_.reset(NULL);
+ return init_status_;
+ }
+
+ db_->BeginTransaction();
+ return init_status_;
+}
+
+void WebDataServiceBackend::ShutdownDatabase(bool should_reinit) {
+ if (db_ && init_status_ == sql::INIT_OK)
+ db_->CommitTransaction();
+ db_.reset(NULL);
+ init_complete_ = !should_reinit; // Setting init_complete_ to true will ensure
+ // that the init sequence is not re-run.
+
+ init_status_ = sql::INIT_FAILURE;
+}
+
+void WebDataServiceBackend::DBWriteTaskWrapper(
+ const WebDatabaseService::WriteTask& task,
+ scoped_ptr<WebDataRequest> request) {
+ if (request->IsCancelled())
+ return;
+
+ ExecuteWriteTask(task);
+ request_manager_->RequestCompleted(request.Pass());
+}
+
+void WebDataServiceBackend::ExecuteWriteTask(
+ const WebDatabaseService::WriteTask& task) {
+ LoadDatabaseIfNecessary();
+ if (db_ && init_status_ == sql::INIT_OK) {
+ WebDatabase::State state = task.Run(db_.get());
+ if (state == WebDatabase::COMMIT_NEEDED)
+ Commit();
+ }
+}
+
+void WebDataServiceBackend::DBReadTaskWrapper(
+ const WebDatabaseService::ReadTask& task,
+ scoped_ptr<WebDataRequest> request) {
+ if (request->IsCancelled())
+ return;
+
+ request->SetResult(ExecuteReadTask(task).Pass());
+ request_manager_->RequestCompleted(request.Pass());
+}
+
+scoped_ptr<WDTypedResult> WebDataServiceBackend::ExecuteReadTask(
+ const WebDatabaseService::ReadTask& task) {
+ LoadDatabaseIfNecessary();
+ if (db_ && init_status_ == sql::INIT_OK) {
+ return task.Run(db_.get());
+ }
+ return scoped_ptr<WDTypedResult>();
+}
+
+WebDataServiceBackend::~WebDataServiceBackend() {
+ ShutdownDatabase(false);
+}
+
+void WebDataServiceBackend::Commit() {
+ if (db_ && init_status_ == sql::INIT_OK) {
+ db_->CommitTransaction();
+ db_->BeginTransaction();
+ } else {
+ NOTREACHED() << "Commit scheduled after Shutdown()";
+ }
+}
diff --git a/chromium/components/webdata/common/web_data_service_backend.h b/chromium/components/webdata/common/web_data_service_backend.h
new file mode 100644
index 00000000000..3a6fe6fc91f
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_backend.h
@@ -0,0 +1,130 @@
+// 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_WEBDATA_COMMON_WEB_DATA_SERVICE_BACKEND_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_BACKEND_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "components/webdata/common/web_database_service.h"
+#include "components/webdata/common/webdata_export.h"
+
+class WebDatabase;
+class WebDatabaseTable;
+class WebDataRequest;
+class WebDataRequestManager;
+
+namespace tracked_objects {
+class Location;
+}
+
+// WebDataServiceBackend handles all database tasks posted by
+// WebDatabaseService. It is refcounted to allow asynchronous destruction on the
+// DB thread.
+
+// TODO(caitkp): Rename this class to WebDatabaseBackend.
+
+class WEBDATA_EXPORT WebDataServiceBackend
+ : public base::RefCountedDeleteOnMessageLoop<WebDataServiceBackend> {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Invoked when the backend has finished loading the db.
+ virtual void DBLoaded(sql::InitStatus status) = 0;
+ };
+
+ WebDataServiceBackend(const base::FilePath& path,
+ Delegate* delegate,
+ const scoped_refptr<base::MessageLoopProxy>& db_thread);
+
+ // Must call only before InitDatabaseWithCallback.
+ void AddTable(scoped_ptr<WebDatabaseTable> table);
+
+ // Initializes the database and notifies caller via callback when complete.
+ // Callback is called synchronously.
+ void InitDatabase();
+
+ // Opens the database file from the profile path if an init has not yet been
+ // attempted. Separated from the constructor to ease construction/destruction
+ // of this object on one thread but database access on the DB thread. Returns
+ // the status of the database.
+ sql::InitStatus LoadDatabaseIfNecessary();
+
+ // Shuts down database. |should_reinit| tells us whether or not it should be
+ // possible to re-initialize the DB after the shutdown.
+ void ShutdownDatabase(bool should_reinit);
+
+ // Task wrappers to update requests and and notify |request_manager_|. These
+ // are used in cases where the request is being made from the UI thread and an
+ // asyncronous callback is required to notify the client of |request|'s
+ // completion.
+ void DBWriteTaskWrapper(
+ const WebDatabaseService::WriteTask& task,
+ scoped_ptr<WebDataRequest> request);
+ void DBReadTaskWrapper(
+ const WebDatabaseService::ReadTask& task,
+ scoped_ptr<WebDataRequest> request);
+
+ // Task runners to run database tasks.
+ void ExecuteWriteTask(const WebDatabaseService::WriteTask& task);
+ scoped_ptr<WDTypedResult> ExecuteReadTask(
+ const WebDatabaseService::ReadTask& task);
+
+ const scoped_refptr<WebDataRequestManager>& request_manager() {
+ return request_manager_;
+ }
+
+ WebDatabase* database() { return db_.get(); }
+
+ protected:
+ friend class base::RefCountedDeleteOnMessageLoop<WebDataServiceBackend>;
+ friend class base::DeleteHelper<WebDataServiceBackend>;
+
+ virtual ~WebDataServiceBackend();
+
+ private:
+ // Commit the current transaction.
+ void Commit();
+
+ // Path to database file.
+ base::FilePath db_path_;
+
+ // The tables that participate in managing the database. These are
+ // owned here but other than that this class does nothing with
+ // them. Their initialization is in whatever factory creates
+ // WebDatabaseService, and lookup by type is provided by the
+ // WebDatabase class. The tables need to be owned by this refcounted
+ // object, or they themselves would need to be refcounted. Owning
+ // them here rather than having WebDatabase own them makes for
+ // easier unit testing of WebDatabase.
+ ScopedVector<WebDatabaseTable> tables_;
+
+ scoped_ptr<WebDatabase> db_;
+
+ // Keeps track of all pending requests made to the db.
+ scoped_refptr<WebDataRequestManager> request_manager_;
+
+ // State of database initialization. Used to prevent the executing of tasks
+ // before the db is ready.
+ sql::InitStatus init_status_;
+
+ // True if an attempt has been made to load the database (even if the attempt
+ // fails), used to avoid continually trying to reinit if the db init fails.
+ bool init_complete_;
+
+ // Delegate. See the class definition above for more information.
+ scoped_ptr<Delegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDataServiceBackend);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_BACKEND_H_
diff --git a/chromium/components/webdata/common/web_data_service_base.cc b/chromium/components/webdata/common/web_data_service_base.cc
new file mode 100644
index 00000000000..6427e5d4869
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_base.cc
@@ -0,0 +1,78 @@
+// 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/webdata/common/web_data_service_base.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/threading/thread.h"
+#include "components/webdata/common/web_database_service.h"
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDataServiceBase implementation.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+using base::Bind;
+using base::Time;
+
+WebDataServiceBase::WebDataServiceBase(
+ scoped_refptr<WebDatabaseService> wdbs,
+ const ProfileErrorCallback& callback,
+ const scoped_refptr<base::MessageLoopProxy>& ui_thread)
+ : base::RefCountedDeleteOnMessageLoop<WebDataServiceBase>(ui_thread),
+ wdbs_(wdbs),
+ profile_error_callback_(callback) {
+}
+
+void WebDataServiceBase::ShutdownOnUIThread() {
+}
+
+void WebDataServiceBase::Init() {
+ DCHECK(wdbs_.get());
+ wdbs_->RegisterDBErrorCallback(profile_error_callback_);
+ wdbs_->LoadDatabase();
+}
+
+void WebDataServiceBase::UnloadDatabase() {
+ if (!wdbs_.get())
+ return;
+ wdbs_->UnloadDatabase();
+}
+
+void WebDataServiceBase::ShutdownDatabase() {
+ if (!wdbs_.get())
+ return;
+ wdbs_->ShutdownDatabase();
+}
+
+void WebDataServiceBase::CancelRequest(Handle h) {
+ if (!wdbs_.get())
+ return;
+ wdbs_->CancelRequest(h);
+}
+
+bool WebDataServiceBase::IsDatabaseLoaded() {
+ if (!wdbs_.get())
+ return false;
+ return wdbs_->db_loaded();
+}
+
+void WebDataServiceBase::RegisterDBLoadedCallback(
+ const DBLoadedCallback& callback) {
+ if (!wdbs_.get())
+ return;
+ wdbs_->RegisterDBLoadedCallback(callback);
+}
+
+WebDatabase* WebDataServiceBase::GetDatabase() {
+ if (!wdbs_.get())
+ return NULL;
+ return wdbs_->GetDatabaseOnDB();
+}
+
+WebDataServiceBase::~WebDataServiceBase() {
+}
diff --git a/chromium/components/webdata/common/web_data_service_base.h b/chromium/components/webdata/common/web_data_service_base.h
new file mode 100644
index 00000000000..3a396229415
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_base.h
@@ -0,0 +1,103 @@
+// 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_WEBDATA_COMMON_WEB_DATA_SERVICE_BASE_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_BASE_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/webdata/common/webdata_export.h"
+#include "sql/init_status.h"
+
+class WebDatabase;
+class WebDatabaseService;
+class WebDatabaseTable;
+
+namespace base {
+class Thread;
+}
+
+// Base for WebDataService class hierarchy.
+// WebDataServiceBase is destroyed on the UI thread.
+class WEBDATA_EXPORT WebDataServiceBase
+ : public base::RefCountedDeleteOnMessageLoop<WebDataServiceBase> {
+ public:
+ // All requests return an opaque handle of the following type.
+ typedef int Handle;
+
+ // Users of this class may provide a callback to handle errors
+ // (e.g. by showing a UI). The callback is called only on error, and
+ // takes a single parameter, the sql::InitStatus value from trying
+ // to open the database.
+ // TODO(joi): Should we combine this with WebDatabaseService::InitCallback?
+ typedef base::Callback<void(sql::InitStatus)> ProfileErrorCallback;
+
+ typedef base::Closure DBLoadedCallback;
+
+ // |callback| will only be invoked on error, and only if
+ // |callback.is_null()| evaluates to false.
+ //
+ // The ownership of |wdbs| is shared, with the primary owner being the
+ // WebDataServiceWrapper, and secondary owners being subclasses of
+ // WebDataServiceBase, which receive |wdbs| upon construction. The
+ // WebDataServiceWrapper handles the initializing and shutting down and of
+ // the |wdbs| object.
+ // WebDataServiceBase is destroyed on |ui_thread|.
+ WebDataServiceBase(scoped_refptr<WebDatabaseService> wdbs,
+ const ProfileErrorCallback& callback,
+ const scoped_refptr<base::MessageLoopProxy>& ui_thread);
+
+ // Cancel any pending request. You need to call this method if your
+ // WebDataServiceConsumer is about to be deleted.
+ virtual void CancelRequest(Handle h);
+
+ // Shutdown the web data service. The service can no longer be used after this
+ // call.
+ virtual void ShutdownOnUIThread();
+
+ // Initializes the web data service.
+ virtual void Init();
+
+ // Unloads the database without actually shutting down the service. This can
+ // be used to temporarily reduce the browser process' memory footprint.
+ void UnloadDatabase();
+
+ // Unloads the database permanently and shuts down service.
+ void ShutdownDatabase();
+
+ // Register a callback to be notified that the database has loaded. Multiple
+ // callbacks may be registered, and each will be called at most once
+ // (following a successful database load), then cleared.
+ // Note: if the database load is already complete, then the callback will NOT
+ // be stored or called.
+ virtual void RegisterDBLoadedCallback(const DBLoadedCallback& callback);
+
+ // Returns true if the database load has completetd successfully, and
+ // ShutdownOnUIThread has not yet been called.
+ virtual bool IsDatabaseLoaded();
+
+ // Returns a pointer to the DB (used by SyncableServices). May return NULL if
+ // the database is not loaded or otherwise unavailable. Must be called on
+ // DBThread.
+ virtual WebDatabase* GetDatabase();
+
+ protected:
+ friend class base::RefCountedDeleteOnMessageLoop<WebDataServiceBase>;
+ friend class base::DeleteHelper<WebDataServiceBase>;
+
+ virtual ~WebDataServiceBase();
+
+ // Our database service.
+ scoped_refptr<WebDatabaseService> wdbs_;
+
+ private:
+ ProfileErrorCallback profile_error_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDataServiceBase);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_BASE_H_
diff --git a/chromium/components/webdata/common/web_data_service_consumer.h b/chromium/components/webdata/common/web_data_service_consumer.h
new file mode 100644
index 00000000000..2341a8f4935
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_consumer.h
@@ -0,0 +1,26 @@
+// 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_WEBDATA_COMMON_WEB_DATA_SERVICE_CONSUMER_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_CONSUMER_H_
+
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_base.h"
+
+// All requests to the web data service are asynchronous. When the request has
+// been performed, the data consumer is notified using the following interface.
+class WebDataServiceConsumer {
+ public:
+ // Called when a request is done. h uniquely identifies the request.
+ // result can be NULL, if no result is expected or if the database could
+ // not be opened. The result object is destroyed after this call.
+ virtual void OnWebDataServiceRequestDone(WebDataServiceBase::Handle h,
+ const WDTypedResult* result) = 0;
+
+ protected:
+ virtual ~WebDataServiceConsumer() {}
+};
+
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_CONSUMER_H_
diff --git a/chromium/components/webdata/common/web_data_service_test_util.cc b/chromium/components/webdata/common/web_data_service_test_util.cc
new file mode 100644
index 00000000000..b1f3429f7ad
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_test_util.cc
@@ -0,0 +1,46 @@
+// 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/webdata/common/web_data_service_test_util.h"
+
+#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
+
+using autofill::AutofillWebDataService;
+
+MockWebDataServiceWrapperBase::MockWebDataServiceWrapperBase() {
+}
+
+MockWebDataServiceWrapperBase::~MockWebDataServiceWrapperBase() {
+}
+
+void MockWebDataServiceWrapperBase::Shutdown() {
+}
+
+// TODO(caitkp): This won't scale well. As we get more WebData subclasses, we
+// will probably need a better way to create these mocks rather than passing
+// all the webdatas in.
+MockWebDataServiceWrapper::MockWebDataServiceWrapper(
+ scoped_refptr<WebDataService> fake_service,
+ scoped_refptr<AutofillWebDataService> fake_autofill,
+ scoped_refptr<TokenWebData> fake_token)
+ : fake_autofill_web_data_(fake_autofill),
+ fake_token_web_data_(fake_token),
+ fake_web_data_(fake_service) {
+}
+
+MockWebDataServiceWrapper::~MockWebDataServiceWrapper() {
+}
+
+scoped_refptr<AutofillWebDataService>
+ MockWebDataServiceWrapper::GetAutofillWebData() {
+ return fake_autofill_web_data_;
+}
+
+scoped_refptr<TokenWebData> MockWebDataServiceWrapper::GetTokenWebData() {
+ return fake_token_web_data_;
+}
+
+scoped_refptr<WebDataService> MockWebDataServiceWrapper::GetWebData() {
+ return fake_web_data_;
+}
diff --git a/chromium/components/webdata/common/web_data_service_test_util.h b/chromium/components/webdata/common/web_data_service_test_util.h
new file mode 100644
index 00000000000..ce37506b478
--- /dev/null
+++ b/chromium/components/webdata/common/web_data_service_test_util.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_WEBDATA_COMMON_WEB_DATA_SERVICE_TEST_UTIL_H__
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_TEST_UTIL_H__
+
+#include "base/basictypes.h"
+#include "base/message_loop/message_loop.h"
+#include "chrome/browser/webdata/token_web_data.h"
+#include "chrome/browser/webdata/web_data_service.h"
+#include "chrome/browser/webdata/web_data_service_factory.h"
+
+// Base class for mocks of WebDataService, that does nothing in
+// Shutdown().
+class MockWebDataServiceWrapperBase : public WebDataServiceWrapper {
+ public:
+ MockWebDataServiceWrapperBase();
+ virtual ~MockWebDataServiceWrapperBase();
+
+ virtual void Shutdown() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapperBase);
+};
+
+// Pass your fake WebDataService in the constructor and this will
+// serve it up via GetWebData().
+class MockWebDataServiceWrapper : public MockWebDataServiceWrapperBase {
+ public:
+ MockWebDataServiceWrapper(
+ scoped_refptr<WebDataService> fake_service,
+ scoped_refptr<autofill::AutofillWebDataService> fake_autofill,
+ scoped_refptr<TokenWebData> fake_token);
+
+ virtual ~MockWebDataServiceWrapper();
+
+ virtual scoped_refptr<autofill::AutofillWebDataService>
+ GetAutofillWebData() OVERRIDE;
+
+ virtual scoped_refptr<TokenWebData> GetTokenWebData() OVERRIDE;
+
+ virtual scoped_refptr<WebDataService> GetWebData() OVERRIDE;
+
+ protected:
+ scoped_refptr<autofill::AutofillWebDataService> fake_autofill_web_data_;
+ scoped_refptr<TokenWebData> fake_token_web_data_;
+ scoped_refptr<WebDataService> fake_web_data_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapper);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATA_SERVICE_TEST_UTIL_H__
diff --git a/chromium/components/webdata/common/web_database.cc b/chromium/components/webdata/common/web_database.cc
new file mode 100644
index 00000000000..e4e44ef1e03
--- /dev/null
+++ b/chromium/components/webdata/common/web_database.cc
@@ -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.
+
+#include "components/webdata/common/web_database.h"
+
+#include <algorithm>
+
+#include "base/stl_util.h"
+#include "sql/statement.h"
+#include "sql/transaction.h"
+
+// Current version number. Note: when changing the current version number,
+// corresponding changes must happen in the unit tests, and new migration test
+// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
+// static
+const int WebDatabase::kCurrentVersionNumber = 52;
+
+namespace {
+
+const int kCompatibleVersionNumber = 48;
+
+// Change the version number and possibly the compatibility version of
+// |meta_table_|.
+void ChangeVersion(sql::MetaTable* meta_table,
+ int version_num,
+ bool update_compatible_version_num) {
+ meta_table->SetVersionNumber(version_num);
+ if (update_compatible_version_num) {
+ meta_table->SetCompatibleVersionNumber(
+ std::min(version_num, kCompatibleVersionNumber));
+ }
+}
+
+// Outputs the failed version number as a warning and always returns
+// |sql::INIT_FAILURE|.
+sql::InitStatus FailedMigrationTo(int version_num) {
+ LOG(WARNING) << "Unable to update web database to version "
+ << version_num << ".";
+ NOTREACHED();
+ return sql::INIT_FAILURE;
+}
+
+} // namespace
+
+WebDatabase::WebDatabase() {}
+
+WebDatabase::~WebDatabase() {
+}
+
+void WebDatabase::AddTable(WebDatabaseTable* table) {
+ tables_[table->GetTypeKey()] = table;
+}
+
+WebDatabaseTable* WebDatabase::GetTable(WebDatabaseTable::TypeKey key) {
+ return tables_[key];
+}
+
+void WebDatabase::BeginTransaction() {
+ db_.BeginTransaction();
+}
+
+void WebDatabase::CommitTransaction() {
+ db_.CommitTransaction();
+}
+
+sql::Connection* WebDatabase::GetSQLConnection() {
+ return &db_;
+}
+
+sql::InitStatus WebDatabase::Init(const base::FilePath& db_name) {
+ db_.set_histogram_tag("Web");
+
+ // We don't store that much data in the tables so use a small page size.
+ // This provides a large benefit for empty tables (which is very likely with
+ // the tables we create).
+ db_.set_page_size(2048);
+
+ // We shouldn't have much data and what access we currently have is quite
+ // infrequent. So we go with a small cache size.
+ db_.set_cache_size(32);
+
+ // Run the database in exclusive mode. Nobody else should be accessing the
+ // database while we're running, and this will give somewhat improved perf.
+ db_.set_exclusive_locking();
+
+ if (!db_.Open(db_name))
+ return sql::INIT_FAILURE;
+
+ // Initialize various tables
+ sql::Transaction transaction(&db_);
+ if (!transaction.Begin())
+ return sql::INIT_FAILURE;
+
+ // Version check.
+ if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber))
+ return sql::INIT_FAILURE;
+ if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
+ LOG(WARNING) << "Web database is too new.";
+ return sql::INIT_TOO_NEW;
+ }
+
+ // Initialize the tables.
+ for (TableMap::iterator it = tables_.begin();
+ it != tables_.end();
+ ++it) {
+ if (!it->second->Init(&db_, &meta_table_)) {
+ LOG(WARNING) << "Unable to initialize the web database.";
+ return sql::INIT_FAILURE;
+ }
+ }
+
+ // If the file on disk is an older database version, bring it up to date.
+ // If the migration fails we return an error to caller and do not commit
+ // the migration.
+ sql::InitStatus migration_status = MigrateOldVersionsAsNeeded();
+ if (migration_status != sql::INIT_OK)
+ return migration_status;
+
+ return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
+}
+
+sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded() {
+ // Some malware used to lower the version number, causing migration to
+ // fail. Ensure the version number is at least as high as the compatible
+ // version number.
+ int current_version = std::max(meta_table_.GetVersionNumber(),
+ meta_table_.GetCompatibleVersionNumber());
+ if (current_version > meta_table_.GetVersionNumber())
+ ChangeVersion(&meta_table_, current_version, false);
+
+ if (current_version < 20) {
+ // Versions 1 - 19 are unhandled. Version numbers greater than
+ // kCurrentVersionNumber should have already been weeded out by the caller.
+ //
+ // When the version is too old, we return failure error code. The schema
+ // is too out of date to migrate.
+ //
+ // There should not be a released product that makes a database too old to
+ // migrate. If we do encounter such a legacy database, we will need a
+ // better solution to handle it (i.e., pop up a dialog to tell the user,
+ // erase all their prefs and start over, etc.).
+ LOG(WARNING) << "Web database version " << current_version <<
+ " is too old to handle.";
+ NOTREACHED();
+ return sql::INIT_FAILURE;
+ }
+
+ for (int next_version = current_version + 1;
+ next_version <= kCurrentVersionNumber;
+ ++next_version) {
+ // Give each table a chance to migrate to this version.
+ for (TableMap::iterator it = tables_.begin();
+ it != tables_.end();
+ ++it) {
+ // Any of the tables may set this to true, but by default it is false.
+ bool update_compatible_version = false;
+ if (!it->second->MigrateToVersion(next_version,
+ &update_compatible_version)) {
+ return FailedMigrationTo(next_version);
+ }
+
+ ChangeVersion(&meta_table_, next_version, update_compatible_version);
+ }
+ }
+ return sql::INIT_OK;
+}
diff --git a/chromium/components/webdata/common/web_database.h b/chromium/components/webdata/common/web_database.h
new file mode 100644
index 00000000000..e7fedf072b2
--- /dev/null
+++ b/chromium/components/webdata/common/web_database.h
@@ -0,0 +1,73 @@
+// 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_WEBDATA_COMMON_WEB_DATABASE_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "components/webdata/common/web_database_table.h"
+#include "components/webdata/common/webdata_export.h"
+#include "sql/connection.h"
+#include "sql/init_status.h"
+#include "sql/meta_table.h"
+
+namespace base {
+class FilePath;
+}
+
+// This class manages a SQLite database that stores various web page meta data.
+class WEBDATA_EXPORT WebDatabase {
+ public:
+ enum State {
+ COMMIT_NOT_NEEDED,
+ COMMIT_NEEDED
+ };
+ // Exposed publicly so the keyword table can access it.
+ static const int kCurrentVersionNumber;
+
+ WebDatabase();
+ virtual ~WebDatabase();
+
+ // Adds a database table. Ownership remains with the caller, which
+ // must ensure that the lifetime of |table| exceeds this object's
+ // lifetime. Must only be called before Init.
+ void AddTable(WebDatabaseTable* table);
+
+ // Retrieves a table based on its |key|.
+ WebDatabaseTable* GetTable(WebDatabaseTable::TypeKey key);
+
+ // Initialize the database given a name. The name defines where the SQLite
+ // file is. If this returns an error code, no other method should be called.
+ //
+ // Before calling this method, you must call AddTable for any
+ // WebDatabaseTable objects that are supposed to participate in
+ // managing the database.
+ sql::InitStatus Init(const base::FilePath& db_name);
+
+ // Transactions management
+ void BeginTransaction();
+ void CommitTransaction();
+
+ // Exposed for testing only.
+ sql::Connection* GetSQLConnection();
+
+ private:
+ // Used by |Init()| to migration database schema from older versions to
+ // current version.
+ sql::InitStatus MigrateOldVersionsAsNeeded();
+
+ sql::Connection db_;
+ sql::MetaTable meta_table_;
+
+ // Map of all the different tables that have been added to this
+ // object. Non-owning.
+ typedef std::map<WebDatabaseTable::TypeKey, WebDatabaseTable*> TableMap;
+ TableMap tables_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDatabase);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_H_
diff --git a/chromium/components/webdata/common/web_database_migration_unittest.cc b/chromium/components/webdata/common/web_database_migration_unittest.cc
new file mode 100644
index 00000000000..5e6ef6e9dc3
--- /dev/null
+++ b/chromium/components/webdata/common/web_database_migration_unittest.cc
@@ -0,0 +1,2082 @@
+// 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/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/guid.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/webdata/keyword_table.h"
+#include "chrome/browser/webdata/logins_table.h"
+#include "chrome/browser/webdata/token_service_table.h"
+#include "chrome/browser/webdata/web_apps_table.h"
+#include "chrome/browser/webdata/web_intents_table.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_type.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/autofill/core/browser/webdata/autofill_change.h"
+#include "components/autofill/core/browser/webdata/autofill_entry.h"
+#include "components/autofill/core/browser/webdata/autofill_table.h"
+#include "components/webdata/common/web_database.h"
+#include "sql/statement.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using autofill::AutofillProfile;
+using autofill::AutofillTable;
+using autofill::CreditCard;
+using base::Time;
+
+namespace {
+
+void AutofillProfile31FromStatement(const sql::Statement& s,
+ AutofillProfile* profile,
+ base::string16* label,
+ int* unique_id,
+ int64* date_modified) {
+ DCHECK(profile);
+ DCHECK(label);
+ DCHECK(unique_id);
+ DCHECK(date_modified);
+ *label = s.ColumnString16(0);
+ *unique_id = s.ColumnInt(1);
+ profile->SetRawInfo(autofill::NAME_FIRST, s.ColumnString16(2));
+ profile->SetRawInfo(autofill::NAME_MIDDLE, s.ColumnString16(3));
+ profile->SetRawInfo(autofill::NAME_LAST, s.ColumnString16(4));
+ profile->SetRawInfo(autofill::EMAIL_ADDRESS, s.ColumnString16(5));
+ profile->SetRawInfo(autofill::COMPANY_NAME, s.ColumnString16(6));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_LINE1, s.ColumnString16(7));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_LINE2, s.ColumnString16(8));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_CITY, s.ColumnString16(9));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_STATE, s.ColumnString16(10));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_ZIP, s.ColumnString16(11));
+ profile->SetInfo(
+ autofill::AutofillType(autofill::ADDRESS_HOME_COUNTRY),
+ s.ColumnString16(12), "en-US");
+ profile->SetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(13));
+ *date_modified = s.ColumnInt64(15);
+ profile->set_guid(s.ColumnString(16));
+ EXPECT_TRUE(base::IsValidGUID(profile->guid()));
+}
+
+void AutofillProfile33FromStatement(const sql::Statement& s,
+ AutofillProfile* profile,
+ int64* date_modified) {
+ DCHECK(profile);
+ DCHECK(date_modified);
+ profile->set_guid(s.ColumnString(0));
+ EXPECT_TRUE(base::IsValidGUID(profile->guid()));
+ profile->SetRawInfo(autofill::COMPANY_NAME, s.ColumnString16(1));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_LINE1, s.ColumnString16(2));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_LINE2, s.ColumnString16(3));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_CITY, s.ColumnString16(4));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_STATE, s.ColumnString16(5));
+ profile->SetRawInfo(autofill::ADDRESS_HOME_ZIP, s.ColumnString16(6));
+ profile->SetInfo(
+ autofill::AutofillType(autofill::ADDRESS_HOME_COUNTRY),
+ s.ColumnString16(7), "en-US");
+ *date_modified = s.ColumnInt64(8);
+}
+
+void CreditCard31FromStatement(const sql::Statement& s,
+ CreditCard* credit_card,
+ base::string16* label,
+ int* unique_id,
+ std::string* encrypted_number,
+ int64* date_modified) {
+ DCHECK(credit_card);
+ DCHECK(label);
+ DCHECK(unique_id);
+ DCHECK(encrypted_number);
+ DCHECK(date_modified);
+ *label = s.ColumnString16(0);
+ *unique_id = s.ColumnInt(1);
+ credit_card->SetRawInfo(autofill::CREDIT_CARD_NAME, s.ColumnString16(2));
+ credit_card->SetRawInfo(autofill::CREDIT_CARD_TYPE, s.ColumnString16(3));
+ credit_card->SetRawInfo(autofill::CREDIT_CARD_EXP_MONTH, s.ColumnString16(5));
+ credit_card->SetRawInfo(
+ autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(6));
+ int encrypted_number_len = s.ColumnByteLength(10);
+ if (encrypted_number_len) {
+ encrypted_number->resize(encrypted_number_len);
+ memcpy(&(*encrypted_number)[0], s.ColumnBlob(10), encrypted_number_len);
+ }
+ *date_modified = s.ColumnInt64(12);
+ credit_card->set_guid(s.ColumnString(13));
+ EXPECT_TRUE(base::IsValidGUID(credit_card->guid()));
+}
+
+void CreditCard32FromStatement(const sql::Statement& s,
+ CreditCard* credit_card,
+ std::string* encrypted_number,
+ int64* date_modified) {
+ DCHECK(credit_card);
+ DCHECK(encrypted_number);
+ DCHECK(date_modified);
+ credit_card->set_guid(s.ColumnString(0));
+ EXPECT_TRUE(base::IsValidGUID(credit_card->guid()));
+ credit_card->SetRawInfo(autofill::CREDIT_CARD_NAME, s.ColumnString16(1));
+ credit_card->SetRawInfo(autofill::CREDIT_CARD_EXP_MONTH, s.ColumnString16(2));
+ credit_card->SetRawInfo(
+ autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(3));
+ int encrypted_number_len = s.ColumnByteLength(4);
+ if (encrypted_number_len) {
+ encrypted_number->resize(encrypted_number_len);
+ memcpy(&(*encrypted_number)[0], s.ColumnBlob(4), encrypted_number_len);
+ }
+ *date_modified = s.ColumnInt64(5);
+}
+
+void CheckHasBackupData(sql::MetaTable* meta_table) {
+ std::string value;
+ EXPECT_TRUE(meta_table->GetValue(
+ "Default Search Provider ID Backup", &value));
+ EXPECT_TRUE(meta_table->GetValue(
+ "Default Search Provider ID Backup Signature", &value));
+}
+
+void CheckNoBackupData(const sql::Connection& connection,
+ sql::MetaTable* meta_table) {
+ std::string value;
+ EXPECT_FALSE(meta_table->GetValue(
+ "Default Search Provider ID Backup", &value));
+ EXPECT_FALSE(meta_table->GetValue(
+ "Default Search Provider ID Backup Signature", &value));
+ EXPECT_FALSE(connection.DoesTableExist("keywords_backup"));
+}
+
+} // anonymous namespace
+
+// The WebDatabaseMigrationTest encapsulates testing of database migrations.
+// Specifically, these tests are intended to exercise any schema changes in
+// the WebDatabase and data migrations that occur in
+// |WebDatabase::MigrateOldVersionsAsNeeded()|.
+class WebDatabaseMigrationTest : public testing::Test {
+ public:
+ WebDatabaseMigrationTest() {}
+ virtual ~WebDatabaseMigrationTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ // Load the database via the WebDatabase class and migrate the database to
+ // the current version.
+ void DoMigration() {
+ // TODO(joi): This whole unit test file needs to stay in //chrome
+ // for now, as it needs to know about all the different table
+ // types. Once all webdata datatypes have been componentized, this
+ // could move to components_unittests.
+ AutofillTable autofill_table("en-US");
+ KeywordTable keyword_table;
+ LoginsTable logins_table;
+ TokenServiceTable token_service_table;
+ WebAppsTable web_apps_table;
+ WebIntentsTable web_intents_table;
+
+ WebDatabase db;
+ db.AddTable(&autofill_table);
+ db.AddTable(&keyword_table);
+ db.AddTable(&logins_table);
+ db.AddTable(&token_service_table);
+ db.AddTable(&web_apps_table);
+ db.AddTable(&web_intents_table);
+
+ // This causes the migration to occur.
+ ASSERT_EQ(sql::INIT_OK, db.Init(GetDatabasePath()));
+ }
+
+ protected:
+ // Current tested version number. When adding a migration in
+ // |WebDatabase::MigrateOldVersionsAsNeeded()| and changing the version number
+ // |kCurrentVersionNumber| this value should change to reflect the new version
+ // number and a new migration test added below.
+ static const int kCurrentTestedVersionNumber;
+
+ base::FilePath GetDatabasePath() {
+ const base::FilePath::CharType kWebDatabaseFilename[] =
+ FILE_PATH_LITERAL("TestWebDatabase.sqlite3");
+ return temp_dir_.path().Append(base::FilePath(kWebDatabaseFilename));
+ }
+
+ // The textual contents of |file| are read from
+ // "components/test/data/web_database" and returned in the string |contents|.
+ // Returns true if the file exists and is read successfully, false otherwise.
+ bool GetWebDatabaseData(const base::FilePath& file, std::string* contents) {
+ base::FilePath source_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &source_path);
+ source_path = source_path.AppendASCII("components");
+ source_path = source_path.AppendASCII("test");
+ source_path = source_path.AppendASCII("data");
+ source_path = source_path.AppendASCII("web_database");
+ source_path = source_path.Append(file);
+ return base::PathExists(source_path) &&
+ file_util::ReadFileToString(source_path, contents);
+ }
+
+ static int VersionFromConnection(sql::Connection* connection) {
+ // Get version.
+ sql::Statement s(connection->GetUniqueStatement(
+ "SELECT value FROM meta WHERE key='version'"));
+ if (!s.Step())
+ return 0;
+ return s.ColumnInt(0);
+ }
+
+ // The sql files located in "chrome/test/data/web_database" were generated by
+ // launching the Chromium application prior to schema change, then using the
+ // sqlite3 command-line application to dump the contents of the "Web Data"
+ // database.
+ // Like this:
+ // > .output version_nn.sql
+ // > .dump
+ void LoadDatabase(const base::FilePath::StringType& file);
+
+ private:
+ base::ScopedTempDir temp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
+};
+
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 52;
+
+void WebDatabaseMigrationTest::LoadDatabase(
+ const base::FilePath::StringType& file) {
+ std::string contents;
+ ASSERT_TRUE(GetWebDatabaseData(base::FilePath(file), &contents));
+
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(connection.Execute(contents.data()));
+}
+
+// Tests that the all migrations from an empty database succeed.
+TEST_F(WebDatabaseMigrationTest, MigrateEmptyToCurrent) {
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // Check that expected tables are present.
+ EXPECT_TRUE(connection.DoesTableExist("autofill"));
+ EXPECT_TRUE(connection.DoesTableExist("autofill_dates"));
+ EXPECT_TRUE(connection.DoesTableExist("autofill_profiles"));
+ EXPECT_TRUE(connection.DoesTableExist("credit_cards"));
+ EXPECT_TRUE(connection.DoesTableExist("keywords"));
+ // The logins table is obsolete. (We used to store saved passwords here.)
+ EXPECT_FALSE(connection.DoesTableExist("logins"));
+ EXPECT_TRUE(connection.DoesTableExist("meta"));
+ EXPECT_TRUE(connection.DoesTableExist("token_service"));
+ EXPECT_TRUE(connection.DoesTableExist("web_app_icons"));
+ EXPECT_TRUE(connection.DoesTableExist("web_apps"));
+ EXPECT_TRUE(connection.DoesTableExist("web_intents"));
+ EXPECT_TRUE(connection.DoesTableExist("web_intents_defaults"));
+ }
+}
+
+// Tests that the |credit_card| table gets added to the schema for a version 22
+// database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion22ToCurrent) {
+ // This schema is taken from a build prior to the addition of the
+ // |credit_card| table. Version 22 of the schema. Contrast this with the
+ // corrupt version below.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_22.sql")));
+
+ // Verify pre-conditions.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // No |credit_card| table prior to version 23.
+ ASSERT_FALSE(connection.DoesColumnExist("credit_cards", "guid"));
+ ASSERT_FALSE(
+ connection.DoesColumnExist("credit_cards", "card_number_encrypted"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // |credit_card| table now exists.
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("credit_cards", "card_number_encrypted"));
+ }
+}
+
+// Tests that the |credit_card| table gets added to the schema for a corrupt
+// version 22 database. The corruption is that the |credit_cards| table exists
+// but the schema version number was not set correctly to 23 or later. This
+// test exercises code introduced to fix bug http://crbug.com/50699 that
+// resulted from the corruption.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion22CorruptedToCurrent) {
+ // This schema is taken from a build after the addition of the |credit_card|
+ // table. Due to a bug in the migration logic the version is set incorrectly
+ // to 22 (it should have been updated to 23 at least).
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_22_corrupt.sql")));
+
+ // Verify pre-conditions. These are expectations for corrupt version 22 of
+ // the database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Columns existing and not existing before current version.
+ ASSERT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id"));
+ ASSERT_TRUE(
+ connection.DoesColumnExist("credit_cards", "card_number_encrypted"));
+ ASSERT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+
+ // Columns existing and not existing before version 25.
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid"));
+ EXPECT_TRUE(
+ connection.DoesColumnExist("credit_cards", "card_number_encrypted"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ }
+}
+
+// Tests that the |keywords| |created_by_policy| column gets added to the schema
+// for a version 25 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion25ToCurrent) {
+ // This schema is taken from a build prior to the addition of the |keywords|
+ // |created_by_policy| column.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_25.sql")));
+
+ // Verify pre-conditions. These are expectations for version 25 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // |keywords| |created_by_policy| column should have been added.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "created_by_policy"));
+ }
+}
+
+// Tests that the credit_cards.billing_address column is changed from a string
+// to an int whilst preserving the associated billing address. This version of
+// the test makes sure a stored label is converted to an ID.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion26ToCurrentStringLabels) {
+ // This schema is taken from a build prior to the change of column type for
+ // credit_cards.billing_address from string to int.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_26.sql")));
+
+ // Verify pre-conditions. These are expectations for version 26 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Columns existing and not existing before current version.
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address"));
+
+ std::string stmt = "INSERT INTO autofill_profiles"
+ "(label, unique_id, first_name, middle_name, last_name, email,"
+ " company_name, address_line_1, address_line_2, city, state, zipcode,"
+ " country, phone, fax)"
+ "VALUES ('Home',1,'','','','','','','','','','','','','')";
+ sql::Statement s(connection.GetUniqueStatement(stmt.c_str()));
+ ASSERT_TRUE(s.Run());
+
+ // Insert a CC linked to an existing address.
+ std::string stmt2 = "INSERT INTO credit_cards"
+ "(label, unique_id, name_on_card, type, card_number,"
+ " expiration_month, expiration_year, verification_code, billing_address,"
+ " shipping_address, card_number_encrypted, verification_code_encrypted)"
+ "VALUES ('label',2,'Jack','Visa','1234',2,2012,'','Home','','','')";
+ sql::Statement s2(connection.GetUniqueStatement(stmt2.c_str()));
+ ASSERT_TRUE(s2.Run());
+
+ // |billing_address| is a string.
+ std::string stmt3 = "SELECT billing_address FROM credit_cards";
+ sql::Statement s3(connection.GetUniqueStatement(stmt3.c_str()));
+ ASSERT_TRUE(s3.Step());
+ EXPECT_EQ(s3.ColumnType(0), sql::COLUMN_TYPE_TEXT);
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address"));
+
+ // Verify the credit card data is converted.
+ sql::Statement s(connection.GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards"));
+ ASSERT_TRUE(s.Step());
+ EXPECT_EQ("Jack", s.ColumnString(1));
+ EXPECT_EQ(2, s.ColumnInt(2));
+ EXPECT_EQ(2012, s.ColumnInt(3));
+ // Column 5 is encrypted number blob.
+ // Column 6 is date_modified.
+ }
+}
+
+// Tests that the credit_cards.billing_address column is changed from a string
+// to an int whilst preserving the associated billing address. This version of
+// the test makes sure a stored string ID is converted to an integer ID.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion26ToCurrentStringIDs) {
+ // This schema is taken from a build prior to the change of column type for
+ // credit_cards.billing_address from string to int.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_26.sql")));
+
+ // Verify pre-conditions. These are expectations for version 26 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address"));
+
+ std::string stmt = "INSERT INTO autofill_profiles"
+ "(label, unique_id, first_name, middle_name, last_name, email,"
+ " company_name, address_line_1, address_line_2, city, state, zipcode,"
+ " country, phone, fax)"
+ "VALUES ('Home',1,'','','','','','','','','','','','','')";
+ sql::Statement s(connection.GetUniqueStatement(stmt.c_str()));
+ ASSERT_TRUE(s.Run());
+
+ // Insert a CC linked to an existing address.
+ std::string stmt2 = "INSERT INTO credit_cards"
+ "(label, unique_id, name_on_card, type, card_number,"
+ " expiration_month, expiration_year, verification_code, billing_address,"
+ " shipping_address, card_number_encrypted, verification_code_encrypted)"
+ "VALUES ('label',2,'Jack','Visa','1234',2,2012,'','1','','','')";
+ sql::Statement s2(connection.GetUniqueStatement(stmt2.c_str()));
+ ASSERT_TRUE(s2.Run());
+
+ // |billing_address| is a string.
+ std::string stmt3 = "SELECT billing_address FROM credit_cards";
+ sql::Statement s3(connection.GetUniqueStatement(stmt3.c_str()));
+ ASSERT_TRUE(s3.Step());
+ EXPECT_EQ(s3.ColumnType(0), sql::COLUMN_TYPE_TEXT);
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // |keywords| |created_by_policy| column should have been added.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "created_by_policy"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address"));
+
+ // Verify the credit card data is converted.
+ sql::Statement s(connection.GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, expiration_year, "
+ "card_number_encrypted, date_modified "
+ "FROM credit_cards"));
+ ASSERT_TRUE(s.Step());
+ EXPECT_EQ("Jack", s.ColumnString(1));
+ EXPECT_EQ(2, s.ColumnInt(2));
+ EXPECT_EQ(2012, s.ColumnInt(3));
+ // Column 5 is encrypted credit card number blo b.
+ // Column 6 is date_modified.
+ }
+}
+
+// Makes sure instant_url is added correctly to keywords.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion27ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_27.sql")));
+
+ // Verify pre-conditions. These are expectations for version 27 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ ASSERT_FALSE(connection.DoesColumnExist("keywords", "instant_url"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // Make sure supports_instant (added in Version 28) was ultimately dropped
+ // again and instant_url was added.
+ EXPECT_FALSE(connection.DoesColumnExist("keywords", "supports_instant"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "instant_url"));
+
+ // Check that instant_url is empty.
+ std::string stmt = "SELECT instant_url FROM keywords";
+ sql::Statement s(connection.GetUniqueStatement(stmt.c_str()));
+ ASSERT_TRUE(s.Step());
+ EXPECT_EQ(std::string(), s.ColumnString(0));
+
+ // Verify the data made it over.
+ stmt = "SELECT " + KeywordTable::GetKeywordColumns() + " FROM keywords";
+ sql::Statement s2(connection.GetUniqueStatement(stmt.c_str()));
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ(2, s2.ColumnInt(0));
+ EXPECT_EQ("Google", s2.ColumnString(1));
+ EXPECT_EQ("google.com", s2.ColumnString(2));
+ EXPECT_EQ("http://www.google.com/favicon.ico", s2.ColumnString(3));
+ EXPECT_EQ("{google:baseURL}search?{google:RLZ}{google:acceptedSuggestion}"\
+ "{google:originalQueryForSuggestion}sourceid=chrome&ie={inputEncoding}"\
+ "&q={searchTerms}",
+ s2.ColumnString(4));
+ EXPECT_TRUE(s2.ColumnBool(5));
+ EXPECT_EQ(std::string(), s2.ColumnString(6));
+ EXPECT_EQ(0, s2.ColumnInt(7));
+ EXPECT_EQ(0, s2.ColumnInt(8));
+ EXPECT_EQ(std::string("UTF-8"), s2.ColumnString(9));
+ EXPECT_TRUE(s2.ColumnBool(10));
+ EXPECT_EQ(std::string("{google:baseSuggestURL}search?client=chrome&hl="
+ "{language}&q={searchTerms}"), s2.ColumnString(11));
+ EXPECT_EQ(1, s2.ColumnInt(12));
+ EXPECT_FALSE(s2.ColumnBool(13));
+ EXPECT_EQ(std::string(), s2.ColumnString(14));
+ EXPECT_EQ(0, s2.ColumnInt(15));
+ EXPECT_EQ(std::string(), s2.ColumnString(16));
+ }
+}
+
+// Makes sure date_modified is added correctly to autofill_profiles and
+// credit_cards.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion29ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_29.sql")));
+
+ // Verify pre-conditions. These are expectations for version 29 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles",
+ "date_modified"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards",
+ "date_modified"));
+ }
+
+ Time pre_creation_time = Time::Now();
+ DoMigration();
+ Time post_creation_time = Time::Now();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // Check that the columns were created.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "date_modified"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards",
+ "date_modified"));
+
+ sql::Statement s_profiles(connection.GetUniqueStatement(
+ "SELECT date_modified FROM autofill_profiles "));
+ ASSERT_TRUE(s_profiles.is_valid());
+ while (s_profiles.Step()) {
+ EXPECT_GE(s_profiles.ColumnInt64(0),
+ pre_creation_time.ToTimeT());
+ EXPECT_LE(s_profiles.ColumnInt64(0),
+ post_creation_time.ToTimeT());
+ }
+ EXPECT_TRUE(s_profiles.Succeeded());
+
+ sql::Statement s_credit_cards(connection.GetUniqueStatement(
+ "SELECT date_modified FROM credit_cards "));
+ ASSERT_TRUE(s_credit_cards.is_valid());
+ while (s_credit_cards.Step()) {
+ EXPECT_GE(s_credit_cards.ColumnInt64(0),
+ pre_creation_time.ToTimeT());
+ EXPECT_LE(s_credit_cards.ColumnInt64(0),
+ post_creation_time.ToTimeT());
+ }
+ EXPECT_TRUE(s_credit_cards.Succeeded());
+ }
+}
+
+// Makes sure guids are added to autofill_profiles and credit_cards tables.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion30ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_30.sql")));
+
+ // Verify pre-conditions. These are expectations for version 29 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "guid"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "guid"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+ ASSERT_TRUE(connection.DoesColumnExist("credit_cards", "guid"));
+
+ // Check that guids are non-null, non-empty, conforms to guid format, and
+ // are different.
+ sql::Statement s(
+ connection.GetUniqueStatement("SELECT guid FROM autofill_profiles"));
+
+ ASSERT_TRUE(s.Step());
+ std::string guid1 = s.ColumnString(0);
+ EXPECT_TRUE(base::IsValidGUID(guid1));
+
+ ASSERT_TRUE(s.Step());
+ std::string guid2 = s.ColumnString(0);
+ EXPECT_TRUE(base::IsValidGUID(guid2));
+
+ EXPECT_NE(guid1, guid2);
+ }
+}
+
+// Removes unique IDs and make GUIDs the primary key. Also removes unused
+// columns.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion31ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_31.sql")));
+
+ // Verify pre-conditions. These are expectations for version 30 of the
+ // database.
+ AutofillProfile profile;
+ base::string16 profile_label;
+ int profile_unique_id = 0;
+ int64 profile_date_modified = 0;
+ CreditCard credit_card;
+ base::string16 cc_label;
+ int cc_unique_id = 0;
+ std::string cc_number_encrypted;
+ int64 cc_date_modified = 0;
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Verify existence of columns we'll be changing.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "unique_id"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "unique_id"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "type"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "card_number"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards",
+ "verification_code"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "billing_address"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "shipping_address"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards",
+ "verification_code_encrypted"));
+
+ // Fetch data in the database prior to migration.
+ sql::Statement s1(
+ connection.GetUniqueStatement(
+ "SELECT label, unique_id, first_name, middle_name, last_name, "
+ "email, company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, fax, date_modified, guid "
+ "FROM autofill_profiles"));
+ ASSERT_TRUE(s1.Step());
+ EXPECT_NO_FATAL_FAILURE(AutofillProfile31FromStatement(
+ s1, &profile, &profile_label, &profile_unique_id,
+ &profile_date_modified));
+
+ sql::Statement s2(
+ connection.GetUniqueStatement(
+ "SELECT label, unique_id, name_on_card, type, card_number, "
+ "expiration_month, expiration_year, verification_code, "
+ "billing_address, shipping_address, card_number_encrypted, "
+ "verification_code_encrypted, date_modified, guid "
+ "FROM credit_cards"));
+ ASSERT_TRUE(s2.Step());
+ EXPECT_NO_FATAL_FAILURE(CreditCard31FromStatement(s2,
+ &credit_card,
+ &cc_label,
+ &cc_unique_id,
+ &cc_number_encrypted,
+ &cc_date_modified));
+
+ EXPECT_NE(profile_unique_id, cc_unique_id);
+ EXPECT_NE(profile.guid(), credit_card.guid());
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // Verify existence of columns we'll be changing.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "unique_id"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "guid"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "unique_id"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "type"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "card_number"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards",
+ "verification_code"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "billing_address"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards",
+ "shipping_address"));
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards",
+ "verification_code_encrypted"));
+
+ // Verify data in the database after the migration.
+ sql::Statement s1(
+ connection.GetUniqueStatement(
+ "SELECT guid, company_name, address_line_1, address_line_2, "
+ "city, state, zipcode, country, date_modified "
+ "FROM autofill_profiles"));
+ ASSERT_TRUE(s1.Step());
+
+ AutofillProfile profile_a;
+ int64 profile_date_modified_a = 0;
+ EXPECT_NO_FATAL_FAILURE(AutofillProfile33FromStatement(
+ s1, &profile_a, &profile_date_modified_a));
+ EXPECT_EQ(profile.guid(), profile_a.guid());
+ EXPECT_EQ(profile.GetRawInfo(autofill::COMPANY_NAME),
+ profile_a.GetRawInfo(autofill::COMPANY_NAME));
+ EXPECT_EQ(profile.GetRawInfo(autofill::ADDRESS_HOME_LINE1),
+ profile_a.GetRawInfo(autofill::ADDRESS_HOME_LINE1));
+ EXPECT_EQ(profile.GetRawInfo(autofill::ADDRESS_HOME_LINE2),
+ profile_a.GetRawInfo(autofill::ADDRESS_HOME_LINE2));
+ EXPECT_EQ(profile.GetRawInfo(autofill::ADDRESS_HOME_CITY),
+ profile_a.GetRawInfo(autofill::ADDRESS_HOME_CITY));
+ EXPECT_EQ(profile.GetRawInfo(autofill::ADDRESS_HOME_STATE),
+ profile_a.GetRawInfo(autofill::ADDRESS_HOME_STATE));
+ EXPECT_EQ(profile.GetRawInfo(autofill::ADDRESS_HOME_ZIP),
+ profile_a.GetRawInfo(autofill::ADDRESS_HOME_ZIP));
+ EXPECT_EQ(profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY),
+ profile_a.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
+ EXPECT_EQ(profile_date_modified, profile_date_modified_a);
+
+ sql::Statement s2(
+ connection.GetUniqueStatement(
+ "SELECT guid, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards"));
+ ASSERT_TRUE(s2.Step());
+
+ CreditCard credit_card_a;
+ base::string16 cc_label_a;
+ std::string cc_number_encrypted_a;
+ int64 cc_date_modified_a = 0;
+ EXPECT_NO_FATAL_FAILURE(CreditCard32FromStatement(s2,
+ &credit_card_a,
+ &cc_number_encrypted_a,
+ &cc_date_modified_a));
+ EXPECT_EQ(credit_card, credit_card_a);
+ EXPECT_EQ(cc_label, cc_label_a);
+ EXPECT_EQ(cc_number_encrypted, cc_number_encrypted_a);
+ EXPECT_EQ(cc_date_modified, cc_date_modified_a);
+ }
+}
+
+// Factor |autofill_profiles| address information separately from name, email,
+// and phone.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion32ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_32.sql")));
+
+ // Verify pre-conditions. These are expectations for version 32 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Verify existence of columns we'll be changing.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "label"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "first_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "middle_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "last_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "email"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "company_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "address_line_1"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "address_line_2"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "city"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "state"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "zipcode"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "country"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "phone"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "fax"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "date_modified"));
+
+ EXPECT_FALSE(connection.DoesTableExist("autofill_profile_names"));
+ EXPECT_FALSE(connection.DoesTableExist("autofill_profile_emails"));
+ EXPECT_FALSE(connection.DoesTableExist("autofill_profile_phones"));
+
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "label"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // Verify changes to columns.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "label"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "first_name"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles",
+ "middle_name"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "last_name"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "email"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "company_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "address_line_1"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "address_line_2"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "city"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "state"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "zipcode"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "country"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "phone"));
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles", "fax"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "date_modified"));
+
+ // New "names" table.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names", "guid"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names",
+ "first_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names",
+ "middle_name"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_names",
+ "last_name"));
+
+ // New "emails" table.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_emails", "guid"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_emails", "email"));
+
+ // New "phones" table.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_phones", "guid"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_phones", "type"));
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profile_phones",
+ "number"));
+
+ EXPECT_FALSE(connection.DoesColumnExist("credit_cards", "label"));
+
+ // Verify data in the database after the migration.
+ sql::Statement s1(
+ connection.GetUniqueStatement(
+ "SELECT guid, company_name, address_line_1, address_line_2, "
+ "city, state, zipcode, country, date_modified "
+ "FROM autofill_profiles"));
+
+ // John Doe.
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s1.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("Doe Enterprises"), s1.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("1 Main St"), s1.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Apt 1"), s1.ColumnString16(3));
+ EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4));
+ EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5));
+ EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6));
+ EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7));
+ EXPECT_EQ(1297882100L, s1.ColumnInt64(8));
+
+ // John P. Doe.
+ // Gets merged during migration from 35 to 37 due to multi-valued fields.
+
+ // Dave Smith.
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s1.ColumnString(0));
+ EXPECT_EQ(base::string16(), s1.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("2 Main Street"), s1.ColumnString16(2));
+ EXPECT_EQ(base::string16(), s1.ColumnString16(3));
+ EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4));
+ EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5));
+ EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6));
+ EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7));
+ EXPECT_EQ(1297882100L, s1.ColumnInt64(8));
+
+ // Dave Smith (Part 2).
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s1.ColumnString(0));
+ EXPECT_EQ(base::string16(), s1.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("2 Main St"), s1.ColumnString16(2));
+ EXPECT_EQ(base::string16(), s1.ColumnString16(3));
+ EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4));
+ EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5));
+ EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6));
+ EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7));
+ EXPECT_EQ(1297882100L, s1.ColumnInt64(8));
+
+ // Alfred E Newman.
+ // Gets culled during migration from 35 to 36 due to incomplete address.
+
+ // 3 Main St.
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s1.ColumnString(0));
+ EXPECT_EQ(base::string16(), s1.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("3 Main St"), s1.ColumnString16(2));
+ EXPECT_EQ(base::string16(), s1.ColumnString16(3));
+ EXPECT_EQ(ASCIIToUTF16("Los Altos"), s1.ColumnString16(4));
+ EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5));
+ EXPECT_EQ(ASCIIToUTF16("94022"), s1.ColumnString16(6));
+ EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7));
+ EXPECT_EQ(1297882100L, s1.ColumnInt64(8));
+
+ // That should be all.
+ EXPECT_FALSE(s1.Step());
+
+ sql::Statement s2(
+ connection.GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name "
+ "FROM autofill_profile_names"));
+
+ // John Doe.
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s2.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("John"), s2.ColumnString16(1));
+ EXPECT_EQ(base::string16(), s2.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Doe"), s2.ColumnString16(3));
+
+ // John P. Doe. Note same guid as above due to merging of multi-valued
+ // fields.
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s2.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("John"), s2.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("P."), s2.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Doe"), s2.ColumnString16(3));
+
+ // Dave Smith.
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s2.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("Dave"), s2.ColumnString16(1));
+ EXPECT_EQ(base::string16(), s2.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Smith"), s2.ColumnString16(3));
+
+ // Dave Smith (Part 2).
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s2.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("Dave"), s2.ColumnString16(1));
+ EXPECT_EQ(base::string16(), s2.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Smith"), s2.ColumnString16(3));
+
+ // Alfred E Newman.
+ // Gets culled during migration from 35 to 36 due to incomplete address.
+
+ // 3 Main St.
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s2.ColumnString(0));
+ EXPECT_EQ(base::string16(), s2.ColumnString16(1));
+ EXPECT_EQ(base::string16(), s2.ColumnString16(2));
+ EXPECT_EQ(base::string16(), s2.ColumnString16(3));
+
+ // Should be all.
+ EXPECT_FALSE(s2.Step());
+
+ sql::Statement s3(
+ connection.GetUniqueStatement(
+ "SELECT guid, email "
+ "FROM autofill_profile_emails"));
+
+ // John Doe.
+ ASSERT_TRUE(s3.Step());
+ EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s3.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("john@doe.com"), s3.ColumnString16(1));
+
+ // John P. Doe.
+ // Gets culled during migration from 35 to 37 due to merging of John Doe and
+ // John P. Doe addresses.
+
+ // 2 Main Street.
+ ASSERT_TRUE(s3.Step());
+ EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s3.ColumnString(0));
+ EXPECT_EQ(base::string16(), s3.ColumnString16(1));
+
+ // 2 Main St.
+ ASSERT_TRUE(s3.Step());
+ EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s3.ColumnString(0));
+ EXPECT_EQ(base::string16(), s3.ColumnString16(1));
+
+ // Alfred E Newman.
+ // Gets culled during migration from 35 to 36 due to incomplete address.
+
+ // 3 Main St.
+ ASSERT_TRUE(s3.Step());
+ EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s3.ColumnString(0));
+ EXPECT_EQ(base::string16(), s3.ColumnString16(1));
+
+ // Should be all.
+ EXPECT_FALSE(s3.Step());
+
+ sql::Statement s4(
+ connection.GetUniqueStatement(
+ "SELECT guid, type, number "
+ "FROM autofill_profile_phones"));
+
+ // John Doe phone.
+ ASSERT_TRUE(s4.Step());
+ EXPECT_EQ("00580526-FF81-EE2A-0546-1AC593A32E2F", s4.ColumnString(0));
+ EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone.
+ EXPECT_EQ(ASCIIToUTF16("4151112222"), s4.ColumnString16(2));
+
+ // John Doe fax.
+ // Gets culled after fax type removed.
+
+ // John P. Doe phone.
+ // Gets culled during migration from 35 to 37 due to merging of John Doe and
+ // John P. Doe addresses.
+
+ // John P. Doe fax.
+ // Gets culled during migration from 35 to 37 due to merging of John Doe and
+ // John P. Doe addresses.
+
+ // 2 Main Street phone.
+ ASSERT_TRUE(s4.Step());
+ EXPECT_EQ("4C74A9D8-7EEE-423E-F9C2-E7FA70ED1396", s4.ColumnString(0));
+ EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone.
+ EXPECT_EQ(base::string16(), s4.ColumnString16(2));
+
+ // 2 Main Street fax.
+ // Gets culled after fax type removed.
+
+ // 2 Main St phone.
+ ASSERT_TRUE(s4.Step());
+ EXPECT_EQ("722DF5C4-F74A-294A-46F0-31FFDED0D635", s4.ColumnString(0));
+ EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone.
+ EXPECT_EQ(base::string16(), s4.ColumnString16(2));
+
+ // 2 Main St fax.
+ // Gets culled after fax type removed.
+
+ // Note no phone or fax for Alfred E Newman.
+
+ // 3 Main St phone.
+ ASSERT_TRUE(s4.Step());
+ EXPECT_EQ("9E5FE298-62C7-83DF-6293-381BC589183F", s4.ColumnString(0));
+ EXPECT_EQ(0, s4.ColumnInt(1)); // 0 means phone.
+ EXPECT_EQ(base::string16(), s4.ColumnString16(2));
+
+ // 2 Main St fax.
+ // Gets culled after fax type removed.
+
+ // Should be all.
+ EXPECT_FALSE(s4.Step());
+ }
+}
+
+// Adds a column for the autofill profile's country code.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion33ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_33.sql")));
+
+ // Verify pre-conditions. These are expectations for version 33 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ EXPECT_FALSE(connection.DoesColumnExist("autofill_profiles",
+ "country_code"));
+
+ // Check that the country value is the one we expect.
+ sql::Statement s(
+ connection.GetUniqueStatement("SELECT country FROM autofill_profiles"));
+
+ ASSERT_TRUE(s.Step());
+ std::string country = s.ColumnString(0);
+ EXPECT_EQ("United States", country);
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "country_code"));
+
+ // Check that the country code is properly converted.
+ sql::Statement s(connection.GetUniqueStatement(
+ "SELECT country_code FROM autofill_profiles"));
+
+ ASSERT_TRUE(s.Step());
+ std::string country_code = s.ColumnString(0);
+ EXPECT_EQ("US", country_code);
+ }
+}
+
+// Cleans up bad country code "UK" in favor of good country code "GB".
+TEST_F(WebDatabaseMigrationTest, MigrateVersion34ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_34.sql")));
+
+ // Verify pre-conditions. These are expectations for version 34 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "country_code"));
+
+ // Check that the country_code value is the one we expect.
+ sql::Statement s(
+ connection.GetUniqueStatement("SELECT country_code "
+ "FROM autofill_profiles"));
+
+ ASSERT_TRUE(s.Step());
+ std::string country_code = s.ColumnString(0);
+ EXPECT_EQ("UK", country_code);
+
+ // Should have only one.
+ ASSERT_FALSE(s.Step());
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles",
+ "country_code"));
+
+ // Check that the country_code code is properly converted.
+ sql::Statement s(connection.GetUniqueStatement(
+ "SELECT country_code FROM autofill_profiles"));
+
+ ASSERT_TRUE(s.Step());
+ std::string country_code = s.ColumnString(0);
+ EXPECT_EQ("GB", country_code);
+
+ // Should have only one.
+ ASSERT_FALSE(s.Step());
+ }
+}
+
+// Cleans up invalid profiles based on more agressive merging. Filters out
+// profiles that are subsets of other profiles, and profiles with invalid email,
+// state, and incomplete address.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion35ToCurrent) {
+ // Initialize the database.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_35.sql")));
+
+ // Verify pre-conditions. These are expectations for version 34 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ EXPECT_FALSE(connection.DoesTableExist("autofill_profiles_trash"));
+ ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+
+ // Check that there are 6 profiles prior to merge.
+ sql::Statement s(
+ connection.GetUniqueStatement("SELECT guid FROM autofill_profiles"));
+ int i = 0;
+ while (s.Step())
+ ++i;
+ EXPECT_EQ(6, i);
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ ASSERT_TRUE(connection.DoesTableExist("autofill_profiles_trash"));
+ ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles_trash", "guid"));
+ ASSERT_TRUE(connection.DoesColumnExist("autofill_profiles", "guid"));
+
+ // Verify data in the database after the migration.
+ sql::Statement s1(
+ connection.GetUniqueStatement(
+ "SELECT guid, company_name, address_line_1, address_line_2, "
+ "city, state, zipcode, country, date_modified "
+ "FROM autofill_profiles"));
+
+ // John Doe.
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000001", s1.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("Acme Inc."), s1.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("1 Main Street"), s1.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Apt 2"), s1.ColumnString16(3));
+ EXPECT_EQ(ASCIIToUTF16("San Francisco"), s1.ColumnString16(4));
+ EXPECT_EQ(ASCIIToUTF16("CA"), s1.ColumnString16(5));
+ EXPECT_EQ(ASCIIToUTF16("94102"), s1.ColumnString16(6));
+ EXPECT_EQ(ASCIIToUTF16("United States"), s1.ColumnString16(7));
+ EXPECT_EQ(1300131704, s1.ColumnInt64(8));
+
+ // That should be it.
+ ASSERT_FALSE(s1.Step());
+
+ // Check that there 5 trashed profile after the merge.
+ sql::Statement s2(
+ connection.GetUniqueStatement("SELECT guid "
+ "FROM autofill_profiles_trash"));
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000002", s2.ColumnString(0));
+
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000003", s2.ColumnString(0));
+
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000004", s2.ColumnString(0));
+
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000005", s2.ColumnString(0));
+
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("00000000-0000-0000-0000-000000000006", s2.ColumnString(0));
+
+ // That should be it.
+ ASSERT_FALSE(s2.Step());
+ }
+}
+
+// Tests that the |keywords| |last_modified| column gets added to the schema for
+// a version 37 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion37ToCurrent) {
+ // This schema is taken from a build prior to the addition of the |keywords|
+ // |last_modified| column.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_37.sql")));
+
+ // Verify pre-conditions. These are expectations for version 37 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Columns existing and not existing before current version.
+ ASSERT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords", "last_modified"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // |keywords| |last_modified| column should have been added.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "last_modified"));
+ }
+}
+
+// Tests that the |keywords| |sync_guid| column gets added to the schema for
+// a version 38 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion38ToCurrent) {
+ // This schema is taken from a build prior to the addition of the |keywords|
+ // |sync_guid| column.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_38.sql")));
+
+ // Verify pre-conditions. These are expectations for version 38 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Columns existing and not existing before current version.
+ ASSERT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords", "sync_guid"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // |keywords| |sync_guid| column should have been added.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "id"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "sync_guid"));
+ }
+}
+
+// Tests that no backup data is added to a version 39 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion39ToCurrent) {
+ // This schema is taken from a build prior to the addition of the default
+ // search provider backup field to the meta table.
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_39.sql")));
+
+ // Verify pre-conditions. These are expectations for version 39 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 39, 39));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(0, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the backup data is removed from the database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion40ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_40.sql")));
+
+ // Verify pre-conditions. These are expectations for version 40 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 40, 40));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+
+ EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(0, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the backup data is removed from the database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion41ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_41.sql")));
+
+ // Verify pre-conditions. These are expectations for version 41 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 41, 41));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+
+ EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(0, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the backup data is removed from the database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion42ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_42.sql")));
+
+ // Verify pre-conditions. These are expectations for version 42 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 42, 42));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+
+ EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table));
+
+ EXPECT_FALSE(connection.DoesTableExist("keywords_backup"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(0, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the backup data is removed from the database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion43ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_43.sql")));
+
+ int64 previous_default_search_provider_id;
+
+ // Verify pre-conditions. These are expectations for version 43 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 43, 43));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(default_search_provider_id, 0);
+ previous_default_search_provider_id = default_search_provider_id;
+
+ EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table));
+ EXPECT_TRUE(connection.DoesTableExist("keywords_backup"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(
+ &connection,
+ kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ // Default search provider ID should not change.
+ EXPECT_EQ(previous_default_search_provider_id, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the |autogenerate_keyword| and |logo_id| columns get removed from
+// the keyword table schema for a version 45 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion44ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_44.sql")));
+
+ // Verify pre-conditions. These are expectations for version 44 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 44, 44));
+
+ ASSERT_TRUE(connection.DoesColumnExist("keywords", "autogenerate_keyword"));
+ ASSERT_TRUE(connection.DoesColumnExist("keywords", "logo_id"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ // We should have removed this obsolete key.
+ std::string default_search_provider_backup;
+ EXPECT_FALSE(meta_table.GetValue("Default Search Provider Backup",
+ &default_search_provider_backup));
+
+ // Two columns should have been removed.
+ EXPECT_FALSE(connection.DoesColumnExist("keywords",
+ "autogenerate_keyword"));
+ EXPECT_FALSE(connection.DoesColumnExist("keywords", "logo_id"));
+
+ // Backup data should have been removed.
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the web_intents and web_intents_defaults tables are
+// modified to include "scheme" columns.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion45ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_45.sql")));
+
+ // Verify pre-conditions. These are expectations for version 45 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 45, 45));
+
+ ASSERT_FALSE(connection.DoesColumnExist("scheme", "web_intents"));
+ ASSERT_FALSE(connection.DoesColumnExist(
+ "scheme", "web_intents_defaults"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(
+ &connection,
+ kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ // A new "scheme" column should have been added to each web_intents table.
+ EXPECT_TRUE(connection.DoesColumnExist("web_intents", "scheme"));
+ EXPECT_TRUE(connection.DoesColumnExist("web_intents_defaults", "scheme"));
+
+ // Verify existing user data was copied.
+ sql::Statement s1(
+ connection.GetUniqueStatement("SELECT * FROM web_intents"));
+
+ ASSERT_TRUE(s1.Step());
+ EXPECT_EQ("http://poodles.com/fuzzer", s1.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("fuzz"), s1.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16("poodle/*"), s1.ColumnString16(2));
+ EXPECT_EQ(ASCIIToUTF16("Poodle Fuzzer"), s1.ColumnString16(3));
+ EXPECT_EQ(ASCIIToUTF16("window"), s1.ColumnString16(4));
+ EXPECT_EQ(ASCIIToUTF16(""), s1.ColumnString16(5));
+ ASSERT_FALSE(s1.Step());
+
+ // Now we want to verify existing user data was copied
+ sql::Statement s2(
+ connection.GetUniqueStatement("SELECT * FROM web_intents_defaults"));
+
+ ASSERT_TRUE(s2.Step());
+ EXPECT_EQ("fuzz", s2.ColumnString(0));
+ EXPECT_EQ(ASCIIToUTF16("poodle/*"), s2.ColumnString16(1));
+ EXPECT_EQ(ASCIIToUTF16(""), s2.ColumnString16(2));
+ EXPECT_EQ(0, s2.ColumnInt(3));
+ EXPECT_EQ(0, s2.ColumnInt(4));
+ EXPECT_EQ(ASCIIToUTF16("http://poodles.com/fuzzer"), s2.ColumnString16(5));
+ EXPECT_EQ(ASCIIToUTF16(""), s2.ColumnString16(6));
+ ASSERT_FALSE(s2.Step());
+
+ // finally ensure the migration code cleaned up after itself
+ EXPECT_FALSE(connection.DoesTableExist("old_web_intents"));
+ EXPECT_FALSE(connection.DoesTableExist("old_web_intents_defaults"));
+ }
+}
+
+// Tests that the web_intents and web_intents_defaults tables are
+// modified to include "scheme" columns.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion45InvalidToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_45_invalid.sql")));
+
+ // Verify pre-conditions. These are expectations for version 45 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 45, 45));
+
+ ASSERT_FALSE(connection.DoesColumnExist("scheme", "web_intents"));
+ ASSERT_FALSE(connection.DoesColumnExist(
+ "scheme", "web_intents_defaults"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(
+ &connection,
+ kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ // A new "scheme" column should have been added to each web_intents table.
+ EXPECT_TRUE(connection.DoesColumnExist("web_intents", "scheme"));
+ EXPECT_TRUE(connection.DoesColumnExist("web_intents_defaults", "scheme"));
+
+ // Verify existing user data was copied.
+ sql::Statement s1(
+ connection.GetUniqueStatement("SELECT * FROM web_intents"));
+
+ ASSERT_FALSE(s1.Step()); // Basically should be empty at this point.
+
+ // Now we want to verify existing user data was copied
+ sql::Statement s2(
+ connection.GetUniqueStatement("SELECT * FROM web_intents_defaults"));
+
+ // We were able to create the new tables, but unable to copy any data
+ // Given the initial bad state of the tables.
+ ASSERT_FALSE(s2.Step());
+
+ // Finally ensure the migration code cleaned up after itself.
+ EXPECT_FALSE(connection.DoesTableExist("old_web_intents"));
+ EXPECT_FALSE(connection.DoesTableExist("old_web_intents_defaults"));
+ }
+}
+
+// Check that current version is forced to compatible version before migration,
+// if the former is smaller.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion45CompatibleToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_45_compatible.sql")));
+
+ // Verify pre-conditions. These are expectations for version 45 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ // Database is actually version 45 but the version field states 40.
+ ASSERT_TRUE(meta_table.Init(&connection, 40, 45));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+ EXPECT_LE(45, VersionFromConnection(&connection));
+ }
+}
+
+// Tests that the |alternate_urls| column is added to the keyword table schema
+// for a version 47 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion46ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_46.sql")));
+
+ // Verify pre-conditions. These are expectations for version 46 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 46, 46));
+
+ ASSERT_FALSE(connection.DoesColumnExist("keywords", "alternate_urls"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords_backup",
+ "alternate_urls"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // A new column should have been created.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "alternate_urls"));
+ }
+}
+
+// Tests that the backup data is removed from the database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion47ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_47.sql")));
+
+ // Verify pre-conditions. These are expectations for version 47 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 47, 47));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(0, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckHasBackupData(&meta_table));
+ EXPECT_TRUE(connection.DoesTableExist("keywords_backup"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, kCurrentTestedVersionNumber,
+ kCurrentTestedVersionNumber));
+
+ int64 default_search_provider_id = 0;
+ EXPECT_TRUE(meta_table.GetValue(KeywordTable::kDefaultSearchProviderKey,
+ &default_search_provider_id));
+ EXPECT_NE(0, default_search_provider_id);
+
+ EXPECT_NO_FATAL_FAILURE(CheckNoBackupData(connection, &meta_table));
+ }
+}
+
+// Tests that the |search_terms_replacement_key| column is added to the keyword
+// table schema for a version 49 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion48ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_48.sql")));
+
+ // Verify pre-conditions. These are expectations for version 48 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 48, 48));
+
+ ASSERT_FALSE(connection.DoesColumnExist("keywords",
+ "search_terms_replacement_key"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // A new column should have been created.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords",
+ "search_terms_replacement_key"));
+ }
+}
+
+// Tests that the |origin| column is added to the autofill_profiles and
+// credit_cards table schemas for a version 50 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion49ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_49.sql")));
+
+ // Verify pre-conditions. These are expectations for version 49 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+
+ ASSERT_FALSE(connection.DoesColumnExist("autofill_profiles", "origin"));
+ ASSERT_FALSE(connection.DoesColumnExist("credit_cards", "origin"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // A new column should have been created in both tables.
+ EXPECT_TRUE(connection.DoesColumnExist("autofill_profiles", "origin"));
+ EXPECT_TRUE(connection.DoesColumnExist("credit_cards", "origin"));
+ }
+}
+
+// Tests that the columns |image_url|, |search_url_post_params|,
+// |suggest_url_post_params|, |instant_url_post_params|, |image_url_post_params|
+// are added to the keyword table schema for a version 52 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion50ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_50.sql")));
+
+ // Verify pre-conditions. These are expectations for version 50 of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ sql::MetaTable meta_table;
+ ASSERT_TRUE(meta_table.Init(&connection, 50, 50));
+
+ ASSERT_FALSE(connection.DoesColumnExist("keywords", "image_url"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords",
+ "search_url_post_params"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords",
+ "suggest_url_post_params"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords",
+ "instant_url_post_params"));
+ ASSERT_FALSE(connection.DoesColumnExist("keywords",
+ "image_url_post_params"));
+ }
+
+ DoMigration();
+
+ // Verify post-conditions. These are expectations for current version of the
+ // database.
+ {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.Open(GetDatabasePath()));
+ ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
+
+ // Check version.
+ EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
+
+ // New columns should have been created.
+ EXPECT_TRUE(connection.DoesColumnExist("keywords", "image_url"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords",
+ "search_url_post_params"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords",
+ "suggest_url_post_params"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords",
+ "instant_url_post_params"));
+ EXPECT_TRUE(connection.DoesColumnExist("keywords",
+ "image_url_post_params"));
+ }
+}
diff --git a/chromium/components/webdata/common/web_database_service.cc b/chromium/components/webdata/common/web_database_service.cc
new file mode 100644
index 00000000000..11eb30b8bf1
--- /dev/null
+++ b/chromium/components/webdata/common/web_database_service.cc
@@ -0,0 +1,181 @@
+// 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/webdata/common/web_database_service.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "components/webdata/common/web_data_request_manager.h"
+#include "components/webdata/common/web_data_results.h"
+#include "components/webdata/common/web_data_service_backend.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+
+using base::Bind;
+using base::FilePath;
+
+// Receives messages from the backend on the DB thread, posts them to
+// WebDatabaseService on the UI thread.
+class WebDatabaseService::BackendDelegate :
+ public WebDataServiceBackend::Delegate {
+ public:
+ BackendDelegate(
+ const base::WeakPtr<WebDatabaseService>& web_database_service)
+ : web_database_service_(web_database_service),
+ callback_thread_(base::MessageLoopProxy::current()) {
+ }
+
+ virtual void DBLoaded(sql::InitStatus status) OVERRIDE {
+ callback_thread_->PostTask(
+ FROM_HERE,
+ base::Bind(&WebDatabaseService::OnDatabaseLoadDone,
+ web_database_service_,
+ status));
+ }
+ private:
+ const base::WeakPtr<WebDatabaseService> web_database_service_;
+ scoped_refptr<base::MessageLoopProxy> callback_thread_;
+};
+
+WebDatabaseService::WebDatabaseService(
+ const base::FilePath& path,
+ const scoped_refptr<base::MessageLoopProxy>& ui_thread,
+ const scoped_refptr<base::MessageLoopProxy>& db_thread)
+ : base::RefCountedDeleteOnMessageLoop<WebDatabaseService>(ui_thread),
+ path_(path),
+ weak_ptr_factory_(this),
+ db_loaded_(false),
+ db_thread_(db_thread) {
+ // WebDatabaseService should be instantiated on UI thread.
+ DCHECK(ui_thread->BelongsToCurrentThread());
+ // WebDatabaseService requires DB thread if instantiated.
+ DCHECK(db_thread.get());
+}
+
+WebDatabaseService::~WebDatabaseService() {
+}
+
+void WebDatabaseService::AddTable(scoped_ptr<WebDatabaseTable> table) {
+ if (!wds_backend_.get()) {
+ wds_backend_ = new WebDataServiceBackend(
+ path_, new BackendDelegate(weak_ptr_factory_.GetWeakPtr()),
+ db_thread_);
+ }
+ wds_backend_->AddTable(table.Pass());
+}
+
+void WebDatabaseService::LoadDatabase() {
+ DCHECK(wds_backend_.get());
+
+ db_thread_->PostTask(
+ FROM_HERE,
+ Bind(&WebDataServiceBackend::InitDatabase, wds_backend_));
+}
+
+void WebDatabaseService::UnloadDatabase() {
+ db_loaded_ = false;
+ if (!wds_backend_.get())
+ return;
+ db_thread_->PostTask(FROM_HERE,
+ Bind(&WebDataServiceBackend::ShutdownDatabase,
+ wds_backend_, true));
+}
+
+void WebDatabaseService::ShutdownDatabase() {
+ db_loaded_ = false;
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ loaded_callbacks_.clear();
+ error_callbacks_.clear();
+ if (!wds_backend_.get())
+ return;
+ db_thread_->PostTask(FROM_HERE,
+ Bind(&WebDataServiceBackend::ShutdownDatabase,
+ wds_backend_, false));
+}
+
+WebDatabase* WebDatabaseService::GetDatabaseOnDB() const {
+ DCHECK(db_thread_->BelongsToCurrentThread());
+ if (!wds_backend_.get())
+ return NULL;
+ return wds_backend_->database();
+}
+
+scoped_refptr<WebDataServiceBackend> WebDatabaseService::GetBackend() const {
+ return wds_backend_;
+}
+
+void WebDatabaseService::ScheduleDBTask(
+ const tracked_objects::Location& from_here,
+ const WriteTask& task) {
+ if (!wds_backend_.get()) {
+ NOTREACHED() << "Task scheduled after Shutdown()";
+ return;
+ }
+
+ scoped_ptr<WebDataRequest> request(
+ new WebDataRequest(NULL, wds_backend_->request_manager().get()));
+
+ db_thread_->PostTask(from_here,
+ Bind(&WebDataServiceBackend::DBWriteTaskWrapper, wds_backend_,
+ task, base::Passed(&request)));
+}
+
+WebDataServiceBase::Handle WebDatabaseService::ScheduleDBTaskWithResult(
+ const tracked_objects::Location& from_here,
+ const ReadTask& task,
+ WebDataServiceConsumer* consumer) {
+ DCHECK(consumer);
+ WebDataServiceBase::Handle handle = 0;
+
+ if (!wds_backend_.get()) {
+ NOTREACHED() << "Task scheduled after Shutdown()";
+ return handle;
+ }
+
+ scoped_ptr<WebDataRequest> request(
+ new WebDataRequest(consumer, wds_backend_->request_manager().get()));
+ handle = request->GetHandle();
+
+ db_thread_->PostTask(from_here,
+ Bind(&WebDataServiceBackend::DBReadTaskWrapper, wds_backend_,
+ task, base::Passed(&request)));
+
+ return handle;
+}
+
+void WebDatabaseService::CancelRequest(WebDataServiceBase::Handle h) {
+ if (!wds_backend_.get())
+ return;
+ wds_backend_->request_manager()->CancelRequest(h);
+}
+
+void WebDatabaseService::RegisterDBLoadedCallback(
+ const DBLoadedCallback& callback) {
+ loaded_callbacks_.push_back(callback);
+}
+
+void WebDatabaseService::RegisterDBErrorCallback(
+ const DBLoadErrorCallback& callback) {
+ error_callbacks_.push_back(callback);
+}
+
+void WebDatabaseService::OnDatabaseLoadDone(sql::InitStatus status) {
+ if (status == sql::INIT_OK) {
+ db_loaded_ = true;
+
+ for (size_t i = 0; i < loaded_callbacks_.size(); i++) {
+ if (!loaded_callbacks_[i].is_null())
+ loaded_callbacks_[i].Run();
+ }
+
+ loaded_callbacks_.clear();
+ } else {
+ // Notify that the database load failed.
+ for (size_t i = 0; i < error_callbacks_.size(); i++) {
+ if (!error_callbacks_[i].is_null())
+ error_callbacks_[i].Run(status);
+ }
+
+ error_callbacks_.clear();
+ }
+}
diff --git a/chromium/components/webdata/common/web_database_service.h b/chromium/components/webdata/common/web_database_service.h
new file mode 100644
index 00000000000..95ef5e2c7a1
--- /dev/null
+++ b/chromium/components/webdata/common/web_database_service.h
@@ -0,0 +1,155 @@
+// 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.
+
+// Chromium settings and storage represent user-selected preferences and
+// information and MUST not be extracted, overwritten or modified except
+// through Chromium defined APIs.
+
+#ifndef COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_SERVICE_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_SERVICE_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/ref_counted_delete_on_message_loop.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/observer_list.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_database.h"
+#include "components/webdata/common/webdata_export.h"
+
+class WebDataServiceBackend;
+class WebDataRequestManager;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace tracked_objects {
+class Location;
+}
+
+class WDTypedResult;
+class WebDataServiceConsumer;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WebDatabaseService defines the interface to a generic data repository
+// responsible for controlling access to the web database (metadata associated
+// with web pages).
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class WEBDATA_EXPORT WebDatabaseService
+ : public base::RefCountedDeleteOnMessageLoop<WebDatabaseService> {
+ public:
+ typedef base::Callback<scoped_ptr<WDTypedResult>(WebDatabase*)> ReadTask;
+ typedef base::Callback<WebDatabase::State(WebDatabase*)> WriteTask;
+
+ // Types for managing DB loading callbacks.
+ typedef base::Closure DBLoadedCallback;
+ typedef base::Callback<void(sql::InitStatus)> DBLoadErrorCallback;
+
+ // Takes the path to the WebDatabase file.
+ // WebDatabaseService lives on |ui_thread| and posts tasks to |db_thread|.
+ WebDatabaseService(const base::FilePath& path,
+ const scoped_refptr<base::MessageLoopProxy>& ui_thread,
+ const scoped_refptr<base::MessageLoopProxy>& db_thread);
+
+ // Adds |table| as a WebDatabaseTable that will participate in
+ // managing the database, transferring ownership. All calls to this
+ // method must be made before |LoadDatabase| is called.
+ virtual void AddTable(scoped_ptr<WebDatabaseTable> table);
+
+ // Initializes the web database service.
+ virtual void LoadDatabase();
+
+ // Unloads the database without actually shutting down the service. This can
+ // be used to temporarily reduce the browser process' memory footprint.
+ virtual void UnloadDatabase();
+
+ // Unloads database and will not reload.
+ virtual void ShutdownDatabase();
+
+ // Gets a pointer to the WebDatabase (owned by WebDatabaseService).
+ // TODO(caitkp): remove this method once SyncServices no longer depend on it.
+ virtual WebDatabase* GetDatabaseOnDB() const;
+
+ // Returns a pointer to the WebDataServiceBackend.
+ scoped_refptr<WebDataServiceBackend> GetBackend() const;
+
+ // Schedule an update/write task on the DB thread.
+ virtual void ScheduleDBTask(
+ const tracked_objects::Location& from_here,
+ const WriteTask& task);
+
+ // Schedule a read task on the DB thread.
+ virtual WebDataServiceBase::Handle ScheduleDBTaskWithResult(
+ const tracked_objects::Location& from_here,
+ const ReadTask& task,
+ WebDataServiceConsumer* consumer);
+
+ // Cancel an existing request for a task on the DB thread.
+ // TODO(caitkp): Think about moving the definition of the Handle type to
+ // somewhere else.
+ virtual void CancelRequest(WebDataServiceBase::Handle h);
+
+ // Register a callback to be notified that the database has loaded. Multiple
+ // callbacks may be registered, and each will be called at most once
+ // (following a successful database load), then cleared.
+ // Note: if the database load is already complete, then the callback will NOT
+ // be stored or called.
+ void RegisterDBLoadedCallback(const DBLoadedCallback& callback);
+
+ // Register a callback to be notified that the database has failed to load.
+ // Multiple callbacks may be registered, and each will be called at most once
+ // (following a database load failure), then cleared.
+ // Note: if the database load is already complete, then the callback will NOT
+ // be stored or called.
+ void RegisterDBErrorCallback(const DBLoadErrorCallback& callback);
+
+ bool db_loaded() const { return db_loaded_; };
+
+ private:
+ class BackendDelegate;
+ friend class BackendDelegate;
+ friend class base::RefCountedDeleteOnMessageLoop<WebDatabaseService>;
+ friend class base::DeleteHelper<WebDatabaseService>;
+
+ typedef std::vector<DBLoadedCallback> LoadedCallbacks;
+ typedef std::vector<DBLoadErrorCallback> ErrorCallbacks;
+
+ virtual ~WebDatabaseService();
+
+ void OnDatabaseLoadDone(sql::InitStatus status);
+
+ base::FilePath path_;
+
+ // The primary owner is |WebDatabaseService| but is refcounted because
+ // PostTask on DB thread may outlive us.
+ scoped_refptr<WebDataServiceBackend> wds_backend_;
+
+ // All vended weak pointers are invalidated in ShutdownDatabase().
+ base::WeakPtrFactory<WebDatabaseService> weak_ptr_factory_;
+
+ // Callbacks to be called once the DB has loaded.
+ LoadedCallbacks loaded_callbacks_;
+
+ // Callbacks to be called if the DB has failed to load.
+ ErrorCallbacks error_callbacks_;
+
+ // True if the WebDatabase has loaded.
+ bool db_loaded_;
+
+ scoped_refptr<base::MessageLoopProxy> db_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDatabaseService);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_SERVICE_H_
diff --git a/chromium/components/webdata/common/web_database_table.cc b/chromium/components/webdata/common/web_database_table.cc
new file mode 100644
index 00000000000..57b5b96f20b
--- /dev/null
+++ b/chromium/components/webdata/common/web_database_table.cc
@@ -0,0 +1,17 @@
+// 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/webdata/common/web_database_table.h"
+
+WebDatabaseTable::WebDatabaseTable() : db_(NULL), meta_table_(NULL) {
+}
+
+WebDatabaseTable::~WebDatabaseTable() {
+}
+
+bool WebDatabaseTable::Init(sql::Connection* db, sql::MetaTable* meta_table) {
+ db_ = db;
+ meta_table_ = meta_table;
+ return true;
+}
diff --git a/chromium/components/webdata/common/web_database_table.h b/chromium/components/webdata/common/web_database_table.h
new file mode 100644
index 00000000000..b920ff61f16
--- /dev/null
+++ b/chromium/components/webdata/common/web_database_table.h
@@ -0,0 +1,66 @@
+// 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_WEBDATA_COMMON_WEB_DATABASE_TABLE_H_
+#define COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_TABLE_H_
+
+#include "base/logging.h"
+#include "components/webdata/common/webdata_export.h"
+
+namespace sql {
+class Connection;
+class MetaTable;
+}
+
+// An abstract base class representing a table within a WebDatabase.
+// Each table should subclass this, adding type-specific methods as needed.
+class WEBDATA_EXPORT WebDatabaseTable {
+ public:
+ // To look up a WebDatabaseTable of a certain type from WebDatabase,
+ // we use a void* key, so that we can simply use the address of one
+ // of the type's statics.
+ typedef void* TypeKey;
+
+ // The object is not ready for use until Init() has been called.
+ WebDatabaseTable();
+ virtual ~WebDatabaseTable();
+
+ // Retrieves the TypeKey for the concrete subtype.
+ virtual TypeKey GetTypeKey() const = 0;
+
+ // Attempts to initialize the table and returns true if successful.
+ //
+ // The base class stores the members passed and always return true;
+ // subclasses may perform other initialization as needed.
+ virtual bool Init(sql::Connection* db, sql::MetaTable* meta_table);
+
+ // In order to encourage developers to think about sync when adding or
+ // or altering new tables, this method must be implemented. Please get in
+ // contact with the sync team if you believe you're making a change that they
+ // should be aware of (or if you could break something).
+ // TODO(andybons): Implement something more robust.
+ virtual bool IsSyncable() = 0;
+
+ // Migrates this table to |version|. Returns false if there was
+ // migration work to do and it failed, true otherwise.
+ //
+ // Implementations may set |*update_compatible_version| to true if
+ // the compatible version should be changed to |version|.
+ // Implementations should otherwise not modify this parameter.
+ virtual bool MigrateToVersion(int version,
+ bool* update_compatible_version) = 0;
+
+ protected:
+ // Non-owning. These are owned by WebDatabase, valid as long as that
+ // class exists. Since lifetime of WebDatabaseTable objects slightly
+ // exceeds that of WebDatabase, they should not be used in
+ // ~WebDatabaseTable.
+ sql::Connection* db_;
+ sql::MetaTable* meta_table_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebDatabaseTable);
+};
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEB_DATABASE_TABLE_H_
diff --git a/chromium/components/webdata/common/webdata_constants.cc b/chromium/components/webdata/common/webdata_constants.cc
new file mode 100644
index 00000000000..66dbdb40354
--- /dev/null
+++ b/chromium/components/webdata/common/webdata_constants.cc
@@ -0,0 +1,8 @@
+// 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/webdata/common/webdata_constants.h"
+
+const base::FilePath::CharType kWebDataFilename[] =
+ FILE_PATH_LITERAL("Web Data");
diff --git a/chromium/components/webdata/common/webdata_constants.h b/chromium/components/webdata/common/webdata_constants.h
new file mode 100644
index 00000000000..0aeb1f70478
--- /dev/null
+++ b/chromium/components/webdata/common/webdata_constants.h
@@ -0,0 +1,13 @@
+// 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_WEBDATA_COMMON_WEBDATA_CONSTANTS_H_
+#define COMPONENTS_WEBDATA_COMMON_WEBDATA_CONSTANTS_H_
+
+#include "base/files/file_path.h"
+#include "components/webdata/common/webdata_export.h"
+
+WEBDATA_EXPORT extern const base::FilePath::CharType kWebDataFilename[];
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEBDATA_CONSTANTS_H_
diff --git a/chromium/components/webdata/common/webdata_export.h b/chromium/components/webdata/common/webdata_export.h
new file mode 100644
index 00000000000..9441f191588
--- /dev/null
+++ b/chromium/components/webdata/common/webdata_export.h
@@ -0,0 +1,29 @@
+// 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_WEBDATA_COMMON_WEBDATA_EXPORT_H_
+#define COMPONENTS_WEBDATA_COMMON_WEBDATA_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(WEBDATA_IMPLEMENTATION)
+#define WEBDATA_EXPORT __declspec(dllexport)
+#else
+#define WEBDATA_EXPORT __declspec(dllimport)
+#endif // defined(WEBDATA_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(WEBDATA_IMPLEMENTATION)
+#define WEBDATA_EXPORT __attribute__((visibility("default")))
+#else
+#define WEBDATA_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define WEBDATA_EXPORT
+#endif
+
+#endif // COMPONENTS_WEBDATA_COMMON_WEBDATA_EXPORT_H_
diff --git a/chromium/components/webdata/encryptor/DEPS b/chromium/components/webdata/encryptor/DEPS
new file mode 100644
index 00000000000..d175bda6fe7
--- /dev/null
+++ b/chromium/components/webdata/encryptor/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "-content",
+ "+crypto"
+]
diff --git a/chromium/components/webdata/encryptor/OWNERS b/chromium/components/webdata/encryptor/OWNERS
new file mode 100644
index 00000000000..1967bf567e8
--- /dev/null
+++ b/chromium/components/webdata/encryptor/OWNERS
@@ -0,0 +1 @@
+thestig@chromium.org
diff --git a/chromium/components/webdata/encryptor/README b/chromium/components/webdata/encryptor/README
new file mode 100644
index 00000000000..ae18586d7b5
--- /dev/null
+++ b/chromium/components/webdata/encryptor/README
@@ -0,0 +1,4 @@
+Encryptor gives access to simple encryption and decryption of strings.
+
+On systems where available (currently Linux and Mac), this uses system
+services to perform the encryption.
diff --git a/chromium/components/webdata/encryptor/encryptor.h b/chromium/components/webdata/encryptor/encryptor.h
new file mode 100644
index 00000000000..d8f1a363db2
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 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_WEBDATA_ENCRYPTOR_ENCRYPTOR_H_
+#define COMPONENTS_WEBDATA_ENCRYPTOR_ENCRYPTOR_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+// The Encryptor class gives access to simple encryption and decryption of
+// strings. Note that on Mac, access to the system Keychain is required and
+// these calls can block the current thread to collect user input.
+class Encryptor {
+ public:
+ // Encrypt a string16. The output (second argument) is
+ // really an array of bytes, but we're passing it back
+ // as a std::string
+ static bool EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext);
+
+ // Decrypt an array of bytes obtained with EncryptString16
+ // back into a string16. Note that the input (first argument)
+ // is a std::string, so you need to first get your (binary)
+ // data into a string.
+ static bool DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext);
+
+ // Encrypt a string.
+ static bool EncryptString(const std::string& plaintext,
+ std::string* ciphertext);
+
+ // Decrypt an array of bytes obtained with EnctryptString
+ // back into a string. Note that the input (first argument)
+ // is a std::string, so you need to first get your (binary)
+ // data into a string.
+ static bool DecryptString(const std::string& ciphertext,
+ std::string* plaintext);
+
+#if defined(OS_MACOSX)
+ // For unit testing purposes we instruct the Encryptor to use a mock Keychain
+ // on the Mac. The default is to use the real Keychain.
+ static void UseMockKeychain(bool use_mock);
+#endif
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Encryptor);
+};
+
+#endif // COMPONENTS_WEBDATA_ENCRYPTOR_ENCRYPTOR_H_
diff --git a/chromium/components/webdata/encryptor/encryptor_mac.mm b/chromium/components/webdata/encryptor/encryptor_mac.mm
new file mode 100644
index 00000000000..089bf6c96f5
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_mac.mm
@@ -0,0 +1,150 @@
+// 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/webdata/encryptor/encryptor.h"
+
+#include <CommonCrypto/CommonCryptor.h> // for kCCBlockSizeAES128
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/webdata/encryptor/encryptor_password_mac.h"
+#include "crypto/apple_keychain.h"
+#include "crypto/encryptor.h"
+#include "crypto/symmetric_key.h"
+
+using crypto::AppleKeychain;
+
+namespace {
+
+// Salt for Symmetric key derivation.
+const char kSalt[] = "saltysalt";
+
+// Key size required for 128 bit AES.
+const size_t kDerivedKeySizeInBits = 128;
+
+// Constant for Symmetic key derivation.
+const size_t kEncryptionIterations = 1003;
+
+// TODO(dhollowa): Refactor to allow dependency injection of Keychain.
+static bool use_mock_keychain = false;
+
+// Prefix for cypher text returned by current encryption version. We prefix
+// the cypher text with this string so that future data migration can detect
+// this and migrate to different encryption without data loss.
+const char kEncryptionVersionPrefix[] = "v10";
+
+// Generates a newly allocated SymmetricKey object based on the password found
+// in the Keychain. The generated key is for AES encryption. Ownership of the
+// key is passed to the caller. Returns NULL key in the case password access
+// is denied or key generation error occurs.
+crypto::SymmetricKey* GetEncryptionKey() {
+
+ std::string password;
+ if (use_mock_keychain) {
+ password = "mock_password";
+ } else {
+ AppleKeychain keychain;
+ EncryptorPassword encryptor_password(keychain);
+ password = encryptor_password.GetEncryptorPassword();
+ }
+
+ if (password.empty())
+ return NULL;
+
+ std::string salt(kSalt);
+
+ // Create an encryption key from our password and salt.
+ scoped_ptr<crypto::SymmetricKey> encryption_key(
+ crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
+ password,
+ salt,
+ kEncryptionIterations,
+ kDerivedKeySizeInBits));
+ DCHECK(encryption_key.get());
+
+ return encryption_key.release();
+}
+
+} // namespace
+
+bool Encryptor::EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext) {
+ return EncryptString(UTF16ToUTF8(plaintext), ciphertext);
+}
+
+bool Encryptor::DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext) {
+ std::string utf8;
+ if (!DecryptString(ciphertext, &utf8))
+ return false;
+
+ *plaintext = UTF8ToUTF16(utf8);
+ return true;
+}
+
+bool Encryptor::EncryptString(const std::string& plaintext,
+ std::string* ciphertext) {
+ if (plaintext.empty()) {
+ *ciphertext = std::string();
+ return true;
+ }
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kCCBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Encrypt(plaintext, ciphertext))
+ return false;
+
+ // Prefix the cypher text with version information.
+ ciphertext->insert(0, kEncryptionVersionPrefix);
+ return true;
+}
+
+bool Encryptor::DecryptString(const std::string& ciphertext,
+ std::string* plaintext) {
+ if (ciphertext.empty()) {
+ *plaintext = std::string();
+ return true;
+ }
+
+ // Check that the incoming cyphertext was indeed encrypted with the expected
+ // version. If the prefix is not found then we'll assume we're dealing with
+ // old data saved as clear text and we'll return it directly.
+ // Credit card numbers are current legacy data, so false match with prefix
+ // won't happen.
+ if (ciphertext.find(kEncryptionVersionPrefix) != 0) {
+ *plaintext = ciphertext;
+ return true;
+ }
+
+ // Strip off the versioning prefix before decrypting.
+ std::string raw_ciphertext =
+ ciphertext.substr(strlen(kEncryptionVersionPrefix));
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kCCBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Decrypt(raw_ciphertext, plaintext))
+ return false;
+
+ return true;
+}
+
+void Encryptor::UseMockKeychain(bool use_mock) {
+ use_mock_keychain = use_mock;
+}
+
diff --git a/chromium/components/webdata/encryptor/encryptor_password_mac.h b/chromium/components/webdata/encryptor/encryptor_password_mac.h
new file mode 100644
index 00000000000..f03db495c44
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_password_mac.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_WEBDATA_ENCRYPTOR_ENCRYPTOR_PASSWORD_MAC_H_
+#define COMPONENTS_WEBDATA_ENCRYPTOR_ENCRYPTOR_PASSWORD_MAC_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace crypto {
+class AppleKeychain;
+} // namespace crypto
+
+class EncryptorPassword {
+ public:
+ explicit EncryptorPassword(const crypto::AppleKeychain& keychain)
+ : keychain_(keychain) {
+ }
+
+ // Get the Encryptor password for this system. If no password exists
+ // in the Keychain then one is generated, stored in the Mac keychain, and
+ // returned.
+ // If one exists then it is fetched from the Keychain and returned.
+ // If the user disallows access to the Keychain (or an error occurs) then an
+ // empty string is returned.
+ std::string GetEncryptorPassword() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EncryptorPassword);
+ const crypto::AppleKeychain& keychain_;
+};
+
+#endif // COMPONENTS_WEBDATA_ENCRYPTOR_ENCRYPTOR_PASSWORD_MAC_H_
diff --git a/chromium/components/webdata/encryptor/encryptor_password_mac.mm b/chromium/components/webdata/encryptor/encryptor_password_mac.mm
new file mode 100644
index 00000000000..f8178014df0
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_password_mac.mm
@@ -0,0 +1,79 @@
+// 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/webdata/encryptor/encryptor_password_mac.h"
+
+#import <Security/Security.h>
+
+#include "base/base64.h"
+#include "base/mac/mac_logging.h"
+#include "base/rand_util.h"
+#include "crypto/apple_keychain.h"
+
+using crypto::AppleKeychain;
+
+namespace {
+
+// Generates a random password and adds it to the Keychain. The added password
+// is returned from the function. If an error occurs, an empty password is
+// returned.
+std::string AddRandomPasswordToKeychain(const AppleKeychain& keychain,
+ const std::string& service_name,
+ const std::string& account_name) {
+ // Generate a password with 128 bits of randomness.
+ const int kBytes = 128 / 8;
+ std::string password;
+ base::Base64Encode(base::RandBytesAsString(kBytes), &password);
+ void* password_data =
+ const_cast<void*>(static_cast<const void*>(password.data()));
+
+ OSStatus error = keychain.AddGenericPassword(NULL,
+ service_name.size(),
+ service_name.data(),
+ account_name.size(),
+ account_name.data(),
+ password.size(),
+ password_data,
+ NULL);
+
+ if (error != noErr) {
+ OSSTATUS_DLOG(ERROR, error) << "Keychain add failed";
+ return std::string();
+ }
+
+ return password;
+}
+
+} // namespace
+
+std::string EncryptorPassword::GetEncryptorPassword() const {
+ // These two strings ARE indeed user facing. But they are used to access
+ // the encryption keyword. So as to not lose encrypted data when system
+ // locale changes we DO NOT LOCALIZE.
+ const std::string service_name = "Chrome Safe Storage";
+ const std::string account_name = "Chrome";
+
+ UInt32 password_length = 0;
+ void* password_data = NULL;
+ OSStatus error = keychain_.FindGenericPassword(NULL,
+ service_name.size(),
+ service_name.data(),
+ account_name.size(),
+ account_name.data(),
+ &password_length,
+ &password_data,
+ NULL);
+
+ if (error == noErr) {
+ std::string password =
+ std::string(static_cast<char*>(password_data), password_length);
+ keychain_.ItemFreeContent(NULL, password_data);
+ return password;
+ } else if (error == errSecItemNotFound) {
+ return AddRandomPasswordToKeychain(keychain_, service_name, account_name);
+ } else {
+ OSSTATUS_DLOG(ERROR, error) << "Keychain lookup failed";
+ return std::string();
+ }
+}
diff --git a/chromium/components/webdata/encryptor/encryptor_password_mac_unittest.cc b/chromium/components/webdata/encryptor/encryptor_password_mac_unittest.cc
new file mode 100644
index 00000000000..97462229046
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_password_mac_unittest.cc
@@ -0,0 +1,79 @@
+// 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/webdata/encryptor/encryptor_password_mac.h"
+#include "crypto/mock_apple_keychain.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using crypto::MockAppleKeychain;
+
+// Test that if we have an existing password in the Keychain and we are
+// authorized by the user to read it then we get it back correctly.
+TEST(EncryptorPasswordTest, FindPasswordSuccess) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(noErr);
+ EncryptorPassword password(keychain);
+ EXPECT_FALSE(password.GetEncryptorPassword().empty());
+ EXPECT_FALSE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that if we do not have an existing password in the Keychain then it
+// gets added successfully and returned.
+TEST(EncryptorPasswordTest, FindPasswordNotFound) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(errSecItemNotFound);
+ EncryptorPassword password(keychain);
+ EXPECT_EQ(24U, password.GetEncryptorPassword().length());
+ EXPECT_TRUE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that if get denied access by the user then we return an empty password.
+// And we should not try to add one.
+TEST(EncryptorPasswordTest, FindPasswordNotAuthorized) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(errSecAuthFailed);
+ EncryptorPassword password(keychain);
+ EXPECT_TRUE(password.GetEncryptorPassword().empty());
+ EXPECT_FALSE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that if some random other error happens then we return an empty
+// password, and we should not try to add one.
+TEST(EncryptorPasswordTest, FindPasswordOtherError) {
+ MockAppleKeychain keychain;
+ keychain.set_find_generic_result(errSecNotAvailable);
+ EncryptorPassword password(keychain);
+ EXPECT_TRUE(password.GetEncryptorPassword().empty());
+ EXPECT_FALSE(keychain.called_add_generic());
+ EXPECT_EQ(0, keychain.password_data_count());
+}
+
+// Test that subsequent additions to the keychain give different passwords.
+TEST(EncryptorPasswordTest, PasswordsDiffer) {
+ MockAppleKeychain keychain1;
+ keychain1.set_find_generic_result(errSecItemNotFound);
+ EncryptorPassword encryptor_password1(keychain1);
+ std::string password1 = encryptor_password1.GetEncryptorPassword();
+ EXPECT_FALSE(password1.empty());
+ EXPECT_TRUE(keychain1.called_add_generic());
+ EXPECT_EQ(0, keychain1.password_data_count());
+
+ MockAppleKeychain keychain2;
+ keychain2.set_find_generic_result(errSecItemNotFound);
+ EncryptorPassword encryptor_password2(keychain2);
+ std::string password2 = encryptor_password2.GetEncryptorPassword();
+ EXPECT_FALSE(password2.empty());
+ EXPECT_TRUE(keychain2.called_add_generic());
+ EXPECT_EQ(0, keychain2.password_data_count());
+
+ // And finally check that the passwords are different.
+ EXPECT_NE(password1, password2);
+}
+
+} // namespace
diff --git a/chromium/components/webdata/encryptor/encryptor_posix.cc b/chromium/components/webdata/encryptor/encryptor_posix.cc
new file mode 100644
index 00000000000..ae16af559c7
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_posix.cc
@@ -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.
+
+#include "components/webdata/encryptor/encryptor.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "crypto/encryptor.h"
+#include "crypto/symmetric_key.h"
+
+namespace {
+
+// Salt for Symmetric key derivation.
+const char kSalt[] = "saltysalt";
+
+// Key size required for 128 bit AES.
+const size_t kDerivedKeySizeInBits = 128;
+
+// Constant for Symmetic key derivation.
+const size_t kEncryptionIterations = 1;
+
+// Size of initialization vector for AES 128-bit.
+const size_t kIVBlockSizeAES128 = 16;
+
+// Prefix for cypher text returned by obfuscation version. We prefix the
+// cyphertext with this string so that future data migration can detect
+// this and migrate to full encryption without data loss.
+const char kObfuscationPrefix[] = "v10";
+
+// Generates a newly allocated SymmetricKey object based a hard-coded password.
+// Ownership of the key is passed to the caller. Returns NULL key if a key
+// generation error occurs.
+crypto::SymmetricKey* GetEncryptionKey() {
+ // We currently "obfuscate" by encrypting and decrypting with hard-coded
+ // password. We need to improve this password situation by moving a secure
+ // password into a system-level key store.
+ // http://crbug.com/25404 and http://crbug.com/49115
+ std::string password = "peanuts";
+ std::string salt(kSalt);
+
+ // Create an encryption key from our password and salt.
+ scoped_ptr<crypto::SymmetricKey> encryption_key(
+ crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
+ password,
+ salt,
+ kEncryptionIterations,
+ kDerivedKeySizeInBits));
+ DCHECK(encryption_key.get());
+
+ return encryption_key.release();
+}
+
+} // namespace
+
+bool Encryptor::EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext) {
+ return EncryptString(UTF16ToUTF8(plaintext), ciphertext);
+}
+
+bool Encryptor::DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext) {
+ std::string utf8;
+ if (!DecryptString(ciphertext, &utf8))
+ return false;
+
+ *plaintext = UTF8ToUTF16(utf8);
+ return true;
+}
+
+bool Encryptor::EncryptString(const std::string& plaintext,
+ std::string* ciphertext) {
+ // This currently "obfuscates" by encrypting with hard-coded password.
+ // We need to improve this password situation by moving a secure password
+ // into a system-level key store.
+ // http://crbug.com/25404 and http://crbug.com/49115
+
+ if (plaintext.empty()) {
+ *ciphertext = std::string();
+ return true;
+ }
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kIVBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Encrypt(plaintext, ciphertext))
+ return false;
+
+ // Prefix the cypher text with version information.
+ ciphertext->insert(0, kObfuscationPrefix);
+ return true;
+}
+
+bool Encryptor::DecryptString(const std::string& ciphertext,
+ std::string* plaintext) {
+ // This currently "obfuscates" by encrypting with hard-coded password.
+ // We need to improve this password situation by moving a secure password
+ // into a system-level key store.
+ // http://crbug.com/25404 and http://crbug.com/49115
+
+ if (ciphertext.empty()) {
+ *plaintext = std::string();
+ return true;
+ }
+
+ // Check that the incoming cyphertext was indeed encrypted with the expected
+ // version. If the prefix is not found then we'll assume we're dealing with
+ // old data saved as clear text and we'll return it directly.
+ // Credit card numbers are current legacy data, so false match with prefix
+ // won't happen.
+ if (ciphertext.find(kObfuscationPrefix) != 0) {
+ *plaintext = ciphertext;
+ return true;
+ }
+
+ // Strip off the versioning prefix before decrypting.
+ std::string raw_ciphertext = ciphertext.substr(strlen(kObfuscationPrefix));
+
+ scoped_ptr<crypto::SymmetricKey> encryption_key(GetEncryptionKey());
+ if (!encryption_key.get())
+ return false;
+
+ std::string iv(kIVBlockSizeAES128, ' ');
+ crypto::Encryptor encryptor;
+ if (!encryptor.Init(encryption_key.get(), crypto::Encryptor::CBC, iv))
+ return false;
+
+ if (!encryptor.Decrypt(raw_ciphertext, plaintext))
+ return false;
+
+ return true;
+}
diff --git a/chromium/components/webdata/encryptor/encryptor_unittest.cc b/chromium/components/webdata/encryptor/encryptor_unittest.cc
new file mode 100644
index 00000000000..95158f1f41f
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2010 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/webdata/encryptor/encryptor.h"
+
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class EncryptorTest : public testing::Test {
+ public:
+ EncryptorTest() {}
+
+ virtual void SetUp() {
+#if defined(OS_MACOSX)
+ Encryptor::UseMockKeychain(true);
+#endif
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EncryptorTest);
+};
+
+TEST_F(EncryptorTest, String16EncryptionDecryption) {
+ base::string16 plaintext;
+ base::string16 result;
+ std::string utf8_plaintext;
+ std::string utf8_result;
+ std::string ciphertext;
+
+ // Test borderline cases (empty strings).
+ EXPECT_TRUE(Encryptor::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(Encryptor::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test a simple string.
+ plaintext = ASCIIToUTF16("hello");
+ EXPECT_TRUE(Encryptor::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(Encryptor::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test a 16-byte aligned string. This previously hit a boundary error in
+ // base::Encryptor::Crypt() on Mac.
+ plaintext = ASCIIToUTF16("1234567890123456");
+ EXPECT_TRUE(Encryptor::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(Encryptor::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test Unicode.
+ char16 wchars[] = { 0xdbeb, 0xdf1b, 0x4e03, 0x6708, 0x8849,
+ 0x661f, 0x671f, 0x56db, 0x597c, 0x4e03,
+ 0x6708, 0x56db, 0x6708, 0xe407, 0xdbaf,
+ 0xdeb5, 0x4ec5, 0x544b, 0x661f, 0x671f,
+ 0x65e5, 0x661f, 0x671f, 0x4e94, 0xd8b1,
+ 0xdce1, 0x7052, 0x5095, 0x7c0b, 0xe586, 0};
+ plaintext = wchars;
+ utf8_plaintext = UTF16ToUTF8(plaintext);
+ EXPECT_EQ(plaintext, UTF8ToUTF16(utf8_plaintext));
+ EXPECT_TRUE(Encryptor::EncryptString16(plaintext, &ciphertext));
+ EXPECT_TRUE(Encryptor::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+ EXPECT_TRUE(Encryptor::DecryptString(ciphertext, &utf8_result));
+ EXPECT_EQ(utf8_plaintext, UTF16ToUTF8(result));
+
+ EXPECT_TRUE(Encryptor::EncryptString(utf8_plaintext, &ciphertext));
+ EXPECT_TRUE(Encryptor::DecryptString16(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+ EXPECT_TRUE(Encryptor::DecryptString(ciphertext, &utf8_result));
+ EXPECT_EQ(utf8_plaintext, UTF16ToUTF8(result));
+}
+
+TEST_F(EncryptorTest, EncryptionDecryption) {
+ std::string plaintext;
+ std::string result;
+ std::string ciphertext;
+
+ // Test borderline cases (empty strings).
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(Encryptor::DecryptString(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Test a simple string.
+ plaintext = "hello";
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(Encryptor::DecryptString(ciphertext, &result));
+ EXPECT_EQ(plaintext, result);
+
+ // Make sure it null terminates.
+ plaintext.assign("hello", 3);
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(Encryptor::DecryptString(ciphertext, &result));
+ EXPECT_EQ(plaintext, "hel");
+}
+
+TEST_F(EncryptorTest, CypherTextDiffers) {
+ std::string plaintext;
+ std::string result;
+ std::string ciphertext;
+
+ // Test borderline cases (empty strings).
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(Encryptor::DecryptString(ciphertext, &result));
+ // |cyphertext| is empty on the Mac, different on Windows.
+ EXPECT_TRUE(ciphertext.empty() || plaintext != ciphertext);
+ EXPECT_EQ(plaintext, result);
+
+ // Test a simple string.
+ plaintext = "hello";
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(Encryptor::DecryptString(ciphertext, &result));
+ EXPECT_NE(plaintext, ciphertext);
+ EXPECT_EQ(plaintext, result);
+
+ // Make sure it null terminates.
+ plaintext.assign("hello", 3);
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ ASSERT_TRUE(Encryptor::DecryptString(ciphertext, &result));
+ EXPECT_NE(plaintext, ciphertext);
+ EXPECT_EQ(result, "hel");
+}
+
+TEST_F(EncryptorTest, DecryptError) {
+ std::string plaintext;
+ std::string result;
+ std::string ciphertext;
+
+ // Test a simple string, messing with ciphertext prior to decrypting.
+ plaintext = "hello";
+ ASSERT_TRUE(Encryptor::EncryptString(plaintext, &ciphertext));
+ EXPECT_NE(plaintext, ciphertext);
+ ASSERT_LT(4UL, ciphertext.size());
+ ciphertext[3] = ciphertext[3] + 1;
+ EXPECT_FALSE(Encryptor::DecryptString(ciphertext, &result));
+ EXPECT_NE(plaintext, result);
+ EXPECT_TRUE(result.empty());
+}
+
+} // namespace
diff --git a/chromium/components/webdata/encryptor/encryptor_win.cc b/chromium/components/webdata/encryptor/encryptor_win.cc
new file mode 100644
index 00000000000..adf792ff326
--- /dev/null
+++ b/chromium/components/webdata/encryptor/encryptor_win.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2006-2008 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/webdata/encryptor/encryptor.h"
+
+#include <windows.h>
+#include <wincrypt.h>
+#include "base/strings/utf_string_conversions.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+bool Encryptor::EncryptString16(const base::string16& plaintext,
+ std::string* ciphertext) {
+ return EncryptString(UTF16ToUTF8(plaintext), ciphertext);
+}
+
+bool Encryptor::DecryptString16(const std::string& ciphertext,
+ base::string16* plaintext) {
+ std::string utf8;
+ if (!DecryptString(ciphertext, &utf8))
+ return false;
+
+ *plaintext = UTF8ToUTF16(utf8);
+ return true;
+}
+
+bool Encryptor::EncryptString(const std::string& plaintext,
+ std::string* ciphertext) {
+ DATA_BLOB input;
+ input.pbData = const_cast<BYTE*>(
+ reinterpret_cast<const BYTE*>(plaintext.data()));
+ input.cbData = static_cast<DWORD>(plaintext.length());
+
+ DATA_BLOB output;
+ BOOL result = CryptProtectData(&input, L"", NULL, NULL, NULL,
+ 0, &output);
+ if (!result)
+ return false;
+
+ // this does a copy
+ ciphertext->assign(reinterpret_cast<std::string::value_type*>(output.pbData),
+ output.cbData);
+
+ LocalFree(output.pbData);
+ return true;
+}
+
+bool Encryptor::DecryptString(const std::string& ciphertext,
+ std::string* plaintext) {
+ DATA_BLOB input;
+ input.pbData = const_cast<BYTE*>(
+ reinterpret_cast<const BYTE*>(ciphertext.data()));
+ input.cbData = static_cast<DWORD>(ciphertext.length());
+
+ DATA_BLOB output;
+ BOOL result = CryptUnprotectData(&input, NULL, NULL, NULL, NULL,
+ 0, &output);
+ if (!result)
+ return false;
+
+ plaintext->assign(reinterpret_cast<char*>(output.pbData), output.cbData);
+ LocalFree(output.pbData);
+ return true;
+}
diff --git a/chromium/components/webdata/encryptor/ie7_password.cc b/chromium/components/webdata/encryptor/ie7_password.cc
new file mode 100644
index 00000000000..e7db3ab3be0
--- /dev/null
+++ b/chromium/components/webdata/encryptor/ie7_password.cc
@@ -0,0 +1,144 @@
+// 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/webdata/encryptor/ie7_password.h"
+
+#include <wincrypt.h>
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/sha1.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+// Structures that IE7/IE8 use to store a username/password.
+// Some of the fields might have been incorrectly reverse engineered.
+struct PreHeader {
+ DWORD pre_header_size; // Size of this header structure. Always 12.
+ DWORD header_size; // Size of the real Header: sizeof(Header) +
+ // item_count * sizeof(Entry);
+ DWORD data_size; // Size of the data referenced by the entries.
+};
+
+struct Header {
+ char wick[4]; // The string "WICK". I don't know what it means.
+ DWORD fixed_header_size; // The size of this structure without the entries:
+ // sizeof(Header).
+ DWORD item_count; // Number of entries. It should always be 2. One for
+ // the username, and one for the password.
+ wchar_t two_letters[2]; // Two unknown bytes.
+ DWORD unknown[2]; // Two unknown DWORDs.
+};
+
+struct Entry {
+ DWORD offset; // Offset where the data referenced by this entry is
+ // located.
+ FILETIME time_stamp; // Timestamp when the password got added.
+ DWORD string_length; // The length of the data string.
+};
+
+// Main data structure.
+struct PasswordEntry {
+ PreHeader pre_header; // Contains the size of the different sections.
+ Header header; // Contains the number of items.
+ Entry entry[1]; // List of entries containing a string. The first one
+ // is the username, the second one if the password.
+};
+
+} // namespace
+
+namespace ie7_password {
+
+bool GetUserPassFromData(const std::vector<unsigned char>& data,
+ std::wstring* username,
+ std::wstring* password) {
+ const PasswordEntry* information =
+ reinterpret_cast<const PasswordEntry*>(&data.front());
+
+ // Some expected values. If it's not what we expect we don't even try to
+ // understand the data.
+ if (information->pre_header.pre_header_size != sizeof(PreHeader))
+ return false;
+
+ if (information->header.item_count != 2) // Username and Password
+ return false;
+
+ if (information->header.fixed_header_size != sizeof(Header))
+ return false;
+
+ const uint8* ptr = &data.front();
+ const uint8* offset_to_data = ptr + information->pre_header.header_size +
+ information->pre_header.pre_header_size;
+
+ const Entry* user_entry = information->entry;
+ const Entry* pass_entry = user_entry+1;
+
+ *username = reinterpret_cast<const wchar_t*>(offset_to_data +
+ user_entry->offset);
+ *password = reinterpret_cast<const wchar_t*>(offset_to_data +
+ pass_entry->offset);
+ return true;
+}
+
+std::wstring GetUrlHash(const std::wstring& url) {
+ std::wstring lower_case_url = StringToLowerASCII(url);
+ // Get a data buffer out of our std::wstring to pass to SHA1HashString.
+ std::string url_buffer(
+ reinterpret_cast<const char*>(lower_case_url.c_str()),
+ (lower_case_url.size() + 1) * sizeof(wchar_t));
+ std::string hash_bin = base::SHA1HashString(url_buffer);
+
+ std::wstring url_hash;
+
+ // Transform the buffer to an hexadecimal string.
+ unsigned char checksum = 0;
+ for (size_t i = 0; i < hash_bin.size(); ++i) {
+ // std::string gives signed chars, which mess with StringPrintf and
+ // check_sum.
+ unsigned char hash_byte = static_cast<unsigned char>(hash_bin[i]);
+ checksum += hash_byte;
+ url_hash += base::StringPrintf(L"%2.2X", static_cast<unsigned>(hash_byte));
+ }
+ url_hash += base::StringPrintf(L"%2.2X", checksum);
+
+ return url_hash;
+}
+
+bool DecryptPassword(const std::wstring& url,
+ const std::vector<unsigned char>& data,
+ std::wstring* username, std::wstring* password) {
+ std::wstring lower_case_url = StringToLowerASCII(url);
+ DATA_BLOB input = {0};
+ DATA_BLOB output = {0};
+ DATA_BLOB url_key = {0};
+
+ input.pbData = const_cast<unsigned char*>(&data.front());
+ input.cbData = static_cast<DWORD>((data.size()) *
+ sizeof(std::string::value_type));
+
+ url_key.pbData = reinterpret_cast<unsigned char*>(
+ const_cast<wchar_t*>(lower_case_url.data()));
+ url_key.cbData = static_cast<DWORD>((lower_case_url.size() + 1) *
+ sizeof(std::wstring::value_type));
+
+ if (CryptUnprotectData(&input, NULL, &url_key, NULL, NULL,
+ CRYPTPROTECT_UI_FORBIDDEN, &output)) {
+ // Now that we have the decrypted information, we need to understand it.
+ std::vector<unsigned char> decrypted_data;
+ decrypted_data.resize(output.cbData);
+ memcpy(&decrypted_data.front(), output.pbData, output.cbData);
+
+ GetUserPassFromData(decrypted_data, username, password);
+
+ LocalFree(output.pbData);
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace ie7_password
diff --git a/chromium/components/webdata/encryptor/ie7_password.h b/chromium/components/webdata/encryptor/ie7_password.h
new file mode 100644
index 00000000000..458426640d8
--- /dev/null
+++ b/chromium/components/webdata/encryptor/ie7_password.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2006-2008 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_WEBDATA_ENCRYPTOR_IE7_PASSWORD_H_
+#define COMPONENTS_WEBDATA_ENCRYPTOR_IE7_PASSWORD_H_
+
+#include <windows.h>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+// Contains the information read from the IE7/IE8 Storage2 key in the registry.
+struct IE7PasswordInfo {
+ // Hash of the url.
+ std::wstring url_hash;
+
+ // Encrypted data containing the username, password and some more undocumented
+ // fields.
+ std::vector<unsigned char> encrypted_data;
+
+ // When the login was imported.
+ base::Time date_created;
+};
+
+namespace ie7_password {
+
+// Parses a data structure to find the password and the username.
+bool GetUserPassFromData(const std::vector<unsigned char>& data,
+ std::wstring* username,
+ std::wstring* password);
+
+// Decrypts the username and password for a given data vector using the url as
+// the key.
+bool DecryptPassword(const std::wstring& url,
+ const std::vector<unsigned char>& data,
+ std::wstring* username, std::wstring* password);
+
+// Returns the hash of a url.
+std::wstring GetUrlHash(const std::wstring& url);
+
+} // namespace ie7_password
+
+#endif // COMPONENTS_WEBDATA_ENCRYPTOR_IE7_PASSWORD_H_
diff --git a/chromium/components/webdata/encryptor/ie7_password_unittest_win.cc b/chromium/components/webdata/encryptor/ie7_password_unittest_win.cc
new file mode 100644
index 00000000000..37021e8b99e
--- /dev/null
+++ b/chromium/components/webdata/encryptor/ie7_password_unittest_win.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 "testing/gtest/include/gtest/gtest.h"
+
+#include <windows.h>
+
+#include <vector>
+
+#include "base/strings/string16.h"
+#include "components/webdata/encryptor/ie7_password.h"
+
+TEST(IE7PasswordTest, GetUserPassword) {
+ // This is the unencrypted values of my keys under Storage2.
+ // The passwords have been manually changed to abcdef... but the size remains
+ // the same.
+ unsigned char data1[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x2c\x00\x00\x00"
+ "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
+ "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x4e\xfa\x67\x76\x22\x94\xc8\x01"
+ "\x08\x00\x00\x00\x12\x00\x00\x00\x4e\xfa\x67\x76"
+ "\x22\x94\xc8\x01\x0c\x00\x00\x00\x61\x00\x62\x00"
+ "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
+ "\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00"
+ "\x66\x00\x67\x00\x68\x00\x69\x00\x6a\x00\x6b\x00"
+ "\x6c\x00\x00\x00";
+
+ unsigned char data2[] = "\x0c\x00\x00\x00\x38\x00\x00\x00\x24\x00\x00\x00"
+ "\x57\x49\x43\x4b\x18\x00\x00\x00\x02\x00\x00\x00"
+ "\x67\x00\x72\x00\x01\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\xa8\xea\xf4\xe5\x9f\x9a\xc8\x01"
+ "\x09\x00\x00\x00\x14\x00\x00\x00\xa8\xea\xf4\xe5"
+ "\x9f\x9a\xc8\x01\x07\x00\x00\x00\x61\x00\x62\x00"
+ "\x63\x00\x64\x00\x65\x00\x66\x00\x67\x00\x68\x00"
+ "\x69\x00\x00\x00\x61\x00\x62\x00\x63\x00\x64\x00"
+ "\x65\x00\x66\x00\x67\x00\x00\x00";
+
+
+
+ std::vector<unsigned char> decrypted_data1;
+ decrypted_data1.resize(arraysize(data1));
+ memcpy(&decrypted_data1.front(), data1, sizeof(data1));
+
+ std::vector<unsigned char> decrypted_data2;
+ decrypted_data2.resize(arraysize(data2));
+ memcpy(&decrypted_data2.front(), data2, sizeof(data2));
+
+ string16 password;
+ string16 username;
+ ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data1, &username,
+ &password));
+ EXPECT_EQ(L"abcdefgh", username);
+ EXPECT_EQ(L"abcdefghijkl", password);
+
+ ASSERT_TRUE(ie7_password::GetUserPassFromData(decrypted_data2, &username,
+ &password));
+ EXPECT_EQ(L"abcdefghi", username);
+ EXPECT_EQ(L"abcdefg", password);
+}