diff options
author | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-02 20:42:04 +0000 |
---|---|---|
committer | isherman@chromium.org <isherman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-02 20:42:04 +0000 |
commit | 1d14f58b753c59924cef2f5bb4a0f1cb88db07d5 (patch) | |
tree | a0bdee96486c4f0cbe4bb8e23ed5b890c8131587 | |
parent | fd346b76799059213d36109a799bc029f3a2ed1d (diff) | |
download | chromium_src-1d14f58b753c59924cef2f5bb4a0f1cb88db07d5.zip chromium_src-1d14f58b753c59924cef2f5bb4a0f1cb88db07d5.tar.gz chromium_src-1d14f58b753c59924cef2f5bb4a0f1cb88db07d5.tar.bz2 |
Add metrics to measure time elapsed between form load and form submission with or without Autofill.
BUG=none
TEST=unit_tests --gtest_filter=AutofillMetricsTest.*
Review URL: http://codereview.chromium.org/7740070
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99447 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/time.h | 18 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.cc | 32 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager.h | 24 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_manager_unittest.cc | 5 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_metrics.cc | 39 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_metrics.h | 30 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_metrics_unittest.cc | 194 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_type.cc | 40 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.cc | 53 | ||||
-rw-r--r-- | chrome/browser/autofill/form_structure.h | 13 | ||||
-rw-r--r-- | chrome/common/autofill_messages.h | 20 | ||||
-rw-r--r-- | chrome/renderer/autofill/autofill_agent.cc | 19 | ||||
-rw-r--r-- | chrome/renderer/autofill/form_autocomplete_browsertest.cc | 7 | ||||
-rw-r--r-- | ipc/ipc_message_utils.cc | 25 | ||||
-rw-r--r-- | ipc/ipc_message_utils.h | 9 |
15 files changed, 439 insertions, 89 deletions
diff --git a/base/time.h b/base/time.h index aa97f0a..8590e99 100644 --- a/base/time.h +++ b/base/time.h @@ -60,9 +60,18 @@ class BASE_EXPORT TimeDelta { static TimeDelta FromMilliseconds(int64 ms); static TimeDelta FromMicroseconds(int64 us); + // Converts an integer value representing TimeDelta to a class. This is used + // when deserializing a |TimeDelta| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static TimeDelta FromInternalValue(int64 delta) { + return TimeDelta(delta); + } + // Returns the internal numeric value of the TimeDelta object. Please don't // use this and do arithmetic on it, as it is more error prone than using the // provided operators. + // For serializing, use FromInternalValue to reconstitute. int64 ToInternalValue() const { return delta_; } @@ -487,7 +496,16 @@ class BASE_EXPORT TimeTicks { return ticks_ == 0; } + // Converts an integer value representing TimeTicks to a class. This is used + // when deserializing a |TimeTicks| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static TimeTicks FromInternalValue(int64 ticks) { + return TimeTicks(ticks); + } + // Returns the internal numeric value of the TimeTicks object. + // For serializing, use FromInternalValue to reconstitute. int64 ToInternalValue() const { return ticks_; } diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index c5d4755..ce8e9df 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -52,6 +52,7 @@ #include "webkit/glue/form_data_predictions.h" #include "webkit/glue/form_field.h" +using base::TimeTicks; using switches::kEnableAutofillFeedback; using webkit_glue::FormData; using webkit_glue::FormDataPredictions; @@ -303,7 +304,8 @@ bool AutofillManager::OnMessageReceived(const IPC::Message& message) { return handled; } -void AutofillManager::OnFormSubmitted(const FormData& form) { +void AutofillManager::OnFormSubmitted(const FormData& form, + const TimeTicks& timestamp) { // Let AutoComplete know as well. tab_contents_wrapper_->autocomplete_history_manager()->OnFormSubmitted(form); @@ -336,7 +338,10 @@ void AutofillManager::OnFormSubmitted(const FormData& form) { if (!personal_data_->profiles().empty() || !personal_data_->credit_cards().empty()) { DeterminePossibleFieldTypesForUpload(&submitted_form); - submitted_form.LogQualityMetrics(*metric_logger_); + submitted_form.LogQualityMetrics(*metric_logger_, + forms_loaded_timestamp_, + initial_interaction_timestamp_, + timestamp); if (submitted_form.ShouldBeCrowdsourced()) UploadFormData(submitted_form); @@ -348,7 +353,8 @@ void AutofillManager::OnFormSubmitted(const FormData& form) { ImportFormData(submitted_form); } -void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms) { +void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms, + const TimeTicks& timestamp) { bool enabled = IsAutofillEnabled(); if (!has_logged_autofill_enabled_) { metric_logger_->LogIsAutofillEnabledAtPageLoad(enabled); @@ -358,11 +364,13 @@ void AutofillManager::OnFormsSeen(const std::vector<FormData>& forms) { if (!enabled) return; + forms_loaded_timestamp_ = timestamp; ParseForms(forms); } void AutofillManager::OnTextFieldDidChange(const FormData& form, - const FormField& field) { + const FormField& field, + const TimeTicks& timestamp) { FormStructure* form_structure = NULL; AutofillField* autofill_field = NULL; if (!FindCachedFormAndField(form, field, &form_structure, &autofill_field)) @@ -384,6 +392,8 @@ void AutofillManager::OnTextFieldDidChange(const FormData& form, AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD_ONCE); } } + + UpdateInitialInteractionTimestamp(timestamp); } void AutofillManager::OnQueryFormFieldAutofill( @@ -633,7 +643,7 @@ void AutofillManager::OnDidPreviewAutofillFormData() { } -void AutofillManager::OnDidFillAutofillFormData() { +void AutofillManager::OnDidFillAutofillFormData(const TimeTicks& timestamp) { NotificationService::current()->Notify( chrome::NOTIFICATION_AUTOFILL_DID_FILL_FORM_DATA, Source<RenderViewHost>(tab_contents()->render_view_host()), @@ -645,6 +655,8 @@ void AutofillManager::OnDidFillAutofillFormData() { metric_logger_->LogUserHappinessMetric( AutofillMetrics::USER_DID_AUTOFILL_ONCE); } + + UpdateInitialInteractionTimestamp(timestamp); } void AutofillManager::OnDidShowAutofillSuggestions(bool is_new_popup) { @@ -775,6 +787,8 @@ void AutofillManager::Reset() { user_did_type_ = false; user_did_autofill_ = false; user_did_edit_autofilled_field_ = false; + forms_loaded_timestamp_ = TimeTicks(); + initial_interaction_timestamp_ = TimeTicks(); } AutofillManager::AutofillManager(TabContentsWrapper* tab_contents, @@ -1151,3 +1165,11 @@ void AutofillManager::UnpackGUIDs(int id, *cc_guid = IDToGUID(cc_id); *profile_guid = IDToGUID(profile_id); } + +void AutofillManager::UpdateInitialInteractionTimestamp( + const TimeTicks& interaction_timestamp) { + if (initial_interaction_timestamp_.is_null() || + interaction_timestamp < initial_interaction_timestamp_) { + initial_interaction_timestamp_ = interaction_timestamp; + } +} diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index d488a30..79dab88 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -17,6 +17,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/string16.h" +#include "base/time.h" #include "chrome/browser/autofill/autofill_download.h" #include "chrome/browser/autofill/field_types.h" #include "chrome/browser/autofill/form_structure.h" @@ -113,10 +114,13 @@ class AutofillManager : public TabContentsObserver, void UnpackGUIDs(int id, GUIDPair* cc_guid, GUIDPair* profile_guid); private: - void OnFormSubmitted(const webkit_glue::FormData& form); - void OnFormsSeen(const std::vector<webkit_glue::FormData>& forms); + void OnFormSubmitted(const webkit_glue::FormData& form, + const base::TimeTicks& timestamp); + void OnFormsSeen(const std::vector<webkit_glue::FormData>& forms, + const base::TimeTicks& timestamp); void OnTextFieldDidChange(const webkit_glue::FormData& form, - const webkit_glue::FormField& field); + const webkit_glue::FormField& field, + const base::TimeTicks& timestamp); void OnQueryFormFieldAutofill(int query_id, const webkit_glue::FormData& form, const webkit_glue::FormField& field); @@ -126,7 +130,7 @@ class AutofillManager : public TabContentsObserver, int unique_id); void OnShowAutofillDialog(); void OnDidPreviewAutofillFormData(); - void OnDidFillAutofillFormData(); + void OnDidFillAutofillFormData(const base::TimeTicks& timestamp); void OnDidShowAutofillSuggestions(bool is_new_popup); // Fills |host| with the RenderViewHost for this tab. @@ -199,6 +203,12 @@ class AutofillManager : public TabContentsObserver, // |submitted_form|. void DeterminePossibleFieldTypesForUpload(FormStructure* submitted_form); + // If |initial_interaction_timestamp_| is unset or is set to a later time than + // |interaction_timestamp|, updates the cached timestamp. The latter check is + // needed because IPC messages can arrive out of order. + void UpdateInitialInteractionTimestamp( + const base::TimeTicks& interaction_timestamp); + // The owning TabContentsWrapper. TabContentsWrapper* tab_contents_wrapper_; @@ -233,6 +243,11 @@ class AutofillManager : public TabContentsObserver, bool user_did_autofill_; // Has the user edited a field that was previously autofilled? bool user_did_edit_autofilled_field_; + // When the page finished loading. + base::TimeTicks forms_loaded_timestamp_; + // When the user first interacted with a potentially fillable form on this + // page. + base::TimeTicks initial_interaction_timestamp_; // Our copy of the form data. ScopedVector<FormStructure> form_structures_; @@ -277,6 +292,7 @@ class AutofillManager : public TabContentsObserver, FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, UserHappinessFormLoadAndSubmission); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, UserHappinessFormInteraction); + FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, FormFillDuration); DISALLOW_COPY_AND_ASSIGN(AutofillManager); }; diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index b850a41..8804aa7 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -10,6 +10,7 @@ #include "base/string16.h" #include "base/string_number_conversions.h" #include "base/stringprintf.h" +#include "base/time.h" #include "base/tuple.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete_history_manager.h" @@ -512,11 +513,11 @@ class AutofillManagerTest : public TabContentsWrapperTestHarness { } void FormsSeen(const std::vector<webkit_glue::FormData>& forms) { - autofill_manager_->OnFormsSeen(forms); + autofill_manager_->OnFormsSeen(forms, base::TimeTicks()); } void FormSubmitted(const FormData& form) { - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, base::TimeTicks::Now()); } void FillAutofillFormData(int query_id, diff --git a/chrome/browser/autofill/autofill_metrics.cc b/chrome/browser/autofill/autofill_metrics.cc index c3ad149..dae51a2 100644 --- a/chrome/browser/autofill/autofill_metrics.cc +++ b/chrome/browser/autofill/autofill_metrics.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/metrics/histogram.h" +#include "base/time.h" #include "chrome/browser/autofill/autofill_type.h" #include "chrome/browser/autofill/form_structure.h" #include "webkit/glue/form_data.h" @@ -298,6 +299,44 @@ void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric) const { NUM_USER_HAPPINESS_METRICS); } +void AutofillMetrics::LogFormFillDurationFromLoadWithAutofill( + const base::TimeDelta& duration) const { + UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithAutofill", + duration, + base::TimeDelta::FromMilliseconds(100), + base::TimeDelta::FromMinutes(10), + 50); +} + +void AutofillMetrics::LogFormFillDurationFromLoadWithoutAutofill( + const base::TimeDelta& duration) const { + UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithoutAutofill", + duration, + base::TimeDelta::FromMilliseconds(100), + base::TimeDelta::FromMinutes(10), + 50); +} + +void AutofillMetrics::LogFormFillDurationFromInteractionWithAutofill( + const base::TimeDelta& duration) const { + UMA_HISTOGRAM_CUSTOM_TIMES( + "Autofill.FillDuration.FromInteraction.WithAutofill", + duration, + base::TimeDelta::FromMilliseconds(100), + base::TimeDelta::FromMinutes(10), + 50); +} + +void AutofillMetrics::LogFormFillDurationFromInteractionWithoutAutofill( + const base::TimeDelta& duration) const { + UMA_HISTOGRAM_CUSTOM_TIMES( + "Autofill.FillDuration.FromInteraction.WithoutAutofill", + duration, + base::TimeDelta::FromMilliseconds(100), + base::TimeDelta::FromMinutes(10), + 50); +} + void AutofillMetrics::LogIsAutofillEnabledAtStartup(bool enabled) const { UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.Startup", enabled); } diff --git a/chrome/browser/autofill/autofill_metrics.h b/chrome/browser/autofill/autofill_metrics.h index 6e316e1..41d1351 100644 --- a/chrome/browser/autofill/autofill_metrics.h +++ b/chrome/browser/autofill/autofill_metrics.h @@ -12,6 +12,10 @@ #include "base/basictypes.h" #include "chrome/browser/autofill/field_types.h" +namespace base { +class TimeDelta; +} + class AutofillMetrics { public: enum InfoBarMetric { @@ -116,10 +120,6 @@ class AutofillMetrics { NUM_USER_HAPPINESS_METRICS }; - // TODO(isherman): Add histograms to measure time elapsed between form load - // form submission, comparing autofilled and non-autofilled forms. So that we - // are measuring apples to apples, restrict just to fillable forms. - AutofillMetrics(); virtual ~AutofillMetrics(); @@ -144,6 +144,28 @@ class AutofillMetrics { virtual void LogUserHappinessMetric(UserHappinessMetric metric) const; + // This should be called when a form that has been Autofilled is submitted. + // |duration| should be the time elapsed between form load and submission. + virtual void LogFormFillDurationFromLoadWithAutofill( + const base::TimeDelta& duration) const; + + // This should be called when a fillable form that has not been Autofilled is + // submitted. |duration| should be the time elapsed between form load and + // submission. + virtual void LogFormFillDurationFromLoadWithoutAutofill( + const base::TimeDelta& duration) const; + + // This should be called when a form that has been Autofilled is submitted. + // |duration| should be the time elapsed between the initial form interaction + // and submission. + virtual void LogFormFillDurationFromInteractionWithAutofill( + const base::TimeDelta& duration) const; + + // This should be called when a fillable form that has not been Autofilled is + // submitted. |duration| should be the time elapsed between the initial form + // interaction and submission. + virtual void LogFormFillDurationFromInteractionWithoutAutofill( + const base::TimeDelta& duration) const; // This should be called each time a page containing forms is loaded. virtual void LogIsAutofillEnabledAtPageLoad(bool enabled) const; diff --git a/chrome/browser/autofill/autofill_metrics_unittest.cc b/chrome/browser/autofill/autofill_metrics_unittest.cc index 76a953d..2015888 100644 --- a/chrome/browser/autofill/autofill_metrics_unittest.cc +++ b/chrome/browser/autofill/autofill_metrics_unittest.cc @@ -7,6 +7,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/string16.h" +#include "base/time.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_cc_infobar_delegate.h" #include "chrome/browser/autofill/autofill_common_test.h" @@ -22,6 +23,11 @@ #include "webkit/glue/form_data.h" #include "webkit/glue/form_field.h" +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Mock; +using base::TimeTicks; +using base::TimeDelta; using webkit_glue::FormData; using webkit_glue::FormField; @@ -47,6 +53,14 @@ class MockAutofillMetrics : public AutofillMetrics { const std::string& experiment_id)); MOCK_CONST_METHOD1(LogServerQueryMetric, void(ServerQueryMetric metric)); MOCK_CONST_METHOD1(LogUserHappinessMetric, void(UserHappinessMetric metric)); + MOCK_CONST_METHOD1(LogFormFillDurationFromLoadWithAutofill, + void(const TimeDelta& duration)); + MOCK_CONST_METHOD1(LogFormFillDurationFromLoadWithoutAutofill, + void(const TimeDelta& duration)); + MOCK_CONST_METHOD1(LogFormFillDurationFromInteractionWithAutofill, + void(const TimeDelta& duration)); + MOCK_CONST_METHOD1(LogFormFillDurationFromInteractionWithoutAutofill, + void(const TimeDelta& duration)); MOCK_CONST_METHOD1(LogIsAutofillEnabledAtPageLoad, void(bool enabled)); MOCK_CONST_METHOD1(LogIsAutofillEnabledAtStartup, void(bool enabled)); MOCK_CONST_METHOD1(LogStoredProfileCount, void(size_t num_profiles)); @@ -169,9 +183,9 @@ class TestAutofillManager : public AutofillManager { autofill_enabled_ = autofill_enabled; } - const MockAutofillMetrics* metric_logger() const { - return static_cast<const MockAutofillMetrics*>( - AutofillManager::metric_logger()); + MockAutofillMetrics* metric_logger() { + return static_cast<MockAutofillMetrics*>(const_cast<AutofillMetrics*>( + AutofillManager::metric_logger())); } void AddSeenForm(const FormData& form, @@ -395,7 +409,8 @@ TEST_F(AutofillMetricsTest, QualityMetrics) { AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME)); // Simulate form submission. - EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form)); + EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form, + TimeTicks::Now())); } // Test that we log the appropriate additional metrics when Autofill failed. @@ -512,7 +527,8 @@ TEST_F(AutofillMetricsTest, QualityMetricsForFailure) { } // Simulate form submission. - EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form)); + EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form, + TimeTicks::Now())); } // Test that we behave sanely when the cached form differs from the submitted @@ -669,7 +685,8 @@ TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { std::string())); // Simulate form submission. - EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form)); + EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form, + TimeTicks::Now())); } // Test that we don't log quality metrics for non-autofillable forms. @@ -695,7 +712,8 @@ TEST_F(AutofillMetricsTest, NoQualityMetricsForNonAutofillableForms) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED, std::string())).Times(0); - EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form)); + EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form, + TimeTicks::Now())); // Search forms are not auto-fillable. form.action = GURL("http://example.com/search?q=Elvis%20Presley"); @@ -707,7 +725,8 @@ TEST_F(AutofillMetricsTest, NoQualityMetricsForNonAutofillableForms) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogQualityMetric(AutofillMetrics::FIELD_SUBMITTED, std::string())).Times(0); - EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form)); + EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form, + TimeTicks::Now())); } // Test that we recored the experiment id appropriately. @@ -832,7 +851,8 @@ TEST_F(AutofillMetricsTest, QualityMetricsWithExperimentId) { ADDRESS_HOME_COUNTRY, experiment_id)); // Simulate form submission. - EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form)); + EXPECT_NO_FATAL_FAILURE(autofill_manager_->OnFormSubmitted(form, + TimeTicks::Now())); } // Test that the profile count is logged correctly. @@ -937,7 +957,7 @@ TEST_F(AutofillMetricsTest, AutofillIsEnabledAtPageLoad) { LogIsAutofillEnabledAtPageLoad(true)).Times(1); autofill_manager_->set_autofill_enabled(true); - autofill_manager_->OnFormsSeen(std::vector<FormData>()); + autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks()); // Reset the autofill manager state. autofill_manager_->Reset(); @@ -947,7 +967,7 @@ TEST_F(AutofillMetricsTest, AutofillIsEnabledAtPageLoad) { LogIsAutofillEnabledAtPageLoad(false)).Times(1); autofill_manager_->set_autofill_enabled(false); - autofill_manager_->OnFormsSeen(std::vector<FormData>()); + autofill_manager_->OnFormsSeen(std::vector<FormData>(), TimeTicks()); } // Test that credit card infobar metrics are logged correctly. @@ -1054,7 +1074,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED)).Times(0); - autofill_manager_->OnFormsSeen(forms); + autofill_manager_->OnFormsSeen(forms, TimeTicks()); } @@ -1076,7 +1096,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { *autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM)).Times(0); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } // Add more fields to the form. @@ -1090,7 +1110,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED)); - autofill_manager_->OnFormsSeen(forms); + autofill_manager_->OnFormsSeen(forms, TimeTicks()); } // Expect a notification when the form is submitted. @@ -1098,7 +1118,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM)); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } // Fill in two of the fields. @@ -1111,7 +1131,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM)); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } // Fill in the third field. @@ -1123,7 +1143,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE)); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } @@ -1136,7 +1156,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME)); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } // Mark all of the fillable fields as autofilled. @@ -1149,7 +1169,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL)); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } // Clear out the third field's value. @@ -1161,7 +1181,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormLoadAndSubmission) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM)); - autofill_manager_->OnFormSubmitted(form); + autofill_manager_->OnFormSubmitted(form, TimeTicks::Now()); } } @@ -1190,14 +1210,15 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED)); - autofill_manager_->OnFormsSeen(forms); + autofill_manager_->OnFormsSeen(forms, TimeTicks()); } // Simulate typing. { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE)); - autofill_manager_->OnTextFieldDidChange(form, form.fields.front()); + autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), + TimeTicks()); } // Simulate suggestions shown twice for a single edit (i.e. multiple @@ -1230,7 +1251,7 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::USER_DID_AUTOFILL_ONCE)); - autofill_manager_->OnDidFillAutofillFormData(); + autofill_manager_->OnDidFillAutofillFormData(TimeTicks()); } // Simulate editing an autofilled field. @@ -1246,9 +1267,11 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { autofill_manager_->OnFillAutofillFormData( 0, form, form.fields.front(), autofill_manager_->PackGUIDs(empty, guid)); - autofill_manager_->OnTextFieldDidChange(form, form.fields.front()); + autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), + TimeTicks()); // Simulate a second keystroke; make sure we don't log the metric twice. - autofill_manager_->OnTextFieldDidChange(form, form.fields.front()); + autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), + TimeTicks()); } // Simulate invoking autofill again. @@ -1257,13 +1280,130 @@ TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::USER_DID_AUTOFILL_ONCE)).Times(0); - autofill_manager_->OnDidFillAutofillFormData(); + autofill_manager_->OnDidFillAutofillFormData(TimeTicks()); // Simulate editing another autofilled field. { EXPECT_CALL(*autofill_manager_->metric_logger(), LogUserHappinessMetric( AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD)); - autofill_manager_->OnTextFieldDidChange(form, form.fields[1]); + autofill_manager_->OnTextFieldDidChange(form, form.fields[1], TimeTicks()); } } + +// Verify that we correctly log metrics tracking the duration of form fill. +TEST_F(AutofillMetricsTest, FormFillDuration) { + // Load a fillable form. + FormData form; + form.name = ASCIIToUTF16("TestForm"); + form.method = ASCIIToUTF16("POST"); + form.origin = GURL("http://example.com/form.html"); + form.action = GURL("http://example.com/submit.html"); + form.user_submitted = true; + + FormField field; + autofill_test::CreateTestFormField("Name", "name", "", "text", &field); + form.fields.push_back(field); + autofill_test::CreateTestFormField("Email", "email", "", "text", &field); + form.fields.push_back(field); + autofill_test::CreateTestFormField("Phone", "phone", "", "text", &field); + form.fields.push_back(field); + + std::vector<FormData> forms(1, form); + + // Fill the field values for form submission. + form.fields[0].value = ASCIIToUTF16("Elvis Aaron Presley"); + form.fields[1].value = ASCIIToUTF16("theking@gmail.com"); + form.fields[2].value = ASCIIToUTF16("12345678901"); + + // Ignore any non-timing metrics. + // CAUTION: This is a global variable. So as to not affect other tests, this + // _must_ be restored to "warning" at the end of the test. + ::testing::FLAGS_gmock_verbose = "error"; + + // Expect only form load metrics to be logged if the form is submitted without + // user interaction. + { + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithAutofill(_)).Times(0); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithoutAutofill( + TimeDelta::FromInternalValue(16))); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithAutofill(_)).Times(0); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithoutAutofill(_)).Times(0); + autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1)); + autofill_manager_->OnFormSubmitted(form, TimeTicks::FromInternalValue(17)); + autofill_manager_->Reset(); + Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger()); + } + + // Expect metric to be logged if the user manually edited a form field. + { + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithAutofill(_)).Times(0); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithoutAutofill( + TimeDelta::FromInternalValue(16))); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithAutofill(_)).Times(0); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithoutAutofill( + TimeDelta::FromInternalValue(14))); + autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1)); + autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), + TimeTicks::FromInternalValue(3)); + autofill_manager_->OnFormSubmitted(form, TimeTicks::FromInternalValue(17)); + autofill_manager_->Reset(); + Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger()); + } + + // Expect metric to be logged if the user autofilled the form. + form.fields[0].is_autofilled = true; + { + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithAutofill( + TimeDelta::FromInternalValue(16))); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithoutAutofill(_)).Times(0); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithAutofill( + TimeDelta::FromInternalValue(12))); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithoutAutofill(_)).Times(0); + autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1)); + autofill_manager_->OnDidFillAutofillFormData( + TimeTicks::FromInternalValue(5)); + autofill_manager_->OnFormSubmitted(form, TimeTicks::FromInternalValue(17)); + autofill_manager_->Reset(); + Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger()); + } + + // Expect metric to be logged if the user both manually filled some fields + // and autofilled others. Messages can arrive out of order, so make sure they + // take precedence appropriately. + { + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithAutofill( + TimeDelta::FromInternalValue(16))); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromLoadWithoutAutofill(_)).Times(0); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithAutofill( + TimeDelta::FromInternalValue(14))); + EXPECT_CALL(*autofill_manager_->metric_logger(), + LogFormFillDurationFromInteractionWithoutAutofill(_)).Times(0); + autofill_manager_->OnFormsSeen(forms, TimeTicks::FromInternalValue(1)); + autofill_manager_->OnDidFillAutofillFormData( + TimeTicks::FromInternalValue(5)); + autofill_manager_->OnTextFieldDidChange(form, form.fields.front(), + TimeTicks::FromInternalValue(3)); + autofill_manager_->OnFormSubmitted(form, TimeTicks::FromInternalValue(17)); + autofill_manager_->Reset(); + Mock::VerifyAndClearExpectations(autofill_manager_->metric_logger()); + } + + // Restore the global Gmock verbosity level to its default value. + ::testing::FLAGS_gmock_verbose = "warning"; +} diff --git a/chrome/browser/autofill/autofill_type.cc b/chrome/browser/autofill/autofill_type.cc index b53d997..4ee6350 100644 --- a/chrome/browser/autofill/autofill_type.cc +++ b/chrome/browser/autofill/autofill_type.cc @@ -10,11 +10,15 @@ namespace { +const AutofillType::AutofillTypeDefinition kUnknownAutofillTypeDefinition = { + /* UNKNOWN_TYPE */ AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP +}; + AutofillType::AutofillTypeDefinition kAutofillTypeDefinitions[] = { // NO_SERVER_DATA { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, // UNKNOWN_TYPE - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, + kUnknownAutofillTypeDefinition, // EMPTY_TYPE { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, @@ -46,11 +50,11 @@ AutofillType::AutofillTypeDefinition kAutofillTypeDefinitions[] = { { AutofillType::PHONE_HOME, AutofillType::PHONE_WHOLE_NUMBER }, // Work phone numbers (values [15,19]) are deprecated. - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, // PHONE_FAX_NUMBER { AutofillType::PHONE_FAX, AutofillType::PHONE_NUMBER }, @@ -64,11 +68,11 @@ AutofillType::AutofillTypeDefinition kAutofillTypeDefinitions[] = { { AutofillType::PHONE_FAX, AutofillType::PHONE_WHOLE_NUMBER }, // Cell phone numbers (values [25, 29]) are deprecated. - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, // ADDRESS_HOME_LINE1 { AutofillType::ADDRESS_HOME, AutofillType::ADDRESS_LINE1 }, @@ -101,13 +105,13 @@ AutofillType::AutofillTypeDefinition kAutofillTypeDefinitions[] = { { AutofillType::ADDRESS_BILLING, AutofillType::ADDRESS_COUNTRY }, // ADDRESS_SHIPPING values [44,50] are deprecated. - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, - { AutofillType::NO_GROUP, AutofillType::NO_SUBGROUP }, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, + kUnknownAutofillTypeDefinition, // CREDIT_CARD_NAME { AutofillType::CREDIT_CARD, AutofillType::NO_SUBGROUP }, diff --git a/chrome/browser/autofill/form_structure.cc b/chrome/browser/autofill/form_structure.cc index 97e706d..90250bf 100644 --- a/chrome/browser/autofill/form_structure.cc +++ b/chrome/browser/autofill/form_structure.cc @@ -12,6 +12,7 @@ #include "base/stringprintf.h" #include "base/string_number_conversions.h" #include "base/string_util.h" +#include "base/time.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autofill/autofill_metrics.h" #include "chrome/browser/autofill/autofill_type.h" @@ -646,7 +647,10 @@ void FormStructure::UpdateFromCache(const FormStructure& cached_form) { } void FormStructure::LogQualityMetrics( - const AutofillMetrics& metric_logger) const { + const AutofillMetrics& metric_logger, + const base::TimeTicks& load_time, + const base::TimeTicks& interaction_time, + const base::TimeTicks& submission_time) const { std::string experiment_id = server_experiment_id(); metric_logger.LogServerExperimentIdForUpload(experiment_id); @@ -778,15 +782,46 @@ void FormStructure::LogQualityMetrics( if (num_detected_field_types < kRequiredFillableFields) { metric_logger.LogUserHappinessMetric( AutofillMetrics::SUBMITTED_NON_FILLABLE_FORM); - } else if (did_autofill_all_possible_fields) { - metric_logger.LogUserHappinessMetric( - AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL); - } else if (did_autofill_some_possible_fields) { - metric_logger.LogUserHappinessMetric( - AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME); } else { - metric_logger.LogUserHappinessMetric( - AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE); + if (did_autofill_all_possible_fields) { + metric_logger.LogUserHappinessMetric( + AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_ALL); + } else if (did_autofill_some_possible_fields) { + metric_logger.LogUserHappinessMetric( + AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_SOME); + } else { + metric_logger.LogUserHappinessMetric( + AutofillMetrics::SUBMITTED_FILLABLE_FORM_AUTOFILLED_NONE); + } + + // Unlike the other times, the |submission_time| should always be available. + DCHECK(!submission_time.is_null()); + + // The |load_time| might be unset, in the case that the form was dynamically + // added to the DOM. + if (!load_time.is_null()) { + // Submission should always chronologically follow form load. + DCHECK(submission_time > load_time); + base::TimeDelta elapsed = submission_time - load_time; + if (did_autofill_some_possible_fields) + metric_logger.LogFormFillDurationFromLoadWithAutofill(elapsed); + else + metric_logger.LogFormFillDurationFromLoadWithoutAutofill(elapsed); + } + + // The |interaction_time| might be unset, in the case that the user + // submitted a blank form. + if (!interaction_time.is_null()) { + // Submission should always chronologically follow interaction. + DCHECK(submission_time > interaction_time); + base::TimeDelta elapsed = submission_time - interaction_time; + if (did_autofill_some_possible_fields) { + metric_logger.LogFormFillDurationFromInteractionWithAutofill(elapsed); + } else { + metric_logger.LogFormFillDurationFromInteractionWithoutAutofill( + elapsed); + } + } } } diff --git a/chrome/browser/autofill/form_structure.h b/chrome/browser/autofill/form_structure.h index 4f154d4..9824c36 100644 --- a/chrome/browser/autofill/form_structure.h +++ b/chrome/browser/autofill/form_structure.h @@ -30,6 +30,10 @@ enum UploadRequired { class AutofillMetrics; +namespace base { +class TimeTicks; +} + namespace buzz { class XmlElement; } @@ -106,8 +110,13 @@ class FormStructure { // Logs quality metrics for |this|, which should be a user-submitted form. // This method should only be called after the possible field types have been - // set for each field. - void LogQualityMetrics(const AutofillMetrics& metric_logger) const; + // set for each field. |interaction_time| should be a timestamp corresponding + // to the user's first interaction with the form. |submission_time| should be + // a timestamp corresponding to the form's submission. + void LogQualityMetrics(const AutofillMetrics& metric_logger, + const base::TimeTicks& load_time, + const base::TimeTicks& interaction_time, + const base::TimeTicks& submission_time) const; const AutofillField* field(size_t index) const; AutofillField* field(size_t index); diff --git a/chrome/common/autofill_messages.h b/chrome/common/autofill_messages.h index 75cc2fb..213f081 100644 --- a/chrome/common/autofill_messages.h +++ b/chrome/common/autofill_messages.h @@ -6,8 +6,10 @@ #include <string> +#include "base/time.h" #include "content/common/webkit_param_traits.h" #include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" #include "webkit/glue/form_data.h" #include "webkit/glue/form_data_predictions.h" #include "webkit/glue/form_field.h" @@ -90,8 +92,9 @@ IPC_MESSAGE_ROUTED1( // Notification that forms have been seen that are candidates for // filling/submitting by the AutofillManager. -IPC_MESSAGE_ROUTED1(AutofillHostMsg_FormsSeen, - std::vector<webkit_glue::FormData> /* forms */) +IPC_MESSAGE_ROUTED2(AutofillHostMsg_FormsSeen, + std::vector<webkit_glue::FormData> /* forms */, + base::TimeTicks /* timestamp */) // Notification that password forms have been seen that are candidates for // filling/submitting by the password manager. @@ -104,13 +107,15 @@ IPC_MESSAGE_ROUTED1(AutofillHostMsg_PasswordFormsVisible, std::vector<webkit_glue::PasswordForm> /* forms */) // Notification that a form has been submitted. The user hit the button. -IPC_MESSAGE_ROUTED1(AutofillHostMsg_FormSubmitted, - webkit_glue::FormData /* form */) +IPC_MESSAGE_ROUTED2(AutofillHostMsg_FormSubmitted, + webkit_glue::FormData /* form */, + base::TimeTicks /* timestamp */) // Notification that a form field's value has changed. -IPC_MESSAGE_ROUTED2(AutofillHostMsg_TextFieldDidChange, +IPC_MESSAGE_ROUTED3(AutofillHostMsg_TextFieldDidChange, webkit_glue::FormData /* the form */, - webkit_glue::FormField /* the form field */) + webkit_glue::FormField /* the form field */, + base::TimeTicks /* timestamp */) // Queries the browser for Autofill suggestions for a form input field. IPC_MESSAGE_ROUTED3(AutofillHostMsg_QueryFormFieldAutofill, @@ -134,7 +139,8 @@ IPC_MESSAGE_ROUTED4(AutofillHostMsg_FillAutofillFormData, IPC_MESSAGE_ROUTED0(AutofillHostMsg_DidPreviewAutofillFormData) // Sent when a form is filled with Autofill suggestions. -IPC_MESSAGE_ROUTED0(AutofillHostMsg_DidFillAutofillFormData) +IPC_MESSAGE_ROUTED1(AutofillHostMsg_DidFillAutofillFormData, + base::TimeTicks /* timestamp */) // Instructs the browser to remove the specified Autocomplete entry from the // database. diff --git a/chrome/renderer/autofill/autofill_agent.cc b/chrome/renderer/autofill/autofill_agent.cc index 9b55fcc..98334d7 100644 --- a/chrome/renderer/autofill/autofill_agent.cc +++ b/chrome/renderer/autofill/autofill_agent.cc @@ -5,6 +5,7 @@ #include "chrome/renderer/autofill/autofill_agent.h" #include "base/message_loop.h" +#include "base/time.h" #include "base/utf_string_conversions.h" #include "chrome/common/autofill_messages.h" #include "chrome/common/chrome_constants.h" @@ -79,8 +80,10 @@ void AutofillAgent::DidFinishDocumentLoad(WebFrame* frame) { std::vector<webkit_glue::FormData> forms; form_manager_.ExtractForms(frame, &forms); - if (!forms.empty()) - Send(new AutofillHostMsg_FormsSeen(routing_id(), forms)); + if (!forms.empty()) { + Send(new AutofillHostMsg_FormsSeen(routing_id(), forms, + base::TimeTicks::Now())); + } } void AutofillAgent::FrameDetached(WebFrame* frame) { @@ -100,7 +103,8 @@ void AutofillAgent::WillSubmitForm(WebFrame* frame, static_cast<FormManager::ExtractMask>( FormManager::EXTRACT_VALUE | FormManager::EXTRACT_OPTION_TEXT), &form_data)) { - Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data)); + Send(new AutofillHostMsg_FormSubmitted(routing_id(), form_data, + base::TimeTicks::Now())); } } @@ -201,8 +205,10 @@ void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { webkit_glue::FormData form; webkit_glue::FormField field; - if (FindFormAndFieldForNode(element, &form, &field)) - Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field)); + if (FindFormAndFieldForNode(element, &form, &field)) { + Send(new AutofillHostMsg_TextFieldDidChange(routing_id(), form, field, + base::TimeTicks::Now())); + } } void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, @@ -311,7 +317,8 @@ void AutofillAgent::OnFormDataFilled(int query_id, switch (autofill_action_) { case AUTOFILL_FILL: form_manager_.FillForm(form, autofill_query_element_); - Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id())); + Send(new AutofillHostMsg_DidFillAutofillFormData(routing_id(), + base::TimeTicks::Now())); break; case AUTOFILL_PREVIEW: form_manager_.PreviewForm(form, autofill_query_element_); diff --git a/chrome/renderer/autofill/form_autocomplete_browsertest.cc b/chrome/renderer/autofill/form_autocomplete_browsertest.cc index 9d58e94..101b812 100644 --- a/chrome/renderer/autofill/form_autocomplete_browsertest.cc +++ b/chrome/renderer/autofill/form_autocomplete_browsertest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/time.h" #include "chrome/common/autofill_messages.h" #include "chrome/test/base/render_view_test.h" #include "testing/gtest/include/gtest/gtest.h" @@ -35,7 +36,8 @@ TEST_F(FormAutocompleteTest, NormalFormSubmit) { AutofillHostMsg_FormSubmitted::ID); ASSERT_TRUE(message != NULL); - Tuple1<FormData> forms; + // The tuple also includes a timestamp, which is ignored. + Tuple2<FormData, base::TimeTicks> forms; AutofillHostMsg_FormSubmitted::Read(message, &forms); ASSERT_EQ(2U, forms.a.fields.size()); @@ -83,7 +85,8 @@ TEST_F(FormAutocompleteTest, AutoCompleteOffInputSubmit) { AutofillHostMsg_FormSubmitted::ID); ASSERT_TRUE(message != NULL); - Tuple1<FormData> forms; + // The tuple also includes a timestamp, which is ignored. + Tuple2<FormData, base::TimeTicks> forms; AutofillHostMsg_FormSubmitted::Read(message, &forms); ASSERT_EQ(1U, forms.a.fields.size()); diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc index 6ef03f4..dcbde0b 100644 --- a/ipc/ipc_message_utils.cc +++ b/ipc/ipc_message_utils.cc @@ -269,7 +269,7 @@ void ParamTraits<base::Time>::Log(const param_type& p, std::string* l) { } void ParamTraits<base::TimeDelta> ::Write(Message* m, const param_type& p) { - ParamTraits<int64> ::Write(m, p.InMicroseconds()); + ParamTraits<int64> ::Write(m, p.ToInternalValue()); } bool ParamTraits<base::TimeDelta> ::Read(const Message* m, @@ -278,13 +278,32 @@ bool ParamTraits<base::TimeDelta> ::Read(const Message* m, int64 value; bool ret = ParamTraits<int64> ::Read(m, iter, &value); if (ret) - *r = base::TimeDelta::FromMicroseconds(value); + *r = base::TimeDelta::FromInternalValue(value); return ret; } void ParamTraits<base::TimeDelta> ::Log(const param_type& p, std::string* l) { - ParamTraits<int64> ::Log(p.InMicroseconds(), l); + ParamTraits<int64> ::Log(p.ToInternalValue(), l); +} + +void ParamTraits<base::TimeTicks> ::Write(Message* m, const param_type& p) { + ParamTraits<int64> ::Write(m, p.ToInternalValue()); +} + +bool ParamTraits<base::TimeTicks> ::Read(const Message* m, + void** iter, + param_type* r) { + int64 value; + bool ret = ParamTraits<int64> ::Read(m, iter, &value); + if (ret) + *r = base::TimeTicks::FromInternalValue(value); + + return ret; +} + +void ParamTraits<base::TimeTicks> ::Log(const param_type& p, std::string* l) { + ParamTraits<int64> ::Log(p.ToInternalValue(), l); } void ParamTraits<DictionaryValue>::Write(Message* m, const param_type& p) { diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index e7bbe34..7c46f90 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -105,6 +105,7 @@ class DictionaryValue; class ListValue; class Time; class TimeDelta; +class TimeTicks; struct FileDescriptor; } @@ -332,6 +333,14 @@ struct IPC_EXPORT ParamTraits<base::TimeDelta> { static void Log(const param_type& p, std::string* l); }; +template <> +struct IPC_EXPORT ParamTraits<base::TimeTicks> { + typedef base::TimeTicks param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + #if defined(OS_WIN) template <> struct ParamTraits<LOGFONT> { |