| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/core/browser/autofill_metrics.h" |
| |
| #include "base/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/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" |
| #include "components/autofill/core/common/form_data.h" |
| |
| namespace autofill { |
| |
| namespace { |
| |
| enum FieldTypeGroupForMetrics { |
| AMBIGUOUS = 0, |
| NAME, |
| COMPANY, |
| ADDRESS_LINE_1, |
| ADDRESS_LINE_2, |
| ADDRESS_CITY, |
| ADDRESS_STATE, |
| ADDRESS_ZIP, |
| ADDRESS_COUNTRY, |
| PHONE, |
| FAX, // Deprecated. |
| EMAIL, |
| CREDIT_CARD_NAME, |
| CREDIT_CARD_NUMBER, |
| CREDIT_CARD_DATE, |
| CREDIT_CARD_TYPE, |
| PASSWORD, |
| ADDRESS_LINE_3, |
| NUM_FIELD_TYPE_GROUPS_FOR_METRICS |
| }; |
| |
| // First, translates |field_type| to the corresponding logical |group| from |
| // |FieldTypeGroupForMetrics|. Then, interpolates this with the given |metric|, |
| // which should be in the range [0, |num_possible_metrics|). |
| // Returns the interpolated index. |
| // |
| // The interpolation maps the pair (|group|, |metric|) to a single index, so |
| // that all the indicies for a given group are adjacent. In particular, with |
| // the groups {AMBIGUOUS, NAME, ...} combining with the metrics {UNKNOWN, MATCH, |
| // MISMATCH}, we create this set of mapped indices: |
| // { |
| // AMBIGUOUS+UNKNOWN, |
| // AMBIGUOUS+MATCH, |
| // AMBIGUOUS+MISMATCH, |
| // NAME+UNKNOWN, |
| // NAME+MATCH, |
| // NAME+MISMATCH, |
| // ... |
| // }. |
| // |
| // Clients must ensure that |field_type| is one of the types Chrome supports |
| // natively, e.g. |field_type| must not be a billng address. |
| int GetFieldTypeGroupMetric(const ServerFieldType field_type, |
| const int metric, |
| const int num_possible_metrics) { |
| DCHECK_LT(metric, num_possible_metrics); |
| |
| FieldTypeGroupForMetrics group = AMBIGUOUS; |
| switch (AutofillType(field_type).group()) { |
| case ::autofill::NO_GROUP: |
| group = AMBIGUOUS; |
| break; |
| |
| case ::autofill::NAME: |
| case ::autofill::NAME_BILLING: |
| group = NAME; |
| break; |
| |
| case ::autofill::COMPANY: |
| group = COMPANY; |
| break; |
| |
| case ::autofill::ADDRESS_HOME: |
| case ::autofill::ADDRESS_BILLING: |
| switch (AutofillType(field_type).GetStorableType()) { |
| case ADDRESS_HOME_LINE1: |
| group = ADDRESS_LINE_1; |
| break; |
| case ADDRESS_HOME_LINE2: |
| group = ADDRESS_LINE_2; |
| break; |
| case ADDRESS_HOME_LINE3: |
| group = ADDRESS_LINE_3; |
| break; |
| case ADDRESS_HOME_CITY: |
| group = ADDRESS_CITY; |
| break; |
| case ADDRESS_HOME_STATE: |
| group = ADDRESS_STATE; |
| break; |
| case ADDRESS_HOME_ZIP: |
| group = ADDRESS_ZIP; |
| break; |
| case ADDRESS_HOME_COUNTRY: |
| group = ADDRESS_COUNTRY; |
| break; |
| default: |
| NOTREACHED(); |
| group = AMBIGUOUS; |
| break; |
| } |
| break; |
| |
| case ::autofill::EMAIL: |
| group = EMAIL; |
| break; |
| |
| case ::autofill::PHONE_HOME: |
| case ::autofill::PHONE_BILLING: |
| group = PHONE; |
| break; |
| |
| case ::autofill::CREDIT_CARD: |
| switch (field_type) { |
| case ::autofill::CREDIT_CARD_NAME: |
| group = CREDIT_CARD_NAME; |
| break; |
| case ::autofill::CREDIT_CARD_NUMBER: |
| group = CREDIT_CARD_NUMBER; |
| break; |
| case ::autofill::CREDIT_CARD_TYPE: |
| group = CREDIT_CARD_TYPE; |
| 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; |
| |
| case ::autofill::PASSWORD_FIELD: |
| group = PASSWORD; |
| break; |
| |
| case ::autofill::TRANSACTION: |
| NOTREACHED(); |
| break; |
| } |
| |
| // Interpolate the |metric| with the |group|, so that all metrics for a given |
| // |group| are adjacent. |
| return (group * num_possible_metrics) + metric; |
| } |
| |
| std::string WalletApiMetricToString( |
| AutofillMetrics::WalletApiCallMetric metric) { |
| switch (metric) { |
| case AutofillMetrics::ACCEPT_LEGAL_DOCUMENTS: |
| return "AcceptLegalDocuments"; |
| case AutofillMetrics::AUTHENTICATE_INSTRUMENT: |
| return "AuthenticateInstrument"; |
| case AutofillMetrics::GET_FULL_WALLET: |
| return "GetFullWallet"; |
| case AutofillMetrics::GET_WALLET_ITEMS: |
| return "GetWalletItems"; |
| case AutofillMetrics::SAVE_TO_WALLET: |
| return "SaveToWallet"; |
| case AutofillMetrics::UNKNOWN_API_CALL: |
| case AutofillMetrics::NUM_WALLET_API_CALLS: |
| NOTREACHED(); |
| return "UnknownApiCall"; |
| } |
| |
| NOTREACHED(); |
| return "UnknownApiCall"; |
| } |
| |
| // A version of the UMA_HISTOGRAM_ENUMERATION macro that allows the |name| |
| // to vary over the program's runtime. |
| void LogUMAHistogramEnumeration(const std::string& name, |
| int sample, |
| int boundary_value) { |
| DCHECK_LT(sample, boundary_value); |
| |
| // Note: This leaks memory, which is expected behavior. |
| base::HistogramBase* histogram = |
| base::LinearHistogram::FactoryGet( |
| name, |
| 1, |
| boundary_value, |
| boundary_value + 1, |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| histogram->Add(sample); |
| } |
| |
| // A version of the UMA_HISTOGRAM_TIMES macro that allows the |name| |
| // to vary over the program's runtime. |
| void LogUMAHistogramTimes(const std::string& name, |
| const base::TimeDelta& duration) { |
| // Note: This leaks memory, which is expected behavior. |
| base::HistogramBase* histogram = |
| base::Histogram::FactoryTimeGet( |
| name, |
| base::TimeDelta::FromMilliseconds(1), |
| base::TimeDelta::FromSeconds(10), |
| 50, |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| histogram->AddTime(duration); |
| } |
| |
| // A version of the UMA_HISTOGRAM_LONG_TIMES macro that allows the |name| |
| // to vary over the program's runtime. |
| void LogUMAHistogramLongTimes(const std::string& name, |
| const base::TimeDelta& duration) { |
| // Note: This leaks memory, which is expected behavior. |
| base::HistogramBase* histogram = |
| base::Histogram::FactoryTimeGet( |
| name, |
| base::TimeDelta::FromMilliseconds(1), |
| base::TimeDelta::FromHours(1), |
| 50, |
| base::HistogramBase::kUmaTargetedHistogramFlag); |
| histogram->AddTime(duration); |
| } |
| |
| // Logs a type quality metric. The primary histogram name is constructed based |
| // on |base_name|. The field-specific histogram name also factors in the |
| // |field_type|. Logs a sample of |metric|, which should be in the range |
| // [0, |num_possible_metrics|). |
| void LogTypeQualityMetric(const std::string& base_name, |
| const int metric, |
| const int num_possible_metrics, |
| const ServerFieldType field_type) { |
| DCHECK_LT(metric, num_possible_metrics); |
| |
| std::string histogram_name = base_name; |
| LogUMAHistogramEnumeration(histogram_name, metric, num_possible_metrics); |
| |
| std::string sub_histogram_name = base_name + ".ByFieldType"; |
| const int field_type_group_metric = |
| GetFieldTypeGroupMetric(field_type, metric, num_possible_metrics); |
| const int num_field_type_group_metrics = |
| num_possible_metrics * NUM_FIELD_TYPE_GROUPS_FOR_METRICS; |
| LogUMAHistogramEnumeration(sub_histogram_name, |
| field_type_group_metric, |
| num_field_type_group_metrics); |
| } |
| |
| } // namespace |
| |
| AutofillMetrics::AutofillMetrics() { |
| } |
| |
| AutofillMetrics::~AutofillMetrics() { |
| } |
| |
| void AutofillMetrics::LogCreditCardInfoBarMetric(InfoBarMetric metric) const { |
| DCHECK_LT(metric, NUM_INFO_BAR_METRICS); |
| |
| UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardInfoBar", metric, |
| NUM_INFO_BAR_METRICS); |
| } |
| |
| void AutofillMetrics::LogDialogDismissalState( |
| DialogDismissalState state) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.DismissalState", |
| state, NUM_DIALOG_DISMISSAL_STATES); |
| } |
| |
| void AutofillMetrics::LogDialogInitialUserState( |
| DialogInitialUserStateMetric user_type) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.InitialUserState", |
| user_type, NUM_DIALOG_INITIAL_USER_STATE_METRICS); |
| } |
| |
| void AutofillMetrics::LogDialogLatencyToShow( |
| const base::TimeDelta& duration) const { |
| LogUMAHistogramTimes("RequestAutocomplete.UiLatencyToShow", duration); |
| } |
| |
| void AutofillMetrics::LogDialogPopupEvent(DialogPopupEvent event) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.PopupInDialog", |
| event, NUM_DIALOG_POPUP_EVENTS); |
| } |
| |
| void AutofillMetrics::LogDialogSecurityMetric( |
| DialogSecurityMetric metric) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.Security", |
| metric, NUM_DIALOG_SECURITY_METRICS); |
| } |
| |
| void AutofillMetrics::LogDialogUiDuration( |
| const base::TimeDelta& duration, |
| DialogDismissalAction dismissal_action) const { |
| std::string suffix; |
| switch (dismissal_action) { |
| case DIALOG_ACCEPTED: |
| suffix = "Submit"; |
| break; |
| |
| case DIALOG_CANCELED: |
| suffix = "Cancel"; |
| break; |
| } |
| |
| LogUMAHistogramLongTimes("RequestAutocomplete.UiDuration", duration); |
| LogUMAHistogramLongTimes("RequestAutocomplete.UiDuration." + suffix, |
| duration); |
| } |
| |
| void AutofillMetrics::LogDialogUiEvent(DialogUiEvent event) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.UiEvents", event, |
| NUM_DIALOG_UI_EVENTS); |
| } |
| |
| void AutofillMetrics::LogWalletErrorMetric(WalletErrorMetric metric) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.WalletErrors", metric, |
| NUM_WALLET_ERROR_METRICS); |
| } |
| |
| void AutofillMetrics::LogWalletApiCallDuration( |
| WalletApiCallMetric metric, |
| const base::TimeDelta& duration) const { |
| LogUMAHistogramTimes("Wallet.ApiCallDuration." + |
| WalletApiMetricToString(metric), duration); |
| } |
| |
| void AutofillMetrics::LogWalletMalformedResponseMetric( |
| WalletApiCallMetric metric) const { |
| UMA_HISTOGRAM_ENUMERATION("Wallet.MalformedResponse", metric, |
| NUM_WALLET_API_CALLS); |
| } |
| |
| void AutofillMetrics::LogWalletRequiredActionMetric( |
| WalletRequiredActionMetric required_action) const { |
| UMA_HISTOGRAM_ENUMERATION("RequestAutocomplete.WalletRequiredActions", |
| required_action, NUM_WALLET_REQUIRED_ACTIONS); |
| } |
| |
| void AutofillMetrics::LogWalletResponseCode(int response_code) const { |
| UMA_HISTOGRAM_SPARSE_SLOWLY("Wallet.ResponseCode", response_code); |
| } |
| |
| void AutofillMetrics::LogDeveloperEngagementMetric( |
| DeveloperEngagementMetric metric) const { |
| DCHECK_LT(metric, NUM_DEVELOPER_ENGAGEMENT_METRICS); |
| |
| UMA_HISTOGRAM_ENUMERATION("Autofill.DeveloperEngagement", metric, |
| NUM_DEVELOPER_ENGAGEMENT_METRICS); |
| } |
| |
| void AutofillMetrics::LogHeuristicTypePrediction( |
| FieldTypeQualityMetric metric, |
| ServerFieldType field_type) const { |
| LogTypeQualityMetric("Autofill.Quality.HeuristicType", |
| metric, NUM_FIELD_TYPE_QUALITY_METRICS, field_type); |
| } |
| |
| void AutofillMetrics::LogOverallTypePrediction( |
| FieldTypeQualityMetric metric, |
| ServerFieldType field_type) const { |
| LogTypeQualityMetric("Autofill.Quality.PredictedType", |
| metric, NUM_FIELD_TYPE_QUALITY_METRICS, field_type); |
| } |
| |
| void AutofillMetrics::LogServerTypePrediction( |
| FieldTypeQualityMetric metric, |
| ServerFieldType field_type) const { |
| LogTypeQualityMetric("Autofill.Quality.ServerType", |
| metric, NUM_FIELD_TYPE_QUALITY_METRICS, field_type); |
| } |
| |
| void AutofillMetrics::LogServerQueryMetric(ServerQueryMetric metric) const { |
| DCHECK_LT(metric, NUM_SERVER_QUERY_METRICS); |
| |
| UMA_HISTOGRAM_ENUMERATION("Autofill.ServerQueryResponse", metric, |
| NUM_SERVER_QUERY_METRICS); |
| } |
| |
| void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric) const { |
| DCHECK_LT(metric, NUM_USER_HAPPINESS_METRICS); |
| |
| UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness", metric, |
| NUM_USER_HAPPINESS_METRICS); |
| } |
| |
| void AutofillMetrics::LogFormFillDurationFromLoadWithAutofill( |
| const base::TimeDelta& duration) const { |
| UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithAutofill", |
| duration, |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMinutes(10), |
| 50); |
| } |
| |
| void AutofillMetrics::LogFormFillDurationFromLoadWithoutAutofill( |
| const base::TimeDelta& duration) const { |
| UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithoutAutofill", |
| duration, |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMinutes(10), |
| 50); |
| } |
| |
| void AutofillMetrics::LogFormFillDurationFromInteractionWithAutofill( |
| const base::TimeDelta& duration) const { |
| UMA_HISTOGRAM_CUSTOM_TIMES( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", |
| duration, |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMinutes(10), |
| 50); |
| } |
| |
| void AutofillMetrics::LogFormFillDurationFromInteractionWithoutAutofill( |
| const base::TimeDelta& duration) const { |
| UMA_HISTOGRAM_CUSTOM_TIMES( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", |
| duration, |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMinutes(10), |
| 50); |
| } |
| |
| void AutofillMetrics::LogIsAutofillEnabledAtStartup(bool enabled) const { |
| UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.Startup", enabled); |
| } |
| |
| void AutofillMetrics::LogIsAutofillEnabledAtPageLoad(bool enabled) const { |
| UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.PageLoad", enabled); |
| } |
| |
| void AutofillMetrics::LogStoredProfileCount(size_t num_profiles) const { |
| UMA_HISTOGRAM_COUNTS("Autofill.StoredProfileCount", num_profiles); |
| } |
| |
| void AutofillMetrics::LogAddressSuggestionsCount(size_t num_suggestions) const { |
| UMA_HISTOGRAM_COUNTS("Autofill.AddressSuggestionsCount", num_suggestions); |
| } |
| |
| } // namespace autofill |