summaryrefslogtreecommitdiff
path: root/chromium/components
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2013-12-11 21:33:03 +0100
committerAndras Becsi <andras.becsi@digia.com>2013-12-13 12:34:07 +0100
commitf2a33ff9cbc6d19943f1c7fbddd1f23d23975577 (patch)
tree0586a32aa390ade8557dfd6b4897f43a07449578 /chromium/components
parent5362912cdb5eea702b68ebe23702468d17c3017a (diff)
downloadqtwebengine-chromium-f2a33ff9cbc6d19943f1c7fbddd1f23d23975577.tar.gz
Update Chromium to branch 1650 (31.0.1650.63)
Change-Id: I57d8c832eaec1eb2364e0a8e7352a6dd354db99f Reviewed-by: Jocelyn Turcotte <jocelyn.turcotte@digia.com>
Diffstat (limited to 'chromium/components')
-rw-r--r--chromium/components/DEPS3
-rw-r--r--chromium/components/OWNERS27
-rw-r--r--chromium/components/autofill.gypi18
-rw-r--r--chromium/components/autofill/content/DEPS6
-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.cc11
-rw-r--r--chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc4
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.cc190
-rw-r--r--chromium/components/autofill/content/browser/risk/fingerprint.h1
-rw-r--r--chromium/components/autofill/content/browser/wallet/mock_wallet_client.h6
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client.cc148
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client.h25
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h3
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc227
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_items.cc49
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_items.h15
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc22
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_service_url.cc16
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc56
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_test_util.cc26
-rw-r--r--chromium/components/autofill/content/browser/wallet/wallet_test_util.h16
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.cc185
-rw-r--r--chromium/components/autofill/content/renderer/autofill_agent.h45
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.cc94
-rw-r--r--chromium/components/autofill/content/renderer/form_autofill_util.h22
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.cc94
-rw-r--r--chromium/components/autofill/content/renderer/password_autofill_agent.h20
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.cc62
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils.h27
-rw-r--r--chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc91
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_manager.cc90
-rw-r--r--chromium/components/autofill/content/renderer/password_generation_manager.h18
-rw-r--r--chromium/components/autofill/content/renderer/test_password_autofill_agent.cc20
-rw-r--r--chromium/components/autofill/content/renderer/test_password_autofill_agent.h27
-rw-r--r--chromium/components/autofill/core/DEPS1
-rw-r--r--chromium/components/autofill/core/browser/DEPS4
-rw-r--r--chromium/components/autofill/core/browser/address.cc8
-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/autocheckout_bubble_state.h22
-rw-r--r--chromium/components/autofill/core/browser/autofill_common_test.cc13
-rw-r--r--chromium/components/autofill/core/browser/autofill_common_test.h6
-rw-r--r--chromium/components/autofill/core/browser/autofill_download_unittest.cc12
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.cc27
-rw-r--r--chromium/components/autofill/core/browser/autofill_external_delegate.h13
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.cc246
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager.h51
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_delegate.h58
-rw-r--r--chromium/components/autofill/core/browser/autofill_manager_unittest.cc283
-rw-r--r--chromium/components/autofill/core/browser/autofill_merge_unittest.cc12
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.cc165
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics.h136
-rw-r--r--chromium/components/autofill/core/browser/autofill_metrics_unittest.cc18
-rw-r--r--chromium/components/autofill/core/browser/autofill_popup_delegate.h19
-rw-r--r--chromium/components/autofill/core/browser/autofill_profile.cc26
-rw-r--r--chromium/components/autofill/core/browser/autofill_type.cc14
-rw-r--r--chromium/components/autofill/core/browser/autofill_xml_parser.cc75
-rw-r--r--chromium/components/autofill/core/browser/autofill_xml_parser.h32
-rw-r--r--chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc186
-rw-r--r--chromium/components/autofill/core/browser/contact_info.cc10
-rw-r--r--chromium/components/autofill/core/browser/credit_card.cc3
-rw-r--r--chromium/components/autofill/core/browser/data_driven_test.cc2
-rw-r--r--chromium/components/autofill/core/browser/field_types.h15
-rw-r--r--chromium/components/autofill/core/browser/form_structure.cc81
-rw-r--r--chromium/components/autofill/core/browser/form_structure.h24
-rw-r--r--chromium/components/autofill/core/browser/form_structure_unittest.cc222
-rw-r--r--chromium/components/autofill/core/browser/password_autofill_manager.cc2
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.cc28
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager.h17
-rw-r--r--chromium/components/autofill/core/browser/personal_data_manager_unittest.cc80
-rw-r--r--chromium/components/autofill/core/browser/phone_number.cc7
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc32
-rw-r--r--chromium/components/autofill/core/browser/test_autofill_manager_delegate.h20
-rw-r--r--chromium/components/autofill/core/browser/test_personal_data_manager.cc9
-rw-r--r--chromium/components/autofill/core/browser/test_personal_data_manager.h11
-rw-r--r--chromium/components/autofill/core/browser/webdata/autofill_table.cc5
-rw-r--r--chromium/components/autofill/core/common/autocheckout_status.h22
-rw-r--r--chromium/components/autofill/core/common/autofill_message_generator.cc6
-rw-r--r--chromium/components/autofill/core/common/autofill_messages.h68
-rw-r--r--chromium/components/autofill/core/common/autofill_param_traits_macros.h46
-rw-r--r--chromium/components/autofill/core/common/autofill_switches.cc11
-rw-r--r--chromium/components/autofill/core/common/autofill_switches.h4
-rw-r--r--chromium/components/autofill/core/common/form_data.cc93
-rw-r--r--chromium/components/autofill/core/common/form_data.h12
-rw-r--r--chromium/components/autofill/core/common/form_data_unittest.cc57
-rw-r--r--chromium/components/autofill/core/common/form_field_data.cc106
-rw-r--r--chromium/components/autofill/core/common/form_field_data.h11
-rw-r--r--chromium/components/autofill/core/common/form_field_data_unittest.cc43
-rw-r--r--chromium/components/autofill/core/common/password_form.cc79
-rw-r--r--chromium/components/autofill/core/common/password_form.h211
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.cc8
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data.h8
-rw-r--r--chromium/components/autofill/core/common/password_form_fill_data_unittest.cc5
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc24
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h27
-rw-r--r--chromium/components/browser_context_keyed_service/browser_context_keyed_base_factory.cc1
-rw-r--r--chromium/components/component_strings.grd13
-rw-r--r--chromium/components/components.gyp5
-rw-r--r--chromium/components/components_tests.gypi33
-rw-r--r--chromium/components/components_unittests.isolate3
-rw-r--r--chromium/components/dom_distiller.gypi77
-rw-r--r--chromium/components/dom_distiller/DEPS11
-rw-r--r--chromium/components/dom_distiller/OWNERS3
-rw-r--r--chromium/components/dom_distiller/README12
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_constants.cc12
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_constants.h15
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_database.cc228
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_database.h144
-rw-r--r--chromium/components/dom_distiller/core/dom_distiller_database_unittest.cc305
-rw-r--r--chromium/components/dom_distiller/core/proto/article_entry.proto26
-rw-r--r--chromium/components/dom_distiller/webui/DEPS13
-rw-r--r--chromium/components/dom_distiller/webui/dom_distiller_handler.cc42
-rw-r--r--chromium/components/dom_distiller/webui/dom_distiller_handler.h38
-rw-r--r--chromium/components/dom_distiller/webui/dom_distiller_ui.cc42
-rw-r--r--chromium/components/dom_distiller/webui/dom_distiller_ui.h24
-rw-r--r--chromium/components/dom_distiller/webui/resources/about_dom_distiller.css8
-rw-r--r--chromium/components/dom_distiller/webui/resources/about_dom_distiller.html27
-rw-r--r--chromium/components/dom_distiller/webui/resources/about_dom_distiller.js32
-rw-r--r--chromium/components/dom_distiller_resources.grd17
-rw-r--r--chromium/components/dom_distiller_strings.grdp8
-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/broker/nacl_broker_listener.cc11
-rw-r--r--chromium/components/nacl/common/nacl_cmd_line.cc1
-rw-r--r--chromium/components/nacl/common/nacl_helper_linux.h9
-rw-r--r--chromium/components/nacl/common/nacl_host_messages.h2
-rw-r--r--chromium/components/nacl/common/nacl_switches.cc3
-rw-r--r--chromium/components/nacl/common/nacl_switches.h1
-rw-r--r--chromium/components/nacl/common/nacl_types.cc10
-rw-r--r--chromium/components/nacl/common/nacl_types.h4
-rw-r--r--chromium/components/nacl/common/pnacl_types.cc3
-rw-r--r--chromium/components/nacl/common/pnacl_types.h1
-rw-r--r--chromium/components/nacl/loader/nacl_main.cc22
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate.h20
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc22
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm58
-rw-r--r--chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc65
-rw-r--r--chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc111
-rw-r--r--chromium/components/nacl/zygote/nacl_fork_delegate_linux.h3
-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/policy.gypi46
-rw-r--r--chromium/components/policy/OWNERS6
-rw-r--r--chromium/components/policy/core/common/policy_pref_names.cc15
-rw-r--r--chromium/components/policy/core/common/policy_pref_names.h21
-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/core/common/schema.cc276
-rw-r--r--chromium/components/policy/core/common/schema.h150
-rw-r--r--chromium/components/policy/core/common/schema_internal.h45
-rw-r--r--chromium/components/policy/core/common/schema_unittest.cc338
-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/serialized_navigation_entry.cc13
-rw-r--r--chromium/components/sessions/serialized_navigation_entry.h2
-rw-r--r--chromium/components/sessions/serialized_navigation_entry_test_helper.cc1
-rw-r--r--chromium/components/sessions/serialized_navigation_entry_unittest.cc9
-rw-r--r--chromium/components/startup_metric_utils.gypi22
-rw-r--r--chromium/components/startup_metric_utils/OWNERS1
-rw-r--r--chromium/components/startup_metric_utils/startup_metric_utils.cc169
-rw-r--r--chromium/components/startup_metric_utils/startup_metric_utils.h70
-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/child_trace_message_filter.cc26
-rw-r--r--chromium/components/tracing/child_trace_message_filter.h3
-rw-r--r--chromium/components/tracing_untrusted.gyp2
-rw-r--r--chromium/components/variations.gypi36
-rw-r--r--chromium/components/variations/OWNERS3
-rw-r--r--chromium/components/variations/entropy_provider.cc119
-rw-r--r--chromium/components/variations/entropy_provider.h94
-rw-r--r--chromium/components/variations/entropy_provider_unittest.cc369
-rw-r--r--chromium/components/variations/metrics_util.cc27
-rw-r--r--chromium/components/variations/metrics_util.h20
-rw-r--r--chromium/components/variations/metrics_util_unittest.cc31
-rw-r--r--chromium/components/variations/proto/study.proto162
-rw-r--r--chromium/components/variations/proto/variations_seed.proto22
-rw-r--r--chromium/components/variations/variations_associated_data.cc236
-rw-r--r--chromium/components/variations/variations_associated_data.h155
-rw-r--r--chromium/components/variations/variations_associated_data_unittest.cc310
-rw-r--r--chromium/components/variations/variations_seed_processor.cc342
-rw-r--r--chromium/components/variations/variations_seed_processor.h96
-rw-r--r--chromium/components/variations/variations_seed_processor_unittest.cc531
-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/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.cc24
-rw-r--r--chromium/components/web_contents_delegate_android/web_contents_delegate_android.h6
-rw-r--r--chromium/components/web_modal/OWNERS2
-rw-r--r--chromium/components/web_modal/native_web_contents_modal_dialog_manager.h5
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_host.h11
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager.cc64
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager.h29
-rw-r--r--chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc334
-rw-r--r--chromium/components/webdata/common/web_database.cc2
-rw-r--r--chromium/components/webdata/common/web_database_migration_unittest.cc45
223 files changed, 10278 insertions, 6133 deletions
diff --git a/chromium/components/DEPS b/chromium/components/DEPS
index d6fbc7c41ed..0ea897e6ff0 100644
--- a/chromium/components/DEPS
+++ b/chromium/components/DEPS
@@ -13,4 +13,7 @@ include_rules = [
# "+content/public/browser" rule.
"-content",
"+content/public/common",
+
+ # Dependencies of variations component.
+ "+third_party/mt19937ar",
]
diff --git a/chromium/components/OWNERS b/chromium/components/OWNERS
index 7884fe2f806..4c134eb1420 100644
--- a/chromium/components/OWNERS
+++ b/chromium/components/OWNERS
@@ -8,8 +8,15 @@ per-file breakpad.gypi=jochen@chromium.org
per-file breakpad.gypi=rsesek@chromium.org
per-file breakpad.gypi=thestig@chromium.org
-per-file tracing*=jbauman@chromium.org
-per-file tracing*=nduca@chromium.org
+per-file dom_distiller*=bengr@chromium.org
+per-file dom_distiller*=cjhopman@chromium.org
+per-file dom_distiller*=nyquist@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
@@ -21,13 +28,29 @@ 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 startup_metric_utils.gypi=jeremy@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 variations.gypi=asvitkine@chromium.org
+per-file variations.gypi=jwd@chromium.org
+per-file variations.gypi=stevet@chromium.org
+
per-file *.isolate=csharp@chromium.org
per-file *.isolate=maruel@chromium.org
diff --git a/chromium/components/autofill.gypi b/chromium/components/autofill.gypi
index 92eccd3a1d6..92df65af23b 100644
--- a/chromium/components/autofill.gypi
+++ b/chromium/components/autofill.gypi
@@ -56,12 +56,12 @@
'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_param_traits_macros.h',
'autofill/core/common/autofill_pref_names.cc',
'autofill/core/common/autofill_pref_names.h',
'autofill/core/common/autofill_switches.cc',
@@ -74,6 +74,8 @@
'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.cc',
+ 'autofill/core/common/password_form.h',
'autofill/core/common/password_form_fill_data.cc',
'autofill/core/common/password_form_fill_data.h',
'autofill/core/common/password_generation_util.cc',
@@ -220,6 +222,7 @@
'dependencies': [
'autofill_core_common',
'autofill_core_browser',
+ '../skia/skia.gyp:skia',
'../testing/gtest.gyp:gtest',
],
'sources': [
@@ -301,17 +304,6 @@
'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',
@@ -369,6 +361,8 @@
'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_form_conversion_utils.cc',
+ 'autofill/content/renderer/password_form_conversion_utils.h',
'autofill/content/renderer/password_generation_manager.cc',
'autofill/content/renderer/password_generation_manager.h',
],
diff --git a/chromium/components/autofill/content/DEPS b/chromium/components/autofill/content/DEPS
index bd7656f8b4e..4b94f27f569 100644
--- a/chromium/components/autofill/content/DEPS
+++ b/chromium/components/autofill/content/DEPS
@@ -4,3 +4,9 @@ include_rules = [
"+third_party/WebKit/public/platform",
"+third_party/WebKit/public/web",
]
+
+specific_include_rules = {
+ '.*_[a-z]*test\.cc': [
+ "+content/public/test",
+ ],
+}
diff --git a/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc b/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc
deleted file mode 100644
index 7d5423adc8d..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index 40c16f5d697..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout/whitelist_manager.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2013 The Chromium 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
deleted file mode 100644
index 304d8bf02b8..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout/whitelist_manager_unittest.cc
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index be8192362e8..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_manager.cc
+++ /dev/null
@@ -1,581 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index b265d8e2ec4..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_manager.h
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2013 The Chromium 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
deleted file mode 100644
index c8e425566ee..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_manager_unittest.cc
+++ /dev/null
@@ -1,949 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index be03d70c592..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_page_meta_data.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index 6839d1b3ec5..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_page_meta_data.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2013 The Chromium 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
deleted file mode 100644
index 0e740608319..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_page_meta_data_unittest.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index e22e9c7b040..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_request_manager.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index 4d20b5f727b..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_request_manager.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2013 The Chromium 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
deleted file mode 100644
index 9df4f4e4a61..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_statistic.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index 74f7df7cd9b..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_statistic.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2013 The Chromium 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
deleted file mode 100644
index d46abccc40f..00000000000
--- a/chromium/components/autofill/content/browser/autocheckout_steps.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2013 The Chromium 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
index d0cb1f62861..c63a072524f 100644
--- a/chromium/components/autofill/content/browser/autofill_driver_impl.cc
+++ b/chromium/components/autofill/content/browser/autofill_driver_impl.cc
@@ -44,11 +44,6 @@ void AutofillDriverImpl::CreateForWebContentsAndDelegate(
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
@@ -188,12 +183,6 @@ bool AutofillDriverImpl::OnMessageReceived(const IPC::Message& message) {
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;
diff --git a/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc b/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc
index 875b783a565..6986f0779b5 100644
--- a/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc
+++ b/chromium/components/autofill/content/browser/autofill_driver_impl_unittest.cc
@@ -176,7 +176,7 @@ TEST_F(AutofillDriverImplTest, FormDataSentToRenderer) {
TEST_F(AutofillDriverImplTest, TypePredictionsNotSentToRendererWhenDisabled) {
FormData form;
test::CreateTestAddressFormData(&form);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
std::vector<FormStructure*> forms(1, &form_structure);
driver_->SendAutofillTypePredictionsToRenderer(forms);
EXPECT_FALSE(GetFieldTypePredictionsAvailable(NULL));
@@ -188,7 +188,7 @@ TEST_F(AutofillDriverImplTest, TypePredictionsSentToRendererWhenEnabled) {
FormData form;
test::CreateTestAddressFormData(&form);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
std::vector<FormStructure*> forms(1, &form_structure);
std::vector<FormDataPredictions> expected_type_predictions;
FormStructure::GetFieldTypePredictions(forms, &expected_type_predictions);
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.cc b/chromium/components/autofill/content/browser/risk/fingerprint.cc
index 635224d1395..1823d8ec29f 100644
--- a/chromium/components/autofill/content/browser/risk/fingerprint.cc
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.cc
@@ -16,11 +16,13 @@
#include "base/callback.h"
#include "base/cpu.h"
#include "base/logging.h"
+#include "base/memory/weak_ptr.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/timer/timer.h"
#include "base/values.h"
#include "components/autofill/content/browser/risk/proto/fingerprint.pb.h"
#include "content/public/browser/browser_thread.h"
@@ -51,6 +53,10 @@ namespace {
const int32 kFingerprinterVersion = 1;
+// Maximum amount of time, in seconds, to wait for loading asynchronous
+// fingerprint data.
+const int kTimeoutSeconds = 4;
+
// Returns the delta between the local timezone and UTC.
base::TimeDelta GetTimezoneOffset() {
const base::Time utc = base::Time::Now();
@@ -68,20 +74,6 @@ std::string GetOperatingSystemVersion() {
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) {
@@ -172,7 +164,8 @@ void AddCpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) {
void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) {
const gpu::GPUInfo& gpu_info =
content::GpuDataManager::GetInstance()->GetGPUInfo();
- DCHECK(gpu_info.finalized);
+ if (!gpu_info.finalized)
+ return;
Fingerprint::MachineCharacteristics::Graphics* graphics =
machine->mutable_graphics_card();
@@ -188,6 +181,74 @@ void AddGpuInfoToFingerprint(Fingerprint::MachineCharacteristics* machine) {
gpu_performance->set_overall_score(gpu_info.performance_stats.overall);
}
+// Waits for geoposition data to be loaded. Lives on the IO thread.
+class GeopositionLoader {
+ public:
+ // |callback_| will be called on the UI thread with the loaded geoposition,
+ // once it is available.
+ GeopositionLoader(
+ const base::TimeDelta& timeout,
+ const base::Callback<void(const content::Geoposition&)>& callback);
+ ~GeopositionLoader() {}
+
+ private:
+ // Methods to communicate with the GeolocationProvider.
+ void OnGotGeoposition(const content::Geoposition& geoposition);
+
+ // The callback that will be called once the geoposition is available.
+ // Will be called on the UI thread.
+ const base::Callback<void(const content::Geoposition&)> callback_;
+
+ // The callback used as an "observer" of the GeolocationProvider.
+ content::GeolocationProvider::LocationUpdateCallback geolocation_callback_;
+
+ // Timer to enforce a maximum timeout before the |callback_| is called, even
+ // if the geoposition has not been loaded.
+ base::OneShotTimer<GeopositionLoader> timeout_timer_;
+};
+
+GeopositionLoader::GeopositionLoader(
+ const base::TimeDelta& timeout,
+ const base::Callback<void(const content::Geoposition&)>& callback)
+ : callback_(callback) {
+ timeout_timer_.Start(FROM_HERE, timeout,
+ base::Bind(&GeopositionLoader::OnGotGeoposition,
+ base::Unretained(this),
+ content::Geoposition()));
+
+ geolocation_callback_ =
+ base::Bind(&GeopositionLoader::OnGotGeoposition, base::Unretained(this));
+ content::GeolocationProvider::GetInstance()->AddLocationUpdateCallback(
+ geolocation_callback_, false);
+}
+
+void GeopositionLoader::OnGotGeoposition(
+ const content::Geoposition& geoposition) {
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
+ base::Bind(callback_, 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);
+
+ delete this;
+}
+
+// Asynchronously loads the user's current geoposition and calls |callback_| on
+// the UI thread with the loaded geoposition, once it is available. Expected to
+// be called on the IO thread.
+void LoadGeoposition(
+ const base::TimeDelta& timeout,
+ const base::Callback<void(const content::Geoposition&)>& callback) {
+ // The loader is responsible for freeing its own memory.
+ new GeopositionLoader(timeout, callback);
+}
+
// Waits for all asynchronous data required for the fingerprint to be loaded,
// then fills out the fingerprint.
class FingerprintDataLoader : public content::GpuDataManagerObserver {
@@ -201,8 +262,8 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver {
const std::string& charset,
const std::string& accept_languages,
const base::Time& install_time,
- DialogType dialog_type,
const std::string& app_locale,
+ const base::TimeDelta& timeout,
const base::Callback<void(scoped_ptr<Fingerprint>)>& callback);
private:
@@ -216,11 +277,6 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver {
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();
@@ -232,14 +288,10 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver {
// 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
+ // Ensures that any observer registrations 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_;
@@ -250,7 +302,6 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver {
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_;
@@ -258,6 +309,14 @@ class FingerprintDataLoader : public content::GpuDataManagerObserver {
bool waiting_on_plugins_;
content::Geoposition geoposition_;
+ // Timer to enforce a maximum timeout before the |callback_| is called, even
+ // if not all asynchronous data has been loaded.
+ base::OneShotTimer<FingerprintDataLoader> timeout_timer_;
+
+ // For invalidating asynchronous callbacks that might arrive after |this|
+ // instance is destroyed.
+ base::WeakPtrFactory<FingerprintDataLoader> weak_ptr_factory_;
+
// The current application locale.
std::string app_locale_;
@@ -276,8 +335,8 @@ FingerprintDataLoader::FingerprintDataLoader(
const std::string& charset,
const std::string& accept_languages,
const base::Time& install_time,
- DialogType dialog_type,
const std::string& app_locale,
+ const base::TimeDelta& timeout,
const base::Callback<void(scoped_ptr<Fingerprint>)>& callback)
: gpu_data_manager_(content::GpuDataManager::GetInstance()),
gpu_observer_(this),
@@ -289,11 +348,15 @@ FingerprintDataLoader::FingerprintDataLoader(
charset_(charset),
accept_languages_(accept_languages),
install_time_(install_time),
- dialog_type_(dialog_type),
waiting_on_plugins_(true),
+ weak_ptr_factory_(this),
callback_(callback) {
DCHECK(!install_time_.is_null());
+ timeout_timer_.Start(FROM_HERE, timeout,
+ base::Bind(&FingerprintDataLoader::MaybeFillFingerprint,
+ weak_ptr_factory_.GetWeakPtr()));
+
// Load GPU data if needed.
if (!gpu_data_manager_->IsCompleteGpuInfoAvailable()) {
gpu_observer_.Add(gpu_data_manager_);
@@ -303,20 +366,24 @@ FingerprintDataLoader::FingerprintDataLoader(
#if defined(ENABLE_PLUGINS)
// Load plugin data.
content::PluginService::GetInstance()->GetPlugins(
- base::Bind(&FingerprintDataLoader::OnGotPlugins, base::Unretained(this)));
+ base::Bind(&FingerprintDataLoader::OnGotPlugins,
+ weak_ptr_factory_.GetWeakPtr()));
#else
waiting_on_plugins_ = false;
#endif
// Load font data.
content::GetFontListAsync(
- base::Bind(&FingerprintDataLoader::OnGotFonts, base::Unretained(this)));
+ base::Bind(&FingerprintDataLoader::OnGotFonts,
+ weak_ptr_factory_.GetWeakPtr()));
// Load geolocation data.
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
- base::Bind(&FingerprintDataLoader::LoadGeoposition,
- base::Unretained(this)));
+ base::Bind(&LoadGeoposition,
+ timeout,
+ base::Bind(&FingerprintDataLoader::OnGotGeoposition,
+ weak_ptr_factory_.GetWeakPtr())));
}
void FingerprintDataLoader::OnGpuInfoUpdate() {
@@ -352,38 +419,15 @@ void FingerprintDataLoader::OnGotGeoposition(
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)) {
+ // If all of the data has been loaded, or if the |timeout_timer_| has expired,
+ // fill the fingerprint and clean up.
+ if (!timeout_timer_.IsRunning() ||
+ (gpu_data_manager_->IsCompleteGpuInfoAvailable() &&
+ fonts_ &&
+ !waiting_on_plugins_ &&
+ (geoposition_.Validate() ||
+ geoposition_.error_code != content::Geoposition::ERROR_CODE_NONE))) {
FillFingerprint();
delete this;
}
@@ -404,8 +448,10 @@ void FingerprintDataLoader::FillFingerprint() {
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);
+ machine->set_browser_feature(
+ Fingerprint::MachineCharacteristics::FEATURE_REQUEST_AUTOCOMPLETE);
+ if (fonts_)
+ AddFontsToFingerprint(*fonts_, machine);
AddPluginsToFingerprint(plugins_, machine);
AddAcceptLanguagesToFingerprint(accept_languages_, machine);
AddScreenInfoToFingerprint(screen_info_, machine);
@@ -430,7 +476,8 @@ void FingerprintDataLoader::FillFingerprint() {
// available to JS.
// TODO(isherman): Record more user behavior data.
- if (geoposition_.error_code == content::Geoposition::ERROR_CODE_NONE) {
+ if (geoposition_.Validate() &&
+ geoposition_.error_code == content::Geoposition::ERROR_CODE_NONE) {
Fingerprint::UserCharacteristics::Location* location =
fingerprint->mutable_user_characteristics()->mutable_location();
location->set_altitude(geoposition_.altitude);
@@ -463,14 +510,14 @@ void GetFingerprintInternal(
const std::string& charset,
const std::string& accept_languages,
const base::Time& install_time,
- DialogType dialog_type,
const std::string& app_locale,
+ const base::TimeDelta& timeout,
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);
+ install_time, app_locale, timeout, callback);
}
} // namespace internal
@@ -483,7 +530,6 @@ void GetFingerprint(
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;
@@ -497,8 +543,8 @@ void GetFingerprint(
internal::GetFingerprintInternal(
obfuscated_gaia_id, window_bounds, content_bounds, screen_info, version,
- charset, accept_languages, install_time, dialog_type, app_locale,
- callback);
+ charset, accept_languages, install_time, app_locale,
+ base::TimeDelta::FromSeconds(kTimeoutSeconds), callback);
}
} // namespace risk
diff --git a/chromium/components/autofill/content/browser/risk/fingerprint.h b/chromium/components/autofill/content/browser/risk/fingerprint.h
index d66bde43b86..283e60777ff 100644
--- a/chromium/components/autofill/content/browser/risk/fingerprint.h
+++ b/chromium/components/autofill/content/browser/risk/fingerprint.h
@@ -58,7 +58,6 @@ void GetFingerprint(
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);
diff --git a/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h b/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h
index c8d03b33a46..a2f1fea7f5d 100644
--- a/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h
+++ b/chromium/components/autofill/content/browser/wallet/mock_wallet_client.h
@@ -51,12 +51,6 @@ class MockWalletClient : public WalletClient {
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);
};
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client.cc b/chromium/components/autofill/content/browser/wallet/wallet_client.cc
index bb5c525273d..04addee47dc 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_client.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client.cc
@@ -52,40 +52,6 @@ const size_t kMaxBits = 56;
// 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) {
@@ -122,9 +88,9 @@ WalletClient::ErrorType StringToErrorType(const std::string& error_type) {
// Get the more specific WalletClient::ErrorType when the error is
// |BUYER_ACCOUNT_ERROR|.
WalletClient::ErrorType BuyerErrorStringToErrorType(
- const std::string& buyer_error_type) {
+ const std::string& message_type_for_buyer) {
std::string trimmed;
- TrimWhitespaceASCII(buyer_error_type,
+ TrimWhitespaceASCII(message_type_for_buyer,
TRIM_ALL,
&trimmed);
if (LowerCaseEqualsASCII(trimmed, "bla_country_not_supported"))
@@ -245,31 +211,26 @@ AutofillMetrics::WalletRequiredActionMetric RequiredActionToUmaMetric(
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 kMessageTypeForBuyerKey[] = "wallet_error.message_type_for_buyer";
+const char kNewWalletUser[] = "new_wallet_user";
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";
@@ -281,12 +242,14 @@ WalletClient::FullWalletRequest::FullWalletRequest(
const std::string& address_id,
const GURL& source_url,
const std::string& google_transaction_id,
- const std::vector<RiskCapability> risk_capabilities)
+ const std::vector<RiskCapability> risk_capabilities,
+ bool new_wallet_user)
: instrument_id(instrument_id),
address_id(address_id),
source_url(source_url),
google_transaction_id(google_transaction_id),
- risk_capabilities(risk_capabilities) {}
+ risk_capabilities(risk_capabilities),
+ new_wallet_user(new_wallet_user) {}
WalletClient::FullWalletRequest::~FullWalletRequest() {}
@@ -368,6 +331,7 @@ void WalletClient::GetFullWallet(const FullWalletRequest& full_wallet_request) {
request_dict.SetString(kRiskParamsKey, delegate_->GetRiskData());
request_dict.SetBoolean(kUseMinimalAddresses, false);
request_dict.SetBoolean(kPhoneNumberRequired, true);
+ request_dict.SetBoolean(kNewWalletUser, full_wallet_request.new_wallet_user);
request_dict.SetString(kSelectedInstrumentIdKey,
full_wallet_request.instrument_id);
@@ -377,8 +341,7 @@ void WalletClient::GetFullWallet(const FullWalletRequest& full_wallet_request) {
full_wallet_request.source_url.GetWithEmptyPath().spec());
request_dict.SetString(kGoogleTransactionIdKey,
full_wallet_request.google_transaction_id);
- request_dict.SetString(kFeatureKey,
- DialogTypeToFeatureString(delegate_->GetDialogType()));
+ request_dict.SetString(kFeatureKey, "REQUEST_AUTOCOMPLETE");
scoped_ptr<base::ListValue> risk_capabilities_list(new base::ListValue());
for (std::vector<RiskCapability>::const_iterator it =
@@ -528,52 +491,6 @@ void WalletClient::GetWalletItems(const GURL& source_url) {
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_;
}
@@ -639,10 +556,8 @@ void WalletClient::MakeWalletRequest(const GURL& url,
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);
}
@@ -675,6 +590,8 @@ void WalletClient::OnURLFetchComplete(
scoped_ptr<base::DictionaryValue> response_dict;
int response_code = source->GetResponseCode();
+ delegate_->GetMetricLogger().LogWalletResponseCode(response_code);
+
switch (response_code) {
// HTTP_BAD_REQUEST means the arguments are invalid. No point retrying.
case net::HTTP_BAD_REQUEST: {
@@ -703,12 +620,14 @@ void WalletClient::OnURLFetchComplete(
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);
+ // If the error_type is |BUYER_ACCOUNT_ERROR|, then
+ // message_type_for_buyer field contains more specific information
+ // about the error.
+ std::string message_type_for_buyer_string;
+ if (response_dict->GetString(kMessageTypeForBuyerKey,
+ &message_type_for_buyer_string)) {
+ error_type = BuyerErrorStringToErrorType(
+ message_type_for_buyer_string);
}
}
@@ -728,9 +647,8 @@ void WalletClient::OnURLFetchComplete(
RequestType type = request_type_;
request_type_ = NO_PENDING_REQUEST;
- if (!(type == ACCEPT_LEGAL_DOCUMENTS || type == SEND_STATUS) &&
- !response_dict) {
- HandleMalformedResponse(scoped_request.get());
+ if (type != ACCEPT_LEGAL_DOCUMENTS && !response_dict) {
+ HandleMalformedResponse(type, scoped_request.get());
return;
}
@@ -749,14 +667,11 @@ void WalletClient::OnURLFetchComplete(
delegate_->OnDidAuthenticateInstrument(
LowerCaseEqualsASCII(trimmed, "success"));
} else {
- HandleMalformedResponse(scoped_request.get());
+ HandleMalformedResponse(type, scoped_request.get());
}
break;
}
- case SEND_STATUS:
- break;
-
case GET_FULL_WALLET: {
scoped_ptr<FullWallet> full_wallet(
FullWallet::CreateFullWallet(*response_dict));
@@ -765,7 +680,7 @@ void WalletClient::OnURLFetchComplete(
LogRequiredActions(full_wallet->required_actions());
delegate_->OnDidGetFullWallet(full_wallet.Pass());
} else {
- HandleMalformedResponse(scoped_request.get());
+ HandleMalformedResponse(type, scoped_request.get());
}
break;
}
@@ -777,7 +692,7 @@ void WalletClient::OnURLFetchComplete(
LogRequiredActions(wallet_items->required_actions());
delegate_->OnDidGetWalletItems(wallet_items.Pass());
} else {
- HandleMalformedResponse(scoped_request.get());
+ HandleMalformedResponse(type, scoped_request.get());
}
break;
}
@@ -794,7 +709,7 @@ void WalletClient::OnURLFetchComplete(
GetFormFieldErrors(*response_dict, &form_errors);
if (instrument_id.empty() && shipping_address_id.empty() &&
required_actions.empty()) {
- HandleMalformedResponse(scoped_request.get());
+ HandleMalformedResponse(type, scoped_request.get());
} else {
LogRequiredActions(required_actions);
delegate_->OnDidSaveToWallet(instrument_id,
@@ -819,9 +734,13 @@ void WalletClient::StartNextPendingRequest() {
next_request.Run();
}
-void WalletClient::HandleMalformedResponse(net::URLFetcher* request) {
+void WalletClient::HandleMalformedResponse(RequestType request_type,
+ net::URLFetcher* request) {
// Called to inform exponential backoff logic of the error.
request->ReceivedContentWasMalformed();
+ // Record failed API call in metrics.
+ delegate_->GetMetricLogger().LogWalletMalformedResponseMetric(
+ RequestTypeToUmaMetric(request_type));
HandleWalletError(MALFORMED_RESPONSE);
}
@@ -870,7 +789,7 @@ void WalletClient::HandleWalletError(WalletClient::ErrorType error_type) {
delegate_->OnWalletError(error_type);
delegate_->GetMetricLogger().LogWalletErrorMetric(
- delegate_->GetDialogType(), ErrorTypeToUmaMetric(error_type));
+ ErrorTypeToUmaMetric(error_type));
}
// Logs an UMA metric for each of the |required_actions|.
@@ -878,7 +797,6 @@ 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]));
}
}
@@ -896,8 +814,6 @@ AutofillMetrics::WalletApiCallMetric WalletClient::RequestTypeToUmaMetric(
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;
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client.h b/chromium/components/autofill/content/browser/wallet/wallet_client.h
index d4f96073127..89827a8611d 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_client.h
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client.h
@@ -14,12 +14,10 @@
#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"
@@ -56,8 +54,6 @@ class WalletClientDelegate;
// 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.
@@ -110,7 +106,8 @@ class WalletClient : public net::URLFetcherDelegate {
const std::string& address_id,
const GURL& source_url,
const std::string& google_transaction_id,
- const std::vector<RiskCapability> risk_capabilities);
+ const std::vector<RiskCapability> risk_capabilities,
+ bool new_wallet_user);
~FullWalletRequest();
// The ID of the backing instrument. Should have been selected by the user
@@ -130,6 +127,9 @@ class WalletClient : public net::URLFetcherDelegate {
// The Risk challenges supported by the user of WalletClient
std::vector<RiskCapability> risk_capabilities;
+ // True if the user does not have Wallet profile.
+ bool new_wallet_user;
+
private:
DISALLOW_ASSIGN(FullWalletRequest);
};
@@ -174,17 +174,6 @@ class WalletClient : public net::URLFetcherDelegate {
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).
@@ -201,7 +190,6 @@ class WalletClient : public net::URLFetcherDelegate {
GET_FULL_WALLET,
GET_WALLET_ITEMS,
SAVE_TO_WALLET,
- SEND_STATUS,
};
// Like AcceptLegalDocuments, but takes a vector of document ids.
@@ -217,7 +205,8 @@ class WalletClient : public net::URLFetcherDelegate {
const std::string& mime_type);
// Performs bookkeeping tasks for any invalid requests.
- void HandleMalformedResponse(net::URLFetcher* request);
+ void HandleMalformedResponse(RequestType request_type,
+ net::URLFetcher* request);
void HandleNetworkError(int response_code);
void HandleWalletError(ErrorType error_type);
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h b/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h
index 8de95046e5f..8380fbf27b8 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client_delegate.h
@@ -33,9 +33,6 @@ class WalletClientDelegate {
// 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;
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc
index 031e5cd958b..32d598ea6d9 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_client_unittest.cc
@@ -13,7 +13,6 @@
#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"
@@ -21,7 +20,6 @@
#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"
@@ -319,6 +317,23 @@ const char kGetFullWalletValidRequest[] =
"\"feature\":\"REQUEST_AUTOCOMPLETE\","
"\"google_transaction_id\":\"google_transaction_id\","
"\"merchant_domain\":\"https://example.com/\","
+ "\"new_wallet_user\":false,"
+ "\"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 kGetFullWalletValidRequestNewUser[] =
+ "{"
+ "\"feature\":\"REQUEST_AUTOCOMPLETE\","
+ "\"google_transaction_id\":\"google_transaction_id\","
+ "\"merchant_domain\":\"https://example.com/\","
+ "\"new_wallet_user\":true,"
"\"phone_number_required\":true,"
"\"risk_params\":\"risky business\","
"\"selected_address_id\":\"shipping_address_id\","
@@ -334,6 +349,7 @@ const char kGetFullWalletWithRiskCapabilitesValidRequest[] =
"\"feature\":\"REQUEST_AUTOCOMPLETE\","
"\"google_transaction_id\":\"google_transaction_id\","
"\"merchant_domain\":\"https://example.com/\","
+ "\"new_wallet_user\":false,"
"\"phone_number_required\":true,"
"\"risk_params\":\"risky business\","
"\"selected_address_id\":\"shipping_address_id\","
@@ -469,30 +485,6 @@ const char kSaveInstrumentAndAddressValidRequest[] =
"\"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/\","
@@ -589,11 +581,11 @@ class MockAutofillMetrics : public AutofillMetrics {
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));
+ MOCK_CONST_METHOD1(LogWalletErrorMetric, void(WalletErrorMetric metric));
+ MOCK_CONST_METHOD1(LogWalletRequiredActionMetric,
+ void(WalletRequiredActionMetric action));
+ MOCK_CONST_METHOD1(LogWalletMalformedResponseMetric,
+ void(WalletApiCallMetric metric));
private:
DISALLOW_COPY_AND_ASSIGN(MockAutofillMetrics);
};
@@ -610,10 +602,6 @@ class MockWalletClientDelegate : public WalletClientDelegate {
return metric_logger_;
}
- virtual DialogType GetDialogType() const OVERRIDE {
- return DIALOG_TYPE_REQUEST_AUTOCOMPLETE;
- }
-
virtual std::string GetRiskData() const OVERRIDE {
return "risky business";
}
@@ -637,26 +625,26 @@ class MockWalletClientDelegate : public WalletClientDelegate {
LogWalletApiCallDuration(metric, testing::_)).Times(times);
}
+ void ExpectLogWalletMalformedResponse(
+ AutofillMetrics::WalletApiCallMetric metric) {
+ EXPECT_CALL(metric_logger_,
+ LogWalletMalformedResponseMetric(metric)).Times(1);
+ }
+
void ExpectWalletErrorMetric(AutofillMetrics::WalletErrorMetric metric) {
- EXPECT_CALL(
- metric_logger_,
- LogWalletErrorMetric(
- DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric)).Times(1);
+ EXPECT_CALL(metric_logger_, LogWalletErrorMetric(metric)).Times(1);
}
void ExpectWalletRequiredActionMetric(
AutofillMetrics::WalletRequiredActionMetric metric) {
- EXPECT_CALL(
- metric_logger_,
- LogWalletRequiredActionMetric(
- DIALOG_TYPE_REQUEST_AUTOCOMPLETE, metric)).Times(1);
+ EXPECT_CALL(metric_logger_,
+ LogWalletRequiredActionMetric(metric)).Times(1);
}
void ExpectBaselineMetrics() {
EXPECT_CALL(
metric_logger_,
LogWalletErrorMetric(
- DIALOG_TYPE_REQUEST_AUTOCOMPLETE,
AutofillMetrics::WALLET_ERROR_BASELINE_ISSUED_REQUEST))
.Times(1);
ExpectWalletRequiredActionMetric(
@@ -797,7 +785,7 @@ class WalletClientTest : public testing::Test {
void TestWalletErrorCode(
const std::string& error_type_string,
- const std::string& buyer_error_type_string,
+ const std::string& message_type_for_buyer_string,
WalletClient::ErrorType expected_error_type,
AutofillMetrics::WalletErrorMetric expected_autofill_metric) {
static const char kResponseTemplate[] =
@@ -825,25 +813,22 @@ class WalletClientTest : public testing::Test {
" }"
"}";
EXPECT_CALL(delegate_, OnWalletError(expected_error_type)).Times(1);
- delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::GET_WALLET_ITEMS, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(expected_autofill_metric);
- std::vector<AutocheckoutStatistic> statistics;
- wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
- GURL(kMerchantUrl),
- statistics,
- "google_transaction_id");
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
std::string buyer_error;
- if (!buyer_error_type_string.empty()) {
- buyer_error = base::StringPrintf("\"buyer_error_type\":\"%s\",",
- buyer_error_type_string.c_str());
+ if (!message_type_for_buyer_string.empty()) {
+ buyer_error = base::StringPrintf("\"message_type_for_buyer\":\"%s\",",
+ message_type_for_buyer_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,
+ kGetWalletItemsValidRequest,
response);
}
@@ -875,11 +860,11 @@ class WalletClientTest : public testing::Test {
TEST_F(WalletClientTest, WalletErrorCodes) {
struct {
std::string error_type_string;
- std::string buyer_error_type_string;
+ std::string message_type_for_buyer_string;
WalletClient::ErrorType expected_error_type;
AutofillMetrics::WalletErrorMetric expected_autofill_metric;
} test_cases[] = {
- // General |BUYER_ACCOUNT_ERROR| with no |buyer_error_type_string|.
+ // General |BUYER_ACCOUNT_ERROR| with no |message_type_for_buyer_string|.
{
"buyer_account_error",
"",
@@ -887,21 +872,22 @@ TEST_F(WalletClientTest, WalletErrorCodes) {
AutofillMetrics::WALLET_BUYER_ACCOUNT_ERROR
},
// |BUYER_ACCOUNT_ERROR| with "buyer_legal_address_not_supported" in
- // buyer_error_type field.
+ // message_type_for_buyer 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| with KYC error code in message_type_for_buyer
+ // 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| with un-recognizable |message_type_for_buyer|.
{
"buyer_account_error",
"random_string",
@@ -956,11 +942,12 @@ TEST_F(WalletClientTest, WalletErrorCodes) {
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()));
+ base::StringPrintf(
+ "%s - %s",
+ test_cases[i].error_type_string.c_str(),
+ test_cases[i].message_type_for_buyer_string.c_str()));
TestWalletErrorCode(test_cases[i].error_type_string,
- test_cases[i].buyer_error_type_string,
+ test_cases[i].message_type_for_buyer_string,
test_cases[i].expected_error_type,
test_cases[i].expected_autofill_metric);
}
@@ -969,36 +956,17 @@ TEST_F(WalletClientTest, WalletErrorCodes) {
TEST_F(WalletClientTest, WalletErrorResponseMissing) {
EXPECT_CALL(delegate_, OnWalletError(
WalletClient::UNKNOWN_ERROR)).Times(1);
- delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::GET_WALLET_ITEMS, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_UNKNOWN_ERROR);
- std::vector<AutocheckoutStatistic> statistics;
- wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
- GURL(kMerchantUrl),
- statistics,
- "google_transaction_id");
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
VerifyAndFinishRequest(net::HTTP_INTERNAL_SERVER_ERROR,
- kSendAutocheckoutStatusOfSuccessValidRequest,
+ kGetWalletItemsValidRequest,
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,
@@ -1014,17 +982,14 @@ TEST_F(WalletClientTest, NetworkFailureOnExpectedResponse) {
TEST_F(WalletClientTest, RequestError) {
EXPECT_CALL(delegate_, OnWalletError(WalletClient::BAD_REQUEST)).Times(1);
- delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SEND_STATUS, 1);
+ delegate_.ExpectLogWalletApiCallDuration(
+ AutofillMetrics::GET_WALLET_ITEMS, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_BAD_REQUEST);
- std::vector<AutocheckoutStatistic> statistics;
- wallet_client_->SendAutocheckoutStatus(autofill::SUCCESS,
- GURL(kMerchantUrl),
- statistics,
- "google_transaction_id");
+ wallet_client_->GetWalletItems(GURL(kMerchantUrl));
VerifyAndFinishRequest(net::HTTP_BAD_REQUEST,
- kSendAutocheckoutStatusOfSuccessValidRequest,
+ kGetWalletItemsValidRequest,
std::string());
}
@@ -1037,7 +1002,8 @@ TEST_F(WalletClientTest, GetFullWalletSuccess) {
"shipping_address_id",
GURL(kMerchantUrl),
"google_transaction_id",
- std::vector<WalletClient::RiskCapability>());
+ std::vector<WalletClient::RiskCapability>(),
+ false);
wallet_client_->GetFullWallet(full_wallet_request);
VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
@@ -1047,6 +1013,26 @@ TEST_F(WalletClientTest, GetFullWalletSuccess) {
EXPECT_EQ(1U, delegate_.full_wallets_received());
}
+TEST_F(WalletClientTest, GetFullWalletSuccessNewuser) {
+ 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>(),
+ true);
+ wallet_client_->GetFullWallet(full_wallet_request);
+
+ VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
+ kGetFullWalletValidRequestNewUser,
+ kGetFullWalletValidResponse,
+ 3U);
+ EXPECT_EQ(1U, delegate_.full_wallets_received());
+}
+
TEST_F(WalletClientTest, GetFullWalletWithRiskCapabilitesSuccess) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_FULL_WALLET, 1);
delegate_.ExpectBaselineMetrics();
@@ -1058,7 +1044,8 @@ TEST_F(WalletClientTest, GetFullWalletWithRiskCapabilitesSuccess) {
"shipping_address_id",
GURL(kMerchantUrl),
"google_transaction_id",
- risk_capabilities);
+ risk_capabilities,
+ false);
wallet_client_->GetFullWallet(full_wallet_request);
VerifyAndFinishFormEncodedRequest(
@@ -1076,13 +1063,15 @@ TEST_F(WalletClientTest, GetFullWalletMalformedResponse) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::GET_FULL_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::GET_FULL_WALLET);
WalletClient::FullWalletRequest full_wallet_request(
"instrument_id",
"shipping_address_id",
GURL(kMerchantUrl),
"google_transaction_id",
- std::vector<WalletClient::RiskCapability>());
+ std::vector<WalletClient::RiskCapability>(),
+ false);
wallet_client_->GetFullWallet(full_wallet_request);
VerifyAndFinishFormEncodedRequest(net::HTTP_OK,
@@ -1157,6 +1146,8 @@ TEST_F(WalletClientTest, AuthenticateInstrumentFailedMalformedResponse) {
1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(
+ AutofillMetrics::AUTHENTICATE_INSTRUMENT);
wallet_client_->AuthenticateInstrument("instrument_id", "123");
@@ -1250,6 +1241,7 @@ TEST_F(WalletClientTest, SaveAddressFailedInvalidRequiredAction) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
scoped_ptr<Address> address = GetTestSaveableAddress();
wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
@@ -1266,6 +1258,7 @@ TEST_F(WalletClientTest, SaveAddressFailedMalformedResponse) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
scoped_ptr<Address> address = GetTestSaveableAddress();
wallet_client_->SaveToWallet(scoped_ptr<Instrument>(),
@@ -1334,6 +1327,7 @@ TEST_F(WalletClientTest, SaveInstrumentFailedInvalidRequiredActions) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
EXPECT_CALL(delegate_,
OnWalletError(WalletClient::MALFORMED_RESPONSE));
@@ -1355,6 +1349,7 @@ TEST_F(WalletClientTest, SaveInstrumentFailedMalformedResponse) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
scoped_ptr<Instrument> instrument = GetTestInstrument();
wallet_client_->SaveToWallet(instrument.Pass(),
@@ -1435,6 +1430,7 @@ TEST_F(WalletClientTest, SaveInstrumentAndAddressFailedInvalidRequiredAction) {
1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
scoped_ptr<Instrument> instrument = GetTestInstrument();
scoped_ptr<Address> address = GetTestSaveableAddress();
@@ -1506,6 +1502,7 @@ TEST_F(WalletClientTest, UpdateAddressFailedInvalidRequiredAction) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
scoped_ptr<Address> address = GetTestShippingAddress();
address->set_object_id("shipping_address_id");
@@ -1524,6 +1521,7 @@ TEST_F(WalletClientTest, UpdateAddressMalformedResponse) {
delegate_.ExpectLogWalletApiCallDuration(AutofillMetrics::SAVE_TO_WALLET, 1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
scoped_ptr<Address> address = GetTestShippingAddress();
address->set_object_id("shipping_address_id");
@@ -1635,6 +1633,7 @@ TEST_F(WalletClientTest, UpdateInstrumentFailedInvalidRequiredAction) {
1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
wallet_client_->SaveToWallet(GetTestAddressUpgradeInstrument(),
scoped_ptr<Address>(),
@@ -1652,6 +1651,7 @@ TEST_F(WalletClientTest, UpdateInstrumentMalformedResponse) {
1);
delegate_.ExpectBaselineMetrics();
delegate_.ExpectWalletErrorMetric(AutofillMetrics::WALLET_MALFORMED_RESPONSE);
+ delegate_.ExpectLogWalletMalformedResponse(AutofillMetrics::SAVE_TO_WALLET);
wallet_client_->SaveToWallet(GetTestAddressUpgradeInstrument(),
scoped_ptr<Address>(),
@@ -1662,39 +1662,6 @@ TEST_F(WalletClientTest, UpdateInstrumentMalformedResponse) {
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,
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_items.cc b/chromium/components/autofill/content/browser/wallet/wallet_items.cc
index 03cf3f442c2..0ef44d2f0b8 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_items.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_items.cc
@@ -80,6 +80,21 @@ WalletItems::MaskedInstrument::Status
return WalletItems::MaskedInstrument::INAPPLICABLE;
}
+base::string16 DisplayStringFromType(WalletItems::MaskedInstrument::Type type) {
+ switch (type) {
+ case WalletItems::MaskedInstrument::AMEX:
+ return CreditCard::TypeForDisplay(kAmericanExpressCard);
+ case WalletItems::MaskedInstrument::DISCOVER:
+ return CreditCard::TypeForDisplay(kDiscoverCard);
+ case WalletItems::MaskedInstrument::MASTER_CARD:
+ return CreditCard::TypeForDisplay(kMasterCard);
+ case WalletItems::MaskedInstrument::VISA:
+ return CreditCard::TypeForDisplay(kVisaCard);
+ default:
+ return CreditCard::TypeForDisplay(kGenericCard);
+ }
+}
+
} // anonymous namespace
WalletItems::MaskedInstrument::MaskedInstrument(
@@ -261,21 +276,9 @@ base::string16 WalletItems::MaskedInstrument::DisplayNameDetail() const {
}
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();
+ return DisplayStringFromType(type_) + ASCIIToUTF16(" - ") +
+ last_four_digits();
}
const gfx::Image& WalletItems::MaskedInstrument::CardIcon() const {
@@ -327,6 +330,9 @@ base::string16 WalletItems::MaskedInstrument::GetInfo(
case CREDIT_CARD_VERIFICATION_CODE:
break;
+ case CREDIT_CARD_TYPE:
+ return DisplayStringFromType(type_);
+
default:
NOTREACHED();
}
@@ -386,12 +392,14 @@ 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)
+ const std::string& obfuscated_gaia_id,
+ AmexPermission amex_permission)
: 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) {}
+ obfuscated_gaia_id_(obfuscated_gaia_id),
+ amex_permission_(amex_permission) {}
WalletItems::~WalletItems() {}
@@ -435,11 +443,18 @@ scoped_ptr<WalletItems>
if (!dictionary.GetString("obfuscated_gaia_id", &obfuscated_gaia_id))
DVLOG(1) << "Response from Google wallet missing obfuscated gaia id";
+ bool amex_disallowed = true;
+ if (!dictionary.GetBoolean("amex_disallowed", &amex_disallowed))
+ DVLOG(1) << "Response from Google wallet missing the amex_disallowed field";
+ AmexPermission amex_permission =
+ amex_disallowed ? AMEX_DISALLOWED : AMEX_ALLOWED;
+
scoped_ptr<WalletItems> wallet_items(new WalletItems(required_action,
google_transaction_id,
default_instrument_id,
default_address_id,
- obfuscated_gaia_id));
+ obfuscated_gaia_id,
+ amex_permission));
const ListValue* legal_docs;
if (dictionary.GetList("required_legal_document", &legal_docs)) {
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_items.h b/chromium/components/autofill/content/browser/wallet/wallet_items.h
index 53a49a17d05..9838ec83dfd 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_items.h
+++ b/chromium/components/autofill/content/browser/wallet/wallet_items.h
@@ -38,6 +38,11 @@ namespace wallet {
class WalletItemsTest;
+enum AmexPermission {
+ AMEX_ALLOWED,
+ AMEX_DISALLOWED,
+};
+
// 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.
@@ -252,10 +257,12 @@ class WalletItems {
const std::vector<LegalDocument*>& legal_documents() const {
return legal_documents_.get();
}
+ bool is_amex_allowed() const { return amex_permission_ == AMEX_ALLOWED; }
private:
friend class WalletItemsTest;
- friend scoped_ptr<WalletItems> GetTestWalletItems();
+ friend scoped_ptr<WalletItems> GetTestWalletItemsWithDefaultIds(
+ const std::string&, const std::string&, AmexPermission);
FRIEND_TEST_ALL_PREFIXES(WalletItemsTest, CreateWalletItems);
FRIEND_TEST_ALL_PREFIXES(WalletItemsTest,
CreateWalletItemsWithRequiredActions);
@@ -264,7 +271,8 @@ class WalletItems {
const std::string& google_transaction_id,
const std::string& default_instrument_id,
const std::string& default_address_id,
- const std::string& obfuscated_gaia_id);
+ const std::string& obfuscated_gaia_id,
+ AmexPermission amex_permission);
// Actions that must be completed by the user before a FullWallet can be
// issued to them by the Online Wallet service.
@@ -291,6 +299,9 @@ class WalletItems {
// Legal documents the user must accept before using Online Wallet.
ScopedVector<LegalDocument> legal_documents_;
+ // Whether Google Wallet allows American Express card for this merchant.
+ AmexPermission amex_permission_;
+
DISALLOW_COPY_AND_ASSIGN(WalletItems);
};
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc b/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc
index 83f8b1939fc..75da0fdc3e2 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_items_unittest.cc
@@ -63,7 +63,8 @@ const char kMaskedInstrumentMissingStatus[] =
" \"phone_number\":\"phone_number\","
" \"country_code\":\"country_code\""
" },"
- " \"object_id\":\"object_id\""
+ " \"object_id\":\"object_id\","
+ " \"amex_disallowed\":true"
"}";
const char kMaskedInstrumentMissingType[] =
@@ -278,6 +279,7 @@ const char kWalletItemsMissingGoogleTransactionId[] =
" ],"
" \"default_address_id\":\"default_address_id\","
" \"obfuscated_gaia_id\":\"obfuscated_gaia_id\","
+ " \"amex_disallowed\":true,"
" \"required_legal_document\":"
" ["
" {"
@@ -343,7 +345,8 @@ const char kWalletItems[] =
" }"
" ],"
" \"default_address_id\":\"default_address_id\","
- " \"obfuscated_gaia_id\":\"obfuscated_gaia_id\"";
+ " \"obfuscated_gaia_id\":\"obfuscated_gaia_id\","
+ " \"amex_disallowed\":true";
const char kRequiredLegalDocument[] =
" ,"
@@ -484,7 +487,8 @@ TEST_F(WalletItemsTest, CreateWalletItemsWithRequiredActions) {
std::string(),
std::string(),
std::string(),
- std::string());
+ std::string(),
+ AMEX_DISALLOWED);
EXPECT_EQ(expected, *WalletItems::CreateWalletItems(*dict));
ASSERT_FALSE(required_actions.empty());
@@ -493,7 +497,8 @@ TEST_F(WalletItemsTest, CreateWalletItemsWithRequiredActions) {
std::string(),
std::string(),
std::string(),
- std::string());
+ std::string(),
+ AMEX_DISALLOWED);
EXPECT_NE(expected, different_required_actions);
}
@@ -507,6 +512,12 @@ TEST_F(WalletItemsTest, CreateWalletItemsMissingGoogleTransactionId) {
EXPECT_EQ(NULL, WalletItems::CreateWalletItems(*dict).get());
}
+TEST_F(WalletItemsTest, CreateWalletItemsMissingAmexDisallowed) {
+ SetUpDictionary(std::string(kWalletItems) + std::string(kCloseJson));
+ EXPECT_TRUE(dict->Remove("amex_disallowed", NULL));
+ EXPECT_FALSE(WalletItems::CreateWalletItems(*dict)->is_amex_allowed());
+}
+
TEST_F(WalletItemsTest, CreateWalletItems) {
SetUpDictionary(std::string(kWalletItems) + std::string(kCloseJson));
std::vector<RequiredAction> required_actions;
@@ -514,7 +525,8 @@ TEST_F(WalletItemsTest, CreateWalletItems) {
"google_transaction_id",
"default_instrument_id",
"default_address_id",
- "obfuscated_gaia_id");
+ "obfuscated_gaia_id",
+ AMEX_DISALLOWED);
scoped_ptr<Address> billing_address(new Address("country_code",
ASCIIToUTF16("name"),
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc b/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc
index e95a201a446..c56d5864fa8 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_service_url.cc
@@ -27,10 +27,16 @@ 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";
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ std::string sandbox_enabled(
+ command_line->GetSwitchValueASCII(switches::kWalletServiceUseSandbox));
+ if (!sandbox_enabled.empty())
+ return sandbox_enabled != "1";
+#if defined(OS_MACOSX)
+ return false;
+#else
+ return true;
+#endif
}
GURL GetWalletHostUrl() {
@@ -122,6 +128,8 @@ GURL GetSignInUrl() {
GURL url(GaiaUrls::GetInstance()->service_login_url());
url = net::AppendQueryParameter(url, "service", "toolbar");
url = net::AppendQueryParameter(url, "nui", "1");
+ // Prevents promos from showing (see http://crbug.com/235227).
+ url = net::AppendQueryParameter(url, "sarp", "1");
url = net::AppendQueryParameter(url,
"continue",
GetSignInContinueUrl().spec());
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
index 3ed3600fc0a..b028a15ce9e 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_service_url_unittest.cc
@@ -12,9 +12,11 @@ namespace autofill {
namespace wallet {
TEST(WalletServiceUrl, CheckDefaultUrls) {
+#if defined(OS_MACOSX)
+ // Default on Mac (for now) is to use sandbox servers.
EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/wallet/"
- "autocheckout/v1/getWalletItemsJwtless",
- GetGetWalletItemsUrl().spec());
+ "autocheckout/v1/getWalletItemsJwtless",
+ GetGetWalletItemsUrl().spec());
EXPECT_EQ("https://wallet-web.sandbox.google.com/online-secure/v2/"
"autocheckout/v1/getFullWalletJwtless?s7e=otp",
GetGetFullWalletUrl().spec());
@@ -40,21 +42,57 @@ TEST(WalletServiceUrl, CheckDefaultUrls) {
EXPECT_EQ("https://payments-form-dogfood.sandbox.google.com/online/v2/"
"passiveauth?isChromePayments=true",
GetPassiveAuthUrl().spec());
+#else
+ EXPECT_EQ("https://wallet.google.com/online/v2/wallet/"
+ "autocheckout/v1/getWalletItemsJwtless",
+ GetGetWalletItemsUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online-secure/v2/"
+ "autocheckout/v1/getFullWalletJwtless?s7e=otp",
+ GetGetFullWalletUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/manage/paymentMethods",
+ GetManageInstrumentsUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/manage/settings/addresses",
+ GetManageAddressesUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online/v2/wallet/"
+ "autocheckout/v1/acceptLegalDocument",
+ GetAcceptLegalDocumentsUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online-secure/v2/"
+ "autocheckout/v1/authenticateInstrument?s7e=cvn",
+ GetAuthenticateInstrumentUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online/v2/wallet/"
+ "autocheckout/v1/reportStatus",
+ GetSendStatusUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online/v2/wallet/"
+ "autocheckout/v1/saveToWallet",
+ GetSaveToWalletNoEscrowUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online-secure/v2/"
+ "autocheckout/v1/saveToWallet?s7e=card_number%3Bcvn",
+ GetSaveToWalletUrl().spec());
+ EXPECT_EQ("https://wallet.google.com/online/v2/"
+ "passiveauth?isChromePayments=true",
+ GetPassiveAuthUrl().spec());
+#endif
}
TEST(WalletServiceUrl, IsUsingProd) {
- // The sandbox servers are the default (for now). Update if this changes.
+#if defined(OS_MACOSX)
EXPECT_FALSE(IsUsingProd());
-
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- command_line->AppendSwitch(switches::kWalletServiceUseProd);
+#else
EXPECT_TRUE(IsUsingProd());
+#endif
- const GURL prod_get_items_url = GetGetWalletItemsUrl();
- command_line->AppendSwitchASCII(switches::kWalletServiceUrl, "http://goo.gl");
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitchASCII(switches::kWalletServiceUseSandbox, "1");
EXPECT_FALSE(IsUsingProd());
- ASSERT_NE(prod_get_items_url, GetGetWalletItemsUrl());
+ const GURL sandbox_get_items_url = GetGetWalletItemsUrl();
+ const GURL fake_service_url = GURL("http://goo.gl");
+ command_line->AppendSwitchASCII(switches::kWalletServiceUrl,
+ fake_service_url.spec());
+
+ const GURL flag_get_items_url = GetGetWalletItemsUrl();
+ ASSERT_NE(sandbox_get_items_url, flag_get_items_url);
+ EXPECT_EQ(fake_service_url.GetOrigin(), flag_get_items_url.GetOrigin());
}
} // namespace wallet
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc b/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc
index 15f916afdc0..0a15c3e338e 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc
+++ b/chromium/components/autofill/content/browser/wallet/wallet_test_util.cc
@@ -179,13 +179,15 @@ scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentInvalid() {
WalletItems::MaskedInstrument::DECLINED);
}
-scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentAmex() {
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentAmex(
+ AmexPermission amex_permission) {
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);
+ (amex_permission == AMEX_ALLOWED)
+ ? WalletItems::MaskedInstrument::VALID
+ : WalletItems::MaskedInstrument::AMEX_NOT_SUPPORTED);
}
scoped_ptr<WalletItems::MaskedInstrument> GetTestNonDefaultMaskedInstrument() {
@@ -224,13 +226,23 @@ scoped_ptr<Address> GetTestNonDefaultShippingAddress() {
return address.Pass();
}
-scoped_ptr<WalletItems> GetTestWalletItems() {
+scoped_ptr<WalletItems> GetTestWalletItems(AmexPermission amex_permission) {
+ return GetTestWalletItemsWithDefaultIds("default_instrument_id",
+ "default_address_id",
+ amex_permission);
+}
+
+scoped_ptr<WalletItems> GetTestWalletItemsWithDefaultIds(
+ const std::string& default_instrument_id,
+ const std::string& default_address_id,
+ AmexPermission amex_permission) {
return scoped_ptr<WalletItems>(
new wallet::WalletItems(std::vector<RequiredAction>(),
"google_transaction_id",
- "default_instrument_id",
- "default_address_id",
- "obfuscated_gaia_id"));
+ default_instrument_id,
+ default_address_id,
+ "obfuscated_gaia_id",
+ amex_permission));
}
} // namespace wallet
diff --git a/chromium/components/autofill/content/browser/wallet/wallet_test_util.h b/chromium/components/autofill/content/browser/wallet/wallet_test_util.h
index fd78b05b865..b9bfbdd4a37 100644
--- a/chromium/components/autofill/content/browser/wallet/wallet_test_util.h
+++ b/chromium/components/autofill/content/browser/wallet/wallet_test_util.h
@@ -27,15 +27,27 @@ 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> GetTestMaskedInstrumentAmex(
+ AmexPermission amex_permission);
scoped_ptr<WalletItems::MaskedInstrument> GetTestNonDefaultMaskedInstrument();
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentWithId(
+ const std::string& id);
scoped_ptr<WalletItems::MaskedInstrument>
GetTestMaskedInstrumentWithIdAndAddress(
const std::string& id, scoped_ptr<Address> address);
+scoped_ptr<WalletItems::MaskedInstrument> GetTestMaskedInstrumentWithDetails(
+ const std::string& id,
+ scoped_ptr<Address> address,
+ WalletItems::MaskedInstrument::Type type,
+ WalletItems::MaskedInstrument::Status status);
scoped_ptr<Address> GetTestSaveableAddress();
scoped_ptr<Address> GetTestShippingAddress();
scoped_ptr<Address> GetTestNonDefaultShippingAddress();
-scoped_ptr<WalletItems> GetTestWalletItems();
+scoped_ptr<WalletItems> GetTestWalletItems(AmexPermission amex_permission);
+scoped_ptr<WalletItems> GetTestWalletItemsWithDefaultIds(
+ const std::string& default_instrument_id,
+ const std::string& default_address_id,
+ AmexPermission amex_permission);
} // namespace wallet
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/autofill_agent.cc b/chromium/components/autofill/content/renderer/autofill_agent.cc
index e8509c74750..ca57847f7d4 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/autofill_agent.cc
@@ -20,8 +20,8 @@
#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.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"
@@ -39,8 +39,8 @@
#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"
+#include "ui/events/keycodes/keyboard_codes.h"
using WebKit::WebAutofillClient;
using WebKit::WebFormControlElement;
@@ -63,8 +63,6 @@ const size_t kMaximumTextSizeForAutofill = 1000;
// 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.
@@ -130,14 +128,11 @@ AutofillAgent::AutofillAgent(content::RenderView* 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) {
@@ -153,7 +148,6 @@ 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)
@@ -173,10 +167,6 @@ bool AutofillAgent::OnMessageReceived(const IPC::Message& message) {
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)
@@ -194,7 +184,6 @@ void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) {
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_);
@@ -213,41 +202,13 @@ void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) {
}
}
-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,
@@ -288,24 +249,10 @@ void AutofillAgent::FocusedNodeChanged(const WebKit::WebNode& node) {
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::OrientationChangeEvent(int orientation) {
+ HideAutofillUI();
}
void AutofillAgent::DidChangeScrollOffset(WebKit::WebFrame*) {
@@ -318,7 +265,7 @@ void AutofillAgent::didRequestAutocomplete(WebKit::WebFrame* frame,
content::SSLStatus ssl_status = render_view()->GetSSLStatusOfFrame(frame);
FormData form_data;
if (!in_flight_request_form_.isNull() ||
- (url.SchemeIs(chrome::kHttpsScheme) &&
+ (url.SchemeIs(content::kHttpsScheme) &&
(net::IsCertStatusError(ssl_status.cert_status) ||
net::IsCertStatusMinorError(ssl_status.cert_status))) ||
!WebFormElementToFormData(form,
@@ -524,24 +471,6 @@ void AutofillAgent::OnAcceptPasswordAutofillSuggestion(
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())
@@ -557,77 +486,7 @@ void AutofillAgent::OnRequestAutocompleteResult(
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,
@@ -748,38 +607,18 @@ void AutofillAgent::HideAutofillUI() {
Send(new AutofillHostMsg_HideAutofillUI(routing_id()));
}
+// TODO(isherman): Decide if we want to support non-password autofill with AJAX.
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;
+ WebKit::WebFrame* frame = nodes[i].document().frame();
+ // Only monitors dynamic forms created in the top frame. Dynamic forms
+ // inserted in iframes are not captured yet.
+ if (!frame->parent()) {
+ password_autofill_agent_->OnDynamicFormsSeen(frame);
+ return;
}
}
-
- 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
index 9236246c18d..3f979bf95b3 100644
--- a/chromium/components/autofill/content/renderer/autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/autofill_agent.h
@@ -15,7 +15,6 @@
#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"
@@ -61,10 +60,6 @@ class AutofillAgent : public content::RenderViewObserver,
// 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;
@@ -73,6 +68,7 @@ class AutofillAgent : public content::RenderViewObserver,
virtual void ZoomLevelChanged() OVERRIDE;
virtual void DidChangeScrollOffset(WebKit::WebFrame* frame) OVERRIDE;
virtual void FocusedNodeChanged(const WebKit::WebNode& node) OVERRIDE;
+ virtual void OrientationChangeEvent(int orientation) OVERRIDE;
// PageClickListener:
virtual void InputElementClicked(const WebKit::WebInputElement& element,
@@ -108,7 +104,6 @@ class AutofillAgent : public content::RenderViewObserver,
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(
@@ -119,29 +114,10 @@ class AutofillAgent : public content::RenderViewObserver,
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);
@@ -191,11 +167,6 @@ class AutofillAgent : public content::RenderViewObserver,
// 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.
@@ -216,10 +187,6 @@ class AutofillAgent : public content::RenderViewObserver,
// 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_;
@@ -236,16 +203,6 @@ class AutofillAgent : public content::RenderViewObserver,
// 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_;
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.cc b/chromium/components/autofill/content/renderer/form_autofill_util.cc
index 55f3a03e7c9..b9eca17434f 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.cc
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.cc
@@ -79,7 +79,7 @@ bool IsNoScriptElement(const WebElement& element) {
}
bool HasTagName(const WebNode& node, const WebKit::WebString& tag) {
- return node.isElementNode() && node.toConst<WebElement>().hasTagName(tag);
+ return node.isElementNode() && node.toConst<WebElement>().hasHTMLTagName(tag);
}
bool IsAutofillableElement(const WebFormControlElement& element) {
@@ -87,18 +87,9 @@ bool IsAutofillableElement(const WebFormControlElement& 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();
+ return input_element.autoComplete();
}
// Appends |suffix| to |prefix| so that any intermediary whitespace is collapsed
@@ -599,9 +590,30 @@ std::string RetrievalMethodToString(
return "UNKNOWN";
}
+// Recursively checks whether |node| or any of its children have a non-empty
+// bounding box. The recursion depth is bounded by |depth|.
+bool IsWebNodeVisibleImpl(const WebKit::WebNode& node, const int depth) {
+ if (depth < 0)
+ return false;
+ if (node.hasNonEmptyBoundingBox())
+ return true;
+
+ // The childNodes method is not a const method. Therefore it cannot be called
+ // on a const reference. Therefore we need a const cast.
+ const WebKit::WebNodeList& children =
+ const_cast<WebKit::WebNode&>(node).childNodes();
+ size_t length = children.length();
+ for (size_t i = 0; i < length; ++i) {
+ const WebKit::WebNode& item = children.item(i);
+ if (IsWebNodeVisibleImpl(item, depth - 1))
+ return true;
+ }
+ return false;
+}
+
} // namespace
-const size_t kMaxParseableFields = 100;
+const size_t kMaxParseableFields = 200;
// All text fields, including password fields, should be extracted.
bool IsTextInput(const WebInputElement* element) {
@@ -634,6 +646,14 @@ const base::string16 GetFormIdentifier(const WebFormElement& form) {
return identifier;
}
+bool IsWebNodeVisible(const WebKit::WebNode& node) {
+ // In the bug http://crbug.com/237216 the form's bounding box is empty
+ // however the form has non empty children. Thus we need to look at the
+ // form's children.
+ int kNodeSearchDepth = 2;
+ return IsWebNodeVisibleImpl(node, kNodeSearchDepth);
+}
+
bool ClickElement(const WebDocument& document,
const WebElementDescriptor& element_descriptor) {
WebString web_descriptor = WebString::fromUTF8(element_descriptor.descriptor);
@@ -1056,4 +1076,54 @@ bool FormWithElementIsAutofilled(const WebInputElement& element) {
return false;
}
+bool IsWebpageEmpty(const WebKit::WebFrame* frame) {
+ WebKit::WebDocument document = frame->document();
+
+ return IsWebElementEmpty(document.head()) &&
+ IsWebElementEmpty(document.body());
+}
+
+bool IsWebElementEmpty(const WebKit::WebElement& element) {
+ // This array contains all tags which can be present in an empty page.
+ const char* const kAllowedValue[] = {
+ "script",
+ "meta",
+ "title",
+ };
+ const size_t kAllowedValueLength = arraysize(kAllowedValue);
+
+ if (element.isNull())
+ return true;
+ // The childNodes method is not a const method. Therefore it cannot be called
+ // on a const reference. Therefore we need a const cast.
+ const WebKit::WebNodeList& children =
+ const_cast<WebKit::WebElement&>(element).childNodes();
+ for (size_t i = 0; i < children.length(); ++i) {
+ const WebKit::WebNode& item = children.item(i);
+
+ if (item.isTextNode() &&
+ !ContainsOnlyWhitespaceASCII(item.nodeValue().utf8()))
+ return false;
+
+ // We ignore all other items with names which begin with
+ // the character # because they are not html tags.
+ if (item.nodeName().utf8()[0] == '#')
+ continue;
+
+ bool tag_is_allowed = false;
+ // Test if the item name is in the kAllowedValue array
+ for (size_t allowed_value_index = 0;
+ allowed_value_index < kAllowedValueLength; ++allowed_value_index) {
+ if (HasTagName(item,
+ WebString::fromUTF8(kAllowedValue[allowed_value_index]))) {
+ tag_is_allowed = true;
+ break;
+ }
+ }
+ if (!tag_is_allowed)
+ return false;
+ }
+ return true;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/form_autofill_util.h b/chromium/components/autofill/content/renderer/form_autofill_util.h
index 5a1241897f8..8c109c467e5 100644
--- a/chromium/components/autofill/content/renderer/form_autofill_util.h
+++ b/chromium/components/autofill/content/renderer/form_autofill_util.h
@@ -11,9 +11,12 @@
namespace WebKit {
class WebDocument;
+class WebElement;
class WebFormElement;
class WebFormControlElement;
+class WebFrame;
class WebInputElement;
+class WebNode;
}
namespace autofill {
@@ -61,6 +64,10 @@ bool IsCheckableElement(const WebKit::WebInputElement* element);
// autofilled. {Text, Radiobutton, Checkbox}.
bool IsAutofillableInputElement(const WebKit::WebInputElement* element);
+// Recursively checks whether |node| or any of its children have a non-empty
+// bounding box.
+bool IsWebNodeVisible(const WebKit::WebNode& node);
+
// Returns the form's |name| attribute if non-empty; otherwise the form's |id|
// attribute.
const base::string16 GetFormIdentifier(const WebKit::WebFormElement& form);
@@ -139,6 +146,21 @@ bool ClearPreviewedFormWithElement(const WebKit::WebInputElement& element,
// Returns true if |form| has any auto-filled fields.
bool FormWithElementIsAutofilled(const WebKit::WebInputElement& element);
+// Checks if the webpage is empty.
+// This kind of webpage is considered as empty:
+// <html>
+// <head>
+// <head/>
+// <body>
+// <body/>
+// <html/>
+// Meta, script and title tags don't influence the emptiness of a webpage.
+bool IsWebpageEmpty(const WebKit::WebFrame* frame);
+
+// This function checks whether the children of |element|
+// are of the type <script>, <meta>, or <title>.
+bool IsWebElementEmpty(const WebKit::WebElement& element);
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_FORM_AUTOFILL_UTIL_H_
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.cc b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
index b6294abf59e..ce2f1031844 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.cc
@@ -10,11 +10,11 @@
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/content/renderer/password_form_conversion_utils.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.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"
@@ -23,9 +23,12 @@
#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/WebNodeList.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
+#include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
#include "third_party/WebKit/public/web/WebView.h"
-#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/events/keycodes/keyboard_codes.h"
namespace autofill {
namespace {
@@ -348,27 +351,39 @@ bool PasswordAutofillAgent::ShowSuggestions(
return ShowSuggestionPopup(iter->second.fill_data, element);
}
+bool PasswordAutofillAgent::OriginCanAccessPasswordManager(
+ const WebKit::WebSecurityOrigin& origin) {
+ return origin.canAccessPasswordManager();
+}
+
+void PasswordAutofillAgent::OnDynamicFormsSeen(WebKit::WebFrame* frame) {
+ SendPasswordForms(frame, false /* only_visible */);
+}
+
void PasswordAutofillAgent::SendPasswordForms(WebKit::WebFrame* frame,
- bool only_visible) {
+ bool only_visible) {
// Make sure that this security origin is allowed to use password manager.
WebKit::WebSecurityOrigin origin = frame->document().securityOrigin();
- if (!origin.canAccessPasswordManager())
+ if (!OriginCanAccessPasswordManager(origin))
+ return;
+
+ // Checks whether the webpage is a redirect page or an empty page.
+ if (IsWebpageEmpty(frame))
return;
WebKit::WebVector<WebKit::WebFormElement> forms;
frame->document().forms(forms);
- std::vector<content::PasswordForm> password_forms;
+ std::vector<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())
+ if (only_visible && !IsWebNodeVisible(form))
continue;
- scoped_ptr<content::PasswordForm> password_form(
- content::CreatePasswordForm(form));
+ scoped_ptr<PasswordForm> password_form(CreatePasswordForm(form));
if (password_form.get())
password_forms.push_back(*password_form);
}
@@ -381,8 +396,8 @@ void PasswordAutofillAgent::SendPasswordForms(WebKit::WebFrame* frame,
}
if (only_visible) {
- Send(new AutofillHostMsg_PasswordFormsRendered(
- routing_id(), password_forms));
+ Send(new AutofillHostMsg_PasswordFormsRendered(routing_id(),
+ password_forms));
} else {
Send(new AutofillHostMsg_PasswordFormsParsed(routing_id(), password_forms));
}
@@ -428,6 +443,63 @@ void PasswordAutofillAgent::FrameWillClose(WebKit::WebFrame* frame) {
FrameClosing(frame);
}
+void PasswordAutofillAgent::WillSendSubmitEvent(
+ WebKit::WebFrame* frame,
+ const WebKit::WebFormElement& form) {
+ // Some login forms have onSubmit handlers that put a hash of the password
+ // into a hidden field and then clear the password (http://crbug.com/28910).
+ // This method gets called before any of those handlers run, so save away
+ // a copy of the password in case it gets lost.
+ provisionally_saved_forms_[frame].reset(CreatePasswordForm(form).release());
+}
+
+void PasswordAutofillAgent::WillSubmitForm(WebKit::WebFrame* frame,
+ const WebKit::WebFormElement& form) {
+ scoped_ptr<PasswordForm> submitted_form = CreatePasswordForm(form);
+
+ // If there is a provisionally saved password, copy over the previous
+ // password value so we get the user's typed password, not the value that
+ // may have been transformed for submit.
+ // TODO(gcasto): Do we need to have this action equality check? Is it trying
+ // to prevent accidentally copying over passwords from a different form?
+ if (submitted_form) {
+ if (provisionally_saved_forms_[frame].get() &&
+ submitted_form->action == provisionally_saved_forms_[frame]->action) {
+ submitted_form->password_value =
+ provisionally_saved_forms_[frame]->password_value;
+ }
+
+ // Some observers depend on sending this information now instead of when
+ // the frame starts loading. If there are redirects that cause a new
+ // RenderView to be instantiated (such as redirects to the WebStore)
+ // we will never get to finish the load.
+ Send(new AutofillHostMsg_PasswordFormSubmitted(routing_id(),
+ *submitted_form));
+ // Remove reference since we have already submitted this form.
+ provisionally_saved_forms_.erase(frame);
+ }
+}
+
+void PasswordAutofillAgent::DidStartProvisionalLoad(WebKit::WebFrame* frame) {
+ if (!frame->parent()) {
+ // If the navigation is not triggered by a user gesture, e.g. by some ajax
+ // callback, then inherit the submitted password form from the previous
+ // state. This fixes the no password save issue for ajax login, tracked in
+ // [http://crbug/43219]. Note that there are still some sites that this
+ // fails for because they use some element other than a submit button to
+ // trigger submission (which means WillSendSubmitEvent will not be called).
+ if (!WebKit::WebUserGestureIndicator::isProcessingUserGesture() &&
+ provisionally_saved_forms_[frame].get()) {
+ Send(new AutofillHostMsg_PasswordFormSubmitted(
+ routing_id(),
+ *provisionally_saved_forms_[frame]));
+ provisionally_saved_forms_.erase(frame);
+ }
+ // Clear the whole map during main frame navigation.
+ provisionally_saved_forms_.clear();
+ }
+}
+
void PasswordAutofillAgent::OnFillPasswordForm(
const PasswordFormFillData& form_data) {
if (usernames_usage_ == NOTHING_TO_AUTOFILL) {
diff --git a/chromium/components/autofill/content/renderer/password_autofill_agent.h b/chromium/components/autofill/content/renderer/password_autofill_agent.h
index 30fef628c15..a389ed2fae6 100644
--- a/chromium/components/autofill/content/renderer/password_autofill_agent.h
+++ b/chromium/components/autofill/content/renderer/password_autofill_agent.h
@@ -8,6 +8,7 @@
#include <map>
#include <vector>
+#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
#include "components/autofill/core/common/password_form_fill_data.h"
#include "content/public/renderer/render_view_observer.h"
@@ -16,6 +17,7 @@
namespace WebKit {
class WebInputElement;
class WebKeyboardEvent;
+class WebSecurityOrigin;
class WebView;
}
@@ -48,6 +50,13 @@ class PasswordAutofillAgent : public content::RenderViewObserver {
// Returns true if any suggestions were shown, false otherwise.
bool ShowSuggestions(const WebKit::WebInputElement& element);
+ // Called when new form controls are inserted.
+ void OnDynamicFormsSeen(WebKit::WebFrame* frame);
+
+ protected:
+ virtual bool OriginCanAccessPasswordManager(
+ const WebKit::WebSecurityOrigin& origin);
+
private:
friend class PasswordAutofillAgentTest;
@@ -67,14 +76,21 @@ class PasswordAutofillAgent : public content::RenderViewObserver {
PasswordInfo() : backspace_pressed_last(false) {}
};
typedef std::map<WebKit::WebElement, PasswordInfo> LoginToPasswordInfoMap;
+ typedef std::map<WebKit::WebFrame*,
+ linked_ptr<PasswordForm> > FrameToPasswordFormMap;
// RenderViewObserver:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void DidStartProvisionalLoad(WebKit::WebFrame* frame) 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;
+ virtual void WillSendSubmitEvent(WebKit::WebFrame* frame,
+ const WebKit::WebFormElement& form) OVERRIDE;
+ virtual void WillSubmitForm(WebKit::WebFrame* frame,
+ const WebKit::WebFormElement& form) OVERRIDE;
// RenderView IPC handlers:
void OnFillPasswordForm(const PasswordFormFillData& form_data);
@@ -123,6 +139,10 @@ class PasswordAutofillAgent : public content::RenderViewObserver {
// Pointer to the WebView. Used to access page scale factor.
WebKit::WebView* web_view_;
+ // Set if the user might be submitting a password form on the current page,
+ // but the submit may still fail (i.e. doesn't pass JavaScript validation).
+ FrameToPasswordFormMap provisionally_saved_forms_;
+
base::WeakPtrFactory<PasswordAutofillAgent> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgent);
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
new file mode 100644
index 00000000000..7dfd45dcaa6
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -0,0 +1,62 @@
+// Copyright 2013 The Chromium Authors. 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_form_conversion_utils.h"
+
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/core/common/password_form.h"
+#include "third_party/WebKit/public/web/WebFormControlElement.h"
+#include "third_party/WebKit/public/web/WebPasswordFormData.h"
+
+using WebKit::WebFormElement;
+using WebKit::WebPasswordFormData;
+
+namespace autofill {
+namespace {
+
+scoped_ptr<PasswordForm> InitPasswordFormFromWebPasswordForm(
+ const WebFormElement& web_form,
+ const WebKit::WebPasswordFormData& web_password_form) {
+ PasswordForm* password_form = new PasswordForm();
+ password_form->signon_realm = web_password_form.signonRealm.utf8();
+ password_form->origin = web_password_form.origin;
+ password_form->action = web_password_form.action;
+ password_form->submit_element = web_password_form.submitElement;
+ password_form->username_element = web_password_form.userNameElement;
+ password_form->username_value = web_password_form.userNameValue;
+ password_form->other_possible_usernames.insert(
+ password_form->other_possible_usernames.begin(),
+ web_password_form.possibleUserNames.data(),
+ web_password_form.possibleUserNames.data() +
+ web_password_form.possibleUserNames.size());
+ password_form->password_element = web_password_form.passwordElement;
+ password_form->password_value = web_password_form.passwordValue;
+ password_form->password_autocomplete_set =
+ web_password_form.passwordShouldAutocomplete;
+ password_form->old_password_element = web_password_form.oldPasswordElement;
+ password_form->old_password_value = web_password_form.oldPasswordValue;
+ password_form->scheme = PasswordForm::SCHEME_HTML;
+ password_form->ssl_valid = false;
+ password_form->preferred = false;
+ password_form->blacklisted_by_user = false;
+ password_form->type = PasswordForm::TYPE_MANUAL;
+ WebFormElementToFormData(web_form,
+ WebKit::WebFormControlElement(),
+ REQUIRE_NONE,
+ EXTRACT_NONE,
+ &password_form->form_data,
+ NULL /* FormFieldData */);
+ return scoped_ptr<PasswordForm>(password_form);
+}
+
+} // namespace
+
+scoped_ptr<PasswordForm> CreatePasswordForm(const WebFormElement& webform) {
+ WebPasswordFormData web_password_form(webform);
+ if (web_password_form.isValid())
+ return InitPasswordFormFromWebPasswordForm(webform, web_password_form);
+ return scoped_ptr<PasswordForm>();
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils.h b/chromium/components/autofill/content/renderer/password_form_conversion_utils.h
new file mode 100644
index 00000000000..59786ede987
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils.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_CONTENT_RENDERER_PASSWORD_FORM_CONVERSION_UTILS_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_FORM_CONVERSION_UTILS_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace WebKit {
+class WebFormElement;
+}
+
+namespace autofill {
+
+struct PasswordForm;
+
+// Create a PasswordForm from DOM form. Webkit doesn't allow storing
+// custom metadata to DOM nodes, so we have to do this every time an event
+// happens with a given form and compare against previously Create'd forms
+// to identify..which sucks.
+scoped_ptr<PasswordForm> CreatePasswordForm(
+ const WebKit::WebFormElement& form);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_PASSWORD_FORM_CONVERSION_UTILS_H__
diff --git a/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
new file mode 100644
index 00000000000..01b5f55e905
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/password_form_conversion_utils_browsertest.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 "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/renderer/password_form_conversion_utils.h"
+#include "components/autofill/core/common/password_form.h"
+#include "content/public/test/render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.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/WebPasswordFormData.h"
+
+using WebKit::WebFormElement;
+using WebKit::WebFrame;
+using WebKit::WebPasswordFormData;
+using WebKit::WebVector;
+
+namespace autofill {
+namespace {
+
+class PasswordFormConversionUtilsTest : public content::RenderViewTest {
+ public:
+ PasswordFormConversionUtilsTest() : content::RenderViewTest() {}
+ virtual ~PasswordFormConversionUtilsTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest);
+};
+
+} // namespace
+
+TEST_F(PasswordFormConversionUtilsTest, ValidWebFormElementToPasswordForm) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"http://cnn.com\" method=\"post\">"
+ " <INPUT type=\"text\" name=\"username\" "
+ " id=\"username\" value=\"johnsmith\"/>"
+ " <INPUT type=\"submit\" name=\"submit\" value=\"Submit\"/>"
+ " <INPUT type=\"password\" name=\"password\" id=\"password\" "
+ " value=\"secret\"/>"
+ "</FORM>");
+
+ WebFrame* frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), frame);
+
+ WebVector<WebFormElement> forms;
+ frame->document().forms(forms);
+ ASSERT_EQ(1U, forms.size());
+ WebPasswordFormData web_password_form(forms[0]);
+ ASSERT_TRUE(web_password_form.isValid());
+
+ scoped_ptr<PasswordForm> password_form = CreatePasswordForm(forms[0]);
+ ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get());
+
+ EXPECT_EQ("data:", password_form->signon_realm);
+ EXPECT_EQ(GURL("http://cnn.com"), password_form->action);
+ EXPECT_EQ(UTF8ToUTF16("username"), password_form->username_element);
+ EXPECT_EQ(UTF8ToUTF16("johnsmith"), password_form->username_value);
+ EXPECT_EQ(UTF8ToUTF16("password"), password_form->password_element);
+ EXPECT_EQ(UTF8ToUTF16("secret"), password_form->password_value);
+ EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme);
+ EXPECT_FALSE(password_form->ssl_valid);
+ EXPECT_FALSE(password_form->preferred);
+ EXPECT_FALSE(password_form->blacklisted_by_user);
+ EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type);
+}
+
+TEST_F(PasswordFormConversionUtilsTest, InvalidWebFormElementToPasswordForm) {
+ LoadHTML("<FORM name=\"TestForm\" action=\"invalid\" method=\"post\">"
+ " <INPUT type=\"text\" name=\"username\" "
+ " id=\"username\" value=\"johnsmith\"/>"
+ " <INPUT type=\"submit\" name=\"submit\" value=\"Submit\"/>"
+ " <INPUT type=\"password\" name=\"password\" id=\"password\" "
+ " value=\"secret\"/>"
+ "</FORM>");
+
+ WebFrame* frame = GetMainFrame();
+ ASSERT_NE(static_cast<WebFrame*>(NULL), frame);
+
+ WebVector<WebFormElement> forms;
+ frame->document().forms(forms);
+ ASSERT_EQ(1U, forms.size());
+ WebPasswordFormData web_password_form(forms[0]);
+ ASSERT_FALSE(web_password_form.isValid());
+
+ scoped_ptr<PasswordForm> password_form = CreatePasswordForm(forms[0]);
+ EXPECT_EQ(static_cast<PasswordForm*>(NULL), password_form.get());
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_generation_manager.cc b/chromium/components/autofill/content/renderer/password_generation_manager.cc
index aa50cdc0b4d..8c035c7c0c4 100644
--- a/chromium/components/autofill/content/renderer/password_generation_manager.cc
+++ b/chromium/components/autofill/content/renderer/password_generation_manager.cc
@@ -5,9 +5,12 @@
#include "components/autofill/content/renderer/password_generation_manager.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/autofill/content/renderer/password_form_conversion_utils.h"
#include "components/autofill/core/common/autofill_messages.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/password_form.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"
@@ -64,6 +67,27 @@ bool GetAccountCreationPasswordFields(
return false;
}
+bool ContainsURL(const std::vector<GURL>& urls, const GURL& url) {
+ return std::find(urls.begin(), urls.end(), url) != urls.end();
+}
+
+// Returns true if the |form1| is essentially equal to |form2|.
+bool FormEquals(const autofill::FormData& form1,
+ const PasswordForm& form2) {
+ // TODO(zysxqn): use more signals than just origin to compare.
+ return form1.origin == form2.origin;
+}
+
+bool ContainsForm(const std::vector<autofill::FormData>& forms,
+ const PasswordForm& form) {
+ for (std::vector<autofill::FormData>::const_iterator it =
+ forms.begin(); it != forms.end(); ++it) {
+ if (FormEquals(*it, form))
+ return true;
+ }
+ return false;
+}
+
} // namespace
PasswordGenerationManager::PasswordGenerationManager(
@@ -83,10 +107,13 @@ void PasswordGenerationManager::DidFinishDocumentLoad(WebKit::WebFrame* frame) {
// 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.
+ //
+ // TODO(zysxqn): Add stat when local heuristic fires but we don't show the
+ // password generation icon.
if (!frame->parent()) {
not_blacklisted_password_form_origins_.clear();
- // Initialize to an empty and invalid GURL.
- account_creation_form_origin_ = GURL();
+ account_creation_forms_.clear();
+ possible_account_creation_form_.reset(new PasswordForm());
passwords_.clear();
}
}
@@ -108,8 +135,8 @@ void PasswordGenerationManager::DidFinishLoad(WebKit::WebFrame* frame) {
// 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]));
+ scoped_ptr<PasswordForm> password_form(
+ CreatePasswordForm(forms[i]));
if (!password_form.get()) {
DVLOG(2) << "Skipping form as it would not be saved";
continue;
@@ -118,7 +145,7 @@ void PasswordGenerationManager::DidFinishLoad(WebKit::WebFrame* frame) {
// 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()))
+ if (realm == GaiaUrls::GetInstance()->gaia_login_form_realm())
continue;
std::vector<WebKit::WebInputElement> passwords;
@@ -127,7 +154,7 @@ void PasswordGenerationManager::DidFinishLoad(WebKit::WebFrame* frame) {
password_generation::LogPasswordGenerationEvent(
password_generation::SIGN_UP_DETECTED);
passwords_ = passwords;
- account_creation_form_origin_ = password_form->origin;
+ possible_account_creation_form_.swap(password_form);
MaybeShowIcon();
// We assume that there is only one account creation field per URL.
return;
@@ -154,8 +181,8 @@ 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()));
+ scoped_ptr<PasswordForm> password_form(
+ CreatePasswordForm(element.form()));
// We should not have shown the icon we can't create a valid PasswordForm.
DCHECK(password_form.get());
@@ -176,13 +203,14 @@ bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) {
OnPasswordAccepted)
IPC_MESSAGE_HANDLER(AutofillMsg_PasswordGenerationEnabled,
OnPasswordGenerationEnabled)
+ IPC_MESSAGE_HANDLER(AutofillMsg_AccountCreationFormsDetected,
+ OnAccountCreationFormsDetected)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
-void PasswordGenerationManager::OnFormNotBlacklisted(
- const content::PasswordForm& form) {
+void PasswordGenerationManager::OnFormNotBlacklisted(const PasswordForm& form) {
not_blacklisted_password_form_origins_.push_back(form.origin);
MaybeShowIcon();
}
@@ -203,27 +231,39 @@ void PasswordGenerationManager::OnPasswordGenerationEnabled(bool enabled) {
enabled_ = enabled;
}
+void PasswordGenerationManager::OnAccountCreationFormsDetected(
+ const std::vector<autofill::FormData>& forms) {
+ account_creation_forms_.insert(
+ account_creation_forms_.end(), forms.begin(), forms.end());
+ MaybeShowIcon();
+}
+
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() ||
+ // account creation form, we have confirmed from browser that this form
+ // is not blacklisted by the users, and the Autofill server has marked one
+ // of its field as ACCOUNT_CREATION_PASSWORD.
+ if (!possible_account_creation_form_.get() ||
passwords_.empty() ||
- not_blacklisted_password_form_origins_.empty()) {
+ not_blacklisted_password_form_origins_.empty() ||
+ account_creation_forms_.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;
- }
+ if (!ContainsURL(not_blacklisted_password_form_origins_,
+ possible_account_creation_form_->origin)) {
+ return;
}
+
+ if (!ContainsForm(account_creation_forms_,
+ *possible_account_creation_form_)) {
+ return;
+ }
+
+ passwords_[0].passwordGeneratorButtonElement().setAttribute("style",
+ "display:block");
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::ICON_SHOWN);
}
} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/password_generation_manager.h b/chromium/components/autofill/content/renderer/password_generation_manager.h
index a6154286167..82ce48932d8 100644
--- a/chromium/components/autofill/content/renderer/password_generation_manager.h
+++ b/chromium/components/autofill/content/renderer/password_generation_manager.h
@@ -9,6 +9,7 @@
#include <utility>
#include <vector>
+#include "base/memory/scoped_ptr.h"
#include "content/public/renderer/render_view_observer.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
#include "third_party/WebKit/public/web/WebPasswordGeneratorClient.h"
@@ -19,12 +20,11 @@ class WebCString;
class WebDocument;
}
-namespace content {
-struct PasswordForm;
-}
-
namespace autofill {
+struct FormData;
+struct PasswordForm;
+
// 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).
@@ -51,9 +51,11 @@ class PasswordGenerationManager : public content::RenderViewObserver,
virtual void openPasswordGenerator(WebKit::WebInputElement& element) OVERRIDE;
// Message handlers.
- void OnFormNotBlacklisted(const content::PasswordForm& form);
+ void OnFormNotBlacklisted(const PasswordForm& form);
void OnPasswordAccepted(const base::string16& password);
void OnPasswordGenerationEnabled(bool enabled);
+ void OnAccountCreationFormsDetected(
+ const std::vector<autofill::FormData>& forms);
// Helper function to decide whether we should show password generation icon.
void MaybeShowIcon();
@@ -65,13 +67,17 @@ class PasswordGenerationManager : public content::RenderViewObserver,
bool enabled_;
// Stores the origin of the account creation form we detected.
- GURL account_creation_form_origin_;
+ scoped_ptr<PasswordForm> possible_account_creation_form_;
// 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_;
+ // Stores each password form for which the Autofill server classifies one of
+ // the form's fields as an ACCOUNT_CREATION_PASSWORD.
+ std::vector<autofill::FormData> account_creation_forms_;
+
std::vector<WebKit::WebInputElement> passwords_;
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationManager);
diff --git a/chromium/components/autofill/content/renderer/test_password_autofill_agent.cc b/chromium/components/autofill/content/renderer/test_password_autofill_agent.cc
new file mode 100644
index 00000000000..10252d4f1f5
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/test_password_autofill_agent.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/content/renderer/test_password_autofill_agent.h"
+
+namespace autofill {
+
+TestPasswordAutofillAgent::TestPasswordAutofillAgent(
+ content::RenderView* render_view)
+ : PasswordAutofillAgent(render_view) {}
+
+TestPasswordAutofillAgent::~TestPasswordAutofillAgent() {}
+
+bool TestPasswordAutofillAgent::OriginCanAccessPasswordManager(
+ const WebKit::WebSecurityOrigin& origin) {
+ return true;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/content/renderer/test_password_autofill_agent.h b/chromium/components/autofill/content/renderer/test_password_autofill_agent.h
new file mode 100644
index 00000000000..1045ae01018
--- /dev/null
+++ b/chromium/components/autofill/content/renderer/test_password_autofill_agent.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_CONTENT_RENDERER_TEST_PASSWORD_AUTOFILL_AGENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_TEST_PASSWORD_AUTOFILL_AGENT_H_
+
+#include "components/autofill/content/renderer/password_autofill_agent.h"
+
+namespace autofill {
+
+class TestPasswordAutofillAgent : public PasswordAutofillAgent {
+ public:
+ explicit TestPasswordAutofillAgent(content::RenderView* render_view);
+ virtual ~TestPasswordAutofillAgent();
+
+ private:
+ // Always returns true. This allows browser tests with "data: " URL scheme to
+ // work with the password manager.
+ // PasswordAutofillAgent:
+ virtual bool OriginCanAccessPasswordManager(
+ const WebKit::WebSecurityOrigin& origin) OVERRIDE;
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_TEST_PASSWORD_AUTOFILL_AGENT_H_
diff --git a/chromium/components/autofill/core/DEPS b/chromium/components/autofill/core/DEPS
index caaebde2f13..d4d63b4a380 100644
--- a/chromium/components/autofill/core/DEPS
+++ b/chromium/components/autofill/core/DEPS
@@ -5,6 +5,5 @@ include_rules = [
# 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
index c59b4e28000..77ddfea7270 100644
--- a/chromium/components/autofill/core/browser/DEPS
+++ b/chromium/components/autofill/core/browser/DEPS
@@ -12,10 +12,6 @@ include_rules = [
#
# 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",
diff --git a/chromium/components/autofill/core/browser/address.cc b/chromium/components/autofill/core/browser/address.cc
index 6f7f2a93ac7..e96183c15b0 100644
--- a/chromium/components/autofill/core/browser/address.cc
+++ b/chromium/components/autofill/core/browser/address.cc
@@ -44,8 +44,8 @@ Address& Address::operator=(const Address& address) {
}
base::string16 Address::GetRawInfo(ServerFieldType type) const {
- // TODO(isherman): Is GetStorableType even necessary?
- switch (AutofillType(type).GetStorableType()) {
+ DCHECK_EQ(ADDRESS_HOME, AutofillType(type).group());
+ switch (type) {
case ADDRESS_HOME_LINE1:
return line1_;
@@ -70,8 +70,8 @@ base::string16 Address::GetRawInfo(ServerFieldType type) const {
}
void Address::SetRawInfo(ServerFieldType type, const base::string16& value) {
- // TODO(isherman): Is GetStorableType even necessary?
- switch (AutofillType(type).GetStorableType()) {
+ DCHECK_EQ(ADDRESS_HOME, AutofillType(type).group());
+ switch (type) {
case ADDRESS_HOME_LINE1:
line1_ = value;
break;
diff --git a/chromium/components/autofill/core/browser/android/component_jni_registrar.cc b/chromium/components/autofill/core/browser/android/component_jni_registrar.cc
deleted file mode 100644
index f3aac1f02ad..00000000000
--- a/chromium/components/autofill/core/browser/android/component_jni_registrar.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2013 The Chromium Authors. 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
deleted file mode 100644
index 6cc0900cfdd..00000000000
--- a/chromium/components/autofill/core/browser/android/component_jni_registrar.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2013 The Chromium 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
deleted file mode 100644
index b00ed61df29..00000000000
--- a/chromium/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java
+++ /dev/null
@@ -1,319 +0,0 @@
-// Copyright 2013 The Chromium 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/autocheckout_bubble_state.h b/chromium/components/autofill/core/browser/autocheckout_bubble_state.h
deleted file mode 100644
index b015cb01d30..00000000000
--- a/chromium/components/autofill/core/browser/autocheckout_bubble_state.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2013 The Chromium 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/autofill_common_test.cc b/chromium/components/autofill/core/browser/autofill_common_test.cc
index f477b25c2e6..2187d8b7bea 100644
--- a/chromium/components/autofill/core/browser/autofill_common_test.cc
+++ b/chromium/components/autofill/core/browser/autofill_common_test.cc
@@ -128,12 +128,25 @@ CreditCard GetCreditCard() {
return credit_card;
}
+CreditCard GetCreditCard2() {
+ CreditCard credit_card(base::GenerateGUID(), "https://www.example.com");
+ SetCreditCardInfo(
+ &credit_card, "Someone Else", "378282246310005" /* AmEx */, "07", "2019");
+ return credit_card;
+}
+
CreditCard GetVerifiedCreditCard() {
CreditCard credit_card(GetCreditCard());
credit_card.set_origin(kSettingsOrigin);
return credit_card;
}
+CreditCard GetVerifiedCreditCard2() {
+ CreditCard credit_card(GetCreditCard2());
+ 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,
diff --git a/chromium/components/autofill/core/browser/autofill_common_test.h b/chromium/components/autofill/core/browser/autofill_common_test.h
index b0818f70df7..8d6a56ff0fa 100644
--- a/chromium/components/autofill/core/browser/autofill_common_test.h
+++ b/chromium/components/autofill/core/browser/autofill_common_test.h
@@ -46,9 +46,15 @@ AutofillProfile GetVerifiedProfile2();
// Returns a credit card full of dummy info.
CreditCard GetCreditCard();
+// Returns a credit card full of dummy info, different to the above.
+CreditCard GetCreditCard2();
+
// Returns a verified credit card full of dummy info.
CreditCard GetVerifiedCreditCard();
+// Returns a verified credit card full of dummy info, different to the above.
+CreditCard GetVerifiedCreditCard2();
+
// 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.
diff --git a/chromium/components/autofill/core/browser/autofill_download_unittest.cc b/chromium/components/autofill/core/browser/autofill_download_unittest.cc
index 9035c9cdafa..834772f9d04 100644
--- a/chromium/components/autofill/core/browser/autofill_download_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_download_unittest.cc
@@ -164,7 +164,7 @@ TEST_F(AutofillDownloadTest, QueryAndUploadTest) {
field.form_control_type = "submit";
form.fields.push_back(field);
- FormStructure *form_structure = new FormStructure(form, std::string());
+ FormStructure *form_structure = new FormStructure(form);
ScopedVector<FormStructure> form_structures;
form_structures.push_back(form_structure);
@@ -190,7 +190,7 @@ TEST_F(AutofillDownloadTest, QueryAndUploadTest) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure = new FormStructure(form, std::string());
+ form_structure = new FormStructure(form);
form_structures.push_back(form_structure);
// Request with id 0.
@@ -283,7 +283,7 @@ TEST_F(AutofillDownloadTest, QueryAndUploadTest) {
field.name = ASCIIToUTF16("address2");
field.form_control_type = "text";
form.fields.push_back(field);
- form_structure = new FormStructure(form, std::string());
+ form_structure = new FormStructure(form);
form_structures.push_back(form_structure);
// Request with id 3.
@@ -354,7 +354,7 @@ TEST_F(AutofillDownloadTest, CacheQueryTest) {
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
- FormStructure *form_structure = new FormStructure(form, std::string());
+ FormStructure *form_structure = new FormStructure(form);
ScopedVector<FormStructure> form_structures0;
form_structures0.push_back(form_structure);
@@ -362,7 +362,7 @@ TEST_F(AutofillDownloadTest, CacheQueryTest) {
field.label = ASCIIToUTF16("email");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
- form_structure = new FormStructure(form, std::string());
+ form_structure = new FormStructure(form);
ScopedVector<FormStructure> form_structures1;
form_structures1.push_back(form_structure);
@@ -371,7 +371,7 @@ TEST_F(AutofillDownloadTest, CacheQueryTest) {
field.label = ASCIIToUTF16("email2");
field.name = ASCIIToUTF16("email2");
form.fields.push_back(field);
- form_structure = new FormStructure(form, std::string());
+ form_structure = new FormStructure(form);
ScopedVector<FormStructure> form_structures2;
form_structures2.push_back(form_structure);
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.cc b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
index 95119bfad99..ca6696e32a2 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.cc
@@ -36,7 +36,7 @@ AutofillExternalDelegate::AutofillExternalDelegate(
display_warning_if_disabled_(false),
has_autofill_suggestion_(false),
has_shown_autofill_popup_for_current_edit_(false),
- registered_keyboard_listener_with_(NULL),
+ registered_key_press_event_callback_with_(NULL),
weak_ptr_factory_(this) {
DCHECK(autofill_manager);
}
@@ -162,10 +162,12 @@ void AutofillExternalDelegate::SetCurrentDataListValues(
}
void AutofillExternalDelegate::OnPopupShown(
- content::KeyboardListener* listener) {
- if (!registered_keyboard_listener_with_) {
- registered_keyboard_listener_with_ = web_contents_->GetRenderViewHost();
- registered_keyboard_listener_with_->AddKeyboardListener(listener);
+ content::RenderWidgetHost::KeyPressEventCallback* callback) {
+ if (callback && !registered_key_press_event_callback_with_) {
+ registered_key_press_event_callback_with_ =
+ web_contents_->GetRenderViewHost();
+ registered_key_press_event_callback_with_->AddKeyPressEventCallback(
+ *callback);
}
autofill_manager_->OnDidShowAutofillSuggestions(
@@ -174,14 +176,19 @@ void AutofillExternalDelegate::OnPopupShown(
}
void AutofillExternalDelegate::OnPopupHidden(
- content::KeyboardListener* listener) {
- if ((!web_contents_->IsBeingDestroyed()) &&
- (registered_keyboard_listener_with_ ==
+ content::RenderWidgetHost::KeyPressEventCallback* callback) {
+ if (callback && (!web_contents_->IsBeingDestroyed()) &&
+ (registered_key_press_event_callback_with_ ==
web_contents_->GetRenderViewHost())) {
- web_contents_->GetRenderViewHost()->RemoveKeyboardListener(listener);
+ web_contents_->GetRenderViewHost()->RemoveKeyPressEventCallback(*callback);
}
- registered_keyboard_listener_with_ = NULL;
+ registered_key_press_event_callback_with_ = NULL;
+}
+
+bool AutofillExternalDelegate::ShouldRepostEvent(const ui::MouseEvent& event) {
+ NOTREACHED();
+ return true;
}
void AutofillExternalDelegate::DidSelectSuggestion(int identifier) {
diff --git a/chromium/components/autofill/core/browser/autofill_external_delegate.h b/chromium/components/autofill/core/browser/autofill_external_delegate.h
index 3362ff8e19b..0a0093e9ed1 100644
--- a/chromium/components/autofill/core/browser/autofill_external_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_external_delegate.h
@@ -47,8 +47,11 @@ class AutofillExternalDelegate
virtual ~AutofillExternalDelegate();
// AutofillPopupDelegate implementation.
- virtual void OnPopupShown(content::KeyboardListener* listener) OVERRIDE;
- virtual void OnPopupHidden(content::KeyboardListener* listener) OVERRIDE;
+ virtual void OnPopupShown(
+ content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE;
+ virtual void OnPopupHidden(
+ content::RenderWidgetHost::KeyPressEventCallback* callback) OVERRIDE;
+ virtual bool ShouldRepostEvent(const ui::MouseEvent& event) OVERRIDE;
virtual void DidSelectSuggestion(int identifier) OVERRIDE;
virtual void DidAcceptSuggestion(const base::string16& value,
int identifier) OVERRIDE;
@@ -167,9 +170,9 @@ class AutofillExternalDelegate
// 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 RenderViewHost that this object has been registered with as a key press
+ // event callback.
+ content::RenderViewHost* registered_key_press_event_callback_with_;
// The current data list values.
std::vector<base::string16> data_list_values_;
diff --git a/chromium/components/autofill/core/browser/autofill_manager.cc b/chromium/components/autofill/core/browser/autofill_manager.cc
index 4c66699b819..6f99772f1ec 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager.cc
@@ -20,8 +20,6 @@
#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"
@@ -129,7 +127,7 @@ bool SectionIsAutofilled(const FormStructure& form_structure,
}
bool FormIsHTTPS(const FormStructure& form) {
- return form.source_url().SchemeIs(chrome::kHttpsScheme);
+ return form.source_url().SchemeIs(content::kHttpsScheme);
}
// Uses the existing personal data in |profiles| and |credit_cards| to determine
@@ -148,16 +146,21 @@ void DeterminePossibleFieldTypesForUpload(
// profile or credit card, identify any stored types that match the value.
for (size_t i = 0; i < submitted_form->field_count(); ++i) {
AutofillField* field = submitted_form->field(i);
- 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 it's a password field, set the type directly.
+ if (field->form_control_type == "password") {
+ matching_types.insert(autofill::PASSWORD);
+ } else {
+ base::string16 value = CollapseWhitespace(field->value, false);
+ 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())
@@ -190,7 +193,6 @@ AutofillManager::AutofillManager(
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),
@@ -251,24 +253,16 @@ bool AutofillManager::OnFormSubmitted(const FormData& form,
// Let Autocomplete know as well.
autocomplete_history_manager_->OnFormSubmitted(form);
- if (!IsAutofillEnabled())
- return false;
+ // Grab a copy of the form data.
+ scoped_ptr<FormStructure> submitted_form(new FormStructure(form));
- if (driver_->GetWebContents()->GetBrowserContext()->IsOffTheRecord())
+ if (!ShouldUploadForm(*submitted_form))
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;
@@ -276,9 +270,7 @@ bool AutofillManager::OnFormSubmitted(const FormData& 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())
+ if (submitted_form->IsAutofillable(true))
ImportFormData(*submitted_form);
// Only upload server statistics and UMA metrics if at least some local data
@@ -328,34 +320,14 @@ 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;
-
+ // If new forms were added dynamically, treat as a new page.
+ if (is_post_document_load)
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);
@@ -378,7 +350,6 @@ void AutofillManager::OnTextFieldDidChange(const FormData& form,
return;
if (!user_did_type_) {
- autocheckout_manager_.set_should_show_bubble(false);
user_did_type_ = true;
metric_logger_->LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE);
}
@@ -403,9 +374,6 @@ void AutofillManager::OnQueryFormFieldAutofill(int query_id,
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;
@@ -616,7 +584,6 @@ void AutofillManager::OnHideAutofillUI() {
return;
manager_delegate_->HideAutofillPopup();
- manager_delegate_->HideAutocheckoutBubble();
}
void AutofillManager::RemoveAutofillProfileOrCreditCard(int unique_id) {
@@ -649,16 +616,6 @@ 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;
@@ -698,11 +655,10 @@ void AutofillManager::OnRequestAutocomplete(
return;
}
- base::Callback<void(const FormStructure*, const std::string&)> callback =
+ base::Callback<void(const FormStructure*)> callback =
base::Bind(&AutofillManager::ReturnAutocompleteData,
weak_ptr_factory_.GetWeakPtr());
- ShowRequestAutocompleteDialog(
- form, frame_url, autofill::DIALOG_TYPE_REQUEST_AUTOCOMPLETE, callback);
+ ShowRequestAutocompleteDialog(form, frame_url, callback);
}
void AutofillManager::ReturnAutocompleteResult(
@@ -721,9 +677,7 @@ void AutofillManager::ReturnAutocompleteResult(
form_data));
}
-void AutofillManager::ReturnAutocompleteData(
- const FormStructure* result,
- const std::string& unused_transaction_id) {
+void AutofillManager::ReturnAutocompleteData(const FormStructure* result) {
if (!result) {
ReturnAutocompleteResult(WebFormElement::AutocompleteResultErrorCancel,
FormData());
@@ -735,27 +689,14 @@ void AutofillManager::ReturnAutocompleteData(
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)
+ // Forward form structures to the password generation manager to detect
+ // account creation forms.
+ manager_delegate_->DetectAccountCreationForms(form_structures_.get());
// If the corresponding flag is set, annotate forms with the predicted types.
driver_->SendAutofillTypePredictionsToRenderer(form_structures_.get());
@@ -765,22 +706,6 @@ 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);
}
@@ -796,8 +721,9 @@ void AutofillManager::ImportFormData(const FormStructure& submitted_form) {
manager_delegate_->ConfirmSaveCreditCard(
*metric_logger_,
*imported_credit_card,
- base::Bind(&PersonalDataManager::SaveImportedCreditCard,
- base::Unretained(personal_data_), *imported_credit_card));
+ base::Bind(
+ base::IgnoreResult(&PersonalDataManager::SaveImportedCreditCard),
+ base::Unretained(personal_data_), *imported_credit_card));
}
}
@@ -819,26 +745,6 @@ void AutofillManager::UploadFormDataAsyncCallback(
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;
@@ -856,9 +762,59 @@ void AutofillManager::UploadFormData(const FormStructure& submitted_form) {
ServerFieldTypeSet non_empty_types;
personal_data_->GetNonEmptyTypes(&non_empty_types);
+ // Always add PASSWORD to |non_empty_types| so that if |submitted_form|
+ // contains a password field it will be uploaded to the server. If
+ // |submitted_form| doesn't contain a password field, there is no side
+ // effect from adding PASSWORD to |non_empty_types|.
+ non_empty_types.insert(autofill::PASSWORD);
download_manager_->StartUploadRequest(submitted_form, was_autofilled,
- non_empty_types);
+ non_empty_types);
+}
+
+bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) {
+ FormStructure form_structure(form);
+
+ if (!ShouldUploadForm(form_structure))
+ return false;
+
+ if (!form_structure.ShouldBeCrowdsourced())
+ return false;
+
+ // TODO(gcasto): Check that PasswordGeneration is enabled?
+
+ // Find the first password field to label. We don't try to label anything
+ // else.
+ bool found_password_field = false;
+ for (size_t i = 0; i < form_structure.field_count(); ++i) {
+ AutofillField* field = form_structure.field(i);
+
+ ServerFieldTypeSet types;
+ if (!found_password_field && field->form_control_type == "password") {
+ types.insert(ACCOUNT_CREATION_PASSWORD);
+ found_password_field = true;
+ } else {
+ types.insert(UNKNOWN_TYPE);
+ }
+ field->set_possible_types(types);
+ }
+ DCHECK(found_password_field);
+
+ // Only one field type should be present.
+ ServerFieldTypeSet available_field_types;
+ available_field_types.insert(ACCOUNT_CREATION_PASSWORD);
+
+ // Force uploading as these events are relatively rare and we want to make
+ // sure to receive them. It also makes testing easier if these requests
+ // always pass.
+ form_structure.set_upload_required(UPLOAD_REQUIRED);
+
+ if (!download_manager_)
+ return false;
+
+ return download_manager_->StartUploadRequest(form_structure,
+ false /* was_autofilled */,
+ available_field_types);
}
void AutofillManager::Reset() {
@@ -883,7 +839,6 @@ AutofillManager::AutofillManager(AutofillDriver* driver,
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),
@@ -983,7 +938,7 @@ bool AutofillManager::GetCachedFormAndField(const FormData& 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)) {
+ !FormStructure(form).ShouldBeParsed(false)) {
return false;
}
@@ -1030,8 +985,7 @@ bool AutofillManager::UpdateCachedForm(const FormData& live_form,
return false;
// Add the new or updated form to our cache.
- form_structures_.push_back(
- new FormStructure(live_form, GetAutocheckoutURLPrefix()));
+ form_structures_.push_back(new FormStructure(live_form));
*updated_form = *form_structures_.rbegin();
(*updated_form)->DetermineHeuristicTypes(*metric_logger_);
@@ -1107,11 +1061,9 @@ void AutofillManager::GetCreditCardSuggestions(
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));
+ scoped_ptr<FormStructure> form_structure(new FormStructure(*iter));
if (!form_structure->ShouldBeParsed(false))
continue;
@@ -1125,13 +1077,7 @@ void AutofillManager::ParseForms(const std::vector<FormData>& forms) {
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_) {
+ if (!form_structures_.empty() && download_manager_) {
// Query the server if we have at least one of the forms were parsed.
download_manager_->StartQueryRequest(form_structures_.get(),
*metric_logger_);
@@ -1208,6 +1154,14 @@ void AutofillManager::UnpackGUIDs(int id,
*profile_guid = IDToGUID(profile_id);
}
+void AutofillManager::ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const base::Callback<void(const FormStructure*)>& callback) {
+ manager_delegate_->ShowRequestAutocompleteDialog(
+ form, source_url, callback);
+}
+
void AutofillManager::UpdateInitialInteractionTimestamp(
const TimeTicks& interaction_timestamp) {
if (initial_interaction_timestamp_.is_null() ||
@@ -1216,4 +1170,18 @@ void AutofillManager::UpdateInitialInteractionTimestamp(
}
}
+bool AutofillManager::ShouldUploadForm(const FormStructure& form) {
+ if (!IsAutofillEnabled())
+ return false;
+
+ if (driver_->GetWebContents()->GetBrowserContext()->IsOffTheRecord())
+ return false;
+
+ // Disregard forms that we wouldn't ever autofill in the first place.
+ if (!form.ShouldBeParsed(true))
+ return false;
+
+ return true;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_manager.h b/chromium/components/autofill/core/browser/autofill_manager.h
index b9e4d311522..a149e2856b9 100644
--- a/chromium/components/autofill/core/browser/autofill_manager.h
+++ b/chromium/components/autofill/core/browser/autofill_manager.h
@@ -19,13 +19,11 @@
#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"
@@ -110,14 +108,6 @@ class AutofillManager : public AutofillDownloadManager::Observer {
// 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();
@@ -167,23 +157,19 @@ class AutofillManager : public AutofillDownloadManager::Observer {
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);
+ // Try and upload |form|. This differs from OnFormSubmitted() in a few ways.
+ // - This function will only label the first <input type="password"> field
+ // as ACCOUNT_CREATION_PASSWORD. Other fields will stay unlabeled, as they
+ // should have been labeled during the upload for OnFormSubmitted().
+ // - This function does not assume that |form| is being uploaded during
+ // the same browsing session as it was originally submitted (as we may
+ // not have the necessary information to classify the form at that time)
+ // so it bypasses the cache and doesn't log the same quality UMA metrics.
+ bool UploadPasswordGenerationForm(const FormData& form);
// Resets cache.
virtual void Reset();
- autofill::AutocheckoutManager* autocheckout_manager() {
- return &autocheckout_manager_;
- }
-
protected:
// Test code should prefer to use this constructor.
AutofillManager(AutofillDriver* driver,
@@ -227,23 +213,24 @@ class AutofillManager : public AutofillDownloadManager::Observer {
return external_delegate_;
}
+ // Causes the dialog for request autocomplete feature to be shown.
+ virtual void ShowRequestAutocompleteDialog(
+ const FormData& form,
+ const GURL& source_url,
+ const base::Callback<void(const FormStructure*)>& callback);
+
// 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;
+ void ReturnAutocompleteData(const FormStructure* result);
// Fills |host| with the RenderViewHost for this tab.
// Returns false if Autofill is disabled or if the host is unavailable.
@@ -310,6 +297,9 @@ class AutofillManager : public AutofillDownloadManager::Observer {
void UpdateInitialInteractionTimestamp(
const base::TimeTicks& interaction_timestamp);
+ // Shared code to determine if |form| should be uploaded.
+ bool ShouldUploadForm(const FormStructure& form);
+
// Provides driver-level context to the shared code of the component. Must
// outlive this object.
AutofillDriver* driver_;
@@ -333,9 +323,6 @@ class AutofillManager : public AutofillDownloadManager::Observer {
// 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?
diff --git a/chromium/components/autofill/core/browser/autofill_manager_delegate.h b/chromium/components/autofill/core/browser/autofill_manager_delegate.h
index 374c105fd5d..cadd1f7a283 100644
--- a/chromium/components/autofill/core/browser/autofill_manager_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_manager_delegate.h
@@ -11,12 +11,6 @@
#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;
@@ -36,17 +30,7 @@ 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,
-};
+struct PasswordForm;
// A delegate interface that needs to be supplied to AutofillManager
// by the embedder.
@@ -66,23 +50,9 @@ class AutofillManagerDelegate {
// 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;
@@ -93,26 +63,11 @@ class AutofillManagerDelegate {
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;
+ const base::Callback<void(const FormStructure*)>& callback) = 0;
// Shows an Autofill popup with the given |values|, |labels|, |icons|, and
// |identifiers| for the element at |element_bounds|. |delegate| will be
@@ -137,11 +92,10 @@ class AutofillManagerDelegate {
// 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;
+ // Pass the form structures to the password generation manager to detect
+ // account creation forms.
+ virtual void DetectAccountCreationForms(
+ const std::vector<autofill::FormStructure*>& forms) = 0;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
index 7aeb81ec6d5..a56e86bfafc 100644
--- a/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -84,7 +84,7 @@ class TestPersonalDataManager : public PersonalDataManager {
return NULL;
}
- MOCK_METHOD1(SaveImportedProfile, void(const AutofillProfile&));
+ MOCK_METHOD1(SaveImportedProfile, std::string(const AutofillProfile&));
AutofillProfile* GetProfileWithGUID(const char* guid) {
for (std::vector<AutofillProfile *>::iterator it = web_profiles_.begin();
@@ -479,14 +479,6 @@ class TestAutofillManager : public AutofillManager {
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_;
@@ -533,14 +525,6 @@ class TestAutofillManager : public AutofillManager {
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() {
@@ -597,35 +581,6 @@ class TestAutofillManager : public AutofillManager {
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_;
@@ -636,7 +591,6 @@ class TestAutofillManager : public AutofillManager {
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
- std::string autocheckout_url_prefix_;
std::string submitted_form_signature_;
std::vector<ServerFieldTypeSet> expected_submitted_field_types_;
@@ -839,14 +793,6 @@ class AutofillManagerTest : public ChromeRenderViewHostTestHarness {
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_;
@@ -861,7 +807,7 @@ class AutofillManagerTest : public ChromeRenderViewHostTestHarness {
class TestFormStructure : public FormStructure {
public:
explicit TestFormStructure(const FormData& form)
- : FormStructure(form, std::string()) {}
+ : FormStructure(form) {}
virtual ~TestFormStructure() {}
void SetFieldTypes(const std::vector<ServerFieldType>& heuristic_types,
@@ -883,19 +829,6 @@ class TestFormStructure : public FormStructure {
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) {
@@ -930,79 +863,6 @@ TEST_F(AutofillManagerTest, GetProfileSuggestionsEmptyValue) {
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) {
@@ -2683,7 +2543,7 @@ TEST_F(AutofillManagerTest, FormSubmittedWithDifferentFields) {
FormsSeen(forms);
// Cache the expected form signature.
- std::string signature = FormStructure(form, std::string()).FormSignature();
+ std::string signature = FormStructure(form).FormSignature();
// Change the structure of the form prior to submission.
// Websites would typically invoke JavaScript either on page load or on form
@@ -3037,6 +2897,12 @@ TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUpload) {
form.fields.push_back(field);
expected_types.push_back(types);
+ test::CreateTestFormField("", "40", "mypassword", "password", &field);
+ types.clear();
+ types.insert(PASSWORD);
+ form.fields.push_back(field);
+ expected_types.push_back(types);
+
autofill_manager_->set_expected_submitted_field_types(expected_types);
FormSubmitted(form);
}
@@ -3108,39 +2974,22 @@ namespace {
class MockAutofillManagerDelegate : public TestAutofillManagerDelegate {
public:
- MockAutofillManagerDelegate()
- : autocheckout_bubble_shown_(false) {}
+ MockAutofillManagerDelegate() {}
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");
+ const base::Callback<void(const FormStructure*)>& callback) OVERRIDE {
+ callback.Run(user_supplied_data_.get());
}
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);
@@ -3148,65 +2997,6 @@ class MockAutofillManagerDelegate : public TestAutofillManagerDelegate {
} // 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;
@@ -3219,53 +3009,4 @@ TEST_F(AutofillManagerTest, TestExternalDelegate) {
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
index e007a59d905..ef60bf7b728 100644
--- a/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -88,7 +88,8 @@ class PersonalDataManagerMock : public PersonalDataManager {
void Reset();
// PersonalDataManager:
- virtual void SaveImportedProfile(const AutofillProfile& profile) OVERRIDE;
+ virtual std::string SaveImportedProfile(
+ const AutofillProfile& profile) OVERRIDE;
virtual const std::vector<AutofillProfile*>& web_profiles() const OVERRIDE;
private:
@@ -108,11 +109,14 @@ void PersonalDataManagerMock::Reset() {
profiles_.clear();
}
-void PersonalDataManagerMock::SaveImportedProfile(
+std::string PersonalDataManagerMock::SaveImportedProfile(
const AutofillProfile& profile) {
std::vector<AutofillProfile> profiles;
- if (!MergeProfile(profile, profiles_.get(), "en-US", &profiles))
+ std::string merged_guid =
+ MergeProfile(profile, profiles_.get(), "en-US", &profiles);
+ if (merged_guid == profile.guid())
profiles_.push_back(new AutofillProfile(profile));
+ return merged_guid;
}
const std::vector<AutofillProfile*>& PersonalDataManagerMock::web_profiles()
@@ -213,7 +217,7 @@ void AutofillMergeTest::MergeProfiles(const std::string& profiles,
// 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());
+ FormStructure form_structure(form);
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.
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.cc b/chromium/components/autofill/core/browser/autofill_metrics.cc
index 57743c3b7fd..930783d93b5 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/form_structure.h"
@@ -55,6 +56,7 @@ enum FieldTypeGroupForMetrics {
CREDIT_CARD_NUMBER,
CREDIT_CARD_DATE,
CREDIT_CARD_TYPE,
+ PASSWORD,
NUM_FIELD_TYPE_GROUPS_FOR_METRICS
};
@@ -84,13 +86,14 @@ int GetFieldTypeGroupMetric(const ServerFieldType field_type,
const int num_possible_metrics) {
DCHECK_LT(metric, num_possible_metrics);
- FieldTypeGroupForMetrics group;
+ FieldTypeGroupForMetrics group = AMBIGUOUS;
switch (AutofillType(field_type).group()) {
case ::autofill::NO_GROUP:
group = AMBIGUOUS;
break;
case ::autofill::NAME:
+ case ::autofill::NAME_BILLING:
group = NAME;
break;
@@ -99,7 +102,8 @@ int GetFieldTypeGroupMetric(const ServerFieldType field_type,
break;
case ::autofill::ADDRESS_HOME:
- switch (field_type) {
+ case ::autofill::ADDRESS_BILLING:
+ switch (AutofillType(field_type).GetStorableType()) {
case ADDRESS_HOME_LINE1:
group = ADDRESS_LINE_1;
break;
@@ -121,6 +125,7 @@ int GetFieldTypeGroupMetric(const ServerFieldType field_type,
default:
NOTREACHED();
group = AMBIGUOUS;
+ break;
}
break;
@@ -129,6 +134,7 @@ int GetFieldTypeGroupMetric(const ServerFieldType field_type,
break;
case ::autofill::PHONE_HOME:
+ case ::autofill::PHONE_BILLING:
group = PHONE;
break;
@@ -142,14 +148,24 @@ int GetFieldTypeGroupMetric(const ServerFieldType field_type,
break;
case ::autofill::CREDIT_CARD_TYPE:
group = CREDIT_CARD_TYPE;
- default:
+ break;
+ case ::autofill::CREDIT_CARD_EXP_MONTH:
+ case ::autofill::CREDIT_CARD_EXP_2_DIGIT_YEAR:
+ case ::autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR:
+ case ::autofill::CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
+ case ::autofill::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
group = CREDIT_CARD_DATE;
+ break;
+ default:
+ NOTREACHED();
+ group = AMBIGUOUS;
+ break;
}
break;
- default:
- NOTREACHED();
- group = AMBIGUOUS;
+ case ::autofill::PASSWORD_FIELD:
+ group = PASSWORD;
+ break;
}
// Interpolate the |metric| with the |group|, so that all metrics for a given
@@ -157,20 +173,6 @@ int GetFieldTypeGroupMetric(const ServerFieldType field_type,
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) {
@@ -184,9 +186,8 @@ std::string WalletApiMetricToString(
return "GetWalletItems";
case AutofillMetrics::SAVE_TO_WALLET:
return "SaveToWallet";
- case AutofillMetrics::SEND_STATUS:
- return "SendStatus";
case AutofillMetrics::UNKNOWN_API_CALL:
+ case AutofillMetrics::NUM_WALLET_API_CALLS:
NOTREACHED();
return "UnknownApiCall";
}
@@ -276,7 +277,7 @@ void LogServerExperimentId(const std::string& histogram_name,
ServerExperiment metric = UNKNOWN_EXPERIMENT;
const std::string default_experiment_name =
- FormStructure(FormData(), std::string()).server_experiment_id();
+ FormStructure(FormData()).server_experiment_id();
if (experiment_id.empty())
metric = NO_EXPERIMENT;
else if (experiment_id == "ar06")
@@ -324,20 +325,6 @@ 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);
@@ -346,47 +333,36 @@ void AutofillMetrics::LogCreditCardInfoBarMetric(InfoBarMetric metric) const {
}
void AutofillMetrics::LogDialogDismissalState(
- autofill::DialogType dialog_type,
DialogDismissalState state) const {
- std::string name = GetPrefixForDialogType(dialog_type) + ".DismissalState";
- LogUMAHistogramEnumeration(name, state, NUM_DIALOG_DISMISSAL_STATES);
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.DismissalState",
+ 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);
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.InitialUserState",
+ 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);
+ LogUMAHistogramTimes("RequestAutocomplete.UiLatencyToShow", 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::LogDialogPopupEvent(DialogPopupEvent event) const {
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.PopupInDialog",
+ 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);
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.Security",
+ 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:
@@ -398,20 +374,19 @@ void AutofillMetrics::LogDialogUiDuration(
break;
}
- LogUMAHistogramLongTimes(prefix + ".UiDuration", duration);
- LogUMAHistogramLongTimes(prefix + ".UiDuration." + suffix, duration);
+ LogUMAHistogramLongTimes("RequestAutocomplete.UiDuration", duration);
+ LogUMAHistogramLongTimes("RequestAutocomplete.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::LogDialogUiEvent(DialogUiEvent event) const {
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.UiEvents", 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::LogWalletErrorMetric(WalletErrorMetric metric) const {
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.WalletErrors", metric,
+ NUM_WALLET_ERROR_METRICS);
}
void AutofillMetrics::LogWalletApiCallDuration(
@@ -421,54 +396,20 @@ void AutofillMetrics::LogWalletApiCallDuration(
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::LogWalletMalformedResponseMetric(
+ WalletApiCallMetric metric) const {
+ UMA_HISTOGRAM_ENUMERATION("Wallet.MalformedResponse", metric,
+ NUM_WALLET_API_CALLS);
}
-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::LogWalletRequiredActionMetric(
+ WalletRequiredActionMetric required_action) const {
+ UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.WalletRequiredActions",
+ required_action, NUM_WALLET_REQUIRED_ACTIONS);
}
-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::LogWalletResponseCode(int response_code) const {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Wallet.ResponseCode", response_code);
}
void AutofillMetrics::LogDeveloperEngagementMetric(
diff --git a/chromium/components/autofill/core/browser/autofill_metrics.h b/chromium/components/autofill/core/browser/autofill_metrics.h
index d6ae790b392..c9f56a9cc75 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics.h
+++ b/chromium/components/autofill/core/browser/autofill_metrics.h
@@ -20,47 +20,6 @@ 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,
@@ -119,8 +78,8 @@ class AutofillMetrics {
NUM_DIALOG_INITIAL_USER_STATE_METRICS
};
- // Events related to the Autofill popup shown in a requestAutocomplete or
- // Autocheckout dialog.
+ // Events related to the Autofill popup shown in a requestAutocomplete
+ // dialog.
enum DialogPopupEvent {
// An Autofill popup was shown.
DIALOG_POPUP_SHOWN = 0,
@@ -163,21 +122,21 @@ class AutofillMetrics {
DIALOG_UI_SIGNIN_SHOWN,
// Selecting a different item from a suggestion menu dropdown:
- DIALOG_UI_EMAIL_SELECTED_SUGGESTION_CHANGED,
+ DEPRECATED_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,
+ DEPRECATED_DIALOG_UI_EMAIL_EDIT_UI_SHOWN,
+ DEPRECATED_DIALOG_UI_BILLING_EDIT_UI_SHOWN,
+ DEPRECATED_DIALOG_UI_CC_BILLING_EDIT_UI_SHOWN,
+ DEPRECATED_DIALOG_UI_SHIPPING_EDIT_UI_SHOWN,
+ DEPRECATED_DIALOG_UI_CC_EDIT_UI_SHOWN,
// Adding a new item in a section of the dialog:
- DIALOG_UI_EMAIL_ITEM_ADDED,
+ DEPRECATED_DIALOG_UI_EMAIL_ITEM_ADDED,
DIALOG_UI_BILLING_ITEM_ADDED,
DIALOG_UI_CC_BILLING_ITEM_ADDED,
DIALOG_UI_SHIPPING_ITEM_ADDED,
@@ -296,8 +255,8 @@ class AutofillMetrics {
AUTHENTICATE_INSTRUMENT,
GET_FULL_WALLET,
GET_WALLET_ITEMS,
- SEND_STATUS,
SAVE_TO_WALLET,
+ NUM_WALLET_API_CALLS
};
// For measuring the frequency of errors while communicating with the Wallet
@@ -362,22 +321,9 @@ class AutofillMetrics {
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(
@@ -402,66 +348,54 @@ class AutofillMetrics {
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;
+ // Logs |state| to the dismissal states histogram.
+ virtual void LogDialogDismissalState(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|.
+ // interacting with a dialog.
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 the time elapsed between the dialog being shown and when it is ready
+ // for user interaction.
+ virtual void LogDialogLatencyToShow(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 |event| to the popup events histogram.
+ virtual void LogDialogPopupEvent(DialogPopupEvent event) const;
- // Logs |metric| to the security metrics histogram for |dialog_type|.
- virtual void LogDialogSecurityMetric(autofill::DialogType dialog_type,
- DialogSecurityMetric metric) const;
+ // Logs |metric| to the security metrics histogram.
+ virtual void LogDialogSecurityMetric(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.
+ // This should be called when the Autofill dialog 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 |event| to the UI events histogram.
+ virtual void LogDialogUiEvent(DialogUiEvent event) const;
- // Logs |metric| to the Wallet errors histogram for |dialog_type|.
- virtual void LogWalletErrorMetric(autofill::DialogType dialog_type,
- WalletErrorMetric metric) const;
+ // Logs |metric| to the Wallet errors histogram.
+ virtual void LogWalletErrorMetric(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|.
+ // Logs that the Wallet API call corresponding to |metric| was malformed.
+ virtual void LogWalletMalformedResponseMetric(
+ WalletApiCallMetric metric) const;
+
+ // Logs |required_action| to the required actions histogram.
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;
+ // Logs HTTP response codes recieved by wallet client.
+ virtual void LogWalletResponseCode(int response_code) 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.
diff --git a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
index ece18d8dfd3..c12b2ff5f54 100644
--- a/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -16,7 +16,6 @@
#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"
@@ -126,7 +125,7 @@ class TestPersonalDataManager : public PersonalDataManager {
}
MOCK_METHOD1(SaveImportedCreditCard,
- void(const CreditCard& imported_credit_card));
+ std::string(const CreditCard& imported_credit_card));
private:
void CreateTestAutofillProfiles(ScopedVector<AutofillProfile>* profiles) {
@@ -154,8 +153,7 @@ class TestPersonalDataManager : public PersonalDataManager {
class TestFormStructure : public FormStructure {
public:
- explicit TestFormStructure(const FormData& form)
- : FormStructure(form, std::string()) {}
+ explicit TestFormStructure(const FormData& form) : FormStructure(form) {}
virtual ~TestFormStructure() {}
void SetFieldTypes(const std::vector<ServerFieldType>& heuristic_types,
@@ -196,10 +194,6 @@ class TestAutofillManager : public AutofillManager {
}
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) {
@@ -325,8 +319,9 @@ scoped_ptr<ConfirmInfoBarDelegate> AutofillMetricsTest::CreateDelegate(
CreditCard credit_card;
return AutofillCCInfoBarDelegate::Create(
metric_logger,
- base::Bind(&TestPersonalDataManager::SaveImportedCreditCard,
- base::Unretained(personal_data_.get()), credit_card));
+ base::Bind(
+ base::IgnoreResult(&TestPersonalDataManager::SaveImportedCreditCard),
+ base::Unretained(personal_data_.get()), credit_card));
}
// Test that we log quality metrics appropriately.
@@ -1176,11 +1171,9 @@ TEST_F(AutofillMetricsTest, ServerQueryExperimentIdForQuery) {
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.
@@ -1196,7 +1189,6 @@ TEST_F(AutofillMetricsTest, ServerQueryExperimentIdForQuery) {
FormStructure::ParseQueryResponse(
"<autofillqueryresponse experimentid=\"ar1\"></autofillqueryresponse>",
std::vector<FormStructure*>(),
- &page_meta_data,
metric_logger);
}
diff --git a/chromium/components/autofill/core/browser/autofill_popup_delegate.h b/chromium/components/autofill/core/browser/autofill_popup_delegate.h
index ec9a2207bc6..86b5eff4266 100644
--- a/chromium/components/autofill/core/browser/autofill_popup_delegate.h
+++ b/chromium/components/autofill/core/browser/autofill_popup_delegate.h
@@ -6,9 +6,10 @@
#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_POPUP_DELEGATE_H_
#include "base/strings/string16.h"
+#include "content/public/browser/render_view_host.h"
-namespace content {
-class KeyboardListener;
+namespace ui {
+class MouseEvent;
}
namespace autofill {
@@ -17,13 +18,19 @@ namespace autofill {
// of events by the controller.
class AutofillPopupDelegate {
public:
- // Called when the Autofill popup is shown. |listener| may be used to pass
+ // Called when the Autofill popup is shown. |callback| may be used to pass
// keyboard events to the popup.
- virtual void OnPopupShown(content::KeyboardListener* listener) = 0;
+ virtual void OnPopupShown(
+ content::RenderWidgetHost::KeyPressEventCallback* callback) = 0;
- // Called when the Autofill popup is hidden. |listener| must be unregistered
+ // Called when the Autofill popup is hidden. |callback| must be unregistered
// if it was registered in OnPopupShown.
- virtual void OnPopupHidden(content::KeyboardListener* listener) = 0;
+ virtual void OnPopupHidden(
+ content::RenderWidgetHost::KeyPressEventCallback* callback) = 0;
+
+ // Called when the Autofill popup recieves a click outside of the popup view
+ // to determine if the event should be reposted to the native window manager.
+ virtual bool ShouldRepostEvent(const ui::MouseEvent& event) = 0;
// Called when the autofill suggestion indicated by |identifier| has been
// temporarily selected (e.g., hovered).
diff --git a/chromium/components/autofill/core/browser/autofill_profile.cc b/chromium/components/autofill/core/browser/autofill_profile.cc
index 6e18011e5c4..735549f7768 100644
--- a/chromium/components/autofill/core/browser/autofill_profile.cc
+++ b/chromium/components/autofill/core/browser/autofill_profile.cc
@@ -417,29 +417,26 @@ bool AutofillProfile::IsEmpty(const std::string& app_locale) const {
bool AutofillProfile::IsPresentButInvalid(ServerFieldType type) const {
std::string country = UTF16ToUTF8(GetRawInfo(ADDRESS_HOME_COUNTRY));
base::string16 data = GetRawInfo(type);
+ if (data.empty())
+ return false;
+
switch (type) {
case ADDRESS_HOME_STATE:
- if (!data.empty() && country == "US" && !autofill::IsValidState(data))
- return true;
- break;
+ return country == "US" && !autofill::IsValidState(data);
case ADDRESS_HOME_ZIP:
- if (!data.empty() && country == "US" && !autofill::IsValidZip(data))
- return true;
- break;
+ return country == "US" && !autofill::IsValidZip(data);
- case PHONE_HOME_WHOLE_NUMBER: {
- if (!data.empty() && !i18n::PhoneObject(data, country).IsValidNumber())
- return true;
- break;
- }
+ case PHONE_HOME_WHOLE_NUMBER:
+ return !i18n::PhoneObject(data, country).IsValidNumber();
+
+ case EMAIL_ADDRESS:
+ return !autofill::IsValidEmailAddress(data);
default:
NOTREACHED();
- break;
+ return false;
}
-
- return false;
}
@@ -862,6 +859,7 @@ FormGroup* AutofillProfile::MutableFormGroupForType(const AutofillType& type) {
case NO_GROUP:
case CREDIT_CARD:
+ case PASSWORD_FIELD:
return NULL;
}
diff --git a/chromium/components/autofill/core/browser/autofill_type.cc b/chromium/components/autofill/core/browser/autofill_type.cc
index f1e280ba9d8..97168acffdf 100644
--- a/chromium/components/autofill/core/browser/autofill_type.cc
+++ b/chromium/components/autofill/core/browser/autofill_type.cc
@@ -108,6 +108,10 @@ FieldTypeGroup AutofillType::group() const {
case COMPANY_NAME:
return COMPANY;
+ case PASSWORD:
+ case ACCOUNT_CREATION_PASSWORD:
+ return PASSWORD_FIELD;
+
case NO_SERVER_DATA:
case EMPTY_TYPE:
case PHONE_FAX_NUMBER:
@@ -116,6 +120,8 @@ FieldTypeGroup AutofillType::group() const {
case PHONE_FAX_CITY_AND_NUMBER:
case PHONE_FAX_WHOLE_NUMBER:
case FIELD_WITH_DEFAULT_VALUE:
+ case MERCHANT_EMAIL_SIGNUP:
+ case MERCHANT_PROMO_CODE:
return NO_GROUP;
case MAX_VALID_FIELD_TYPE:
@@ -529,6 +535,14 @@ std::string AutofillType::ToString() const {
return "PHONE_BILLING_CITY_AND_NUMBER";
case PHONE_BILLING_WHOLE_NUMBER:
return "PHONE_BILLING_WHOLE_NUMBER";
+ case MERCHANT_EMAIL_SIGNUP:
+ return "MERCHANT_EMAIL_SIGNUP";
+ case MERCHANT_PROMO_CODE:
+ return "MERCHANT_PROMO_CODE";
+ case PASSWORD:
+ return "PASSWORD";
+ case ACCOUNT_CREATION_PASSWORD:
+ return "ACCOUNT_CREATION_PASSWORD";
case MAX_VALID_FIELD_TYPE:
return std::string();
}
diff --git a/chromium/components/autofill/core/browser/autofill_xml_parser.cc b/chromium/components/autofill/core/browser/autofill_xml_parser.cc
index c1acbe90f7f..caa101b227f 100644
--- a/chromium/components/autofill/core/browser/autofill_xml_parser.cc
+++ b/chromium/components/autofill/core/browser/autofill_xml_parser.cc
@@ -8,8 +8,6 @@
#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"
@@ -37,18 +35,12 @@ void AutofillXmlParser::Error(buzz::XmlParseContext* context,
AutofillQueryXmlParser::AutofillQueryXmlParser(
std::vector<AutofillServerFieldInfo>* field_infos,
UploadRequired* upload_required,
- std::string* experiment_id,
- AutocheckoutPageMetaData* page_meta_data)
+ std::string* experiment_id)
: 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) {
+ experiment_id_(experiment_id) {
DCHECK(upload_required_);
DCHECK(experiment_id_);
- DCHECK(page_meta_data_);
}
AutofillQueryXmlParser::~AutofillQueryXmlParser() {}
@@ -112,48 +104,6 @@ void AutofillQueryXmlParser::StartElement(buzz::XmlParseContext* context,
// 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;
}
}
@@ -185,27 +135,6 @@ void AutofillQueryXmlParser::ParseElementDescriptor(
}
}
-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;
diff --git a/chromium/components/autofill/core/browser/autofill_xml_parser.h b/chromium/components/autofill/core/browser/autofill_xml_parser.h
index c01f13759c9..46836f67e19 100644
--- a/chromium/components/autofill/core/browser/autofill_xml_parser.h
+++ b/chromium/components/autofill/core/browser/autofill_xml_parser.h
@@ -18,8 +18,6 @@
namespace autofill {
-struct AutocheckoutPageMetaData;
-
// The base class that contains common functionality between
// AutofillQueryXmlParser and AutofillUploadXmlParser.
class AutofillXmlParser : public buzz::XmlParseHandler {
@@ -76,8 +74,7 @@ class AutofillQueryXmlParser : public AutofillXmlParser {
public:
AutofillQueryXmlParser(std::vector<AutofillServerFieldInfo>* field_infos,
UploadRequired* upload_required,
- std::string* experiment_id,
- AutocheckoutPageMetaData* page_meta_data);
+ std::string* experiment_id);
virtual ~AutofillQueryXmlParser();
private:
@@ -97,21 +94,6 @@ class AutofillQueryXmlParser : public AutofillXmlParser {
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.
@@ -129,18 +111,6 @@ class AutofillQueryXmlParser : public AutofillXmlParser {
// 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);
};
diff --git a/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc b/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc
index 0c6b0f2154b..a622bef6d0f 100644
--- a/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc
+++ b/chromium/components/autofill/core/browser/autofill_xml_parser_unittest.cc
@@ -7,7 +7,6 @@
#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"
@@ -26,8 +25,7 @@ class AutofillQueryXmlParserTest : public testing::Test {
// Create a parser.
AutofillQueryXmlParser parse_handler(&field_infos_,
&upload_required_,
- &experiment_id_,
- &page_meta_data_);
+ &experiment_id_);
buzz::XmlParser parser(&parse_handler);
parser.Parse(xml.c_str(), xml.length(), true);
EXPECT_EQ(should_succeed, parse_handler.succeeded());
@@ -36,7 +34,6 @@ class AutofillQueryXmlParserTest : public testing::Test {
std::vector<AutofillServerFieldInfo> field_infos_;
UploadRequired upload_required_;
std::string experiment_id_;
- autofill::AutocheckoutPageMetaData page_meta_data_;
};
class AutofillUploadXmlParserTest : public testing::Test {
@@ -165,187 +162,6 @@ TEST_F(AutofillQueryXmlParserTest, ParseExperimentId) {
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.
diff --git a/chromium/components/autofill/core/browser/contact_info.cc b/chromium/components/autofill/core/browser/contact_info.cc
index 603c4fa3b54..b3ef9857474 100644
--- a/chromium/components/autofill/core/browser/contact_info.cc
+++ b/chromium/components/autofill/core/browser/contact_info.cc
@@ -52,8 +52,8 @@ void NameInfo::GetSupportedTypes(ServerFieldTypeSet* supported_types) const {
}
base::string16 NameInfo::GetRawInfo(ServerFieldType type) const {
- // TODO(isherman): Is GetStorableType even necessary?
- switch (AutofillType(type).GetStorableType()) {
+ DCHECK_EQ(NAME, AutofillType(type).group());
+ switch (type) {
case NAME_FIRST:
return first();
@@ -75,10 +75,8 @@ base::string16 NameInfo::GetRawInfo(ServerFieldType type) const {
}
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) {
+ DCHECK_EQ(NAME, AutofillType(type).group());
+ switch (type) {
case NAME_FIRST:
first_ = value;
break;
diff --git a/chromium/components/autofill/core/browser/credit_card.cc b/chromium/components/autofill/core/browser/credit_card.cc
index f3ac6b112ab..a3e3044f18b 100644
--- a/chromium/components/autofill/core/browser/credit_card.cc
+++ b/chromium/components/autofill/core/browser/credit_card.cc
@@ -6,6 +6,7 @@
#include <stddef.h>
+#include <algorithm>
#include <ostream>
#include <string>
@@ -276,6 +277,7 @@ std::string CreditCard::GetCreditCardType(const base::string16& number) {
}
base::string16 CreditCard::GetRawInfo(ServerFieldType type) const {
+ DCHECK_EQ(CREDIT_CARD, AutofillType(type).group());
switch (type) {
case CREDIT_CARD_NAME:
return name_on_card_;
@@ -323,6 +325,7 @@ base::string16 CreditCard::GetRawInfo(ServerFieldType type) const {
void CreditCard::SetRawInfo(ServerFieldType type,
const base::string16& value) {
+ DCHECK_EQ(CREDIT_CARD, AutofillType(type).group());
switch (type) {
case CREDIT_CARD_NAME:
name_on_card_ = value;
diff --git a/chromium/components/autofill/core/browser/data_driven_test.cc b/chromium/components/autofill/core/browser/data_driven_test.cc
index 18f461ece0e..3218b17ec00 100644
--- a/chromium/components/autofill/core/browser/data_driven_test.cc
+++ b/chromium/components/autofill/core/browser/data_driven_test.cc
@@ -15,7 +15,7 @@ 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))
+ if (!base::ReadFileToString(file, content))
return false;
ReplaceSubstringsAfterOffset(content, 0, "\r\n", "\n");
diff --git a/chromium/components/autofill/core/browser/field_types.h b/chromium/components/autofill/core/browser/field_types.h
index 081a7a75c42..0eda2e278c8 100644
--- a/chromium/components/autofill/core/browser/field_types.h
+++ b/chromium/components/autofill/core/browser/field_types.h
@@ -99,10 +99,22 @@ enum ServerFieldType {
NAME_BILLING_FULL = 71,
NAME_BILLING_SUFFIX = 72,
+ // Field types for options generally found in merchant buyflows. Given that
+ // these are likely to be filled out differently on a case by case basis,
+ // they are here primarly for use by Autocheckout.
+ MERCHANT_EMAIL_SIGNUP = 73,
+ MERCHANT_PROMO_CODE = 74,
+
+ // Field types for the password fields. PASSWORD is the default type for all
+ // password fields. ACCOUNT_CREATION_PASSWORD is the first password field in
+ // an account creation form and will trigger password generation.
+ PASSWORD = 75,
+ ACCOUNT_CREATION_PASSWORD = 76,
+
// No new types can be added without a corresponding change to the Autofill
// server.
- MAX_VALID_FIELD_TYPE = 73,
+ MAX_VALID_FIELD_TYPE = 77,
};
// The list of all HTML autocomplete field type hints supported by Chrome.
@@ -181,6 +193,7 @@ enum FieldTypeGroup {
PHONE_HOME,
PHONE_BILLING,
CREDIT_CARD,
+ PASSWORD_FIELD,
};
typedef std::set<ServerFieldType> ServerFieldTypeSet;
diff --git a/chromium/components/autofill/core/browser/form_structure.cc b/chromium/components/autofill/core/browser/form_structure.cc
index 0a534f8d847..ae4de043a11 100644
--- a/chromium/components/autofill/core/browser/form_structure.cc
+++ b/chromium/components/autofill/core/browser/form_structure.cc
@@ -16,7 +16,6 @@
#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"
@@ -48,7 +47,6 @@ 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";
@@ -324,8 +322,7 @@ std::string StripDigitsIfRequired(const base::string16& input) {
} // namespace
-FormStructure::FormStructure(const FormData& form,
- const std::string& autocheckout_url_prefix)
+FormStructure::FormStructure(const FormData& form)
: form_name_(form.name),
source_url_(form.origin),
target_url_(form.action),
@@ -333,9 +330,7 @@ FormStructure::FormStructure(const FormData& form,
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) {
+ has_author_specified_types_(false) {
// Copy the form fields.
std::map<base::string16, size_t> unique_names;
for (std::vector<FormFieldData>::const_iterator field =
@@ -495,16 +490,6 @@ bool FormStructure::EncodeQueryRequest(
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;
@@ -524,15 +509,6 @@ bool FormStructure::EncodeQueryRequest(
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);
}
@@ -540,15 +516,8 @@ bool FormStructure::EncodeQueryRequest(
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));
- }
+ autofill_request_xml.SetAttr(buzz::QName(kAttributeAcceptedFeatures),
+ kAcceptedFeaturesExperiment);
// Obtain the XML structure as a string.
*encoded_xml = kXMLDeclaration;
@@ -561,7 +530,6 @@ bool FormStructure::EncodeQueryRequest(
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);
@@ -571,8 +539,7 @@ void FormStructure::ParseQueryResponse(
std::string experiment_id;
AutofillQueryXmlParser parse_handler(&field_infos,
&upload_required,
- &experiment_id,
- page_meta_data);
+ &experiment_id);
buzz::XmlParser parser(&parse_handler);
parser.Parse(response_xml.c_str(), response_xml.length(), true);
if (!parse_handler.succeeded())
@@ -692,21 +659,12 @@ std::string FormStructure::FormSignature() const {
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;
+ return field.is_checkable;
}
bool FormStructure::IsAutofillable(bool require_method_post) const {
- if (autofill_count() < RequiredFillableFields())
+ if (autofill_count() < kRequiredAutofillFields)
return false;
return ShouldBeParsed(require_method_post);
@@ -723,7 +681,7 @@ void FormStructure::UpdateAutofillCount() {
}
bool FormStructure::ShouldBeParsed(bool require_method_post) const {
- if (active_field_count() < RequiredFillableFields())
+ if (active_field_count() < kRequiredAutofillFields)
return false;
// Rule out http(s)://*/search?...
@@ -732,25 +690,19 @@ bool FormStructure::ShouldBeParsed(bool require_method_post) const {
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;
+ 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();
+ return !has_author_specified_types_ && ShouldBeParsed(true);
}
void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
@@ -782,7 +734,6 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) {
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
@@ -929,7 +880,7 @@ void FormStructure::LogQualityMetrics(
}
}
- if (num_detected_field_types < RequiredFillableFields()) {
+ if (num_detected_field_types < kRequiredAutofillFields) {
metric_logger.LogUserHappinessMetric(
AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM);
} else {
diff --git a/chromium/components/autofill/core/browser/form_structure.h b/chromium/components/autofill/core/browser/form_structure.h
index 1af765057c2..6382c20a715 100644
--- a/chromium/components/autofill/core/browser/form_structure.h
+++ b/chromium/components/autofill/core/browser/form_structure.h
@@ -40,7 +40,6 @@ namespace autofill {
class AutofillMetrics;
-struct AutocheckoutPageMetaData;
struct FormData;
struct FormDataPredictions;
@@ -48,8 +47,7 @@ struct FormDataPredictions;
// in the fields along with additional information needed by Autofill.
class FormStructure {
public:
- FormStructure(const FormData& form,
- const std::string& autocheckout_url_prefix);
+ FormStructure(const FormData& form);
virtual ~FormStructure();
// Runs several heuristics against the form fields to determine their possible
@@ -82,7 +80,6 @@ class FormStructure {
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
@@ -156,6 +153,9 @@ class FormStructure {
const GURL& source_url() const { return source_url_; }
+ void set_upload_required(UploadRequired required) {
+ upload_required_ = required;
+ }
UploadRequired upload_required() const { return upload_required_; }
virtual std::string server_experiment_id() const;
@@ -164,11 +164,6 @@ class FormStructure {
// |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;
@@ -199,13 +194,9 @@ class FormStructure {
// 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.
@@ -247,13 +238,6 @@ class FormStructure {
// 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);
};
diff --git a/chromium/components/autofill/core/browser/form_structure_unittest.cc b/chromium/components/autofill/core/browser/form_structure_unittest.cc
index 7d38165b4d1..a6d08a7cb3f 100644
--- a/chromium/components/autofill/core/browser/form_structure_unittest.cc
+++ b/chromium/components/autofill/core/browser/form_structure_unittest.cc
@@ -7,7 +7,6 @@
#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"
@@ -92,11 +91,7 @@ TEST(FormStructureTest, FieldCount) {
// 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"));
+ form_structure.reset(new FormStructure(form));
EXPECT_EQ(4U, form_structure->field_count());
}
@@ -127,7 +122,7 @@ TEST(FormStructureTest, AutofillCount) {
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.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_EQ(1U, form_structure->autofill_count());
@@ -138,25 +133,20 @@ TEST(FormStructureTest, AutofillCount) {
field.should_autocomplete = false;
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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());
+ FormStructure form_structure(form);
EXPECT_EQ(form.origin, form_structure.source_url());
}
@@ -169,11 +159,6 @@ TEST(FormStructureTest, IsAutofillable) {
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");
@@ -190,15 +175,10 @@ TEST(FormStructureTest, IsAutofillable) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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");
@@ -210,7 +190,7 @@ TEST(FormStructureTest, IsAutofillable) {
field.form_control_type = "text";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_FALSE(form_structure->IsAutofillable(true));
@@ -220,14 +200,14 @@ TEST(FormStructureTest, IsAutofillable) {
field.form_control_type = "email";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_FALSE(form_structure->IsAutofillable(true));
EXPECT_TRUE(form_structure->IsAutofillable(false));
@@ -235,13 +215,13 @@ TEST(FormStructureTest, IsAutofillable) {
// 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.reset(new FormStructure(form));
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.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
}
@@ -270,14 +250,9 @@ TEST(FormStructureTest, ShouldBeParsed) {
form.fields.push_back(checkable_field);
// We have only one text field, should not be parsed.
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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");
@@ -289,25 +264,25 @@ TEST(FormStructureTest, ShouldBeParsed) {
field.form_control_type = "text";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
EXPECT_TRUE(form_structure->ShouldBeParsed(true));
// The form need only have three fields, but at least one must be a text
@@ -329,17 +304,13 @@ TEST(FormStructureTest, ShouldBeParsed) {
field.form_control_type = "select-one";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
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) {
@@ -383,7 +354,7 @@ TEST(FormStructureTest, HeuristicsContactInfo) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
@@ -434,7 +405,7 @@ TEST(FormStructureTest, HeuristicsAutocompleteAttribute) {
field.autocomplete_attribute = "email";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
@@ -475,7 +446,7 @@ TEST(FormStructureTest, HeuristicsAutocompleteAttributePhoneTypes) {
field.autocomplete_attribute = "tel-local-suffix";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
@@ -516,7 +487,7 @@ TEST(FormStructureTest, AutocompleteAttributeOverridesOtherHeuristics) {
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
EXPECT_TRUE(form_structure->ShouldBeCrowdsourced());
@@ -530,7 +501,7 @@ TEST(FormStructureTest, AutocompleteAttributeOverridesOtherHeuristics) {
// 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.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_FALSE(form_structure->IsAutofillable(true));
EXPECT_FALSE(form_structure->ShouldBeCrowdsourced());
@@ -541,20 +512,6 @@ TEST(FormStructureTest, AutocompleteAttributeOverridesOtherHeuristics) {
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|
@@ -604,7 +561,7 @@ TEST(FormStructureTest, HeuristicsAutocompleteAttributeWithSections) {
field.autocomplete_attribute = "section-foo cc-number";
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure.IsAutofillable(true));
@@ -649,7 +606,7 @@ TEST(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsDegenerate) {
field.autocomplete_attribute = "garbage billing email";
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
// Expect the correct number of fields.
@@ -679,7 +636,7 @@ TEST(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsRepeated) {
field.autocomplete_attribute = "section-foo address-line1";
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
// Expect the correct number of fields.
@@ -717,7 +674,7 @@ TEST(FormStructureTest, HeuristicsDontOverrideAutocompleteAttributeSections) {
field.autocomplete_attribute = "address-line1";
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
// Expect the correct number of fields.
@@ -782,7 +739,7 @@ TEST(FormStructureTest, HeuristicsSample8) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(10U, form_structure->field_count());
@@ -850,7 +807,7 @@ TEST(FormStructureTest, HeuristicsSample6) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(7U, form_structure->field_count());
@@ -916,7 +873,7 @@ TEST(FormStructureTest, HeuristicsLabelsOnly) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(8U, form_structure->field_count());
@@ -974,7 +931,7 @@ TEST(FormStructureTest, HeuristicsCreditCardInfo) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(6U, form_structure->field_count());
@@ -1035,7 +992,7 @@ TEST(FormStructureTest, HeuristicsCreditCardInfoWithUnknownCardField) {
field.form_control_type = "submit";
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(7U, form_structure->field_count());
@@ -1083,7 +1040,7 @@ TEST(FormStructureTest, ThreeAddressLines) {
field.name = ASCIIToUTF16("city");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(4U, form_structure->field_count());
@@ -1125,7 +1082,7 @@ TEST(FormStructureTest, BillingAndShippingAddresses) {
field.name = ASCIIToUTF16("billing.address.addressLine2");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(4U, form_structure->field_count());
@@ -1166,7 +1123,7 @@ TEST(FormStructureTest, SurplusAddressLinesIgnored) {
field.name = ASCIIToUTF16("billing.address.addressLine4");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(2U, form_structure->autofill_count());
@@ -1210,7 +1167,7 @@ TEST(FormStructureTest, ThreeAddressLinesExpedia) {
field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adct");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(4U, form_structure->field_count());
@@ -1249,7 +1206,7 @@ TEST(FormStructureTest, TwoAddressLinesEbay) {
field.name = ASCIIToUTF16("city");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(3U, form_structure->field_count());
@@ -1283,7 +1240,7 @@ TEST(FormStructureTest, HeuristicsStateWithProvince) {
field.name = ASCIIToUTF16("State");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(3U, form_structure->field_count());
@@ -1350,7 +1307,7 @@ TEST(FormStructureTest, HeuristicsWithBilling) {
field.name = ASCIIToUTF16("email$emailBox");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(11U, form_structure->field_count());
@@ -1401,7 +1358,7 @@ TEST(FormStructureTest, ThreePartPhoneNumber) {
field.max_length = 0;
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
ASSERT_EQ(4U, form_structure->field_count());
@@ -1447,7 +1404,7 @@ TEST(FormStructureTest, HeuristicsInfernoCC) {
field.name = ASCIIToUTF16("expiration_year");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
@@ -1500,7 +1457,7 @@ TEST(FormStructureTest, CVCCodeClash) {
field.name = ASCIIToUTF16("csc");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(form_structure->IsAutofillable(true));
@@ -1559,7 +1516,7 @@ TEST(FormStructureTest, EncodeQueryRequest) {
form.fields.push_back(checkable_field);
ScopedVector<FormStructure> forms;
- forms.push_back(new FormStructure(form, std::string()));
+ forms.push_back(new FormStructure(form));
std::vector<std::string> encoded_signatures;
std::string encoded_xml;
const char * const kSignature1 = "11337937696949187602";
@@ -1579,7 +1536,7 @@ TEST(FormStructureTest, EncodeQueryRequest) {
// Add the same form, only one will be encoded, so EncodeQueryRequest() should
// return the same data.
- forms.push_back(new FormStructure(form, std::string()));
+ forms.push_back(new FormStructure(form));
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
&encoded_signatures,
&encoded_xml));
@@ -1593,7 +1550,7 @@ TEST(FormStructureTest, EncodeQueryRequest) {
form.fields.push_back(field);
}
- forms.push_back(new FormStructure(form, std::string()));
+ forms.push_back(new FormStructure(form));
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
&encoded_signatures,
&encoded_xml));
@@ -1624,7 +1581,7 @@ TEST(FormStructureTest, EncodeQueryRequest) {
malformed_form.fields.push_back(field);
}
- forms.push_back(new FormStructure(malformed_form, std::string()));
+ forms.push_back(new FormStructure(malformed_form));
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms.get(),
&encoded_signatures,
&encoded_xml));
@@ -1635,37 +1592,12 @@ TEST(FormStructureTest, EncodeQueryRequest) {
// 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()));
+ bad_forms.push_back(new FormStructure(malformed_form));
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) {
@@ -1673,7 +1605,7 @@ TEST(FormStructureTest, EncodeUploadRequest) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form.method = ASCIIToUTF16("post");
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
FormFieldData field;
@@ -1721,7 +1653,7 @@ TEST(FormStructureTest, EncodeUploadRequest) {
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_COUNTRY);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
for (size_t i = 0; i < form_structure->field_count(); ++i)
@@ -1779,7 +1711,7 @@ TEST(FormStructureTest, EncodeUploadRequest) {
possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
}
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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]);
@@ -1818,7 +1750,7 @@ TEST(FormStructureTest, EncodeUploadRequest) {
possible_field_types.back().insert(ADDRESS_BILLING_LINE1);
possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
}
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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]);
@@ -1831,7 +1763,7 @@ TEST(FormStructureTest, EncodeFieldAssignments) {
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form.method = ASCIIToUTF16("post");
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(TestAutofillMetrics());
FormFieldData field;
@@ -1879,7 +1811,7 @@ TEST(FormStructureTest, EncodeFieldAssignments) {
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_COUNTRY);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
for (size_t i = 0; i < form_structure->field_count(); ++i)
@@ -1924,7 +1856,7 @@ TEST(FormStructureTest, EncodeFieldAssignments) {
possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
}
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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]);
@@ -1973,7 +1905,7 @@ TEST(FormStructureTest, CheckDataPresence) {
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
ServerFieldTypeSet unknown_type;
unknown_type.insert(UNKNOWN_TYPE);
@@ -2239,7 +2171,7 @@ TEST(FormStructureTest, CheckMultipleTypes) {
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_LINE1);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
for (size_t i = 0; i < form_structure->field_count(); ++i)
form_structure->field(i)->set_possible_types(possible_field_types[i]);
@@ -2329,36 +2261,38 @@ TEST(FormStructureTest, CheckFormSignature) {
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";
+ // Checkable fields shouldn't affect the signature.
+ field.label = ASCIIToUTF16("Select");
+ field.name = ASCIIToUTF16("Select");
+ field.form_control_type = "checkbox";
+ field.is_checkable = true;
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
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()));
+ form_structure.reset(new FormStructure(form));
EXPECT_EQ(FormStructureTest::Hash64Bit(
std::string("https://login.facebook.com&login_form&email&first")),
form_structure->FormSignature());
+ field.is_checkable = false;
field.label = ASCIIToUTF16("Random Field label");
field.name = ASCIIToUTF16("random1234");
field.form_control_type = "text";
@@ -2372,7 +2306,7 @@ TEST(FormStructureTest, CheckFormSignature) {
field.label = ASCIIToUTF16("Random Field label3");
field.name = ASCIIToUTF16("12345random");
form.fields.push_back(field);
- form_structure.reset(new FormStructure(form, std::string()));
+ form_structure.reset(new FormStructure(form));
EXPECT_EQ(FormStructureTest::Hash64Bit(
std::string("https://login.facebook.com&login_form&email&first&"
"random1234&random&1random&random")),
@@ -2403,12 +2337,12 @@ TEST(FormStructureTest, ToFormData) {
field.form_control_type = "submit";
form.fields.push_back(field);
- EXPECT_EQ(form, FormStructure(form, std::string()).ToFormData());
+ EXPECT_EQ(form, FormStructure(form).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());
+ EXPECT_NE(form, FormStructure(form).ToFormData());
}
TEST(FormStructureTest, SkipFieldTest) {
@@ -2424,18 +2358,20 @@ TEST(FormStructureTest, SkipFieldTest) {
field.form_control_type = "text";
form.fields.push_back(field);
- field.label = ASCIIToUTF16("password");
- field.name = ASCIIToUTF16("password");
- field.form_control_type = "password";
+ field.label = ASCIIToUTF16("select");
+ field.name = ASCIIToUTF16("select");
+ field.form_control_type = "checkbox";
+ field.is_checkable = true;
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("email");
field.form_control_type = "text";
+ field.is_checkable = false;
form.fields.push_back(field);
ScopedVector<FormStructure> forms;
- forms.push_back(new FormStructure(form, std::string()));
+ forms.push_back(new FormStructure(form));
std::vector<std::string> encoded_signatures;
std::string encoded_xml;
@@ -2451,16 +2387,6 @@ TEST(FormStructureTest, SkipFieldTest) {
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/password_autofill_manager.cc b/chromium/components/autofill/core/browser/password_autofill_manager.cc
index b568f8d36ed..def93f407f2 100644
--- a/chromium/components/autofill/core/browser/password_autofill_manager.cc
+++ b/chromium/components/autofill/core/browser/password_autofill_manager.cc
@@ -6,7 +6,7 @@
#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"
+#include "ui/events/keycodes/keyboard_codes.h"
namespace autofill {
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.cc b/chromium/components/autofill/core/browser/personal_data_manager.cc
index 9ddff255d5d..c3bd991fc10 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager.cc
@@ -726,7 +726,7 @@ bool PersonalDataManager::IsValidLearnableProfile(
}
// static
-bool PersonalDataManager::MergeProfile(
+std::string PersonalDataManager::MergeProfile(
const AutofillProfile& new_profile,
const std::vector<AutofillProfile*>& existing_profiles,
const std::string& app_locale,
@@ -735,6 +735,7 @@ bool PersonalDataManager::MergeProfile(
// Set to true if |existing_profiles| already contains an equivalent profile.
bool matching_profile_found = false;
+ std::string guid = new_profile.guid();
// If we have already saved this address, merge in any missing values.
// Only merge with the first match.
@@ -751,6 +752,7 @@ bool PersonalDataManager::MergeProfile(
// data. If an automatically aggregated profile would overwrite a
// verified profile, just drop it.
matching_profile_found = true;
+ guid = existing_profile->guid();
if (!existing_profile->IsVerified() || new_profile.IsVerified())
existing_profile->OverwriteWithOrAddTo(new_profile, app_locale);
}
@@ -761,7 +763,7 @@ bool PersonalDataManager::MergeProfile(
if (!matching_profile_found)
merged_profiles->push_back(new_profile);
- return matching_profile_found;
+ return guid;
}
void PersonalDataManager::SetProfiles(std::vector<AutofillProfile>* profiles) {
@@ -962,10 +964,10 @@ void PersonalDataManager::CancelPendingQuery(
*handle = 0;
}
-void PersonalDataManager::SaveImportedProfile(
+std::string PersonalDataManager::SaveImportedProfile(
const AutofillProfile& imported_profile) {
if (browser_context_->IsOffTheRecord())
- return;
+ return std::string();
// Don't save a web profile if the data in the profile is a subset of an
// auxiliary profile.
@@ -973,32 +975,39 @@ void PersonalDataManager::SaveImportedProfile(
auxiliary_profiles_.begin();
iter != auxiliary_profiles_.end(); ++iter) {
if (imported_profile.IsSubsetOf(**iter, app_locale_))
- return;
+ return (*iter)->guid();
}
std::vector<AutofillProfile> profiles;
- MergeProfile(imported_profile, web_profiles_.get(), app_locale_, &profiles);
+ std::string guid =
+ MergeProfile(imported_profile, web_profiles_.get(), app_locale_,
+ &profiles);
SetProfiles(&profiles);
+ return guid;
}
-void PersonalDataManager::SaveImportedCreditCard(
+std::string PersonalDataManager::SaveImportedCreditCard(
const CreditCard& imported_card) {
DCHECK(!imported_card.number().empty());
if (browser_context_->IsOffTheRecord())
- return;
+ return std::string();
// Set to true if |imported_card| is merged into the credit card list.
bool merged = false;
+ std::string guid = imported_card.guid();
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_))
+ if (!merged &&
+ (*card)->UpdateFromImportedCard(imported_card, app_locale_)) {
+ guid = (*card)->guid();
merged = true;
+ }
credit_cards.push_back(**card);
}
@@ -1007,6 +1016,7 @@ void PersonalDataManager::SaveImportedCreditCard(
credit_cards.push_back(imported_card);
SetCreditCards(&credit_cards);
+ return guid;
}
void PersonalDataManager::LogProfileCount() const {
diff --git a/chromium/components/autofill/core/browser/personal_data_manager.h b/chromium/components/autofill/core/browser/personal_data_manager.h
index 9b71aa55c58..d5dc85ea81b 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/personal_data_manager.h
@@ -79,11 +79,15 @@ class PersonalDataManager : public WebDataServiceConsumer,
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 |imported_profile| to the WebDB if it exists. Returns the guid of
+ // the new or updated profile, or the empty string if no profile was saved.
+ virtual std::string SaveImportedProfile(
+ const AutofillProfile& imported_profile);
- // Saves a credit card value detected in |ImportedFormData|.
- virtual void SaveImportedCreditCard(const CreditCard& imported_credit_card);
+ // Saves a credit card value detected in |ImportedFormData|. Returns the guid
+ // of the new or updated card, or the empty string if no card was saved.
+ virtual std::string SaveImportedCreditCard(
+ const CreditCard& imported_credit_card);
// Adds |profile| to the web database.
void AddProfile(const AutofillProfile& profile);
@@ -169,8 +173,9 @@ class PersonalDataManager : public WebDataServiceConsumer,
// 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(
+ // |merged_profiles| with the result. Returns the |guid| of the new or updated
+ // profile.
+ static std::string MergeProfile(
const AutofillProfile& new_profile,
const std::vector<AutofillProfile*>& existing_profiles,
const std::string& app_locale,
diff --git a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
index 7c08e5f8983..cc378e56f7f 100644
--- a/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/chromium/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -99,7 +99,9 @@ class PersonalDataManagerTest : public testing::Test {
}
void MakeProfileIncognito() {
- profile_->set_incognito(true);
+ // Switch to an incognito profile.
+ profile_->ForceIncognito(true);
+ DCHECK(profile_->IsOffTheRecord());
}
base::MessageLoopForUI message_loop_;
@@ -594,7 +596,7 @@ TEST_F(PersonalDataManagerTest, ImportFormData) {
form.fields.push_back(field);
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -635,7 +637,7 @@ TEST_F(PersonalDataManagerTest, ImportFormDataBadEmail) {
form.fields.push_back(field);
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_FALSE(personal_data_->ImportFormData(form_structure,
@@ -668,7 +670,7 @@ TEST_F(PersonalDataManagerTest, ImportFormDataTwoEmails) {
test::CreateTestFormField(
"Confirm email:", "confirm_email", "example@example.com", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -699,7 +701,7 @@ TEST_F(PersonalDataManagerTest, ImportFormDataTwoDifferentEmails) {
test::CreateTestFormField(
"Email:", "email2", "example2@example.com", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_FALSE(personal_data_->ImportFormData(form_structure,
@@ -720,7 +722,7 @@ TEST_F(PersonalDataManagerTest, ImportFormDataNotEnoughFilledFields) {
test::CreateTestFormField(
"Card number:", "card_number", "4111 1111 1111 1111", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_FALSE(personal_data_->ImportFormData(form_structure,
@@ -751,7 +753,7 @@ TEST_F(PersonalDataManagerTest, ImportFormMinimumAddressUSA) {
form.fields.push_back(field);
test::CreateTestFormField("Country:", "country", "USA", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -778,7 +780,7 @@ TEST_F(PersonalDataManagerTest, ImportFormMinimumAddressGB) {
test::CreateTestFormField(
"Country:", "country", "United Kingdom", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -800,7 +802,7 @@ TEST_F(PersonalDataManagerTest, ImportFormMinimumAddressGI) {
form.fields.push_back(field);
test::CreateTestFormField("Country:", "country", "Gibraltar", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -839,7 +841,7 @@ TEST_F(PersonalDataManagerTest, ImportPhoneNumberSplitAcrossMultipleFields) {
form.fields.push_back(field);
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -919,7 +921,7 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentProfiles) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -960,7 +962,7 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentProfiles) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1004,7 +1006,7 @@ TEST_F(PersonalDataManagerTest, AggregateTwoProfilesWithMultiValue) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1044,7 +1046,7 @@ TEST_F(PersonalDataManagerTest, AggregateTwoProfilesWithMultiValue) {
test::CreateTestFormField("Zip:", "zip", "94102", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1097,7 +1099,7 @@ TEST_F(PersonalDataManagerTest, AggregateSameProfileWithConflict) {
test::CreateTestFormField("Phone:", "phone", "6505556666", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1148,7 +1150,7 @@ TEST_F(PersonalDataManagerTest, AggregateSameProfileWithConflict) {
test::CreateTestFormField("Phone:", "phone", "6502231234", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1190,7 +1192,7 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInOld) {
test::CreateTestFormField("Zip:", "zipcode", "19106", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1231,7 +1233,7 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInOld) {
test::CreateTestFormField("Zip:", "zipcode", "19106", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1277,7 +1279,7 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInNew) {
test::CreateTestFormField("Zip:", "zipcode", "19106", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1319,7 +1321,7 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithMissingInfoInNew) {
test::CreateTestFormField("Zip:", "zipcode", "19106", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1358,7 +1360,7 @@ TEST_F(PersonalDataManagerTest, AggregateProfileWithInsufficientAddress) {
test::CreateTestFormField("City:", "city", "Philadelphia", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_FALSE(personal_data_->ImportFormData(form_structure1,
@@ -1412,7 +1414,7 @@ TEST_F(PersonalDataManagerTest, AggregateExistingAuxiliaryProfile) {
test::CreateTestFormField("Phone:", "phone", "4158889999", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -1445,7 +1447,7 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentCreditCards) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1479,7 +1481,7 @@ TEST_F(PersonalDataManagerTest, AggregateTwoDifferentCreditCards) {
test::CreateTestFormField("Exp Year:", "exp_year", "2012", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1516,7 +1518,7 @@ TEST_F(PersonalDataManagerTest, AggregateInvalidCreditCard) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1550,7 +1552,7 @@ TEST_F(PersonalDataManagerTest, AggregateInvalidCreditCard) {
test::CreateTestFormField("Exp Year:", "exp_year", "2012", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_FALSE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1581,7 +1583,7 @@ TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithConflict) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1616,7 +1618,7 @@ TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithConflict) {
test::CreateTestFormField("Exp Year:", "exp_year", "2012", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1653,7 +1655,7 @@ TEST_F(PersonalDataManagerTest, AggregateEmptyCreditCardWithConflict) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1684,7 +1686,7 @@ TEST_F(PersonalDataManagerTest, AggregateEmptyCreditCardWithConflict) {
test::CreateTestFormField("Exp Year:", "exp_year", "2012", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_FALSE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1719,7 +1721,7 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInNew) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -1752,7 +1754,7 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInNew) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
@@ -1780,7 +1782,7 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInNew) {
form3.fields.push_back(field);
// Note missing expiration month and year..
- FormStructure form_structure3(form3, std::string());
+ FormStructure form_structure3(form3);
form_structure3.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_FALSE(personal_data_->ImportFormData(form_structure3,
&imported_credit_card));
@@ -1832,7 +1834,7 @@ TEST_F(PersonalDataManagerTest, AggregateCreditCardWithMissingInfoInOld) {
test::CreateTestFormField("Exp Year:", "exp_year", "2012", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -1887,7 +1889,7 @@ TEST_F(PersonalDataManagerTest, AggregateSameCreditCardWithSeparators) {
test::CreateTestFormField("Exp Year:", "exp_year", "2011", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -1945,7 +1947,7 @@ TEST_F(PersonalDataManagerTest, AggregateExistingVerifiedProfileWithConflict) {
test::CreateTestFormField("Zip:", "zip", "91601", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -1995,7 +1997,7 @@ TEST_F(PersonalDataManagerTest,
test::CreateTestFormField("Exp Year:", "exp_year", "2012", "text", &field);
form.fields.push_back(field);
- FormStructure form_structure(form, std::string());
+ FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure,
@@ -2278,7 +2280,7 @@ TEST_F(PersonalDataManagerTest, CaseInsensitiveMultiValueAggregation) {
"Phone number:", "phone_number", "817-555-6789", "text", &field);
form1.fields.push_back(field);
- FormStructure form_structure1(form1, std::string());
+ FormStructure form_structure1(form1);
form_structure1.DetermineHeuristicTypes(TestAutofillMetrics());
const CreditCard* imported_credit_card;
EXPECT_TRUE(personal_data_->ImportFormData(form_structure1,
@@ -2322,7 +2324,7 @@ TEST_F(PersonalDataManagerTest, CaseInsensitiveMultiValueAggregation) {
"Phone number:", "phone_number", "214-555-1234", "text", &field);
form2.fields.push_back(field);
- FormStructure form_structure2(form2, std::string());
+ FormStructure form_structure2(form2);
form_structure2.DetermineHeuristicTypes(TestAutofillMetrics());
EXPECT_TRUE(personal_data_->ImportFormData(form_structure2,
&imported_credit_card));
diff --git a/chromium/components/autofill/core/browser/phone_number.cc b/chromium/components/autofill/core/browser/phone_number.cc
index 2ee67f98b72..b068b038f2f 100644
--- a/chromium/components/autofill/core/browser/phone_number.cc
+++ b/chromium/components/autofill/core/browser/phone_number.cc
@@ -74,8 +74,8 @@ void PhoneNumber::GetSupportedTypes(ServerFieldTypeSet* supported_types) const {
}
base::string16 PhoneNumber::GetRawInfo(ServerFieldType type) const {
- // TODO(isherman): Is GetStorableType even necessary?
- if (AutofillType(type).GetStorableType() == PHONE_HOME_WHOLE_NUMBER)
+ DCHECK_EQ(PHONE_HOME, AutofillType(type).group());
+ if (type == PHONE_HOME_WHOLE_NUMBER)
return number_;
// Only the whole number is available as raw data. All of the other types are
@@ -86,8 +86,7 @@ base::string16 PhoneNumber::GetRawInfo(ServerFieldType type) const {
void PhoneNumber::SetRawInfo(ServerFieldType type,
const base::string16& value) {
- // TODO(isherman): Is GetStorableType even necessary?
- type = AutofillType(type).GetStorableType();
+ DCHECK_EQ(PHONE_HOME, AutofillType(type).group());
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.
diff --git a/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc
index dc9781d11e0..b9f2c86f5c4 100644
--- a/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc
+++ b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.cc
@@ -13,21 +13,12 @@ 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(
@@ -35,21 +26,10 @@ void TestAutofillManagerDelegate::ConfirmSaveCreditCard(
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) {}
+ const base::Callback<void(const FormStructure*)>& callback) {}
void TestAutofillManagerDelegate::ShowAutofillPopup(
const gfx::RectF& element_bounds,
@@ -66,15 +46,11 @@ void TestAutofillManagerDelegate::UpdateAutofillPopupDataListValues(
void TestAutofillManagerDelegate::HideAutofillPopup() {}
-void TestAutofillManagerDelegate::AddAutocheckoutStep(
- AutocheckoutStepType step_type) {}
-
-void TestAutofillManagerDelegate::UpdateAutocheckoutStep(
- AutocheckoutStepType step_type,
- AutocheckoutStepStatus step_status) {}
-
bool TestAutofillManagerDelegate::IsAutocompleteEnabled() {
return true;
}
+void TestAutofillManagerDelegate::DetectAccountCreationForms(
+ const std::vector<autofill::FormStructure*>& forms) {}
+
} // 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
index 1fee0ba24a9..6c4681cad93 100644
--- a/chromium/components/autofill/core/browser/test_autofill_manager_delegate.h
+++ b/chromium/components/autofill/core/browser/test_autofill_manager_delegate.h
@@ -7,7 +7,6 @@
#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 {
@@ -22,27 +21,16 @@ class TestAutofillManagerDelegate : public AutofillManagerDelegate {
// 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;
+ const base::Callback<void(const FormStructure*)>& callback) OVERRIDE;
virtual void ShowAutofillPopup(
const gfx::RectF& element_bounds,
base::i18n::TextDirection text_direction,
@@ -57,10 +45,8 @@ class TestAutofillManagerDelegate : public AutofillManagerDelegate {
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;
+ virtual void DetectAccountCreationForms(
+ const std::vector<autofill::FormStructure*>& forms) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(TestAutofillManagerDelegate);
diff --git a/chromium/components/autofill/core/browser/test_personal_data_manager.cc b/chromium/components/autofill/core/browser/test_personal_data_manager.cc
index 0a517d7655b..7ce487bd24d 100644
--- a/chromium/components/autofill/core/browser/test_personal_data_manager.cc
+++ b/chromium/components/autofill/core/browser/test_personal_data_manager.cc
@@ -34,9 +34,16 @@ const std::vector<CreditCard*>& TestPersonalDataManager::
return credit_cards_;
}
-void TestPersonalDataManager::SaveImportedProfile(
+std::string TestPersonalDataManager::SaveImportedProfile(
const AutofillProfile& imported_profile) {
imported_profile_ = imported_profile;
+ return imported_profile.guid();
+}
+
+std::string TestPersonalDataManager::SaveImportedCreditCard(
+ const CreditCard& imported_credit_card) {
+ imported_credit_card_ = imported_credit_card;
+ return imported_credit_card.guid();
}
} // 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
index c9f9a43d4a4..9d6767a6082 100644
--- a/chromium/components/autofill/core/browser/test_personal_data_manager.h
+++ b/chromium/components/autofill/core/browser/test_personal_data_manager.h
@@ -27,16 +27,21 @@ class TestPersonalDataManager : public PersonalDataManager {
void AddTestingCreditCard(CreditCard* credit_card);
virtual const std::vector<AutofillProfile*>& GetProfiles() OVERRIDE;
- virtual void SaveImportedProfile(const AutofillProfile& imported_profile)
- OVERRIDE;
+ virtual const std::vector<CreditCard*>& GetCreditCards() const OVERRIDE;
+
+ virtual std::string SaveImportedProfile(
+ const AutofillProfile& imported_profile) OVERRIDE;
+ virtual std::string SaveImportedCreditCard(
+ const CreditCard& imported_credit_card) OVERRIDE;
const AutofillProfile& imported_profile() { return imported_profile_; }
- virtual const std::vector<CreditCard*>& GetCreditCards() const OVERRIDE;
+ const CreditCard& imported_credit_card() { return imported_credit_card_; }
private:
std::vector<AutofillProfile*> profiles_;
std::vector<CreditCard*> credit_cards_;
AutofillProfile imported_profile_;
+ CreditCard imported_credit_card_;
};
} // namespace autofill
diff --git a/chromium/components/autofill/core/browser/webdata/autofill_table.cc b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
index 9066d9c0df0..1f61bb3967e 100644
--- a/chromium/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/chromium/components/autofill/core/browser/webdata/autofill_table.cc
@@ -2095,7 +2095,7 @@ bool AutofillTable::MigrateToVersion37MergeAndCullOlderProfiles() {
if (PersonalDataManager::IsValidLearnableProfile(*profile, app_locale_)) {
std::vector<AutofillProfile> merged_profiles;
- bool merged = PersonalDataManager::MergeProfile(
+ std::string merged_guid = PersonalDataManager::MergeProfile(
*profile, accumulated_profiles_p, app_locale_, &merged_profiles);
std::swap(accumulated_profiles, merged_profiles);
@@ -2108,9 +2108,8 @@ bool AutofillTable::MigrateToVersion37MergeAndCullOlderProfiles() {
address_of<AutofillProfile>);
// If the profile got merged trash the original.
- if (merged)
+ if (merged_guid != profile->guid())
AddAutofillGUIDToTrash(profile->guid());
-
} else {
// An invalid profile, so trash it.
AddAutofillGUIDToTrash(profile->guid());
diff --git a/chromium/components/autofill/core/common/autocheckout_status.h b/chromium/components/autofill/core/common/autocheckout_status.h
deleted file mode 100644
index 81bc7005e80..00000000000
--- a/chromium/components/autofill/core/common/autocheckout_status.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2013 The Chromium 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_message_generator.cc b/chromium/components/autofill/core/common/autofill_message_generator.cc
index 48988146ab4..d55373b60e3 100644
--- a/chromium/components/autofill/core/common/autofill_message_generator.cc
+++ b/chromium/components/autofill/core/common/autofill_message_generator.cc
@@ -17,17 +17,23 @@
// Generate param traits write methods.
#include "ipc/param_traits_write_macros.h"
namespace IPC {
+#undef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
#include "components/autofill/core/common/autofill_message_generator.h"
+#include "components/autofill/core/common/autofill_param_traits_macros.h"
} // namespace IPC
// Generate param traits read methods.
#include "ipc/param_traits_read_macros.h"
namespace IPC {
+#undef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
#include "components/autofill/core/common/autofill_message_generator.h"
+#include "components/autofill/core/common/autofill_param_traits_macros.h"
} // namespace IPC
// Generate param traits log methods.
#include "ipc/param_traits_log_macros.h"
namespace IPC {
+#undef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
#include "components/autofill/core/common/autofill_message_generator.h"
+#include "components/autofill/core/common/autofill_param_traits_macros.h"
} // namespace IPC
diff --git a/chromium/components/autofill/core/common/autofill_messages.h b/chromium/components/autofill/core/common/autofill_messages.h
index a432887af6f..006744fbc32 100644
--- a/chromium/components/autofill/core/common/autofill_messages.h
+++ b/chromium/components/autofill/core/common/autofill_messages.h
@@ -7,27 +7,24 @@
#include <string>
#include "base/time/time.h"
-#include "components/autofill/core/common/autocheckout_status.h"
+#include "components/autofill/core/common/autofill_param_traits_macros.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.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,
@@ -38,7 +35,8 @@ IPC_STRUCT_TRAITS_BEGIN(autofill::WebElementDescriptor)
IPC_STRUCT_TRAITS_MEMBER(retrieval_method)
IPC_STRUCT_TRAITS_END()
-IPC_ENUM_TRAITS(autofill::WebElementDescriptor::RetrievalMethod)
+IPC_ENUM_TRAITS_MAX_VALUE(autofill::WebElementDescriptor::RetrievalMethod,
+ autofill::WebElementDescriptor::NONE)
IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldData)
IPC_STRUCT_TRAITS_MEMBER(label)
@@ -65,15 +63,6 @@ IPC_STRUCT_TRAITS_BEGIN(autofill::FormFieldDataPredictions)
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)
@@ -99,13 +88,12 @@ IPC_STRUCT_TRAITS_BEGIN(autofill::PasswordAndRealm)
IPC_STRUCT_TRAITS_MEMBER(realm)
IPC_STRUCT_TRAITS_END()
-IPC_ENUM_TRAITS(WebKit::WebFormElement::AutocompleteResult)
+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,
@@ -159,7 +147,7 @@ IPC_MESSAGE_ROUTED1(AutofillMsg_AcceptPasswordAutofillSuggestion,
// 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 */)
+ autofill::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
@@ -168,25 +156,15 @@ 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)
+// Sent when Autofill manager gets the query response from the Autofill server
+// and there are fields classified as ACCOUNT_CREATION_PASSWORD in the response.
+IPC_MESSAGE_ROUTED1(AutofillMsg_AccountCreationFormsDetected,
+ std::vector<autofill::FormData> /* forms */)
+
// Autofill messages sent from the renderer to the browser.
// TODO(creis): check in the browser that the renderer actually has permission
@@ -202,12 +180,16 @@ IPC_MESSAGE_ROUTED3(AutofillHostMsg_FormsSeen,
// 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 */)
+ std::vector<autofill::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 */)
+ std::vector<autofill::PasswordForm> /* forms */)
+
+// Notification that this password form was submitted by the user.
+IPC_MESSAGE_ROUTED1(AutofillHostMsg_PasswordFormSubmitted,
+ autofill::PasswordForm /* form */)
// Notification that a form has been submitted. The user hit the button.
IPC_MESSAGE_ROUTED2(AutofillHostMsg_FormSubmitted,
@@ -220,11 +202,6 @@ IPC_MESSAGE_ROUTED3(AutofillHostMsg_TextFieldDidChange,
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 */,
@@ -262,18 +239,13 @@ 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)
+ autofill::PasswordForm)
// Instruct the browser that a password mapping has been found for a field.
IPC_MESSAGE_ROUTED2(AutofillHostMsg_AddPasswordFormMapping,
diff --git a/chromium/components/autofill/core/common/autofill_param_traits_macros.h b/chromium/components/autofill/core/common/autofill_param_traits_macros.h
new file mode 100644
index 00000000000..91d357a1e1f
--- /dev/null
+++ b/chromium/components/autofill/core/common/autofill_param_traits_macros.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.
+
+// Singly or multiply-included shared traits file depending on circumstances.
+// This allows the use of Autofill IPC serialization macros in more than one IPC
+// message file.
+#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
+#define COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
+
+#include "components/autofill/core/common/password_form.h"
+#include "ipc/ipc_message_macros.h"
+
+IPC_ENUM_TRAITS(autofill::PasswordForm::Type)
+
+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::PasswordForm)
+ IPC_STRUCT_TRAITS_MEMBER(signon_realm)
+ IPC_STRUCT_TRAITS_MEMBER(origin)
+ IPC_STRUCT_TRAITS_MEMBER(action)
+ IPC_STRUCT_TRAITS_MEMBER(submit_element)
+ IPC_STRUCT_TRAITS_MEMBER(username_element)
+ IPC_STRUCT_TRAITS_MEMBER(username_value)
+ IPC_STRUCT_TRAITS_MEMBER(other_possible_usernames)
+ IPC_STRUCT_TRAITS_MEMBER(password_element)
+ IPC_STRUCT_TRAITS_MEMBER(password_value)
+ IPC_STRUCT_TRAITS_MEMBER(password_autocomplete_set)
+ IPC_STRUCT_TRAITS_MEMBER(old_password_element)
+ IPC_STRUCT_TRAITS_MEMBER(old_password_value)
+ IPC_STRUCT_TRAITS_MEMBER(ssl_valid)
+ IPC_STRUCT_TRAITS_MEMBER(preferred)
+ IPC_STRUCT_TRAITS_MEMBER(blacklisted_by_user)
+ IPC_STRUCT_TRAITS_MEMBER(type)
+ IPC_STRUCT_TRAITS_MEMBER(times_used)
+ IPC_STRUCT_TRAITS_MEMBER(form_data)
+IPC_STRUCT_TRAITS_END()
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_AUTOFILL_PARAM_TRAITS_MACROS_H_
diff --git a/chromium/components/autofill/core/common/autofill_switches.cc b/chromium/components/autofill/core/common/autofill_switches.cc
index a41a9c7b96a..c752cc7d468 100644
--- a/chromium/components/autofill/core/common/autofill_switches.cc
+++ b/chromium/components/autofill/core/common/autofill_switches.cc
@@ -7,15 +7,9 @@
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[] =
@@ -41,9 +35,8 @@ const char kWalletSecureServiceUrl[] = "wallet-secure-service-url";
// 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";
+// Use the sandbox Online Wallet service URL (for developer testing).
+const char kWalletServiceUseSandbox[] = "wallet-service-use-sandbox";
} // namespace switches
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/autofill_switches.h b/chromium/components/autofill/core/common/autofill_switches.h
index 84d76d59c59..7061bb61172 100644
--- a/chromium/components/autofill/core/common/autofill_switches.h
+++ b/chromium/components/autofill/core/common/autofill_switches.h
@@ -10,16 +10,14 @@ 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[];
+extern const char kWalletServiceUseSandbox[];
} // namespace switches
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_data.cc b/chromium/components/autofill/core/common/form_data.cc
index e0c3c1c4636..050e2e57582 100644
--- a/chromium/components/autofill/core/common/form_data.cc
+++ b/chromium/components/autofill/core/common/form_data.cc
@@ -4,10 +4,52 @@
#include "components/autofill/core/common/form_data.h"
+#include "base/pickle.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/form_field_data.h"
namespace autofill {
+namespace {
+
+const int kPickleVersion = 1;
+
+bool ReadGURL(PickleIterator* iter, GURL* url) {
+ std::string spec;
+ if (!iter->ReadString(&spec))
+ return false;
+
+ *url = GURL(spec);
+ return true;
+}
+
+void SerializeFormFieldDataVector(const std::vector<FormFieldData> fields,
+ Pickle* pickle) {
+ pickle->WriteInt(static_cast<int>(fields.size()));
+ for (size_t i = 0; i < fields.size(); ++i) {
+ SerializeFormFieldData(fields[i], pickle);
+ }
+}
+
+bool DeserializeFormFieldDataVector(PickleIterator* iter,
+ std::vector<FormFieldData>* fields) {
+ int size;
+ if (!iter->ReadInt(&size))
+ return false;
+
+ FormFieldData temp;
+ for (int i = 0; i < size; ++i) {
+ if (!DeserializeFormFieldData(iter, &temp))
+ return false;
+
+ fields->push_back(temp);
+ }
+ return true;
+}
+
+} // namespace
+
FormData::FormData()
: user_submitted(false) {
}
@@ -37,4 +79,55 @@ bool FormData::operator!=(const FormData& form) const {
return !operator==(form);
}
+std::ostream& operator<<(std::ostream& os, const FormData& form) {
+ os << UTF16ToUTF8(form.name) << " "
+ << UTF16ToUTF8(form.method) << " "
+ << form.origin << " "
+ << form.action << " "
+ << form.user_submitted << " "
+ << "Fields:";
+ for (size_t i = 0; i < form.fields.size(); ++i) {
+ os << form.fields[i] << ",";
+ }
+ return os;
+}
+
+void SerializeFormData(const FormData& form_data, Pickle* pickle) {
+ pickle->WriteInt(kPickleVersion);
+ pickle->WriteString16(form_data.name);
+ pickle->WriteString16(form_data.method);
+ pickle->WriteString(form_data.origin.spec());
+ pickle->WriteString(form_data.action.spec());
+ pickle->WriteBool(form_data.user_submitted);
+ SerializeFormFieldDataVector(form_data.fields, pickle);
+}
+
+bool DeserializeFormData(PickleIterator* iter, FormData* form_data) {
+ int version;
+ if (!iter->ReadInt(&version)) {
+ LOG(ERROR) << "Bad pickle of FormData, no version present";
+ return false;
+ }
+
+ switch (version) {
+ case 1: {
+ if (!iter->ReadString16(&form_data->name) ||
+ !iter->ReadString16(&form_data->method) ||
+ !ReadGURL(iter, &form_data->origin) ||
+ !ReadGURL(iter, &form_data->action) ||
+ !iter->ReadBool(&form_data->user_submitted) ||
+ !DeserializeFormFieldDataVector(iter, &form_data->fields)) {
+ LOG(ERROR) << "Could not deserialize FormData from pickle";
+ return false;
+ }
+ break;
+ }
+ default: {
+ LOG(ERROR) << "Unknown FormData pickle version " << version;
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_data.h b/chromium/components/autofill/core/common/form_data.h
index 202fb8d3f95..b41619c5390 100644
--- a/chromium/components/autofill/core/common/form_data.h
+++ b/chromium/components/autofill/core/common/form_data.h
@@ -19,7 +19,7 @@ struct FormData {
FormData(const FormData& data);
~FormData();
- // Used by FormStructureTest.
+ // Used in testing.
bool operator==(const FormData& form) const;
bool operator!=(const FormData& form) const;
@@ -37,6 +37,16 @@ struct FormData {
std::vector<FormFieldData> fields;
};
+// For testing.
+std::ostream& operator<<(std::ostream& os, const FormData& form);
+
+// Serialize FormData. Used by the PasswordManager to persist FormData
+// pertaining to password forms. Serialized data is appended to |pickle|
+void SerializeFormData(const FormData& form_data, Pickle* pickle);
+// Deserialize FormData. This assumes that |iter| is currently pointing to
+// the part of a pickle created by SerializeFormData. Returns true on success.
+bool DeserializeFormData(PickleIterator* iter, FormData* form_data);
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_DATA_H__
diff --git a/chromium/components/autofill/core/common/form_data_unittest.cc b/chromium/components/autofill/core/common/form_data_unittest.cc
new file mode 100644
index 00000000000..2d4b7aacb24
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_data_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2013 The Chromium Authors. 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/pickle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(FormDataTest, SerializeAndDeserialize) {
+ FormData data;
+ data.name = ASCIIToUTF16("name");
+ data.method = ASCIIToUTF16("POST");
+ data.origin = GURL("origin");
+ data.action = GURL("action");
+ data.user_submitted = true;
+
+ FormFieldData field_data;
+ field_data.label = ASCIIToUTF16("label");
+ field_data.name = ASCIIToUTF16("name");
+ field_data.value = ASCIIToUTF16("value");
+ field_data.form_control_type = "password";
+ field_data.autocomplete_attribute = "off";
+ field_data.max_length = 200;
+ field_data.is_autofilled = true;
+ field_data.is_checked = true;
+ field_data.is_checkable = true;
+ field_data.is_focusable = true;
+ field_data.should_autocomplete = false;
+ field_data.text_direction = base::i18n::RIGHT_TO_LEFT;
+ field_data.option_values.push_back(ASCIIToUTF16("First"));
+ field_data.option_values.push_back(ASCIIToUTF16("Second"));
+ field_data.option_contents.push_back(ASCIIToUTF16("First"));
+ field_data.option_contents.push_back(ASCIIToUTF16("Second"));
+
+ data.fields.push_back(field_data);
+
+ // Change a few fields.
+ field_data.max_length = 150;
+ field_data.option_values.push_back(ASCIIToUTF16("Third"));
+ field_data.option_contents.push_back(ASCIIToUTF16("Third"));
+ data.fields.push_back(field_data);
+
+ Pickle pickle;
+ SerializeFormData(data, &pickle);
+
+ PickleIterator iter(pickle);
+ FormData actual;
+ EXPECT_TRUE(DeserializeFormData(&iter, &actual));
+
+ EXPECT_EQ(actual, data);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/form_field_data.cc b/chromium/components/autofill/core/common/form_field_data.cc
index 1de786dcf3c..afa9f53e0bc 100644
--- a/chromium/components/autofill/core/common/form_field_data.cc
+++ b/chromium/components/autofill/core/common/form_field_data.cc
@@ -4,9 +4,59 @@
#include "components/autofill/core/common/form_field_data.h"
+#include "base/pickle.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
+namespace {
+
+const int kPickleVersion = 1;
+
+void AddVectorToPickle(std::vector<base::string16> strings,
+ Pickle* pickle) {
+ pickle->WriteInt(static_cast<int>(strings.size()));
+ for (size_t i = 0; i < strings.size(); ++i) {
+ pickle->WriteString16(strings[i]);
+ }
+}
+
+bool ReadStringVector(PickleIterator* iter,
+ std::vector<base::string16>* strings) {
+ int size;
+ if (!iter->ReadInt(&size))
+ return false;
+
+ string16 pickle_data;
+ for (int i = 0; i < size; i++) {
+ if (!iter->ReadString16(&pickle_data))
+ return false;
+
+ strings->push_back(pickle_data);
+ }
+ return true;
+}
+
+bool ReadTextDirection(PickleIterator* iter,
+ base::i18n::TextDirection* direction) {
+ int pickle_data;
+ if (!iter->ReadInt(&pickle_data))
+ return false;
+
+ *direction = static_cast<base::i18n::TextDirection>(pickle_data);
+ return true;
+}
+
+bool ReadSize(PickleIterator* iter, size_t* size) {
+ uint64 pickle_data;
+ if (!iter->ReadUInt64(&pickle_data))
+ return false;
+
+ *size = static_cast<size_t>(pickle_data);
+ return true;
+}
+
+} // namespace
+
namespace autofill {
FormFieldData::FormFieldData()
@@ -43,6 +93,62 @@ bool FormFieldData::operator<(const FormFieldData& field) const {
return label < field.label;
}
+void SerializeFormFieldData(const FormFieldData& field_data,
+ Pickle* pickle) {
+ pickle->WriteInt(kPickleVersion);
+ pickle->WriteString16(field_data.label);
+ pickle->WriteString16(field_data.name);
+ pickle->WriteString16(field_data.value);
+ pickle->WriteString(field_data.form_control_type);
+ pickle->WriteString(field_data.autocomplete_attribute);
+ pickle->WriteUInt64(static_cast<uint64>(field_data.max_length));
+ pickle->WriteBool(field_data.is_autofilled);
+ pickle->WriteBool(field_data.is_checked);
+ pickle->WriteBool(field_data.is_checkable);
+ pickle->WriteBool(field_data.is_focusable);
+ pickle->WriteBool(field_data.should_autocomplete);
+ pickle->WriteInt(field_data.text_direction);
+ AddVectorToPickle(field_data.option_values, pickle);
+ AddVectorToPickle(field_data.option_contents, pickle);
+}
+
+bool DeserializeFormFieldData(PickleIterator* iter,
+ FormFieldData* field_data) {
+ int version;
+ if (!iter->ReadInt(&version)) {
+ LOG(ERROR) << "Bad pickle of FormFieldData, no version present";
+ return false;
+ }
+
+ switch (version) {
+ case 1: {
+ if (!iter->ReadString16(&field_data->label) ||
+ !iter->ReadString16(&field_data->name) ||
+ !iter->ReadString16(&field_data->value) ||
+ !iter->ReadString(&field_data->form_control_type) ||
+ !iter->ReadString(&field_data->autocomplete_attribute) ||
+ !ReadSize(iter, &field_data->max_length) ||
+ !iter->ReadBool(&field_data->is_autofilled) ||
+ !iter->ReadBool(&field_data->is_checked) ||
+ !iter->ReadBool(&field_data->is_checkable) ||
+ !iter->ReadBool(&field_data->is_focusable) ||
+ !iter->ReadBool(&field_data->should_autocomplete) ||
+ !ReadTextDirection(iter, &field_data->text_direction) ||
+ !ReadStringVector(iter, &field_data->option_values) ||
+ !ReadStringVector(iter, &field_data->option_contents)) {
+ LOG(ERROR) << "Could not deserialize FormFieldData from pickle";
+ return false;
+ }
+ break;
+ }
+ default: {
+ LOG(ERROR) << "Unknown FormFieldData pickle version " << version;
+ return false;
+ }
+ }
+ return true;
+}
+
std::ostream& operator<<(std::ostream& os, const FormFieldData& field) {
return os
<< UTF16ToUTF8(field.label)
diff --git a/chromium/components/autofill/core/common/form_field_data.h b/chromium/components/autofill/core/common/form_field_data.h
index f0e7579cd0a..1bf163e95f2 100644
--- a/chromium/components/autofill/core/common/form_field_data.h
+++ b/chromium/components/autofill/core/common/form_field_data.h
@@ -10,6 +10,9 @@
#include "base/i18n/rtl.h"
#include "base/strings/string16.h"
+class Pickle;
+class PickleIterator;
+
namespace autofill {
// Stores information about a field in a form.
@@ -45,6 +48,13 @@ struct FormFieldData {
std::vector<base::string16> option_contents;
};
+// Serialize and deserialize FormFieldData. These are used when FormData objects
+// are serialized and deserialized.
+void SerializeFormFieldData(const FormFieldData& form_field_data,
+ Pickle* serialized);
+bool DeserializeFormFieldData(PickleIterator* pickle_iterator,
+ FormFieldData* form_field_data);
+
// So we can compare FormFieldDatas with EXPECT_EQ().
std::ostream& operator<<(std::ostream& os, const FormFieldData& field);
@@ -66,4 +76,3 @@ std::ostream& operator<<(std::ostream& os, const FormFieldData& field);
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CORE_COMMON_FORM_FIELD_DATA_H_
-
diff --git a/chromium/components/autofill/core/common/form_field_data_unittest.cc b/chromium/components/autofill/core/common/form_field_data_unittest.cc
new file mode 100644
index 00000000000..7f4ef09f2d2
--- /dev/null
+++ b/chromium/components/autofill/core/common/form_field_data_unittest.cc
@@ -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.
+
+#include "components/autofill/core/common/form_field_data.h"
+
+#include "base/i18n/rtl.h"
+#include "base/pickle.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace autofill {
+
+TEST(FormFieldDataTest, SerializeAndDeserialize) {
+ FormFieldData data;
+ data.label = ASCIIToUTF16("label");
+ data.name = ASCIIToUTF16("name");
+ data.value = ASCIIToUTF16("value");
+ data.form_control_type = "password";
+ data.autocomplete_attribute = "off";
+ data.max_length = 200;
+ data.is_autofilled = true;
+ data.is_checked = true;
+ data.is_checkable = true;
+ data.is_focusable = true;
+ data.should_autocomplete = false;
+ data.text_direction = base::i18n::RIGHT_TO_LEFT;
+ data.option_values.push_back(ASCIIToUTF16("First"));
+ data.option_values.push_back(ASCIIToUTF16("Second"));
+ data.option_contents.push_back(ASCIIToUTF16("First"));
+ data.option_contents.push_back(ASCIIToUTF16("Second"));
+
+ Pickle pickle;
+ SerializeFormFieldData(data, &pickle);
+
+ PickleIterator iter(pickle);
+ FormFieldData actual;
+ EXPECT_TRUE(DeserializeFormFieldData(&iter, &actual));
+
+ EXPECT_EQ(actual, data);
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/password_form.cc b/chromium/components/autofill/core/common/password_form.cc
new file mode 100644
index 00000000000..dcbc874ac42
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_form.cc
@@ -0,0 +1,79 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ostream>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/password_form.h"
+
+namespace autofill {
+
+PasswordForm::PasswordForm()
+ : scheme(SCHEME_HTML),
+ password_autocomplete_set(true),
+ ssl_valid(false),
+ preferred(false),
+ blacklisted_by_user(false),
+ type(TYPE_MANUAL),
+ times_used(0) {
+}
+
+PasswordForm::~PasswordForm() {
+}
+
+bool PasswordForm::IsPublicSuffixMatch() const {
+ return !original_signon_realm.empty();
+}
+
+bool PasswordForm::operator==(const PasswordForm& form) const {
+ return signon_realm == form.signon_realm &&
+ origin == form.origin &&
+ action == form.action &&
+ submit_element == form.submit_element &&
+ username_element == form.username_element &&
+ username_value == form.username_value &&
+ other_possible_usernames == form.other_possible_usernames &&
+ password_element == form.password_element &&
+ password_value == form.password_value &&
+ password_autocomplete_set == form.password_autocomplete_set &&
+ old_password_element == form.old_password_element &&
+ old_password_value == form.old_password_value &&
+ ssl_valid == form.ssl_valid &&
+ preferred == form.preferred &&
+ date_created == form.date_created &&
+ blacklisted_by_user == form.blacklisted_by_user &&
+ type == form.type &&
+ times_used == form.times_used &&
+ form_data == form.form_data;
+}
+
+bool PasswordForm::operator!=(const PasswordForm& form) const {
+ return !operator==(form);
+}
+
+std::ostream& operator<<(std::ostream& os, const PasswordForm& form) {
+ return os << "scheme: " << form.scheme
+ << " signon_realm: " << form.signon_realm
+ << " origin: " << form.origin
+ << " action: " << form.action
+ << " submit_element: " << UTF16ToUTF8(form.submit_element)
+ << " username_elem: " << UTF16ToUTF8(form.username_element)
+ << " username_value: " << UTF16ToUTF8(form.username_value)
+ << " password_elem: " << UTF16ToUTF8(form.password_element)
+ << " password_value: " << UTF16ToUTF8(form.password_value)
+ << " old_password_element: "
+ << UTF16ToUTF8(form.old_password_element)
+ << " old_password_value: " << UTF16ToUTF8(form.old_password_value)
+ << " autocomplete_set:" << form.password_autocomplete_set
+ << " blacklisted: " << form.blacklisted_by_user
+ << " preferred: " << form.preferred
+ << " ssl_valid: " << form.ssl_valid
+ << " date_created: " << form.date_created.ToDoubleT()
+ << " type: " << form.type
+ << " times_used: " << form.times_used
+ << " form_data: " << form.form_data;
+}
+
+} // namespace autofill
diff --git a/chromium/components/autofill/core/common/password_form.h b/chromium/components/autofill/core/common/password_form.h
new file mode 100644
index 00000000000..8485225a35f
--- /dev/null
+++ b/chromium/components/autofill/core/common/password_form.h
@@ -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.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_H__
+#define COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/time/time.h"
+#include "components/autofill/core/common/form_data.h"
+#include "url/gurl.h"
+
+namespace autofill {
+
+// The PasswordForm struct encapsulates information about a login form,
+// which can be an HTML form or a dialog with username/password text fields.
+//
+// The Web Data database stores saved username/passwords and associated form
+// metdata using a PasswordForm struct, typically one that was created from
+// a parsed HTMLFormElement or LoginDialog, but the saved entries could have
+// also been created by imported data from another browser.
+//
+// The PasswordManager implements a fuzzy-matching algorithm to compare saved
+// PasswordForm entries against PasswordForms that were created from a parsed
+// HTML or dialog form. As one might expect, the more data contained in one
+// of the saved PasswordForms, the better the job the PasswordManager can do
+// in matching it against the actual form it was saved on, and autofill
+// accurately. But it is not always possible, especially when importing from
+// other browsers with different data models, to copy over all the information
+// about a particular "saved password entry" to our PasswordForm
+// representation.
+//
+// The field descriptions in the struct specification below are intended to
+// describe which fields are not strictly required when adding a saved password
+// entry to the database and how they can affect the matching process.
+
+struct PasswordForm {
+ // Enum to differentiate between HTML form based authentication, and dialogs
+ // using basic or digest schemes. Default is SCHEME_HTML. Only PasswordForms
+ // of the same Scheme will be matched/autofilled against each other.
+ enum Scheme {
+ SCHEME_HTML,
+ SCHEME_BASIC,
+ SCHEME_DIGEST,
+ SCHEME_OTHER
+ } scheme;
+
+ // The "Realm" for the sign-on (scheme, host, port for SCHEME_HTML, and
+ // contains the HTTP realm for dialog-based forms).
+ // The signon_realm is effectively the primary key used for retrieving
+ // data from the database, so it must not be empty.
+ std::string signon_realm;
+
+ // The original "Realm" for the sign-on (scheme, host, port for SCHEME_HTML,
+ // and contains the HTTP realm for dialog-based forms). This realm is only set
+ // when two PasswordForms are matched when trying to find a login/pass pair
+ // for a site. It is only set to a non-empty value during a match of the
+ // original stored login/pass and the current observed form if all these
+ // statements are true:
+ // 1) The full signon_realm is not the same.
+ // 2) The registry controlled domain is the same. For example; example.com,
+ // m.example.com, foo.login.example.com and www.example.com would all resolve
+ // to example.com since .com is the public suffix.
+ // 3) The scheme is the same.
+ // 4) The port is the same.
+ // For example, if there exists a stored password for http://www.example.com
+ // (where .com is the public suffix) and the observed form is
+ // http://m.example.com, |original_signon_realm| must be set to
+ // http://www.example.com.
+ std::string original_signon_realm;
+
+ // The URL (minus query parameters) containing the form. This is the primary
+ // data used by the PasswordManager to decide (in longest matching prefix
+ // fashion) whether or not a given PasswordForm result from the database is a
+ // good fit for a particular form on a page, so it must not be empty.
+ GURL origin;
+
+ // The action target of the form. This is the primary data used by the
+ // PasswordManager for form autofill; that is, the action of the saved
+ // credentials must match the action of the form on the page to be autofilled.
+ // If this is empty / not available, it will result in a "restricted"
+ // IE-like autofill policy, where we wait for the user to type in his
+ // username before autofilling the password. In these cases, after successful
+ // login the action URL will automatically be assigned by the
+ // PasswordManager.
+ //
+ // When parsing an HTML form, this must always be set.
+ GURL action;
+
+ // The name of the submit button used. Optional; only used in scoring
+ // of PasswordForm results from the database to make matches as tight as
+ // possible.
+ //
+ // When parsing an HTML form, this must always be set.
+ string16 submit_element;
+
+ // The name of the username input element. Optional (improves scoring).
+ //
+ // When parsing an HTML form, this must always be set.
+ string16 username_element;
+
+ // The username. Optional.
+ //
+ // When parsing an HTML form, this is typically empty unless the site
+ // has implemented some form of autofill.
+ string16 username_value;
+
+ // This member is populated in cases where we there are multiple input
+ // elements that could possibly be the username. Used when our heuristics for
+ // determining the username are incorrect. Optional.
+ //
+ // When parsing an HTML form, this is typically empty.
+ std::vector<string16> other_possible_usernames;
+
+ // The name of the password input element, Optional (improves scoring).
+ //
+ // When parsing an HTML form, this must always be set.
+ string16 password_element;
+
+ // The password. Required.
+ //
+ // When parsing an HTML form, this is typically empty.
+ string16 password_value;
+
+ // False if autocomplete is set to "off" for the password input element;
+ // True otherwise.
+ bool password_autocomplete_set;
+
+ // If the form was a change password form, the name of the
+ // 'old password' input element. Optional.
+ string16 old_password_element;
+
+ // The old password. Optional.
+ string16 old_password_value;
+
+ // Whether or not this login was saved under an HTTPS session with a valid
+ // SSL cert. We will never match or autofill a PasswordForm where
+ // ssl_valid == true with a PasswordForm where ssl_valid == false. This means
+ // passwords saved under HTTPS will never get autofilled onto an HTTP page.
+ // When importing, this should be set to true if the page URL is HTTPS, thus
+ // giving it "the benefit of the doubt" that the SSL cert was valid when it
+ // was saved. Default to false.
+ bool ssl_valid;
+
+ // True if this PasswordForm represents the last username/password login the
+ // user selected to log in to the site. If there is only one saved entry for
+ // the site, this will always be true, but when there are multiple entries
+ // the PasswordManager ensures that only one of them has a preferred bit set
+ // to true. Default to false.
+ //
+ // When parsing an HTML form, this is not used.
+ bool preferred;
+
+ // When the login was saved (by chrome).
+ //
+ // When parsing an HTML form, this is not used.
+ base::Time date_created;
+
+ // Tracks if the user opted to never remember passwords for this form. Default
+ // to false.
+ //
+ // When parsing an HTML form, this is not used.
+ bool blacklisted_by_user;
+
+ // Enum to differentiate between manually filled forms and forms with auto
+ // generated passwords.
+ enum Type {
+ TYPE_MANUAL,
+ TYPE_GENERATED,
+ };
+
+ // The form type. Not used yet. Please see http://crbug.com/152422
+ Type type;
+
+ // The number of times that this username/password has been used to
+ // authenticate the user.
+ //
+ // When parsing an HTML form, this is not used.
+ int times_used;
+
+ // Autofill representation of this form. Used to communicate with the
+ // Autofill servers if necessary. Currently this is only used to help
+ // determine forms where we can trigger password generation.
+ //
+ // When parsing an HTML form, this is normally set.
+ FormData form_data;
+
+ // Returns true if this match was found using public suffix matching.
+ bool IsPublicSuffixMatch() const;
+
+ // Equality operators for testing.
+ bool operator==(const PasswordForm& form) const;
+ bool operator!=(const PasswordForm& form) const;
+
+ PasswordForm();
+ ~PasswordForm();
+};
+
+// Map username to PasswordForm* for convenience. See password_form_manager.h.
+typedef std::map<string16, PasswordForm*> PasswordFormMap;
+
+// For testing.
+std::ostream& operator<<(std::ostream& os,
+ const autofill::PasswordForm& form);
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CORE_COMMON_PASSWORD_FORM_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
index 80b26989cce..7dbc97d0ab6 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data.cc
+++ b/chromium/components/autofill/core/common/password_form_fill_data.cc
@@ -30,9 +30,9 @@ PasswordFormFillData::~PasswordFormFillData() {
}
void InitPasswordFormFillData(
- const content::PasswordForm& form_on_page,
- const content::PasswordFormMap& matches,
- const content::PasswordForm* const preferred_match,
+ const PasswordForm& form_on_page,
+ const PasswordFormMap& matches,
+ const PasswordForm* const preferred_match,
bool wait_for_username_before_autofill,
bool enable_other_possible_usernames,
PasswordFormFillData* result) {
@@ -57,7 +57,7 @@ void InitPasswordFormFillData(
result->preferred_realm = preferred_match->original_signon_realm;
// Copy additional username/value pairs.
- content::PasswordFormMap::const_iterator iter;
+ PasswordFormMap::const_iterator iter;
for (iter = matches.begin(); iter != matches.end(); iter++) {
if (iter->second != preferred_match) {
PasswordAndRealm value;
diff --git a/chromium/components/autofill/core/common/password_form_fill_data.h b/chromium/components/autofill/core/common/password_form_fill_data.h
index 9bcd5388d7a..14c4759865b 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data.h
+++ b/chromium/components/autofill/core/common/password_form_fill_data.h
@@ -9,7 +9,7 @@
#include "base/memory/scoped_ptr.h"
#include "components/autofill/core/common/form_data.h"
-#include "content/public/common/password_form.h"
+#include "components/autofill/core/common/password_form.h"
namespace autofill {
@@ -75,9 +75,9 @@ struct PasswordFormFillData {
// 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,
+ const PasswordForm& form_on_page,
+ const PasswordFormMap& matches,
+ const PasswordForm* const preferred_match,
bool wait_for_username_before_autofill,
bool enable_other_possible_usernames,
PasswordFormFillData* result);
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
index 1242774b12a..7c30f81f3e1 100644
--- a/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
+++ b/chromium/components/autofill/core/common/password_form_fill_data_unittest.cc
@@ -5,13 +5,10 @@
#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 "components/autofill/core/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
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
index 1031f4e8310..66134233c76 100644
--- a/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc
+++ b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.cc
@@ -40,9 +40,22 @@ void BrowserContextDependencyManager::AddEdge(
}
void BrowserContextDependencyManager::CreateBrowserContextServices(
- content::BrowserContext* context, bool is_testing_context) {
+ content::BrowserContext* context) {
+ DoCreateBrowserContextServices(context, false, false);
+}
+
+void BrowserContextDependencyManager::CreateBrowserContextServicesForTest(
+ content::BrowserContext* context,
+ bool force_register_prefs) {
+ DoCreateBrowserContextServices(context, true, force_register_prefs);
+}
+
+void BrowserContextDependencyManager::DoCreateBrowserContextServices(
+ content::BrowserContext* context,
+ bool is_testing_context,
+ bool force_register_prefs) {
TRACE_EVENT0("browser",
- "BrowserContextDependencyManager::CreateBrowserContextServices")
+ "BrowserContextDependencyManager::DoCreateBrowserContextServices")
#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
@@ -64,9 +77,12 @@ void BrowserContextDependencyManager::CreateBrowserContextServices(
BrowserContextKeyedBaseFactory* factory =
static_cast<BrowserContextKeyedBaseFactory*>(construction_order[i]);
- if (!context->IsOffTheRecord()) {
+ if (!context->IsOffTheRecord() || force_register_prefs) {
// We only register preferences on normal contexts because the incognito
- // context shares the pref service with the normal one.
+ // context shares the pref service with the normal one. Always register
+ // for standalone testing contexts (testing contexts that don't have an
+ // "original" profile set) as otherwise the preferences won't be
+ // registered.
factory->RegisterUserPrefsOnBrowserContext(context);
}
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
index 3e96eff98b5..5482aaa9283 100644
--- a/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h
+++ b/chromium/components/browser_context_keyed_service/browser_context_dependency_manager.h
@@ -34,12 +34,22 @@ class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT BrowserContextDependencyManager {
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);
+ // want to be started when a context is created. If you want your
+ // BrowserContextKeyedService to be started with the BrowserContext, override
+ // BrowserContextKeyedBaseFactory::ServiceIsCreatedWithBrowserContext() to
+ // return true. This method also registers any service-related preferences
+ // for non-incognito profiles.
+ void CreateBrowserContextServices(content::BrowserContext* context);
+
+ // Similar to CreateBrowserContextServices(), except this is used for creating
+ // test BrowserContexts - these contexts will not create services for any
+ // BrowserContextKeyedBaseFactories that return true from
+ // ServiceIsNULLWhileTesting(). Callers can pass |force_register_prefs| as
+ // true to have preferences registered even for incognito profiles - this
+ // allows tests to specify a standalone incognito profile without an
+ // associated normal profile.
+ void CreateBrowserContextServicesForTest(content::BrowserContext* context,
+ bool force_register_prefs);
// Called by each BrowserContext to alert us that we should destroy services
// associated with it.
@@ -58,6 +68,11 @@ class BROWSER_CONTEXT_KEYED_SERVICE_EXPORT BrowserContextDependencyManager {
friend class BrowserContextDependencyManagerUnittests;
friend struct DefaultSingletonTraits<BrowserContextDependencyManager>;
+ // Helper function used by CreateBrowserContextServices[ForTest].
+ void DoCreateBrowserContextServices(content::BrowserContext* context,
+ bool is_testing_context,
+ bool force_register_prefs);
+
BrowserContextDependencyManager();
virtual ~BrowserContextDependencyManager();
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
index e6caa69d63c..cc3f487d188 100644
--- 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
@@ -68,7 +68,6 @@ void BrowserContextKeyedBaseFactory::RegisterUserPrefsOnBrowserContext(
// 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);
diff --git a/chromium/components/component_strings.grd b/chromium/components/component_strings.grd
index ba3840e2dbb..28e2a464953 100644
--- a/chromium/components/component_strings.grd
+++ b/chromium/components/component_strings.grd
@@ -33,7 +33,13 @@
<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" />
+ <if expr="is_ios">
+ <!-- iOS uses es-MX for es-419 -->
+ <output filename="component_strings_es-MX.pak" type="data_package" lang="es-419" />
+ </if>
+ <if expr="not is_ios">
+ <output filename="component_strings_es-419.pak" type="data_package" lang="es-419" />
+ </if>
<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" />
@@ -77,11 +83,11 @@
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')">
+ <if expr="is_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')">
+ <if expr="not is_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" />
@@ -162,6 +168,7 @@
<release seq="1" allow_pseudo="false">
<messages fallback_to_english="true">
<part file="autofill_strings.grdp" />
+ <part file="dom_distiller_strings.grdp" />
</messages>
</release>
</grit>
diff --git a/chromium/components/components.gyp b/chromium/components/components.gyp
index 61c496c19d6..0bfe536e2f9 100644
--- a/chromium/components/components.gyp
+++ b/chromium/components/components.gyp
@@ -15,9 +15,14 @@
'breakpad.gypi',
'browser_context_keyed_service.gypi',
'components_tests.gypi',
+ 'dom_distiller.gypi',
+ 'json_schema.gypi',
'navigation_interception.gypi',
+ 'policy.gypi',
'sessions.gypi',
+ 'startup_metric_utils.gypi',
'user_prefs.gypi',
+ 'variations.gypi',
'visitedlink.gypi',
'webdata.gypi',
'web_contents_delegate_android.gypi',
diff --git a/chromium/components/components_tests.gypi b/chromium/components/components_tests.gypi
index b8e560008b9..491e21445d6 100644
--- a/chromium/components/components_tests.gypi
+++ b/chromium/components/components_tests.gypi
@@ -10,12 +10,23 @@
'target_name': 'components_unittests',
'type': '<(gtest_target_type)',
'sources': [
+ 'autofill/core/common/form_data_unittest.cc',
+ 'autofill/core/common/form_field_data_unittest.cc',
'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',
+ 'dom_distiller/core/dom_distiller_database_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',
+ # TODO(asvitkine): These should be tested on iOS too.
+ 'variations/entropy_provider_unittest.cc',
+ 'variations/metrics_util_unittest.cc',
+ 'variations/variations_associated_data_unittest.cc',
+ 'variations/variations_seed_processor_unittest.cc',
'visitedlink/test/visitedlink_unittest.cc',
'webdata/encryptor/encryptor_password_mac_unittest.cc',
'webdata/encryptor/encryptor_unittest.cc',
@@ -29,25 +40,41 @@
'../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
+ # Dependencies of autofill
+ 'autofill_core_common',
+
# Dependencies of auto_login_parser
'auto_login_parser',
# Dependencies of browser_context_keyed_service
'browser_context_keyed_service',
+ # Dependencies of dom_distiller
+ 'dom_distiller_core',
+ 'dom_distiller_core_proto',
+
# 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 variations
+ 'variations',
+
# Dependencies of visitedlink
'visitedlink_browser',
'visitedlink_renderer',
@@ -87,6 +114,12 @@
'ldflags': ['-rdynamic'],
},
}],
+ ['configuration_policy==1', {
+ 'sources': [
+ 'policy/core/common/policy_schema_unittest.cc',
+ 'policy/core/common/schema_unittest.cc',
+ ],
+ }],
],
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [4267, ],
diff --git a/chromium/components/components_unittests.isolate b/chromium/components/components_unittests.isolate
index 0c18d8fe838..f60a380f60d 100644
--- a/chromium/components/components_unittests.isolate
+++ b/chromium/components/components_unittests.isolate
@@ -8,6 +8,9 @@
'isolate_dependency_tracked': [
'<(PRODUCT_DIR)/content_resources.pak',
],
+ 'isolate_dependency_untracked': [
+ 'test/data/',
+ ],
},
}],
],
diff --git a/chromium/components/dom_distiller.gypi b/chromium/components/dom_distiller.gypi
new file mode 100644
index 00000000000..d4f9f44d893
--- /dev/null
+++ b/chromium/components/dom_distiller.gypi
@@ -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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'dom_distiller_webui',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'component_strings.gyp:component_strings',
+ 'dom_distiller_core',
+ 'dom_distiller_resources',
+ '../base/base.gyp:base',
+ '../content/content.gyp:content_browser',
+ '../skia/skia.gyp:skia',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'dom_distiller/webui/dom_distiller_ui.cc',
+ 'dom_distiller/webui/dom_distiller_ui.h',
+ 'dom_distiller/webui/dom_distiller_handler.cc',
+ 'dom_distiller/webui/dom_distiller_handler.h',
+ ],
+ },
+ {
+ 'target_name': 'dom_distiller_resources',
+ 'type': 'none',
+ 'variables': {
+ 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/components',
+ },
+ 'actions': [
+ {
+ 'action_name': 'dom_distiller_resources',
+ 'variables': {
+ 'grit_grd_file': 'dom_distiller_resources.grd',
+ },
+ 'includes': [ '../build/grit_action.gypi' ],
+ },
+ ],
+ 'includes': [ '../build/grit_target.gypi' ],
+ },
+ {
+ 'target_name': 'dom_distiller_core',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'dom_distiller_core_proto',
+ '../base/base.gyp:base',
+ '../third_party/protobuf/protobuf.gyp:protobuf_lite',
+ '../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'dom_distiller/core/dom_distiller_constants.cc',
+ 'dom_distiller/core/dom_distiller_constants.h',
+ 'dom_distiller/core/dom_distiller_database.cc',
+ 'dom_distiller/core/dom_distiller_database.h',
+ ],
+ },
+ {
+ 'target_name': 'dom_distiller_core_proto',
+ 'type': 'static_library',
+ 'sources': [
+ 'dom_distiller/core/proto/article_entry.proto',
+ ],
+ 'variables': {
+ 'proto_in_dir': 'dom_distiller/core/proto',
+ 'proto_out_dir': 'components/dom_distiller/core/proto',
+ },
+ 'includes': [ '../build/protoc.gypi', ],
+ },
+ ],
+}
diff --git a/chromium/components/dom_distiller/DEPS b/chromium/components/dom_distiller/DEPS
new file mode 100644
index 00000000000..c53eb5fd098
--- /dev/null
+++ b/chromium/components/dom_distiller/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+grit", # For generated headers.
+ "+third_party/leveldatabase/src/include",
+
+ # The dom distiller is a layered component; subdirectories must explicitly
+ # introduce the ability to use the content layer as appropriate.
+ # http://www.chromium.org/developers/design-documents/layered-components-design
+ "-components/dom_distiller",
+ "+components/dom_distiller/core",
+ "-content/public",
+]
diff --git a/chromium/components/dom_distiller/OWNERS b/chromium/components/dom_distiller/OWNERS
new file mode 100644
index 00000000000..f2ce81c073c
--- /dev/null
+++ b/chromium/components/dom_distiller/OWNERS
@@ -0,0 +1,3 @@
+bengr@chromium.org
+cjhopman@chromium.org
+nyquist@chromium.org
diff --git a/chromium/components/dom_distiller/README b/chromium/components/dom_distiller/README
new file mode 100644
index 00000000000..e8d7bb37f6b
--- /dev/null
+++ b/chromium/components/dom_distiller/README
@@ -0,0 +1,12 @@
+The DOM Distiller component contains code for an experimental prototype for
+distilling the core part of a web page.
+
+To enable this feature, use the command line flag --enable-distiller.
+
+The DOM Distiller is a layered component. See:
+http://www.chromium.org/developers/design-documents/layered-components-design
+
+Folder structure:
+
+ core/ contains the business logic of the component.
+ webui/ contains the WebUI code and resources for the debug page.
diff --git a/chromium/components/dom_distiller/core/dom_distiller_constants.cc b/chromium/components/dom_distiller/core/dom_distiller_constants.cc
new file mode 100644
index 00000000000..5dea000fe1b
--- /dev/null
+++ b/chromium/components/dom_distiller/core/dom_distiller_constants.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/dom_distiller/core/dom_distiller_constants.h"
+
+namespace dom_distiller {
+
+const char kChromeUIDomDistillerURL[] = "chrome://dom-distiller/";
+const char kChromeUIDomDistillerHost[] = "dom-distiller";
+
+} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/core/dom_distiller_constants.h b/chromium/components/dom_distiller/core/dom_distiller_constants.h
new file mode 100644
index 00000000000..c39c88e359d
--- /dev/null
+++ b/chromium/components/dom_distiller/core/dom_distiller_constants.h
@@ -0,0 +1,15 @@
+// Copyright 2013 The Chromium 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_DOM_DISTILLER_CORE_DOM_DISTILLER_CONSTANTS_H_
+#define COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_CONSTANTS_H_
+
+namespace dom_distiller {
+
+extern const char kChromeUIDomDistillerURL[];
+extern const char kChromeUIDomDistillerHost[];
+
+} // namespace dom_distiller
+
+#endif // COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_CONSTANTS_H_
diff --git a/chromium/components/dom_distiller/core/dom_distiller_database.cc b/chromium/components/dom_distiller/core/dom_distiller_database.cc
new file mode 100644
index 00000000000..ba85337b705
--- /dev/null
+++ b/chromium/components/dom_distiller/core/dom_distiller_database.cc
@@ -0,0 +1,228 @@
+// Copyright 2013 The Chromium Authors. 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/dom_distiller/core/dom_distiller_database.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "components/dom_distiller/core/proto/article_entry.pb.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+#include "third_party/leveldatabase/src/include/leveldb/iterator.h"
+#include "third_party/leveldatabase/src/include/leveldb/options.h"
+#include "third_party/leveldatabase/src/include/leveldb/slice.h"
+#include "third_party/leveldatabase/src/include/leveldb/status.h"
+#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
+
+using base::MessageLoop;
+using base::SequencedTaskRunner;
+
+namespace dom_distiller {
+
+DomDistillerDatabase::LevelDB::LevelDB() {}
+
+DomDistillerDatabase::LevelDB::~LevelDB() {}
+
+bool DomDistillerDatabase::LevelDB::Init(const base::FilePath& database_dir) {
+ leveldb::Options options;
+ options.create_if_missing = true;
+ options.max_open_files = 0; // Use minimum.
+
+ std::string path = database_dir.AsUTF8Unsafe();
+
+ leveldb::DB* db = NULL;
+ leveldb::Status status = leveldb::DB::Open(options, path, &db);
+ if (status.IsCorruption()) {
+ LOG(WARNING) << "Deleting possibly-corrupt database";
+ base::DeleteFile(database_dir, true);
+ status = leveldb::DB::Open(options, path, &db);
+ }
+
+ if (status.ok()) {
+ CHECK(db);
+ db_.reset(db);
+ return true;
+ }
+
+ LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
+ << status.ToString();
+ return false;
+}
+
+bool DomDistillerDatabase::LevelDB::Save(const EntryVector& entries) {
+ leveldb::WriteBatch updates;
+ for (EntryVector::const_iterator it = entries.begin(); it != entries.end();
+ ++it) {
+ updates.Put(leveldb::Slice(it->entry_id()),
+ leveldb::Slice(it->SerializeAsString()));
+ }
+
+ leveldb::WriteOptions options;
+ options.sync = true;
+ leveldb::Status status = db_->Write(options, &updates);
+ if (status.ok())
+ return true;
+
+ LOG(WARNING) << "Failed writing dom_distiller entries: " << status.ToString();
+ return false;
+}
+
+bool DomDistillerDatabase::LevelDB::Load(EntryVector* entries) {
+ leveldb::ReadOptions options;
+ scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
+ for (db_iterator->SeekToFirst(); db_iterator->Valid(); db_iterator->Next()) {
+ leveldb::Slice value_slice = db_iterator->value();
+
+ ArticleEntry entry;
+ if (!entry.ParseFromArray(value_slice.data(), value_slice.size())) {
+ LOG(WARNING) << "Unable to parse dom_distiller entry "
+ << db_iterator->key().ToString();
+ // TODO(cjhopman): Decide what to do about un-parseable entries.
+ }
+ entries->push_back(entry);
+ }
+ return true;
+}
+
+DomDistillerDatabase::DomDistillerDatabase(
+ scoped_refptr<base::SequencedTaskRunner> task_runner)
+ : task_runner_(task_runner), weak_ptr_factory_(this) {
+ main_loop_ = MessageLoop::current();
+}
+
+void DomDistillerDatabase::Destroy() {
+ DCHECK(IsRunOnMainLoop());
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ task_runner_->PostNonNestableTask(
+ FROM_HERE,
+ base::Bind(&DomDistillerDatabase::DestroyFromTaskRunner,
+ base::Unretained(this)));
+}
+
+void DomDistillerDatabase::Init(const base::FilePath& database_dir,
+ InitCallback callback) {
+ InitWithDatabase(scoped_ptr<Database>(new LevelDB()), database_dir, callback);
+}
+
+namespace {
+
+void RunInitCallback(DomDistillerDatabaseInterface::InitCallback callback,
+ const bool* success) {
+ callback.Run(*success);
+}
+
+void RunSaveCallback(DomDistillerDatabaseInterface::SaveCallback callback,
+ const bool* success) {
+ callback.Run(*success);
+}
+
+void RunLoadCallback(DomDistillerDatabaseInterface::LoadCallback callback,
+ const bool* success,
+ scoped_ptr<EntryVector> entries) {
+ callback.Run(*success, entries.Pass());
+}
+
+} // namespace
+
+void DomDistillerDatabase::InitWithDatabase(scoped_ptr<Database> database,
+ const base::FilePath& database_dir,
+ InitCallback callback) {
+ DCHECK(IsRunOnMainLoop());
+ DCHECK(!db_);
+ DCHECK(database);
+ db_.reset(database.release());
+ bool* success = new bool(false);
+ task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DomDistillerDatabase::InitFromTaskRunner,
+ base::Unretained(this),
+ database_dir,
+ success),
+ base::Bind(RunInitCallback, callback, base::Owned(success)));
+}
+
+void DomDistillerDatabase::SaveEntries(scoped_ptr<EntryVector> entries,
+ SaveCallback callback) {
+ DCHECK(IsRunOnMainLoop());
+ bool* success = new bool(false);
+ task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DomDistillerDatabase::SaveEntriesFromTaskRunner,
+ base::Unretained(this),
+ base::Passed(&entries),
+ success),
+ base::Bind(RunSaveCallback, callback, base::Owned(success)));
+}
+
+void DomDistillerDatabase::LoadEntries(LoadCallback callback) {
+ DCHECK(IsRunOnMainLoop());
+
+ bool* success = new bool(false);
+
+ scoped_ptr<EntryVector> entries(new EntryVector());
+ // Get this pointer before entries is base::Passed() so we can use it below.
+ EntryVector* entries_ptr = entries.get();
+
+ task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&DomDistillerDatabase::LoadEntriesFromTaskRunner,
+ base::Unretained(this),
+ entries_ptr,
+ success),
+ base::Bind(RunLoadCallback,
+ callback,
+ base::Owned(success),
+ base::Passed(&entries)));
+}
+
+DomDistillerDatabase::~DomDistillerDatabase() { DCHECK(IsRunByTaskRunner()); }
+
+bool DomDistillerDatabase::IsRunByTaskRunner() const {
+ return task_runner_->RunsTasksOnCurrentThread();
+}
+
+bool DomDistillerDatabase::IsRunOnMainLoop() const {
+ return MessageLoop::current() == main_loop_;
+}
+
+void DomDistillerDatabase::DestroyFromTaskRunner() {
+ DCHECK(IsRunByTaskRunner());
+ delete this;
+}
+
+void DomDistillerDatabase::InitFromTaskRunner(
+ const base::FilePath& database_dir,
+ bool* success) {
+ DCHECK(IsRunByTaskRunner());
+ DCHECK(success);
+
+ VLOG(1) << "Opening " << database_dir.value();
+
+ // TODO(cjhopman): Histogram for database size.
+ *success = db_->Init(database_dir);
+}
+
+void DomDistillerDatabase::SaveEntriesFromTaskRunner(
+ scoped_ptr<EntryVector> entries,
+ bool* success) {
+ DCHECK(IsRunByTaskRunner());
+ DCHECK(success);
+ VLOG(1) << "Saving " << entries->size() << " entry(ies) to database ";
+ *success = db_->Save(*entries);
+}
+
+void DomDistillerDatabase::LoadEntriesFromTaskRunner(EntryVector* entries,
+ bool* success) {
+ DCHECK(IsRunByTaskRunner());
+ DCHECK(success);
+ DCHECK(entries);
+
+ entries->clear();
+ *success = db_->Load(entries);
+}
+
+} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/core/dom_distiller_database.h b/chromium/components/dom_distiller/core/dom_distiller_database.h
new file mode 100644
index 00000000000..2fb06c61a6b
--- /dev/null
+++ b/chromium/components/dom_distiller/core/dom_distiller_database.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_DOM_DISTILLER_CORE_DOM_DISTILLER_DATABASE_H_
+#define COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_DATABASE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace leveldb {
+class DB;
+}
+
+namespace dom_distiller {
+
+class ArticleEntry;
+typedef std::vector<ArticleEntry> EntryVector;
+
+// Interface for classes providing persistent storage of DomDistiller entries.
+class DomDistillerDatabaseInterface {
+ public:
+ typedef std::vector<std::string> ArticleEntryIds;
+ typedef base::Callback<void(bool success)> InitCallback;
+ typedef base::Callback<void(bool success)> SaveCallback;
+ typedef base::Callback<void(bool success, scoped_ptr<EntryVector>)>
+ LoadCallback;
+
+ // Asynchronously destroys the object after all in-progress file operations
+ // have completed. The callbacks for in-progress operations will still be
+ // called.
+ virtual void Destroy() {}
+
+ // Asynchronously initializes the object. |callback| will be invoked on the UI
+ // thread when complete.
+ virtual void Init(const base::FilePath& database_dir,
+ InitCallback callback) = 0;
+
+ // Asynchronously saves |entries_to_save| database. |callback| will be invoked
+ // on the UI thread when complete.
+ virtual void SaveEntries(scoped_ptr<EntryVector> entries_to_save,
+ SaveCallback callback) = 0;
+
+ // Asynchronously loads all entries from the database and invokes |callback|
+ // when complete.
+ virtual void LoadEntries(LoadCallback callback) = 0;
+
+ protected:
+ virtual ~DomDistillerDatabaseInterface() {}
+};
+
+class DomDistillerDatabase
+ : public DomDistillerDatabaseInterface {
+ public:
+ // The underlying database. Calls to this type may be blocking.
+ class Database {
+ public:
+ virtual bool Init(const base::FilePath& database_dir) = 0;
+ virtual bool Save(const EntryVector& entries) = 0;
+ virtual bool Load(EntryVector* entries) = 0;
+ virtual ~Database() {}
+ };
+
+ class LevelDB : public Database {
+ public:
+ LevelDB();
+ virtual ~LevelDB();
+ virtual bool Init(const base::FilePath& database_dir) OVERRIDE;
+ virtual bool Save(const EntryVector& entries) OVERRIDE;
+ virtual bool Load(EntryVector* entries) OVERRIDE;
+
+ private:
+
+ scoped_ptr<leveldb::DB> db_;
+ };
+
+ DomDistillerDatabase(scoped_refptr<base::SequencedTaskRunner> task_runner);
+
+ // DomDistillerDatabaseInterface implementation.
+ virtual void Destroy() OVERRIDE;
+ virtual void Init(const base::FilePath& database_dir,
+ InitCallback callback) OVERRIDE;
+ virtual void SaveEntries(scoped_ptr<EntryVector> entries_to_save,
+ SaveCallback callback) OVERRIDE;
+ virtual void LoadEntries(LoadCallback callback) OVERRIDE;
+
+ // Allow callers to provide their own Database implementation.
+ void InitWithDatabase(scoped_ptr<Database> database,
+ const base::FilePath& database_dir,
+ InitCallback callback);
+
+ protected:
+ virtual ~DomDistillerDatabase();
+
+ private:
+ // Whether currently being run by |task_runner_|.
+ bool IsRunByTaskRunner() const;
+
+ // Whether currently being run on |main_loop_|.
+ bool IsRunOnMainLoop() const;
+
+ // Deletes |this|.
+ void DestroyFromTaskRunner();
+
+ // Initializes the database in |database_dir| and updates |success|.
+ void InitFromTaskRunner(const base::FilePath& database_dir, bool* success);
+
+ // Saves data to disk and updates |success|.
+ void SaveEntriesFromTaskRunner(scoped_ptr<EntryVector> entries_to_save,
+ bool* success);
+
+ // Loads entries from disk and updates |success|.
+ void LoadEntriesFromTaskRunner(EntryVector* entries, bool* success);
+
+ // The MessageLoop that the database was created on.
+ base::MessageLoop* main_loop_;
+
+ // Used to run blocking tasks in-order.
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ scoped_ptr<Database> db_;
+
+ // Note: This should remain the last member so it'll be destroyed and
+ // invalidate its weak pointers before any other members are destroyed.
+ base::WeakPtrFactory<DomDistillerDatabase> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomDistillerDatabase);
+};
+
+} // namespace dom_distiller
+
+#endif // COMPONENTS_DOM_DISTILLER_CORE_DOM_DISTILLER_DATABASE_H_
diff --git a/chromium/components/dom_distiller/core/dom_distiller_database_unittest.cc b/chromium/components/dom_distiller/core/dom_distiller_database_unittest.cc
new file mode 100644
index 00000000000..906596800c7
--- /dev/null
+++ b/chromium/components/dom_distiller/core/dom_distiller_database_unittest.cc
@@ -0,0 +1,305 @@
+// Copyright 2013 The Chromium Authors. 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/dom_distiller/core/dom_distiller_database.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "components/dom_distiller/core/proto/article_entry.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::MessageLoop;
+using base::ScopedTempDir;
+using testing::Invoke;
+using testing::Return;
+using testing::_;
+
+namespace dom_distiller {
+
+namespace {
+
+typedef std::map<std::string, ArticleEntry> EntryMap;
+
+class MockDB : public DomDistillerDatabase::Database {
+ public:
+ MOCK_METHOD1(Init, bool(const base::FilePath&));
+ MOCK_METHOD1(Save, bool(const EntryVector&));
+ MOCK_METHOD1(Load, bool(EntryVector*));
+
+ MockDB() {
+ ON_CALL(*this, Init(_)).WillByDefault(Return(true));
+ ON_CALL(*this, Save(_)).WillByDefault(Return(true));
+ ON_CALL(*this, Load(_)).WillByDefault(Return(true));
+ }
+
+ bool LoadEntries(EntryVector* entries);
+};
+
+class MockDatabaseCaller {
+ public:
+ MOCK_METHOD1(InitCallback, void(bool));
+ MOCK_METHOD1(SaveCallback, void(bool));
+ void LoadCallback(bool success, scoped_ptr<EntryVector> entries) {
+ LoadCallback1(success, entries.get());
+ }
+ MOCK_METHOD2(LoadCallback1, void(bool, EntryVector*));
+};
+
+} // namespace
+
+EntryMap GetSmallModel() {
+ EntryMap model;
+
+ model["key0"].set_entry_id("key0");
+ model["key0"].add_pages()->set_url("http://foo.com/1");
+ model["key0"].add_pages()->set_url("http://foo.com/2");
+ model["key0"].add_pages()->set_url("http://foo.com/3");
+
+ model["key1"].set_entry_id("key1");
+ model["key1"].add_pages()->set_url("http://bar.com/all");
+
+ model["key2"].set_entry_id("key2");
+ model["key2"].add_pages()->set_url("http://baz.com/1");
+
+ return model;
+}
+
+void ExpectEntryPointersEquals(EntryMap expected, const EntryVector& actual) {
+ EXPECT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < actual.size(); i++) {
+ EntryMap::iterator expected_it =
+ expected.find(std::string(actual[i].entry_id()));
+ EXPECT_TRUE(expected_it != expected.end());
+ std::string serialized_expected = expected_it->second.SerializeAsString();
+ std::string serialized_actual = actual[i].SerializeAsString();
+ EXPECT_EQ(serialized_expected, serialized_actual);
+ expected.erase(expected_it);
+ }
+}
+
+class DomDistillerDatabaseTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ main_loop_.reset(new MessageLoop());
+ db_ = new DomDistillerDatabase(main_loop_->message_loop_proxy());
+ }
+
+ virtual void TearDown() {
+ DestroyDB();
+ main_loop_.reset(NULL);
+ }
+
+ void DestroyDB() {
+ if (db_) {
+ db_->Destroy();
+ base::RunLoop().RunUntilIdle();
+ db_ = NULL;
+ }
+ }
+
+ DomDistillerDatabase* db_;
+ scoped_ptr<MessageLoop> main_loop_;
+};
+
+// Test that DomDistillerDatabase calls Init on the underlying database and that
+// the caller's InitCallback is called with the correct value.
+TEST_F(DomDistillerDatabaseTest, TestDBInitSuccess) {
+ base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
+
+ MockDB* mock_db = new MockDB();
+ EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(true));
+
+ MockDatabaseCaller caller;
+ EXPECT_CALL(caller, InitCallback(true));
+
+ db_->InitWithDatabase(
+ scoped_ptr<DomDistillerDatabase::Database>(mock_db),
+ base::FilePath(path),
+ base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(DomDistillerDatabaseTest, TestDBInitFailure) {
+ base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
+
+ MockDB* mock_db = new MockDB();
+ EXPECT_CALL(*mock_db, Init(path)).WillOnce(Return(false));
+
+ MockDatabaseCaller caller;
+ EXPECT_CALL(caller, InitCallback(false));
+
+ db_->InitWithDatabase(
+ scoped_ptr<DomDistillerDatabase::Database>(mock_db),
+ base::FilePath(path),
+ base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+ACTION_P(AppendLoadEntries, model) {
+ EntryVector* output = arg0;
+ for (EntryMap::const_iterator it = model.begin(); it != model.end(); ++it) {
+ output->push_back(it->second);
+ }
+ return true;
+}
+
+ACTION_P(VerifyLoadEntries, expected) {
+ EntryVector* actual = arg1;
+ ExpectEntryPointersEquals(expected, *actual);
+}
+
+// Test that DomDistillerDatabase calls Load on the underlying database and that
+// the caller's LoadCallback is called with the correct success value. Also
+// confirms that on success, the expected entries are passed to the caller's
+// LoadCallback.
+TEST_F(DomDistillerDatabaseTest, TestDBLoadSuccess) {
+ base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
+
+ MockDB* mock_db = new MockDB();
+ MockDatabaseCaller caller;
+ EntryMap model = GetSmallModel();
+
+ EXPECT_CALL(*mock_db, Init(_));
+ EXPECT_CALL(caller, InitCallback(_));
+ db_->InitWithDatabase(
+ scoped_ptr<DomDistillerDatabase::Database>(mock_db),
+ base::FilePath(path),
+ base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
+
+ EXPECT_CALL(*mock_db, Load(_)).WillOnce(AppendLoadEntries(model));
+ EXPECT_CALL(caller, LoadCallback1(true, _))
+ .WillOnce(VerifyLoadEntries(testing::ByRef(model)));
+ db_->LoadEntries(
+ base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(DomDistillerDatabaseTest, TestDBLoadFailure) {
+ base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
+
+ MockDB* mock_db = new MockDB();
+ MockDatabaseCaller caller;
+
+ EXPECT_CALL(*mock_db, Init(_));
+ EXPECT_CALL(caller, InitCallback(_));
+ db_->InitWithDatabase(
+ scoped_ptr<DomDistillerDatabase::Database>(mock_db),
+ base::FilePath(path),
+ base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
+
+ EXPECT_CALL(*mock_db, Load(_)).WillOnce(Return(false));
+ EXPECT_CALL(caller, LoadCallback1(false, _));
+ db_->LoadEntries(
+ base::Bind(&MockDatabaseCaller::LoadCallback, base::Unretained(&caller)));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+ACTION_P(VerifySaveEntries, expected) {
+ const EntryVector& actual = arg0;
+ ExpectEntryPointersEquals(expected, actual);
+ return true;
+}
+
+// Test that DomDistillerDatabase calls Save on the underlying database with the
+// correct entries to save and that the caller's SaveCallback is called with the
+// correct success value.
+TEST_F(DomDistillerDatabaseTest, TestDBSaveSuccess) {
+ base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
+
+ MockDB* mock_db = new MockDB();
+ MockDatabaseCaller caller;
+ EntryMap model = GetSmallModel();
+
+ EXPECT_CALL(*mock_db, Init(_));
+ EXPECT_CALL(caller, InitCallback(_));
+ db_->InitWithDatabase(
+ scoped_ptr<DomDistillerDatabase::Database>(mock_db),
+ base::FilePath(path),
+ base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
+
+ scoped_ptr<EntryVector> entries(new EntryVector());
+ for (EntryMap::iterator it = model.begin(); it != model.end(); ++it) {
+ entries->push_back(it->second);
+ }
+
+ EXPECT_CALL(*mock_db, Save(_)).WillOnce(VerifySaveEntries(model));
+ EXPECT_CALL(caller, SaveCallback(true));
+ db_->SaveEntries(
+ entries.Pass(),
+ base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(DomDistillerDatabaseTest, TestDBSaveFailure) {
+ base::FilePath path(FILE_PATH_LITERAL("/fake/path"));
+
+ MockDB* mock_db = new MockDB();
+ MockDatabaseCaller caller;
+ scoped_ptr<EntryVector> entries(new EntryVector());
+
+ EXPECT_CALL(*mock_db, Init(_));
+ EXPECT_CALL(caller, InitCallback(_));
+ db_->InitWithDatabase(
+ scoped_ptr<DomDistillerDatabase::Database>(mock_db),
+ base::FilePath(path),
+ base::Bind(&MockDatabaseCaller::InitCallback, base::Unretained(&caller)));
+
+ EXPECT_CALL(*mock_db, Save(_)).WillOnce(Return(false));
+ EXPECT_CALL(caller, SaveCallback(false));
+ db_->SaveEntries(
+ entries.Pass(),
+ base::Bind(&MockDatabaseCaller::SaveCallback, base::Unretained(&caller)));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test that the LevelDB properly saves entries and that load returns the saved
+// entries. If |close_after_save| is true, the database will be closed after
+// saving and then re-opened to ensure that the data is properly persisted.
+void TestLevelDBSaveAndLoad(bool close_after_save) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ EntryMap model = GetSmallModel();
+ EntryVector save_entries;
+ EntryVector load_entries;
+
+ for (EntryMap::iterator it = model.begin(); it != model.end(); ++it) {
+ save_entries.push_back(it->second);
+ }
+
+ scoped_ptr<DomDistillerDatabase::LevelDB> db(
+ new DomDistillerDatabase::LevelDB());
+ EXPECT_TRUE(db->Init(temp_dir.path()));
+ EXPECT_TRUE(db->Save(save_entries));
+
+ if (close_after_save) {
+ db.reset(new DomDistillerDatabase::LevelDB());
+ EXPECT_TRUE(db->Init(temp_dir.path()));
+ }
+
+ EXPECT_TRUE(db->Load(&load_entries));
+
+ ExpectEntryPointersEquals(model, load_entries);
+}
+
+TEST(DomDistillerDatabaseLevelDBTest, TestDBSaveAndLoad) {
+ TestLevelDBSaveAndLoad(false);
+}
+
+TEST(DomDistillerDatabaseLevelDBTest, TestDBCloseAndReopen) {
+ TestLevelDBSaveAndLoad(true);
+}
+
+} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/core/proto/article_entry.proto b/chromium/components/dom_distiller/core/proto/article_entry.proto
new file mode 100644
index 00000000000..70b769d97b0
--- /dev/null
+++ b/chromium/components/dom_distiller/core/proto/article_entry.proto
@@ -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.
+//
+// Protocol buffer definition for a DomDistiller entry.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package dom_distiller;
+
+message ArticleEntry {
+ // Next ID to use: 3
+
+ optional string entry_id = 1;
+
+ message Page {
+ // Next ID to use: 2
+
+ optional string url = 1;
+ }
+
+ repeated Page pages = 2;
+}
+
diff --git a/chromium/components/dom_distiller/webui/DEPS b/chromium/components/dom_distiller/webui/DEPS
new file mode 100644
index 00000000000..db46e2e03e5
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/DEPS
@@ -0,0 +1,13 @@
+include_rules = [
+ "+components/dom_distiller/webui",
+ "+ui/webui/resources",
+ # The webui of this component needs to depend on content, but since it is
+ # also supposed to be working on iOS, and there is currently no concrete plan
+ # yet for extracting webui as something that is reusable across iOS and other
+ # platforms, this DEPS rule is kept instead.
+ # To ensure this DEPS rule is not a nuisance to engineers refactoring the
+ # content layer, it is currently quite broad instead of the more strict
+ # approach directly allowing the header files that are currently included from
+ # content.
+ "+content/public/browser",
+]
diff --git a/chromium/components/dom_distiller/webui/dom_distiller_handler.cc b/chromium/components/dom_distiller/webui/dom_distiller_handler.cc
new file mode 100644
index 00000000000..c05cc0dec73
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/dom_distiller_handler.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/dom_distiller/webui/dom_distiller_handler.h"
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui.h"
+
+namespace dom_distiller {
+
+DomDistillerHandler::DomDistillerHandler()
+ : weak_ptr_factory_(this) {
+}
+
+DomDistillerHandler::~DomDistillerHandler() {}
+
+void DomDistillerHandler::RegisterMessages() {
+ web_ui()->RegisterMessageCallback(
+ "requestEntries",
+ base::Bind(&DomDistillerHandler::HandleRequestEntries,
+ base::Unretained(this)));
+}
+
+void DomDistillerHandler::HandleRequestEntries(const ListValue* args) {
+ base::ListValue entries;
+
+ // Add some temporary placeholder entries.
+ scoped_ptr<base::DictionaryValue> entry1(new base::DictionaryValue());
+ entry1->SetString("title", "Google");
+ entry1->SetString("url", "http://www.google.com/");
+ entries.Append(entry1.release());
+ scoped_ptr<base::DictionaryValue> entry2(new base::DictionaryValue());
+ entry2->SetString("title", "Chrome");
+ entry2->SetString("url", "http://www.chrome.com/");
+ entries.Append(entry2.release());
+
+ web_ui()->CallJavascriptFunction("onGotEntries", entries);
+}
+
+} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/webui/dom_distiller_handler.h b/chromium/components/dom_distiller/webui/dom_distiller_handler.h
new file mode 100644
index 00000000000..2af6d348b00
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/dom_distiller_handler.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_DOM_DISTILLER_WEBUI_DOM_DISTILLER_HANDLER_H_
+#define COMPONENTS_DOM_DISTILLER_WEBUI_DOM_DISTILLER_HANDLER_H_
+
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "content/public/browser/web_ui_message_handler.h"
+
+namespace dom_distiller {
+
+// Handler class for DOM Distiller page operations.
+class DomDistillerHandler : public content::WebUIMessageHandler {
+ public:
+ DomDistillerHandler();
+ virtual ~DomDistillerHandler();
+
+ // content::WebUIMessageHandler implementation.
+ virtual void RegisterMessages() OVERRIDE;
+
+ // Callback for the "requestEntries" message. This synchronously requests the
+ // list of entries and returns it to the front end.
+ virtual void HandleRequestEntries(const ListValue* args);
+
+ private:
+ // Factory for the creating refs in callbacks.
+ base::WeakPtrFactory<DomDistillerHandler> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(DomDistillerHandler);
+};
+
+} // namespace dom_distiller
+
+#endif // COMPONENTS_DOM_DISTILLER_WEBUI_DOM_DISTILLER_HANDLER_H_
diff --git a/chromium/components/dom_distiller/webui/dom_distiller_ui.cc b/chromium/components/dom_distiller/webui/dom_distiller_ui.cc
new file mode 100644
index 00000000000..e1418434cc4
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/dom_distiller_ui.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/dom_distiller/webui/dom_distiller_ui.h"
+
+#include "components/dom_distiller/core/dom_distiller_constants.h"
+#include "components/dom_distiller/webui/dom_distiller_handler.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "grit/component_strings.h"
+#include "grit/dom_distiller_resources.h"
+
+namespace dom_distiller {
+
+DomDistillerUI::DomDistillerUI(content::WebUI* web_ui)
+ : content::WebUIController(web_ui) {
+ // Set up WebUIDataSource.
+ content::WebUIDataSource* source =
+ content::WebUIDataSource::Create(kChromeUIDomDistillerHost);
+ source->SetDefaultResource(IDR_ABOUT_DOM_DISTILLER_HTML);
+ source->AddResourcePath("about_dom_distiller.css",
+ IDR_ABOUT_DOM_DISTILLER_CSS);
+ source->AddResourcePath("about_dom_distiller.js",
+ IDR_ABOUT_DOM_DISTILLER_JS);
+
+ source->SetUseJsonJSFormatV2();
+ source->AddLocalizedString("domDistillerTitle", IDS_DOM_DISTILLER_TITLE);
+ content::BrowserContext* browser_context =
+ web_ui->GetWebContents()->GetBrowserContext();
+ content::WebUIDataSource::Add(browser_context, source);
+ source->SetJsonPath("strings.js");
+
+ // Add message handler.
+ web_ui->AddMessageHandler(new DomDistillerHandler());
+}
+
+DomDistillerUI::~DomDistillerUI() {}
+
+} // namespace dom_distiller
diff --git a/chromium/components/dom_distiller/webui/dom_distiller_ui.h b/chromium/components/dom_distiller/webui/dom_distiller_ui.h
new file mode 100644
index 00000000000..1b9f1c2a292
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/dom_distiller_ui.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_DOM_DISTILLER_WEBUI_DOM_DISTILLER_H_
+#define COMPONENTS_DOM_DISTILLER_WEBUI_DOM_DISTILLER_H_
+
+#include "content/public/browser/web_ui_controller.h"
+
+namespace dom_distiller {
+
+// The WebUI handler for chrome://dom-distiller.
+class DomDistillerUI : public content::WebUIController {
+ public:
+ explicit DomDistillerUI(content::WebUI* web_ui);
+ virtual ~DomDistillerUI();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DomDistillerUI);
+};
+
+} // namespace dom_distiller
+
+#endif // COMPONENTS_DOM_DISTILLER_WEBUI_DOM_DISTILLER_UI_H_
diff --git a/chromium/components/dom_distiller/webui/resources/about_dom_distiller.css b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.css
new file mode 100644
index 00000000000..088136a8204
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.css
@@ -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.
+ */
+
+a:visited {
+ color: orange;
+}
diff --git a/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
new file mode 100644
index 00000000000..1e3b537b302
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title i18n-content="domDistillerTitle"></title>
+ <link rel="stylesheet" href="chrome://resources/css/chrome_shared.css">
+ <link rel="stylesheet" href="chrome://resources/css/overlay.css">
+ <link rel="stylesheet" href="about_dom_distiller.css">
+
+ <script src="chrome://resources/js/cr.js"></script>
+ <script src="chrome://resources/js/util.js"></script>
+ <script src="chrome://resources/js/load_time_data.js"></script>
+ <script src="chrome://resources/js/cr/ui/overlay.js"></script>
+ <script src="about_dom_distiller.js"></script>
+ <script src="strings.js"></script>
+</head>
+<body>
+ <header>
+ <h1 i18n-content="domDistillerTitle"></h1>
+ </header>
+ <div id="entries-section">
+ <div id="entries-list"></div>
+ </div>
+ <script src="chrome://resources/js/i18n_template2.js"></script>
+ <script src="chrome://resources/js/jstemplate_compiled.js"></script>
+</body>
+</html>
diff --git a/chromium/components/dom_distiller/webui/resources/about_dom_distiller.js b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.js
new file mode 100644
index 00000000000..f558d7fa9fb
--- /dev/null
+++ b/chromium/components/dom_distiller/webui/resources/about_dom_distiller.js
@@ -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.
+
+/**
+ * Callback from the backend with the list of entries to display.
+ * This call will build the entries section of the DOM distiller page, or hide
+ * that section if there are none to display.
+ * @param {!Array.<string>} entries The entries.
+ */
+function onGotEntries(entries) {
+ $('entries-section').hidden = !entries.length;
+ if (entries.length > 0) {
+ var list = document.createElement('ul');
+ for (var i = 0; i < entries.length; i++) {
+ var listItem = document.createElement('li');
+ var link = document.createElement('a');
+ link.innerText = entries[i].title;
+ link.setAttribute('href', entries[i].url);
+ listItem.appendChild(link);
+ list.appendChild(listItem);
+ }
+ $('entries-list').appendChild(list);
+ }
+}
+
+/* All the work we do on load. */
+function onLoadWork() {
+ chrome.send('requestEntries');
+}
+
+document.addEventListener('DOMContentLoaded', onLoadWork);
diff --git a/chromium/components/dom_distiller_resources.grd b/chromium/components/dom_distiller_resources.grd
new file mode 100644
index 00000000000..dd2a5f57490
--- /dev/null
+++ b/chromium/components/dom_distiller_resources.grd
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1">
+ <outputs>
+ <output filename="grit/dom_distiller_resources.h" type="rc_header">
+ <emit emit_type='prepend'></emit>
+ </output>
+ <output filename="dom_distiller_resources.pak" type="data_package" />
+ <output filename="dom_distiller_resources.rc" type="rc_all" />
+ </outputs>
+ <release seq="1">
+ <includes>
+ <include name="IDR_ABOUT_DOM_DISTILLER_HTML" file="dom_distiller/webui/resources/about_dom_distiller.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+ <include name="IDR_ABOUT_DOM_DISTILLER_CSS" file="dom_distiller/webui/resources/about_dom_distiller.css" type="BINDATA" />
+ <include name="IDR_ABOUT_DOM_DISTILLER_JS" file="dom_distiller/webui/resources/about_dom_distiller.js" type="BINDATA" />
+ </includes>
+ </release>
+</grit>
diff --git a/chromium/components/dom_distiller_strings.grdp b/chromium/components/dom_distiller_strings.grdp
new file mode 100644
index 00000000000..3d81fae78a0
--- /dev/null
+++ b/chromium/components/dom_distiller_strings.grdp
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+
+ <message name="IDS_DOM_DISTILLER_TITLE" desc="The title to show on the DOM Distiller debug page.">
+ DOM Distiller
+ </message>
+
+</grit-part>
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/broker/nacl_broker_listener.cc b/chromium/components/nacl/broker/nacl_broker_listener.cc
index cc365d64a7f..c4f268da3cd 100644
--- a/chromium/components/nacl/broker/nacl_broker_listener.cc
+++ b/chromium/components/nacl/broker/nacl_broker_listener.cc
@@ -105,9 +105,18 @@ void NaClBrokerListener::OnLaunchLoaderThroughBroker(
loader_process = content::StartSandboxedProcess(this, cmd_line);
if (loader_process) {
+ // Note: PROCESS_DUP_HANDLE is necessary here, because:
+ // 1) The current process is the broker, which is the loader's parent.
+ // 2) The browser is not the loader's parent, and so only gets the
+ // access rights we confer here.
+ // 3) The browser calls DuplicateHandle to set up communications with
+ // the loader.
+ // 4) The target process handle to DuplicateHandle needs to have
+ // PROCESS_DUP_HANDLE access rights.
DuplicateHandle(::GetCurrentProcess(), loader_process,
browser_handle_, &loader_handle_in_browser,
- PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION , FALSE, 0);
+ PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE,
+ FALSE, 0);
base::CloseProcessHandle(loader_process);
}
}
diff --git a/chromium/components/nacl/common/nacl_cmd_line.cc b/chromium/components/nacl/common/nacl_cmd_line.cc
index b21a671ae1d..d9bbd6557f4 100644
--- a/chromium/components/nacl/common/nacl_cmd_line.cc
+++ b/chromium/components/nacl/common/nacl_cmd_line.cc
@@ -18,7 +18,6 @@ void CopyNaClCommandLineArguments(CommandLine* cmd_line) {
// TODO(gregoryd): check which flags of those below can be supported.
static const char* const kSwitchNames[] = {
switches::kNoSandbox,
- switches::kTestNaClSandbox,
switches::kDisableBreakpad,
switches::kFullMemoryCrashReport,
switches::kEnableLogging,
diff --git a/chromium/components/nacl/common/nacl_helper_linux.h b/chromium/components/nacl/common/nacl_helper_linux.h
index 732b21570a9..a9324b3fa1d 100644
--- a/chromium/components/nacl/common/nacl_helper_linux.h
+++ b/chromium/components/nacl/common/nacl_helper_linux.h
@@ -9,10 +9,15 @@
// constants used to implement communication between the nacl_helper
// process and the Chrome zygote.
+#define kNaClMaxIPCMessageLength 2048
+
// 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"
+
+enum NaClZygoteIPCCommand {
+ kNaClForkRequest,
+ kNaClGetTerminationStatusRequest,
+};
// The next set of constants define global Linux file descriptors.
// For communications between NaCl loader and browser.
diff --git a/chromium/components/nacl/common/nacl_host_messages.h b/chromium/components/nacl/common/nacl_host_messages.h
index d9a835ba28c..4afa1c727e7 100644
--- a/chromium/components/nacl/common/nacl_host_messages.h
+++ b/chromium/components/nacl/common/nacl_host_messages.h
@@ -26,6 +26,7 @@ IPC_STRUCT_TRAITS_BEGIN(nacl::NaClLaunchParams)
IPC_STRUCT_TRAITS_MEMBER(uses_irt)
IPC_STRUCT_TRAITS_MEMBER(enable_dyncode_syscalls)
IPC_STRUCT_TRAITS_MEMBER(enable_exception_handling)
+ IPC_STRUCT_TRAITS_MEMBER(enable_crash_throttling)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(nacl::NaClLaunchResult)
@@ -41,6 +42,7 @@ IPC_STRUCT_TRAITS_BEGIN(nacl::PnaclCacheInfo)
IPC_STRUCT_TRAITS_MEMBER(opt_level)
IPC_STRUCT_TRAITS_MEMBER(last_modified)
IPC_STRUCT_TRAITS_MEMBER(etag)
+ IPC_STRUCT_TRAITS_MEMBER(has_no_store_header)
IPC_STRUCT_TRAITS_END()
// A renderer sends this to the browser process when it wants to start
diff --git a/chromium/components/nacl/common/nacl_switches.cc b/chromium/components/nacl/common/nacl_switches.cc
index b9db537b252..0dfdc949857 100644
--- a/chromium/components/nacl/common/nacl_switches.cc
+++ b/chromium/components/nacl/common/nacl_switches.cc
@@ -36,7 +36,4 @@ const char kNaClLoaderCmdPrefix[] = "nacl-loader-cmd-prefix";
// Causes the process to run as a NativeClient loader.
const char kNaClLoaderProcess[] = "nacl-loader";
-// Runs the security test for the NaCl loader sandbox.
-const char kTestNaClSandbox[] = "test-nacl-sandbox";
-
} // namespace switches
diff --git a/chromium/components/nacl/common/nacl_switches.h b/chromium/components/nacl/common/nacl_switches.h
index 8b12206a21a..9bc1bcb4697 100644
--- a/chromium/components/nacl/common/nacl_switches.h
+++ b/chromium/components/nacl/common/nacl_switches.h
@@ -18,7 +18,6 @@ extern const char kNaClGdb[];
extern const char kNaClGdbScript[];
extern const char kNaClLoaderCmdPrefix[];
extern const char kNaClLoaderProcess[];
-extern const char kTestNaClSandbox[];
} // namespace switches
diff --git a/chromium/components/nacl/common/nacl_types.cc b/chromium/components/nacl/common/nacl_types.cc
index dea02391125..6b2f51ea4fb 100644
--- a/chromium/components/nacl/common/nacl_types.cc
+++ b/chromium/components/nacl/common/nacl_types.cc
@@ -24,7 +24,8 @@ NaClLaunchParams::NaClLaunchParams()
permission_bits(0),
uses_irt(false),
enable_dyncode_syscalls(false),
- enable_exception_handling(false) {
+ enable_exception_handling(false),
+ enable_crash_throttling(false) {
}
NaClLaunchParams::NaClLaunchParams(const std::string& manifest_url,
@@ -32,13 +33,15 @@ NaClLaunchParams::NaClLaunchParams(const std::string& manifest_url,
uint32 permission_bits,
bool uses_irt,
bool enable_dyncode_syscalls,
- bool enable_exception_handling)
+ bool enable_exception_handling,
+ bool enable_crash_throttling)
: 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) {
+ enable_exception_handling(enable_exception_handling),
+ enable_crash_throttling(enable_crash_throttling) {
}
NaClLaunchParams::NaClLaunchParams(const NaClLaunchParams& l) {
@@ -48,6 +51,7 @@ NaClLaunchParams::NaClLaunchParams(const NaClLaunchParams& l) {
uses_irt = l.uses_irt;
enable_dyncode_syscalls = l.enable_dyncode_syscalls;
enable_exception_handling = l.enable_exception_handling;
+ enable_crash_throttling = l.enable_crash_throttling;
}
NaClLaunchParams::~NaClLaunchParams() {
diff --git a/chromium/components/nacl/common/nacl_types.h b/chromium/components/nacl/common/nacl_types.h
index b3fee25c869..47fccddd963 100644
--- a/chromium/components/nacl/common/nacl_types.h
+++ b/chromium/components/nacl/common/nacl_types.h
@@ -70,7 +70,8 @@ struct NaClLaunchParams {
NaClLaunchParams();
NaClLaunchParams(const std::string& u, int r, uint32 p, bool uses_irt,
bool enable_dyncode_syscalls,
- bool enable_exception_handling);
+ bool enable_exception_handling,
+ bool enable_crash_throttling);
NaClLaunchParams(const NaClLaunchParams& l);
~NaClLaunchParams();
@@ -80,6 +81,7 @@ struct NaClLaunchParams {
bool uses_irt;
bool enable_dyncode_syscalls;
bool enable_exception_handling;
+ bool enable_crash_throttling;
};
struct NaClLaunchResult {
diff --git a/chromium/components/nacl/common/pnacl_types.cc b/chromium/components/nacl/common/pnacl_types.cc
index 6c43319c6b2..75384b6faed 100644
--- a/chromium/components/nacl/common/pnacl_types.cc
+++ b/chromium/components/nacl/common/pnacl_types.cc
@@ -6,7 +6,8 @@
namespace nacl {
-PnaclCacheInfo::PnaclCacheInfo() {}
+PnaclCacheInfo::PnaclCacheInfo()
+ : abi_version(0), opt_level(0), has_no_store_header(0) {}
PnaclCacheInfo::~PnaclCacheInfo() {}
// static
diff --git a/chromium/components/nacl/common/pnacl_types.h b/chromium/components/nacl/common/pnacl_types.h
index 3fc405a980d..d1a12c86b07 100644
--- a/chromium/components/nacl/common/pnacl_types.h
+++ b/chromium/components/nacl/common/pnacl_types.h
@@ -27,6 +27,7 @@ struct PnaclCacheInfo {
int opt_level;
base::Time last_modified;
std::string etag;
+ bool has_no_store_header;
};
// Progress information for PNaCl on-demand installs.
diff --git a/chromium/components/nacl/loader/nacl_main.cc b/chromium/components/nacl/loader/nacl_main.cc
index a9ec5c93c37..15b6fd2dc8c 100644
--- a/chromium/components/nacl/loader/nacl_main.cc
+++ b/chromium/components/nacl/loader/nacl_main.cc
@@ -29,10 +29,7 @@ int NaClMain(const content::MainFunctionParams& parameters) {
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
NaClMainPlatformDelegate platform(parameters);
-
- platform.PlatformInitialize();
bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox);
- platform.InitSandboxTests(no_sandbox);
#if defined(OS_POSIX)
// The number of cores must be obtained before the invocation of
@@ -43,25 +40,14 @@ int NaClMain(const content::MainFunctionParams& parameters) {
if (!no_sandbox) {
platform.EnableSandbox();
}
- bool sandbox_test_result = platform.RunSandboxTests();
-
- if (sandbox_test_result) {
- NaClListener listener;
+ NaClListener listener;
#if defined(OS_POSIX)
- listener.set_number_of_cores(number_of_cores);
+ listener.set_number_of_cores(number_of_cores);
#endif
- listener.Listen();
- } else {
- // This indirectly prevents the test-harness-success-cookie from being set,
- // as a way of communicating test failure, because the nexe won't reply.
- // TODO(jvoung): find a better way to indicate failure that doesn't
- // require waiting for a timeout.
- VLOG(1) << "Sandbox test failed: Not launching NaCl process";
- }
+
+ listener.Listen();
#else
NOTIMPLEMENTED() << " not implemented startup, plugin startup dialog etc.";
#endif
-
- platform.PlatformUninitialize();
return 0;
}
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate.h b/chromium/components/nacl/loader/nacl_main_platform_delegate.h
index 191831cfefe..ca740b85bcd 100644
--- a/chromium/components/nacl/loader/nacl_main_platform_delegate.h
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate.h
@@ -5,38 +5,20 @@
#ifndef CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
#define CHROME_NACL_NACL_MAIN_PLATFORM_DELEGATE_H_
-#include "base/native_library.h"
+#include "base/basictypes.h"
#include "content/public/common/main_function_params.h"
-typedef bool (*RunNaClLoaderTests)(void);
-const char kNaClLoaderTestCall[] = "RunNaClLoaderTests";
-
class NaClMainPlatformDelegate {
public:
explicit NaClMainPlatformDelegate(
const content::MainFunctionParams& parameters);
~NaClMainPlatformDelegate();
- // Called first thing and last thing in the process' lifecycle, i.e. before
- // the sandbox is enabled.
- void PlatformInitialize();
- void PlatformUninitialize();
-
- // Gives us an opportunity to initialize state used for tests before enabling
- // the sandbox.
- void InitSandboxTests(bool no_sandbox);
-
// Initiate Lockdown.
void EnableSandbox();
- // Runs the sandbox tests for the NaCl Loader, if tests supplied.
- // Cannot run again, after this (resources freed).
- // Returns false if the tests are supplied and fail.
- bool RunSandboxTests();
-
private:
const content::MainFunctionParams& parameters_;
- base::NativeLibrary sandbox_test_module_;
DISALLOW_COPY_AND_ASSIGN(NaClMainPlatformDelegate);
};
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc b/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc
index e9d2f731787..cbe886bc006 100644
--- a/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate_linux.cc
@@ -4,28 +4,14 @@
#include "components/nacl/loader/nacl_main_platform_delegate.h"
-#include "base/command_line.h"
-
NaClMainPlatformDelegate::NaClMainPlatformDelegate(
const content::MainFunctionParams& parameters)
- : parameters_(parameters), sandbox_test_module_(NULL) {
+ : parameters_(parameters) {
}
NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
}
-void NaClMainPlatformDelegate::PlatformInitialize() {
-}
-
-void NaClMainPlatformDelegate::PlatformUninitialize() {
-}
-
-void NaClMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
- // The sandbox is started in the zygote process: zygote_main_linux.cc
- // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
- return;
-}
-
void NaClMainPlatformDelegate::EnableSandbox() {
// The setuid sandbox is started in the zygote process: zygote_main_linux.cc
// http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
@@ -43,9 +29,3 @@ void NaClMainPlatformDelegate::EnableSandbox() {
// At best, NaCl will not work. At worst, enabling the seccomp sandbox
// could create a hole in the NaCl sandbox.
}
-
-bool NaClMainPlatformDelegate::RunSandboxTests() {
- // The sandbox is started in the zygote process: zygote_main_linux.cc
- // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
- return true;
-}
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm b/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm
index 67ddced4396..78fa5390454 100644
--- a/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate_mac.mm
@@ -5,78 +5,22 @@
#include "components/nacl/loader/nacl_main_platform_delegate.h"
#import <Cocoa/Cocoa.h>
-#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/native_library.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), sandbox_test_module_(NULL) {
+ : parameters_(parameters) {
}
NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
}
-// TODO(jvoung): see if this old comment (from renderer_main_platform...)
-// is relevant to the nacl loader.
-// TODO(mac-port): Any code needed to initialize a process for purposes of
-// running a NaClLoader needs to also be reflected in chrome_main.cc for
-// --single-process support.
-void NaClMainPlatformDelegate::PlatformInitialize() {
-}
-
-void NaClMainPlatformDelegate::PlatformUninitialize() {
-}
-
-void NaClMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
- const CommandLine& command_line = parameters_.command_line;
-
- DVLOG(1) << "Started NaClLdr with ";
- const std::vector<std::string>& argstrings = command_line.argv();
- for (std::vector<std::string>::const_iterator ii = argstrings.begin();
- ii != argstrings.end(); ++ii)
- DVLOG(1) << *ii;
-
- // Be sure not to load the sandbox test DLL if the sandbox isn't on.
- // Comment-out guard and recompile if you REALLY want to test w/out the SB.
- // TODO(jvoung): allow testing without sandbox, but change expected ret vals.
- if (!no_sandbox) {
- base::FilePath test_dll_name =
- command_line.GetSwitchValuePath(switches::kTestNaClSandbox);
- if (!test_dll_name.empty()) {
- sandbox_test_module_ = base::LoadNativeLibrary(test_dll_name, NULL);
- CHECK(sandbox_test_module_);
- }
- }
-}
-
void NaClMainPlatformDelegate::EnableSandbox() {
CHECK(content::InitializeSandbox(NACL_SANDBOX_TYPE_NACL_LOADER,
base::FilePath()))
<< "Error initializing sandbox for " << switches::kNaClLoaderProcess;
}
-
-bool NaClMainPlatformDelegate::RunSandboxTests() {
- // TODO(jvoung): Win and mac should share this identical code.
- bool result = true;
- if (sandbox_test_module_) {
- RunNaClLoaderTests run_security_tests =
- reinterpret_cast<RunNaClLoaderTests>(
- base::GetFunctionPointerFromNativeLibrary(sandbox_test_module_,
- kNaClLoaderTestCall));
- if (run_security_tests) {
- DVLOG(1) << "Running NaCl Loader security tests";
- result = (*run_security_tests)();
- } else {
- VLOG(1) << "Failed to get NaCl sandbox test function";
- result = false;
- }
- base::UnloadNativeLibrary(sandbox_test_module_);
- sandbox_test_module_ = NULL;
- }
- return result;
-}
diff --git a/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc b/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc
index e79fe17725c..f530961700f 100644
--- a/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc
+++ b/chromium/components/nacl/loader/nacl_main_platform_delegate_win.cc
@@ -4,59 +4,17 @@
#include "components/nacl/loader/nacl_main_platform_delegate.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/native_library.h"
-#include "components/nacl/common/nacl_switches.h"
#include "sandbox/win/src/sandbox.h"
NaClMainPlatformDelegate::NaClMainPlatformDelegate(
const content::MainFunctionParams& parameters)
- : parameters_(parameters), sandbox_test_module_(NULL) {
+ : parameters_(parameters) {
}
NaClMainPlatformDelegate::~NaClMainPlatformDelegate() {
}
-void NaClMainPlatformDelegate::PlatformInitialize() {
- // Be mindful of what resources you acquire here. They can be used by
- // malicious code if the renderer gets compromised.
-}
-
-void NaClMainPlatformDelegate::PlatformUninitialize() {
-}
-
-void NaClMainPlatformDelegate::InitSandboxTests(bool no_sandbox) {
- const CommandLine& command_line = parameters_.command_line;
-
- DVLOG(1) << "Started NaClLdr with " << command_line.GetCommandLineString();
-
- sandbox::TargetServices* target_services =
- parameters_.sandbox_info->target_services;
-
- if (target_services && !no_sandbox) {
- base::FilePath test_dll_name =
- command_line.GetSwitchValuePath(switches::kTestNaClSandbox);
- if (!test_dll_name.empty()) {
- // At this point, hack on the suffix according to with bitness
- // of your windows process.
-#if defined(_WIN64)
- DVLOG(1) << "Using 64-bit test dll\n";
- test_dll_name = test_dll_name.InsertBeforeExtension(L"64");
- test_dll_name = test_dll_name.ReplaceExtension(L"dll");
-#else
- DVLOG(1) << "Using 32-bit test dll\n";
- test_dll_name = test_dll_name.ReplaceExtension(L"dll");
-#endif
- DVLOG(1) << "Loading test lib " << test_dll_name.value() << "\n";
- sandbox_test_module_ = base::LoadNativeLibrary(test_dll_name, NULL);
- CHECK(sandbox_test_module_);
- VLOG(1) << "Testing NaCl sandbox\n";
- }
- }
-}
-
void NaClMainPlatformDelegate::EnableSandbox() {
sandbox::TargetServices* target_services =
parameters_.sandbox_info->target_services;
@@ -71,24 +29,3 @@ void NaClMainPlatformDelegate::EnableSandbox() {
// Turn the sandbox on.
target_services->LowerToken();
}
-
-bool NaClMainPlatformDelegate::RunSandboxTests() {
- // TODO(jvoung): Win and mac should share this code.
- bool result = true;
- if (sandbox_test_module_) {
- RunNaClLoaderTests run_security_tests =
- reinterpret_cast<RunNaClLoaderTests>(
- base::GetFunctionPointerFromNativeLibrary(sandbox_test_module_,
- kNaClLoaderTestCall));
- if (run_security_tests) {
- DVLOG(1) << "Running NaCl Loader security tests";
- result = (*run_security_tests)();
- } else {
- VLOG(1) << "Failed to get NaCl sandbox test function";
- result = false;
- }
- base::UnloadNativeLibrary(sandbox_test_module_);
- sandbox_test_module_ = NULL;
- }
- return result;
-}
diff --git a/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc
index 8445342fdaa..d94ad6b9fdf 100644
--- a/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc
+++ b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.cc
@@ -17,8 +17,10 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
+#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "components/nacl/common/nacl_helper_linux.h"
@@ -63,6 +65,41 @@ bool NonZeroSegmentBaseIsSlow() {
}
#endif
+// Send an IPC request on |ipc_channel|. The request is contained in
+// |request_pickle| and can have file descriptors attached in |attached_fds|.
+// |reply_data_buffer| must be allocated by the caller and will contain the
+// reply. The size of the reply will be written to |reply_size|.
+// This code assumes that only one thread can write to |ipc_channel| to make
+// requests.
+bool SendIPCRequestAndReadReply(int ipc_channel,
+ const std::vector<int>& attached_fds,
+ const Pickle& request_pickle,
+ char* reply_data_buffer,
+ size_t reply_data_buffer_size,
+ ssize_t* reply_size) {
+ DCHECK_LE(static_cast<size_t>(kNaClMaxIPCMessageLength),
+ reply_data_buffer_size);
+ DCHECK(reply_size);
+
+ if (!UnixDomainSocket::SendMsg(ipc_channel, request_pickle.data(),
+ request_pickle.size(), attached_fds)) {
+ LOG(ERROR) << "SendIPCRequestAndReadReply: SendMsg failed";
+ return false;
+ }
+
+ // Then read the remote reply.
+ std::vector<int> received_fds;
+ const ssize_t msg_len =
+ UnixDomainSocket::RecvMsg(ipc_channel, reply_data_buffer,
+ reply_data_buffer_size, &received_fds);
+ if (msg_len <= 0) {
+ LOG(ERROR) << "SendIPCRequestAndReadReply: RecvMsg failed";
+ return false;
+ }
+ *reply_size = msg_len;
+ return true;
+}
+
} // namespace.
NaClForkDelegate::NaClForkDelegate()
@@ -219,22 +256,34 @@ bool NaClForkDelegate::CanHelp(const std::string& process_type,
}
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";
+
+ // First, send a remote fork request.
+ Pickle write_pickle;
+ write_pickle.WriteInt(kNaClForkRequest);
+
+ char reply_buf[kNaClMaxIPCMessageLength];
+ ssize_t reply_size = 0;
+ bool got_reply =
+ SendIPCRequestAndReadReply(fd_, fds, write_pickle,
+ reply_buf, sizeof(reply_buf), &reply_size);
+ if (!got_reply) {
+ LOG(ERROR) << "Could not perform remote fork.";
return -1;
}
- int nread = HANDLE_EINTR(read(fd_, &naclchild, sizeof(naclchild)));
- if (nread != sizeof(naclchild)) {
- LOG(ERROR) << "NaClForkDelegate::Fork: read failed";
+
+ // Now see if the other end managed to fork.
+ Pickle reply_pickle(reply_buf, reply_size);
+ PickleIterator iter(reply_pickle);
+ pid_t nacl_child;
+ if (!iter.ReadInt(&nacl_child)) {
+ LOG(ERROR) << "NaClForkDelegate::Fork: pickle failed";
return -1;
}
- VLOG(1) << "nacl_child is " << naclchild << " (" << nread << " bytes)";
- return naclchild;
+ VLOG(1) << "nacl_child is " << nacl_child;
+ return nacl_child;
}
bool NaClForkDelegate::AckChild(const int fd,
@@ -246,3 +295,47 @@ bool NaClForkDelegate::AckChild(const int fd,
}
return true;
}
+
+bool NaClForkDelegate::GetTerminationStatus(pid_t pid, bool known_dead,
+ base::TerminationStatus* status,
+ int* exit_code) {
+ VLOG(1) << "NaClForkDelegate::GetTerminationStatus";
+ DCHECK(status);
+ DCHECK(exit_code);
+
+ Pickle write_pickle;
+ write_pickle.WriteInt(kNaClGetTerminationStatusRequest);
+ write_pickle.WriteInt(pid);
+ write_pickle.WriteBool(known_dead);
+
+ const std::vector<int> empty_fds;
+ char reply_buf[kNaClMaxIPCMessageLength];
+ ssize_t reply_size = 0;
+ bool got_reply =
+ SendIPCRequestAndReadReply(fd_, empty_fds, write_pickle,
+ reply_buf, sizeof(reply_buf), &reply_size);
+ if (!got_reply) {
+ LOG(ERROR) << "Could not perform remote GetTerminationStatus.";
+ return false;
+ }
+
+ Pickle reply_pickle(reply_buf, reply_size);
+ PickleIterator iter(reply_pickle);
+ int termination_status;
+ if (!iter.ReadInt(&termination_status) ||
+ termination_status < 0 ||
+ termination_status >= base::TERMINATION_STATUS_MAX_ENUM) {
+ LOG(ERROR) << "GetTerminationStatus: pickle failed";
+ return false;
+ }
+
+ int remote_exit_code;
+ if (!iter.ReadInt(&remote_exit_code)) {
+ LOG(ERROR) << "GetTerminationStatus: pickle failed";
+ return false;
+ }
+
+ *status = static_cast<base::TerminationStatus>(termination_status);
+ *exit_code = remote_exit_code;
+ return true;
+}
diff --git a/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h
index 5812f33fe26..a5ec534aa21 100644
--- a/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h
+++ b/chromium/components/nacl/zygote/nacl_fork_delegate_linux.h
@@ -31,6 +31,9 @@ class NaClForkDelegate : public content::ZygoteForkDelegate {
virtual pid_t Fork(const std::vector<int>& fds) OVERRIDE;
virtual bool AckChild(int fd,
const std::string& channel_switch) OVERRIDE;
+ virtual bool GetTerminationStatus(pid_t pid, bool known_dead,
+ base::TerminationStatus* status,
+ int* exit_code) OVERRIDE;
private:
// These values are reported via UMA and hence they become permanent
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
deleted file mode 100644
index f2144675e1b..00000000000
--- a/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/InterceptNavigationDelegate.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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
deleted file mode 100644
index cdfd88313f7..00000000000
--- a/chromium/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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
deleted file mode 100644
index 9663529d7cc..00000000000
--- a/chromium/components/navigation_interception/component_jni_registrar.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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
deleted file mode 100644
index c79f3778e9f..00000000000
--- a/chromium/components/navigation_interception/component_jni_registrar.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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/policy.gypi b/chromium/components/policy.gypi
new file mode 100644
index 00000000000..7471212c27e
--- /dev/null
+++ b/chromium/components/policy.gypi
@@ -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.
+
+{
+ '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_pref_names.cc',
+ 'policy/core/common/policy_pref_names.h',
+ 'policy/core/common/policy_schema.cc',
+ 'policy/core/common/policy_schema.h',
+ 'policy/core/common/schema.cc',
+ 'policy/core/common/schema.h',
+ 'policy/core/common/schema_internal.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_pref_names.cc b/chromium/components/policy/core/common/policy_pref_names.cc
new file mode 100644
index 00000000000..ca86fcd0870
--- /dev/null
+++ b/chromium/components/policy/core/common/policy_pref_names.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 The Chromium Authors. 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_pref_names.h"
+
+namespace policy {
+namespace prefs {
+
+// 64-bit serialization of the time last policy usage statistics were collected
+// by UMA_HISTOGRAM_ENUMERATION.
+const char kLastPolicyStatisticsUpdate[] = "policy.last_statistics_update";
+
+} // namespace prefs
+} // namespace policy
diff --git a/chromium/components/policy/core/common/policy_pref_names.h b/chromium/components/policy/core/common/policy_pref_names.h
new file mode 100644
index 00000000000..0a65262fec0
--- /dev/null
+++ b/chromium/components/policy/core/common/policy_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_POLICY_CORE_COMMON_PREF_NAMES_H_
+#define COMPONENTS_POLICY_CORE_COMMON_PREF_NAMES_H_
+
+#include "components/policy/policy_export.h"
+
+namespace policy {
+namespace prefs {
+
+// Constants for the names of policy-related preferences.
+// TODO(dconnelly): remove POLICY_EXPORT once the statistics collector moves
+// to the policy component (crbug.com/271392).
+POLICY_EXPORT extern const char kLastPolicyStatisticsUpdate[];
+
+} // prefs
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_PREF_NAMES_H_
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/core/common/schema.cc b/chromium/components/policy/core/common/schema.cc
new file mode 100644
index 00000000000..cbfde46faf3
--- /dev/null
+++ b/chromium/components/policy/core/common/schema.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/policy/core/common/schema.h"
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "components/json_schema/json_schema_constants.h"
+#include "components/json_schema/json_schema_validator.h"
+#include "components/policy/core/common/schema_internal.h"
+
+namespace policy {
+
+namespace {
+
+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;
+}
+
+} // namespace
+
+Schema::Iterator::Iterator(const internal::PropertiesNode* properties)
+ : it_(properties->begin),
+ end_(properties->end) {}
+
+Schema::Iterator::Iterator(const Iterator& iterator)
+ : it_(iterator.it_),
+ end_(iterator.end_) {}
+
+Schema::Iterator::~Iterator() {}
+
+Schema::Iterator& Schema::Iterator::operator=(const Iterator& iterator) {
+ it_ = iterator.it_;
+ end_ = iterator.end_;
+ return *this;
+}
+
+bool Schema::Iterator::IsAtEnd() const {
+ return it_ == end_;
+}
+
+void Schema::Iterator::Advance() {
+ ++it_;
+}
+
+const char* Schema::Iterator::key() const {
+ return it_->key;
+}
+
+Schema Schema::Iterator::schema() const {
+ return Schema(it_->schema);
+}
+
+Schema::Schema(const internal::SchemaNode* schema) : schema_(schema) {}
+
+Schema::Schema(const Schema& schema) : schema_(schema.schema_) {}
+
+Schema& Schema::operator=(const Schema& schema) {
+ schema_ = schema.schema_;
+ return *this;
+}
+
+base::Value::Type Schema::type() const {
+ CHECK(valid());
+ return schema_->type;
+}
+
+Schema::Iterator Schema::GetPropertiesIterator() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ return Iterator(
+ static_cast<const internal::PropertiesNode*>(schema_->extra));
+}
+
+namespace {
+
+bool CompareKeys(const internal::PropertyNode& node, const std::string& key) {
+ return node.key < key;
+}
+
+} // namespace
+
+Schema Schema::GetKnownProperty(const std::string& key) const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ const internal::PropertiesNode* properties_node =
+ static_cast<const internal::PropertiesNode*>(schema_->extra);
+ const internal::PropertyNode* it = std::lower_bound(
+ properties_node->begin, properties_node->end, key, CompareKeys);
+ if (it != properties_node->end && it->key == key)
+ return Schema(it->schema);
+ return Schema(NULL);
+}
+
+Schema Schema::GetAdditionalProperties() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_DICTIONARY, type());
+ return Schema(
+ static_cast<const internal::PropertiesNode*>(schema_->extra)->additional);
+}
+
+Schema Schema::GetProperty(const std::string& key) const {
+ Schema schema = GetKnownProperty(key);
+ return schema.valid() ? schema : GetAdditionalProperties();
+}
+
+Schema Schema::GetItems() const {
+ CHECK(valid());
+ CHECK_EQ(base::Value::TYPE_LIST, type());
+ return Schema(static_cast<const internal::SchemaNode*>(schema_->extra));
+}
+
+SchemaOwner::SchemaOwner(const internal::SchemaNode* root) : root_(root) {}
+
+SchemaOwner::~SchemaOwner() {
+ for (size_t i = 0; i < property_nodes_.size(); ++i)
+ delete[] property_nodes_[i];
+}
+
+// static
+scoped_ptr<SchemaOwner> SchemaOwner::Wrap(const internal::SchemaNode* schema) {
+ return scoped_ptr<SchemaOwner>(new SchemaOwner(schema));
+}
+
+// static
+scoped_ptr<SchemaOwner> SchemaOwner::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<SchemaOwner>();
+
+ // Validate the main type.
+ std::string string_value;
+ 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<SchemaOwner>();
+ }
+
+ // 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<SchemaOwner>();
+ }
+
+ scoped_ptr<SchemaOwner> impl(new SchemaOwner(NULL));
+ impl->root_ = impl->Parse(*dict, error);
+ if (!impl->root_)
+ impl.reset();
+ return impl.PassAs<SchemaOwner>();
+}
+
+const internal::SchemaNode* SchemaOwner::Parse(
+ 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 NULL;
+ }
+
+ base::Value::Type type = base::Value::TYPE_NULL;
+ if (!SchemaTypeToValueType(type_string, &type)) {
+ *error = "Type not supported: " + type_string;
+ return NULL;
+ }
+
+ if (type == base::Value::TYPE_DICTIONARY)
+ return ParseDictionary(schema, error);
+ if (type == base::Value::TYPE_LIST)
+ return ParseList(schema, error);
+
+ internal::SchemaNode* node = new internal::SchemaNode;
+ node->type = type;
+ node->extra = NULL;
+ schema_nodes_.push_back(node);
+ return node;
+}
+
+const internal::SchemaNode* SchemaOwner::ParseDictionary(
+ const base::DictionaryValue& schema,
+ std::string* error) {
+ internal::PropertiesNode* properties_node = new internal::PropertiesNode;
+ properties_node->begin = NULL;
+ properties_node->end = NULL;
+ properties_node->additional = NULL;
+ properties_nodes_.push_back(properties_node);
+
+ const base::DictionaryValue* dict = NULL;
+ const base::DictionaryValue* properties = NULL;
+ if (schema.GetDictionary(json_schema_constants::kProperties, &properties)) {
+ internal::PropertyNode* property_nodes =
+ new internal::PropertyNode[properties->size()];
+ property_nodes_.push_back(property_nodes);
+
+ size_t index = 0;
+ for (base::DictionaryValue::Iterator it(*properties);
+ !it.IsAtEnd(); it.Advance(), ++index) {
+ // This should have been verified by the JSONSchemaValidator.
+ CHECK(it.value().GetAsDictionary(&dict));
+ const internal::SchemaNode* sub_schema = Parse(*dict, error);
+ if (!sub_schema)
+ return NULL;
+ std::string* key = new std::string(it.key());
+ keys_.push_back(key);
+ property_nodes[index].key = key->c_str();
+ property_nodes[index].schema = sub_schema;
+ }
+ CHECK_EQ(properties->size(), index);
+ properties_node->begin = property_nodes;
+ properties_node->end = property_nodes + index;
+ }
+
+ if (schema.GetDictionary(json_schema_constants::kAdditionalProperties,
+ &dict)) {
+ const internal::SchemaNode* sub_schema = Parse(*dict, error);
+ if (!sub_schema)
+ return NULL;
+ properties_node->additional = sub_schema;
+ }
+
+ internal::SchemaNode* schema_node = new internal::SchemaNode;
+ schema_node->type = base::Value::TYPE_DICTIONARY;
+ schema_node->extra = properties_node;
+ schema_nodes_.push_back(schema_node);
+ return schema_node;
+}
+
+const internal::SchemaNode* SchemaOwner::ParseList(
+ 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 NULL;
+ }
+ const internal::SchemaNode* items_schema_node = Parse(*dict, error);
+ if (!items_schema_node)
+ return NULL;
+
+ internal::SchemaNode* schema_node = new internal::SchemaNode;
+ schema_node->type = base::Value::TYPE_LIST;
+ schema_node->extra = items_schema_node;
+ schema_nodes_.push_back(schema_node);
+ return schema_node;
+}
+
+} // namespace policy
diff --git a/chromium/components/policy/core/common/schema.h b/chromium/components/policy/core/common/schema.h
new file mode 100644
index 00000000000..a81b7adae30
--- /dev/null
+++ b/chromium/components/policy/core/common/schema.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_POLICY_CORE_COMMON_SCHEMA_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/values.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+namespace internal {
+
+struct POLICY_EXPORT SchemaNode;
+struct POLICY_EXPORT PropertyNode;
+struct POLICY_EXPORT PropertiesNode;
+
+} // namespace internal
+
+// Describes the expected type of one policy. Also recursively describes the
+// types of inner elements, for structured types.
+// Objects of this class refer to external, immutable data and are cheap to
+// copy.
+// Use the SchemaOwner class to parse a schema and get Schema objects.
+class POLICY_EXPORT Schema {
+ public:
+ explicit Schema(const internal::SchemaNode* schema);
+ Schema(const Schema& schema);
+
+ Schema& operator=(const Schema& schema);
+
+ // Returns true if this Schema is valid. Schemas returned by the methods below
+ // may be invalid, and in those cases the other methods must not be used.
+ bool valid() const { return schema_ != NULL; }
+
+ base::Value::Type type() const;
+
+ // Used to iterate over the known properties of TYPE_DICTIONARY schemas.
+ class POLICY_EXPORT Iterator {
+ public:
+ explicit Iterator(const internal::PropertiesNode* properties);
+ Iterator(const Iterator& iterator);
+ ~Iterator();
+
+ Iterator& operator=(const Iterator& iterator);
+
+ // The other methods must not be called if the iterator is at the end.
+ bool IsAtEnd() const;
+
+ // Advances the iterator to the next property.
+ void Advance();
+
+ // Returns the name of the current property.
+ const char* key() const;
+
+ // Returns the Schema for the current property. This Schema is always valid.
+ Schema schema() const;
+
+ private:
+ const internal::PropertyNode* it_;
+ const internal::PropertyNode* end_;
+ };
+
+ // These methods should be called only if type() == TYPE_DICTIONARY,
+ // otherwise invalid memory will be read. A CHECK is currently enforcing this.
+
+ // Returns an iterator that goes over the named properties of this schema.
+ // The returned iterator is at the beginning.
+ Iterator GetPropertiesIterator() const;
+
+ // Returns the Schema for the property named |key|. If |key| is not a known
+ // property name then the returned Schema is not valid.
+ Schema GetKnownProperty(const std::string& key) const;
+
+ // Returns the Schema for additional properties. If additional properties are
+ // not allowed for this Schema then the Schema returned is not valid.
+ Schema GetAdditionalProperties() const;
+
+ // Returns the Schema for |key| if it is a known property, otherwise returns
+ // the Schema for additional properties.
+ Schema GetProperty(const std::string& key) const;
+
+ // Returns the Schema for items of an array.
+ // This method should be called only if type() == TYPE_LIST,
+ // otherwise invalid memory will be read. A CHECK is currently enforcing this.
+ Schema GetItems() const;
+
+ private:
+ const internal::SchemaNode* schema_;
+};
+
+// Owns schemas for policies of a given component.
+class POLICY_EXPORT SchemaOwner {
+ public:
+ ~SchemaOwner();
+
+ // The returned Schema is valid only during the lifetime of the SchemaOwner
+ // that created it. It may be obtained multiple times.
+ Schema schema() const { return Schema(root_); }
+
+ // Returns a SchemaOwner that references static data. This can be used by
+ // the embedder to pass structures generated at compile time, which can then
+ // be quickly loaded at runtime.
+ // Note: PropertiesNodes must have their PropertyNodes sorted by key.
+ static scoped_ptr<SchemaOwner> Wrap(const internal::SchemaNode* schema);
+
+ // Parses the JSON schema in |schema| and returns a SchemaOwner that owns
+ // the internal representation. If |schema| is invalid then NULL is returned
+ // and |error| contains a reason for the failure.
+ static scoped_ptr<SchemaOwner> Parse(const std::string& schema,
+ std::string* error);
+
+ private:
+ explicit SchemaOwner(const internal::SchemaNode* root);
+
+ // Parses the JSON schema in |schema| and returns the root SchemaNode if
+ // successful, otherwise returns NULL. Any intermediate objects built by
+ // this method are appended to the ScopedVectors.
+ const internal::SchemaNode* Parse(const base::DictionaryValue& schema,
+ std::string* error);
+
+ // Helper for Parse().
+ const internal::SchemaNode* ParseDictionary(
+ const base::DictionaryValue& schema,
+ std::string* error);
+
+ // Helper for Parse().
+ const internal::SchemaNode* ParseList(const base::DictionaryValue& schema,
+ std::string* error);
+
+ const internal::SchemaNode* root_;
+ ScopedVector<internal::SchemaNode> schema_nodes_;
+ // Note: |property_nodes_| contains PropertyNode[] elements and must be
+ // cleared manually to properly use delete[].
+ std::vector<internal::PropertyNode*> property_nodes_;
+ ScopedVector<internal::PropertiesNode> properties_nodes_;
+ ScopedVector<std::string> keys_;
+
+ DISALLOW_COPY_AND_ASSIGN(SchemaOwner);
+};
+
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_H_
diff --git a/chromium/components/policy/core/common/schema_internal.h b/chromium/components/policy/core/common/schema_internal.h
new file mode 100644
index 00000000000..025a4373ee7
--- /dev/null
+++ b/chromium/components/policy/core/common/schema_internal.h
@@ -0,0 +1,45 @@
+// Copyright 2013 The Chromium 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_SCHEMA_INTERNAL_H_
+#define COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_
+
+#include "base/values.h"
+#include "components/policy/policy_export.h"
+
+namespace policy {
+namespace internal {
+
+// These types are used internally by the SchemaOwner parser, and by the
+// compile-time code generator. They shouldn't be used directly.
+
+struct POLICY_EXPORT SchemaNode {
+ base::Value::Type type;
+
+ // If |type| is TYPE_LIST then this is a SchemaNode* describing the
+ // element type.
+ //
+ // If |type| is TYPE_DICTIONARY then this is a PropertiesNode* that can
+ // contain any number of named properties and optionally a SchemaNode* for
+ // additional properties.
+ //
+ // This is NULL if |type| has any other values.
+ const void* extra;
+};
+
+struct POLICY_EXPORT PropertyNode {
+ const char* key;
+ const SchemaNode* schema;
+};
+
+struct POLICY_EXPORT PropertiesNode {
+ const PropertyNode* begin;
+ const PropertyNode* end;
+ const SchemaNode* additional;
+};
+
+} // namespace internal
+} // namespace policy
+
+#endif // COMPONENTS_POLICY_CORE_COMMON_SCHEMA_INTERNAL_H_
diff --git a/chromium/components/policy/core/common/schema_unittest.cc b/chromium/components/policy/core/common/schema_unittest.cc
new file mode 100644
index 00000000000..06fb3bf34a3
--- /dev/null
+++ b/chromium/components/policy/core/common/schema_unittest.cc
@@ -0,0 +1,338 @@
+// Copyright 2013 The Chromium Authors. 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/schema.h"
+
+#include "components/policy/core/common/schema_internal.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+namespace {
+
+#define OBJECT_TYPE "\"type\":\"object\""
+
+const internal::SchemaNode kTypeBoolean = { base::Value::TYPE_BOOLEAN, NULL, };
+const internal::SchemaNode kTypeInteger = { base::Value::TYPE_INTEGER, NULL, };
+const internal::SchemaNode kTypeNumber = { base::Value::TYPE_DOUBLE, NULL, };
+const internal::SchemaNode kTypeString = { base::Value::TYPE_STRING, NULL, };
+
+bool ParseFails(const std::string& content) {
+ std::string error;
+ scoped_ptr<SchemaOwner> schema = SchemaOwner::Parse(content, &error);
+ if (schema)
+ EXPECT_TRUE(schema->schema().valid());
+ else
+ EXPECT_FALSE(error.empty());
+ return !schema;
+}
+
+} // namespace
+
+TEST(SchemaTest, MinimalSchema) {
+ EXPECT_FALSE(ParseFails(
+ "{"
+ OBJECT_TYPE
+ "}"));
+}
+
+TEST(SchemaTest, 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(
+ "{"
+ OBJECT_TYPE ","
+ "\"additionalProperties\": { \"type\":\"object\" }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"patternProperties\": { \"a+b*\": { \"type\": \"object\" } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": \"bogus\" } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": [\"string\", \"number\"] } }"
+ "}"));
+
+ EXPECT_TRUE(ParseFails(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": { \"Policy\": { \"type\": \"any\" } }"
+ "}"));
+}
+
+TEST(SchemaTest, ValidSchema) {
+ std::string error;
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Parse(
+ "{"
+ 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(policy_schema) << error;
+ ASSERT_TRUE(policy_schema->schema().valid());
+
+ Schema schema = policy_schema->schema();
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+ EXPECT_FALSE(schema.GetProperty("invalid").valid());
+
+ Schema sub = schema.GetProperty("Boolean");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_BOOLEAN, sub.type());
+
+ sub = schema.GetProperty("Integer");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_INTEGER, sub.type());
+
+ sub = schema.GetProperty("Null");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_NULL, sub.type());
+
+ sub = schema.GetProperty("Number");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_DOUBLE, sub.type());
+
+ sub = schema.GetProperty("String");
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+
+ sub = schema.GetProperty("Array");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+
+ sub = schema.GetProperty("ArrayOfObjects");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+ Schema subsub = sub.GetProperty("one");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
+ subsub = sub.GetProperty("two");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
+ subsub = sub.GetProperty("invalid");
+ EXPECT_FALSE(subsub.valid());
+
+ sub = schema.GetProperty("ArrayOfArray");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_LIST, sub.type());
+ sub = sub.GetItems();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, sub.type());
+
+ sub = schema.GetProperty("Object");
+ ASSERT_TRUE(sub.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, sub.type());
+ subsub = sub.GetProperty("one");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_BOOLEAN, subsub.type());
+ subsub = sub.GetProperty("two");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_INTEGER, subsub.type());
+ subsub = sub.GetProperty("undeclared");
+ ASSERT_TRUE(subsub.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, subsub.type());
+
+ struct {
+ const char* expected_key;
+ base::Value::Type expected_type;
+ } kExpectedProperties[] = {
+ { "Array", base::Value::TYPE_LIST },
+ { "ArrayOfArray", base::Value::TYPE_LIST },
+ { "ArrayOfObjects", base::Value::TYPE_LIST },
+ { "Boolean", base::Value::TYPE_BOOLEAN },
+ { "Integer", base::Value::TYPE_INTEGER },
+ { "Null", base::Value::TYPE_NULL },
+ { "Number", base::Value::TYPE_DOUBLE },
+ { "Object", base::Value::TYPE_DICTIONARY },
+ { "String", base::Value::TYPE_STRING },
+ };
+ Schema::Iterator it = schema.GetPropertiesIterator();
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedProperties); ++i) {
+ ASSERT_FALSE(it.IsAtEnd());
+ EXPECT_STREQ(kExpectedProperties[i].expected_key, it.key());
+ ASSERT_TRUE(it.schema().valid());
+ EXPECT_EQ(kExpectedProperties[i].expected_type, it.schema().type());
+ it.Advance();
+ }
+ EXPECT_TRUE(it.IsAtEnd());
+}
+
+TEST(SchemaTest, Lookups) {
+ std::string error;
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ Schema schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ // This empty schema should never find named properties.
+ EXPECT_FALSE(schema.GetKnownProperty("").valid());
+ EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+ EXPECT_TRUE(schema.GetPropertiesIterator().IsAtEnd());
+
+ policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": {"
+ " \"Boolean\": { \"type\": \"boolean\" }"
+ "}"
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ EXPECT_FALSE(schema.GetKnownProperty("").valid());
+ EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+ EXPECT_TRUE(schema.GetKnownProperty("Boolean").valid());
+
+ policy_schema = SchemaOwner::Parse(
+ "{"
+ OBJECT_TYPE ","
+ "\"properties\": {"
+ " \"bb\" : { \"type\": \"null\" },"
+ " \"aa\" : { \"type\": \"boolean\" },"
+ " \"abab\" : { \"type\": \"string\" },"
+ " \"ab\" : { \"type\": \"number\" },"
+ " \"aba\" : { \"type\": \"integer\" }"
+ "}"
+ "}", &error);
+ ASSERT_TRUE(policy_schema) << error;
+ schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ ASSERT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ EXPECT_FALSE(schema.GetKnownProperty("").valid());
+ EXPECT_FALSE(schema.GetKnownProperty("xyz").valid());
+
+ struct {
+ const char* expected_key;
+ base::Value::Type expected_type;
+ } kExpectedKeys[] = {
+ { "aa", base::Value::TYPE_BOOLEAN },
+ { "ab", base::Value::TYPE_DOUBLE },
+ { "aba", base::Value::TYPE_INTEGER },
+ { "abab", base::Value::TYPE_STRING },
+ { "bb", base::Value::TYPE_NULL },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kExpectedKeys); ++i) {
+ Schema sub = schema.GetKnownProperty(kExpectedKeys[i].expected_key);
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(kExpectedKeys[i].expected_type, sub.type());
+ }
+}
+
+TEST(SchemaTest, WrapSimpleNode) {
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Wrap(&kTypeString);
+ ASSERT_TRUE(policy_schema);
+ Schema schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ EXPECT_EQ(base::Value::TYPE_STRING, schema.type());
+}
+
+TEST(SchemaTest, WrapDictionary) {
+ const internal::SchemaNode kList = {
+ base::Value::TYPE_LIST,
+ &kTypeString,
+ };
+
+ const internal::PropertyNode kPropertyNodes[] = {
+ { "Boolean", &kTypeBoolean },
+ { "Integer", &kTypeInteger },
+ { "List", &kList },
+ { "Number", &kTypeNumber },
+ { "String", &kTypeString },
+ };
+
+ const internal::PropertiesNode kProperties = {
+ kPropertyNodes,
+ kPropertyNodes + arraysize(kPropertyNodes),
+ NULL,
+ };
+
+ const internal::SchemaNode root = {
+ base::Value::TYPE_DICTIONARY,
+ &kProperties,
+ };
+
+ scoped_ptr<SchemaOwner> policy_schema = SchemaOwner::Wrap(&root);
+ ASSERT_TRUE(policy_schema);
+ Schema schema = policy_schema->schema();
+ ASSERT_TRUE(schema.valid());
+ EXPECT_EQ(base::Value::TYPE_DICTIONARY, schema.type());
+
+ Schema::Iterator it = schema.GetPropertiesIterator();
+ for (size_t i = 0; i < arraysize(kPropertyNodes); ++i) {
+ ASSERT_FALSE(it.IsAtEnd());
+ EXPECT_STREQ(kPropertyNodes[i].key, it.key());
+ Schema sub = it.schema();
+ ASSERT_TRUE(sub.valid());
+ EXPECT_EQ(kPropertyNodes[i].schema->type, sub.type());
+ it.Advance();
+ }
+ EXPECT_TRUE(it.IsAtEnd());
+}
+
+} // 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/serialized_navigation_entry.cc b/chromium/components/sessions/serialized_navigation_entry.cc
index 5e4183cf5d2..0fc6ff184b3 100644
--- a/chromium/components/sessions/serialized_navigation_entry.cc
+++ b/chromium/components/sessions/serialized_navigation_entry.cc
@@ -26,6 +26,7 @@ SerializedNavigationEntry::SerializedNavigationEntry()
has_post_data_(false),
post_id_(-1),
is_overriding_user_agent_(false),
+ http_status_code_(0),
blocked_state_(STATE_INVALID) {}
SerializedNavigationEntry::~SerializedNavigationEntry() {}
@@ -53,6 +54,7 @@ SerializedNavigationEntry SerializedNavigationEntry::FromNavigationEntry(
entry.GetExtraData(kSearchTermsKey, &navigation.search_terms_);
if (entry.GetFavicon().valid)
navigation.favicon_url_ = entry.GetFavicon().url;
+ navigation.http_status_code_ = entry.GetHttpStatusCode();
return navigation;
}
@@ -143,6 +145,8 @@ SerializedNavigationEntry SerializedNavigationEntry::FromSyncData(
if (sync_data.has_favicon_url())
navigation.favicon_url_ = GURL(sync_data.favicon_url());
+ navigation.http_status_code_ = sync_data.http_status_code();
+
// 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());
@@ -216,6 +220,7 @@ enum TypeMask {
// is_overriding_user_agent_
// timestamp_
// search_terms_
+// http_status_code_
void SerializedNavigationEntry::WriteToPickle(int max_size,
Pickle* pickle) const {
@@ -255,6 +260,8 @@ void SerializedNavigationEntry::WriteToPickle(int max_size,
pickle->WriteInt64(timestamp_.ToInternalValue());
WriteString16ToPickle(pickle, &bytes_written, max_size, search_terms_);
+
+ pickle->WriteInt(http_status_code_);
}
bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
@@ -313,6 +320,9 @@ bool SerializedNavigationEntry::ReadFromPickle(PickleIterator* iterator) {
// If the search terms field can't be found, leave it empty.
if (!iterator->ReadString16(&search_terms_))
search_terms_.clear();
+
+ if (!iterator->ReadInt(&http_status_code_))
+ http_status_code_ = 0;
}
return true;
@@ -342,6 +352,7 @@ scoped_ptr<NavigationEntry> SerializedNavigationEntry::ToNavigationEntry(
entry->SetIsOverridingUserAgent(is_overriding_user_agent_);
entry->SetTimestamp(timestamp_);
entry->SetExtraData(kSearchTermsKey, search_terms_);
+ entry->SetHttpStatusCode(http_status_code_);
// These fields should have default values.
DCHECK_EQ(STATE_INVALID, blocked_state_);
@@ -440,6 +451,8 @@ sync_pb::TabNavigation SerializedNavigationEntry::ToSyncData() const {
sync_data.set_search_terms(UTF16ToUTF8(search_terms_));
+ sync_data.set_http_status_code(http_status_code_);
+
if (favicon_url_.is_valid())
sync_data.set_favicon_url(favicon_url_.spec());
diff --git a/chromium/components/sessions/serialized_navigation_entry.h b/chromium/components/sessions/serialized_navigation_entry.h
index e5813dce0d8..80581cde3d5 100644
--- a/chromium/components/sessions/serialized_navigation_entry.h
+++ b/chromium/components/sessions/serialized_navigation_entry.h
@@ -100,6 +100,7 @@ class SESSIONS_EXPORT SerializedNavigationEntry {
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_; }
+ int http_status_code() const { return http_status_code_; }
const content::Referrer& referrer() const { return referrer_; }
content::PageTransition transition_type() const {
return transition_type_;
@@ -149,6 +150,7 @@ class SESSIONS_EXPORT SerializedNavigationEntry {
base::Time timestamp_;
string16 search_terms_;
GURL favicon_url_;
+ int http_status_code_;
// Additional information.
BlockedState blocked_state_;
diff --git a/chromium/components/sessions/serialized_navigation_entry_test_helper.cc b/chromium/components/sessions/serialized_navigation_entry_test_helper.cc
index f4a7e2eba6a..64d3be27578 100644
--- a/chromium/components/sessions/serialized_navigation_entry_test_helper.cc
+++ b/chromium/components/sessions/serialized_navigation_entry_test_helper.cc
@@ -43,6 +43,7 @@ SerializedNavigationEntry SerializedNavigationEntryTestHelper::CreateNavigation(
navigation.page_state_ =
content::PageState::CreateFromEncodedData("fake_state");
navigation.timestamp_ = base::Time::Now();
+ navigation.http_status_code_ = 200;
return navigation;
}
diff --git a/chromium/components/sessions/serialized_navigation_entry_unittest.cc b/chromium/components/sessions/serialized_navigation_entry_unittest.cc
index 5f052fe23e7..6aa35a13ae1 100644
--- a/chromium/components/sessions/serialized_navigation_entry_unittest.cc
+++ b/chromium/components/sessions/serialized_navigation_entry_unittest.cc
@@ -48,6 +48,7 @@ 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 kHttpStatusCode = 404;
const int kPageID = 10;
@@ -68,6 +69,7 @@ scoped_ptr<content::NavigationEntry> MakeNavigationEntryForTest() {
navigation_entry->SetExtraData(kSearchTermsKey, kSearchTerms);
navigation_entry->GetFavicon().valid = true;
navigation_entry->GetFavicon().url = kFaviconURL;
+ navigation_entry->SetHttpStatusCode(kHttpStatusCode);
return navigation_entry.Pass();
}
@@ -86,6 +88,7 @@ sync_pb::TabNavigation MakeSyncDataForTest() {
sync_data.set_navigation_home_page(true);
sync_data.set_search_terms(UTF16ToUTF8(kSearchTerms));
sync_data.set_favicon_url(kFaviconURL.spec());
+ sync_data.set_http_status_code(kHttpStatusCode);
return sync_data;
}
@@ -108,6 +111,7 @@ TEST(SerializedNavigationEntryTest, DefaultInitializer) {
EXPECT_TRUE(navigation.timestamp().is_null());
EXPECT_TRUE(navigation.search_terms().empty());
EXPECT_FALSE(navigation.favicon_url().is_valid());
+ EXPECT_EQ(0, navigation.http_status_code());
}
// Create a SerializedNavigationEntry from a NavigationEntry. All its fields
@@ -134,6 +138,7 @@ TEST(SerializedNavigationEntryTest, FromNavigationEntry) {
EXPECT_EQ(kIsOverridingUserAgent, navigation.is_overriding_user_agent());
EXPECT_EQ(kTimestamp, navigation.timestamp());
EXPECT_EQ(kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(kHttpStatusCode, navigation.http_status_code());
}
// Create a SerializedNavigationEntry from a sync_pb::TabNavigation. All its
@@ -160,6 +165,7 @@ TEST(SerializedNavigationEntryTest, FromSyncData) {
EXPECT_TRUE(navigation.timestamp().is_null());
EXPECT_EQ(kSearchTerms, navigation.search_terms());
EXPECT_EQ(kFaviconURL, navigation.favicon_url());
+ EXPECT_EQ(kHttpStatusCode, navigation.http_status_code());
}
// Create a SerializedNavigationEntry, pickle it, then create another one by
@@ -192,6 +198,7 @@ TEST(SerializedNavigationEntryTest, Pickle) {
EXPECT_EQ(kIsOverridingUserAgent, new_navigation.is_overriding_user_agent());
EXPECT_EQ(kTimestamp, new_navigation.timestamp());
EXPECT_EQ(kSearchTerms, new_navigation.search_terms());
+ EXPECT_EQ(kHttpStatusCode, new_navigation.http_status_code());
}
// Create a NavigationEntry, then create another one by converting to
@@ -226,6 +233,7 @@ TEST(SerializedNavigationEntryTest, ToNavigationEntry) {
string16 search_terms;
new_navigation_entry->GetExtraData(kSearchTermsKey, &search_terms);
EXPECT_EQ(kSearchTerms, search_terms);
+ EXPECT_EQ(kHttpStatusCode, new_navigation_entry->GetHttpStatusCode());
}
// Create a NavigationEntry, convert it to a SerializedNavigationEntry, then
@@ -251,6 +259,7 @@ TEST(SerializedNavigationEntryTest, ToSyncData) {
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());
+ EXPECT_EQ(kHttpStatusCode, sync_data.http_status_code());
}
// Ensure all transition types and qualifiers are converted to/from the sync
diff --git a/chromium/components/startup_metric_utils.gypi b/chromium/components/startup_metric_utils.gypi
new file mode 100644
index 00000000000..55763c8db22
--- /dev/null
+++ b/chromium/components/startup_metric_utils.gypi
@@ -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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'startup_metric_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'startup_metric_utils/startup_metric_utils.cc',
+ 'startup_metric_utils/startup_metric_utils.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/startup_metric_utils/OWNERS b/chromium/components/startup_metric_utils/OWNERS
new file mode 100644
index 00000000000..b112a8471fb
--- /dev/null
+++ b/chromium/components/startup_metric_utils/OWNERS
@@ -0,0 +1 @@
+jeremy@chromium.org \ No newline at end of file
diff --git a/chromium/components/startup_metric_utils/startup_metric_utils.cc b/chromium/components/startup_metric_utils/startup_metric_utils.cc
new file mode 100644
index 00000000000..b2ac1bcada4
--- /dev/null
+++ b/chromium/components/startup_metric_utils/startup_metric_utils.cc
@@ -0,0 +1,169 @@
+// Copyright 2013 The Chromium Authors. 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/startup_metric_utils/startup_metric_utils.h"
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/statistics_recorder.h"
+#include "base/synchronization/lock.h"
+#include "base/sys_info.h"
+#include "base/time/time.h"
+
+namespace {
+
+// Mark as volatile to defensively make sure usage is thread-safe.
+// Note that at the time of this writing, access is only on the UI thread.
+volatile bool g_non_browser_ui_displayed = false;
+
+base::Time* MainEntryPointTimeInternal() {
+ static base::Time main_start_time = base::Time::Now();
+ return &main_start_time;
+}
+
+typedef base::hash_map<std::string,base::TimeDelta> SubsystemStartupTimeHash;
+
+SubsystemStartupTimeHash* GetSubsystemStartupTimeHash() {
+ static SubsystemStartupTimeHash* slow_startup_time_hash =
+ new SubsystemStartupTimeHash;
+ return slow_startup_time_hash;
+}
+
+base::Lock* GetSubsystemStartupTimeHashLock() {
+ static base::Lock* slow_startup_time_hash_lock = new base::Lock;
+ return slow_startup_time_hash_lock;
+}
+
+bool g_main_entry_time_was_recorded = false;
+bool g_startup_stats_collection_finished = false;
+bool g_was_slow_startup = false;
+
+} // namespace
+
+namespace startup_metric_utils {
+
+bool WasNonBrowserUIDisplayed() {
+ return g_non_browser_ui_displayed;
+}
+
+void SetNonBrowserUIDisplayed() {
+ g_non_browser_ui_displayed = true;
+}
+
+void RecordMainEntryPointTime() {
+ DCHECK(!g_main_entry_time_was_recorded);
+ g_main_entry_time_was_recorded = true;
+ MainEntryPointTimeInternal();
+}
+
+#if defined(OS_ANDROID)
+void RecordSavedMainEntryPointTime(const base::Time& entry_point_time) {
+ DCHECK(!g_main_entry_time_was_recorded);
+ g_main_entry_time_was_recorded = true;
+ *MainEntryPointTimeInternal() = entry_point_time;
+}
+#endif // OS_ANDROID
+
+// Return the time recorded by RecordMainEntryPointTime().
+const base::Time MainEntryStartTime() {
+ DCHECK(g_main_entry_time_was_recorded);
+ return *MainEntryPointTimeInternal();
+}
+
+void OnBrowserStartupComplete(bool is_first_run) {
+ // Bail if uptime < 7 minutes, to filter out cases where Chrome may have been
+ // autostarted and the machine is under io pressure.
+ const int64 kSevenMinutesInMilliseconds =
+ base::TimeDelta::FromMinutes(7).InMilliseconds();
+ if (base::SysInfo::Uptime() < kSevenMinutesInMilliseconds) {
+ g_startup_stats_collection_finished = true;
+ return;
+ }
+
+ // The Startup.BrowserMessageLoopStartTime histogram recorded in
+ // chrome_browser_main.cc exhibits instability in the field which limits its
+ // usefulness in all scenarios except when we have a very large sample size.
+ // Attempt to mitigate this with a new metric:
+ // * Measure time from main entry rather than the OS' notion of process start
+ // time.
+ // * Only measure launches that occur 7 minutes after boot to try to avoid
+ // cases where Chrome is auto-started and IO is heavily loaded.
+ base::TimeDelta startup_time_from_main_entry =
+ base::Time::Now() - MainEntryStartTime();
+ if (is_first_run) {
+ UMA_HISTOGRAM_LONG_TIMES(
+ "Startup.BrowserMessageLoopStartTimeFromMainEntry.FirstRun",
+ startup_time_from_main_entry);
+ } else {
+ UMA_HISTOGRAM_LONG_TIMES(
+ "Startup.BrowserMessageLoopStartTimeFromMainEntry",
+ startup_time_from_main_entry);
+ }
+
+ // Record histograms for the subsystem times for startups > 10 seconds.
+ const base::TimeDelta kTenSeconds = base::TimeDelta::FromSeconds(10);
+ if (startup_time_from_main_entry < kTenSeconds) {
+ g_startup_stats_collection_finished = true;
+ return;
+ }
+
+ // If we got here this was what we consider to be a slow startup which we
+ // want to record stats for.
+ g_was_slow_startup = true;
+}
+
+void OnInitialPageLoadComplete() {
+ if (!g_was_slow_startup)
+ return;
+ DCHECK(!g_startup_stats_collection_finished);
+
+ const base::TimeDelta kStartupTimeMin(
+ base::TimeDelta::FromMilliseconds(1));
+ const base::TimeDelta kStartupTimeMax(base::TimeDelta::FromMinutes(5));
+ static const size_t kStartupTimeBuckets = 100;
+
+ // Set UMA flag for histograms outside chrome/ that can't use the
+ // ScopedSlowStartupUMA class.
+ base::HistogramBase* histogram =
+ base::StatisticsRecorder::FindHistogram("Startup.SlowStartupNSSInit");
+ if (histogram)
+ histogram->SetFlags(base::HistogramBase::kUmaTargetedHistogramFlag);
+
+ // Iterate over the stats recorded by ScopedSlowStartupUMA and create
+ // histograms for them.
+ base::AutoLock locker(*GetSubsystemStartupTimeHashLock());
+ SubsystemStartupTimeHash* time_hash = GetSubsystemStartupTimeHash();
+ for (SubsystemStartupTimeHash::iterator i = time_hash->begin();
+ i != time_hash->end();
+ ++i) {
+ const std::string histogram_name = i->first;
+ base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
+ histogram_name,
+ kStartupTimeMin,
+ kStartupTimeMax,
+ kStartupTimeBuckets,
+ base::Histogram::kUmaTargetedHistogramFlag);
+ counter->AddTime(i->second);
+ }
+
+ g_startup_stats_collection_finished = true;
+}
+
+ScopedSlowStartupUMA::~ScopedSlowStartupUMA() {
+ if (g_startup_stats_collection_finished)
+ return;
+
+ base::AutoLock locker(*GetSubsystemStartupTimeHashLock());
+ SubsystemStartupTimeHash* hash = GetSubsystemStartupTimeHash();
+ // Only record the initial sample for a given histogram.
+ if (hash->find(histogram_name_) != hash->end())
+ return;
+
+ (*hash)[histogram_name_] =
+ base::TimeTicks::Now() - start_time_;
+}
+
+} // namespace startup_metric_utils
diff --git a/chromium/components/startup_metric_utils/startup_metric_utils.h b/chromium/components/startup_metric_utils/startup_metric_utils.h
new file mode 100644
index 00000000000..594ad15109a
--- /dev/null
+++ b/chromium/components/startup_metric_utils/startup_metric_utils.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_STARTUP_METRIC_UTILS_STARTUP_METRIC_UTILS_H_
+#define COMPONENTS_STARTUP_METRIC_UTILS_STARTUP_METRIC_UTILS_H_
+
+#include <string>
+
+#include "base/time/time.h"
+
+// Utility functions to support metric collection for browser startup.
+
+namespace startup_metric_utils {
+
+// Returns true if any UI other than the browser window has been displayed
+// so far. Useful to test if UI has been displayed before the first browser
+// window was shown, which would invalidate any surrounding timing metrics.
+bool WasNonBrowserUIDisplayed();
+
+// Call this when displaying UI that might potentially delay the appearance
+// of the initial browser window on Chrome startup.
+//
+// Note on usage: This function is idempotent and its overhead is low enough
+// in comparison with UI display that it's OK to call it on every
+// UI invocation regardless of whether the browser window has already
+// been displayed or not.
+void SetNonBrowserUIDisplayed();
+
+// Call this as early as possible in the startup process to record a
+// timestamp.
+void RecordMainEntryPointTime();
+
+#if defined(OS_ANDROID)
+// On Android the entry point time is the time at which the Java code starts.
+// This is recorded on the Java side, and then passed to the C++ side once the
+// C++ library is loaded and running.
+void RecordSavedMainEntryPointTime(const base::Time& entry_point_time);
+#endif // OS_ANDROID
+
+// Called just before the message loop is about to start. Entry point to record
+// startup stats.
+// |is_first_run| - is the current launch part of a first run.
+void OnBrowserStartupComplete(bool is_first_run);
+
+// Called when the initial page load has finished in order to record startup
+// stats.
+void OnInitialPageLoadComplete();
+
+// Scoper that records the time period before it's destructed in a histogram
+// with the given name. The histogram is only recorded for slow chrome startups.
+// Useful for trying to figure out what parts of Chrome cause slow startup.
+class ScopedSlowStartupUMA {
+ public:
+ explicit ScopedSlowStartupUMA(const std::string& histogram_name)
+ : start_time_(base::TimeTicks::Now()),
+ histogram_name_(histogram_name) {}
+
+ ~ScopedSlowStartupUMA();
+
+ private:
+ const base::TimeTicks start_time_;
+ const std::string histogram_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSlowStartupUMA);
+};
+
+} // namespace startup_metric_utils
+
+#endif // COMPONENTS_STARTUP_METRIC_UTILS_STARTUP_METRIC_UTILS_H_
diff --git a/chromium/components/tools/metrics/testdata/foo.cc b/chromium/components/tools/metrics/testdata/foo.cc
deleted file mode 100644
index bd3d3c716d2..00000000000
--- a/chromium/components/tools/metrics/testdata/foo.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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
deleted file mode 100644
index 2b186f93da4..00000000000
--- a/chromium/components/tools/metrics/testdata/foo_ignored.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-#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
deleted file mode 100644
index adf1f891ea9..00000000000
--- a/chromium/components/tools/metrics/testdata/subdir/foo_test.mm
+++ /dev/null
@@ -1,8 +0,0 @@
-// 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/child_trace_message_filter.cc b/chromium/components/tracing/child_trace_message_filter.cc
index 154903a15d0..8310ec52db5 100644
--- a/chromium/components/tracing/child_trace_message_filter.cc
+++ b/chromium/components/tracing/child_trace_message_filter.cc
@@ -65,16 +65,12 @@ void ChildTraceMessageFilter::OnBeginTracing(
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
+ // Flush will generate one or more callbacks to OnTraceDataCollected
+ // synchronously or asynchronously. EndTracingAck will be sent in the last
+ // OnTraceDataCollected. 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() {
@@ -94,15 +90,23 @@ void ChildTraceMessageFilter::OnCancelWatchEvent() {
}
void ChildTraceMessageFilter::OnTraceDataCollected(
- const scoped_refptr<base::RefCountedString>& events_str_ptr) {
+ const scoped_refptr<base::RefCountedString>& events_str_ptr,
+ bool has_more_events) {
if (!ipc_message_loop_->BelongsToCurrentThread()) {
ipc_message_loop_->PostTask(FROM_HERE,
base::Bind(&ChildTraceMessageFilter::OnTraceDataCollected, this,
- events_str_ptr));
+ events_str_ptr, has_more_events));
return;
}
- channel_->Send(new TracingHostMsg_TraceDataCollected(
- events_str_ptr->data()));
+ if (events_str_ptr->data().size()) {
+ channel_->Send(new TracingHostMsg_TraceDataCollected(
+ events_str_ptr->data()));
+ }
+ if (!has_more_events) {
+ std::vector<std::string> category_groups;
+ TraceLog::GetInstance()->GetKnownCategoryGroups(&category_groups);
+ channel_->Send(new TracingHostMsg_EndTracingAck(category_groups));
+ }
}
void ChildTraceMessageFilter::OnTraceNotification(int notification) {
diff --git a/chromium/components/tracing/child_trace_message_filter.h b/chromium/components/tracing/child_trace_message_filter.h
index 9631ced7b34..9f10b4a83a9 100644
--- a/chromium/components/tracing/child_trace_message_filter.h
+++ b/chromium/components/tracing/child_trace_message_filter.h
@@ -41,7 +41,8 @@ class ChildTraceMessageFilter : public IPC::ChannelProxy::MessageFilter {
// Callback from trace subsystem.
void OnTraceDataCollected(
- const scoped_refptr<base::RefCountedString>& events_str_ptr);
+ const scoped_refptr<base::RefCountedString>& events_str_ptr,
+ bool has_more_events);
void OnTraceNotification(int notification);
IPC::Channel* channel_;
diff --git a/chromium/components/tracing_untrusted.gyp b/chromium/components/tracing_untrusted.gyp
index f2215de6a1b..1991ec28a08 100644
--- a/chromium/components/tracing_untrusted.gyp
+++ b/chromium/components/tracing_untrusted.gyp
@@ -26,7 +26,7 @@
'nacl_untrusted_build': 1,
'nlib_target': 'libtracing_untrusted.a',
'build_glibc': 0,
- 'build_newlib': 1,
+ 'build_newlib': 0,
'build_irt': 1,
},
'sources': [
diff --git a/chromium/components/variations.gypi b/chromium/components/variations.gypi
new file mode 100644
index 00000000000..a5b9c6e6453
--- /dev/null
+++ b/chromium/components/variations.gypi
@@ -0,0 +1,36 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'variations',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../third_party/mt19937ar/mt19937ar.gyp:mt19937ar',
+ ],
+ 'sources': [
+ 'variations/entropy_provider.cc',
+ 'variations/entropy_provider.h',
+ 'variations/proto/variations_seed.proto',
+ 'variations/proto/study.proto',
+ 'variations/metrics_util.cc',
+ 'variations/metrics_util.h',
+ 'variations/variations_associated_data.cc',
+ 'variations/variations_associated_data.h',
+ 'variations/variations_seed_processor.cc',
+ 'variations/variations_seed_processor.h',
+ ],
+ 'variables': {
+ 'proto_in_dir': 'variations/proto',
+ 'proto_out_dir': 'components/variations/proto',
+ },
+ 'includes': [ '../build/protoc.gypi' ]
+ },
+ ],
+}
diff --git a/chromium/components/variations/OWNERS b/chromium/components/variations/OWNERS
new file mode 100644
index 00000000000..aadd23c5252
--- /dev/null
+++ b/chromium/components/variations/OWNERS
@@ -0,0 +1,3 @@
+asvitkine@chromium.org
+jwd@chromium.org
+stevet@chromium.org
diff --git a/chromium/components/variations/entropy_provider.cc b/chromium/components/variations/entropy_provider.cc
new file mode 100644
index 00000000000..a547cb1cf2b
--- /dev/null
+++ b/chromium/components/variations/entropy_provider.cc
@@ -0,0 +1,119 @@
+// 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/variations/entropy_provider.h"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/sha1.h"
+#include "base/sys_byteorder.h"
+#include "components/variations/metrics_util.h"
+
+namespace metrics {
+
+namespace internal {
+
+SeededRandGenerator::SeededRandGenerator(uint32 seed) {
+ mersenne_twister_.init_genrand(seed);
+}
+
+SeededRandGenerator::~SeededRandGenerator() {
+}
+
+uint32 SeededRandGenerator::operator()(uint32 range) {
+ // Based on base::RandGenerator().
+ DCHECK_GT(range, 0u);
+
+ // We must discard random results above this number, as they would
+ // make the random generator non-uniform (consider e.g. if
+ // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice
+ // as likely as a result of 3 or 4).
+ uint32 max_acceptable_value =
+ (std::numeric_limits<uint32>::max() / range) * range - 1;
+
+ uint32 value;
+ do {
+ value = mersenne_twister_.genrand_int32();
+ } while (value > max_acceptable_value);
+
+ return value % range;
+}
+
+void PermuteMappingUsingRandomizationSeed(uint32 randomization_seed,
+ std::vector<uint16>* mapping) {
+ for (size_t i = 0; i < mapping->size(); ++i)
+ (*mapping)[i] = static_cast<uint16>(i);
+
+ SeededRandGenerator generator(randomization_seed);
+ std::random_shuffle(mapping->begin(), mapping->end(), generator);
+}
+
+} // namespace internal
+
+SHA1EntropyProvider::SHA1EntropyProvider(const std::string& entropy_source)
+ : entropy_source_(entropy_source) {
+}
+
+SHA1EntropyProvider::~SHA1EntropyProvider() {
+}
+
+double SHA1EntropyProvider::GetEntropyForTrial(
+ const std::string& trial_name,
+ uint32 randomization_seed) const {
+ // Given enough input entropy, SHA-1 will produce a uniformly random spread
+ // in its output space. In this case, the input entropy that is used is the
+ // combination of the original |entropy_source_| and the |trial_name|.
+ //
+ // Note: If |entropy_source_| has very low entropy, such as 13 bits or less,
+ // it has been observed that this method does not result in a uniform
+ // distribution given the same |trial_name|. When using such a low entropy
+ // source, PermutedEntropyProvider should be used instead.
+ std::string input(entropy_source_ + trial_name);
+ unsigned char sha1_hash[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
+ input.size(),
+ sha1_hash);
+
+ uint64 bits;
+ COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data);
+ memcpy(&bits, sha1_hash, sizeof(bits));
+ bits = base::ByteSwapToLE64(bits);
+
+ return base::BitsToOpenEndedUnitInterval(bits);
+}
+
+PermutedEntropyProvider::PermutedEntropyProvider(
+ uint16 low_entropy_source,
+ size_t low_entropy_source_max)
+ : low_entropy_source_(low_entropy_source),
+ low_entropy_source_max_(low_entropy_source_max) {
+ DCHECK_LT(low_entropy_source, low_entropy_source_max);
+ DCHECK_LE(low_entropy_source_max, std::numeric_limits<uint16>::max());
+}
+
+PermutedEntropyProvider::~PermutedEntropyProvider() {
+}
+
+double PermutedEntropyProvider::GetEntropyForTrial(
+ const std::string& trial_name,
+ uint32 randomization_seed) const {
+ if (randomization_seed == 0)
+ randomization_seed = HashName(trial_name);
+
+ return GetPermutedValue(randomization_seed) /
+ static_cast<double>(low_entropy_source_max_);
+}
+
+uint16 PermutedEntropyProvider::GetPermutedValue(
+ uint32 randomization_seed) const {
+ std::vector<uint16> mapping(low_entropy_source_max_);
+ internal::PermuteMappingUsingRandomizationSeed(randomization_seed, &mapping);
+ return mapping[low_entropy_source_];
+}
+
+} // namespace metrics
diff --git a/chromium/components/variations/entropy_provider.h b/chromium/components/variations/entropy_provider.h
new file mode 100644
index 00000000000..786ae28165b
--- /dev/null
+++ b/chromium/components/variations/entropy_provider.h
@@ -0,0 +1,94 @@
+// 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_VARIATIONS_ENTROPY_PROVIDER_H_
+#define COMPONENTS_VARIATIONS_ENTROPY_PROVIDER_H_
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/metrics/field_trial.h"
+#include "third_party/mt19937ar/mt19937ar.h"
+
+namespace metrics {
+
+// Internals of entropy_provider.cc exposed for testing.
+namespace internal {
+
+// A functor that generates random numbers based on a seed, using the Mersenne
+// Twister algorithm. Suitable for use with std::random_shuffle().
+struct SeededRandGenerator : std::unary_function<uint32, uint32> {
+ explicit SeededRandGenerator(uint32 seed);
+ ~SeededRandGenerator();
+
+ // Returns a random number in range [0, range).
+ uint32 operator()(uint32 range);
+
+ MersenneTwister mersenne_twister_;
+};
+
+// Fills |mapping| to create a bijection of values in the range of
+// [0, |mapping.size()|), permuted based on |randomization_seed|.
+void PermuteMappingUsingRandomizationSeed(uint32 randomization_seed,
+ std::vector<uint16>* mapping);
+
+} // namespace internal
+
+// SHA1EntropyProvider is an entropy provider suitable for high entropy
+// sources. It works by taking the first 64 bits of the SHA1 hash of the
+// entropy source concatenated with the trial name and using that for the
+// final entropy value.
+class SHA1EntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+ // Creates a SHA1EntropyProvider with the given |entropy_source|, which
+ // should contain a large amount of entropy - for example, a textual
+ // representation of a persistent randomly-generated 128-bit value.
+ explicit SHA1EntropyProvider(const std::string& entropy_source);
+ virtual ~SHA1EntropyProvider();
+
+ // base::FieldTrial::EntropyProvider implementation:
+ virtual double GetEntropyForTrial(const std::string& trial_name,
+ uint32 randomization_seed) const OVERRIDE;
+
+ private:
+ std::string entropy_source_;
+
+ DISALLOW_COPY_AND_ASSIGN(SHA1EntropyProvider);
+};
+
+// PermutedEntropyProvider is an entropy provider suitable for low entropy
+// sources (below 16 bits). It uses the field trial name to generate a
+// permutation of a mapping array from an initial entropy value to a new value.
+// Note: This provider's performance is O(2^n), where n is the number of bits
+// in the entropy source.
+class PermutedEntropyProvider : public base::FieldTrial::EntropyProvider {
+ public:
+ // Creates a PermutedEntropyProvider with the given |low_entropy_source|,
+ // which should have a value in the range of [0, low_entropy_source_max).
+ PermutedEntropyProvider(uint16 low_entropy_source,
+ size_t low_entropy_source_max);
+ virtual ~PermutedEntropyProvider();
+
+ // base::FieldTrial::EntropyProvider implementation:
+ virtual double GetEntropyForTrial(const std::string& trial_name,
+ uint32 randomization_seed) const OVERRIDE;
+
+ protected:
+ // Performs the permutation algorithm and returns the permuted value that
+ // corresponds to |low_entropy_source_|.
+ virtual uint16 GetPermutedValue(uint32 randomization_seed) const;
+
+ private:
+ uint16 low_entropy_source_;
+ size_t low_entropy_source_max_;
+
+ DISALLOW_COPY_AND_ASSIGN(PermutedEntropyProvider);
+};
+
+} // namespace metrics
+
+#endif // COMPONENTS_VARIATIONS_ENTROPY_PROVIDER_H_
diff --git a/chromium/components/variations/entropy_provider_unittest.cc b/chromium/components/variations/entropy_provider_unittest.cc
new file mode 100644
index 00000000000..4e9a63718bf
--- /dev/null
+++ b/chromium/components/variations/entropy_provider_unittest.cc
@@ -0,0 +1,369 @@
+// 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/variations/entropy_provider.h"
+
+#include <cmath>
+#include <limits>
+#include <numeric>
+
+#include "base/basictypes.h"
+#include "base/guid.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "components/variations/metrics_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+namespace {
+
+// Size of the low entropy source to use for the permuted entropy provider
+// in tests.
+const size_t kMaxLowEntropySize = 8000;
+
+// Field trial names used in unit tests.
+const char* const kTestTrialNames[] = { "TestTrial", "AnotherTestTrial",
+ "NewTabButton" };
+
+// Computes the Chi-Square statistic for |values| assuming they follow a uniform
+// distribution, where each entry has expected value |expected_value|.
+//
+// The Chi-Square statistic is defined as Sum((O-E)^2/E) where O is the observed
+// value and E is the expected value.
+double ComputeChiSquare(const std::vector<int>& values,
+ double expected_value) {
+ double sum = 0;
+ for (size_t i = 0; i < values.size(); ++i) {
+ const double delta = values[i] - expected_value;
+ sum += (delta * delta) / expected_value;
+ }
+ return sum;
+}
+
+// Computes SHA1-based entropy for the given |trial_name| based on
+// |entropy_source|
+double GenerateSHA1Entropy(const std::string& entropy_source,
+ const std::string& trial_name) {
+ SHA1EntropyProvider sha1_provider(entropy_source);
+ return sha1_provider.GetEntropyForTrial(trial_name, 0);
+}
+
+// Generates permutation-based entropy for the given |trial_name| based on
+// |entropy_source| which must be in the range [0, entropy_max).
+double GeneratePermutedEntropy(uint16 entropy_source,
+ size_t entropy_max,
+ const std::string& trial_name) {
+ PermutedEntropyProvider permuted_provider(entropy_source, entropy_max);
+ return permuted_provider.GetEntropyForTrial(trial_name, 0);
+}
+
+// Helper interface for testing used to generate entropy values for a given
+// field trial. Unlike EntropyProvider, which keeps the low/high entropy source
+// value constant and generates entropy for different trial names, instances
+// of TrialEntropyGenerator keep the trial name constant and generate low/high
+// entropy source values internally to produce each output entropy value.
+class TrialEntropyGenerator {
+ public:
+ virtual ~TrialEntropyGenerator() {}
+ virtual double GenerateEntropyValue() const = 0;
+};
+
+// An TrialEntropyGenerator that uses the SHA1EntropyProvider with the high
+// entropy source (random GUID with 128 bits of entropy + 13 additional bits of
+// entropy corresponding to a low entropy source).
+class SHA1EntropyGenerator : public TrialEntropyGenerator {
+ public:
+ explicit SHA1EntropyGenerator(const std::string& trial_name)
+ : trial_name_(trial_name) {
+ }
+
+ virtual ~SHA1EntropyGenerator() {
+ }
+
+ virtual double GenerateEntropyValue() const OVERRIDE {
+ // Use a random GUID + 13 additional bits of entropy to match how the
+ // SHA1EntropyProvider is used in metrics_service.cc.
+ const int low_entropy_source =
+ static_cast<uint16>(base::RandInt(0, kMaxLowEntropySize - 1));
+ const std::string high_entropy_source =
+ base::GenerateGUID() + base::IntToString(low_entropy_source);
+ return GenerateSHA1Entropy(high_entropy_source, trial_name_);
+ }
+
+ private:
+ std::string trial_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(SHA1EntropyGenerator);
+};
+
+// An TrialEntropyGenerator that uses the permuted entropy provider algorithm,
+// using 13-bit low entropy source values.
+class PermutedEntropyGenerator : public TrialEntropyGenerator {
+ public:
+ explicit PermutedEntropyGenerator(const std::string& trial_name)
+ : mapping_(kMaxLowEntropySize) {
+ // Note: Given a trial name, the computed mapping will be the same.
+ // As a performance optimization, pre-compute the mapping once per trial
+ // name and index into it for each entropy value.
+ const uint32 randomization_seed = HashName(trial_name);
+ internal::PermuteMappingUsingRandomizationSeed(randomization_seed,
+ &mapping_);
+ }
+
+ virtual ~PermutedEntropyGenerator() {
+ }
+
+ virtual double GenerateEntropyValue() const OVERRIDE {
+ const int low_entropy_source =
+ static_cast<uint16>(base::RandInt(0, kMaxLowEntropySize - 1));
+ return mapping_[low_entropy_source] /
+ static_cast<double>(kMaxLowEntropySize);
+ }
+
+ private:
+ std::vector<uint16> mapping_;
+
+ DISALLOW_COPY_AND_ASSIGN(PermutedEntropyGenerator);
+};
+
+// Tests uniformity of a given |entropy_generator| using the Chi-Square Goodness
+// of Fit Test.
+void PerformEntropyUniformityTest(
+ const std::string& trial_name,
+ const TrialEntropyGenerator& entropy_generator) {
+ // Number of buckets in the simulated field trials.
+ const size_t kBucketCount = 20;
+ // Max number of iterations to perform before giving up and failing.
+ const size_t kMaxIterationCount = 100000;
+ // The number of iterations to perform before each time the statistical
+ // significance of the results is checked.
+ const size_t kCheckIterationCount = 10000;
+ // This is the Chi-Square threshold from the Chi-Square statistic table for
+ // 19 degrees of freedom (based on |kBucketCount|) with a 99.9% confidence
+ // level. See: http://www.medcalc.org/manual/chi-square-table.php
+ const double kChiSquareThreshold = 43.82;
+
+ std::vector<int> distribution(kBucketCount);
+
+ for (size_t i = 1; i <= kMaxIterationCount; ++i) {
+ const double entropy_value = entropy_generator.GenerateEntropyValue();
+ const size_t bucket = static_cast<size_t>(kBucketCount * entropy_value);
+ ASSERT_LT(bucket, kBucketCount);
+ distribution[bucket] += 1;
+
+ // After |kCheckIterationCount| iterations, compute the Chi-Square
+ // statistic of the distribution. If the resulting statistic is greater
+ // than |kChiSquareThreshold|, we can conclude with 99.9% confidence
+ // that the observed samples do not follow a uniform distribution.
+ //
+ // However, since 99.9% would still result in a false negative every
+ // 1000 runs of the test, do not treat it as a failure (else the test
+ // will be flaky). Instead, perform additional iterations to determine
+ // if the distribution will converge, up to |kMaxIterationCount|.
+ if ((i % kCheckIterationCount) == 0) {
+ const double expected_value_per_bucket =
+ static_cast<double>(i) / kBucketCount;
+ const double chi_square =
+ ComputeChiSquare(distribution, expected_value_per_bucket);
+ if (chi_square < kChiSquareThreshold)
+ break;
+
+ // If |i == kMaxIterationCount|, the Chi-Square statistic did not
+ // converge after |kMaxIterationCount|.
+ EXPECT_NE(i, kMaxIterationCount) << "Failed for trial " <<
+ trial_name << " with chi_square = " << chi_square <<
+ " after " << kMaxIterationCount << " iterations.";
+ }
+ }
+}
+
+} // namespace
+
+TEST(EntropyProviderTest, UseOneTimeRandomizationSHA1) {
+ // Simply asserts that two trials using one-time randomization
+ // that have different names, normally generate different results.
+ //
+ // Note that depending on the one-time random initialization, they
+ // _might_ actually give the same result, but we know that given
+ // the particular client_id we use for unit tests they won't.
+ base::FieldTrialList field_trial_list(new SHA1EntropyProvider("client_id"));
+ const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+ scoped_refptr<base::FieldTrial> trials[] = {
+ base::FieldTrialList::FactoryGetFieldTrial(
+ "one", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+ base::FieldTrialList::FactoryGetFieldTrial(
+ "two", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+ };
+
+ for (size_t i = 0; i < arraysize(trials); ++i) {
+ for (int j = 0; j < 100; ++j)
+ trials[i]->AppendGroup(std::string(), 1);
+ }
+
+ // The trials are most likely to give different results since they have
+ // different names.
+ EXPECT_NE(trials[0]->group(), trials[1]->group());
+ EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
+}
+
+TEST(EntropyProviderTest, UseOneTimeRandomizationPermuted) {
+ // Simply asserts that two trials using one-time randomization
+ // that have different names, normally generate different results.
+ //
+ // Note that depending on the one-time random initialization, they
+ // _might_ actually give the same result, but we know that given
+ // the particular client_id we use for unit tests they won't.
+ base::FieldTrialList field_trial_list(
+ new PermutedEntropyProvider(1234, kMaxLowEntropySize));
+ const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+ scoped_refptr<base::FieldTrial> trials[] = {
+ base::FieldTrialList::FactoryGetFieldTrial(
+ "one", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+ base::FieldTrialList::FactoryGetFieldTrial(
+ "two", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, NULL),
+ };
+
+ for (size_t i = 0; i < arraysize(trials); ++i) {
+ for (int j = 0; j < 100; ++j)
+ trials[i]->AppendGroup(std::string(), 1);
+ }
+
+ // The trials are most likely to give different results since they have
+ // different names.
+ EXPECT_NE(trials[0]->group(), trials[1]->group());
+ EXPECT_NE(trials[0]->group_name(), trials[1]->group_name());
+}
+
+TEST(EntropyProviderTest, UseOneTimeRandomizationWithCustomSeedPermuted) {
+ // Ensures that two trials with different names but the same custom seed used
+ // for one time randomization produce the same group assignments.
+ base::FieldTrialList field_trial_list(
+ new PermutedEntropyProvider(1234, kMaxLowEntropySize));
+ const int kNoExpirationYear = base::FieldTrialList::kNoExpirationYear;
+ const uint32 kCustomSeed = 9001;
+ scoped_refptr<base::FieldTrial> trials[] = {
+ base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+ "one", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL),
+ base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+ "two", 100, "default", kNoExpirationYear, 1, 1,
+ base::FieldTrial::ONE_TIME_RANDOMIZED, kCustomSeed, NULL),
+ };
+
+ for (size_t i = 0; i < arraysize(trials); ++i) {
+ for (int j = 0; j < 100; ++j)
+ trials[i]->AppendGroup(std::string(), 1);
+ }
+
+ // Normally, these trials should produce different groups, but if the same
+ // custom seed is used, they should produce the same group assignment.
+ EXPECT_EQ(trials[0]->group(), trials[1]->group());
+ EXPECT_EQ(trials[0]->group_name(), trials[1]->group_name());
+}
+
+TEST(EntropyProviderTest, SHA1Entropy) {
+ const double results[] = { GenerateSHA1Entropy("hi", "1"),
+ GenerateSHA1Entropy("there", "1") };
+
+ EXPECT_NE(results[0], results[1]);
+ for (size_t i = 0; i < arraysize(results); ++i) {
+ EXPECT_LE(0.0, results[i]);
+ EXPECT_GT(1.0, results[i]);
+ }
+
+ EXPECT_EQ(GenerateSHA1Entropy("yo", "1"),
+ GenerateSHA1Entropy("yo", "1"));
+ EXPECT_NE(GenerateSHA1Entropy("yo", "something"),
+ GenerateSHA1Entropy("yo", "else"));
+}
+
+TEST(EntropyProviderTest, PermutedEntropy) {
+ const double results[] = {
+ GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
+ GeneratePermutedEntropy(4321, kMaxLowEntropySize, "1") };
+
+ EXPECT_NE(results[0], results[1]);
+ for (size_t i = 0; i < arraysize(results); ++i) {
+ EXPECT_LE(0.0, results[i]);
+ EXPECT_GT(1.0, results[i]);
+ }
+
+ EXPECT_EQ(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"),
+ GeneratePermutedEntropy(1234, kMaxLowEntropySize, "1"));
+ EXPECT_NE(GeneratePermutedEntropy(1234, kMaxLowEntropySize, "something"),
+ GeneratePermutedEntropy(1234, kMaxLowEntropySize, "else"));
+}
+
+TEST(EntropyProviderTest, PermutedEntropyProviderResults) {
+ // Verifies that PermutedEntropyProvider produces expected results. This
+ // ensures that the results are the same between platforms and ensures that
+ // changes to the implementation do not regress this accidentally.
+
+ EXPECT_DOUBLE_EQ(2194 / static_cast<double>(kMaxLowEntropySize),
+ GeneratePermutedEntropy(1234, kMaxLowEntropySize, "XYZ"));
+ EXPECT_DOUBLE_EQ(5676 / static_cast<double>(kMaxLowEntropySize),
+ GeneratePermutedEntropy(1, kMaxLowEntropySize, "Test"));
+ EXPECT_DOUBLE_EQ(1151 / static_cast<double>(kMaxLowEntropySize),
+ GeneratePermutedEntropy(5000, kMaxLowEntropySize, "Foo"));
+}
+
+TEST(EntropyProviderTest, SHA1EntropyIsUniform) {
+ for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
+ SHA1EntropyGenerator entropy_generator(kTestTrialNames[i]);
+ PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
+ }
+}
+
+TEST(EntropyProviderTest, PermutedEntropyIsUniform) {
+ for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
+ PermutedEntropyGenerator entropy_generator(kTestTrialNames[i]);
+ PerformEntropyUniformityTest(kTestTrialNames[i], entropy_generator);
+ }
+}
+
+TEST(EntropyProviderTest, SeededRandGeneratorIsUniform) {
+ // Verifies that SeededRandGenerator has a uniform distribution.
+ //
+ // Mirrors RandUtilTest.RandGeneratorIsUniform in base/rand_util_unittest.cc.
+
+ const uint32 kTopOfRange = (std::numeric_limits<uint32>::max() / 4ULL) * 3ULL;
+ const uint32 kExpectedAverage = kTopOfRange / 2ULL;
+ const uint32 kAllowedVariance = kExpectedAverage / 50ULL; // +/- 2%
+ const int kMinAttempts = 1000;
+ const int kMaxAttempts = 1000000;
+
+ for (size_t i = 0; i < arraysize(kTestTrialNames); ++i) {
+ const uint32 seed = HashName(kTestTrialNames[i]);
+ internal::SeededRandGenerator rand_generator(seed);
+
+ double cumulative_average = 0.0;
+ int count = 0;
+ while (count < kMaxAttempts) {
+ uint32 value = rand_generator(kTopOfRange);
+ cumulative_average = (count * cumulative_average + value) / (count + 1);
+
+ // Don't quit too quickly for things to start converging, or we may have
+ // a false positive.
+ if (count > kMinAttempts &&
+ kExpectedAverage - kAllowedVariance < cumulative_average &&
+ cumulative_average < kExpectedAverage + kAllowedVariance) {
+ break;
+ }
+
+ ++count;
+ }
+
+ ASSERT_LT(count, kMaxAttempts) << "Expected average was " <<
+ kExpectedAverage << ", average ended at " << cumulative_average <<
+ ", for trial " << kTestTrialNames[i];
+ }
+}
+
+} // namespace metrics
diff --git a/chromium/components/variations/metrics_util.cc b/chromium/components/variations/metrics_util.cc
new file mode 100644
index 00000000000..031c3d37a31
--- /dev/null
+++ b/chromium/components/variations/metrics_util.cc
@@ -0,0 +1,27 @@
+// 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/variations/metrics_util.h"
+
+#include "base/sha1.h"
+#include "base/sys_byteorder.h"
+
+namespace metrics {
+
+uint32 HashName(const std::string& name) {
+ // SHA-1 is designed to produce a uniformly random spread in its output space,
+ // even for nearly-identical inputs.
+ unsigned char sha1_hash[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()),
+ name.size(),
+ sha1_hash);
+
+ uint32 bits;
+ COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data);
+ memcpy(&bits, sha1_hash, sizeof(bits));
+
+ return base::ByteSwapToLE32(bits);
+}
+
+} // namespace metrics
diff --git a/chromium/components/variations/metrics_util.h b/chromium/components/variations/metrics_util.h
new file mode 100644
index 00000000000..b331d4eaf1f
--- /dev/null
+++ b/chromium/components/variations/metrics_util.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_VARIATIONS_METRICS_UTIL_H_
+#define COMPONENTS_VARIATIONS_METRICS_UTIL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace metrics {
+
+// Computes a uint32 hash of a given string based on its SHA1 hash. Suitable for
+// uniquely identifying field trial names and group names.
+uint32 HashName(const std::string& name);
+
+} // namespace metrics
+
+#endif // COMPONENTS_VARIATIONS_METRICS_UTIL_H_
diff --git a/chromium/components/variations/metrics_util_unittest.cc b/chromium/components/variations/metrics_util_unittest.cc
new file mode 100644
index 00000000000..4f324947176
--- /dev/null
+++ b/chromium/components/variations/metrics_util_unittest.cc
@@ -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.
+
+#include "components/variations/metrics_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+
+TEST(MetricsUtilTest, HashName) {
+ // Checks that hashing is stable on all platforms.
+ struct {
+ const char* name;
+ uint32 hash_value;
+ } known_hashes[] = {
+ {"a", 937752454u},
+ {"1", 723085877u},
+ {"Trial Name", 2713117220u},
+ {"Group Name", 3201815843u},
+ {"My Favorite Experiment", 3722155194u},
+ {"My Awesome Group Name", 4109503236u},
+ {"abcdefghijklmonpqrstuvwxyz", 787728696u},
+ {"0123456789ABCDEF", 348858318U}
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(known_hashes); ++i)
+ EXPECT_EQ(known_hashes[i].hash_value, HashName(known_hashes[i].name));
+}
+
+} // namespace metrics
diff --git a/chromium/components/variations/proto/study.proto b/chromium/components/variations/proto/study.proto
new file mode 100644
index 00000000000..fdaa41f1ae2
--- /dev/null
+++ b/chromium/components/variations/proto/study.proto
@@ -0,0 +1,162 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package chrome_variations;
+
+// This defines the Protocol Buffer representation of a Chrome Variations study
+// as sent to clients of the Variations server.
+//
+// Next tag: 12
+message Study {
+ // The name of the study. Should not contain spaces or special characters.
+ // Ex: "my_study"
+ required string name = 1;
+
+ // The expiry date of the study in Unix time format. (Seconds since midnight
+ // January 1, 1970 UTC). See: http://en.wikipedia.org/wiki/Unix_time
+ //
+ // A study that has expired will be disabled, which will take precedence over
+ // a corresponding hardcoded field trial in the client.
+ //
+ // Ex: 1330893974 (corresponds to 2012-03-04 20:46:14Z)
+ optional int64 expiry_date = 3;
+
+ // Consistency setting for a study.
+ enum Consistency {
+ SESSION = 0; // Can't change within a session.
+ PERMANENT = 1; // Can't change for a given user.
+ }
+
+ // Consistency setting for this study. Optional - defaults to SESSION.
+ // Ex: PERMANENT
+ optional Consistency consistency = 7 [default = SESSION];
+
+ // Name of the experiment that gets the default experience. This experiment
+ // must be included in the list below.
+ // Ex: "default"
+ optional string default_experiment_name = 8;
+
+ // An experiment within the study.
+ //
+ // Next tag: 7
+ message Experiment {
+ // A named parameter value for this experiment.
+ //
+ // Next tag: 3
+ message Param {
+ // The name of the parameter.
+ optional string name = 1;
+
+ // The value of the parameter.
+ optional string value = 2;
+ }
+
+ // The name of the experiment within the study.
+ // Ex: "bucketA"
+ required string name = 1;
+
+ // The cut of the total probability taken for this experiment (the x in
+ // x / N, where N is the sum of all x’s). Ex: "50"
+ required uint32 probability_weight = 2;
+
+ // Optional id used to uniquely identify this experiment for Google web
+ // properties.
+ optional uint64 google_web_experiment_id = 3;
+
+ // Optional id used to uniquely identify this experiment for Google Update.
+ optional uint64 google_update_experiment_id = 4;
+
+ // Optional name of a Chrome flag that, when present, causes this experiment
+ // to be forced. If the forcing_flag field is set, users will not be
+ // assigned to this experiment unless that flag is present in Chrome's
+ // command line.
+ optional string forcing_flag = 5;
+
+ // Parameter values for this experiment.
+ repeated Param param = 6;
+ }
+
+ // List of experiments in this study. This list should include the default /
+ // control experiment.
+ //
+ // For example, to specify that 99% of users get the default behavior, while
+ // 0.5% of users get experience "A" and 0.5% of users get experience "B",
+ // specify the values below.
+ // Ex: { "default": 990, "A": 5, "B": 5 }
+ repeated Experiment experiment = 9;
+
+ // Possible Chrome release channels.
+ // See: http://dev.chromium.org/getting-involved/dev-channel
+ enum Channel {
+ // UNKNOWN value is defined here for the benefit of code using this enum
+ // type, but is not actually meant to be encoded in the protobuf.
+ UNKNOWN = -1;
+ CANARY = 0;
+ DEV = 1;
+ BETA = 2;
+ STABLE = 3;
+ }
+
+ // Possible Chrome operating system platforms.
+ enum Platform {
+ PLATFORM_WINDOWS = 0;
+ PLATFORM_MAC = 1;
+ PLATFORM_LINUX = 2;
+ PLATFORM_CHROMEOS = 3;
+ PLATFORM_ANDROID = 4;
+ PLATFORM_IOS = 5;
+ }
+
+ // Filtering criteria specifying whether this study is applicable to a given
+ // Chrome instance.
+ //
+ // Next tag: 7
+ message Filter {
+ // The start date of the study in Unix time format. (Seconds since midnight
+ // January 1, 1970 UTC). See: http://en.wikipedia.org/wiki/Unix_time
+ // Ex: 1330893974 (corresponds to 2012-03-04 20:46:14Z)
+ optional int64 start_date = 1;
+
+ // The minimum Chrome version for this study, allowing a trailing '*'
+ // character for pattern matching. Inclusive. (To check for a match, iterate
+ // over each component checking >= until a * or end of string is reached.)
+ // Optional - if not specified, there is no minimum version.
+ // Ex: "17.0.963.46", "17.0.963.*", "17.*"
+ optional string min_version = 2;
+
+ // The maximum Chrome version for this study; same formatting as
+ // |min_version| above. Inclusive. (To check for a match, iterate over each
+ // component checking <= until a * or end of string is reached.)
+ // Optional - if not specified, there is no maximum version.
+ // Ex: "19.*"
+ optional string max_version = 3;
+
+ // List of channels that will receive this study. If omitted, the study
+ // applies to all channels.
+ // Ex: [BETA, STABLE]
+ repeated Channel channel = 4;
+
+ // List of platforms that will receive this study. If omitted, the study
+ // applies to all platforms.
+ // Ex: [PLATFORM_WINDOWS, PLATFORM_MAC]
+ repeated Platform platform = 5;
+
+ // List of locales that will receive this study. If omitted, the study
+ // applies to all locales.
+ // Ex: ["en-US", "en-CA"]
+ repeated string locale = 6;
+ }
+
+ // Filtering criteria for this study. A study that is filtered out for a given
+ // client is equivalent to that study not being sent at all.
+ optional Filter filter = 10;
+
+ // Randomization seed to be used when |consistency| is set to PERMANENT. If
+ // not specified, randomization will be done using the trial name.
+ optional uint32 randomization_seed = 11;
+}
diff --git a/chromium/components/variations/proto/variations_seed.proto b/chromium/components/variations/proto/variations_seed.proto
new file mode 100644
index 00000000000..4d3ae35dd26
--- /dev/null
+++ b/chromium/components/variations/proto/variations_seed.proto
@@ -0,0 +1,22 @@
+// 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package chrome_variations;
+
+import "study.proto";
+
+// The VariationsSeed is a protobuf response from the server that contains the
+// list of studies and a serial number to uniquely identify its contents. The
+// serial number allows the client to easily determine if the list of
+// experiments has changed from the previous VariationsSeed seen by the client.
+//
+// Next tag: 3
+message VariationsSeed {
+ optional string serial_number = 1;
+ repeated Study study = 2;
+}
diff --git a/chromium/components/variations/variations_associated_data.cc b/chromium/components/variations/variations_associated_data.cc
new file mode 100644
index 00000000000..64a6d6e49a8
--- /dev/null
+++ b/chromium/components/variations/variations_associated_data.cc
@@ -0,0 +1,236 @@
+// Copyright 2013 The Chromium Authors. 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/variations/variations_associated_data.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/memory/singleton.h"
+#include "components/variations/metrics_util.h"
+
+namespace chrome_variations {
+
+namespace {
+
+// The internal singleton accessor for the map, used to keep it thread-safe.
+class GroupMapAccessor {
+ public:
+ typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare>
+ GroupToIDMap;
+
+ // Retrieve the singleton.
+ static GroupMapAccessor* GetInstance() {
+ return Singleton<GroupMapAccessor>::get();
+ }
+
+ // Note that this normally only sets the ID for a group the first time, unless
+ // |force| is set to true, in which case it will always override it.
+ void AssociateID(IDCollectionKey key,
+ const ActiveGroupId& group_identifier,
+ const VariationID id,
+ const bool force) {
+#if !defined(NDEBUG)
+ // Validate that all collections with this |group_identifier| have the same
+ // associated ID.
+ DCHECK_EQ(2, ID_COLLECTION_COUNT);
+ IDCollectionKey other_key = GOOGLE_WEB_PROPERTIES;
+ if (key == GOOGLE_WEB_PROPERTIES)
+ other_key = GOOGLE_UPDATE_SERVICE;
+ VariationID other_id = GetID(other_key, group_identifier);
+ DCHECK(other_id == EMPTY_ID || other_id == id);
+#endif
+
+ base::AutoLock scoped_lock(lock_);
+
+ GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
+ if (force ||
+ group_to_id_map->find(group_identifier) == group_to_id_map->end())
+ (*group_to_id_map)[group_identifier] = id;
+ }
+
+ VariationID GetID(IDCollectionKey key,
+ const ActiveGroupId& group_identifier) {
+ base::AutoLock scoped_lock(lock_);
+ GroupToIDMap* group_to_id_map = GetGroupToIDMap(key);
+ GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier);
+ if (it == group_to_id_map->end())
+ return EMPTY_ID;
+ return it->second;
+ }
+
+ void ClearAllMapsForTesting() {
+ base::AutoLock scoped_lock(lock_);
+
+ for (int i = 0; i < ID_COLLECTION_COUNT; ++i) {
+ GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i));
+ DCHECK(map);
+ map->clear();
+ }
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<GroupMapAccessor>;
+
+ // Retrieves the GroupToIDMap for |key|.
+ GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) {
+ return &group_to_id_maps_[key];
+ }
+
+ GroupMapAccessor() {
+ group_to_id_maps_.resize(ID_COLLECTION_COUNT);
+ }
+ ~GroupMapAccessor() {}
+
+ base::Lock lock_;
+ std::vector<GroupToIDMap> group_to_id_maps_;
+
+ DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor);
+};
+
+// Singleton helper class that keeps track of the parameters of all variations
+// and ensures access to these is thread-safe.
+class VariationsParamAssociator {
+ public:
+ typedef std::pair<std::string, std::string> VariationKey;
+ typedef std::map<std::string, std::string> VariationParams;
+
+ // Retrieve the singleton.
+ static VariationsParamAssociator* GetInstance() {
+ return Singleton<VariationsParamAssociator>::get();
+ }
+
+ bool AssociateVariationParams(const std::string& trial_name,
+ const std::string& group_name,
+ const VariationParams& params) {
+ base::AutoLock scoped_lock(lock_);
+
+ if (IsFieldTrialActive(trial_name))
+ return false;
+
+ const VariationKey key(trial_name, group_name);
+ if (ContainsKey(variation_params_, key))
+ return false;
+
+ variation_params_[key] = params;
+ return true;
+ }
+
+ bool GetVariationParams(const std::string& trial_name,
+ VariationParams* params) {
+ base::AutoLock scoped_lock(lock_);
+
+ const std::string group_name =
+ base::FieldTrialList::FindFullName(trial_name);
+ const VariationKey key(trial_name, group_name);
+ if (!ContainsKey(variation_params_, key))
+ return false;
+
+ *params = variation_params_[key];
+ return true;
+ }
+
+ void ClearAllParamsForTesting() {
+ base::AutoLock scoped_lock(lock_);
+ variation_params_.clear();
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<VariationsParamAssociator>;
+
+ VariationsParamAssociator() {}
+ ~VariationsParamAssociator() {}
+
+ // Tests whether a field trial is active (i.e. group() has been called on it).
+ // TODO(asvitkine): Expose this as an API on base::FieldTrial.
+ bool IsFieldTrialActive(const std::string& trial_name) {
+ base::FieldTrial::ActiveGroups active_groups;
+ base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ for (size_t i = 0; i < active_groups.size(); ++i) {
+ if (active_groups[i].trial_name == trial_name)
+ return true;
+ }
+ return false;
+ }
+
+ base::Lock lock_;
+ std::map<VariationKey, VariationParams> variation_params_;
+
+ DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator);
+};
+
+} // namespace
+
+ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
+ const std::string& group_name) {
+ ActiveGroupId id;
+ id.name = metrics::HashName(trial_name);
+ id.group = metrics::HashName(group_name);
+ return id;
+}
+
+void AssociateGoogleVariationID(IDCollectionKey key,
+ const std::string& trial_name,
+ const std::string& group_name,
+ VariationID id) {
+ GroupMapAccessor::GetInstance()->AssociateID(
+ key, MakeActiveGroupId(trial_name, group_name), id, false);
+}
+
+void AssociateGoogleVariationIDForce(IDCollectionKey key,
+ const std::string& trial_name,
+ const std::string& group_name,
+ VariationID id) {
+ GroupMapAccessor::GetInstance()->AssociateID(
+ key, MakeActiveGroupId(trial_name, group_name), id, true);
+}
+
+VariationID GetGoogleVariationID(IDCollectionKey key,
+ const std::string& trial_name,
+ const std::string& group_name) {
+ return GroupMapAccessor::GetInstance()->GetID(
+ key, MakeActiveGroupId(trial_name, group_name));
+}
+
+bool AssociateVariationParams(
+ const std::string& trial_name,
+ const std::string& group_name,
+ const std::map<std::string, std::string>& params) {
+ return VariationsParamAssociator::GetInstance()->AssociateVariationParams(
+ trial_name, group_name, params);
+}
+
+bool GetVariationParams(const std::string& trial_name,
+ std::map<std::string, std::string>* params) {
+ return VariationsParamAssociator::GetInstance()->GetVariationParams(
+ trial_name, params);
+}
+
+std::string GetVariationParamValue(const std::string& trial_name,
+ const std::string& param_name) {
+ std::map<std::string, std::string> params;
+ if (GetVariationParams(trial_name, &params)) {
+ std::map<std::string, std::string>::iterator it = params.find(param_name);
+ if (it != params.end())
+ return it->second;
+ }
+ return std::string();
+}
+
+// Functions below are exposed for testing explicitly behind this namespace.
+// They simply wrap existing functions in this file.
+namespace testing {
+
+void ClearAllVariationIDs() {
+ GroupMapAccessor::GetInstance()->ClearAllMapsForTesting();
+}
+
+void ClearAllVariationParams() {
+ VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting();
+}
+
+} // namespace testing
+
+} // namespace chrome_variations
diff --git a/chromium/components/variations/variations_associated_data.h b/chromium/components/variations/variations_associated_data.h
new file mode 100644
index 00000000000..bfc4a8fd1f9
--- /dev/null
+++ b/chromium/components/variations/variations_associated_data.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.
+
+#ifndef COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
+
+#include <map>
+#include <string>
+
+#include "base/metrics/field_trial.h"
+
+// This file provides various helpers that extend the functionality around
+// base::FieldTrial.
+//
+// This includes several simple APIs to handle getting and setting additional
+// data related to Chrome variations, such as parameters and Google variation
+// IDs. These APIs are meant to extend the base::FieldTrial APIs to offer extra
+// functionality that is not offered by the simpler base::FieldTrial APIs.
+//
+// The AssociateGoogleVariationID and AssociateVariationParams functions are
+// generally meant to be called by the VariationsService based on server-side
+// variation configs, but may also be used for client-only field trials by
+// invoking them directly after appending all the groups to a FieldTrial.
+//
+// Experiment code can then use the getter APIs to retrieve variation parameters
+// or IDs:
+//
+// std::map<std::string, std::string> params;
+// if (GetVariationParams("trial", &params)) {
+// // use |params|
+// }
+//
+// std::string value = GetVariationParamValue("trial", "param_x");
+// // use |value|, which will be "" if it does not exist
+//
+// VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial",
+// "group1");
+// if (id != chrome_variations::kEmptyID) {
+// // use |id|
+// }
+
+namespace chrome_variations {
+
+typedef int VariationID;
+
+const VariationID EMPTY_ID = 0;
+
+// The Unique ID of a trial and its active group, where the name and group
+// identifiers are hashes of the trial and group name strings.
+struct ActiveGroupId {
+ uint32 name;
+ uint32 group;
+};
+
+// Returns an ActiveGroupId struct for the given trial and group names.
+ActiveGroupId MakeActiveGroupId(const std::string& trial_name,
+ const std::string& group_name);
+
+// We need to supply a Compare class for templates since ActiveGroupId is a
+// user-defined type.
+struct ActiveGroupIdCompare {
+ bool operator() (const ActiveGroupId& lhs, const ActiveGroupId& rhs) const {
+ // The group and name fields are just SHA-1 Hashes, so we just need to treat
+ // them as IDs and do a less-than comparison. We test group first, since
+ // name is more likely to collide.
+ if (lhs.group != rhs.group)
+ return lhs.group < rhs.group;
+ return lhs.name < rhs.name;
+ }
+};
+
+// A key into the Associate/Get methods for VariationIDs. This is used to create
+// separate ID associations for separate parties interested in VariationIDs.
+enum IDCollectionKey {
+ // This collection is used by Google web properties, transmitted through the
+ // X-Chrome-Variations header.
+ GOOGLE_WEB_PROPERTIES,
+ // This collection is used by Google update services, transmitted through the
+ // Google Update experiment labels.
+ GOOGLE_UPDATE_SERVICE,
+ // The total count of collections.
+ ID_COLLECTION_COUNT,
+};
+
+// Associate a chrome_variations::VariationID value with a FieldTrial group for
+// collection |key|. If an id was previously set for |trial_name| and
+// |group_name|, this does nothing. The group is denoted by |trial_name| and
+// |group_name|. This must be called whenever a FieldTrial is prepared (create
+// the trial and append groups) and needs to have a
+// chrome_variations::VariationID associated with it so Google servers can
+// recognize the FieldTrial. Thread safe.
+void AssociateGoogleVariationID(IDCollectionKey key,
+ const std::string& trial_name,
+ const std::string& group_name,
+ VariationID id);
+
+// As above, but overwrites any previously set id. Thread safe.
+void AssociateGoogleVariationIDForce(IDCollectionKey key,
+ const std::string& trial_name,
+ const std::string& group_name,
+ VariationID id);
+
+// Retrieve the chrome_variations::VariationID associated with a FieldTrial
+// group for collection |key|. The group is denoted by |trial_name| and
+// |group_name|. This will return chrome_variations::kEmptyID if there is
+// currently no associated ID for the named group. This API can be nicely
+// combined with FieldTrial::GetActiveFieldTrialGroups() to enumerate the
+// variation IDs for all active FieldTrial groups. Thread safe.
+VariationID GetGoogleVariationID(IDCollectionKey key,
+ const std::string& trial_name,
+ const std::string& group_name);
+
+// Associates the specified set of key-value |params| with the variation
+// specified by |trial_name| and |group_name|. Fails and returns false if the
+// specified variation already has params associated with it or the field trial
+// is already active (group() has been called on it). Thread safe.
+bool AssociateVariationParams(const std::string& trial_name,
+ const std::string& group_name,
+ const std::map<std::string, std::string>& params);
+
+// Retrieves the set of key-value |params| for the variation associated with
+// the specified field trial, based on its selected group. If the field trial
+// does not exist or its selected group does not have any parameters associated
+// with it, returns false and does not modify |params|. Calling this function
+// will result in the field trial being marked as active if found (i.e. group()
+// will be called on it), if it wasn't already. Currently, this information is
+// only available from the browser process. Thread safe.
+bool GetVariationParams(const std::string& trial_name,
+ std::map<std::string, std::string>* params);
+
+// Retrieves a specific parameter value corresponding to |param_name| for the
+// variation associated with the specified field trial, based on its selected
+// group. If the field trial does not exist or the specified parameter does not
+// exist, returns an empty string. Calling this function will result in the
+// field trial being marked as active if found (i.e. group() will be called on
+// it), if it wasn't already. Currently, this information is only available from
+// the browser process. Thread safe.
+std::string GetVariationParamValue(const std::string& trial_name,
+ const std::string& param_name);
+
+// Expose some functions for testing.
+namespace testing {
+
+// Clears all of the mapped associations.
+void ClearAllVariationIDs();
+
+// Clears all of the associated params.
+void ClearAllVariationParams();
+
+} // namespace testing
+
+} // namespace chrome_variations
+
+#endif // COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_
diff --git a/chromium/components/variations/variations_associated_data_unittest.cc b/chromium/components/variations/variations_associated_data_unittest.cc
new file mode 100644
index 00000000000..d9d4b5d2faa
--- /dev/null
+++ b/chromium/components/variations/variations_associated_data_unittest.cc
@@ -0,0 +1,310 @@
+// Copyright 2013 The Chromium Authors. 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/variations/variations_associated_data.h"
+
+#include "base/metrics/field_trial.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_variations {
+
+namespace {
+
+const VariationID TEST_VALUE_A = 3300200;
+const VariationID TEST_VALUE_B = 3300201;
+
+// Convenience helper to retrieve the chrome_variations::VariationID for a
+// FieldTrial. Note that this will do the group assignment in |trial| if not
+// already done.
+VariationID GetIDForTrial(IDCollectionKey key, base::FieldTrial* trial) {
+ return GetGoogleVariationID(key, trial->trial_name(), trial->group_name());
+}
+
+// Tests whether a field trial is active (i.e. group() has been called on it).
+bool IsFieldTrialActive(const std::string& trial_name) {
+ base::FieldTrial::ActiveGroups active_groups;
+ base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ for (size_t i = 0; i < active_groups.size(); ++i) {
+ if (active_groups[i].trial_name == trial_name)
+ return true;
+ }
+ return false;
+}
+
+// Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date.
+scoped_refptr<base::FieldTrial> CreateFieldTrial(
+ const std::string& trial_name,
+ int total_probability,
+ const std::string& default_group_name,
+ int* default_group_number) {
+ return base::FieldTrialList::FactoryGetFieldTrial(
+ trial_name, total_probability, default_group_name,
+ base::FieldTrialList::kNoExpirationYear, 1, 1,
+ base::FieldTrial::SESSION_RANDOMIZED, default_group_number);
+}
+
+} // namespace
+
+class VariationsAssociatedDataTest : public ::testing::Test {
+ public:
+ VariationsAssociatedDataTest() : field_trial_list_(NULL) {
+ }
+
+ virtual ~VariationsAssociatedDataTest() {
+ // Ensure that the maps are cleared between tests, since they are stored as
+ // process singletons.
+ testing::ClearAllVariationIDs();
+ }
+
+ private:
+ base::FieldTrialList field_trial_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(VariationsAssociatedDataTest);
+};
+
+// Test that if the trial is immediately disabled, GetGoogleVariationID just
+// returns the empty ID.
+TEST_F(VariationsAssociatedDataTest, DisableImmediately) {
+ int default_group_number = -1;
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial("trial", 100, "default", &default_group_number));
+
+ ASSERT_EQ(default_group_number, trial->group());
+ ASSERT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get()));
+}
+
+// Test that successfully associating the FieldTrial with some ID, and then
+// disabling the FieldTrial actually makes GetGoogleVariationID correctly
+// return the empty ID.
+TEST_F(VariationsAssociatedDataTest, DisableAfterInitialization) {
+ const std::string default_name = "default";
+ const std::string non_default_name = "non_default";
+
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial("trial", 100, default_name, NULL));
+
+ trial->AppendGroup(non_default_name, 100);
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(),
+ default_name, TEST_VALUE_A);
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial->trial_name(),
+ non_default_name, TEST_VALUE_B);
+ trial->Disable();
+ ASSERT_EQ(default_name, trial->group_name());
+ ASSERT_EQ(TEST_VALUE_A, GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial.get()));
+}
+
+// Test various successful association cases.
+TEST_F(VariationsAssociatedDataTest, AssociateGoogleVariationID) {
+ const std::string default_name1 = "default";
+ scoped_refptr<base::FieldTrial> trial_true(
+ CreateFieldTrial("d1", 10, default_name1, NULL));
+ const std::string winner = "TheWinner";
+ int winner_group = trial_true->AppendGroup(winner, 10);
+
+ // Set GoogleVariationIDs so we can verify that they were chosen correctly.
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
+ default_name1, TEST_VALUE_A);
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
+ winner, TEST_VALUE_B);
+
+ EXPECT_EQ(winner_group, trial_true->group());
+ EXPECT_EQ(winner, trial_true->group_name());
+ EXPECT_EQ(TEST_VALUE_B,
+ GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+
+ const std::string default_name2 = "default2";
+ scoped_refptr<base::FieldTrial> trial_false(
+ CreateFieldTrial("d2", 10, default_name2, NULL));
+ const std::string loser = "ALoser";
+ const int loser_group = trial_false->AppendGroup(loser, 0);
+
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(),
+ default_name2, TEST_VALUE_A);
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_false->trial_name(),
+ loser, TEST_VALUE_B);
+
+ EXPECT_NE(loser_group, trial_false->group());
+ EXPECT_EQ(TEST_VALUE_A,
+ GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_false.get()));
+}
+
+// Test that not associating a FieldTrial with any IDs ensure that the empty ID
+// will be returned.
+TEST_F(VariationsAssociatedDataTest, NoAssociation) {
+ const std::string default_name = "default";
+ scoped_refptr<base::FieldTrial> no_id_trial(
+ CreateFieldTrial("d3", 10, default_name, NULL));
+
+ const std::string winner = "TheWinner";
+ const int winner_group = no_id_trial->AppendGroup(winner, 10);
+
+ // Ensure that despite the fact that a normal winner is elected, it does not
+ // have a valid VariationID associated with it.
+ EXPECT_EQ(winner_group, no_id_trial->group());
+ EXPECT_EQ(winner, no_id_trial->group_name());
+ EXPECT_EQ(EMPTY_ID, GetIDForTrial(GOOGLE_WEB_PROPERTIES, no_id_trial.get()));
+}
+
+// Ensure that the AssociateGoogleVariationIDForce works as expected.
+TEST_F(VariationsAssociatedDataTest, ForceAssociation) {
+ EXPECT_EQ(EMPTY_ID,
+ GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group",
+ TEST_VALUE_A);
+ EXPECT_EQ(TEST_VALUE_A,
+ GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group",
+ TEST_VALUE_B);
+ EXPECT_EQ(TEST_VALUE_A,
+ GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+ AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES, "trial", "group",
+ TEST_VALUE_B);
+ EXPECT_EQ(TEST_VALUE_B,
+ GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, "trial", "group"));
+}
+
+// Ensure that two collections can coexist without affecting each other.
+TEST_F(VariationsAssociatedDataTest, CollectionsCoexist) {
+ const std::string default_name = "default";
+ int default_group_number = -1;
+ scoped_refptr<base::FieldTrial> trial_true(
+ CreateFieldTrial("d1", 10, default_name, &default_group_number));
+ ASSERT_EQ(default_group_number, trial_true->group());
+ ASSERT_EQ(default_name, trial_true->group_name());
+
+ EXPECT_EQ(EMPTY_ID,
+ GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+ EXPECT_EQ(EMPTY_ID,
+ GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
+
+ AssociateGoogleVariationID(GOOGLE_WEB_PROPERTIES, trial_true->trial_name(),
+ default_name, TEST_VALUE_A);
+ EXPECT_EQ(TEST_VALUE_A,
+ GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+ EXPECT_EQ(EMPTY_ID,
+ GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
+
+ AssociateGoogleVariationID(GOOGLE_UPDATE_SERVICE, trial_true->trial_name(),
+ default_name, TEST_VALUE_A);
+ EXPECT_EQ(TEST_VALUE_A,
+ GetIDForTrial(GOOGLE_WEB_PROPERTIES, trial_true.get()));
+ EXPECT_EQ(TEST_VALUE_A,
+ GetIDForTrial(GOOGLE_UPDATE_SERVICE, trial_true.get()));
+}
+
+TEST_F(VariationsAssociatedDataTest, AssociateVariationParams) {
+ const std::string kTrialName = "AssociateVariationParams";
+
+ {
+ std::map<std::string, std::string> params;
+ params["a"] = "10";
+ params["b"] = "test";
+ ASSERT_TRUE(AssociateVariationParams(kTrialName, "A", params));
+ }
+ {
+ std::map<std::string, std::string> params;
+ params["a"] = "5";
+ ASSERT_TRUE(AssociateVariationParams(kTrialName, "B", params));
+ }
+
+ base::FieldTrialList::CreateFieldTrial(kTrialName, "B");
+ EXPECT_EQ("5", GetVariationParamValue(kTrialName, "a"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+
+ std::map<std::string, std::string> params;
+ EXPECT_TRUE(GetVariationParams(kTrialName, &params));
+ EXPECT_EQ(1U, params.size());
+ EXPECT_EQ("5", params["a"]);
+}
+
+TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_Fail) {
+ const std::string kTrialName = "AssociateVariationParams_Fail";
+ const std::string kGroupName = "A";
+
+ std::map<std::string, std::string> params;
+ params["a"] = "10";
+ ASSERT_TRUE(AssociateVariationParams(kTrialName, kGroupName, params));
+ params["a"] = "1";
+ params["b"] = "2";
+ ASSERT_FALSE(AssociateVariationParams(kTrialName, kGroupName, params));
+
+ base::FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
+ EXPECT_EQ("10", GetVariationParamValue(kTrialName, "a"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "b"));
+}
+
+TEST_F(VariationsAssociatedDataTest, AssociateVariationParams_TrialActiveFail) {
+ const std::string kTrialName = "AssociateVariationParams_TrialActiveFail";
+ base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
+ ASSERT_EQ("A", base::FieldTrialList::FindFullName(kTrialName));
+
+ std::map<std::string, std::string> params;
+ params["a"] = "10";
+ EXPECT_FALSE(AssociateVariationParams(kTrialName, "B", params));
+ EXPECT_FALSE(AssociateVariationParams(kTrialName, "A", params));
+}
+
+TEST_F(VariationsAssociatedDataTest,
+ AssociateVariationParams_DoesntActivateTrial) {
+ const std::string kTrialName = "AssociateVariationParams_DoesntActivateTrial";
+
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial(kTrialName, 100, "A", NULL));
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+
+ std::map<std::string, std::string> params;
+ params["a"] = "10";
+ EXPECT_TRUE(AssociateVariationParams(kTrialName, "A", params));
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoTrial) {
+ const std::string kTrialName = "GetVariationParams_NoParams";
+
+ std::map<std::string, std::string> params;
+ EXPECT_FALSE(GetVariationParams(kTrialName, &params));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y"));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParams_NoParams) {
+ const std::string kTrialName = "GetVariationParams_NoParams";
+
+ base::FieldTrialList::CreateFieldTrial(kTrialName, "A");
+
+ std::map<std::string, std::string> params;
+ EXPECT_FALSE(GetVariationParams(kTrialName, &params));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "y"));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParams_ActivatesTrial) {
+ const std::string kTrialName = "GetVariationParams_ActivatesTrial";
+
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial(kTrialName, 100, "A", NULL));
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+
+ std::map<std::string, std::string> params;
+ EXPECT_FALSE(GetVariationParams(kTrialName, &params));
+ ASSERT_TRUE(IsFieldTrialActive(kTrialName));
+}
+
+TEST_F(VariationsAssociatedDataTest, GetVariationParamValue_ActivatesTrial) {
+ const std::string kTrialName = "GetVariationParamValue_ActivatesTrial";
+
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+ scoped_refptr<base::FieldTrial> trial(
+ CreateFieldTrial(kTrialName, 100, "A", NULL));
+ ASSERT_FALSE(IsFieldTrialActive(kTrialName));
+
+ std::map<std::string, std::string> params;
+ EXPECT_EQ(std::string(), GetVariationParamValue(kTrialName, "x"));
+ ASSERT_TRUE(IsFieldTrialActive(kTrialName));
+}
+
+} // namespace chrome_variations
diff --git a/chromium/components/variations/variations_seed_processor.cc b/chromium/components/variations/variations_seed_processor.cc
new file mode 100644
index 00000000000..d5d4e55399b
--- /dev/null
+++ b/chromium/components/variations/variations_seed_processor.cc
@@ -0,0 +1,342 @@
+// Copyright 2013 The Chromium Authors. 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/variations/variations_seed_processor.h"
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "base/stl_util.h"
+#include "base/version.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace chrome_variations {
+
+namespace {
+
+Study_Platform GetCurrentPlatform() {
+#if defined(OS_WIN)
+ return Study_Platform_PLATFORM_WINDOWS;
+#elif defined(OS_IOS)
+ return Study_Platform_PLATFORM_IOS;
+#elif defined(OS_MACOSX)
+ return Study_Platform_PLATFORM_MAC;
+#elif defined(OS_CHROMEOS)
+ return Study_Platform_PLATFORM_CHROMEOS;
+#elif defined(OS_ANDROID)
+ return Study_Platform_PLATFORM_ANDROID;
+#elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
+ // Default BSD and SOLARIS to Linux to not break those builds, although these
+ // platforms are not officially supported by Chrome.
+ return Study_Platform_PLATFORM_LINUX;
+#else
+#error Unknown platform
+#endif
+}
+
+// Converts |date_time| in Study date format to base::Time.
+base::Time ConvertStudyDateToBaseTime(int64 date_time) {
+ return base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(date_time);
+}
+
+} // namespace
+
+VariationsSeedProcessor::VariationsSeedProcessor() {
+}
+
+VariationsSeedProcessor::~VariationsSeedProcessor() {
+}
+
+void VariationsSeedProcessor::CreateTrialsFromSeed(
+ const VariationsSeed& seed,
+ const std::string& locale,
+ const base::Time& reference_date,
+ const base::Version& version,
+ Study_Channel channel) {
+ DCHECK(version.IsValid());
+
+ // Add expired studies (in a disabled state) only after all the non-expired
+ // studies have been added (and do not add an expired study if a corresponding
+ // non-expired study got added). This way, if there's both an expired and a
+ // non-expired study that applies, the non-expired study takes priority.
+ std::set<std::string> created_studies;
+ std::vector<const Study*> expired_studies;
+
+ for (int i = 0; i < seed.study_size(); ++i) {
+ const Study& study = seed.study(i);
+ if (!ShouldAddStudy(study, locale, reference_date, version, channel))
+ continue;
+
+ if (IsStudyExpired(study, reference_date)) {
+ expired_studies.push_back(&study);
+ } else {
+ CreateTrialFromStudy(study, false);
+ created_studies.insert(study.name());
+ }
+ }
+
+ for (size_t i = 0; i < expired_studies.size(); ++i) {
+ if (!ContainsKey(created_studies, expired_studies[i]->name()))
+ CreateTrialFromStudy(*expired_studies[i], true);
+ }
+}
+
+bool VariationsSeedProcessor::CheckStudyChannel(const Study_Filter& filter,
+ Study_Channel channel) {
+ // An empty channel list matches all channels.
+ if (filter.channel_size() == 0)
+ return true;
+
+ for (int i = 0; i < filter.channel_size(); ++i) {
+ if (filter.channel(i) == channel)
+ return true;
+ }
+ return false;
+}
+
+bool VariationsSeedProcessor::CheckStudyLocale(
+ const Study_Filter& filter,
+ const std::string& locale) {
+ // An empty locale list matches all locales.
+ if (filter.locale_size() == 0)
+ return true;
+
+ for (int i = 0; i < filter.locale_size(); ++i) {
+ if (filter.locale(i) == locale)
+ return true;
+ }
+ return false;
+}
+
+bool VariationsSeedProcessor::CheckStudyPlatform(
+ const Study_Filter& filter,
+ Study_Platform platform) {
+ // An empty platform list matches all platforms.
+ if (filter.platform_size() == 0)
+ return true;
+
+ for (int i = 0; i < filter.platform_size(); ++i) {
+ if (filter.platform(i) == platform)
+ return true;
+ }
+ return false;
+}
+
+bool VariationsSeedProcessor::CheckStudyStartDate(
+ const Study_Filter& filter,
+ const base::Time& date_time) {
+ if (filter.has_start_date()) {
+ const base::Time start_date =
+ ConvertStudyDateToBaseTime(filter.start_date());
+ return date_time >= start_date;
+ }
+
+ return true;
+}
+
+bool VariationsSeedProcessor::CheckStudyVersion(
+ const Study_Filter& filter,
+ const base::Version& version) {
+ if (filter.has_min_version()) {
+ if (version.CompareToWildcardString(filter.min_version()) < 0)
+ return false;
+ }
+
+ if (filter.has_max_version()) {
+ if (version.CompareToWildcardString(filter.max_version()) > 0)
+ return false;
+ }
+
+ return true;
+}
+
+void VariationsSeedProcessor::CreateTrialFromStudy(const Study& study,
+ bool is_expired) {
+ base::FieldTrial::Probability total_probability = 0;
+ if (!ValidateStudyAndComputeTotalProbability(study, &total_probability))
+ return;
+
+ // Check if any experiments need to be forced due to a command line
+ // flag. Force the first experiment with an existing flag.
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ for (int i = 0; i < study.experiment_size(); ++i) {
+ const Study_Experiment& experiment = study.experiment(i);
+ if (experiment.has_forcing_flag() &&
+ command_line->HasSwitch(experiment.forcing_flag())) {
+ base::FieldTrialList::CreateFieldTrial(study.name(), experiment.name());
+ DVLOG(1) << "Trial " << study.name() << " forced by flag: "
+ << experiment.forcing_flag();
+ return;
+ }
+ }
+
+ uint32 randomization_seed = 0;
+ base::FieldTrial::RandomizationType randomization_type =
+ base::FieldTrial::SESSION_RANDOMIZED;
+ if (study.has_consistency() &&
+ study.consistency() == Study_Consistency_PERMANENT) {
+ randomization_type = base::FieldTrial::ONE_TIME_RANDOMIZED;
+ if (study.has_randomization_seed())
+ randomization_seed = study.randomization_seed();
+ }
+
+ // The trial is created without specifying an expiration date because the
+ // expiration check in field_trial.cc is based on the build date. Instead,
+ // the expiration check using |reference_date| is done explicitly below.
+ scoped_refptr<base::FieldTrial> trial(
+ base::FieldTrialList::FactoryGetFieldTrialWithRandomizationSeed(
+ study.name(), total_probability, study.default_experiment_name(),
+ base::FieldTrialList::kNoExpirationYear, 1, 1, randomization_type,
+ randomization_seed, NULL));
+
+ for (int i = 0; i < study.experiment_size(); ++i) {
+ const Study_Experiment& experiment = study.experiment(i);
+
+ std::map<std::string, std::string> params;
+ for (int j = 0; j < experiment.param_size(); j++) {
+ if (experiment.param(j).has_name() && experiment.param(j).has_value())
+ params[experiment.param(j).name()] = experiment.param(j).value();
+ }
+ if (!params.empty())
+ AssociateVariationParams(study.name(), experiment.name(), params);
+
+ // Groups with flags can't be selected randomly, so we don't add them to
+ // the field trial.
+ if (experiment.has_forcing_flag())
+ continue;
+
+ if (experiment.name() != study.default_experiment_name())
+ trial->AppendGroup(experiment.name(), experiment.probability_weight());
+
+ if (experiment.has_google_web_experiment_id()) {
+ const VariationID variation_id =
+ static_cast<VariationID>(experiment.google_web_experiment_id());
+ AssociateGoogleVariationIDForce(GOOGLE_WEB_PROPERTIES,
+ study.name(),
+ experiment.name(),
+ variation_id);
+ }
+ if (experiment.has_google_update_experiment_id()) {
+ const VariationID variation_id =
+ static_cast<VariationID>(experiment.google_update_experiment_id());
+ AssociateGoogleVariationIDForce(GOOGLE_UPDATE_SERVICE,
+ study.name(),
+ experiment.name(),
+ variation_id);
+ }
+ }
+
+ trial->SetForced();
+ if (is_expired)
+ trial->Disable();
+}
+
+bool VariationsSeedProcessor::IsStudyExpired(const Study& study,
+ const base::Time& date_time) {
+ if (study.has_expiry_date()) {
+ const base::Time expiry_date =
+ ConvertStudyDateToBaseTime(study.expiry_date());
+ return date_time >= expiry_date;
+ }
+
+ return false;
+}
+
+bool VariationsSeedProcessor::ShouldAddStudy(
+ const Study& study,
+ const std::string& locale,
+ const base::Time& reference_date,
+ const base::Version& version,
+ Study_Channel channel) {
+ if (study.has_filter()) {
+ if (!CheckStudyChannel(study.filter(), channel)) {
+ DVLOG(1) << "Filtered out study " << study.name() << " due to channel.";
+ return false;
+ }
+
+ if (!CheckStudyLocale(study.filter(), locale)) {
+ DVLOG(1) << "Filtered out study " << study.name() << " due to locale.";
+ return false;
+ }
+
+ if (!CheckStudyPlatform(study.filter(), GetCurrentPlatform())) {
+ DVLOG(1) << "Filtered out study " << study.name() << " due to platform.";
+ return false;
+ }
+
+ if (!CheckStudyVersion(study.filter(), version)) {
+ DVLOG(1) << "Filtered out study " << study.name() << " due to version.";
+ return false;
+ }
+
+ if (!CheckStudyStartDate(study.filter(), reference_date)) {
+ DVLOG(1) << "Filtered out study " << study.name() <<
+ " due to start date.";
+ return false;
+ }
+ }
+
+ DVLOG(1) << "Kept study " << study.name() << ".";
+ return true;
+}
+
+bool VariationsSeedProcessor::ValidateStudyAndComputeTotalProbability(
+ const Study& study,
+ base::FieldTrial::Probability* total_probability) {
+ // At the moment, a missing default_experiment_name makes the study invalid.
+ if (study.default_experiment_name().empty()) {
+ DVLOG(1) << study.name() << " has no default experiment defined.";
+ return false;
+ }
+ if (study.filter().has_min_version() &&
+ !Version::IsValidWildcardString(study.filter().min_version())) {
+ DVLOG(1) << study.name() << " has invalid min version: "
+ << study.filter().min_version();
+ return false;
+ }
+ if (study.filter().has_max_version() &&
+ !Version::IsValidWildcardString(study.filter().max_version())) {
+ DVLOG(1) << study.name() << " has invalid max version: "
+ << study.filter().max_version();
+ return false;
+ }
+
+ const std::string& default_group_name = study.default_experiment_name();
+ base::FieldTrial::Probability divisor = 0;
+
+ bool found_default_group = false;
+ std::set<std::string> experiment_names;
+ for (int i = 0; i < study.experiment_size(); ++i) {
+ if (study.experiment(i).name().empty()) {
+ DVLOG(1) << study.name() << " is missing experiment " << i << " name";
+ return false;
+ }
+ if (!experiment_names.insert(study.experiment(i).name()).second) {
+ DVLOG(1) << study.name() << " has a repeated experiment name "
+ << study.experiment(i).name();
+ return false;
+ }
+
+ if (!study.experiment(i).has_forcing_flag())
+ divisor += study.experiment(i).probability_weight();
+ if (study.experiment(i).name() == default_group_name)
+ found_default_group = true;
+ }
+
+ if (!found_default_group) {
+ DVLOG(1) << study.name() << " is missing default experiment in its "
+ << "experiment list";
+ // The default group was not found in the list of groups. This study is not
+ // valid.
+ return false;
+ }
+
+ *total_probability = divisor;
+ return true;
+}
+
+} // namespace chrome_variations
diff --git a/chromium/components/variations/variations_seed_processor.h b/chromium/components/variations/variations_seed_processor.h
new file mode 100644
index 00000000000..cba26214af9
--- /dev/null
+++ b/chromium/components/variations/variations_seed_processor.h
@@ -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.
+
+#ifndef COMPONENTS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
+#define COMPONENTS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/time/time.h"
+#include "base/version.h"
+#include "components/variations/proto/study.pb.h"
+#include "components/variations/proto/variations_seed.pb.h"
+
+namespace chrome_variations {
+
+// Helper class to instantiate field trials from a variations seed.
+class VariationsSeedProcessor {
+ public:
+ VariationsSeedProcessor();
+ virtual ~VariationsSeedProcessor();
+
+ // Creates field trials from the specified variations |seed|, based on the
+ // specified configuration (locale, current date, version and channel).
+ void CreateTrialsFromSeed(const VariationsSeed& seed,
+ const std::string& locale,
+ const base::Time& reference_date,
+ const base::Version& version,
+ Study_Channel channel);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyChannel);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyLocale);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyPlatform);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyStartDate);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, CheckStudyVersion);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
+ CheckStudyVersionWildcards);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ForceGroupWithFlag1);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ForceGroupWithFlag2);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
+ ForceGroup_ChooseFirstGroupWithFlag);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest,
+ ForceGroup_DontChooseGroupWithFlag);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, IsStudyExpired);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, ValidateStudy);
+ FRIEND_TEST_ALL_PREFIXES(VariationsSeedProcessorTest, VariationParams);
+
+ // Checks whether a study is applicable for the given |channel| per |filter|.
+ bool CheckStudyChannel(const Study_Filter& filter, Study_Channel channel);
+
+ // Checks whether a study is applicable for the given |locale| per |filter|.
+ bool CheckStudyLocale(const Study_Filter& filter, const std::string& locale);
+
+ // Checks whether a study is applicable for the given |platform| per |filter|.
+ bool CheckStudyPlatform(const Study_Filter& filter, Study_Platform platform);
+
+ // Checks whether a study is applicable for the given date/time per |filter|.
+ bool CheckStudyStartDate(const Study_Filter& filter,
+ const base::Time& date_time);
+
+ // Checks whether a study is applicable for the given version per |filter|.
+ bool CheckStudyVersion(const Study_Filter& filter,
+ const base::Version& version);
+
+ // Creates and registers a field trial from the |study| data. Disables the
+ // trial if |is_expired| is true.
+ void CreateTrialFromStudy(const Study& study, bool is_expired);
+
+ // Checks whether |study| is expired using the given date/time.
+ bool IsStudyExpired(const Study& study, const base::Time& date_time);
+
+ // Returns whether |study| should be disabled according to its restriction
+ // parameters. Uses |version_info| for min / max version checks,
+ // |reference_date| for the start date check and |channel| for channel
+ // checks.
+ bool ShouldAddStudy(const Study& study,
+ const std::string& locale,
+ const base::Time& reference_date,
+ const base::Version& version,
+ Study_Channel channel);
+
+ // Validates the sanity of |study| and computes the total probability.
+ bool ValidateStudyAndComputeTotalProbability(
+ const Study& study,
+ base::FieldTrial::Probability* total_probability);
+
+ DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessor);
+};
+
+} // namespace chrome_variations
+
+#endif // COMPONENTS_VARIATIONS_VARIATIONS_SEED_PROCESSOR_H_
diff --git a/chromium/components/variations/variations_seed_processor_unittest.cc b/chromium/components/variations/variations_seed_processor_unittest.cc
new file mode 100644
index 00000000000..85692f9265d
--- /dev/null
+++ b/chromium/components/variations/variations_seed_processor_unittest.cc
@@ -0,0 +1,531 @@
+// Copyright 2013 The Chromium Authors. 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/variations/variations_seed_processor.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/strings/string_split.h"
+#include "components/variations/variations_associated_data.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_variations {
+
+namespace {
+
+// Converts |time| to Study proto format.
+int64 TimeToProtoTime(const base::Time& time) {
+ return (time - base::Time::UnixEpoch()).InSeconds();
+}
+
+// Constants for testing associating command line flags with trial groups.
+const char kFlagStudyName[] = "flag_test_trial";
+const char kFlagGroup1Name[] = "flag_group1";
+const char kFlagGroup2Name[] = "flag_group2";
+const char kNonFlagGroupName[] = "non_flag_group";
+const char kForcingFlag1[] = "flag_test1";
+const char kForcingFlag2[] = "flag_test2";
+
+// Adds an experiment to |study| with the specified |name| and |probability|.
+Study_Experiment* AddExperiment(const std::string& name, int probability,
+ Study* study) {
+ Study_Experiment* experiment = study->add_experiment();
+ experiment->set_name(name);
+ experiment->set_probability_weight(probability);
+ return experiment;
+}
+
+// Populates |study| with test data used for testing associating command line
+// flags with trials groups. The study will contain three groups, a default
+// group that isn't associated with a flag, and two other groups, both
+// associated with different flags.
+Study CreateStudyWithFlagGroups(int default_group_probability,
+ int flag_group1_probability,
+ int flag_group2_probability) {
+ DCHECK_GE(default_group_probability, 0);
+ DCHECK_GE(flag_group1_probability, 0);
+ DCHECK_GE(flag_group2_probability, 0);
+ Study study;
+ study.set_name(kFlagStudyName);
+ study.set_default_experiment_name(kNonFlagGroupName);
+
+ AddExperiment(kNonFlagGroupName, default_group_probability, &study);
+ AddExperiment(kFlagGroup1Name, flag_group1_probability, &study)
+ ->set_forcing_flag(kForcingFlag1);
+ AddExperiment(kFlagGroup2Name, flag_group2_probability, &study)
+ ->set_forcing_flag(kForcingFlag2);
+
+ return study;
+}
+
+} // namespace
+
+TEST(VariationsSeedProcessorTest, CheckStudyChannel) {
+ VariationsSeedProcessor seed_processor;
+
+ const Study_Channel channels[] = {
+ Study_Channel_CANARY,
+ Study_Channel_DEV,
+ Study_Channel_BETA,
+ Study_Channel_STABLE,
+ };
+ bool channel_added[arraysize(channels)] = { 0 };
+
+ Study_Filter filter;
+
+ // Check in the forwarded order. The loop cond is <= arraysize(channels)
+ // instead of < so that the result of adding the last channel gets checked.
+ for (size_t i = 0; i <= arraysize(channels); ++i) {
+ for (size_t j = 0; j < arraysize(channels); ++j) {
+ const bool expected = channel_added[j] || filter.channel_size() == 0;
+ const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
+ EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+ }
+
+ if (i < arraysize(channels)) {
+ filter.add_channel(channels[i]);
+ channel_added[i] = true;
+ }
+ }
+
+ // Do the same check in the reverse order.
+ filter.clear_channel();
+ memset(&channel_added, 0, sizeof(channel_added));
+ for (size_t i = 0; i <= arraysize(channels); ++i) {
+ for (size_t j = 0; j < arraysize(channels); ++j) {
+ const bool expected = channel_added[j] || filter.channel_size() == 0;
+ const bool result = seed_processor.CheckStudyChannel(filter, channels[j]);
+ EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+ }
+
+ if (i < arraysize(channels)) {
+ const int index = arraysize(channels) - i - 1;
+ filter.add_channel(channels[index]);
+ channel_added[index] = true;
+ }
+ }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyLocale) {
+ VariationsSeedProcessor seed_processor;
+
+ struct {
+ const char* filter_locales;
+ bool en_us_result;
+ bool en_ca_result;
+ bool fr_result;
+ } test_cases[] = {
+ {"en-US", true, false, false},
+ {"en-US,en-CA,fr", true, true, true},
+ {"en-US,en-CA,en-GB", true, true, false},
+ {"en-GB,en-CA,en-US", true, true, false},
+ {"ja,kr,vi", false, false, false},
+ {"fr-CA", false, false, false},
+ {"", true, true, true},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ std::vector<std::string> filter_locales;
+ Study_Filter filter;
+ base::SplitString(test_cases[i].filter_locales, ',', &filter_locales);
+ for (size_t j = 0; j < filter_locales.size(); ++j)
+ filter.add_locale(filter_locales[j]);
+ EXPECT_EQ(test_cases[i].en_us_result,
+ seed_processor.CheckStudyLocale(filter, "en-US"));
+ EXPECT_EQ(test_cases[i].en_ca_result,
+ seed_processor.CheckStudyLocale(filter, "en-CA"));
+ EXPECT_EQ(test_cases[i].fr_result,
+ seed_processor.CheckStudyLocale(filter, "fr"));
+ }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyPlatform) {
+ VariationsSeedProcessor seed_processor;
+
+ const Study_Platform platforms[] = {
+ Study_Platform_PLATFORM_WINDOWS,
+ Study_Platform_PLATFORM_MAC,
+ Study_Platform_PLATFORM_LINUX,
+ Study_Platform_PLATFORM_CHROMEOS,
+ Study_Platform_PLATFORM_ANDROID,
+ Study_Platform_PLATFORM_IOS,
+ };
+ ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE,
+ static_cast<int>(arraysize(platforms)));
+ bool platform_added[arraysize(platforms)] = { 0 };
+
+ Study_Filter filter;
+
+ // Check in the forwarded order. The loop cond is <= arraysize(platforms)
+ // instead of < so that the result of adding the last channel gets checked.
+ for (size_t i = 0; i <= arraysize(platforms); ++i) {
+ for (size_t j = 0; j < arraysize(platforms); ++j) {
+ const bool expected = platform_added[j] || filter.platform_size() == 0;
+ const bool result = seed_processor.CheckStudyPlatform(filter,
+ platforms[j]);
+ EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+ }
+
+ if (i < arraysize(platforms)) {
+ filter.add_platform(platforms[i]);
+ platform_added[i] = true;
+ }
+ }
+
+ // Do the same check in the reverse order.
+ filter.clear_platform();
+ memset(&platform_added, 0, sizeof(platform_added));
+ for (size_t i = 0; i <= arraysize(platforms); ++i) {
+ for (size_t j = 0; j < arraysize(platforms); ++j) {
+ const bool expected = platform_added[j] || filter.platform_size() == 0;
+ const bool result = seed_processor.CheckStudyPlatform(filter,
+ platforms[j]);
+ EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!";
+ }
+
+ if (i < arraysize(platforms)) {
+ const int index = arraysize(platforms) - i - 1;
+ filter.add_platform(platforms[index]);
+ platform_added[index] = true;
+ }
+ }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyStartDate) {
+ VariationsSeedProcessor seed_processor;
+
+ const base::Time now = base::Time::Now();
+ const base::TimeDelta delta = base::TimeDelta::FromHours(1);
+ const struct {
+ const base::Time start_date;
+ bool expected_result;
+ } start_test_cases[] = {
+ { now - delta, true },
+ { now, true },
+ { now + delta, false },
+ };
+
+ Study_Filter filter;
+
+ // Start date not set should result in true.
+ EXPECT_TRUE(seed_processor.CheckStudyStartDate(filter, now));
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(start_test_cases); ++i) {
+ filter.set_start_date(TimeToProtoTime(start_test_cases[i].start_date));
+ const bool result = seed_processor.CheckStudyStartDate(filter, now);
+ EXPECT_EQ(start_test_cases[i].expected_result, result)
+ << "Case " << i << " failed!";
+ }
+}
+
+TEST(VariationsSeedProcessorTest, CheckStudyVersion) {
+ VariationsSeedProcessor seed_processor;
+
+ const struct {
+ const char* min_version;
+ const char* version;
+ bool expected_result;
+ } min_test_cases[] = {
+ { "1.2.2", "1.2.3", true },
+ { "1.2.3", "1.2.3", true },
+ { "1.2.4", "1.2.3", false },
+ { "1.3.2", "1.2.3", false },
+ { "2.1.2", "1.2.3", false },
+ { "0.3.4", "1.2.3", true },
+ // Wildcards.
+ { "1.*", "1.2.3", true },
+ { "1.2.*", "1.2.3", true },
+ { "1.2.3.*", "1.2.3", true },
+ { "1.2.4.*", "1.2.3", false },
+ { "2.*", "1.2.3", false },
+ { "0.3.*", "1.2.3", true },
+ };
+
+ const struct {
+ const char* max_version;
+ const char* version;
+ bool expected_result;
+ } max_test_cases[] = {
+ { "1.2.2", "1.2.3", false },
+ { "1.2.3", "1.2.3", true },
+ { "1.2.4", "1.2.3", true },
+ { "2.1.1", "1.2.3", true },
+ { "2.1.1", "2.3.4", false },
+ // Wildcards
+ { "2.1.*", "2.3.4", false },
+ { "2.*", "2.3.4", true },
+ { "2.3.*", "2.3.4", true },
+ { "2.3.4.*", "2.3.4", true },
+ { "2.3.4.0.*", "2.3.4", true },
+ { "2.4.*", "2.3.4", true },
+ { "1.3.*", "2.3.4", false },
+ { "1.*", "2.3.4", false },
+ };
+
+ Study_Filter filter;
+
+ // Min/max version not set should result in true.
+ EXPECT_TRUE(seed_processor.CheckStudyVersion(filter, base::Version("1.2.3")));
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
+ filter.set_min_version(min_test_cases[i].min_version);
+ const bool result =
+ seed_processor.CheckStudyVersion(filter,
+ Version(min_test_cases[i].version));
+ EXPECT_EQ(min_test_cases[i].expected_result, result) <<
+ "Min. version case " << i << " failed!";
+ }
+ filter.clear_min_version();
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(max_test_cases); ++i) {
+ filter.set_max_version(max_test_cases[i].max_version);
+ const bool result =
+ seed_processor.CheckStudyVersion(filter,
+ Version(max_test_cases[i].version));
+ EXPECT_EQ(max_test_cases[i].expected_result, result) <<
+ "Max version case " << i << " failed!";
+ }
+
+ // Check intersection semantics.
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) {
+ for (size_t j = 0; j < ARRAYSIZE_UNSAFE(max_test_cases); ++j) {
+ filter.set_min_version(min_test_cases[i].min_version);
+ filter.set_max_version(max_test_cases[j].max_version);
+
+ if (!min_test_cases[i].expected_result) {
+ const bool result =
+ seed_processor.CheckStudyVersion(
+ filter, Version(min_test_cases[i].version));
+ EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
+ }
+
+ if (!max_test_cases[j].expected_result) {
+ const bool result =
+ seed_processor.CheckStudyVersion(
+ filter, Version(max_test_cases[j].version));
+ EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!";
+ }
+ }
+ }
+}
+
+// Test that the group for kForcingFlag1 is forced.
+TEST(VariationsSeedProcessorTest, ForceGroupWithFlag1) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
+
+ base::FieldTrialList field_trial_list(NULL);
+
+ Study study = CreateStudyWithFlagGroups(100, 0, 0);
+ VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+
+ EXPECT_EQ(kFlagGroup1Name,
+ base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+// Test that the group for kForcingFlag2 is forced.
+TEST(VariationsSeedProcessorTest, ForceGroupWithFlag2) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
+
+ base::FieldTrialList field_trial_list(NULL);
+
+ Study study = CreateStudyWithFlagGroups(100, 0, 0);
+ VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+
+ EXPECT_EQ(kFlagGroup2Name,
+ base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+TEST(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) {
+ // Add the flag to the command line arguments so the flag group is forced.
+ CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1);
+ CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2);
+
+ base::FieldTrialList field_trial_list(NULL);
+
+ Study study = CreateStudyWithFlagGroups(100, 0, 0);
+ VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+
+ EXPECT_EQ(kFlagGroup1Name,
+ base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+TEST(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) {
+ base::FieldTrialList field_trial_list(NULL);
+
+ // The two flag groups are given high probability, which would normally make
+ // them very likely to be chosen. They won't be chosen since flag groups are
+ // never chosen when their flag isn't present.
+ Study study = CreateStudyWithFlagGroups(1, 999, 999);
+ VariationsSeedProcessor().CreateTrialFromStudy(study, false);
+ EXPECT_EQ(kNonFlagGroupName,
+ base::FieldTrialList::FindFullName(kFlagStudyName));
+}
+
+TEST(VariationsSeedProcessorTest, IsStudyExpired) {
+ VariationsSeedProcessor seed_processor;
+
+ const base::Time now = base::Time::Now();
+ const base::TimeDelta delta = base::TimeDelta::FromHours(1);
+ const struct {
+ const base::Time expiry_date;
+ bool expected_result;
+ } expiry_test_cases[] = {
+ { now - delta, true },
+ { now, true },
+ { now + delta, false },
+ };
+
+ Study study;
+
+ // Expiry date not set should result in false.
+ EXPECT_FALSE(seed_processor.IsStudyExpired(study, now));
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expiry_test_cases); ++i) {
+ study.set_expiry_date(TimeToProtoTime(expiry_test_cases[i].expiry_date));
+ const bool result = seed_processor.IsStudyExpired(study, now);
+ EXPECT_EQ(expiry_test_cases[i].expected_result, result)
+ << "Case " << i << " failed!";
+ }
+}
+
+TEST(VariationsSeedProcessorTest, NonExpiredStudyPrioritizedOverExpiredStudy) {
+ VariationsSeedProcessor seed_processor;
+
+ const std::string kTrialName = "A";
+ const std::string kGroup1Name = "Group1";
+
+ VariationsSeed seed;
+ Study* study1 = seed.add_study();
+ study1->set_name(kTrialName);
+ study1->set_default_experiment_name("Default");
+ AddExperiment(kGroup1Name, 100, study1);
+ AddExperiment("Default", 0, study1);
+ Study* study2 = seed.add_study();
+ *study2 = *study1;
+ ASSERT_EQ(seed.study(0).name(), seed.study(1).name());
+
+ const base::Time year_ago =
+ base::Time::Now() - base::TimeDelta::FromDays(365);
+
+ const base::Version version("20.0.0.0");
+
+ // Check that adding [expired, non-expired] activates the non-expired one.
+ ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
+ {
+ base::FieldTrialList field_trial_list(NULL);
+ study1->set_expiry_date(TimeToProtoTime(year_ago));
+ seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
+ version, Study_Channel_STABLE);
+ EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
+ }
+
+ // Check that adding [non-expired, expired] activates the non-expired one.
+ ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName));
+ {
+ base::FieldTrialList field_trial_list(NULL);
+ study1->clear_expiry_date();
+ study2->set_expiry_date(TimeToProtoTime(year_ago));
+ seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(),
+ version, Study_Channel_STABLE);
+ EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName));
+ }
+}
+
+TEST(VariationsSeedProcessorTest, ValidateStudy) {
+ VariationsSeedProcessor seed_processor;
+
+ Study study;
+ study.set_default_experiment_name("def");
+ AddExperiment("abc", 100, &study);
+ Study_Experiment* default_group = AddExperiment("def", 200, &study);
+
+ base::FieldTrial::Probability total_probability = 0;
+ bool valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_TRUE(valid);
+ EXPECT_EQ(300, total_probability);
+
+ // Min version checks.
+ study.mutable_filter()->set_min_version("1.2.3.*");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_TRUE(valid);
+ study.mutable_filter()->set_min_version("1.*.3");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_FALSE(valid);
+ study.mutable_filter()->set_min_version("1.2.3");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_TRUE(valid);
+
+ // Max version checks.
+ study.mutable_filter()->set_max_version("2.3.4.*");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_TRUE(valid);
+ study.mutable_filter()->set_max_version("*.3");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_FALSE(valid);
+ study.mutable_filter()->set_max_version("2.3.4");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(
+ study, &total_probability);
+ EXPECT_TRUE(valid);
+
+ study.clear_default_experiment_name();
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+ &total_probability);
+ EXPECT_FALSE(valid);
+
+ study.set_default_experiment_name("xyz");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+ &total_probability);
+ EXPECT_FALSE(valid);
+
+ study.set_default_experiment_name("def");
+ default_group->clear_name();
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+ &total_probability);
+ EXPECT_FALSE(valid);
+
+ default_group->set_name("def");
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+ &total_probability);
+ ASSERT_TRUE(valid);
+ Study_Experiment* repeated_group = study.add_experiment();
+ repeated_group->set_name("abc");
+ repeated_group->set_probability_weight(1);
+ valid = seed_processor.ValidateStudyAndComputeTotalProbability(study,
+ &total_probability);
+ EXPECT_FALSE(valid);
+}
+
+TEST(VariationsSeedProcessorTest, VariationParams) {
+ base::FieldTrialList field_trial_list(NULL);
+ VariationsSeedProcessor seed_processor;
+
+ Study study;
+ study.set_name("Study1");
+ study.set_default_experiment_name("B");
+
+ Study_Experiment* experiment1 = AddExperiment("A", 1, &study);
+ Study_Experiment_Param* param = experiment1->add_param();
+ param->set_name("x");
+ param->set_value("y");
+
+ Study_Experiment* experiment2 = AddExperiment("B", 0, &study);
+
+ seed_processor.CreateTrialFromStudy(study, false);
+ EXPECT_EQ("y", GetVariationParamValue("Study1", "x"));
+
+ study.set_name("Study2");
+ experiment1->set_probability_weight(0);
+ experiment2->set_probability_weight(1);
+ seed_processor.CreateTrialFromStudy(study, false);
+ EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x"));
+}
+
+} // namespace chrome_variations
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
deleted file mode 100644
index babf91ea359..00000000000
--- a/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ColorChooserAndroid.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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
deleted file mode 100644
index c09cb89629c..00000000000
--- a/chromium/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/WebContentsDelegateAndroid.java
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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/component_jni_registrar.cc b/chromium/components/web_contents_delegate_android/component_jni_registrar.cc
deleted file mode 100644
index b5c5e1590e8..00000000000
--- a/chromium/components/web_contents_delegate_android/component_jni_registrar.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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
deleted file mode 100644
index 203585d4972..00000000000
--- a/chromium/components/web_contents_delegate_android/component_jni_registrar.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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
index 8f599a16b03..c908f6a61a9 100644
--- a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
+++ b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.cc
@@ -124,30 +124,6 @@ void WebContentsDelegateAndroid::NavigationStateChanged(
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);
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
index 68bca15cf44..d375be9ab49 100644
--- a/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
+++ b/chromium/components/web_contents_delegate_android/web_contents_delegate_android.h
@@ -57,12 +57,6 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate {
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;
diff --git a/chromium/components/web_modal/OWNERS b/chromium/components/web_modal/OWNERS
new file mode 100644
index 00000000000..ddd924dfa6a
--- /dev/null
+++ b/chromium/components/web_modal/OWNERS
@@ -0,0 +1,2 @@
+wittman@chromium.org
+ben@chromium.org
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
index 7145a08c088..4df36800c71 100644
--- a/chromium/components/web_modal/native_web_contents_modal_dialog_manager.h
+++ b/chromium/components/web_modal/native_web_contents_modal_dialog_manager.h
@@ -13,6 +13,8 @@ class WebContents;
namespace web_modal {
+class WebContentsModalDialogHost;
+
// Interface from NativeWebContentsModalDialogManager to
// WebContentsModalDialogManager.
class NativeWebContentsModalDialogManagerDelegate {
@@ -54,6 +56,9 @@ class NativeWebContentsModalDialogManager {
// Runs a pulse animation for the web contents modal dialog.
virtual void PulseDialog(NativeWebContentsModalDialog dialog) = 0;
+ // Called when the host view for the dialog has changed.
+ virtual void HostChanged(WebContentsModalDialogHost* new_host) = 0;
+
protected:
NativeWebContentsModalDialogManager() {}
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_host.h b/chromium/components/web_modal/web_contents_modal_dialog_host.h
index 499925cac21..73a3a7a0da6 100644
--- a/chromium/components/web_modal/web_contents_modal_dialog_host.h
+++ b/chromium/components/web_modal/web_contents_modal_dialog_host.h
@@ -6,8 +6,11 @@
#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 gfx {
+class Point;
+class Size;
+}
namespace web_modal {
@@ -18,6 +21,7 @@ class WebContentsModalDialogHostObserver {
virtual ~WebContentsModalDialogHostObserver();
virtual void OnPositionRequiresUpdate() = 0;
+ virtual void OnHostDestroying() = 0;
protected:
WebContentsModalDialogHostObserver();
@@ -38,6 +42,9 @@ class WebContentsModalDialogHost {
// view.
virtual gfx::Point GetDialogPosition(const gfx::Size& size) = 0;
+ // Returns the maximum dimensions a dialog can have.
+ virtual gfx::Size GetMaximumDialogSize() = 0;
+
// Add/remove observer.
virtual void AddObserver(WebContentsModalDialogHostObserver* observer) = 0;
virtual void RemoveObserver(WebContentsModalDialogHostObserver* observer) = 0;
diff --git a/chromium/components/web_modal/web_contents_modal_dialog_manager.cc b/chromium/components/web_modal/web_contents_modal_dialog_manager.cc
index 0e07f8dd1d9..4d8aca714c5 100644
--- a/chromium/components/web_modal/web_contents_modal_dialog_manager.cc
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager.cc
@@ -25,9 +25,16 @@ WebContentsModalDialogManager::~WebContentsModalDialogManager() {
DCHECK(child_dialogs_.empty());
}
+void WebContentsModalDialogManager::SetDelegate(
+ WebContentsModalDialogManagerDelegate* d) {
+ delegate_ = d;
+ // Delegate can be NULL on Views/Win32 during tab drag.
+ native_manager_->HostChanged(d ? d->GetWebContentsModalDialogHost() : NULL);
+}
+
void WebContentsModalDialogManager::ShowDialog(
NativeWebContentsModalDialog dialog) {
- child_dialogs_.push_back(dialog);
+ child_dialogs_.push_back(DialogState(dialog));
native_manager_->ManageDialog(dialog);
@@ -38,13 +45,21 @@ void WebContentsModalDialogManager::ShowDialog(
}
}
-bool WebContentsModalDialogManager::IsShowingDialog() const {
+bool WebContentsModalDialogManager::IsDialogActive() const {
return !child_dialogs_.empty();
}
void WebContentsModalDialogManager::FocusTopmostDialog() {
DCHECK(!child_dialogs_.empty());
- native_manager_->FocusDialog(child_dialogs_.front());
+ native_manager_->FocusDialog(child_dialogs_.front().dialog);
+}
+
+void WebContentsModalDialogManager::SetCloseOnInterstitialWebUI(
+ NativeWebContentsModalDialog dialog,
+ bool close) {
+ WebContentsModalDialogList::iterator loc = FindDialogState(dialog);
+ DCHECK(loc != child_dialogs_.end());
+ loc->close_on_interstitial_webui = close;
}
content::WebContents* WebContentsModalDialogManager::GetWebContents() const {
@@ -53,8 +68,7 @@ content::WebContents* WebContentsModalDialogManager::GetWebContents() const {
void WebContentsModalDialogManager::WillClose(
NativeWebContentsModalDialog dialog) {
- WebContentsModalDialogList::iterator i(
- std::find(child_dialogs_.begin(), child_dialogs_.end(), dialog));
+ WebContentsModalDialogList::iterator i = FindDialogState(dialog);
// The Views tab contents modal dialog calls WillClose twice. Ignore the
// second invocation.
@@ -65,7 +79,7 @@ void WebContentsModalDialogManager::WillClose(
child_dialogs_.erase(i);
if (!child_dialogs_.empty() && removed_topmost_dialog &&
!closing_all_dialogs_)
- native_manager_->ShowDialog(child_dialogs_.front());
+ native_manager_->ShowDialog(child_dialogs_.front().dialog);
BlockWebContentsInteraction(!child_dialogs_.empty());
}
@@ -75,15 +89,14 @@ void WebContentsModalDialogManager::Observe(
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());
+ native_manager_->ShowDialog(child_dialogs_.front().dialog);
else
- native_manager_->HideDialog(child_dialogs_.front());
+ native_manager_->HideDialog(child_dialogs_.front().dialog);
}
WebContentsModalDialogManager::WebContentsModalDialogManager(
@@ -98,6 +111,24 @@ WebContentsModalDialogManager::WebContentsModalDialogManager(
content::Source<content::WebContents>(web_contents));
}
+WebContentsModalDialogManager::DialogState::DialogState(
+ NativeWebContentsModalDialog dialog)
+ : dialog(dialog),
+ close_on_interstitial_webui(false) {
+}
+
+WebContentsModalDialogManager::WebContentsModalDialogList::iterator
+ WebContentsModalDialogManager::FindDialogState(
+ NativeWebContentsModalDialog dialog) {
+ WebContentsModalDialogList::iterator i;
+ for (i = child_dialogs_.begin(); i != child_dialogs_.end(); ++i) {
+ if (i->dialog == dialog)
+ break;
+ }
+
+ return i;
+}
+
void WebContentsModalDialogManager::BlockWebContentsInteraction(bool blocked) {
WebContents* contents = web_contents();
if (!contents) {
@@ -118,7 +149,7 @@ void WebContentsModalDialogManager::CloseAllDialogs() {
// Clear out any dialogs since we are leaving this page entirely.
while (!child_dialogs_.empty())
- native_manager_->CloseDialog(child_dialogs_.front());
+ native_manager_->CloseDialog(child_dialogs_.front().dialog);
closing_all_dialogs_ = false;
}
@@ -135,7 +166,7 @@ void WebContentsModalDialogManager::DidNavigateMainFrame(
void WebContentsModalDialogManager::DidGetIgnoredUIEvent() {
if (!child_dialogs_.empty())
- native_manager_->FocusDialog(child_dialogs_.front());
+ native_manager_->FocusDialog(child_dialogs_.front().dialog);
}
void WebContentsModalDialogManager::WebContentsDestroyed(WebContents* tab) {
@@ -146,4 +177,15 @@ void WebContentsModalDialogManager::WebContentsDestroyed(WebContents* tab) {
CloseAllDialogs();
}
+void WebContentsModalDialogManager::DidAttachInterstitialPage() {
+ // Copy the dialogs so we can close and remove them while iterating over the
+ // list.
+ WebContentsModalDialogList dialogs(child_dialogs_);
+ for (WebContentsModalDialogList::iterator it = dialogs.begin();
+ it != dialogs.end(); ++it) {
+ if (it->close_on_interstitial_webui)
+ native_manager_->CloseDialog(it->dialog);
+ }
+}
+
} // 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
index e2548c04e5c..58d5520b9fc 100644
--- a/chromium/components/web_modal/web_contents_modal_dialog_manager.h
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager.h
@@ -29,7 +29,7 @@ class WebContentsModalDialogManager
virtual ~WebContentsModalDialogManager();
WebContentsModalDialogManagerDelegate* delegate() const { return delegate_; }
- void set_delegate(WebContentsModalDialogManagerDelegate* d) { delegate_ = d; }
+ void SetDelegate(WebContentsModalDialogManagerDelegate* d);
static NativeWebContentsModalDialogManager* CreateNativeManager(
NativeWebContentsModalDialogManagerDelegate* native_delegate);
@@ -38,13 +38,17 @@ class WebContentsModalDialogManager
// WillClose() when it is being destroyed.
void ShowDialog(NativeWebContentsModalDialog dialog);
- // Returns true if a dialog is currently being shown.
- bool IsShowingDialog() const;
+ // Returns true if any dialogs are active and not closed.
+ bool IsDialogActive() const;
- // Focus the topmost modal dialog. IsShowingDialog() must be true when
- // calling this function.
+ // Focus the topmost modal dialog. IsDialogActive() must be true when calling
+ // this function.
void FocusTopmostDialog();
+ // Set to true to close the window when a page load starts on the WebContents.
+ void SetCloseOnInterstitialWebUI(NativeWebContentsModalDialog dialog,
+ bool close);
+
// Overriden from NativeWebContentsModalDialogManagerDelegate:
virtual content::WebContents* GetWebContents() const OVERRIDE;
// Called when a WebContentsModalDialogs we own is about to be closed.
@@ -62,6 +66,7 @@ class WebContentsModalDialogManager
: manager_(manager) {}
void CloseAllDialogs() { manager_->CloseAllDialogs(); }
+ void DidAttachInterstitialPage() { manager_->DidAttachInterstitialPage(); }
void ResetNativeManager(NativeWebContentsModalDialogManager* delegate) {
manager_->native_manager_.reset(delegate);
}
@@ -76,7 +81,18 @@ class WebContentsModalDialogManager
explicit WebContentsModalDialogManager(content::WebContents* web_contents);
friend class content::WebContentsUserData<WebContentsModalDialogManager>;
- typedef std::deque<NativeWebContentsModalDialog> WebContentsModalDialogList;
+ struct DialogState {
+ explicit DialogState(NativeWebContentsModalDialog dialog);
+
+ NativeWebContentsModalDialog dialog;
+ bool close_on_interstitial_webui;
+ };
+
+ typedef std::deque<DialogState> WebContentsModalDialogList;
+
+ // Utility function to get the dialog state for a dialog.
+ WebContentsModalDialogList::iterator FindDialogState(
+ NativeWebContentsModalDialog dialog);
// Blocks/unblocks interaction with renderer process.
void BlockWebContentsInteraction(bool blocked);
@@ -92,6 +108,7 @@ class WebContentsModalDialogManager
const content::FrameNavigateParams& params) OVERRIDE;
virtual void DidGetIgnoredUIEvent() OVERRIDE;
virtual void WebContentsDestroyed(content::WebContents* tab) OVERRIDE;
+ virtual void DidAttachInterstitialPage() OVERRIDE;
// Delegate for notifying our owner about stuff. Not owned by us.
WebContentsModalDialogManagerDelegate* delegate_;
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
index 1268896d6af..bb866916264 100644
--- a/chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc
+++ b/chromium/components/web_modal/web_contents_modal_dialog_manager_unittest.cc
@@ -2,80 +2,340 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
#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 "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
+#include "content/public/browser/notification_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/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
+class TestNativeWebContentsModalDialogManager
: public NativeWebContentsModalDialogManager {
public:
- NativeWebContentsModalDialogManagerCloseTest(
+ enum DialogState {
+ UNKNOWN,
+ NOT_SHOWN,
+ SHOWN,
+ HIDDEN,
+ CLOSED
+ };
+
+ explicit TestNativeWebContentsModalDialogManager(
NativeWebContentsModalDialogManagerDelegate* delegate)
: delegate_(delegate) {}
virtual void ManageDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ dialog_state_[dialog] = NOT_SHOWN;
}
virtual void ShowDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ dialog_state_[dialog] = SHOWN;
}
virtual void HideDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
+ dialog_state_[dialog] = HIDDEN;
}
virtual void CloseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
delegate_->WillClose(dialog);
- close_count++;
+ dialog_state_[dialog] = CLOSED;
}
virtual void FocusDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
}
virtual void PulseDialog(NativeWebContentsModalDialog dialog) OVERRIDE {
}
+ virtual void HostChanged(WebContentsModalDialogHost* new_host) OVERRIDE {
+ }
+
+ int GetCloseCount() const {
+ int count = 0;
+ for (DialogStateMap::const_iterator it = dialog_state_.begin();
+ it != dialog_state_.end(); ++it) {
+ if (it->second == CLOSED)
+ count++;
+ }
+ return count;
+ }
+
+ DialogState GetDialogState(NativeWebContentsModalDialog dialog) const {
+ DialogStateMap::const_iterator loc = dialog_state_.find(dialog);
+ return loc == dialog_state_.end() ? UNKNOWN : loc->second;
+ }
+
+ private:
+ typedef std::map<NativeWebContentsModalDialog, DialogState> DialogStateMap;
- int close_count;
NativeWebContentsModalDialogManagerDelegate* delegate_;
+ DialogStateMap dialog_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestNativeWebContentsModalDialogManager);
+};
+
+class TestWebContentsModalDialogManagerDelegate
+ : public WebContentsModalDialogManagerDelegate {
+ public:
+ TestWebContentsModalDialogManagerDelegate()
+ : web_contents_visible_(true),
+ web_contents_blocked_(false) {
+ }
+
+ // WebContentsModalDialogManagerDelegate overrides
+ virtual void SetWebContentsBlocked(content::WebContents* web_contents,
+ bool blocked) OVERRIDE {
+ web_contents_blocked_ = blocked;
+ }
+
+ virtual WebContentsModalDialogHost* GetWebContentsModalDialogHost() OVERRIDE {
+ return NULL;
+ }
+
+ virtual bool IsWebContentsVisible(
+ content::WebContents* web_contents) OVERRIDE {
+ return web_contents_visible_;
+ }
+
+ void set_web_contents_visible(bool visible) {
+ web_contents_visible_ = visible;
+ }
+
+ bool web_contents_blocked() const { return web_contents_blocked_; }
+
+ private:
+ bool web_contents_visible_;
+ bool web_contents_blocked_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWebContentsModalDialogManagerDelegate);
};
-NativeWebContentsModalDialogManager* WebContentsModalDialogManager::
-CreateNativeManager(
+class WebContentsModalDialogManagerTest
+ : public content::RenderViewHostTestHarness {
+ public:
+ WebContentsModalDialogManagerTest()
+ : next_dialog_id(1),
+ manager(NULL),
+ native_manager(NULL) {
+ }
+
+ virtual void SetUp() {
+ content::RenderViewHostTestHarness::SetUp();
+
+ delegate.reset(new TestWebContentsModalDialogManagerDelegate);
+ WebContentsModalDialogManager::CreateForWebContents(web_contents());
+ manager = WebContentsModalDialogManager::FromWebContents(web_contents());
+ manager->SetDelegate(delegate.get());
+ test_api.reset(new WebContentsModalDialogManager::TestApi(manager));
+ native_manager = new TestNativeWebContentsModalDialogManager(manager);
+
+ // |manager| owns |native_manager| as a result.
+ test_api->ResetNativeManager(native_manager);
+ }
+
+ virtual void TearDown() {
+ test_api.reset();
+ content::RenderViewHostTestHarness::TearDown();
+ }
+
+ protected:
+ NativeWebContentsModalDialog MakeFakeDialog() {
+ // WebContentsModalDialogManager treats the NativeWebContentsModalDialog as
+ // an opaque type, so creating fake NativeWebContentsModalDialogs using
+ // reinterpret_cast is valid.
+ return reinterpret_cast<NativeWebContentsModalDialog>(next_dialog_id++);
+ }
+
+ int next_dialog_id;
+ scoped_ptr<TestWebContentsModalDialogManagerDelegate> delegate;
+ WebContentsModalDialogManager* manager;
+ scoped_ptr<WebContentsModalDialogManager::TestApi> test_api;
+ TestNativeWebContentsModalDialogManager* native_manager;
+
+ DISALLOW_COPY_AND_ASSIGN(WebContentsModalDialogManagerTest);
+};
+
+NativeWebContentsModalDialogManager*
+WebContentsModalDialogManager::CreateNativeManager(
NativeWebContentsModalDialogManagerDelegate* native_delegate) {
- return new NativeWebContentsModalDialogManagerCloseTest(native_delegate);
+ return new TestNativeWebContentsModalDialogManager(native_delegate);
+}
+
+// Test that the dialog is shown immediately when the delegate indicates the web
+// contents is visible.
+TEST_F(WebContentsModalDialogManagerTest, WebContentsVisible) {
+ // Dialog should be shown while WebContents is visible.
+ const NativeWebContentsModalDialog dialog1 = MakeFakeDialog();
+
+ manager->ShowDialog(dialog1);
+
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog1));
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
}
-TEST_F(WebContentsModalDialogManagerTest, WebContentsModalDialogs) {
- WebContentsModalDialogManager* web_contents_modal_dialog_manager =
- WebContentsModalDialogManager::FromWebContents(web_contents());
- WebContentsModalDialogManager::TestApi test_api(
- web_contents_modal_dialog_manager);
+// Test that the dialog is not shown immediately when the delegate indicates the
+// web contents is not visible.
+TEST_F(WebContentsModalDialogManagerTest, WebContentsNotVisible) {
+ // Dialog should not be shown while WebContents is not visible.
+ delegate->set_web_contents_visible(false);
+
+ const NativeWebContentsModalDialog dialog1 = MakeFakeDialog();
+
+ manager->ShowDialog(dialog1);
+
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::NOT_SHOWN,
+ native_manager->GetDialogState(dialog1));
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+}
+
+// Test that only the first of multiple dialogs is shown.
+TEST_F(WebContentsModalDialogManagerTest, ShowDialogs) {
+ const NativeWebContentsModalDialog dialog1 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog2 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog3 = MakeFakeDialog();
+
+ manager->ShowDialog(dialog1);
+ manager->ShowDialog(dialog2);
+ manager->ShowDialog(dialog3);
+
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog1));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::NOT_SHOWN,
+ native_manager->GetDialogState(dialog2));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::NOT_SHOWN,
+ native_manager->GetDialogState(dialog3));
+}
+
+// Test that the dialog is shown/hidden on
+// NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED.
+TEST_F(WebContentsModalDialogManagerTest, VisibilityObservation) {
+ const NativeWebContentsModalDialog dialog1 = MakeFakeDialog();
+ bool web_contents_visible = true;
+
+ manager->ShowDialog(dialog1);
+
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog1));
+
+ web_contents_visible = false;
+ manager->Observe(content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ content::NotificationService::AllSources(),
+ content::Details<bool>(&web_contents_visible));
- NativeWebContentsModalDialogManagerCloseTest* native_manager =
- new NativeWebContentsModalDialogManagerCloseTest(
- web_contents_modal_dialog_manager);
- native_manager->close_count = 0;
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::HIDDEN,
+ native_manager->GetDialogState(dialog1));
- test_api.ResetNativeManager(native_manager);
+ web_contents_visible = true;
+ manager->Observe(content::NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED,
+ content::NotificationService::AllSources(),
+ content::Details<bool>(&web_contents_visible));
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog1));
+}
+
+// Test that attaching an interstitial WebUI page closes dialogs configured to
+// close on interstitial WebUI.
+TEST_F(WebContentsModalDialogManagerTest, InterstitialWebUI) {
+ const NativeWebContentsModalDialog dialog1 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog2 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog3 = MakeFakeDialog();
+
+ manager->ShowDialog(dialog1);
+ manager->ShowDialog(dialog2);
+ manager->ShowDialog(dialog3);
+
+ manager->SetCloseOnInterstitialWebUI(dialog1, true);
+ manager->SetCloseOnInterstitialWebUI(dialog3, true);
+
+ test_api->DidAttachInterstitialPage();
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::CLOSED,
+ native_manager->GetDialogState(dialog1));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog2));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::CLOSED,
+ native_manager->GetDialogState(dialog3));
+}
+
+
+// Test that the first dialog is always shown, regardless of the order in which
+// dialogs are closed.
+TEST_F(WebContentsModalDialogManagerTest, CloseDialogs) {
+ // The front dialog is always shown regardless of dialog close order.
+ const NativeWebContentsModalDialog dialog1 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog2 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog3 = MakeFakeDialog();
+ const NativeWebContentsModalDialog dialog4 = MakeFakeDialog();
+
+ manager->ShowDialog(dialog1);
+ manager->ShowDialog(dialog2);
+ manager->ShowDialog(dialog3);
+ manager->ShowDialog(dialog4);
+
+ native_manager->CloseDialog(dialog1);
+
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::CLOSED,
+ native_manager->GetDialogState(dialog1));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog2));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::NOT_SHOWN,
+ native_manager->GetDialogState(dialog3));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::NOT_SHOWN,
+ native_manager->GetDialogState(dialog4));
+
+ native_manager->CloseDialog(dialog3);
+
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog2));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::CLOSED,
+ native_manager->GetDialogState(dialog3));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::NOT_SHOWN,
+ native_manager->GetDialogState(dialog4));
+
+ native_manager->CloseDialog(dialog2);
+
+ EXPECT_TRUE(manager->IsDialogActive());
+ EXPECT_TRUE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::CLOSED,
+ native_manager->GetDialogState(dialog2));
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::SHOWN,
+ native_manager->GetDialogState(dialog4));
+
+ native_manager->CloseDialog(dialog4);
+
+ EXPECT_FALSE(manager->IsDialogActive());
+ EXPECT_FALSE(delegate->web_contents_blocked());
+ EXPECT_EQ(TestNativeWebContentsModalDialogManager::CLOSED,
+ native_manager->GetDialogState(dialog4));
+}
+
+// Test that CloseAllDialogs does what it says.
+TEST_F(WebContentsModalDialogManagerTest, CloseAllDialogs) {
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);
+ manager->ShowDialog(MakeFakeDialog());
+
+ EXPECT_EQ(0, native_manager->GetCloseCount());
- test_api.CloseAllDialogs();
- EXPECT_EQ(native_manager->close_count, kWindowCount);
+ test_api->CloseAllDialogs();
+ EXPECT_FALSE(delegate->web_contents_blocked());
+ EXPECT_FALSE(manager->IsDialogActive());
+ EXPECT_EQ(kWindowCount, native_manager->GetCloseCount());
}
} // namespace web_modal
diff --git a/chromium/components/webdata/common/web_database.cc b/chromium/components/webdata/common/web_database.cc
index e4e44ef1e03..161fb80bae4 100644
--- a/chromium/components/webdata/common/web_database.cc
+++ b/chromium/components/webdata/common/web_database.cc
@@ -14,7 +14,7 @@
// corresponding changes must happen in the unit tests, and new migration test
// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
// static
-const int WebDatabase::kCurrentVersionNumber = 52;
+const int WebDatabase::kCurrentVersionNumber = 53;
namespace {
diff --git a/chromium/components/webdata/common/web_database_migration_unittest.cc b/chromium/components/webdata/common/web_database_migration_unittest.cc
index 5e6ef6e9dc3..57ef02dc175 100644
--- a/chromium/components/webdata/common/web_database_migration_unittest.cc
+++ b/chromium/components/webdata/common/web_database_migration_unittest.cc
@@ -220,7 +220,7 @@ class WebDatabaseMigrationTest : public testing::Test {
source_path = source_path.AppendASCII("web_database");
source_path = source_path.Append(file);
return base::PathExists(source_path) &&
- file_util::ReadFileToString(source_path, contents);
+ base::ReadFileToString(source_path, contents);
}
static int VersionFromConnection(sql::Connection* connection) {
@@ -247,7 +247,7 @@ class WebDatabaseMigrationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
};
-const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 52;
+const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 53;
void WebDatabaseMigrationTest::LoadDatabase(
const base::FilePath::StringType& file) {
@@ -2029,8 +2029,9 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion49ToCurrent) {
}
// 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.
+// |suggest_url_post_params|, |instant_url_post_params|, and
+// |image_url_post_params| are added to the keyword table schema for a version
+// 50 database.
TEST_F(WebDatabaseMigrationTest, MigrateVersion50ToCurrent) {
ASSERT_NO_FATAL_FAILURE(
LoadDatabase(FILE_PATH_LITERAL("version_50.sql")));
@@ -2080,3 +2081,39 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion50ToCurrent) {
"image_url_post_params"));
}
}
+
+// Tests that the column |new_tab_url| is added to the keyword table schema for
+// a version 52 database.
+TEST_F(WebDatabaseMigrationTest, MigrateVersion52ToCurrent) {
+ ASSERT_NO_FATAL_FAILURE(
+ LoadDatabase(FILE_PATH_LITERAL("version_52.sql")));
+
+ // Verify pre-conditions. These are expectations for version 52 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, 52, 52));
+
+ ASSERT_FALSE(connection.DoesColumnExist("keywords", "new_tab_url"));
+ }
+
+ 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", "new_tab_url"));
+ }
+}